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;