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