diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index 232e364d82600ec535e7b236aded95074e5dda05..984a9008e2b0cf4b371049775575eb85be6e1a2a 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -6,6 +6,7 @@ set(SRC Gmsh.cpp GmshMessage.cpp + GmshRemote.cpp GmshDaemon.cpp Context.cpp Options.cpp diff --git a/Common/GmshRemote.cpp b/Common/GmshRemote.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dcd67bf9d39cd370760e54853fffaab06d71d80 --- /dev/null +++ b/Common/GmshRemote.cpp @@ -0,0 +1,100 @@ +// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to <gmsh@geuz.org>. + +#include <sstream> +#include "GmshConfig.h" +#include "GmshMessage.h" +#include "GmshRemote.h" +#include "StringUtils.h" +#include "OS.h" +#include "Context.h" + +std::map<int, GmshRemote*> GmshRemote::_all; + +GmshRemote::GmshRemote() + : clientServer(1), popupMessages(1), mergeViews(1), _pid(0), _server(0) +{ + // we need at least five for the solver menu + buttonName.resize(5); + buttonSwitch.resize(5); + optionName.resize(5); + optionValue.resize(5); +} + +std::string GmshRemote::getSocketName() +{ + std::string sockname; + if(!strstr(CTX::instance()->solver.socketName.c_str(), ":")){ + // Unix socket + std::ostringstream tmp; + tmp << CTX::instance()->homeDir << CTX::instance()->solver.socketName; + sockname = FixWindowsPath(tmp.str()); + } + else{ + // TCP/IP socket + sockname = CTX::instance()->solver.socketName; + // if only the port is given, prepend the host name + if(sockname.size() && sockname[0] == ':') + sockname = GetHostName() + sockname; + } + return sockname; +} + +void GmshRemote::runToGetOptions() +{ + if(inputFileName.empty()) return; + + std::string inputArg = inputFileName.empty() ? "" : ReplacePercentS + (inputFileSwitch, std::string("\"") + FixWindowsPath(inputFileName) + "\""); + std::string optionArg = ReplacePercentS(optionSwitch, ""); + run(inputArg + " " + optionArg); +} + +void GmshRemote::runCommand(int commandIndex, int optionIndex, int optionChoice) +{ + std::string inputArg = inputFileName.empty() ? "" : ReplacePercentS + (inputFileSwitch, std::string("\"") + FixWindowsPath(inputFileName) + "\""); + + std::string meshArg = meshFileName.empty() ? "" : ReplacePercentS + (meshFileSwitch, std::string("\"") + FixWindowsPath(meshFileName) + "\""); + + if(commandIndex < 0 || commandIndex >= buttonSwitch.size()){ + Msg::Error("Wrong command index"); + return; + } + + if(optionIndex < 0 || optionIndex >= optionValue.size()){ + Msg::Error("Wrong option index"); + return; + } + + if(optionChoice < 0 || optionChoice >= optionValue[optionIndex].size()){ + Msg::Error("Wrong option choice"); + return; + } + + std::string commandArg = ReplacePercentS + (buttonSwitch[commandIndex], optionValue[optionIndex][optionChoice]); + + run(inputArg + " " + meshArg + " " + commandArg); +} + +#if !defined(HAVE_FLTK) + +void GmshRemote::run(std::string args) +{ + Msg::Error("Gmsh has to be compiled with FLTK support to run remote apps"); +} + +#endif + +void GmshRemote::kill() +{ + if(_pid > 0) { + if(KillProcess(_pid)) + Msg::Info("Killed %s pid %d", name.c_str(), _pid); + } + _pid = -1; +} diff --git a/Common/GmshRemote.h b/Common/GmshRemote.h new file mode 100644 index 0000000000000000000000000000000000000000..1ba3c030d89fbe72428c76f8d03853524887a917 --- /dev/null +++ b/Common/GmshRemote.h @@ -0,0 +1,106 @@ +// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to <gmsh@geuz.org>. + +#ifndef _GMSH_REMOTE_H_ +#define _GMSH_REMOTE_H_ + +#include <string> +#include <vector> +#include <map> + +class GmshServer; + +class GmshRemote { + public: + // the name of the remote program (e.g. "GetDP") + std::string name; + // the executable command (e.g. "/usr/bin/getdp.exe") + std::string executable; + // the name of the input file for the remote program (e.g. "") + std::string inputFileName; + // the command line switch to specify the input file (e.g. "%s") + std::string inputFileSwitch; + // the standard extension of input file names for the remote program + // (e.g. ".pro") + std::string inputFileExtension; + // the name of the mesh file used by the remote program (e.g. "") + std::string meshFileName; + // the command line switch used to specify the mesh file + // (e.g. "-mesh %s") + std::string meshFileSwitch; + // the command line switch used to specify the socket (e.g. "-socket + // %s") + std::string socketSwitch; + // the names of the action buttons in the GUI (e.g. "Pre", "Cal", + // "Post") + std::vector<std::string> buttonName; + // the command line switches associated with the buttons (e.g. "-pre + // %s", "-cal", "-pos %s") + std::vector<std::string> buttonSwitch; + // the name of the options that can be passed to the remote program + // (e.g. "Resolution", "PostOperation") + std::vector<std::string> optionName; + // the command line switch to get the available values for all the + // options + std::string optionSwitch; + // the possible values for each option (these are normally + // dynamically filled by calling the remote program with the + // optionSwitch command line arg) + std::vector<std::vector<std::string> > optionValue; + // a help string describing the remote program + std::string help; + // set to true if the remote program should communicate with Gmsh + // through a socket + bool clientServer; + // set to true if remote messages should pop up the message window + bool popupMessages; + // set to true if remotely generated post-processing files should be + // merged by Gmsh + bool mergeViews; + private: + // the process id number of the remote program while it is running, + // or -1 when stopped + int _pid; + // a pointer to the server when the remote program is running, or 0 + // when stopped + GmshServer *_server; + // a static map of all available remote programs: ints 0, 1, ... 4 + // are reserved for the main solver menu; int -1 is used when + // permanently listening for incoming connections + static std::map<int, GmshRemote*> _all; + public: + GmshRemote(); + ~GmshRemote(){} + // get the name of the socket + std::string getSocketName(); + // get/set the pid + int getPid(){ return _pid; } + void setPid(int pid){ _pid = pid; } + // get/set the server + GmshServer *getServer(){ return _server; } + void setServer(GmshServer *server){ _server = server; } + // get the num-th remote program; if it does not exist, create it + static GmshRemote *get(int num) + { + std::map<int, GmshRemote*>::iterator it = _all.find(num); + if(it == _all.end()){ + GmshRemote *s = new GmshRemote(); + _all[num] = s; + return s; + } + return it->second; + } + // run the remote program to get its options + void runToGetOptions(); + // run the commandIndex-th command (button), using the specified + // option + void runCommand(int commandIndex, int optionIndex, int optionChoice); + // run the remote program with the given arguments + void run(std::string args); + // kill the remote program + void kill(); +}; + +#endif diff --git a/Common/GmshSocket.h b/Common/GmshSocket.h index 5210367f0b538829446423cd563a3fa9b83a8b47..9ab9ad90c335f08af887cde824b1ebd1fc651f8a 100644 --- a/Common/GmshSocket.h +++ b/Common/GmshSocket.h @@ -282,17 +282,11 @@ class GmshServer : public GmshSocket{ GmshServer() : GmshSocket(), _portno(-1) {} virtual ~GmshServer(){} virtual int SystemCall(const char *str) = 0; - virtual int NonBlockingWait(int socket, int num, double waitint, double timeout) = 0; - int Start(int num, const char *command, const char *sockname, double timeout) + virtual int NonBlockingWait(int socket, double waitint, double timeout) = 0; + int Start(const char *command, const char *sockname, double timeout) { + if(!sockname) throw "Invalid (null) socket name"; _sockname = sockname; - - // no socket? launch the command directly - if(!_sockname) { - SystemCall(command); - return 1; - } - int tmpsock; if(strstr(_sockname, "/") || strstr(_sockname, "\\") || !strstr(_sockname, ":")){ // UNIX socket (testing ":" is not enough with Windows paths) @@ -357,7 +351,7 @@ class GmshServer : public GmshSocket{ } // wait until we get data - int ret = NonBlockingWait(tmpsock, num, 0.5, timeout); + int ret = NonBlockingWait(tmpsock, 0.5, timeout); if(ret){ CloseSocket(tmpsock); if(ret == 2){ diff --git a/Common/Options.cpp b/Common/Options.cpp index f00b6973d256765becbb97efe8e2fde6b7e46bed..5a113c3ba8d05630f2d53c8ce8f742fc2b0cb0b8 100644 --- a/Common/Options.cpp +++ b/Common/Options.cpp @@ -8,6 +8,7 @@ #include "GmshConfig.h" #include "GmshDefines.h" #include "GmshMessage.h" +#include "GmshRemote.h" #include "StringUtils.h" #include "GModel.h" #include "Generator.h" @@ -33,7 +34,6 @@ #if defined(HAVE_FLTK) #include <FL/Fl_Tooltip.H> #include "FlGui.h" -#include "Solvers.h" #include "menuWindow.h" #include "graphicWindow.h" #include "optionWindow.h" @@ -1040,15 +1040,13 @@ std::string opt_solver_socket_name(OPT_ARGS_STR) std::string opt_solver_name(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].name = val; + GmshRemote::get(num)->name = val; +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)) - FlGui::instance()->solver[num]->win->label(SINFO[num].name.c_str()); - return SINFO[num].name; -#else - return "undefined"; + FlGui::instance()->solver[num]->win->label(GmshRemote::get(num)->name.c_str()); #endif + return GmshRemote::get(num)->name; } std::string opt_solver_name0(OPT_ARGS_STR) @@ -1078,16 +1076,14 @@ std::string opt_solver_name4(OPT_ARGS_STR) std::string opt_solver_executable(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].executable_name = val; + GmshRemote::get(num)->executable = val; +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)) FlGui::instance()->solver[num]->input[2]->value - (SINFO[num].executable_name.c_str()); - return SINFO[num].executable_name; -#else - return "undefined"; + (GmshRemote::get(num)->executable.c_str()); #endif + return GmshRemote::get(num)->executable; } std::string opt_solver_executable0(OPT_ARGS_STR) @@ -1117,13 +1113,9 @@ std::string opt_solver_executable4(OPT_ARGS_STR) std::string opt_solver_help(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].help = val; - return SINFO[num].help; -#else - return "undefined"; -#endif + GmshRemote::get(num)->help = val; + return GmshRemote::get(num)->help; } std::string opt_solver_help0(OPT_ARGS_STR) @@ -1153,21 +1145,19 @@ std::string opt_solver_help4(OPT_ARGS_STR) std::string opt_solver_input_name(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET){ #if !defined(HAVE_NO_PARSER) - SINFO[num].input_name = FixRelativePath(gmsh_yyname, val); + GmshRemote::get(num)->inputFileName = FixRelativePath(gmsh_yyname, val); #else - SINFO[num].input_name = val; + GmshRemote::get(num)->inputFileName = val; #endif } +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)) FlGui::instance()->solver[num]->input[0]->value - (SINFO[num].input_name.c_str()); - return SINFO[num].input_name; -#else - return "undefined"; + (GmshRemote::get(num)->inputFileName.c_str()); #endif + return GmshRemote::get(num)->inputFileName; } std::string opt_solver_input_name0(OPT_ARGS_STR) @@ -1197,13 +1187,9 @@ std::string opt_solver_input_name4(OPT_ARGS_STR) std::string opt_solver_extension(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].extension = val; - return SINFO[num].extension; -#else - return "undefined"; -#endif + GmshRemote::get(num)->inputFileExtension = val; + return GmshRemote::get(num)->inputFileExtension; } std::string opt_solver_extension0(OPT_ARGS_STR) @@ -1233,21 +1219,19 @@ std::string opt_solver_extension4(OPT_ARGS_STR) std::string opt_solver_mesh_name(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET){ #if !defined(HAVE_NO_PARSER) - SINFO[num].mesh_name = FixRelativePath(gmsh_yyname, val); + GmshRemote::get(num)->meshFileName = FixRelativePath(gmsh_yyname, val); #else - SINFO[num].mesh_name = val; + GmshRemote::get(num)->meshFileName = val; #endif } +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)) FlGui::instance()->solver[num]->input[1]->value - (SINFO[num].mesh_name.c_str()); - return SINFO[num].mesh_name; -#else - return "undefined"; + (GmshRemote::get(num)->meshFileName.c_str()); #endif + return GmshRemote::get(num)->meshFileName; } std::string opt_solver_mesh_name0(OPT_ARGS_STR) @@ -1277,13 +1261,9 @@ std::string opt_solver_mesh_name4(OPT_ARGS_STR) std::string opt_solver_mesh_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].mesh_command = val; - return SINFO[num].mesh_command; -#else - return "undefined"; -#endif + GmshRemote::get(num)->meshFileSwitch = val; + return GmshRemote::get(num)->meshFileSwitch; } std::string opt_solver_mesh_command0(OPT_ARGS_STR) @@ -1313,13 +1293,9 @@ std::string opt_solver_mesh_command4(OPT_ARGS_STR) std::string opt_solver_socket_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].socket_command = val; - return SINFO[num].socket_command; -#else - return "undefined"; -#endif + GmshRemote::get(num)->socketSwitch = val; + return GmshRemote::get(num)->socketSwitch; } std::string opt_solver_socket_command0(OPT_ARGS_STR) @@ -1349,13 +1325,9 @@ std::string opt_solver_socket_command4(OPT_ARGS_STR) std::string opt_solver_name_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].name_command = val; - return SINFO[num].name_command; -#else - return "undefined"; -#endif + GmshRemote::get(num)->inputFileSwitch = val; + return GmshRemote::get(num)->inputFileSwitch; } std::string opt_solver_name_command0(OPT_ARGS_STR) @@ -1385,13 +1357,9 @@ std::string opt_solver_name_command4(OPT_ARGS_STR) std::string opt_solver_option_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_command = val; - return SINFO[num].option_command; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionSwitch = val; + return GmshRemote::get(num)->optionSwitch; } std::string opt_solver_option_command0(OPT_ARGS_STR) @@ -1421,13 +1389,9 @@ std::string opt_solver_option_command4(OPT_ARGS_STR) std::string opt_solver_first_option(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_name[0] = val; - return SINFO[num].option_name[0]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionName[0] = val; + return GmshRemote::get(num)->optionName[0]; } std::string opt_solver_first_option0(OPT_ARGS_STR) @@ -1457,13 +1421,9 @@ std::string opt_solver_first_option4(OPT_ARGS_STR) std::string opt_solver_second_option(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_name[1] = val; - return SINFO[num].option_name[1]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionName[1] = val; + return GmshRemote::get(num)->optionName[1]; } std::string opt_solver_second_option0(OPT_ARGS_STR) @@ -1493,13 +1453,9 @@ std::string opt_solver_second_option4(OPT_ARGS_STR) std::string opt_solver_third_option(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_name[2] = val; - return SINFO[num].option_name[2]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionName[2] = val; + return GmshRemote::get(num)->optionName[2]; } std::string opt_solver_third_option0(OPT_ARGS_STR) @@ -1529,13 +1485,9 @@ std::string opt_solver_third_option4(OPT_ARGS_STR) std::string opt_solver_fourth_option(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_name[3] = val; - return SINFO[num].option_name[3]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionName[3] = val; + return GmshRemote::get(num)->optionName[3]; } std::string opt_solver_fourth_option0(OPT_ARGS_STR) @@ -1565,13 +1517,9 @@ std::string opt_solver_fourth_option4(OPT_ARGS_STR) std::string opt_solver_fifth_option(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].option_name[4] = val; - return SINFO[num].option_name[4]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->optionName[4] = val; + return GmshRemote::get(num)->optionName[4]; } std::string opt_solver_fifth_option0(OPT_ARGS_STR) @@ -1601,13 +1549,9 @@ std::string opt_solver_fifth_option4(OPT_ARGS_STR) std::string opt_solver_first_button(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_name[0] = val; - return SINFO[num].button_name[0]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonName[0] = val; + return GmshRemote::get(num)->buttonName[0]; } std::string opt_solver_first_button0(OPT_ARGS_STR) @@ -1637,13 +1581,9 @@ std::string opt_solver_first_button4(OPT_ARGS_STR) std::string opt_solver_first_button_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_command[0] = val; - return SINFO[num].button_command[0]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonSwitch[0] = val; + return GmshRemote::get(num)->buttonSwitch[0]; } std::string opt_solver_first_button_command0(OPT_ARGS_STR) @@ -1673,13 +1613,9 @@ std::string opt_solver_first_button_command4(OPT_ARGS_STR) std::string opt_solver_second_button(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_name[1] = val; - return SINFO[num].button_name[1]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonName[1] = val; + return GmshRemote::get(num)->buttonName[1]; } std::string opt_solver_second_button0(OPT_ARGS_STR) @@ -1709,13 +1645,9 @@ std::string opt_solver_second_button4(OPT_ARGS_STR) std::string opt_solver_second_button_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_command[1] = val; - return SINFO[num].button_command[1]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonSwitch[1] = val; + return GmshRemote::get(num)->buttonSwitch[1]; } std::string opt_solver_second_button_command0(OPT_ARGS_STR) @@ -1745,13 +1677,9 @@ std::string opt_solver_second_button_command4(OPT_ARGS_STR) std::string opt_solver_third_button(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_name[2] = val; - return SINFO[num].button_name[2]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonName[2] = val; + return GmshRemote::get(num)->buttonName[2]; } std::string opt_solver_third_button0(OPT_ARGS_STR) @@ -1781,13 +1709,9 @@ std::string opt_solver_third_button4(OPT_ARGS_STR) std::string opt_solver_third_button_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_command[2] = val; - return SINFO[num].button_command[2]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonSwitch[2] = val; + return GmshRemote::get(num)->buttonSwitch[2]; } std::string opt_solver_third_button_command0(OPT_ARGS_STR) @@ -1817,13 +1741,9 @@ std::string opt_solver_third_button_command4(OPT_ARGS_STR) std::string opt_solver_fourth_button(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_name[3] = val; - return SINFO[num].button_name[3]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonName[3] = val; + return GmshRemote::get(num)->buttonName[3]; } std::string opt_solver_fourth_button0(OPT_ARGS_STR) @@ -1853,13 +1773,9 @@ std::string opt_solver_fourth_button4(OPT_ARGS_STR) std::string opt_solver_fourth_button_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_command[3] = val; - return SINFO[num].button_command[3]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonSwitch[3] = val; + return GmshRemote::get(num)->buttonSwitch[3]; } std::string opt_solver_fourth_button_command0(OPT_ARGS_STR) @@ -1889,13 +1805,9 @@ std::string opt_solver_fourth_button_command4(OPT_ARGS_STR) std::string opt_solver_fifth_button(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_name[4] = val; - return SINFO[num].button_name[4]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonName[4] = val; + return GmshRemote::get(num)->buttonName[4]; } std::string opt_solver_fifth_button0(OPT_ARGS_STR) @@ -1925,13 +1837,9 @@ std::string opt_solver_fifth_button4(OPT_ARGS_STR) std::string opt_solver_fifth_button_command(OPT_ARGS_STR) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].button_command[4] = val; - return SINFO[num].button_command[4]; -#else - return "undefined"; -#endif + GmshRemote::get(num)->buttonSwitch[4] = val; + return GmshRemote::get(num)->buttonSwitch[4]; } std::string opt_solver_fifth_button_command0(OPT_ARGS_STR) @@ -6000,19 +5908,17 @@ double opt_solver_plugins(OPT_ARGS_NUM) double opt_solver_client_server(OPT_ARGS_NUM) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].client_server = (int)val; + GmshRemote::get(num)->clientServer = (bool)val; +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)){ - if(SINFO[num].client_server) + if(GmshRemote::get(num)->clientServer) ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[0].set(); else ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[0].clear(); } - return SINFO[num].client_server; -#else - return 0.; #endif + return GmshRemote::get(num)->clientServer ? 1 : 0; } double opt_solver_client_server0(OPT_ARGS_NUM) @@ -6042,19 +5948,17 @@ double opt_solver_client_server4(OPT_ARGS_NUM) double opt_solver_popup_messages(OPT_ARGS_NUM) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].popup_messages = (int)val; + GmshRemote::get(num)->popupMessages = (bool)val; +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)){ - if(SINFO[num].popup_messages) + if(GmshRemote::get(num)->popupMessages) ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[1].set(); else ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[1].clear(); } - return SINFO[num].popup_messages; -#else - return 1.; #endif + return GmshRemote::get(num)->popupMessages ? 1 : 0; } double opt_solver_popup_messages0(OPT_ARGS_NUM) @@ -6084,19 +5988,17 @@ double opt_solver_popup_messages4(OPT_ARGS_NUM) double opt_solver_merge_views(OPT_ARGS_NUM) { -#if defined(HAVE_FLTK) if(action & GMSH_SET) - SINFO[num].merge_views = (int)val; + GmshRemote::get(num)->mergeViews = (bool)val; +#if defined(HAVE_FLTK) if(FlGui::available() && (action & GMSH_GUI)){ - if(SINFO[num].merge_views) + if(GmshRemote::get(num)->mergeViews) ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[2].set(); else ((Fl_Menu_Item*)FlGui::instance()->solver[num]->menu->menu())[2].clear(); } - return SINFO[num].merge_views; -#else - return 1.; #endif + return GmshRemote::get(num)->mergeViews ? 1 : 0; } double opt_solver_merge_views0(OPT_ARGS_NUM) diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index ba7f28b3452ca8931491077d89eb1c935848c624..f254172a49980a54c2d13a5964cd9a38b295157e 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -129,30 +129,3 @@ std::string ReplacePercentS(std::string in, std::string val) } return out; } - -void ReplaceMultiFormat(const char *in, const char *val, char *out) -{ - unsigned int i = 0, j = 0; - - out[0] = '\0'; - while(i < strlen(in)){ - if(in[i] == '%' && i != strlen(in) - 1){ - if(in[i + 1] == 's'){ - strcat(out, val); - i += 2; - j += strlen(val); - } - else{ - Msg::Warning("Skipping unknown format '%%%c' in '%s'", in[i + 1], in); - i += 2; - } - } - else{ - out[j] = in[i]; - out[j + 1] = '\0'; - i++; - j++; - } - } - out[j] = '\0'; -} diff --git a/Common/StringUtils.h b/Common/StringUtils.h index c0f34a6e1ee5bfbdc5deeb2e73a63f281311f5f5..4393b88361aa0e22ea0183c251742a27eff6b88f 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -18,6 +18,5 @@ std::string FixRelativePath(std::string reference, std::string in); std::vector<std::string> SplitFileName(std::string fileName); std::vector<std::string> SplitWhiteSpace(std::string in, unsigned int len); std::string ReplacePercentS(std::string in, std::string val); -void ReplaceMultiFormat(const char *in, const char *val, char *out); #endif diff --git a/Fltk/CMakeLists.txt b/Fltk/CMakeLists.txt index a073d3ac3010a88c7519532371f7c6dfc0936dd4..aa7db415a90dfe36a7857207c509544000307aee 100644 --- a/Fltk/CMakeLists.txt +++ b/Fltk/CMakeLists.txt @@ -25,7 +25,6 @@ set(SRC projectionEditor.cpp classificationEditor.cpp partitionDialog.cpp - Solvers.cpp ) append_gmsh_src(Fltk "${SRC}") diff --git a/Fltk/FlGui.cpp b/Fltk/FlGui.cpp index d7d65fe08a8e01271e27a2ccd66832d332204234..a4c39be1e9c5c63f1bbb3b1a66454f6545374ac2 100644 --- a/Fltk/FlGui.cpp +++ b/Fltk/FlGui.cpp @@ -32,7 +32,6 @@ #include "GModel.h" #include "MElement.h" #include "PView.h" -#include "Solvers.h" #include "Field.h" #include "Plugin.h" #include "PluginManager.h" @@ -264,7 +263,7 @@ FlGui::FlGui(int argc, char **argv) geoContext = new geometryContextWindow(CTX::instance()->deltaFontSize); meshContext = new meshContextWindow(CTX::instance()->deltaFontSize); about = new aboutWindow(); - for(int i = 0; i < MAX_NUM_SOLVERS; i++) + for(int i = 0; i < 5; i++) solver.push_back(new solverWindow(i, CTX::instance()->deltaFontSize)); // init solver plugin stuff diff --git a/Fltk/Main.cpp b/Fltk/Main.cpp index 0ed4d5d912089b17ecb2faf1a0ba6c13bdb49166..2d41035c3ac8f289bafda0c838ca743792e813a7 100644 --- a/Fltk/Main.cpp +++ b/Fltk/Main.cpp @@ -6,6 +6,7 @@ #include <string> #include "Gmsh.h" #include "GmshMessage.h" +#include "GmshRemote.h" #include "FlGui.h" #include "menuWindow.h" #include "drawContext.h" @@ -14,7 +15,6 @@ #include "Parser.h" #include "OpenFile.h" #include "CommandLine.h" -#include "Solvers.h" #include "PluginManager.h" #include "GModel.h" #include "Field.h" @@ -115,7 +115,8 @@ int main(int argc, char *argv[]) drawContext::global()->draw(); // Listen to external solvers - if(CTX::instance()->solver.listen) Solver(-1, NULL); + if(CTX::instance()->solver.listen) + GmshRemote::get(-1)->run(""); // loop return FlGui::instance()->run(); diff --git a/Fltk/Solvers.cpp b/Fltk/Solvers.cpp deleted file mode 100644 index fa94528ec3dd0235be20ba1452b00da8c6e772d3..0000000000000000000000000000000000000000 --- a/Fltk/Solvers.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle -// -// See the LICENSE.txt file for license information. Please report all -// bugs and problems to <gmsh@geuz.org>. - -#include <string.h> -#include <string> -#include <sstream> -#include "FlGui.h" -#include "solverWindow.h" -#include "menuWindow.h" -#include "GmshMessage.h" -#include "StringUtils.h" -#include "Solvers.h" -#include "GmshSocket.h" -#include "OpenFile.h" -#include "PView.h" -#include "drawContext.h" -#include "Context.h" -#include "OS.h" - -SolverInfo SINFO[MAX_NUM_SOLVERS + 1]; - -class myGmshServer : public GmshServer{ - public: - myGmshServer() : GmshServer() {} - ~myGmshServer() {} - int SystemCall(const char *str){ return ::SystemCall(str); } - int NonBlockingWait(int socket, int num, double waitint, double timeout) - { - // This routine polls the socket at least every 'waitint' seconds, - // and for at most timout seconds (or indefinitely if - // timeout==0). The routine returns 0 as soon as data is available - // and 1 if there was en error or if the process was - // killed. Otherwise it just tends to current GUI events (this is - // easier to manage than non-blocking IO, and simpler than using - // the "real" solution, i.e., threads. Another possibility would - // be to use Fl::add_fd()) - double start = GetTimeInSeconds(); - while(1){ - if(timeout > 0 && GetTimeInSeconds() - start > timeout) { - return 2; // timout - } - - if((num >= 0 && SINFO[num].pid < 0) || - (num < 0 && !CTX::instance()->solver.listen)){ - return 1; // process has been killed or we stopped listening - } - - // check if there is data (call select with a zero timeout to - // return immediately, i.e., do polling) - int ret = Select(0, 0, socket); - - if(ret == 0){ - // nothing available: wait at most waitint seconds, and in the - // meantime respond to FLTK events - FlGui::instance()->wait(waitint); - } - else if(ret > 0){ - return 0; // data is there! - } - else{ - // an error happened - if(num >= 0){ - SINFO[num].pid = -1; - SINFO[num].server = 0; - } - return 1; - } - } - } -}; - -std::string GetSocketName(int num) -{ - std::string sockname; - if(!strstr(CTX::instance()->solver.socketName.c_str(), ":")){ - // Unix socket - std::ostringstream tmp; - tmp << CTX::instance()->homeDir << CTX::instance()->solver.socketName; - if(num >= 0) tmp << "-" << num; - sockname = FixWindowsPath(tmp.str()); - } - else{ - // TCP/IP socket - sockname = CTX::instance()->solver.socketName; - // if only the port is given, prepend the host name - if(sockname.size() && sockname[0] == ':') - sockname = GetHostName() + sockname; - } - return sockname; -} - -// This routine either launches a solver and waits for some answer (if -// num >= 0), or simply waits for messages (if num < 0) - -int Solver(int num, const char *args) -{ - std::string command, sockname, prog; - - new_connection: - - GmshServer *server = new myGmshServer; - - if(num >= 0){ - prog = FixWindowsPath(SINFO[num].executable_name); - if(!SINFO[num].client_server) { - command = prog + " " + args; -#if !defined(WIN32) - command += " &"; -#endif - server->Start(num, command.c_str(), 0, 0.); - delete server; - return 1; - } - } - else{ - if(!CTX::instance()->solver.listen){ - Msg::Info("Stopped listening for solver connections"); - delete server; - return 0; - } - // we don't know who will (maybe) contact us - prog = command = ""; - } - - sockname = GetSocketName(num); - - if(num >= 0){ - std::string tmp2 = "\"" + sockname + "\""; - char tmp[1024]; - sprintf(tmp, SINFO[num].socket_command.c_str(), tmp2.c_str()); - command = prog + " " + args + " " + tmp; -#if !defined(WIN32) - command += " &"; -#endif - } - - if(num >= 0){ - SINFO[num].pid = 0; - SINFO[num].server = 0; - } - bool initOption[5] = {true, true, true, true, true}; - - int sock; - try{ - sock = server->Start(num, command.c_str(), sockname.c_str(), 5.); - } - catch(const char *err){ - Msg::Error("%s (on socket '%s')", err, sockname.c_str()); - sock = -1; - } - - if(sock < 0){ - if(num >= 0){ - for(int i = 0; i < SINFO[num].nboptions; i++) - FlGui::instance()->solver[num]->choice[i]->clear(); - } - server->Shutdown(); - delete server; - return 0; - } - - Msg::StatusBar(2, false, "Running '%s'", prog.c_str()); - - while(1) { - - int stop = (num < 0 && !CTX::instance()->solver.listen); - - if(stop || (num >= 0 && SINFO[num].pid < 0)) - break; - - stop = server->NonBlockingWait(sock, num, 0.1, 0.); - - if(stop || (num >= 0 && SINFO[num].pid < 0)) - break; - - int type, length; - if(server->ReceiveHeader(&type, &length)){ - double timer = GetTimeInSeconds(); - char *message = new char[length + 1]; - if(server->ReceiveString(length, message)){ - switch (type) { - case GmshSocket::START: - if(num >= 0){ - SINFO[num].pid = atoi(message); - SINFO[num].server = server; - } - break; - case GmshSocket::STOP: - stop = 1; - if(num >= 0){ - SINFO[num].pid = -1; - SINFO[num].server = 0; - } - break; - case GmshSocket::PROGRESS: - if(num >= 0) - Msg::StatusBar(2, false, "%s %s", SINFO[num].name.c_str(), message); - else - Msg::StatusBar(2, false, "%s", message); - break; - case GmshSocket::OPTION_1: - if(initOption[0]){ - SINFO[num].option[0].clear(); - initOption[0] = false; - } - if(num >= 0) - SINFO[num].option[0].push_back(message); - break; - case GmshSocket::OPTION_2: - if(initOption[1]){ - SINFO[num].option[1].clear(); - initOption[1] = false; - } - if(num >= 0) - SINFO[num].option[1].push_back(message); - break; - case GmshSocket::OPTION_3: - if(initOption[2]){ - SINFO[num].option[2].clear(); - initOption[2] = false; - } - if(num >= 0) - SINFO[num].option[2].push_back(message); - break; - case GmshSocket::OPTION_4: - if(initOption[3]){ - SINFO[num].option[3].clear(); - initOption[3] = false; - } - if(num >= 0) - SINFO[num].option[3].push_back(message); - break; - case GmshSocket::OPTION_5: - if(initOption[4]){ - SINFO[num].option[4].clear(); - initOption[4] = false; - } - if(num >= 0) - SINFO[num].option[4].push_back(message); - break; - case GmshSocket::MERGE_FILE: - if(num < 0 || (num >= 0 && SINFO[num].merge_views)) { - int n = PView::list.size(); - MergeFile(message); - drawContext::global()->draw(); - if(n != (int)PView::list.size()) - FlGui::instance()->menu->setContext(menu_post, 0); - } - break; - case GmshSocket::PARSE_STRING: - ParseString(message); - drawContext::global()->draw(); - break; - case GmshSocket::INFO: - Msg::Direct("%-8.8s: %s", num >= 0 ? SINFO[num].name.c_str() : "Client", - message); - break; - case GmshSocket::WARNING: - Msg::Direct(2, "%-8.8s: %s", num >= 0 ? SINFO[num].name.c_str() : "Client", - message); - break; - case GmshSocket::ERROR: - Msg::Direct(1, "%-8.8s: %s", num >= 0 ? SINFO[num].name.c_str() : "Client", - message); - break; - case GmshSocket::SPEED_TEST: - Msg::Info("got %d Mb message in %g seconds", - strlen(message) / 1024 / 1024, GetTimeInSeconds() - timer); - break; - default: - Msg::Warning("Unknown type of message received from %s", - num >= 0 ? SINFO[num].name.c_str() : "client"); - Msg::Direct("%-8.8s: %s", num >= 0 ? SINFO[num].name.c_str() : "Client", - message); - break; - } - FlGui::instance()->check(); - } - else{ - Msg::Warning("Failed to receive message body on socket: aborting"); - break; - } - delete [] message; - } - else{ - // didn't get any header, just abort - break; - } - } - - if(num >= 0){ - if(!initOption[0] || !initOption[1] || !initOption[2] || - !initOption[3] || !initOption[4]){ // some options have been changed - for(int i = 0; i < SINFO[num].nboptions; i++) { - FlGui::instance()->solver[num]->choice[i]->clear(); - for(unsigned int j = 0; j < SINFO[num].option[i].size(); j++) - FlGui::instance()->solver[num]->choice[i]->add(SINFO[num].option[i][j].c_str()); - FlGui::instance()->solver[num]->choice[i]->value(0); - } - } - - // only necessary in case of error - SINFO[num].server = 0; - } - - server->Shutdown(); - delete server; - - if(num >= 0){ - Msg::StatusBar(2, false, ""); - } - else{ - Msg::Info("Client disconnected: starting new connection"); - goto new_connection; - } - - return 1; -} diff --git a/Fltk/Solvers.h b/Fltk/Solvers.h deleted file mode 100644 index 062a530ae872e00edb26e6f12ffbe9c22a5c0b8a..0000000000000000000000000000000000000000 --- a/Fltk/Solvers.h +++ /dev/null @@ -1,38 +0,0 @@ -// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle -// -// See the LICENSE.txt file for license information. Please report all -// bugs and problems to <gmsh@geuz.org>. - -#ifndef _SOLVERS_H_ -#define _SOLVERS_H_ - -#include <string> -#include <vector> - -#define MAX_NUM_SOLVERS 5 -#define MAX_NUM_SOLVER_OPTIONS 5 - -class GmshServer; - -typedef struct{ - std::string name, input_name, extension, executable_name; - std::string mesh_name, mesh_command; - std::string socket_command; - std::string name_command; - std::string button_name[MAX_NUM_SOLVER_OPTIONS]; - std::string button_command[MAX_NUM_SOLVER_OPTIONS]; - std::string option_name[MAX_NUM_SOLVER_OPTIONS], option_command; - std::vector<std::string> option[MAX_NUM_SOLVER_OPTIONS]; - int nboptions; - std::string help; - int client_server, popup_messages, merge_views; - int pid; - GmshServer *server; -} SolverInfo ; - -extern SolverInfo SINFO[MAX_NUM_SOLVERS + 1]; - -std::string GetSocketName(int num); -int Solver(int num, const char *args); - -#endif diff --git a/Fltk/aboutWindow.cpp b/Fltk/aboutWindow.cpp index d6cd3d37425ffb5220a824f7eb96b8299759c3af..f8cb8aad424584d03b3eca4384fd3f949e771ed2 100644 --- a/Fltk/aboutWindow.cpp +++ b/Fltk/aboutWindow.cpp @@ -18,17 +18,13 @@ static void help_license_cb(Fl_Widget *w, void *data) { std::string prog = FixWindowsPath(CTX::instance()->webBrowser); - char cmd[1024]; - ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/LICENSE.txt", cmd); - SystemCall(cmd); + SystemCall(ReplacePercentS(prog, "http://geuz.org/gmsh/doc/LICENSE.txt")); } static void help_credits_cb(Fl_Widget *w, void *data) { std::string prog = FixWindowsPath(CTX::instance()->webBrowser); - char cmd[1024]; - ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/CREDITS.txt", cmd); - SystemCall(cmd); + SystemCall(ReplacePercentS(prog, "http://geuz.org/gmsh/doc/CREDITS.txt")); } static void help_hide_cb(Fl_Widget *w, void *data) diff --git a/Fltk/menuWindow.cpp b/Fltk/menuWindow.cpp index e5aedf0f07074b9788354c26db269710ef67b6e3..b674befc1889e4b9ade944f4bf3eb5e51e5452b0 100644 --- a/Fltk/menuWindow.cpp +++ b/Fltk/menuWindow.cpp @@ -10,6 +10,8 @@ #include <FL/fl_ask.H> #include "GmshConfig.h" #include "GmshMessage.h" +#include "GmshRemote.h" +#include "GmshSocket.h" #include "FlGui.h" #include "menuWindow.h" #include "mainWindow.h" @@ -30,7 +32,6 @@ #include "projectionEditor.h" #include "classificationEditor.h" #include "Options.h" -#include "Solvers.h" #include "CommandLine.h" #include "Generator.h" #include "HighOrder.h" @@ -47,7 +48,6 @@ #include "GeoStringInterface.h" #include "Options.h" #include "Context.h" -#include "GmshSocket.h" static void file_new_cb(Fl_Widget *w, void *data) { @@ -152,33 +152,29 @@ static void file_remote_cb(Fl_Widget *w, void *data) if(str == "start"){ Msg::Info("Starting remote Gmsh server"); - if(SINFO[MAX_NUM_SOLVERS].server){ + if(GmshRemote::get(99)->getServer()){ Msg::Error("A server is already running"); } else{ - SINFO[MAX_NUM_SOLVERS].name = "Gmsh Server"; - SINFO[MAX_NUM_SOLVERS].executable_name = "./gmsh"; - SINFO[MAX_NUM_SOLVERS].socket_command = "-socket %s"; - SINFO[MAX_NUM_SOLVERS].nboptions = 0; - SINFO[MAX_NUM_SOLVERS].client_server = 1; - SINFO[MAX_NUM_SOLVERS].popup_messages = 1; - SINFO[MAX_NUM_SOLVERS].merge_views = 1; - Solver(MAX_NUM_SOLVERS, ""); + GmshRemote::get(99)->name = "Gmsh server"; + GmshRemote::get(99)->executable = "./gmsh"; + GmshRemote::get(99)->socketSwitch = "-socket %s"; + GmshRemote::get(99)->run(""); } } else if(str == "stop"){ - if(SINFO[MAX_NUM_SOLVERS].server){ + if(GmshRemote::get(99)->getServer()){ Msg::Info("Stopping remote Gmsh server"); - SINFO[MAX_NUM_SOLVERS].server->SendString(GmshSocket::STOP, "DISCONNECTING!"); + GmshRemote::get(99)->getServer()->SendString(GmshSocket::STOP, "DISCONNECTING!"); } else{ Msg::Error("Cannot stop remote Gmsh: server not running"); } } else if(str == "test"){ - if(SINFO[MAX_NUM_SOLVERS].server){ + if(GmshRemote::get(99)->getServer()){ Msg::Info("Testing remote Gmsh server"); - SINFO[MAX_NUM_SOLVERS].server->SendString(9999, "GENERATE A VIEW!"); + GmshRemote::get(99)->getServer()->SendString(9999, "GENERATE A VIEW!"); } else{ Msg::Error("Cannot test remote Gmsh: server not running"); @@ -522,9 +518,7 @@ static void help_command_line_cb(Fl_Widget *w, void *data) static void help_online_cb(Fl_Widget *w, void *data) { std::string prog = FixWindowsPath(CTX::instance()->webBrowser); - char cmd[1024]; - ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/texinfo/", cmd); - SystemCall(cmd); + SystemCall(ReplacePercentS(prog, "http://geuz.org/gmsh/doc/texinfo/")); } static void help_about_cb(Fl_Widget *w, void *data) @@ -576,9 +570,7 @@ static void geometry_edit_cb(Fl_Widget *w, void *data) { std::string prog = FixWindowsPath(CTX::instance()->editor); std::string file = FixWindowsPath(GModel::current()->getFileName()); - char cmd[1024]; - ReplaceMultiFormat(prog.c_str(), file.c_str(), cmd); - SystemCall(cmd); + SystemCall(ReplacePercentS(prog, file)); } void geometry_reload_cb(Fl_Widget *w, void *data) diff --git a/Fltk/optionWindow.cpp b/Fltk/optionWindow.cpp index 9931a1e7c003df6a96550a6ecfff9c2791c931c7..87fd8894c174455a9d4c745f7f8edc3320107b0f 100644 --- a/Fltk/optionWindow.cpp +++ b/Fltk/optionWindow.cpp @@ -10,6 +10,7 @@ #include "GmshConfig.h" #include "GmshDefines.h" #include "GmshMessage.h" +#include "GmshRemote.h" #include "FlGui.h" #include "optionWindow.h" #include "paletteWindow.h" @@ -17,7 +18,6 @@ #include "extraDialogs.h" #include "drawContext.h" #include "Options.h" -#include "Solvers.h" #include "GModel.h" #include "MElement.h" #include "PView.h" @@ -471,7 +471,7 @@ static void solver_options_ok_cb(Fl_Widget *w, void *data) int old_listen = (int)opt_solver_listen(0, GMSH_GET, o->solver.butt[0]->value()); opt_solver_listen(0, GMSH_SET, o->solver.butt[0]->value()); if(!old_listen && o->solver.butt[0]->value()) - Solver(-1, 0); + GmshRemote::get(-1)->run(""); opt_solver_socket_name(0, GMSH_SET, o->solver.input[0]->value()); diff --git a/Fltk/solverWindow.cpp b/Fltk/solverWindow.cpp index 3d8400f93a3cd8e5b6fe7382fae6ffee483d221a..d2f854eec8b7ccee155ed8f7026e2305e1a58a8a 100644 --- a/Fltk/solverWindow.cpp +++ b/Fltk/solverWindow.cpp @@ -8,20 +8,261 @@ #include <FL/Fl_Box.H> #include <FL/Fl_Return_Button.H> #include <FL/Fl_Browser.H> +#include "GmshMessage.h" +#include "GmshRemote.h" +#include "GmshSocket.h" #include "GModel.h" +#include "PView.h" +#include "OpenFile.h" +#include "drawContext.h" #include "FlGui.h" +#include "menuWindow.h" #include "solverWindow.h" #include "paletteWindow.h" -#include "optionWindow.h" #include "messageWindow.h" #include "fileDialogs.h" -#include "GmshMessage.h" -#include "Solvers.h" #include "StringUtils.h" #include "Options.h" #include "OS.h" #include "Context.h" +class FlGmshServer : public GmshServer{ + private: + GmshRemote *_remote; + public: + FlGmshServer(GmshRemote *remote) : _remote(remote), GmshServer() {} + ~FlGmshServer() {} + int SystemCall(const char *str) + { + return ::SystemCall(str); + } + int NonBlockingWait(int socket, double waitint, double timeout) + { + // This routine polls the socket at least every 'waitint' seconds, + // and for at most timout seconds (or indefinitely if timeout == + // 0). The routine returns 0 as soon as data is available and 1 if + // there was en error or if the process was killed. Otherwise it + // just tends to current GUI events. This is easier to manage than + // non-blocking IO, and simpler than using threads. Another + // possibility would be to use Fl::add_fd(). + double start = GetTimeInSeconds(); + while(1){ + if(timeout > 0 && GetTimeInSeconds() - start > timeout) + return 2; // timout + + if((_remote->executable.size() && _remote->getPid() < 0) || + (_remote->executable.empty() && !CTX::instance()->solver.listen)) + return 1; // process has been killed or we stopped listening + + // check if there is data (call select with a zero timeout to + // return immediately, i.e., do polling) + int ret = Select(0, 0, socket); + + if(ret == 0){ + // nothing available: wait at most waitint seconds, and in the + // meantime respond to FLTK events + FlGui::instance()->wait(waitint); + } + else if(ret > 0){ + return 0; // data is there! + } + else{ + // an error happened + _remote->setPid(-1); + _remote->setServer(0); + return 1; + } + } + } +}; + +void GmshRemote::run(std::string args) +{ + new_connection: + + std::string prog = FixWindowsPath(executable); + + if(!clientServer) { + std::string command = prog + " " + args; +#if !defined(WIN32) + command += " &"; +#endif + SystemCall(command); + return; + } + + _pid = 0; + _server = 0; + FlGmshServer *server = new FlGmshServer(this); + + std::string sockname = getSocketName(); + std::string command; + + if(prog.size()){ + command = prog + " " + args + " " + ReplacePercentS + (socketSwitch, std::string("\"") + sockname + "\""); +#if !defined(WIN32) + command += " &"; +#endif + } + + int sock; + try{ + sock = server->Start(command.c_str(), sockname.c_str(), 5.); + } + catch(const char *err){ + Msg::Error("%s (on socket '%s')", err, sockname.c_str()); + sock = -1; + } + + if(sock < 0){ + server->Shutdown(); + delete server; + return; + } + + Msg::Info("Running '%s'...", name.c_str()); + + bool initOption[5] = {true, true, true, true, true}; + while(1) { + + if((prog.size() && _pid < 0) || + (prog.empty() && !CTX::instance()->solver.listen)) break; + + int stop = server->NonBlockingWait(sock, 0.1, 0.); + + if(stop || (prog.size() && _pid < 0) || + (prog.empty() && !CTX::instance()->solver.listen)) break; + + int type, length; + if(server->ReceiveHeader(&type, &length)){ + double timer = GetTimeInSeconds(); + char *message = new char[length + 1]; + if(server->ReceiveString(length, message)){ + switch (type) { + case GmshSocket::START: + _pid = atoi(message); + _server = server; + break; + case GmshSocket::STOP: + _pid = -1; + _server = 0; + break; + case GmshSocket::PROGRESS: + Msg::StatusBar(2, false, "%s %s", name.c_str(), message); + break; + case GmshSocket::OPTION_1: + if(initOption[0]){ + optionValue[0].clear(); + initOption[0] = false; + } + optionValue[0].push_back(message); + break; + case GmshSocket::OPTION_2: + if(initOption[1]){ + optionValue[1].clear(); + initOption[1] = false; + } + optionValue[1].push_back(message); + break; + case GmshSocket::OPTION_3: + if(initOption[2]){ + optionValue[2].clear(); + initOption[2] = false; + } + optionValue[2].push_back(message); + break; + case GmshSocket::OPTION_4: + if(initOption[3]){ + optionValue[3].clear(); + initOption[3] = false; + } + optionValue[3].push_back(message); + break; + case GmshSocket::OPTION_5: + if(initOption[4]){ + optionValue[4].clear(); + initOption[4] = false; + } + optionValue[4].push_back(message); + break; + case GmshSocket::MERGE_FILE: + if(mergeViews) { + int n = PView::list.size(); + MergeFile(message); + drawContext::global()->draw(); + if(n != (int)PView::list.size()) + FlGui::instance()->menu->setContext(menu_post, 0); + } + break; + case GmshSocket::PARSE_STRING: + ParseString(message); + drawContext::global()->draw(); + break; + case GmshSocket::INFO: + Msg::Direct("%-8.8s: %s", name.c_str(), message); + break; + case GmshSocket::WARNING: + Msg::Direct(2, "%-8.8s: %s", name.c_str(), message); + break; + case GmshSocket::ERROR: + Msg::Direct(1, "%-8.8s: %s", name.c_str(), message); + break; + case GmshSocket::SPEED_TEST: + Msg::Info("got %d Mb message in %g seconds", strlen(message) + / 1024 / 1024, GetTimeInSeconds() - timer); + break; + default: + Msg::Warning("Unknown message type"); + Msg::Direct("%-8.8s: %s", name.c_str(), message); + break; + } + FlGui::instance()->check(); + } + else{ + Msg::Warning("Failed to receive message body on socket: aborting"); + break; + } + delete [] message; + } + else{ + // didn't get any header, just abort + break; + } + } + + if(!initOption[0] || !initOption[1] || !initOption[2] || !initOption[3] || + !initOption[4]){ // some options have been changed + // find solver num + int num = 0; + for(std::map<int, GmshRemote*>::iterator it = _all.begin(); + it != _all.end(); it++){ + if(this == it->second) break; + num++; + } + if(num >= 0 && num < 5){ + for(unsigned int i = 0; i < optionName.size(); i++) { + if(optionName[i].empty()) break; + FlGui::instance()->solver[num]->choice[i]->clear(); + for(unsigned int j = 0; j < optionValue[i].size(); j++) + FlGui::instance()->solver[num]->choice[i]->add(optionValue[i][j].c_str()); + FlGui::instance()->solver[num]->choice[i]->value(0); + } + } + } + + _server = 0; + server->Shutdown(); + delete server; + + Msg::Info("...done running '%s'", name.c_str()); + + if(prog.empty()){ + Msg::Info("Client disconnected: starting new connection"); + goto new_connection; + } +} + void solver_cb(Fl_Widget *w, void *data) { int num = (int)(long)data; @@ -31,9 +272,12 @@ void solver_cb(Fl_Widget *w, void *data) // if the input file field is empty, fill it with a name guessed // from the current model name (only if this file actually exists) if(!strlen(FlGui::instance()->solver[num]->input[0]->value())){ - std::string inputFile = split[0] + split[1] + SINFO[num].extension; - if(!StatFile(inputFile)) + std::string inputFile = split[0] + split[1] + + GmshRemote::get(num)->inputFileExtension; + if(!StatFile(inputFile)){ FlGui::instance()->solver[num]->input[0]->value(inputFile.c_str()); + GmshRemote::get(num)->inputFileName = inputFile; + } } // if the mesh file field is empty, fill it with a name guessed with @@ -41,41 +285,66 @@ void solver_cb(Fl_Widget *w, void *data) if(!strlen(FlGui::instance()->solver[num]->input[1]->value())){ std::string meshFile = split[0] + split[1] + ".msh"; FlGui::instance()->solver[num]->input[1]->value(meshFile.c_str()); + GmshRemote::get(num)->meshFileName = meshFile; } // show the window before calling Solver() to avoid race condition on - // Windows (if the message window pops up die to an error, the window + // Windows (if the message window pops up due to an error, the window // callbacks get messed up) FlGui::instance()->solver[num]->win->show(); - std::string inputFile(FlGui::instance()->solver[num]->input[0]->value()); - if(SINFO[num].nboptions && inputFile.size()) { - std::string file = FixWindowsPath(inputFile); - char tmp[256], tmp2[256]; - sprintf(tmp, "\"%s\"", file.c_str()); - sprintf(tmp2, SINFO[num].name_command.c_str(), tmp); - sprintf(tmp, "%s %s", SINFO[num].option_command.c_str(), tmp2); - Solver(num, tmp); + GmshRemote::get(num)->runToGetOptions(); +} + +static void solver_ok_cb(Fl_Widget *w, void *data) +{ + int num = (int)(long)data; + + opt_solver_client_server + (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[0].value() ? 1 : 0); + opt_solver_popup_messages + (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[1].value() ? 1 : 0); + opt_solver_merge_views + (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[2].value() ? 1 : 0); + + opt_solver_mesh_name + (num, GMSH_SET, FlGui::instance()->solver[num]->input[1]->value()); + + bool retry = false; + + std::string input = FlGui::instance()->solver[num]->input[0]->value(); + if(opt_solver_input_name(num, GMSH_GET, "") != input) retry = true; + opt_solver_input_name(num, GMSH_SET, input); + + std::string exe = FlGui::instance()->solver[num]->input[2]->value(); + if(opt_solver_executable(num, GMSH_GET, "") != exe) retry = true; + opt_solver_executable(num, GMSH_SET, exe); + + if(retry) solver_cb(0, data); +} + +static void solver_choose_executable_cb(Fl_Widget *w, void *data) +{ + int num = (int)(long)data; + std::string pattern = "*"; +#if defined(WIN32) + pattern += ".exe"; +#endif + + if(file_chooser(0, 0, "Choose", pattern.c_str())){ + FlGui::instance()->solver[num]->input[2]->value(file_chooser_get_name(1).c_str()); + solver_ok_cb(w, data); } } static void solver_file_open_cb(Fl_Widget *w, void *data) { - char tmp[256], tmp2[256]; int num = (int)(long)data; - sprintf(tmp, "*%s", SINFO[num].extension.c_str()); + std::string pattern = "*" + GmshRemote::get(num)->inputFileExtension; - // We allow to create the .pro file... Or should we add a "New file" - // button? - if(file_chooser(0, 0, "Choose", tmp)) { + if(file_chooser(0, 0, "Choose", pattern.c_str())) { FlGui::instance()->solver[num]->input[0]->value(file_chooser_get_name(1).c_str()); - if(SINFO[num].nboptions) { - std::string file = FixWindowsPath(file_chooser_get_name(1)); - sprintf(tmp, "\"%s\"", file.c_str()); - sprintf(tmp2, SINFO[num].name_command.c_str(), tmp); - sprintf(tmp, "%s %s", SINFO[num].option_command.c_str(), tmp2); - Solver(num, tmp); - } + solver_ok_cb(w, data); } } @@ -84,129 +353,72 @@ static void solver_file_edit_cb(Fl_Widget *w, void *data) int num = (int)(long)data; std::string prog = FixWindowsPath(CTX::instance()->editor); std::string file = FixWindowsPath(FlGui::instance()->solver[num]->input[0]->value()); - char cmd[1024]; - ReplaceMultiFormat(prog.c_str(), file.c_str(), cmd); - SystemCall(cmd); + SystemCall(ReplacePercentS(prog, file)); } static void solver_choose_mesh_cb(Fl_Widget *w, void *data) { int num = (int)(long)data; - if(file_chooser(0, 0, "Choose", "*")) + if(file_chooser(0, 0, "Choose", "*.msh")){ FlGui::instance()->solver[num]->input[1]->value(file_chooser_get_name(1).c_str()); + solver_ok_cb(w, data); + } } -static int nbs(const char *str) +static int numPercentS(std::string &in) { - int i, nb = 0; - for(i = 0; i < (int)strlen(str) - 1; i++) { - if(str[i] == '%' && str[i + 1] == 's') { - nb++; + int n = 0; + for(int i = 0; i < in.size() - 1; i++) { + if(in[i] == '%' && in[i + 1] == 's') { i++; + n++; } } - return nb; + return n; } static void solver_command_cb(Fl_Widget *w, void *data) { - char tmp[256], mesh[256], arg[512], command[256]; int num = ((int *)data)[0]; int idx = ((int *)data)[1]; - int usedopts = 0; - if(SINFO[num].popup_messages) + if(GmshRemote::get(num)->popupMessages) FlGui::instance()->messages->show(true); - if(strlen(FlGui::instance()->solver[num]->input[1]->value())) { - std::string m = FixWindowsPath(FlGui::instance()->solver[num]->input[1]->value()); - sprintf(tmp, "\"%s\"", m.c_str()); - sprintf(mesh, SINFO[num].mesh_command.c_str(), tmp); - } - else { - strcpy(mesh, ""); - } + GmshRemote::get(num)->inputFileName = + FlGui::instance()->solver[num]->input[0]->value(); - if(nbs(SINFO[num].button_command[idx].c_str())) { - for(int i = 0; i < idx; i++) - usedopts += nbs(SINFO[num].button_command[i].c_str()); - if(usedopts > SINFO[num].nboptions) { - Msg::Error("Missing options to execute command"); - return; - } - int val = FlGui::instance()->solver[num]->choice[usedopts]->value(); - if(val >= 0 && val < (int)SINFO[num].option[usedopts].size()) - sprintf(command, SINFO[num].button_command[idx].c_str(), - SINFO[num].option[usedopts][val].c_str()); - else{ - strcpy(command, ""); - } - } - else { - strcpy(command, SINFO[num].button_command[idx].c_str()); - } + GmshRemote::get(num)->meshFileName = + FlGui::instance()->solver[num]->input[1]->value(); - std::string c = FixWindowsPath(FlGui::instance()->solver[num]->input[0]->value()); - sprintf(arg, "\"%s\"", c.c_str()); - sprintf(tmp, SINFO[num].name_command.c_str(), arg); - sprintf(arg, "%s %s %s", tmp, mesh, command); - Solver(num, arg); -} + int optionIndex = 0; + for(int i = 0; i < idx; i++) + optionIndex += numPercentS(GmshRemote::get(num)->buttonSwitch[i]); -static void solver_kill_cb(Fl_Widget *w, void *data) -{ - int num = (int)(long)data; - if(SINFO[num].pid > 0) { - if(KillProcess(SINFO[num].pid)) - Msg::Info("Killed %s pid %d", SINFO[num].name.c_str(), SINFO[num].pid); - } - SINFO[num].pid = -1; + int optionChoice = FlGui::instance()->solver[num]->choice[optionIndex]->value(); + GmshRemote::get(num)->runCommand(idx, optionIndex, optionChoice); } -static void solver_ok_cb(Fl_Widget *w, void *data) -{ - int retry = 0, num = (int)(long)data; - - opt_solver_client_server - (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[0].value() ? 1 : 0); - opt_solver_popup_messages - (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[1].value() ? 1 : 0); - opt_solver_merge_views - (num, GMSH_SET, FlGui::instance()->solver[num]->menu->menu()[2].value() ? 1 : 0); - - const char *exe = FlGui::instance()->solver[num]->input[2]->value(); - if(strcmp(opt_solver_executable(num, GMSH_GET, "").c_str(), exe)) - retry = 1; - opt_solver_executable(num, GMSH_SET, exe); - if(retry) - solver_cb(0, data); -} - -static void solver_choose_executable_cb(Fl_Widget *w, void *data) +static void solver_kill_cb(Fl_Widget *w, void *data) { int num = (int)(long)data; - if(file_chooser(0, 0, "Choose", -#if defined(WIN32) - "*.exe" -#else - "*" -#endif - )){ - FlGui::instance()->solver[num]->input[2]->value(file_chooser_get_name(1).c_str()); - solver_ok_cb(w, data); - } + GmshRemote::get(num)->kill(); } solverWindow::solverWindow(int solverIndex, int deltaFontSize) { FL_NORMAL_SIZE -= deltaFontSize; - for(int i = 0; i < MAX_NUM_SOLVER_OPTIONS; i++) - if(SINFO[solverIndex].option_name[i].size()) - SINFO[solverIndex].nboptions = i + 1; + int numOptions = GmshRemote::get(solverIndex)->optionName.size(); + for(int i = 0; i < GmshRemote::get(solverIndex)->optionName.size(); i++){ + if(GmshRemote::get(solverIndex)->optionName[i].empty()){ + numOptions = i; + break; + } + } int width = 32 * FL_NORMAL_SIZE; - int height = (5 + SINFO[solverIndex].nboptions) * BH + 5 * WB; + int height = (5 + numOptions) * BH + 5 * WB; int BBS = (width - 9 * WB) / 6; int LL = width - (int)(2.75 * BBS); @@ -238,14 +450,19 @@ solverWindow::solverWindow(int solverIndex, int deltaFontSize) Fl_Button *b4 = new Fl_Button (2 * WB, 2 * WB + 2 * BH, BBS, BH, "Edit"); b4->callback(solver_file_edit_cb, (void *)solverIndex); + input[0] = new Fl_Input (2 * WB + BBS, 2 * WB + 2 * BH, LL - BBS, BH, "Input file"); + input[0]->callback(solver_ok_cb, (void *)solverIndex); + Fl_Button *b3 = new Fl_Button (width - 2 * WB - BBS, 2 * WB + 2 * BH, BBS, BH, "Choose"); b3->callback(solver_file_open_cb, (void *)solverIndex); input[1] = new Fl_Input (2 * WB, 2 * WB + 3 * BH, LL, BH, "Mesh file"); + input[1]->callback(solver_ok_cb, (void *)solverIndex); + Fl_Button *b5 = new Fl_Button (width - 2 * WB - BBS, 2 * WB + 3 * BH, BBS, BH, "Choose"); b5->callback(solver_choose_mesh_cb, (void *)solverIndex); @@ -254,29 +471,28 @@ solverWindow::solverWindow(int solverIndex, int deltaFontSize) input[i]->align(FL_ALIGN_RIGHT); } - for(int i = 0; i < SINFO[solverIndex].nboptions; i++) { + for(int i = 0; i < numOptions; i++) { choice[i] = new Fl_Choice - (2 * WB, 2 * WB + (4 + i) * BH, LL, BH, SINFO[solverIndex].option_name[i].c_str()); + (2 * WB, 2 * WB + (4 + i) * BH, LL, BH, + GmshRemote::get(solverIndex)->optionName[i].c_str()); choice[i]->align(FL_ALIGN_RIGHT); } - static int arg[MAX_NUM_SOLVERS][5][2]; - for(int i = 0; i < 5; i++) { - if(SINFO[solverIndex].button_name[i].size()) { + static int arg[5][5][2]; + for(int i = 0; i < GmshRemote::get(solverIndex)->buttonName.size(); i++) { + if(GmshRemote::get(solverIndex)->buttonName[i].size()){ arg[solverIndex][i][0] = solverIndex; arg[solverIndex][i][1] = i; command[i] = new Fl_Button - ((2 + i) * WB + i * BBS, 3 * WB + (4 + SINFO[solverIndex].nboptions) * BH, - BBS, BH, SINFO[solverIndex].button_name[i].c_str()); - command[i]->callback - (solver_command_cb, (void *)arg[solverIndex][i]); + ((2 + i) * WB + i * BBS, 3 * WB + (4 + numOptions) * BH, + BBS, BH, GmshRemote::get(solverIndex)->buttonName[i].c_str()); + command[i]->callback(solver_command_cb, (void *)arg[solverIndex][i]); } } { Fl_Button *b = new Fl_Button - (width - 2 * WB - BBS, 3 * WB + (4 + SINFO[solverIndex].nboptions) * BH, - BBS, BH, "Kill"); + (width - 2 * WB - BBS, 3 * WB + (4 + numOptions) * BH, BBS, BH, "Kill"); b->callback(solver_kill_cb, (void *)solverIndex); } @@ -289,16 +505,19 @@ solverWindow::solverWindow(int solverIndex, int deltaFontSize) Fl_Browser *o = new Fl_Browser (2 * WB, 2 * WB + 1 * BH, width - 4 * WB, height - 4 * WB - BH); o->add(" "); - add_multiline_in_browser(o, "@c@b@.", SINFO[solverIndex].name.c_str(), false); + add_multiline_in_browser + (o, "@c@b@.", GmshRemote::get(solverIndex)->name.c_str(), false); o->add(" "); - add_multiline_in_browser(o, "@c@. ", SINFO[solverIndex].help.c_str(), false); + add_multiline_in_browser + (o, "@c@. ", GmshRemote::get(solverIndex)->help.c_str(), false); g->end(); } o->end(); } - win->position(CTX::instance()->solverPosition[0], CTX::instance()->solverPosition[1]); + win->position + (CTX::instance()->solverPosition[0], CTX::instance()->solverPosition[1]); win->end(); FL_NORMAL_SIZE += deltaFontSize;