[giv] Remote control work in both client and server. Introduction of pick_coordinate command.



commit f165f3b2b5bbd4b78b2bcff9264bfa20d421ba8e
Author: Dov Grobgeld <dov grobgeld gmail com>
Date:   Thu Jan 5 00:30:36 2012 +0200

    Remote control work in both client and server. Introduction of pick_coordinate command.

 ChangeLog                              |    4 +
 SConstruct                             |    4 +-
 configure.in                           |    2 +-
 doc/giv.html                           |   44 ++++++++-
 doc/giv.phtml                          |   43 ++++++++-
 notes.org                              |   19 ++++
 notes.txt                              |   16 ---
 python/giv-client-pick-coordinate.py   |   16 +++
 python/giv-client.py                   |   28 ++----
 src/giv-win.gob                        |  177 ++++++++++++++++++++++++++++----
 src/giv.cc                             |    1 +
 src/glib-jsonrpc/glib-jsonrpc-json.c   |   18 +++-
 src/glib-jsonrpc/glib-jsonrpc-json.h   |    5 +
 src/glib-jsonrpc/glib-jsonrpc-server.c |  121 ++++++++++++++++------
 src/glib-jsonrpc/glib-jsonrpc-server.h |   10 ++-
 15 files changed, 406 insertions(+), 102 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 481eb03..2d574d3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2012-01-05  Dov Grobgeld  <dov grobgeld gmail com>
+
+        * configure.in: Bumped version to 0.1.21.
+
 2011-12-27  Dov Grobgeld  <dov grobgeld gmail com>
 
         * python/giv-client-marks.py : An example of how to use the new jsonrpc command "giv_string".
diff --git a/SConstruct b/SConstruct
index 4f9ab96..e7cb104 100644
--- a/SConstruct
+++ b/SConstruct
@@ -87,7 +87,7 @@ if ARGUMENTS.get('mingw', 0):
                  "src/plugins/npy.dll",
                  ],
                 ["makensis giv.wine.nsi"])
-    env.Append(LINKFLAGS=['-mwindows'])
+    env.Append(LINKFLAGS=[])
 
     # TBD - make this installation dependent
     env['PACKAGE_DOC_DIR'] = '../doc'
@@ -126,7 +126,7 @@ env.Append(CPPPATH=[],
            LIBS=['gtkimageviewer_local', 'agg', 'glib-jsonrpc_local']
            )
 
-env.ParseConfig("${PKGCONFIG} --cflags --libs gtk+-2.0 glib-2.0 json-glib-1.0 gio-2.0")
+env.ParseConfig("${PKGCONFIG} --cflags --libs gtk+-2.0 glib-2.0 json-glib-1.0 gio-2.0 gthread-2.0")
 
 SConscript(['src/SConscript',
             'doc/SConscript',
diff --git a/configure.in b/configure.in
index 1cab855..7884f71 100644
--- a/configure.in
+++ b/configure.in
@@ -5,7 +5,7 @@ AM_CONFIG_HEADER(config.h)
 PACKAGE=givwidget
 GIVWIDGET_API_VERSION=2.0
 
-AM_INIT_AUTOMAKE(giv, 0.9.21beta)
+AM_INIT_AUTOMAKE(giv, 0.9.21)
 
 dnl Use libtool to get shared libraries
 LT_PREREQ
diff --git a/doc/giv.html b/doc/giv.html
index a58b4fa..c273f9a 100644
--- a/doc/giv.html
+++ b/doc/giv.html
@@ -18,7 +18,7 @@
 </center>
 <h2 align=center>
     <img src="giv-logo-150.png"><br>
-    <font size=+2>Version 0.9.20</font><br>
+    <font size=+2>Version 0.9.21</font><br>
     <font size=-1>Dov Grobgeld</font><br>
     <font size=-1>Homepage: <a href="http://giv.sourceforge.net/giv";>http://giv.sourceforge.net/giv</a></font><br>
     <font size=-1><a href="mailto:dov grobgeld gmail com">dov grobgeld gmail com</a></font><br>
@@ -494,8 +494,49 @@ loaders for the following formats:
 <li>Dicom
 <li>FITS
 <li>Tiff
+<li>npy (Numerical python)
 </ul>
 
+<h3><a name="SEC4.1">4.1. Remote access protocol</a></h3>
+
+giv can be remote controlled through json-rpc commands. The parameters of the commands are always given in an array.
+
+<ul>
+<li>load_file FILENAME - Load a file into giv.
+<li>giv_string GIVSTRING - Change the giv annotation.
+<li>pick_coordinate - Giv will change its cursor and wait for the user to click on a coordinate. The coordinate is returned.
+</ul>
+
+Here is an example in python calling the <b>pick_coordinate</b> command:
+<blockquote>
+<pre>
+import httplib,json
+
+conn = httplib.HTTPConnection('localhost:8222')
+
+request = {'method':"pick_coordinate",
+           'params':[]} 
+
+conn.request("POST", url='', body= json.dumps(request))
+response = json.loads(conn.getresponse().read())
+if 'error' in response:
+  print response['error']['message']
+else:
+  print response['result']
+</pre>
+</blockquote>
+<p>
+giv also contains an embedded JSONRPC client that come in two forms, the simplified and the full forms.
+<p>
+The simplified form splits the arguments on whitespace and takes the first argument as the name of the remote method and the rest as the parameters.
+<pre>
+giv -remote "load_file /home/dov/pictures/maja.pgm"
+</pre>
+The full forms allows specifying the parameters in json syntax. The following command is equivalent to the above code:
+<pre>
+./giv -json_method load_file -json_params '["/home/dov/github/giv/examples/lena.pgm"]'
+</pre>
+
 <h2><a name="SEC5">5. Future plans</a></h2>
 
 <blockquote>
@@ -506,6 +547,5 @@ loaders for the following formats:
     <LI> Create a property window to show meta data. E.g. data from a dicom file. </LI>
     <LI> Speed up dealing with huge datasets. </LI>
     <LI> Create a measuring tool for measuring areas, histograms, etc. </LI>
-    <LI> json-rpc interface for remote control. </LI>
   </UL>  
 </blockquote>
diff --git a/doc/giv.phtml b/doc/giv.phtml
index aa9cff2..525c6d9 100644
--- a/doc/giv.phtml
+++ b/doc/giv.phtml
@@ -94,7 +94,7 @@ sub code_snippet {   # usage: code_snippet($filename, title)
 </center>
 <h2 align=center>
     <img src="giv-logo-150.png"><br>
-    <font size=+2>Version 0.9.20</font><br>
+    <font size=+2>Version 0.9.21</font><br>
     <font size=-1>Dov Grobgeld</font><br>
     <font size=-1>Homepage: <a href="http://giv.sourceforge.net/giv";>http://giv.sourceforge.net/giv</a></font><br>
     <font size=-1><a href="mailto:dov grobgeld gmail com">dov grobgeld gmail com</a></font><br>
@@ -413,8 +413,48 @@ loaders for the following formats:
 <li>Dicom
 <li>FITS
 <li>Tiff
+<li>npy (Numerical python)
 </ul>
 
+<: subsection("Remote access protocol") :>
+giv can be remote controlled through json-rpc commands. The parameters of the commands are always given in an array.
+
+<ul>
+<li>load_file FILENAME - Load a file into giv.
+<li>giv_string GIVSTRING - Change the giv annotation.
+<li>pick_coordinate - Giv will change its cursor and wait for the user to click on a coordinate. The coordinate is returned.
+</ul>
+
+Here is an example in python calling the <b>pick_coordinate</b> command:
+<blockquote>
+<pre>
+import httplib,json
+
+conn = httplib.HTTPConnection('localhost:8222')
+
+request = {'method':"pick_coordinate",
+           'params':[]} 
+
+conn.request("POST", url='', body= json.dumps(request))
+response = json.loads(conn.getresponse().read())
+if 'error' in response:
+  print response['error']['message']
+else:
+  print response['result']
+</pre>
+</blockquote>
+<p>
+giv also contains an embedded JSONRPC client that come in two forms, the simplified and the full forms.
+<p>
+The simplified form splits the arguments on whitespace and takes the first argument as the name of the remote method and the rest as the parameters.
+<pre>
+giv -remote "load_file /home/dov/pictures/maja.pgm"
+</pre>
+The full forms allows specifying the parameters in json syntax. The following command is equivalent to the above code:
+<pre>
+./giv -json_method load_file -json_params '["/home/dov/github/giv/examples/lena.pgm"]'
+</pre>
+
 <: section("Future plans") :>
 <blockquote>
   Here is a list of possible future development. The direction taken
@@ -424,6 +464,5 @@ loaders for the following formats:
     <LI> Create a property window to show meta data. E.g. data from a dicom file. </LI>
     <LI> Speed up dealing with huge datasets. </LI>
     <LI> Create a measuring tool for measuring areas, histograms, etc. </LI>
-    <LI> json-rpc interface for remote control. </LI>
   </UL>  
 </blockquote>
diff --git a/notes.org b/notes.org
new file mode 100644
index 0000000..6279753
--- /dev/null
+++ b/notes.org
@@ -0,0 +1,19 @@
+#+startup: hidestars
+* <2010-10-30 Sat>
+** Maemo installation
+  - Don't install "complex plugins".
+  - libpcre3 dependancy.
+  - Have to get around vala and gob2 dependancy by adding generated files to git!
+  - autoconf is badly broken.
+  - Created if's in scons to not run vala or gob2 in sbox.
+  - Optify requires:
+#+begin_src sh
+ cd /usr/lib
+ ln -s /opt/maemo/usr/lib/libgiv-image.so 
+ cd /usr/bin
+ ln -s /opt/maemo/usr/bin/giv
+#+end_src
+
+* <2011-12-30 Fri>
+** Async commands
+   - Something is wrong with async communication as there is no handle to the query but only to the server. What if there are several queries done simultanously. How would the server differentiate between them?
diff --git a/python/giv-client-pick-coordinate.py b/python/giv-client-pick-coordinate.py
new file mode 100644
index 0000000..c48f3bf
--- /dev/null
+++ b/python/giv-client-pick-coordinate.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+import httplib,json
+
+conn = httplib.HTTPConnection('localhost:8222')
+
+request = {'method':"pick_coordinate",
+           'params':[]} 
+
+conn.request("POST", url='', body= json.dumps(request))
+response = json.loads(conn.getresponse().read())
+if 'error' in response:
+  print response['error']['message']
+else:
+  print response['result']
+  
diff --git a/python/giv-client.py b/python/giv-client.py
index 2d55198..2c9e452 100644
--- a/python/giv-client.py
+++ b/python/giv-client.py
@@ -1,26 +1,12 @@
 #!/usr/bin/python
-"""
-An example of how to create an external jsonrpc client for controlling
-giv.
-"""
 
-import httplib
-import random
+import httplib,json
 
 conn = httplib.HTTPConnection('localhost:8222')
-headers = {"Content-type": "application/x-www-form-urlencoded",
-           "Accept": "text/plain",
-           }
-example_dir = "/home/dov/github/giv/examples/"
-#example_dir = "c:/progra~1/giv/examples"
 
-for i in range(10):
-  request = ('{"jsonrpc": "2.0", '
-             '"method": "load_file", '
-             '"params": [\"'+example_dir+'/%s"], '
-             '"id": %d}'%(['lena.pgm','maja.pgm'][i%2],
-                          random.randint(1,100)))
-  
-  conn.request("POST", url="", body= request, headers=headers)
-  response = conn.getresponse()
-  print response.read()
+request = {'method':"load_file",
+           'params':['/home/dov/github/giv/examples/lena.pgm']} # Full path!
+
+conn.request("POST", url='', body= json.dumps(request))
+response = json.loads(conn.getresponse().read())['result']
+print response
diff --git a/src/giv-win.gob b/src/giv-win.gob
index 2c79773..0390252 100644
--- a/src/giv-win.gob
+++ b/src/giv-win.gob
@@ -245,6 +245,7 @@ my_lasso_draw_rectangle(cairo_t *cr,
                         DovtkLassoContext context,
                         gpointer user_data);
 static void create_remote_commands(GivWin *self);
+static void giv_win_json_pick_reply(GivWin *self, const gchar *error, double x, double y);
 
 // The following tables contain all the actions for both the menubar
 // and the popup menus.
@@ -538,9 +539,11 @@ class Giv:Win from Gtk:Window
     private double measure_x2=-1;
     private double measure_y2=-1;
     private double quiver_scale = 1.0;
+    private GdkCursor *current_cursor=0;
     private GdkCursor *cursor_plus=0;
-    private GtkWidget *w_color_map_bar = NULL;
+    private GdkCursor *cursor_diamond_cross = NULL;
     private GdkCursor *cursor_default=0;
+    private GtkWidget *w_color_map_bar = NULL;
     private gboolean is_dragging = FALSE;
     private GtkPrintSettings *print_settings = NULL;
     private gchar *img_comment = NULL;
@@ -553,6 +556,8 @@ class Giv:Win from Gtk:Window
     private double last_measure_distance_in_pixels = -1;
     private GivSettings *giv_settings = NULL destroy { g_object_unref(giv_settings); };
     private GLibJsonRpcServer *jsonrpc_server;
+    private GLibJsonRpcAsyncQuery *json_query = NULL;
+    private gboolean do_pick_coordinate = FALSE;
 
     public GtkWidget *
     new (int argc, char *argv[])
@@ -573,6 +578,9 @@ class Giv:Win from Gtk:Window
         int giv_port = GIV_DEFAULT_PORT;
         gboolean do_remote = FALSE;
         const gchar *giv_host = "localhost"; // Currently fixed
+        gchar *remote_string=NULL;
+        gchar *json_method=NULL;
+        gchar *json_params_string=NULL;
 
         // Parse command line
         while(argp < argc && argv[argp][0] == '-') {
@@ -628,6 +636,18 @@ class Giv:Win from Gtk:Window
             CASE("-remote")
               {
                 do_remote = TRUE;
+                remote_string= argv[argp++];
+                continue;
+              }
+            CASE("-json_method")
+              {
+                do_remote = TRUE;
+                json_method = g_strdup(argv[argp++]);
+                continue;
+              }
+            CASE("-json_params")
+              {
+                json_params_string = argv[argp++];
                 continue;
               }
 
@@ -644,25 +664,41 @@ class Giv:Win from Gtk:Window
 
             if (client)
               {
-                // assume a single image
-                JsonNode *params = glib_jsonrpc_json_csv_to_json_array(argv[argp++]);
+                JsonNode *params = NULL;
+                if (!json_method)
+                  {
+                    // Either split remote string as space separated by params
+                    char **json_args = g_strsplit(remote_string," ",-1);
+                    json_method = g_strdup(json_args[0]);
+                    char **json_params = &json_args[1];
+                    params = glib_jsonrpc_json_strv_to_json_array(json_params);
+                    g_strfreev(json_args);
+                  }
+                else if (json_params_string)
+                  params = glib_jsonrpc_json_string_to_json_node(json_params_string);
+
                 JsonNode *response = NULL;
-                int status = glib_jsonrpc_client_call(client,
-                                                      "load_file",
-                                                      params,
-                                                      // output
-                                                      &response);
+                glib_jsonrpc_client_call(client,
+                                         json_method,
+                                         params,
+                                         // output
+                                         &response);
+                g_free(json_method);
                 json_node_free(params);
                 if (response)
-                  json_node_free(response);
-                if (status==0)
-                  exit(0);
+                  {
+                    gchar *str = glib_jsonrpc_json_to_string(response);
+                    printf("%s\n",str);
+                    g_free(str);
+                    json_node_free(response);
+                  }
+                exit(0);
               }
 
             // Fall through and run giv normally!
           }
             
-
+        selfp->cursor_plus = gdk_cursor_new(GDK_PLUS);
         selfp->cb_names = g_ptr_array_new();
         selfp->filename_list = g_ptr_array_new();
         selfp->pixelsize_unit = g_strdup("");
@@ -1576,6 +1612,16 @@ cb_button_press_event (GtkWidget * widget,
 
     giv_widget_popdown_balloon(GIV_WIDGET(selfp->w_imgv));
 
+    if (event->button == 1 && selfp->do_pick_coordinate) {
+        double world_x, world_y;
+        gtk_image_viewer_canv_coord_to_img_coord(GTK_IMAGE_VIEWER(selfp->w_imgv),
+                                                 event->x, event->y,
+                                                 &world_x, &world_y);
+        giv_win_json_pick_reply(self, NULL, world_x, world_y);
+
+        return true;
+    }
+
     if (event->button == 1) {
         // check if are in measure mode ('z')
         if (selfp->is_measuring_distance
@@ -1676,6 +1722,11 @@ cb_key_press_event (GtkWidget * widget,
         giv_win_shrink_wrap(self);
         return 1;
     }
+    if (k==GDK_Escape)
+      {
+        if (selfp->do_pick_coordinate)
+          giv_win_json_pick_reply(self, "Aborted", -1,-1);
+      }
     if (// reload
         k == GDK_KP_Enter
         || k == GDK_Return
@@ -2363,6 +2414,7 @@ cb_menu_count_marks (GtkAction *action, gpointer data)
         dovtk_lasso_destroy(selfp->lasso);
         selfp->lasso = NULL;
         gdk_window_set_cursor(selfp->w_imgv->window, NULL);
+        selfp->current_cursor = NULL;
     }
   
     return;
@@ -2381,12 +2433,7 @@ cb_menu_measure_distance (GtkAction *action, gpointer data)
         selfp->lasso = dovtk_lasso_create(selfp->w_imgv,
                                           &my_lasso_draw,
                                           self);
-#if 0
-        if (!selfp->cursor_plus) {
-            selfp->cursor_plus = gdk_cursor_new(GDK_PLUS);
-        }
-        gdk_window_set_cursor(selfp->w_imgv->window, selfp->cursor_plus);
-#endif
+        //        gdk_window_set_cursor(selfp->w_imgv->window, selfp->cursor_plus);
     }
     else {
         //free_giv_backstore (selfp->back_store);
@@ -4159,14 +4206,43 @@ static int cmd_giv_string(GLibJsonRpcServer *server,
   GivWin *self = (GivWin*)user_data;
   *response = json_node_new(JSON_NODE_VALUE);
   JsonReader *reader = json_reader_new(params);
+  bool do_append = false;
+
+  int nelem = json_reader_count_elements(reader);
+  if (nelem<1)
+    {
+      json_node_set_string(*response, "Error! Not enough parameters");
+      return -1;
+    }
+
+  int i;
+  const char *S_;
+
+  // Parse arguments that start with -
+  for (i=0; i<nelem; i++)
+    {
+      json_reader_read_element(reader, 0);
+      S_ = json_reader_get_string_value(reader);
+      json_reader_end_element (reader);
+
+      if (S_[0]!='-')
+        break;
+      
+      // Parse the argument
+      CASE("-append")
+        {
+          do_append=true;
+          continue;
+        }
+      // Ignore unknown parameters
+    }
   
-  // Assume array at the moment
-  json_reader_read_element(reader, 0);
-  const char *giv_string = json_reader_get_string_value(reader);
+  const char *giv_string = S_;
 
   // Erase the old giv string
   // TBD, make this configurable?
-  giv_widget_clear_giv(GIV_WIDGET(selfp->w_imgv));
+  if (!do_append)
+      giv_widget_clear_giv(GIV_WIDGET(selfp->w_imgv));
   giv_widget_add_giv_from_string(GIV_WIDGET(selfp->w_imgv), giv_string);
 
   g_object_unref(reader);
@@ -4176,6 +4252,59 @@ static int cmd_giv_string(GLibJsonRpcServer *server,
   return 0;
 }
 
+static gboolean cb_idle_set_cursor(gpointer user_data)
+{
+  GivWin *self = (GivWin*)user_data;
+  gdk_window_set_cursor(selfp->w_imgv->window, selfp->current_cursor);
+  return false;
+}
+
+static int cmd_pick_coordinate(GLibJsonRpcServer *server,
+                               GLibJsonRpcAsyncQuery *query,
+                               const char *method,
+                               JsonNode *params,
+                               gpointer user_data)
+{
+  GivWin *self = (GivWin*)user_data;
+  selfp->do_pick_coordinate = true;
+  selfp->json_query = query;
+  selfp->current_cursor = selfp->cursor_plus;
+  g_idle_add(cb_idle_set_cursor, self);
+  
+  return 0;
+}
+
+static void giv_win_json_pick_reply(GivWin *self, const gchar *error, double x, double y)
+{
+  selfp->do_pick_coordinate = false;
+
+  // Build a json array with the reply
+  if (error==NULL)
+    {
+      JsonBuilder *builder = json_builder_new ();
+      json_builder_begin_array (builder);
+      json_builder_add_double_value(builder,x);
+      json_builder_add_double_value(builder,y);
+      json_builder_end_array (builder);
+      JsonNode *response = json_node_copy(json_builder_get_root (builder));
+      g_object_unref(builder);
+      
+      glib_jsonrpc_server_send_async_response(selfp->json_query,
+                                              0,
+                                              response);
+    }
+  else
+    {
+      JsonNode *error_response = glib_jsonrpc_json_new_string_node(error);
+      glib_jsonrpc_server_send_async_response(selfp->json_query,
+                                              -1,
+                                              error_response);
+    }
+  
+  gdk_window_set_cursor(selfp->w_imgv->window, NULL);
+  selfp->current_cursor = NULL;
+}
+
 // Define remote commands
 static void create_remote_commands(GivWin *self)
 {
@@ -4191,6 +4320,10 @@ static void create_remote_commands(GivWin *self)
                                        "giv_string",
                                        cmd_giv_string,
                                        self);
+  glib_jsonrpc_server_register_async_command(selfp->jsonrpc_server,
+                                             "pick_coordinate",
+                                             cmd_pick_coordinate,
+                                             self);
 }
 
 %}
diff --git a/src/giv.cc b/src/giv.cc
index 37aabcf..d37038f 100644
--- a/src/giv.cc
+++ b/src/giv.cc
@@ -16,6 +16,7 @@
 int main(int argc, char **argv)
 {
     GtkWidget *giv;
+    g_thread_init(NULL);
     gtk_init(&argc, &argv);
 
     giv = giv_win_new(argc, argv);
diff --git a/src/glib-jsonrpc/glib-jsonrpc-json.c b/src/glib-jsonrpc/glib-jsonrpc-json.c
index ee62bfd..5f8d90d 100644
--- a/src/glib-jsonrpc/glib-jsonrpc-json.c
+++ b/src/glib-jsonrpc/glib-jsonrpc-json.c
@@ -73,9 +73,24 @@ JsonNode *glib_jsonrpc_json_string_to_json_node(const gchar *str)
 JsonNode *glib_jsonrpc_json_csv_to_json_array(const gchar *str)
 {
   gchar **str_list = g_strsplit(str, ",", -1);
+  JsonNode *node = glib_jsonrpc_json_strv_to_json_array(str_list);
+  g_strfreev(str_list);
+  return node;
+}
 
+// Create a string node
+JsonNode *glib_jsonrpc_json_new_string_node(const gchar *str)
+{
+  JsonNode *node = json_node_new(JSON_NODE_VALUE);
+  json_node_set_string(node, str);
+  return node;
+}
+
+// Create a gchar** into a json array
+JsonNode *glib_jsonrpc_json_strv_to_json_array(gchar **strv)
+{
   JsonArray *array = json_array_new();
-  gchar **p = str_list;
+  gchar **p = strv;
   while(*p)
     {
       JsonNode *node = json_node_new(JSON_NODE_VALUE);
@@ -85,6 +100,5 @@ JsonNode *glib_jsonrpc_json_csv_to_json_array(const gchar *str)
     }
   JsonNode *node = json_node_new(JSON_NODE_ARRAY);
   json_node_take_array(node, array);
-  g_strfreev(str_list);
   return node;
 }
diff --git a/src/glib-jsonrpc/glib-jsonrpc-json.h b/src/glib-jsonrpc/glib-jsonrpc-json.h
index 3338324..a985729 100644
--- a/src/glib-jsonrpc/glib-jsonrpc-json.h
+++ b/src/glib-jsonrpc/glib-jsonrpc-json.h
@@ -36,6 +36,11 @@ JsonNode *glib_jsonrpc_json_csv_to_json_array(const char *string);
 // A general json parser
 JsonNode *glib_jsonrpc_json_string_to_json_node(const gchar *str);
 
+// Create a string node
+JsonNode *glib_jsonrpc_json_new_string_node(const gchar *str);
+
+// Create a gchar** into a json array
+JsonNode *glib_jsonrpc_json_strv_to_json_array(gchar **strv);
 
 #ifdef __cplusplus
 }
diff --git a/src/glib-jsonrpc/glib-jsonrpc-server.c b/src/glib-jsonrpc/glib-jsonrpc-server.c
index bf26ac7..e2377b7 100644
--- a/src/glib-jsonrpc/glib-jsonrpc-server.c
+++ b/src/glib-jsonrpc/glib-jsonrpc-server.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include "glib-jsonrpc-server.h"
+#include "glib-jsonrpc-json.h"
 
 typedef struct {
   GLibJsonRpcServer server; 
@@ -29,8 +30,6 @@ typedef struct {
   GSocketService *service;
   GHashTable *command_hash;
   GString *req_string;
-  GMutex *mutex;
-  JsonNode *reply;
   gboolean allow_non_loopback_connections;
 
   // This is set whenever an asynchronous command is being run. Only
@@ -45,8 +44,43 @@ typedef struct {
   JsonNode *reply;
 } command_hash_value_t;
 
+typedef struct {
+  GMutex *mutex;
+  GCond *cond;
+  GLibJsonRpcServer *server;
+  JsonNode *reply;
+  int error_num;
+} GLibJsonRpcAsyncQueryPrivate;
 
-static JsonNode* create_fault_response(int error_num, const char *message, int id)
+static GLibJsonRpcAsyncQueryPrivate *glib_jsonrpc_server_query_new(GLibJsonRpcServer *server)
+{
+  GLibJsonRpcAsyncQueryPrivate *query = g_new0(GLibJsonRpcAsyncQueryPrivate, 1);
+  query->cond = g_cond_new();
+  query->mutex = g_mutex_new();
+  query->server = server;
+  query->reply = NULL;
+  query->error_num = 0;
+  //  g_mutex_lock(query->mutex);
+
+  return query;
+}
+
+static void glib_jsonrpc_async_query_free(GLibJsonRpcAsyncQueryPrivate *query)
+{
+  g_mutex_unlock(query->mutex);
+  g_cond_free(query->cond);
+  g_mutex_free(query->mutex); // Why does this crash?
+  json_node_free(query->reply);
+  g_free(query);
+}
+
+GLibJsonRpcServer *glib_jsonrpc_async_query_get_server(GLibJsonRpcAsyncQuery *_query)
+{
+  GLibJsonRpcAsyncQueryPrivate *query = (GLibJsonRpcAsyncQueryPrivate*)_query;
+  return query->server;
+}
+
+static JsonNode* create_fault_value_response(int error_num, JsonNode *msg_node, int id)
 {
   JsonBuilder *builder = json_builder_new ();
 
@@ -58,7 +92,7 @@ static JsonNode* create_fault_response(int error_num, const char *message, int i
   json_builder_set_member_name (builder, "code");
   json_builder_add_int_value(builder, error_num);
   json_builder_set_member_name (builder, "message");
-  json_builder_add_string_value (builder, message);
+  json_builder_add_value (builder, json_node_copy(msg_node));
   json_builder_end_object (builder);
   json_builder_set_member_name(builder, "id");
   if (id < 0)
@@ -71,6 +105,15 @@ static JsonNode* create_fault_response(int error_num, const char *message, int i
   g_object_unref(builder);
   return node;
 }
+  
+static JsonNode* create_fault_msg_response(int error_num, const char *message, int id)
+{
+  JsonNode *msg_node = json_node_new(JSON_NODE_VALUE);
+  json_node_set_string(msg_node, message);
+  JsonNode *node = create_fault_value_response(error_num, msg_node, id);
+  json_node_free(msg_node);
+  return node;
+}
     
 // Creates a response. Transfers ownership of reply.
 static JsonNode* create_response(JsonNode *reply, int id)
@@ -85,19 +128,15 @@ static JsonNode* create_response(JsonNode *reply, int id)
   json_builder_set_member_name (builder, "id");
   json_builder_add_int_value (builder, id);
 
+  json_builder_set_member_name (builder, "result");
+
   if (reply)
-    {
-      json_builder_set_member_name (builder, "result");
-      json_builder_add_value (builder, reply);
-      json_builder_end_object (builder);
-    }
+    json_builder_add_value (builder, json_node_copy(reply));
   else
-    {
-      // default "ok" message
-      json_builder_set_member_name (builder, "result");
-      json_builder_add_string_value (builder, "ok");
-      json_builder_end_object (builder);
-    }
+    // default "ok" message
+    json_builder_add_string_value (builder, "ok");
+
+  json_builder_end_object (builder);
   
   JsonNode *node = json_node_copy(json_builder_get_root (builder));
 
@@ -197,25 +236,39 @@ handler (GThreadedSocketService *service,
     command_val = g_hash_table_lookup(jsonrpc_server->command_hash,
                                       method);
   if (!command_val)
-    response = create_fault_response(-2, "No such method!",id);
+    response = create_fault_msg_response(-2, "No such method!",id);
   else if (command_val->async_callback)
     {
       if (jsonrpc_server->async_busy)
-        response = create_fault_response(-2, "Busy!",id);
+        response = create_fault_msg_response(-2, "Busy!",id);
       else
         {
-          jsonrpc_server->async_busy = TRUE;
+          // With the embedding of the mutex in the query we should
+          // be able to handle more than one connection, so there
+          // is no need to protect against a busy state.
+          // jsonrpc_server->async_busy = TRUE;
+          GLibJsonRpcAsyncQueryPrivate *query = glib_jsonrpc_server_query_new((GLibJsonRpcServer*)jsonrpc_server);
           
           // Create a secondary main loop
           (*command_val->async_callback)((GLibJsonRpcServer*)jsonrpc_server,
+                                         (GLibJsonRpcAsyncQuery*)query,
                                          method,
                                          params,
                                          command_val->user_data);
           
-          // Lock on a mutex
-          g_mutex_lock(jsonrpc_server->mutex);
-          response = create_response(jsonrpc_server->reply, id);
+          // Lock on a mutex 
+          g_mutex_lock(query->mutex);
+          g_cond_wait(query->cond, query->mutex);
+          g_mutex_unlock(query->mutex);
+
+          if (query->error_num != 0)
+            response = create_fault_value_response(query->error_num,query->reply,id);
+          else
+            response = create_response(query->reply, id);
           jsonrpc_server->async_busy = FALSE;
+
+          // Erase the query
+          glib_jsonrpc_async_query_free(query);
         }
     }
   else
@@ -230,9 +283,12 @@ handler (GThreadedSocketService *service,
 
       if (ret == 0)
         response = create_response(reply,id);
-      else 
+      else
         // For faults expect a string response containing the error
-        response = create_fault_response(ret, json_node_get_string(reply),id);
+        response = create_fault_value_response(ret, reply,id);
+
+      if (reply)
+        json_node_free(reply);
     }
 
   if (response)
@@ -286,21 +342,17 @@ GLibJsonRpcServer *glib_jsonrpc_server_new(int port)
 					NULL,
 					&error))
     {
-      // Ignore the error and return NULL
-      // g_printerr ("%s\n", error->message);
+      g_printerr ("%s\n", error->message);
       g_error_free(error);
       g_free(jsonrpc_server);
       return NULL;
     }
 
-
   g_signal_connect (jsonrpc_server->service, "run", G_CALLBACK (handler), jsonrpc_server);
 
   jsonrpc_server->command_hash = g_hash_table_new(g_str_hash,
                                                   g_str_equal);
 
-  jsonrpc_server->mutex = g_mutex_new();
-  g_mutex_lock(jsonrpc_server->mutex);
   return (GLibJsonRpcServer*)jsonrpc_server;
 }
 
@@ -358,13 +410,16 @@ int glib_jsonrpc_server_register_async_command(GLibJsonRpcServer *jsonrpc_server
   return 0;
 }
     
-int  glib_jsonrpc_server_send_async_response(GLibJsonRpcServer *_server,
+int  glib_jsonrpc_server_send_async_response(GLibJsonRpcAsyncQuery *_query,
+                                             int error_num,
                                              JsonNode *reply)
 {
-  GLibJsonRpcServerPrivate *server = (GLibJsonRpcServerPrivate *)_server;
-  server->reply = reply;
-
-  g_mutex_unlock(server->mutex);
+  GLibJsonRpcAsyncQueryPrivate *query = (GLibJsonRpcAsyncQueryPrivate *)_query;
+  query->reply = reply;
+  query->error_num = error_num;
+  g_mutex_lock(query->mutex);
+  g_cond_broadcast(query->cond);
+  g_mutex_unlock(query->mutex);
 
   return TRUE;
 }
diff --git a/src/glib-jsonrpc/glib-jsonrpc-server.h b/src/glib-jsonrpc/glib-jsonrpc-server.h
index d7f2e72..503ddca 100644
--- a/src/glib-jsonrpc/glib-jsonrpc-server.h
+++ b/src/glib-jsonrpc/glib-jsonrpc-server.h
@@ -31,6 +31,12 @@ extern "C" {
 typedef struct {
 } GLibJsonRpcServer; 
 
+// Queries use for async commands
+typedef struct {
+} GLibJsonRpcAsyncQuery;
+
+GLibJsonRpcServer *glib_jsonrpc_async_query_get_server(GLibJsonRpcAsyncQuery *query);
+  
 typedef int (GLibJsonRpcCommandCallback)(GLibJsonRpcServer *server,
                                          const char *method,
                                          JsonNode *params,
@@ -38,6 +44,7 @@ typedef int (GLibJsonRpcCommandCallback)(GLibJsonRpcServer *server,
                                          gpointer user_data);
 
 typedef int (GLibJsonRpcCommandAsyncCallback)(GLibJsonRpcServer *server,
+                                              GLibJsonRpcAsyncQuery *query,
                                               const char *method,
                                               JsonNode *params,
                                               gpointer user_data);
@@ -55,7 +62,8 @@ int glib_jsonrpc_server_register_async_command(GLibJsonRpcServer *jsonrpc_server
                                                GLibJsonRpcCommandAsyncCallback *async_callback,
                                                gpointer user_data);
 // The json node ownershap is transfered.
-int  glib_jsonrpc_server_send_async_response(GLibJsonRpcServer *server,
+int  glib_jsonrpc_server_send_async_response(GLibJsonRpcAsyncQuery *query,
+                                             int error_num,
                                              JsonNode *response);
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]