changeset 7936:78400fde223e

Support for backend-to-octave event management
author John W. Eaton <jwe@octave.org>
date Thu, 17 Jul 2008 07:58:50 -0400
parents 85d6296d51e1
children 6661387827d6
files liboctave/ChangeLog liboctave/cmd-edit.cc liboctave/oct-mutex.h src/ChangeLog src/graphics.cc src/graphics.h.in
diffstat 6 files changed, 677 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/liboctave/ChangeLog	Wed Jul 16 14:28:48 2008 -0400
+++ b/liboctave/ChangeLog	Thu Jul 17 07:58:50 2008 -0400
@@ -1,3 +1,18 @@
+2008-07-16  John W. Eaton  <jwe@octave.org>
+
+	* oct-mutex.h (octave_autolock::octave_autolock (void),
+	octave_autolock (const octave_autolock&),
+	octave_autolock::operator = (const octave_autolock&)):
+	Delete definitions.
+
+2008-07-16  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* cmd-edit.cc (event_hook_lock): New static mutex variable.
+	(command_editor::event_handler): Lock and copy event_hook_set before
+	executing handlers.
+	(command_editor::add_event_hook, command_editor::remove_event_hook):
+	Autolock event_hook_lock.
+
 2008-07-15  Michael Goffioul  <michael.goffioul@gmail.com>
 
 	* oct-mutex.h, oct-mutex.cc: New files.
--- a/liboctave/cmd-edit.cc	Wed Jul 16 14:28:48 2008 -0400
+++ b/liboctave/cmd-edit.cc	Thu Jul 17 07:58:50 2008 -0400
@@ -45,6 +45,7 @@
 #include "lo-error.h"
 #include "lo-utils.h"
 #include "oct-env.h"
+#include "oct-mutex.h"
 #include "oct-time.h"
 
 command_editor *command_editor::instance = 0;
@@ -53,6 +54,8 @@
 
 std::set<command_editor::event_hook_fcn> command_editor::event_hook_set;
 
+static octave_mutex event_hook_lock;
+
 #if defined (USE_READLINE)
 
 #include <cstdio>
@@ -852,8 +855,14 @@
 int
 command_editor::event_handler (void)
 {
-  for (event_hook_set_iterator p = event_hook_set.begin ();
-       p != event_hook_set.end (); p++)
+  event_hook_lock.lock ();
+
+  std::set<event_hook_fcn> hook_set (event_hook_set);
+
+  event_hook_lock.unlock ();
+
+  for (event_hook_set_iterator p = hook_set.begin ();
+       p != hook_set.end (); p++)
     {
       event_hook_fcn f = *p;
 
@@ -1160,6 +1169,8 @@
 void
 command_editor::add_event_hook (event_hook_fcn f)
 {
+  octave_autolock guard (event_hook_lock);
+
   if (instance_ok ())
     {
       event_hook_set.insert (f);
@@ -1171,6 +1182,8 @@
 void
 command_editor::remove_event_hook (event_hook_fcn f)
 {
+  octave_autolock guard (event_hook_lock);
+
   if (instance_ok ())
     {
       event_hook_set_iterator p = event_hook_set.find (f);
--- a/liboctave/oct-mutex.h	Wed Jul 16 14:28:48 2008 -0400
+++ b/liboctave/oct-mutex.h	Thu Jul 17 07:58:50 2008 -0400
@@ -93,9 +93,11 @@
     }
 
 private:
-  octave_autolock (void) { }
-  octave_autolock (const octave_autolock&) { }
-  octave_autolock& operator = (const octave_autolock&) { }
+
+  // No copying or default constructor!
+  octave_autolock (void);
+  octave_autolock (const octave_autolock&);
+  octave_autolock& operator = (const octave_autolock&);
 
 private:
   octave_mutex mutex;
--- a/src/ChangeLog	Wed Jul 16 14:28:48 2008 -0400
+++ b/src/ChangeLog	Thu Jul 17 07:58:50 2008 -0400
@@ -1,3 +1,53 @@
+2008-07-16  John W. Eaton  <jwe@octave.org>
+
+	* graphics.h.in (gh_manager::autolock): Delete copy constructor
+	and assignment definitions.
+
+2008-07-16  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* graphics.h.in (callback_property::execute): Remove static version.
+	(base_properties::is_hittest, base_properties::is_interruptible,
+	base_properties::is_selected, base_properties::is_selectionhighlight):
+	New convenience property accessors.
+	(base_graphics_object::get_handle, graphics_object::get_handle): Idem.
+	(gh_manager::graphics_lock): New global mutex
+	(gh_manager::lock, gh_manager::unlock, gh_manager::do_lock,
+	gh_manager::do_unlock): Add accessors for it.
+	(gh_manager::autolock): New class for easy locking of the graphics
+	system.
+	(gh_manager::event_data): New class for event management.
+	(gh_manager::event_queue): New object to hold pending events.
+	(gh_manager::callback_objects): New stack of callback objects.
+	(gh_manager::execute_callback, gh_manager:post_callback,
+	gh_manager::post_function, gh_manager::post_set,
+	gh_manager::process_events, gh_manager::flush_events,
+	gh_manager::restore_gcbo): New static methods for event management.
+	(gh_manager::do_execute_callback, gh_manager::do_post_callback,
+	gh_manager::do_post_function, gh_manager::do_post_set,
+	gh_manager::do_process_events, gh_manager::do_post_event,
+	gh_manager::do_restore_gcbo): New non-static versions.
+	* graphics.cc (xreset_gcbo): Remove.
+	(execute_callback): Likewise.
+	(base_property::run_listeners, callback_property::execute): Use
+	gh_manager::execute_callback.
+	(class callback_event_data, class function_event_data, class
+	set_event_data): New classes to implement various types of events.
+	(gh_manager::event_data::create_callback_event,
+	gh_manager::event_data::create_function_event,
+	gh_manager::event_data::create_set_event): Implement event factory
+	methods.
+	(gh_manager::do_restore_gcbo, gh_manager::do_execute_callback,
+	gh_manager::do_post_event, gh_manager::do_post_callback,
+	gh_manager::do_post_function, gh_manager::do_post_set,
+	gh_manager::do_process_events): New methods for event management.
+	(Fishandle, Fset, Fget, F__get__, F__go_figure__, F__go_delete__,
+	F__go_axes_init__, F__go_handles__, F__go_figure_handles__,
+	Favailable_backends, Fdrawnow, Faddlistener, Faddproperty,
+	get_property_from_handle, set_property_in_handle): Lock graphics
+	system.
+	(GO_BODY): Likewise.
+	(Fdrawnow): Support single "expose" argument.
+
 2008-07-15  John W. Eaton  <jwe@octave.org>
 
 	* DLD-FUNCTIONS/__convn__.cc (convn): Cast second arg to
--- a/src/graphics.cc	Wed Jul 16 14:28:48 2008 -0400
+++ b/src/graphics.cc	Thu Jul 17 07:58:50 2008 -0400
@@ -37,6 +37,7 @@
 #include "file-ops.h"
 #include "file-stat.h"
 
+#include "cmd-edit.h"
 #include "defun.h"
 #include "error.h"
 #include "graphics.h"
@@ -172,83 +173,6 @@
   return m;
 }
 
-static void
-xset_gcbo (const graphics_handle& h)
-{
-  graphics_object go = gh_manager::get_object (0);
-  root_figure::properties& props =
-      dynamic_cast<root_figure::properties&> (go.get_properties ());
-
-  props.set_callbackobject (h.as_octave_value ());
-}
-
-static void
-xreset_gcbo (void *)
-{
-  xset_gcbo (graphics_handle ());
-}
-
-static void
-execute_callback (const octave_value& cb_arg, const graphics_handle& h,
-                  const octave_value& data)
-{
-  octave_value_list args;
-  octave_function *fcn = 0;
-
-  args(0) = h.as_octave_value ();
-  if (data.is_defined ())
-    args(1) = data;
-  else
-    args(1) = Matrix ();
-
-  unwind_protect::begin_frame ("execute_callback");
-  unwind_protect::add (xreset_gcbo);
-
-  xset_gcbo (h);
-
-  BEGIN_INTERRUPT_WITH_EXCEPTIONS;
-
-  // Copy CB because "function_value" method is non-const.
-
-  octave_value cb = cb_arg;
-
-  if (cb.is_function_handle ())
-    fcn = cb.function_value ();
-  else if (cb.is_string ())
-    {
-      int status;
-      std::string s = cb.string_value ();
-
-      eval_string (s, false, status);
-    }
-  else if (cb.is_cell () && cb.length () > 0
-           && (cb.rows () == 1 || cb.columns () == 1)
-           && cb.cell_value ()(0).is_function_handle ())
-    {
-      Cell c = cb.cell_value ();
-
-      fcn = c(0).function_value ();
-      if (! error_state)
-        {
-          for (int i = 0; i < c.length () ; i++)
-            args(2+i) = c(i);
-        }
-    }
-  else
-    {
-      std::string nm = cb.class_name ();
-      error ("trying to execute non-executable object (class = %s)",
-	     nm.c_str ());
-    }
-
-  if (fcn && ! error_state)
-    feval (fcn, args);
-  
-  END_INTERRUPT_WITH_EXCEPTIONS;
-
-  unwind_protect::run_frame ("execute_callback");
-}
-
 static Matrix
 convert_position (const Matrix& pos, const caseless_str& from_units,
 		  const caseless_str& to_units,
@@ -541,7 +465,7 @@
 
   for (int i = 0; i < l.length (); i++)
     {
-      execute_callback (l(i), parent, octave_value ());
+      gh_manager::execute_callback (parent, l(i), octave_value ());
 
       if (error_state)
 	break;
@@ -829,15 +753,7 @@
 callback_property::execute (const octave_value& data) const
 {
   if (callback.is_defined () && ! callback.is_empty ())
-    execute_callback (callback, get_parent (), data);
-}
-
-void
-callback_property::execute (const octave_value& cb, const graphics_handle& h,
-			    const octave_value& data)
-{
-  if (cb.is_defined () && ! cb.is_empty ())
-    execute_callback (cb, h, data);
+    gh_manager::execute_callback (get_parent (), callback, data);
 }
 
 // Used to cache dummy graphics objects from which dynamic
@@ -3649,6 +3565,323 @@
     }
 }
 
+class
+callback_event_data : public gh_manager::event_data
+{
+public:
+  callback_event_data (const graphics_handle& h, const std::string& name,
+		       const octave_value& data = Matrix ())
+      : gh_manager::event_data (0), handle (h), callback_name (name),
+        callback_data (data) { }
+
+  void execute (void)
+    {
+      gh_manager::execute_callback (handle, callback_name, callback_data);
+    }
+
+private:
+  callback_event_data (void)
+      : gh_manager::event_data (0) { }
+
+private:
+  graphics_handle handle;
+  std::string callback_name;
+  octave_value callback_data;
+};
+
+class
+function_event_data : public gh_manager::event_data
+{
+public:
+  function_event_data (gh_manager::event_fcn fcn, void* data = 0)
+      : gh_manager::event_data (0), function (fcn),
+        function_data (data) { }
+
+  void execute (void)
+    {
+      function (function_data);
+    }
+
+private:
+  function_event_data (void)
+      : gh_manager::event_data (0) { }
+
+private:
+  gh_manager::event_fcn function;
+  void* function_data;
+};
+
+class
+set_event_data : public gh_manager::event_data
+{
+public:
+  set_event_data (const graphics_handle& h, const std::string& name,
+		  const octave_value& value)
+      : gh_manager::event_data (0), handle (h), property_name (name),
+        property_value (value) { }
+
+  void execute (void)
+    {
+      gh_manager::autolock guard;
+
+      xset (handle, property_name, property_value);
+    }
+
+private:
+  set_event_data (void)
+      : gh_manager::event_data (0) { }
+
+private:
+  graphics_handle handle;
+  std::string property_name;
+  octave_value property_value;
+};
+
+gh_manager::event_data
+gh_manager::event_data::create_callback_event (const graphics_handle& h,
+					       const std::string& name,
+					       const octave_value& data)
+{
+  event_data e;
+
+  e.rep = new callback_event_data (h, name, data);
+
+  e.rep->refcount++;
+
+  return e;
+}
+
+gh_manager::event_data
+gh_manager::event_data::create_function_event (gh_manager::event_fcn fcn,
+					       void *data)
+{
+  event_data e;
+
+  e.rep =new function_event_data (fcn, data);
+
+  e.rep->refcount++;
+
+  return e;
+}
+
+gh_manager::event_data
+gh_manager::event_data::create_set_event (const graphics_handle& h,
+					  const std::string& name,
+					  const octave_value& data)
+{
+  event_data e;
+
+  e.rep = new set_event_data (h, name, data);
+
+  e.rep->refcount++;
+
+  return e;
+}
+
+static void
+xset_gcbo (const graphics_handle& h)
+{
+  graphics_object go = gh_manager::get_object (0);
+  root_figure::properties& props =
+      dynamic_cast<root_figure::properties&> (go.get_properties ());
+
+  props.set_callbackobject (h.as_octave_value ());
+}
+
+void
+gh_manager::do_restore_gcbo (void)
+{
+  gh_manager::autolock guard;
+
+  callback_objects.pop_front ();
+
+  xset_gcbo (callback_objects.empty ()
+	     ? graphics_handle ()
+	     : callback_objects.front ().get_handle ());
+}
+
+void
+gh_manager::do_execute_callback (const graphics_handle& h,
+				 const octave_value& cb_arg,
+				 const octave_value& data)
+{
+  octave_value_list args;
+  octave_function *fcn = 0;
+
+  args(0) = h.as_octave_value ();
+  if (data.is_defined ())
+    args(1) = data;
+  else
+    args(1) = Matrix ();
+
+  unwind_protect::begin_frame ("execute_callback");
+  unwind_protect::add (gh_manager::restore_gcbo);
+
+  if (true)
+    {
+      gh_manager::autolock guard;
+  
+      callback_objects.push_front (get_object (h));
+      xset_gcbo (h);
+    }
+
+  BEGIN_INTERRUPT_WITH_EXCEPTIONS;
+
+  // Copy CB because "function_value" method is non-const.
+
+  octave_value cb = cb_arg;
+
+  if (cb.is_function_handle ())
+    fcn = cb.function_value ();
+  else if (cb.is_string ())
+    {
+      int status;
+      std::string s = cb.string_value ();
+
+      eval_string (s, false, status);
+    }
+  else if (cb.is_cell () && cb.length () > 0
+           && (cb.rows () == 1 || cb.columns () == 1)
+           && cb.cell_value ()(0).is_function_handle ())
+    {
+      Cell c = cb.cell_value ();
+
+      fcn = c(0).function_value ();
+      if (! error_state)
+        {
+          for (int i = 0; i < c.length () ; i++)
+            args(2+i) = c(i);
+        }
+    }
+  else
+    {
+      std::string nm = cb.class_name ();
+      error ("trying to execute non-executable object (class = %s)",
+	     nm.c_str ());
+    }
+
+  if (fcn && ! error_state)
+    feval (fcn, args);
+  
+  END_INTERRUPT_WITH_EXCEPTIONS;
+
+  unwind_protect::run_frame ("execute_callback");
+}
+
+void
+gh_manager::do_post_event (const event_data& e)
+{
+  event_queue.push_back (e);
+
+  command_editor::add_event_hook (gh_manager::process_events);
+}
+
+void
+gh_manager::do_post_callback (const graphics_handle& h, const std::string name,
+			      const octave_value& data)
+{
+  gh_manager::autolock guard;
+
+  graphics_object go = get_object (h);
+
+  if (go.valid_object ())
+    {
+      if (callback_objects.empty ())
+	do_post_event (event_data::create_callback_event (h, name, data));
+      else
+	{
+	  const graphics_object& current = callback_objects.front ();
+
+	  if (current.get_properties ().is_interruptible ())
+	    do_post_event (event_data::create_callback_event (h, name, data));
+	  else
+	    {
+	      caseless_str busy_action (go.get_properties ().get_busyaction ());
+
+	      if (busy_action.compare ("queue"))
+		do_post_event (event_data::create_callback_event (h, name, data));
+	      else
+		{
+		  caseless_str cname (name);
+
+		  if (cname.compare ("deletefcn")
+		      || cname.compare ("createfcn")
+		      || (go.isa ("figure")
+			  && (cname.compare ("closerequestfcn")
+			      || cname.compare ("resizefcn"))))
+		    do_post_event (event_data::create_callback_event (h, name, data));
+		}
+	    }
+	}
+    }
+}
+
+void
+gh_manager::do_post_function (event_fcn fcn, void* fcn_data)
+{
+  gh_manager::autolock guard;
+
+  do_post_event (event_data::create_function_event (fcn, fcn_data));
+}
+
+void
+gh_manager::do_post_set (const graphics_handle& h, const std::string name,
+			 const octave_value& value)
+{
+  gh_manager::autolock guard;
+
+  do_post_event (event_data::create_set_event (h, name, value));
+}
+
+int
+gh_manager::do_process_events (bool force)
+{
+  event_data e;
+
+  do
+    {
+      e = event_data ();
+
+      gh_manager::lock ();
+
+      if (! event_queue.empty ())
+	{
+	  if (callback_objects.empty () || force)
+	    {
+	      e = event_queue.front ();
+	      
+	      event_queue.pop_front ();
+	    }
+	  else
+	    {
+	      const graphics_object& go = callback_objects.front ();
+
+	      if (go.get_properties ().is_interruptible ())
+		{
+		  e = event_queue.front ();
+
+		  event_queue.pop_front ();
+		}
+	    }
+	}
+
+      gh_manager::unlock ();
+
+      if (e.ok ())
+	e.execute ();
+    }
+  while (e.ok ());
+
+  gh_manager::lock ();
+
+  if (event_queue.empty ())
+    command_editor::remove_event_hook (gh_manager::process_events);
+
+  gh_manager::unlock ();
+
+  return 0;
+}
+
 property_list::plist_map_type
 root_figure::init_factory_properties (void)
 {
@@ -3674,6 +3907,8 @@
 Return true if @var{h} is a graphics handle and false otherwise.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   if (args.length () == 1)
@@ -3691,6 +3926,8 @@
 for the graphics handle @var{h}.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   int nargin = args.length ();
@@ -3741,6 +3978,8 @@
 values or lists respectively.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
   octave_value_list vlist;
 
@@ -3811,6 +4050,8 @@
 values or lists respectively.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
   octave_value_list vlist;
 
@@ -3932,6 +4173,8 @@
 Undocumented internal function.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   if (args.length () > 0)
@@ -3982,6 +4225,8 @@
 }
 
 #define GO_BODY(TYPE) \
+  gh_manager::autolock guard; \
+ \
   octave_value retval; \
  \
   if (args.length () > 0) \
@@ -4060,6 +4305,8 @@
 Undocumented internal function.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value_list retval;
 
   if (args.length () == 1)
@@ -4108,6 +4355,8 @@
 Undocumented internal function.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   int nargin = args.length ();
@@ -4156,6 +4405,8 @@
 Undocumented internal function.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   return octave_value (gh_manager::handle_list ());
 }
 
@@ -4165,6 +4416,8 @@
 Undocumented internal function.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   return octave_value (gh_manager::figure_handle_list ());
 }
 
@@ -4174,6 +4427,8 @@
 Returns resgistered graphics backends.\n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   return octave_value (graphics_backend::available_backends_list ());
 }
 
@@ -4195,6 +4450,8 @@
 
   octave_value retval;
 
+  gh_manager::lock ();
+
   unwind_protect::begin_frame ("Fdrawnow");
   unwind_protect::add (clear_drawnow_request);
 
@@ -4209,7 +4466,7 @@
 	  __go_close_all_registered__ = true;
 	}
 
-      if (args.length () == 0)
+      if (args.length () == 0 || args.length () == 1)
 	{
 	  Matrix hlist = gh_manager::figure_handle_list ();
 
@@ -4225,7 +4482,13 @@
 		  if (fprops.is_modified ())
 		    {
 		      if (fprops.is_visible ())
-			fprops.get_backend ().redraw_figure (h);
+			{
+			  gh_manager::unlock ();
+
+			  fprops.get_backend ().redraw_figure (h);
+
+			  gh_manager::lock ();
+			}
 		      else if (! fprops.get___plot_stream__ ().is_empty ())
 			{
 			  fprops.close (false);
@@ -4236,6 +4499,27 @@
 		    }
 		}
 	    }
+
+	  bool do_events = true;
+
+	  if (args.length () == 1)
+	    {
+	      caseless_str val (args(0).string_value ());
+
+	      if (! error_state && val.compare ("expose"))
+		do_events = false;
+	      else
+		error ("drawnow: invalid argument, expected `expose' as argument");
+	    }
+
+	  if (do_events)
+	    {
+	      gh_manager::unlock ();
+
+	      gh_manager::process_events ();
+
+	      gh_manager::lock ();
+	    }
 	}
       else if (args.length () >= 2 && args.length () <= 4)
 	{
@@ -4276,8 +4560,12 @@
 			    {
 			      graphics_object go = gh_manager::get_object (h);
 
+			      gh_manager::unlock ();
+
 			      go.get_backend ()
 				.print_figure (h, term, file, mono, debug_file);
+
+			      gh_manager::lock ();
 			    }
 			  else
 			    error ("drawnow: nothing to draw");
@@ -4300,6 +4588,8 @@
 
   unwind_protect::run_frame ("Fdrawnow");
 
+  gh_manager::unlock ();
+
   return retval;
 }
 
@@ -4334,6 +4624,8 @@
 \n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   if (args.length () == 3)
@@ -4430,6 +4722,8 @@
 \n\
 @end deftypefn")
 {
+  gh_manager::autolock guard;
+
   octave_value retval;
 
   if (args.length () >= 3)
@@ -4486,6 +4780,8 @@
 get_property_from_handle (double handle, const std::string& property,
 			  const std::string& func)
 {
+  gh_manager::autolock guard;
+
   graphics_object obj = gh_manager::get_object (handle);
   octave_value retval;
 
@@ -4504,6 +4800,8 @@
 set_property_in_handle (double handle, const std::string& property,
 			const octave_value& arg, const std::string& func)
 {
+  gh_manager::autolock guard;
+
   graphics_object obj = gh_manager::get_object (handle);
   int ret = false;
 
--- a/src/graphics.h.in	Wed Jul 16 14:28:48 2008 -0400
+++ b/src/graphics.h.in	Thu Jul 17 07:58:50 2008 -0400
@@ -37,6 +37,7 @@
 
 #include "gripes.h"
 #include "oct-map.h"
+#include "oct-mutex.h"
 #include "ov.h"
 
 class caseless_str : public std::string
@@ -1124,11 +1125,6 @@
 
   OCTINTERP_API void execute (const octave_value& data = octave_value ()) const;
 
-  OCTINTERP_API static
-      void execute (const octave_value& cb, const graphics_handle& h,
-		    const octave_value& data = octave_value ());
-
-
   callback_property& operator = (const octave_value& val)
     {
       set (val);
@@ -1566,12 +1562,16 @@
 
   std::string get_handlevisibility (void) const { return handlevisibility.current_value (); }
 
+  bool is_hittest (void) const { return hittest.is_on (); }
   std::string get_hittest (void) const { return hittest.current_value (); }
 
+  bool is_interruptible (void) const { return interruptible.is_on (); }
   std::string get_interruptible (void) const { return interruptible.current_value (); }
 
+  bool is_selected (void) const { return selected.is_on (); }
   std::string get_selected (void) const { return selected.current_value (); }
 
+  bool is_selectionhighlight (void) const { return selectionhighlight.is_on (); }
   std::string get_selectionhighlight (void) const { return selectionhighlight.current_value (); }
   
   octave_value get_uicontextmenu (void) const { return uicontextmenu.get (); }
@@ -1899,6 +1899,17 @@
       }
   }
 
+  graphics_handle get_handle (void) const
+  {
+    if (valid_object ())
+      return get_properties ().get___myhandle__ ();
+    else
+      {
+        error ("base_graphics_object::get_handle: invalid graphics object");
+        return graphics_handle ();
+      }
+  }
+
   virtual void remove_child (const graphics_handle& h)
   {
     if (valid_object ())
@@ -2068,6 +2079,8 @@
 
   graphics_handle get_parent (void) const { return rep->get_parent (); }
 
+  graphics_handle get_handle (void) const { return rep->get_handle (); }
+
   void remove_child (const graphics_handle& h) { rep->remove_child (h); }
 
   void adopt (const graphics_handle& h) { rep->adopt (h); }
@@ -3486,6 +3499,8 @@
 
 public:
 
+  typedef void (*event_fcn) (void*);
+
   static bool instance_ok (void)
   {
     bool retval = true;
@@ -3557,11 +3572,160 @@
     return instance_ok () ? instance->do_handle_list () : Matrix ();
   }
 
+  static void lock (void)
+  {
+    if (instance_ok ())
+      instance->do_lock ();
+  }
+
+  static void unlock (void)
+  {
+    if (instance_ok ())
+      instance->do_unlock ();
+  }
+
   static Matrix figure_handle_list (void)
   {
     return instance_ok () ? instance->do_figure_handle_list () : Matrix ();
   }
 
+  static void execute_callback (const graphics_handle& h,
+				const std::string& name,
+				const octave_value& data = Matrix ())
+  {
+    graphics_object go = get_object (h);
+
+    if (go.valid_object ())
+      {
+	octave_value cb = go.get (name);
+
+	if (! error_state)
+	  execute_callback (h, cb, data);
+      }
+  }
+
+  static void execute_callback (const graphics_handle& h,
+				const octave_value& cb,
+				const octave_value& data = Matrix ())
+  {
+    if (instance_ok ())
+      instance->do_execute_callback (h, cb, data);
+  }
+
+  static void post_callback (const graphics_handle& h,
+			     const std::string& name,
+			     const octave_value& data = Matrix ())
+  {
+    if (instance_ok ())
+      instance->do_post_callback (h, name, data);
+  }
+
+  static void post_function (event_fcn fcn, void* data = 0)
+  {
+    if (instance_ok ())
+      instance->do_post_function (fcn, data);
+  }
+
+  static void post_set (const graphics_handle& h,
+			const std::string& name,
+			const octave_value& value)
+  {
+    if (instance_ok ())
+      instance->do_post_set (h, name, value);
+  }
+
+  static int process_events (void)
+  {
+    return (instance_ok () ?  instance->do_process_events () : 0);
+  }
+
+  static int flush_events (void)
+  {
+    return (instance_ok () ?  instance->do_process_events (true) : 0);
+  }
+
+public:
+  class autolock
+  {
+  public:
+    autolock (void) { lock (); }
+
+    ~autolock (void) { unlock (); }
+
+  private:
+
+    // No copying!
+    autolock (const autolock&);
+    autolock& operator = (const autolock&);
+  };
+
+public:
+  class event_data
+    {
+      public:
+	event_data (void) : rep (0) { }
+
+	event_data (const event_data& d)
+	  {
+	    rep = d.rep;
+	    if (rep)
+	      rep->refcount++;
+	  }
+
+	virtual ~event_data (void)
+	  {
+	    if (rep && --rep->refcount == 0)
+	      {
+		delete rep;
+		rep = 0;
+	      }
+	  }
+
+	event_data& operator = (const event_data& d)
+	  {
+	    if (d.rep != rep)
+	      {
+		if (rep && --rep->refcount == 0)
+		  delete rep;
+
+		rep = d.rep;
+		if (rep)
+		  rep->refcount++;
+	      }
+
+	    return *this;
+	  }
+
+	virtual void execute (void)
+	  { if (rep) rep->execute (); }
+
+	bool ok (void) const { return (rep != 0); }
+	
+	static event_data
+	    create_callback_event (const graphics_handle& h,
+				   const std::string& name,
+				   const octave_value& data = Matrix ());
+
+	static event_data
+	    create_function_event (event_fcn fcn, void *data = 0);
+
+	static event_data
+	    create_set_event (const graphics_handle& h,
+			      const std::string& name,
+			      const octave_value& value);
+
+      protected:
+	explicit event_data (int /* dummy */)
+	    : refcount (0) { }
+
+      private:
+	union
+	  {
+	    event_data *rep;
+	    int refcount;
+	  };
+    };
+
 private:
 
   static gh_manager *instance;
@@ -3588,6 +3752,15 @@
   // created.
   std::list<graphics_handle> figure_list;
 
+  // The lock for accessing the graphics sytsem
+  octave_mutex graphics_lock;
+
+  // The list of event queued by backends
+  std::list<event_data> event_queue;
+
+  // The stack of callback objects
+  std::list<graphics_object> callback_objects;
+
   graphics_handle get_handle (const std::string& go_name);
 
   void do_free (const graphics_handle& h);
@@ -3645,6 +3818,33 @@
   {
     return figure_list.empty () ? graphics_handle () : figure_list.front ();
   }
+
+  void do_lock (void) { graphics_lock.lock (); }
+  
+  void do_unlock (void) { graphics_lock.unlock (); }
+
+  void do_execute_callback (const graphics_handle& h, const octave_value& cb,
+			    const octave_value& data);
+
+  void do_post_callback (const graphics_handle& h, const std::string name,
+			 const octave_value& data);
+
+  void do_post_function (event_fcn fcn, void* fcn_data);
+
+  void do_post_set (const graphics_handle& h, const std::string name,
+		    const octave_value& value);
+
+  int do_process_events (bool force = false);
+
+  static void restore_gcbo (void*)
+  {
+    if (instance_ok ())
+      instance->do_restore_gcbo ();
+  }
+
+  void do_restore_gcbo (void);
+
+  void do_post_event (const event_data& e);
 };