
tl;dr : making Fluid maintainable, no changes in code execution and logic. This is a pretty extensive restructuring of the Fluid source tree. It was neccessary because source and header files were getting much too big to handle. Many source files had no header, and many headers declared functions that were in diffrent source files. Reorganized much of the include statements. Added comments to some of the files. Added Doxygen configuration file for standalone Fluid docs. Tested everything by rebuilding Fluid .fl designs with the resorted version of Fluid.
235 lines
5.9 KiB
C++
235 lines
5.9 KiB
C++
//
|
|
// FLUID main entry for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2021 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
#include "shell_command.h"
|
|
|
|
#include "fluid.h"
|
|
#include "alignment_panel.h"
|
|
|
|
#include <FL/Fl_Double_Window.H>
|
|
#include <FL/fl_message.H>
|
|
|
|
#include <errno.h>
|
|
|
|
static Fl_Process s_proc;
|
|
|
|
/** \class Fl_Process
|
|
\todo Explain.
|
|
*/
|
|
|
|
Fl_Process::Fl_Process() {
|
|
_fpt= NULL;
|
|
}
|
|
|
|
Fl_Process::~Fl_Process() {
|
|
if (_fpt) close();
|
|
}
|
|
|
|
// FIXME: popen needs the UTF-8 equivalent fl_popen
|
|
// 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((fl_intptr_t) 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
|
|
}
|
|
|
|
// non-null if file is open
|
|
FILE *Fl_Process::desc() const {
|
|
return _fpt;
|
|
}
|
|
|
|
char *Fl_Process::get_line(char * line, size_t s) const {
|
|
return _fpt ? fgets(line, (int)s, _fpt) : NULL;
|
|
}
|
|
|
|
// returns fileno(FILE*):
|
|
// (file must be open, i.e. _fpt must be non-null)
|
|
// *FIXME* we should find a better solution for the 'fileno' issue
|
|
// non null if file is open
|
|
int Fl_Process::get_fileno() const {
|
|
#ifdef _MSC_VER
|
|
return _fileno(_fpt); // suppress MSVC warning
|
|
#else
|
|
return fileno(_fpt);
|
|
#endif
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
FILE *Fl_Process::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
|
|
}
|
|
|
|
void Fl_Process::clean_close(HANDLE& h) {
|
|
if (h!= INVALID_HANDLE_VALUE) CloseHandle(h);
|
|
h = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// 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()) {
|
|
write_code_files();
|
|
}
|
|
if (shell_writemsgs_button->value()) {
|
|
write_strings_cb(0, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Support the full piped shell command...
|
|
void shell_pipe_cb(FL_SOCKET, void*) {
|
|
char line[1024]=""; // Line from command output...
|
|
|
|
if (s_proc.get_line(line, sizeof(line)) != NULL) {
|
|
// Add the line to the output list...
|
|
shell_run_terminal->append(line);
|
|
} else {
|
|
// End of file; tell the parent...
|
|
Fl::remove_fd(s_proc.get_fileno());
|
|
s_proc.close();
|
|
shell_run_terminal->append("... END SHELL COMMAND ...\n");
|
|
}
|
|
}
|
|
|
|
void do_shell_command(Fl_Return_Button*, void*) {
|
|
const char *command=NULL; // Command to run
|
|
|
|
if (!prepare_shell_command(command)) return;
|
|
|
|
// Show the output window and clear things...
|
|
shell_run_terminal->text("");
|
|
shell_run_terminal->append(command);
|
|
shell_run_terminal->append("\n");
|
|
shell_run_window->label("Shell Command Running...");
|
|
|
|
if (s_proc.popen((char *)command) == NULL) {
|
|
fl_alert("Unable to run shell command: %s", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
shell_run_button->deactivate();
|
|
|
|
Fl_Preferences pos(fluid_prefs, "shell_run_Window_pos");
|
|
int x, y, w, h;
|
|
pos.get("x", x, -1);
|
|
pos.get("y", y, 0);
|
|
pos.get("w", w, 640);
|
|
pos.get("h", h, 480);
|
|
if (x!=-1) {
|
|
shell_run_window->resize(x, y, w, h);
|
|
}
|
|
shell_run_window->show();
|
|
|
|
Fl::add_fd(s_proc.get_fileno(), shell_pipe_cb);
|
|
|
|
while (s_proc.desc()) Fl::wait();
|
|
|
|
shell_run_button->activate();
|
|
shell_run_window->label("Shell Command Complete");
|
|
fl_beep();
|
|
|
|
while (shell_run_window->shown()) Fl::wait();
|
|
}
|
|
|
|
/**
|
|
Show a dialog box to run an external shell command.
|
|
*/
|
|
void show_shell_window() {
|
|
shell_window->hotspot(shell_command_input);
|
|
shell_window->show();
|
|
}
|
|
|