diff --git a/dlangide-monod-linux.dproj b/dlangide-monod-linux.dproj
index 23a6d04..602b037 100644
--- a/dlangide-monod-linux.dproj
+++ b/dlangide-monod-linux.dproj
@@ -33,6 +33,7 @@
USE_OPENGL
USE_SDL
USE_FREETYPE
+ USE_GDB_DEBUG
EmbedStandardResources
@@ -164,6 +165,30 @@
true
0
+
+ bin\DebugX11
+
+
+ USE_X11
+ USE_FREETYPE
+ USE_GDB_DEBUG
+ EmbedStandardResources
+
+
+ obj/DebugX11
+ true
+ -L-lX11
+ -Jviews
+-Jviews/res
+-Jviews/res/i18n
+-Jviews/res/mdpi
+-Jviews/res/hdpi
+ false
+ dlangide-monod-linux
+ Executable
+ true
+ 0
+
@@ -192,5 +217,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/dlangide-monod-linux.sln b/dlangide-monod-linux.sln
index b7e62bd..ba2f7dc 100644
--- a/dlangide-monod-linux.sln
+++ b/dlangide-monod-linux.sln
@@ -13,6 +13,8 @@ Project("{3947E667-4C90-4C3A-BEB9-7148D6FE0D7C}") = "tetris-monod-linux", "..\dl
EndProject
Project("{3947E667-4C90-4C3A-BEB9-7148D6FE0D7C}") = "dmledit-monod-linux", "..\dlangui\examples\dmledit\dmledit-monod-linux.dproj", "{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}"
EndProject
+Project("{3947E667-4C90-4C3A-BEB9-7148D6FE0D7C}") = "disowntty", "tools\disowntty\disowntty.dproj", "{FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,12 +23,15 @@ Global
DebugMinimal|Any CPU = DebugMinimal|Any CPU
ReleaseMinimal|Any CPU = ReleaseMinimal|Any CPU
UnittestMinimal|Any CPU = UnittestMinimal|Any CPU
+ DebugX11|Any CPU = DebugX11|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.Debug|Any CPU.ActiveCfg = Debug|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.Debug|Any CPU.Build.0 = Debug|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.Release|Any CPU.ActiveCfg = Release|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.Release|Any CPU.Build.0 = Release|x64
{075C374A-563A-4CAC-9E9F-0B6E1DFEAEC3}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -39,6 +44,8 @@ Global
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.Debug|Any CPU.Build.0 = Debug|x64
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {0A3A0D08-E4DC-418E-B92B-561CC91C2306}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {0A3A0D08-E4DC-418E-B92B-561CC91C2306}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.Release|Any CPU.ActiveCfg = Release|x64
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.Release|Any CPU.Build.0 = Release|x64
{0A3A0D08-E4DC-418E-B92B-561CC91C2306}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -51,6 +58,8 @@ Global
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.Debug|Any CPU.Build.0 = Debug|x64
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.Release|Any CPU.ActiveCfg = Release|x64
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.Release|Any CPU.Build.0 = Release|x64
{54BDE028-6064-4CA9-B6CA-4C0BEEE70F24}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -63,6 +72,8 @@ Global
{85ECF79F-B75F-4459-8A90-3857961F2029}.Debug|Any CPU.Build.0 = Debug|x64
{85ECF79F-B75F-4459-8A90-3857961F2029}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{85ECF79F-B75F-4459-8A90-3857961F2029}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {85ECF79F-B75F-4459-8A90-3857961F2029}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {85ECF79F-B75F-4459-8A90-3857961F2029}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{85ECF79F-B75F-4459-8A90-3857961F2029}.Release|Any CPU.ActiveCfg = Release|x64
{85ECF79F-B75F-4459-8A90-3857961F2029}.Release|Any CPU.Build.0 = Release|x64
{85ECF79F-B75F-4459-8A90-3857961F2029}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -75,6 +86,8 @@ Global
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.Debug|Any CPU.Build.0 = Debug|x64
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.Release|Any CPU.ActiveCfg = Release|x64
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.Release|Any CPU.Build.0 = Release|x64
{8E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -87,6 +100,8 @@ Global
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.Debug|Any CPU.Build.0 = Debug|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.DebugMinimal|Any CPU.ActiveCfg = DebugMinimal|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.DebugMinimal|Any CPU.Build.0 = DebugMinimal|x64
+ {A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.DebugX11|Any CPU.ActiveCfg = DebugX11|x64
+ {A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.DebugX11|Any CPU.Build.0 = DebugX11|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.Release|Any CPU.ActiveCfg = Release|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.Release|Any CPU.Build.0 = Release|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.ReleaseMinimal|Any CPU.ActiveCfg = ReleaseMinimal|x64
@@ -95,5 +110,19 @@ Global
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.Unittest|Any CPU.Build.0 = Unittest|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.UnittestMinimal|Any CPU.ActiveCfg = UnittestMinimal|x64
{A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}.UnittestMinimal|Any CPU.Build.0 = UnittestMinimal|x64
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.DebugMinimal|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.DebugMinimal|Any CPU.Build.0 = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.DebugX11|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.DebugX11|Any CPU.Build.0 = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.ReleaseMinimal|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.ReleaseMinimal|Any CPU.Build.0 = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Unittest|Any CPU.ActiveCfg = Unittest|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.Unittest|Any CPU.Build.0 = Unittest|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.UnittestMinimal|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}.UnittestMinimal|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
EndGlobal
diff --git a/src/ddebug/common/debugger.d b/src/ddebug/common/debugger.d
index 9c99a16..3176917 100644
--- a/src/ddebug/common/debugger.d
+++ b/src/ddebug/common/debugger.d
@@ -2,26 +2,106 @@ module ddebug.common.debugger;
import core.thread;
import dlangui.core.logger;
+import ddebug.common.queue;
+
+enum ResponseCode : int {
+ /// Operation finished successfully
+ Ok = 0,
+
+ // more success codes here
+
+ /// General purpose failure code
+ Fail = 1000,
+ /// method is not implemented
+ NotImplemented,
+ /// error running debugger
+ CannotRunDebugger,
+
+ // more error codes here
+}
+
+alias Runnable = void delegate();
+alias DebuggerResponse = void delegate(ResponseCode code, string msg);
interface Debugger {
/// start debugging
- void startDebugging(string executable, string[] args, string workingDir);
+ void startDebugging(string debuggerExecutable, string executable, string[] args, string workingDir, DebuggerResponse response);
}
-interface DebuggerCallback {
+
+
+/// proxy for debugger interface implementing async calls
+class DebuggerProxy : Debugger {
+ private DebuggerBase _debugger;
+ private void delegate(Runnable runnable) _callbackDelegate;
+
+ this(DebuggerBase debugger, void delegate(Runnable runnable) callbackDelegate) {
+ _debugger = debugger;
+ _callbackDelegate = callbackDelegate;
+ }
+
+ void startDebugging(string debuggerExecutable, string executable, string[] args, string workingDir, DebuggerResponse response) {
+ _debugger.postRequest(delegate() {
+ _debugger.startDebugging(debuggerExecutable, executable, args, workingDir,
+ delegate(ResponseCode code, string msg) {
+ _callbackDelegate( delegate() { response(code, msg); } );
+ }
+ );
+ });
+ }
+
}
-class DebuggerBase : Thread {
+class DebuggerBase : Thread, Debugger {
private bool _stopRequested;
private bool _finished;
+ protected string _debuggerExecutable;
+ protected BlockingQueue!Runnable _queue;
+
+ void postRequest(Runnable request) {
+ _queue.put(request);
+ }
this() {
super(&run);
+ _queue = new BlockingQueue!Runnable();
}
+ ~this() {
+ stop();
+ destroy(_queue);
+ _queue = null;
+ }
+
+ void stop() {
+ Log.i("Debugger.stop()");
+ _stopRequested = true;
+ _queue.close();
+ }
+
+ protected void onDebuggerThreadStarted() {
+ }
+
+ protected void onDebuggerThreadFinished() {
+ }
+
+ /// thread func: execute all tasks from queue
private void run() {
+ onDebuggerThreadStarted();
Log.i("Debugger thread started");
+ while (!_stopRequested) {
+ Runnable task;
+ if (_queue.get(task, 0)) {
+ task();
+ }
+ }
Log.i("Debugger thread finished");
_finished = true;
+ onDebuggerThreadFinished();
}
+
+ void startDebugging(string debuggerExecutable, string executable, string[] args, string workingDir, DebuggerResponse response) {
+ response(ResponseCode.NotImplemented, "Not Implemented");
+ }
+
}
diff --git a/src/ddebug/common/queue.d b/src/ddebug/common/queue.d
index bac99ec..d4669b9 100644
--- a/src/ddebug/common/queue.d
+++ b/src/ddebug/common/queue.d
@@ -7,20 +7,127 @@ class BlockingQueue(T) {
private Mutex _mutex;
private Condition _condition;
+ private T[] _buffer;
+ private int _readPos;
+ private int _writePos;
+ private shared bool _closed;
this() {
_mutex = new Mutex();
_condition = new Condition(_mutex);
+ _readPos = 0;
+ _writePos = 0;
+ }
+
+ void close() {
+ if (_mutex && !_closed) {
+ synchronized(_mutex) {
+ _closed = true;
+ if (_condition !is null)
+ _condition.notifyAll();
+ }
+ } else {
+ _closed = true;
+ }
+ if (_condition) {
+ destroy(_condition);
+ _condition = null;
+ }
+ if (_mutex) {
+ destroy(_mutex);
+ _mutex = null;
+ }
}
~this() {
// TODO: destroy mutex?
+ close();
+ }
+
+ private void move() {
+ if (_readPos > 1024 && _readPos > _buffer.length * 3 / 4) {
+ // move buffer data
+ for (int i = 0; _readPos + i < _writePos; i++)
+ _buffer[i] = _buffer[_readPos + i];
+ _writePos -= _readPos;
+ _readPos = 0;
+ }
+ }
+
+ private void append(ref T item) {
+ if (_writePos >= _buffer.length) {
+ move();
+ _buffer.length = _buffer.length == 0 ? 64 : _buffer.length * 2;
+ }
+ _buffer[_writePos++] = item;
}
void put(T item) {
+ if (_closed)
+ return;
+ synchronized(_mutex) {
+ if (_closed)
+ return;
+ append(item);
+ _condition.notifyAll();
+ }
}
+ void put(T[] items) {
+ if (_closed)
+ return;
+ synchronized(_mutex) {
+ if (_closed)
+ return;
+ foreach(ref item; items) {
+ append(item);
+ }
+ _condition.notifyAll();
+ }
+ }
+
bool get(ref T value, int timeoutMillis) {
+ if (_closed)
+ return false;
+ synchronized(_mutex) {
+ if (_closed)
+ return false;
+ if (_readPos < _writePos) {
+ value = _buffer[_readPos++];
+ return true;
+ }
+ if (timeoutMillis <= 0)
+ _condition.wait(); // no timeout
+ else if (!_condition.wait(dur!"msecs"(timeoutMillis)))
+ return false; // timeout
+ if (_readPos < _writePos) {
+ value = _buffer[_readPos++];
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool getAll(ref T[] values, int timeoutMillis) {
+ if (_closed)
+ return false;
+ synchronized(_mutex) {
+ if (_closed)
+ return false;
+ values.length = 0;
+ while (_readPos < _writePos)
+ values ~= _buffer[_readPos++];
+ if (values.length > 0)
+ return true;
+ if (timeoutMillis <= 0)
+ _condition.wait(); // no timeout
+ else if (!_condition.wait(dur!"msecs"(timeoutMillis)))
+ return false; // timeout
+ while (_readPos < _writePos)
+ values ~= _buffer[_readPos++];
+ if (values.length > 0)
+ return true;
+ }
return false;
}
}
diff --git a/src/ddebug/gdb/gdbinterface.d b/src/ddebug/gdb/gdbinterface.d
new file mode 100644
index 0000000..b307861
--- /dev/null
+++ b/src/ddebug/gdb/gdbinterface.d
@@ -0,0 +1,205 @@
+module ddebug.gdb.gdbinterface;
+
+public import ddebug.common.debugger;
+import dlangui.core.logger;
+import ddebug.common.queue;
+import dlangide.builders.extprocess;
+import std.utf;
+import std.conv : to;
+
+class ConsoleDebuggerInterface : DebuggerBase, TextWriter {
+ protected ExternalProcess _debuggerProcess;
+
+ protected ExternalProcessState runDebuggerProcess(string executable, string[]args, string dir) {
+ _debuggerProcess = new ExternalProcess();
+ ExternalProcessState state = _debuggerProcess.run(executable, args, dir, this);
+ return state;
+ }
+
+ private string[] _stdoutLines;
+ private char[] _stdoutBuf;
+ /// return true to clear lines list
+ protected bool onDebuggerStdoutLines(string[] lines) {
+ return true;
+ }
+ private void onStdoutText(string text) {
+ _stdoutBuf ~= text;
+ // pass full lines
+ int startPos = 0;
+ bool fullLinesFound = false;
+ for (int i = 0; i < _stdoutBuf.length; i++) {
+ if (_stdoutBuf[i] == '\n' || _stdoutBuf[i] == '\r') {
+ if (i <= startPos)
+ _stdoutLines ~= "";
+ else
+ _stdoutLines ~= _stdoutBuf[startPos .. i].dup;
+ fullLinesFound = true;
+ if (i + 1 < _stdoutBuf.length) {
+ if ((_stdoutBuf[i] == '\n' && _stdoutBuf[i + 1] == '\r')
+ || (_stdoutBuf[i] == '\r' && _stdoutBuf[i + 1] == '\n'))
+ i++;
+ }
+ startPos = i + 1;
+ }
+ }
+ if (fullLinesFound) {
+ for (int i = 0; i + startPos < _stdoutBuf.length; i++)
+ _stdoutBuf[i] = _stdoutBuf[i + startPos];
+ _stdoutBuf.length = _stdoutBuf.length - startPos;
+ if (onDebuggerStdoutLines(_stdoutLines)) {
+ _stdoutLines.length = 0;
+ }
+ }
+ }
+
+ bool sendLine(string text) {
+ return _debuggerProcess.write(text ~ "\n");
+ }
+
+ /// log lines
+ override void writeText(dstring text) {
+ string text8 = toUTF8(text);
+ postRequest(delegate() {
+ onStdoutText(text8);
+ });
+ }
+
+}
+
+import std.process;
+class GDBInterface : ConsoleDebuggerInterface {
+
+ protected int commandId;
+
+
+ int sendCommand(string text) {
+ commandId++;
+ sendLine(to!string(commandId) ~ text);
+ return commandId;
+ }
+
+ Pid terminalPid;
+ string terminalTty;
+
+ string startTerminal(string termExecutable) {
+ Log.d("Starting terminal");
+ import std.random;
+ import std.file;
+ import std.path;
+ import std.string;
+ import core.thread;
+ uint n = uniform(0, 0x10000000, rndGen());
+ terminalTty = null;
+ string termfile = buildPath(tempDir, format("dlangide-term-name-%07x.tmp", n));
+ Log.d("temp file for tty name: ", termfile);
+ try {
+ terminalPid = spawnProcess([
+ termExecutable,
+ "-title",
+ "DLangIDE External Console",
+ "-e",
+ "echo 'DlangIDE External Console' && tty > " ~ termfile ~ " && sleep 1000000"
+ ]);
+ for (int i = 0; i < 20; i++) {
+ Thread.sleep(dur!"msecs"(100));
+ if (!isTerminalActive) {
+ Log.e("Failed to get terminal TTY");
+ return null;
+ }
+ if (!exists(termfile)) {
+ Thread.sleep(dur!"msecs"(20));
+ break;
+ }
+ }
+ // read TTY from file
+ if (exists(termfile)) {
+ terminalTty = readText(termfile);
+ if (terminalTty.endsWith("\n"))
+ terminalTty = terminalTty[0 .. $-1];
+ // delete file
+ remove(termfile);
+ }
+ } catch (Exception e) {
+ Log.e("Failed to start terminal ", e);
+ killTerminal();
+ }
+ if (terminalTty.length == 0) {
+ Log.i("Cannot start terminal");
+ killTerminal();
+ } else {
+ Log.i("Terminal: ", terminalTty);
+ }
+ return terminalTty;
+ }
+
+ bool isTerminalActive() {
+ if (terminalPid is null)
+ return false;
+ auto res = tryWait(terminalPid);
+ if (res.terminated) {
+ Log.d("isTerminalActive: Terminal is stopped");
+ wait(terminalPid);
+ terminalPid = Pid.init;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ void killTerminal() {
+ if (terminalPid is null)
+ return;
+ try {
+ Log.d("Trying to kill terminal");
+ kill(terminalPid, 9);
+ Log.d("Waiting for terminal process termination");
+ wait(terminalPid);
+ terminalPid = Pid.init;
+ Log.d("Killed");
+ } catch (Exception e) {
+ Log.d("Exception while killing terminal", e);
+ terminalPid = Pid.init;
+ }
+ }
+
+ string terminalExecutableFileName = "xterm";
+ override void startDebugging(string debuggerExecutable, string executable, string[] args, string workingDir, DebuggerResponse response) {
+ string[] debuggerArgs;
+ terminalTty = startTerminal(terminalExecutableFileName);
+ if (terminalTty.length == 0) {
+ response(ResponseCode.CannotRunDebugger, "Cannot start terminal");
+ return;
+ }
+ debuggerArgs ~= "-tty";
+ debuggerArgs ~= terminalTty;
+ debuggerArgs ~= "--interpreter=mi";
+ debuggerArgs ~= "--silent";
+ debuggerArgs ~= "--args";
+ debuggerArgs ~= executable;
+ foreach(arg; args)
+ debuggerArgs ~= arg;
+ ExternalProcessState state = runDebuggerProcess(debuggerExecutable, debuggerArgs, workingDir);
+ Log.i("Debugger process state:");
+ if (state == ExternalProcessState.Running) {
+ response(ResponseCode.Ok, "Started");
+ //sendCommand("-break-insert main");
+ sendCommand("-exec-run");
+ } else {
+ response(ResponseCode.CannotRunDebugger, "Error while trying to run debugger process");
+ return;
+ }
+ }
+
+ override void stop() {
+ if (_debuggerProcess !is null)
+ _debuggerProcess.kill();
+ killTerminal();
+ super.stop();
+ }
+
+ /// return true to clear lines list
+ override protected bool onDebuggerStdoutLines(string[] lines) {
+ Log.d("onDebuggerStdout ", lines);
+ return true;
+ }
+}
diff --git a/src/dlangide.d b/src/dlangide.d
index 799ba3b..6d8421b 100644
--- a/src/dlangide.d
+++ b/src/dlangide.d
@@ -59,14 +59,17 @@ extern (C) int UIAppMain(string[] args) {
version(USE_WIN_DEBUG) {
debuggerTest();
}
+ version(USE_GDB_DEBUG) {
+ debuggerTestGDB();
+ }
// create window
Window window = Platform.instance.createWindow("Dlang IDE", null, WindowFlag.Resizable, 800, 600);
// set window icon
window.windowIcon = drawableCache.getImage("dlangui-logo1");
- Widget w = new Widget();
- pragma(msg, w.click.return_t, "", w.click.params_t);
+ //Widget w = new Widget();
+ //pragma(msg, w.click.return_t, "", w.click.params_t);
IDEFrame frame = new IDEFrame(window);
@@ -93,6 +96,40 @@ version(USE_WIN_DEBUG) {
}
}
+version(USE_GDB_DEBUG) {
+ void debuggerTestGDB() {
+ import ddebug.gdb.gdbinterface;
+ import core.thread;
+ Log.d("Testing GDB debugger");
+ DebuggerBase debugger = new DebuggerBase();
+ debugger.startDebugging("gdb", "test", [], "", delegate(ResponseCode code, string msg) {
+ Log.d("startDebugging result: ", code, " : ", msg);
+ //assert(code == ResponseCode.NotImplemented);
+ });
+ debugger.stop();
+ destroy(debugger);
+
+ // async
+
+ debugger = new GDBInterface();
+ DebuggerProxy proxy = new DebuggerProxy(debugger, delegate(Runnable runnable) {
+ runnable();
+ });
+ Log.d("calling debugger.start()");
+ debugger.start();
+ Log.d("calling proxy.startDebugging()");
+ proxy.startDebugging("gdb", "/home/lve/src/d/dlangide/test/gdbtest", ["param1", "param2"], "/home/lve/src/d/dlangide/test", delegate(ResponseCode code, string msg) {
+ Log.d("startDebugging result: ", code, " : ", msg);
+ //assert(code == ResponseCode.NotImplemented);
+ });
+ Thread.sleep(dur!"msecs"(200000));
+ debugger.stop();
+ Thread.sleep(dur!"msecs"(200000));
+ destroy(debugger);
+ Log.d("Testing of GDB debugger is finished");
+ }
+}
+
unittest {
void jsonTest() {
import dlangui.core.settings;
diff --git a/src/dlangide/builders/extprocess.d b/src/dlangide/builders/extprocess.d
index 9d782e9..3d4fd46 100644
--- a/src/dlangide/builders/extprocess.d
+++ b/src/dlangide/builders/extprocess.d
@@ -400,14 +400,16 @@ class ExternalProcess {
return _state;
}
- void write(string data) {
+ bool write(string data) {
if(_state == ExternalProcessState.Error || _state == ExternalProcessState.None || _state == ExternalProcessState.Stopped) {
- return;
+ return false;
}
else {
Log.d("writing ", data.length, " characters to stdin");
_pipes.stdin.write("", data);
- _pipes.stdin.close();
+ _pipes.stdin.flush();
+ //_pipes.stdin.close();
+ return true;
}
}
}
diff --git a/src/dlangide/ui/newproject.d b/src/dlangide/ui/newproject.d
index 0f02d49..deeabb6 100644
--- a/src/dlangide/ui/newproject.d
+++ b/src/dlangide/ui/newproject.d
@@ -4,6 +4,10 @@ import dlangui.core.types;
import dlangui.core.i18n;
import dlangui.platforms.common.platform;
import dlangui.dialogs.dialog;
+import dlangui.widgets.widget;
+import dlangui.widgets.layouts;
+import dlangui.widgets.editors;
+import dlangui.dml.parser;
class NewProjectDlg : Dialog {
@@ -18,6 +22,54 @@ class NewProjectDlg : Dialog {
/// override to implement creation of dialog controls
override void init() {
super.init();
+ Widget content = parseML(q{
+ VerticalLayout {
+ id: vlayout
+ HorizontalLayout {
+ VerticalLayout {
+ layoutWidth: FILL_PARENT; layoutHeight: FILL_PARENT; layoutWidth: 1
+ TextWidget {
+ text: "Project type"
+ }
+ }
+ VerticalLayout {
+ layoutWidth: FILL_PARENT; layoutWeight: FILL_PARENT; layoutWidth: 1
+ TextWidget {
+ text: "Template"
+ }
+ }
+ VerticalLayout {
+ layoutWidth: FILL_PARENT; layoutWeight: FILL_PARENT; layoutWidth: 1
+ TextWidget {
+ text: "Description"
+ }
+ }
+ }
+ margins: Rect { left: 5; right: 3; top: 2; bottom: 4 }
+ padding: Rect { 5, 4, 3, 2 } // same as Rect { left: 5; top: 4; right: 3; bottom: 2 }
+ TextWidget {
+ /* this widget can be accessed via id myLabel1
+ e.g. w.childById!TextWidget("myLabel1")
+ */
+ id: myLabel1
+ text: "Some text"; padding: 5
+ enabled: false
+ }
+ TextWidget {
+ id: myLabel2
+ text: "More text"; margins: 5
+ enabled: true
+ }
+ CheckBox{ id: cb1; text: "Some checkbox" }
+ HorizontalLayout {
+ RadioButton { id: rb1; text: "Radio Button 1" }
+ RadioButton { id: rb1; text: "Radio Button 2" }
+ }
+ }
+ });
+ // you can access loaded items by id - e.g. to assign signal listeners
+ auto edit1 = window.mainWidget.childById!EditLine("edit1");
+
}
}
diff --git a/src/dlangide/ui/searchPanel.d b/src/dlangide/ui/searchPanel.d
index b778b9e..c3bf391 100644
--- a/src/dlangide/ui/searchPanel.d
+++ b/src/dlangide/ui/searchPanel.d
@@ -197,7 +197,7 @@ class SearchWidget : TabWidget {
_layout.addChild(_findText);
auto goButton = new ImageButton("findTextButton", "edit-find");
- goButton.onClickListener = &onFindButtonPressed;
+ goButton.click = &onFindButtonPressed;
_layout.addChild(goButton);
_searchScope = new ComboBox("searchScope", ["File"d, "Project"d, "Dependencies"d, "Everywhere"d]);
diff --git a/src/dlangide/ui/settings.d b/src/dlangide/ui/settings.d
index 61da088..aea0be8 100644
--- a/src/dlangide/ui/settings.d
+++ b/src/dlangide/ui/settings.d
@@ -8,7 +8,7 @@ import dlangui.dialogs.settingsdialog;
const AVAILABLE_THEMES = ["ide_theme_default", "ide_theme_dark"];
-const AVAILABLE_LANGUAGES = ["en", "ru"];
+const AVAILABLE_LANGUAGES = ["en", "ru", "es"];
class IDESettings : SettingsFile {
@@ -142,11 +142,17 @@ SettingsPage createSettingsPages() {
texted.addCheckbox("editors/textEditor/useSpacesForTabs", UIString("Use spaces for tabs"d));
texted.addCheckbox("editors/textEditor/smartIndents", UIString("Smart indents"d));
texted.addCheckbox("editors/textEditor/smartIndentsAfterPaste", UIString("Smart indent after paste"d));
- SettingsPage ui = res.addChild("interface", UIString("Interface"d));
+ SettingsPage dlang = res.addChild("dlang", UIString("D"d));
+ SettingsPage ddebug = dlang.addChild("dlang/debugger", UIString("Debugger"d));
+ ddebug.addStringEdit("dlang/debugger/executable", UIString("Debugger executable"d), "gdb");
+ SettingsPage ui = res.addChild("interface", UIString("Interface"d));
ui.addStringComboBox("interface/theme", UIString("Theme"d), [
StringListValue("ide_theme_default", "Default"d),
StringListValue("ide_theme_dark", "Dark"d)]);
- ui.addStringComboBox("interface/language", UIString("Language"d), [StringListValue("en", "English"d), StringListValue("ru", "Russian"d)]);
+ ui.addStringComboBox("interface/language", UIString("Language"d), [
+ StringListValue("en", "English"d),
+ StringListValue("ru", "Russian"d),
+ StringListValue("es", "Spanish"d)]);
ui.addIntComboBox("interface/hintingMode", UIString("Font hinting mode"d), [StringListValue(0, "Normal"d), StringListValue(1, "Force Auto Hint"d),
StringListValue(2, "Disabled"d), StringListValue(3, "Light"d)]);
ui.addIntComboBox("interface/minAntialiasedFontSize", UIString("Minimum font size for antialiasing"d),
diff --git a/tools/disowntty/disowntty.d b/tools/disowntty/disowntty.d
new file mode 100644
index 0000000..dace944
--- /dev/null
+++ b/tools/disowntty/disowntty.d
@@ -0,0 +1,65 @@
+/* tty;exec disowntty */
+//#include
+//#include
+//#include
+//#include
+//#include
+//#include
+
+//import std.stdio;
+import std.string;
+import std.c.stdlib;
+import core.thread;
+import core.sys.posix.signal;
+import core.sys.posix.sys.ioctl;
+import core.sys.posix.unistd;
+import core.sys.posix.stdio;
+
+//extern(C) void signal(int sig, void function(int) );
+//extern(C) void setbuf(FILE * stream, char * buf);
+//extern(C) void ioctl(int fd, uint request, int param);
+
+void end(string msg)
+{
+ perror(msg.toStringz);
+ for (;;)
+ Thread.sleep(dur!"seconds"(1));
+}
+
+alias extern(C) void function(int) sigfn_t;
+
+void main(string args[])
+{
+ FILE *tty_name_file;
+ if (args.length < 2)
+ exit(1);
+
+ string tty_filename = args[1];
+
+ sigfn_t orig;
+ setbuf (stdout, null);
+ orig = signal (SIGHUP, SIG_IGN);
+ if (orig !is SIG_DFL)
+ end ("signal (SIGHUP)");
+
+ printf("%s %s\n", tty_filename.toStringz, ttyname(STDIN_FILENO));
+ tty_name_file = fopen(tty_filename.toStringz, "w");
+ fprintf(tty_name_file, "%s\n", ttyname(STDIN_FILENO));
+ fclose(tty_name_file);
+
+ /* Verify we are the sole owner of the tty. */
+ if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) != 0)
+ end ("TIOCSCTTY");
+
+ printf("%s %s\n", tty_filename.toStringz, ttyname(STDIN_FILENO));
+ tty_name_file = fopen(tty_filename.toStringz, "w");
+ fprintf(tty_name_file, "%s\n", ttyname(STDIN_FILENO));
+ fclose(tty_name_file);
+
+ /* Disown the tty. */
+ if (ioctl (STDIN_FILENO, TIOCNOTTY) != 0)
+ end ("TIOCNOTTY");
+ end ("OK, disowned");
+
+ exit(1);
+}
diff --git a/tools/disowntty/disowntty.dproj b/tools/disowntty/disowntty.dproj
new file mode 100644
index 0000000..1b1c4b9
--- /dev/null
+++ b/tools/disowntty/disowntty.dproj
@@ -0,0 +1,48 @@
+
+
+
+ Debug
+ AnyCPU
+ {FB7A3FF6-0E67-47D1-BA70-F258F9A0E332}
+ .
+ DMD2
+ true
+ true
+ true
+
+
+ true
+ bin\Debug
+ obj/Debug
+ false
+ false
+ disowntty
+ Executable
+ true
+ 0
+
+
+ bin\Release
+ obj/Release
+ false
+ false
+ disowntty
+ Executable
+ true
+ 0
+
+
+ true
+ bin\Unittest
+ obj/Unittest
+ false
+ true
+ disowntty
+ Executable
+ true
+ 0
+
+
+
+
+
\ No newline at end of file
diff --git a/views/res/i18n/es.ini b/views/res/i18n/es.ini
new file mode 100644
index 0000000..36024f7
--- /dev/null
+++ b/views/res/i18n/es.ini
@@ -0,0 +1,77 @@
+EXIT=Salir
+MENU_FILE=&ARCHIVO
+MENU_FILE_NEW=&Nuevo
+MENU_FILE_NEW_SOURCE_FILE=Nuevo Archivo Fuente
+MENU_FILE_NEW_PROJECT=Nuevo Proyecto
+MENU_FILE_NEW_WORKSPACE=Nuevo Espacio de Trabajo
+MENU_FILE_OPEN=&Abrir archivo...
+MENU_FILE_OPEN_WORKSPACE=Abrir Proyecto o Espacio de Trabajo...
+MENU_FILE_OPEN_RECENT=Abrir reciente
+MENU_FILE_SAVE=&Guardar archivo
+MENU_FILE_SAVE_AS=&Guardar archivo como...
+MENU_FILE_SAVE_ALL=&Guardar todo
+MENU_FILE_EXIT=S&alir
+MENU_EDIT=&EDITAR
+MENU_EDIT_COPY=&Copiar
+MENU_EDIT_PASTE=&Pegar
+MENU_EDIT_CUT=Corta&r
+MENU_EDIT_UNDO=&Deshacer
+MENU_EDIT_REDO=&Rehacer
+MENU_EDIT_INDENT=Sangrar bloque
+MENU_EDIT_UNINDENT=De-sangrar bloque
+MENU_EDIT_TOGGLE_LINE_COMMENT=Alternar comentario de línea
+MENU_EDIT_TOGGLE_BLOCK_COMMENT=Alternar comentario de bloque
+MENU_EDIT_ADVANCED=Avanzado...
+MENU_EDIT_PREFERENCES=&Preferencias
+MENU_BUILD_CONFIGURATIONS=Configuraciones de construcción
+MENU_BUILD=&Contruir
+MENU_BUILD_WORKSPACE_BUILD=Construir espacio de trabajo
+MENU_BUILD_WORKSPACE_REBUILD=Reconstruir espacio de trabajo
+MENU_BUILD_WORKSPACE_CLEAN=Limpiar espacio de trabajo
+MENU_BUILD_PROJECT_BUILD=Construir Proyecto
+MENU_BUILD_PROJECT_REBUILD=Reconstruir Proyecto
+MENU_BUILD_PROJECT_CLEAN=Limpiar Proyecto
+MENU_PROJECT=&PROYECTO
+MENU_PROJECT_CONFIGURATIONS=Configuraciones de Proyecto
+MENU_PROJECT_SET_AS_STARTUP=Establecer como Proyecto de Inicio
+MENU_PROJECT_SETTINGS=Ajustes de Proyecto
+MENU_PROJECT_REFRESH=Refrescar elementos del espacio de trabajo
+MENU_PROJECT_UPDATE_DEPENDENCIES=Actualizar Dependencias
+MENU_DEBUG=&DEPURAR
+MENU_DEBUG_START_DEBUGGING=Iniciar Depuración
+MENU_DEBUG_START_NO_DEBUGGING=Iniciar sin Depuración
+MENU_DEBUG_CONTINUE=Continuar Depurando
+MENU_DEBUG_STOP=Detener
+MENU_DEBUG_PAUSE=Pausar
+MENU_VIEW=&VISTA
+MENU_VIEW_LANGUAGE=Lenguaje de &Interfaz
+MENU_VIEW_LANGUAGE_ES=Spanish
+MENU_VIEW_LANGUAGE_EN=English
+MENU_VIEW_LANGUAGE_RU=Русский
+MENU_VIEW_THEME=&Tema
+MENU_VIEW_THEME_DEFAULT=&Por Defecto
+MENU_VIEW_THEME_CUSTOM1=&Personalizado 1
+MENU_WINDOW=&VENTANA
+MENU_WINDOW_PREFERENCES=&Preferencias
+MENU_WINDOW_CLOSE_ALL_DOCUMENTS=Cerrar todos los Documentos
+MENU_HELP=&AYUDA
+MENU_HELP_VIEW_HELP=&Ver ayuda
+MENU_HELP_ABOUT=&Acerca de
+MENU_NAVIGATE=Navegar
+
+GO_TO_DEFINITION=Ir a Definición
+SHOW_COMPLETIONS=Obtener Autocompletados
+FIND_TEXT=Encontrar texto
+
+
+TAB_LONG_LIST=Larga lista
+TAB_BUTTONS=Botones
+TAB_ANIMATION=Animación
+TAB_TABLE_LAYOUT=Diseño de tabla
+TAB_EDITORS=Editores
+
+
+MENU_PROJECT_FOLDER_ADD_ITEM=Añadir...
+MENU_PROJECT_FOLDER_OPEN_ITEM=Abrir
+MENU_PROJECT_FOLDER_REMOVE_ITEM=Remover
+MENU_PROJECT_FOLDER_RENAME_ITEM=Renombrar...
diff --git a/views/resources.list b/views/resources.list
index a7f97d0..50de379 100644
--- a/views/resources.list
+++ b/views/resources.list
@@ -2,6 +2,7 @@ res/ide_theme_default.xml
res/ide_theme_dark.xml
res/i18n/en.ini
res/i18n/ru.ini
+res/i18n/es.ini
res/hdpi/hdpi_configure.png
res/hdpi/hdpi_debug-run.png
res/hdpi/hdpi_document-close.png