merged modification from my private branch after successful regression testing on win32,cygwin/mingw, mac osx, linux.
Fl_win32.cxx and Fl_x.cxx modified for: ====================================== - STR# 2029 fix: async select using USE_ASYNC_SELECT def. would not work. Obsoleted and commented as discussed - STR# 2032 fix: fl_ready would load wsock dll unnecessarily. Bad indents corrected fluid.cxx modifed for: ====================== Added a local portable Fl_Process class to fluid enabling popen encapsulated win32 impl. based on CreateProcess() to work as expected in non console apps, so that exec command output window is now also implemented in win32, also permitting testing add_fd()/remove_fd() API relying on winsock on win32. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6188 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
parent
295c7720b9
commit
c7f4b8c930
225
fluid/fluid.cxx
225
fluid/fluid.cxx
@ -57,6 +57,7 @@
|
||||
# include <direct.h>
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
# include <FCNTL.H>
|
||||
# include <commdlg.h>
|
||||
# include <FL/x.H>
|
||||
# ifndef __WATCOMC__
|
||||
@ -1847,24 +1848,164 @@ void update_history(const char *flname) {
|
||||
}
|
||||
}
|
||||
|
||||
// Shell command support...
|
||||
#if (!defined(WIN32) || defined(__CYGWIN__)) && !defined(__MWERKS__)
|
||||
// Support the full piped shell command...
|
||||
static FILE *shell_pipe = 0;
|
||||
// ********** portable process class definition **********
|
||||
|
||||
class Fl_Process {
|
||||
public:
|
||||
// construction / destruction
|
||||
Fl_Process() {_fpt= NULL;}
|
||||
~Fl_Process() {if (_fpt) close();}
|
||||
|
||||
FILE * popen (const char *cmd, const char *mode="r");
|
||||
//not necessary here: FILE * fopen (const char *file, const char *mode="r");
|
||||
int close();
|
||||
|
||||
FILE * desc() const { return _fpt;} // non null if file is open
|
||||
char * get_line(char * line, size_t s) const {return _fpt ? fgets(line, s, _fpt) : NULL;}
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
protected:
|
||||
HANDLE pin[2], pout[2], perr[2];
|
||||
char ptmode;
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
|
||||
static bool createPipe(HANDLE * h, BOOL bInheritHnd=TRUE);
|
||||
|
||||
private:
|
||||
FILE * freeHandles() {
|
||||
clean_close(pin[0]); clean_close(pin[1]);
|
||||
clean_close(pout[0]); clean_close(pout[1]);
|
||||
clean_close(perr[0]); clean_close(perr[1]);
|
||||
return NULL; // convenient for error management
|
||||
}
|
||||
static void clean_close(HANDLE& h);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
FILE * _fpt;
|
||||
};
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
bool Fl_Process::createPipe(HANDLE * h, BOOL bInheritHnd) {
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = bInheritHnd;
|
||||
return CreatePipe (&h[0],&h[1],&sa,0) ? true : false;
|
||||
}
|
||||
#endif
|
||||
// portable open process:
|
||||
FILE * Fl_Process::popen(const char *cmd, const char *mode) {
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
// PRECONDITIONS
|
||||
if (!mode || !*mode || (*mode!='r' && *mode!='w') ) return NULL;
|
||||
if (_fpt) close(); // close first before reuse
|
||||
|
||||
ptmode = *mode;
|
||||
pin[0] = pin[1] = pout[0] = pout[1] = perr[0] = perr[1] = INVALID_HANDLE_VALUE;
|
||||
// stderr to stdout wanted ?
|
||||
int fusion = (strstr(cmd,"2>&1") !=NULL);
|
||||
|
||||
// Create windows pipes
|
||||
if (!createPipe(pin) || !createPipe(pout) || (!fusion && !createPipe(perr) ) )
|
||||
return freeHandles(); // error
|
||||
|
||||
// Initialize Startup Info
|
||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = pin[0];
|
||||
si.hStdOutput = pout[1];
|
||||
si.hStdError = fusion ? pout[1] : perr [1];
|
||||
|
||||
if ( CreateProcess(NULL, (LPTSTR) cmd,NULL,NULL,TRUE,
|
||||
DETACHED_PROCESS,NULL,NULL, &si, &pi)) {
|
||||
// don't need theses handles inherited by child process:
|
||||
clean_close(pin[0]); clean_close(pout[1]); clean_close(perr[1]);
|
||||
HANDLE & h = *mode == 'r' ? pout[0] : pin[1];
|
||||
_fpt = _fdopen(_open_osfhandle((long) h,_O_BINARY),mode);
|
||||
h= INVALID_HANDLE_VALUE; // reset the handle pointer that is shared
|
||||
// with _fpt so we don't free it twice
|
||||
}
|
||||
|
||||
if (!_fpt) freeHandles();
|
||||
return _fpt;
|
||||
#else
|
||||
_fpt=::popen(cmd,mode);
|
||||
return _fpt;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Fl_Process::close() {
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
if (_fpt) {
|
||||
fclose(_fpt);
|
||||
clean_close(perr[0]);
|
||||
clean_close(pin[1]);
|
||||
clean_close(pout[0]);
|
||||
_fpt = NULL;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
#else
|
||||
int ret = ::pclose(_fpt);
|
||||
_fpt=NULL;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
void Fl_Process::clean_close(HANDLE& h) {
|
||||
if (h!= INVALID_HANDLE_VALUE) CloseHandle(h);
|
||||
h = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#endif
|
||||
// ********** Fl_Process class end **********
|
||||
|
||||
static Fl_Process s_proc;
|
||||
|
||||
// Shell command support...
|
||||
|
||||
static bool prepare_shell_command(const char * &command) { // common pre-shell command code all platforms
|
||||
shell_window->hide();
|
||||
if (s_proc.desc()) {
|
||||
fl_alert("Previous shell command still running!");
|
||||
return false;
|
||||
}
|
||||
if ((command = shell_command_input->value()) == NULL || !*command) {
|
||||
fl_alert("No shell command entered!");
|
||||
return false;
|
||||
}
|
||||
if (shell_savefl_button->value()) {
|
||||
save_cb(0, 0);
|
||||
}
|
||||
if (shell_writecode_button->value()) {
|
||||
compile_only = 1;
|
||||
write_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
if (shell_writemsgs_button->value()) {
|
||||
compile_only = 1;
|
||||
write_strings_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(__MWERKS__)
|
||||
// Support the full piped shell command...
|
||||
void
|
||||
shell_pipe_cb(int, void*) {
|
||||
char line[1024]; // Line from command output...
|
||||
char line[1024]=""; // Line from command output...
|
||||
|
||||
if (fgets(line, sizeof(line), shell_pipe) != NULL) {
|
||||
if (s_proc.get_line(line, sizeof(line)) != NULL) {
|
||||
// Add the line to the output list...
|
||||
shell_run_buffer->append(line);
|
||||
} else {
|
||||
// End of file; tell the parent...
|
||||
Fl::remove_fd(fileno(shell_pipe));
|
||||
|
||||
pclose(shell_pipe);
|
||||
shell_pipe = NULL;
|
||||
Fl::remove_fd(fileno(s_proc.desc()));
|
||||
s_proc.close();
|
||||
shell_run_buffer->append("... END SHELL COMMAND ...\n");
|
||||
}
|
||||
|
||||
@ -1874,36 +2015,9 @@ shell_pipe_cb(int, void*) {
|
||||
|
||||
void
|
||||
do_shell_command(Fl_Return_Button*, void*) {
|
||||
const char *command; // Command to run
|
||||
const char *command=NULL; // Command to run
|
||||
|
||||
|
||||
shell_window->hide();
|
||||
|
||||
if (shell_pipe) {
|
||||
fl_alert("Previous shell command still running!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((command = shell_command_input->value()) == NULL || !*command) {
|
||||
fl_alert("No shell command entered!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_savefl_button->value()) {
|
||||
save_cb(0, 0);
|
||||
}
|
||||
|
||||
if (shell_writecode_button->value()) {
|
||||
compile_only = 1;
|
||||
write_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
|
||||
if (shell_writemsgs_button->value()) {
|
||||
compile_only = 1;
|
||||
write_strings_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
if (!prepare_shell_command(command)) return;
|
||||
|
||||
// Show the output window and clear things...
|
||||
shell_run_buffer->text("");
|
||||
@ -1911,7 +2025,7 @@ do_shell_command(Fl_Return_Button*, void*) {
|
||||
shell_run_buffer->append("\n");
|
||||
shell_run_window->label("Shell Command Running...");
|
||||
|
||||
if ((shell_pipe = popen((char *)command, "r")) == NULL) {
|
||||
if (s_proc.popen((char *)command) == NULL) {
|
||||
fl_alert("Unable to run shell command: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
@ -1920,9 +2034,9 @@ do_shell_command(Fl_Return_Button*, void*) {
|
||||
shell_run_window->hotspot(shell_run_display);
|
||||
shell_run_window->show();
|
||||
|
||||
Fl::add_fd(fileno(shell_pipe), shell_pipe_cb);
|
||||
Fl::add_fd(fileno(s_proc.desc()), shell_pipe_cb);
|
||||
|
||||
while (shell_pipe) Fl::wait();
|
||||
while (s_proc.desc()) Fl::wait();
|
||||
|
||||
shell_run_button->activate();
|
||||
shell_run_window->label("Shell Command Complete");
|
||||
@ -1937,29 +2051,7 @@ do_shell_command(Fl_Return_Button*, void*) {
|
||||
const char *command; // Command to run
|
||||
int status; // Status from command...
|
||||
|
||||
|
||||
shell_window->hide();
|
||||
|
||||
if ((command = shell_command_input->value()) == NULL || !*command) {
|
||||
fl_alert("No shell command entered!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_savefl_button->value()) {
|
||||
save_cb(0, 0);
|
||||
}
|
||||
|
||||
if (shell_writecode_button->value()) {
|
||||
compile_only = 1;
|
||||
write_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
|
||||
if (shell_writemsgs_button->value()) {
|
||||
compile_only = 1;
|
||||
write_strings_cb(0, 0);
|
||||
compile_only = 0;
|
||||
}
|
||||
if (!prepare_shell_command(command)) return;
|
||||
|
||||
if ((status = system(command)) != 0) {
|
||||
fl_alert("Shell command returned status %d!", status);
|
||||
@ -1967,8 +2059,7 @@ do_shell_command(Fl_Return_Button*, void*) {
|
||||
fl_message("Shell command completed successfully!");
|
||||
}
|
||||
}
|
||||
#endif // (!WIN32 || __CYGWIN__) && !__MWERKS__
|
||||
|
||||
#endif // !__MWERKS__
|
||||
|
||||
void
|
||||
show_shell_window() {
|
||||
|
@ -64,11 +64,23 @@
|
||||
|
||||
//
|
||||
// USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
|
||||
//
|
||||
// This currently doesn't appear to work; needs to be fixed!
|
||||
//
|
||||
// USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons:
|
||||
/**
|
||||
This feature was supposed to provide an efficient alternative to the current polling method,
|
||||
but as it has been discussed (Thanks Albrecht!) :
|
||||
- the async mode would imply to change the socket select mode to non blocking mode,
|
||||
this can have unexpected side effects for 3rd party apps, especially if it is set on-the-fly when
|
||||
socket service is really needed, as it is done today and on purpose, but still
|
||||
the 3rd party developer wouldn't easily control the sequencing of socket operations.
|
||||
- Finer granularity of events furthered by the async select is a plus only for socket 3rd party impl.,
|
||||
it is simply not needed for the 'light' fltk use we make of wsock, so here
|
||||
it would also be a bad point, because of all the logic add-ons necessary for
|
||||
using this functionality, without a clear benefit.
|
||||
|
||||
//#define USE_ASYNC_SELECT
|
||||
So async mode select would not add benefits to fltk, worse,
|
||||
it can slowdown fltk because of this finer granularity and instrumentation code
|
||||
to be added for async mode proper operation, not mentioning the side effects...
|
||||
*/
|
||||
|
||||
// dynamic wsock dll handling api:
|
||||
typedef int (WINAPI* fl_wsk_select_f)(int, fd_set*, fd_set*, fd_set*, const struct timeval*);
|
||||
@ -81,14 +93,14 @@ static fl_wsk_fd_is_set_f fl_wsk_fd_is_set=0;
|
||||
static fl_wsk_async_select_f fl_wsk_async_select=0;
|
||||
|
||||
static HMODULE get_wsock_mod() {
|
||||
if (!s_wsock_mod) {
|
||||
s_wsock_mod = LoadLibrary(WSCK_DLL_NAME);
|
||||
if (s_wsock_mod==NULL)
|
||||
Fl::fatal("FLTK Lib Error: %s file not found! Please check your winsock dll accessibility.\n",WSCK_DLL_NAME);
|
||||
s_wsock_select = (fl_wsk_select_f) GetProcAddress(s_wsock_mod, "select");
|
||||
fl_wsk_fd_is_set = (fl_wsk_fd_is_set_f) GetProcAddress(s_wsock_mod, "__WSAFDIsSet");
|
||||
fl_wsk_async_select = (fl_wsk_async_select_f) GetProcAddress(s_wsock_mod, "WSAAsyncSelect");
|
||||
}
|
||||
if (!s_wsock_mod) {
|
||||
s_wsock_mod = LoadLibrary(WSCK_DLL_NAME);
|
||||
if (s_wsock_mod==NULL)
|
||||
Fl::fatal("FLTK Lib Error: %s file not found! Please check your winsock dll accessibility.\n",WSCK_DLL_NAME);
|
||||
s_wsock_select = (fl_wsk_select_f) GetProcAddress(s_wsock_mod, "select");
|
||||
fl_wsk_fd_is_set = (fl_wsk_fd_is_set_f) GetProcAddress(s_wsock_mod, "__WSAFDIsSet");
|
||||
fl_wsk_async_select = (fl_wsk_async_select_f) GetProcAddress(s_wsock_mod, "WSAAsyncSelect");
|
||||
}
|
||||
return s_wsock_mod;
|
||||
}
|
||||
|
||||
@ -149,9 +161,7 @@ static HMODULE get_wsock_mod() {
|
||||
// select function that sends a WIN32 message when the select condition
|
||||
// exists...
|
||||
static int maxfd = 0;
|
||||
#ifndef USE_ASYNC_SELECT
|
||||
static fd_set fdsets[3];
|
||||
#endif // !USE_ASYNC_SELECT
|
||||
|
||||
#define POLLIN 1
|
||||
#define POLLOUT 4
|
||||
@ -182,18 +192,10 @@ void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
|
||||
fd[i].cb = cb;
|
||||
fd[i].arg = v;
|
||||
|
||||
#ifdef USE_ASYNC_SELECT
|
||||
int mask = 0;
|
||||
if (events & POLLIN) mask |= FD_READ;
|
||||
if (events & POLLOUT) mask |= FD_WRITE;
|
||||
if (events & POLLERR) mask |= FD_CLOSE;
|
||||
if (get_wsock_mod()) fl_wsk_async_select(n, fl_window, WM_FLSELECT, mask);
|
||||
#else
|
||||
if (events & POLLIN) FD_SET((unsigned)n, &fdsets[0]);
|
||||
if (events & POLLOUT) FD_SET((unsigned)n, &fdsets[1]);
|
||||
if (events & POLLERR) FD_SET((unsigned)n, &fdsets[2]);
|
||||
if (n > maxfd) maxfd = n;
|
||||
#endif // USE_ASYNC_SELECT
|
||||
}
|
||||
|
||||
void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
|
||||
@ -216,13 +218,9 @@ void Fl::remove_fd(int n, int events) {
|
||||
}
|
||||
nfds = j;
|
||||
|
||||
#ifdef USE_ASYNC_SELECT
|
||||
if (get_wsock_mod()) fl_wsk_async_select(n, 0, 0, 0);
|
||||
#else
|
||||
if (events & POLLIN) FD_CLR(unsigned(n), &fdsets[0]);
|
||||
if (events & POLLOUT) FD_CLR(unsigned(n), &fdsets[1]);
|
||||
if (events & POLLERR) FD_CLR(unsigned(n), &fdsets[2]);
|
||||
#endif // USE_ASYNC_SELECT
|
||||
}
|
||||
|
||||
void Fl::remove_fd(int n) {
|
||||
@ -261,7 +259,6 @@ int fl_wait(double time_to_wait) {
|
||||
in_idle = 0;
|
||||
}
|
||||
|
||||
#ifndef USE_ASYNC_SELECT
|
||||
if (nfds) {
|
||||
// For WIN32 we need to poll for socket input FIRST, since
|
||||
// the event queue is not something we can select() on...
|
||||
@ -287,7 +284,6 @@ int fl_wait(double time_to_wait) {
|
||||
if (time_to_wait > .001) time_to_wait = .001;
|
||||
}
|
||||
}
|
||||
#endif // USE_ASYNC_SELECT
|
||||
|
||||
if (Fl::idle || Fl::damage())
|
||||
time_to_wait = 0.0;
|
||||
@ -309,18 +305,6 @@ int fl_wait(double time_to_wait) {
|
||||
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
|
||||
if (have_message > 0) {
|
||||
while (have_message != 0 && have_message != -1) {
|
||||
#ifdef USE_ASYNC_SELECT
|
||||
if (fl_msg.message == WM_FLSELECT) {
|
||||
// Got notification for socket
|
||||
for (int i = 0; i < nfds; i ++)
|
||||
if (fd[i].fd == (int)fl_msg.wParam) {
|
||||
(fd[i].cb)(fd[i].fd, fd[i].arg);
|
||||
break;
|
||||
}
|
||||
// looks like it is best to do the dispatch-message anyway:
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fl_msg.message == fl_wake_msg) {
|
||||
// Used for awaking wait() from another thread
|
||||
thread_message_ = (void*)fl_msg.wParam;
|
||||
@ -345,16 +329,13 @@ int fl_wait(double time_to_wait) {
|
||||
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
|
||||
int fl_ready() {
|
||||
if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
|
||||
#ifdef USE_ASYNC_SELECT
|
||||
return 0;
|
||||
#else
|
||||
if (!nfds) return 0;
|
||||
timeval t;
|
||||
t.tv_sec = 0;
|
||||
t.tv_usec = 0;
|
||||
fd_set fdt[3];
|
||||
memcpy(fdt, fdsets, sizeof fdt);
|
||||
return get_wsock_mod() ? s_wsock_select(0,&fdt[0],&fdt[1],&fdt[2],&t) : 0;
|
||||
#endif // USE_ASYNC_SELECT
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
@ -249,6 +249,7 @@ int fl_wait(double time_to_wait) {
|
||||
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
|
||||
int fl_ready() {
|
||||
if (XQLength(fl_display)) return 1;
|
||||
if (!nfds) return 0; // nothing to select or poll
|
||||
# if USE_POLL
|
||||
return ::poll(pollfds, nfds, 0);
|
||||
# else
|
||||
|
Loading…
Reference in New Issue
Block a user