From ef54197a64f49d1fea7d1cd79186ee17d259d5c2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Krzysztof=20Kosi=C5=84ski?= Date: Mon, 29 Mar 2010 22:32:32 +0200 Subject: [PATCH] Add console helper application based on inkscapec.exe from Jos Hirth, which provides Unix-like behavior on Windows. --- .bzrignore | 9 ++ build.xml | 24 +++- share/extensions/webslicer_create_group.py | 0 src/winconsole.cpp | 153 +++++++++++++++++++++ 4 files changed, 183 insertions(+), 3 deletions(-) mode change 100755 => 100644 share/extensions/webslicer_create_group.py create mode 100644 src/winconsole.cpp diff --git a/.bzrignore b/.bzrignore index 3c299bb33..4d9683c27 100644 --- a/.bzrignore +++ b/.bzrignore @@ -136,3 +136,12 @@ src/ui/widget/makefile src/util/makefile src/widgets/makefile src/xml/makefile +src/test-main.cpp +src/test-src.cpp +src/display/test-display.cpp +src/helper/test-helper.cpp +src/libnr/test-nr.cpp +src/svg/test-svg.cpp +src/util/test-util.cpp +src/xml/test-xml.cpp +gc.log diff --git a/build.xml b/build.xml index 69a800082..0c895fb56 100755 --- a/build.xml +++ b/build.xml @@ -512,9 +512,26 @@ + + - - + + + -mconsole + -mthreads + + + + + + + - + diff --git a/share/extensions/webslicer_create_group.py b/share/extensions/webslicer_create_group.py old mode 100755 new mode 100644 diff --git a/src/winconsole.cpp b/src/winconsole.cpp new file mode 100644 index 000000000..f6ee49e13 --- /dev/null +++ b/src/winconsole.cpp @@ -0,0 +1,153 @@ +/** \file + * @brief Command-line wrapper for Windows. + * + * Windows has two types of executables: GUI and console. + * The GUI executables detach immediately when run from the command + * prompt (cmd.exe), and whatever you write to standard output + * disappears into a black hole. Console executables handle + * do display standard output and take standard input from the console, + * but when you run them from the GUI, an extra console window appears. + * It's possible to hide it, but it still flashes from a fraction + * of a second. + * + * To provide an Unix-like experienve, where the application will behave + * correctly in command line mode and at the same time won't create + * the ugly console window when run from the GUI, we have to have two + * executables. The first one, inkscape.exe, is the GUI application. + * Its entry points are in main.cpp and winmain.cpp. The second one, + * called inkscape.com, is a small helper application contained in + * this file. It spawns the GUI application and redirects its output + * to the console. + * + * Note that inkscape.com has nothing to do with "compact executables" + * from DOS. It's a normal PE executable renamed to .com. The trick + * is that cmd.exe picks .com over .exe when both are present in PATH, + * so when you type "inkscape" into the command prompt, inkscape.com + * gets run. The Windows program loader does not inspect the extension, + * just like an Unix program loader; it determines the binary format + * based on the contents of the file. + * + *//* + * Authors: + * Jos Hirth + * Krzysztof KosiƱski + * + * Copyright (C) 2008-2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef WIN32 +#undef DATADIR +#include + +struct echo_thread_info { + HANDLE echo_read; + HANDLE echo_write; + unsigned buffer_size; +}; + +// thread function for echoing from one file handle to another +DWORD WINAPI echo_thread(void *info_void) +{ + echo_thread_info *info = static_cast(info_void); + char *buffer = reinterpret_cast(LocalAlloc(LMEM_FIXED, info->buffer_size)); + DWORD bytes_read, bytes_written; + + while(true){ + if (!ReadFile(info->echo_read, buffer, info->buffer_size, &bytes_read, NULL) || bytes_read == 0) + if (GetLastError() == ERROR_BROKEN_PIPE) + break; + + if (!WriteFile(info->echo_write, buffer, bytes_read, &bytes_written, NULL)) { + if (GetLastError() == ERROR_NO_DATA) + break; + } + } + + LocalFree(reinterpret_cast(buffer)); + CloseHandle(info->echo_read); + CloseHandle(info->echo_write); + + return 1; +} + +int main() +{ + // structs that will store information for our I/O threads + echo_thread_info stdin = {NULL, NULL, 4096}; + echo_thread_info stdout = {NULL, NULL, 4096}; + echo_thread_info stderr = {NULL, NULL, 4096}; + // handles we'll pass to inkscape.exe + HANDLE inkscape_stdin, inkscape_stdout, inkscape_stderr; + HANDLE stdin_thread, stdout_thread, stderr_thread; + + SECURITY_ATTRIBUTES sa; + sa.nLength=sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor=NULL; + sa.bInheritHandle=TRUE; + + // Determine the path to the Inkscape executable. + // Do this by looking up the name of this one and redacting the extension to ".exe" + const int pathbuf = 2048; + WCHAR *inkscape = reinterpret_cast(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR))); + GetModuleFileNameW(NULL, inkscape, pathbuf); + WCHAR *dot_index = wcsrchr(inkscape, L'.'); + wcsncpy(dot_index, L".exe", 4); + + // we simply reuse our own command line for inkscape.exe + // it guarantees perfect behavior w.r.t. quoting + WCHAR *cmd = GetCommandLineW(); + + // set up the pipes and handles + stdin.echo_read = GetStdHandle(STD_INPUT_HANDLE); + stdout.echo_write = GetStdHandle(STD_OUTPUT_HANDLE); + stderr.echo_write = GetStdHandle(STD_ERROR_HANDLE); + CreatePipe(&inkscape_stdin, &stdin.echo_write, &sa, 0); + CreatePipe(&stdout.echo_read, &inkscape_stdout, &sa, 0); + CreatePipe(&stderr.echo_read, &inkscape_stderr, &sa, 0); + + // fill in standard IO handles to be used by the process + PROCESS_INFORMATION pi; + STARTUPINFOW si; + + ZeroMemory(&si,sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = inkscape_stdin; + si.hStdOutput = inkscape_stdout; + si.hStdError = inkscape_stderr; + + // spawn inkscape.exe + CreateProcessW(inkscape, // path to inkscape.exe + cmd, // command line as a single string + NULL, // process security attributes - unused + NULL, // thread security attributes - unused + TRUE, // inherit handles + 0, // flags + NULL, // environment - NULL = inherit from us + NULL, // working directory - NULL = inherit ours + &si, // startup info - see above + &pi); // information about the created process - unused + + // clean up a bit + LocalFree(reinterpret_cast(inkscape)); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + CloseHandle(inkscape_stdin); + CloseHandle(inkscape_stdout); + CloseHandle(inkscape_stderr); + + // create IO echo threads + DWORD unused; + stdin_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdin, 0, &unused); + stdout_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdout, 0, &unused); + stderr_thread = CreateThread(NULL, 0, echo_thread, (void*) &stderr, 0, &unused); + + // wait until the standard output thread terminates + WaitForSingleObject(stdout_thread, INFINITE); + + return 0; +} + +#endif -- 2.30.2