core/vgui/impl/gtk2/vgui_gtk2_adaptor.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/gtk2/vgui_gtk2_adaptor.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief  See vgui_gtk2_adaptor.h for a description of this file.
00008 // \author Philip C. Pritchett, RRG, University of Oxford
00009 // \date   19 Dec 99
00010 //
00011 // \verbatim
00012 //  Modifications
00013 //   04-OCT-2002 K.Y.McGaul - Use event.set_key() to set key for events,
00014 //                            makes all key chars lower case.
00015 //                          - Set event.ascii_char to actual key stroke
00016 // \endverbatim
00017 
00018 #include "vgui_gtk2_adaptor.h"
00019 #include <vcl_cstdlib.h>
00020 #include <vcl_cassert.h>
00021 #include <vcl_utility.h>
00022 #include <gdk/gdkkeysyms.h>
00023 #include <gtk/gtk.h>
00024 #include <gdk/gdkgl.h>
00025 #include <gtk/gtkgl.h>
00026 
00027 #include <vgui/vgui_gl.h>
00028 #include <vgui/vgui_popup_params.h>
00029 #include <vgui/internals/vgui_overlay_helper.h>
00030 #include "vgui_gtk2_utils.h"
00031 #include "vgui_gtk2_window.h"
00032 #include <vcl_iostream.h>
00033 
00034 vgui_menu vgui_gtk2_adaptor::last_popup;
00035 
00036 extern "C" {
00037   static gint timeout_callback(gpointer);
00038 }
00039 
00040 //--------------------------------------------------------------------------------
00041 //: Constructors
00042 vgui_gtk2_adaptor::vgui_gtk2_adaptor(vgui_gtk2_window* win)
00043   : idle_request_posted_(false),
00044     widget(0),
00045     win_(win),
00046     ovl_helper(0),
00047     last_mouse_x(0),
00048     last_mouse_y(0)
00049 {
00050   // Configure OpenGL-capable visual.
00051 
00052   // Try double-buffered visual
00053   GdkGLConfig* glconfig = gdk_gl_config_new_by_mode (GdkGLConfigMode(GDK_GL_MODE_RGB |
00054                                                                      GDK_GL_MODE_DEPTH |
00055                                                                      GDK_GL_MODE_DOUBLE));
00056   if (glconfig == 0)
00057   {
00058     g_print ("*** Cannot find the double-buffered visual.\n");
00059     g_print ("*** Trying single-buffered visual.\n");
00060 
00061     // Try single-buffered visual
00062     glconfig = gdk_gl_config_new_by_mode(GdkGLConfigMode(GDK_GL_MODE_RGB |
00063                                                          GDK_GL_MODE_DEPTH));
00064     if (glconfig == 0)
00065     {
00066       g_print ("*** No appropriate OpenGL-capable visual found.\n");
00067       vcl_abort();
00068     }
00069   }
00070 
00071   widget = gtk_drawing_area_new ();
00072   //gtk_widget_set_size_request (drawing_area, 300, 300);
00073 
00074   if (!widget)
00075   {
00076     vcl_cerr << __FILE__ << " : Could not get a GL widget!\n";
00077     vcl_abort();
00078   }
00079 
00080   // Set OpenGL-capability to the widget.
00081   if ( !gtk_widget_set_gl_capability(widget,
00082                                      glconfig,
00083                                      0 /*NULL*/,
00084                                      TRUE,
00085                                      GDK_GL_RGBA_TYPE) )
00086   {
00087     vcl_cerr << __FILE__ << " : Could not set GL capability!\n";
00088     vcl_abort();
00089   }
00090 
00091   // Since we need to access the widget from time to time (e.g. to
00092   // make this OpenGL context the current context), we need to keep a
00093   // reference to the widget.
00094   gtk_object_ref( GTK_OBJECT(widget) );
00095 
00096   gtk_widget_set_events(widget,
00097                         GDK_EXPOSURE_MASK |
00098                         GDK_POINTER_MOTION_MASK |
00099                         GDK_POINTER_MOTION_HINT_MASK |
00100                         GDK_BUTTON_PRESS_MASK |
00101                         GDK_BUTTON_RELEASE_MASK |
00102                         GDK_KEY_PRESS_MASK |
00103                         GDK_KEY_RELEASE_MASK |
00104                         GDK_ENTER_NOTIFY_MASK |
00105                         GDK_LEAVE_NOTIFY_MASK);
00106 
00107 #if 0
00108   gtk_signal_connect(GTK_OBJECT(widget), "event", GTK_SIGNAL_FUNC(handle), this);
00109 #else
00110   gtk_signal_connect(GTK_OBJECT(widget), "configure_event", GTK_SIGNAL_FUNC(handle_configure), this);
00111   gtk_signal_connect(GTK_OBJECT(widget), "expose_event", GTK_SIGNAL_FUNC(handle_draw), this);
00112   gtk_signal_connect(GTK_OBJECT(widget), "map_event", GTK_SIGNAL_FUNC(handle_draw), this);
00113   gtk_signal_connect(GTK_OBJECT(widget), "motion_notify_event", GTK_SIGNAL_FUNC(handle_motion_notify), this);
00114   gtk_signal_connect(GTK_OBJECT(widget), "button_press_event", GTK_SIGNAL_FUNC(handle_button), this);
00115   gtk_signal_connect(GTK_OBJECT(widget), "button_release_event", GTK_SIGNAL_FUNC(handle_button), this);
00116   gtk_signal_connect(GTK_OBJECT(widget), "key_press_event", GTK_SIGNAL_FUNC(handle_key), this);
00117   gtk_signal_connect(GTK_OBJECT(widget), "key_release_event", GTK_SIGNAL_FUNC(handle_key), this);
00118   gtk_signal_connect(GTK_OBJECT(widget), "enter_notify_event", GTK_SIGNAL_FUNC(handle_enter_leave), this);
00119   gtk_signal_connect(GTK_OBJECT(widget), "leave_notify_event", GTK_SIGNAL_FUNC(handle_enter_leave), this);
00120 #endif
00121   GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
00122 
00123   redraw_requested = false;
00124   destroy_requested = false;
00125 }
00126 
00127 //: Destructor
00128 vgui_gtk2_adaptor::~vgui_gtk2_adaptor()
00129 {
00130   if (ovl_helper)
00131     delete ovl_helper;
00132   ovl_helper = 0;
00133 
00134   glFlush();
00135   gtk_object_unref( GTK_OBJECT(widget) );
00136   widget = 0; // to detect potential bugs
00137 }
00138 
00139 
00140 vgui_window* vgui_gtk2_adaptor::get_window() const
00141 {
00142   return win_;
00143 }
00144 
00145 void vgui_gtk2_adaptor::swap_buffers()
00146 {
00147   make_current();
00148   gdk_gl_drawable_swap_buffers (gtk_widget_get_gl_drawable(widget));
00149 }
00150 
00151 void vgui_gtk2_adaptor::make_current()
00152 {
00153   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
00154   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
00155   assert( gldrawable );
00156   gdk_gl_drawable_make_current(gldrawable, glcontext);
00157 }
00158 
00159 void vgui_gtk2_adaptor::post_redraw()
00160 {
00161   if (!redraw_requested)
00162   {
00163     redraw_requested = true;
00164     gtk_idle_add(idle_callback_for_redraw, this);
00165   }
00166 }
00167 
00168 void vgui_gtk2_adaptor::post_overlay_redraw()
00169 {
00170   if (!ovl_helper)
00171     ovl_helper = new vgui_overlay_helper(this);
00172   ovl_helper->post_overlay_redraw();
00173 }
00174 
00175 void vgui_gtk2_adaptor::post_idle_request()
00176 {
00177   if (!idle_request_posted_){
00178     idle_request_posted_ = true;
00179     g_idle_add(idle_callback_for_tableaux, this);
00180   }
00181 }
00182 
00183 //: gtk will pass this structure to the timer callback.
00184 typedef struct
00185 {
00186   vgui_gtk2_adaptor *adapt;
00187   int name;
00188 } vgui_gtk2_adaptor_callback_data;
00189 
00190 //: timeout is in milliseconds
00191 void vgui_gtk2_adaptor::post_timer(float timeout, int name)
00192 {
00193   vgui_gtk2_adaptor_callback_data *cd = new vgui_gtk2_adaptor_callback_data; // <*> acquire
00194   cd->adapt = this;
00195   cd->name = name;
00196 
00197   gint id = gtk_timeout_add(int(timeout),
00198                             timeout_callback,
00199                             cd);
00200 
00201   // add them to timer map
00202   internal_timer i( id, (void*)cd );
00203   timers_.insert( vcl_pair<int, internal_timer>(name, i) );
00204 }
00205 
00206 //: timeout is in milliseconds
00207 void vgui_gtk2_adaptor::kill_timer(int name)
00208 {
00209   vcl_map<int, internal_timer>::iterator it
00210     = timers_.find( name );
00211   if ( it == timers_.end() )  // if such timer does not exist
00212     return;
00213 
00214   internal_timer timer;
00215   timer = (*it).second;
00216   // remove timer
00217   gtk_timeout_remove(timer.real_id_);
00218   // remove callback ptr
00219   delete  (vgui_gtk2_adaptor_callback_data*)(timer.callback_ptr_);
00220 
00221   // remove timer from map
00222   timers_.erase(it);
00223 }
00224 
00225 void vgui_gtk2_adaptor::post_destroy()
00226 {
00227   if (!destroy_requested)
00228   {
00229     destroy_requested = true;
00230     gtk_idle_add(idle_callback_for_destroy, this);
00231   }
00232 }
00233 
00234 void vgui_gtk2_adaptor::set_default_popup(vgui_menu)
00235 {
00236 #ifdef DEBUG
00237   vcl_cerr << "vgui_gtk2_adaptor::set_default_popup\n";
00238 #endif
00239 }
00240 
00241 vgui_menu vgui_gtk2_adaptor::get_popup()
00242 {
00243 #ifdef DEBUG
00244   vcl_cerr << "vgui_gtk2_adaptor::get_popup\n";
00245 #endif
00246   return vgui_menu();
00247 }
00248 
00249 gint vgui_gtk2_adaptor::handle_configure(
00250                                GtkWidget *widget,
00251                                GdkEvent *gev,
00252                                gpointer context)
00253 {
00254   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00255 
00256   // The following 5 lines are required to make GL context available
00257   // so that some GL functions (such as glGenLists()) can succeed.
00258   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
00259   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
00260   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
00261     return FALSE;
00262   gdk_gl_drawable_gl_end (gldrawable);
00263 
00264   adaptor->reshape();
00265   return TRUE;
00266 }
00267 
00268 gint vgui_gtk2_adaptor::handle_draw(
00269                                GtkWidget *widget,
00270                                GdkEvent *gev,
00271                                gpointer context)
00272 {
00273   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00274   adaptor->draw();
00275   return TRUE;
00276 }
00277 
00278 gint vgui_gtk2_adaptor::handle_motion_notify(
00279                                GtkWidget *widget,
00280                                GdkEvent *gev,
00281                                gpointer context)
00282 {
00283   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00284   vgui_event event;
00285 
00286   event.type = vgui_MOTION;
00287   GdkEventMotion *e = (GdkEventMotion*)gev;
00288   if (e->is_hint)
00289   {
00290     int x,y;
00291     GdkModifierType state;
00292     gdk_window_get_pointer(e->window, &x, &y, &state);
00293     vgui_gtk2_utils::set_modifiers(event, state);
00294     vgui_gtk2_utils::set_coordinates(event, x, y);
00295   }
00296   else
00297   {
00298     vgui_gtk2_utils::set_modifiers(event,e->state);
00299     vgui_gtk2_utils::set_coordinates(event,e->x, e->y);
00300   }
00301   adaptor->last_mouse_x = event.wx;
00302   adaptor->last_mouse_y = event.wy;
00303 
00304   return handle(event, widget, gev, context);
00305 }
00306 
00307 
00308 gint vgui_gtk2_adaptor::handle_button(
00309                                GtkWidget *widget,
00310                                GdkEvent *gev,
00311                                gpointer context)
00312 {
00313   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00314   vgui_event event;
00315   GdkEventType type = gev->type;
00316 
00317   if (type==GDK_BUTTON_PRESS)
00318     event.type = vgui_BUTTON_DOWN;
00319   else if (type==GDK_BUTTON_RELEASE)
00320     event.type = vgui_BUTTON_UP;
00321   GdkEventButton *e = (GdkEventButton*)gev;
00322   event.button = vgui_gtk2_utils::translate_button(e->button);
00323   vgui_gtk2_utils::set_modifiers(event,e->state);
00324   vgui_gtk2_utils::set_coordinates(event,e->x, e->y);
00325   adaptor->last_mouse_x = event.wx;
00326   adaptor->last_mouse_y = event.wy;
00327 
00328   if (event.type == vgui_BUTTON_DOWN &&
00329       event.button == adaptor->popup_button &&
00330       event.modifier == adaptor->popup_modifier)
00331   {
00332     GdkEventButton *bevent = (GdkEventButton *)gev;
00333 
00334     GtkWidget *popup_menu = gtk_menu_new ();    /* Don't need to show menus */
00335 
00336     vgui_popup_params params;
00337     params.x = event.wx;
00338     params.y = event.wy;
00339 
00340     // fsm - assign the popup menu to 'last_popup' to ensure the
00341     // commands stay in scope for the lifetime of the gtk popup.
00342     adaptor->last_popup = adaptor->get_total_popup(params);
00343 
00344     vgui_gtk2_utils::set_menu(popup_menu, adaptor->last_popup, false);
00345     gtk_menu_popup(GTK_MENU(popup_menu), 0/*NULL*/, 0/*NULL*/, 0/*NULL*/, 0/*NULL*/,
00346                    bevent->button, bevent->time);
00347     return TRUE;
00348   }
00349 
00350   return handle(event, widget, gev, context);
00351 }
00352 
00353 gint vgui_gtk2_adaptor::handle_key(
00354                                GtkWidget *widget,
00355                                GdkEvent *gev,
00356                                gpointer context)
00357 {
00358   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00359   vgui_event event;
00360   GdkEventType type = gev->type;
00361 
00362   if (type==GDK_KEY_PRESS)
00363     event.type = vgui_KEY_PRESS;
00364   else if (type==GDK_KEY_RELEASE)
00365     event.type = vgui_KEY_RELEASE;
00366   GdkEventKey *e = (GdkEventKey*)gev;
00367   event.set_key( vgui_gtk2_utils::translate_key(e));
00368   event.ascii_char = vgui_gtk2_utils::translate_key(e);
00369   vgui_gtk2_utils::set_modifiers(event,e->state);
00370   event.wx = adaptor->last_mouse_x;
00371   event.wy = adaptor->last_mouse_y;
00372 
00373   return handle(event, widget, gev, context);
00374 }
00375 
00376 gint vgui_gtk2_adaptor::handle_enter_leave(
00377                                GtkWidget *widget,
00378                                GdkEvent *gev,
00379                                gpointer context)
00380 {
00381   vgui_event event;
00382   GdkEventType type = gev->type;
00383 
00384   if (type==GDK_ENTER_NOTIFY)
00385   {
00386     event.type = vgui_ENTER;
00387     gtk_widget_grab_focus(GTK_WIDGET(widget));
00388   }
00389   else if (type==GDK_LEAVE_NOTIFY)
00390   {
00391     event.type = vgui_LEAVE;
00392   }
00393 
00394   return handle(event, widget, gev, context);
00395 }
00396 
00397 gint vgui_gtk2_adaptor::handle(const vgui_event &event,
00398                                GtkWidget *widget,
00399                                GdkEvent *gev,
00400                                gpointer context)
00401 {
00402   vgui_gtk2_adaptor* adaptor = (vgui_gtk2_adaptor*) context;
00403 
00404   bool ret_value = TRUE;
00405   if (vgui_gtk2_utils::is_modifier(gev))
00406     ret_value = FALSE;
00407 
00408 #ifdef DEBUG
00409   vcl_cerr << "vgui_event " << event << '\n';
00410 #endif
00411   // Only send events to the tableau if the widget is mapped; that is,
00412   // only when an OpenGL context exists.
00413   if ( GTK_WIDGET_MAPPED(widget) )
00414   {
00415     if (adaptor->ovl_helper)
00416       adaptor->ovl_helper->dispatch(event);
00417     else
00418       adaptor->dispatch_to_tableau(event);
00419   }
00420   else
00421     vcl_cerr << __FILE__ << ": error: event " << event
00422              << " while GL area was not mapped\n";
00423 
00424   return ret_value;
00425 }
00426 
00427 
00428 void vgui_gtk2_adaptor::reshape()
00429 {
00430   width = widget->allocation.width;
00431   height = widget->allocation.height;
00432 
00433   // Only send events to the tableau if the widget is mapped; that is,
00434   // only when an OpenGL context exists.
00435   if ( GTK_WIDGET_MAPPED(widget) )
00436   {
00437     make_current();
00438     if (ovl_helper)
00439       ovl_helper->dispatch(vgui_RESHAPE);
00440     else
00441       dispatch_to_tableau(vgui_RESHAPE);
00442   }
00443 }
00444 
00445 bool vgui_gtk2_adaptor::do_idle()
00446 {
00447   if ( idle_request_posted_ )
00448     idle_request_posted_ =  dispatch_to_tableau( vgui_event( vgui_IDLE ) );
00449   return idle_request_posted_;
00450 }
00451 
00452 //--------------------------------------------------------------------------------
00453 //: This is overriding the gtk draw() method.
00454 void vgui_gtk2_adaptor::draw()
00455 {
00456 #ifdef DEBUG
00457   vcl_cerr << "vgui_gtk2_adaptor::draw\n";
00458 #endif
00459   if ( GTK_WIDGET_MAPPED(widget) )
00460   {
00461     make_current();
00462     glDrawBuffer(GL_BACK);
00463     if (ovl_helper)
00464       ovl_helper->dispatch(vgui_DRAW);
00465     else
00466     {
00467       dispatch_to_tableau(vgui_DRAW);
00468       swap_buffers();
00469     }
00470   }
00471 }
00472 
00473 
00474 gint vgui_gtk2_adaptor::idle_callback_for_tableaux(gpointer data)
00475 {
00476   vgui_gtk2_adaptor *adaptor = static_cast<vgui_gtk2_adaptor*>(data);
00477 
00478   return adaptor->do_idle();
00479 }
00480 
00481 
00482 gint vgui_gtk2_adaptor::idle_callback_for_redraw(gpointer data)
00483 {
00484   vgui_gtk2_adaptor *adaptor = static_cast<vgui_gtk2_adaptor*>(data);
00485 
00486   adaptor->draw();
00487 
00488   adaptor->redraw_requested = false;
00489 
00490   // capes - returning FALSE automagically cancels this callback
00491   return FALSE;
00492 }
00493 
00494 // Callback setup by post_destroy. First notifies tableau of the impending
00495 // destruction. Then deletes the adaptor and its associated window.
00496 gint vgui_gtk2_adaptor::idle_callback_for_destroy(gpointer data)
00497 {
00498   vgui_gtk2_adaptor *adaptor = static_cast<vgui_gtk2_adaptor*>(data);
00499 
00500   adaptor->dispatch_to_tableau(vgui_DESTROY);
00501 
00502   vgui_window* win = adaptor->get_window();
00503 
00504   // If we know the parent window then delete it now.
00505   if (win)
00506     delete win;
00507   else
00508     vcl_cerr << __FILE__ " : parent vgui_gtk2_window is unknown, so cannot destroy!\n";
00509 
00510   // The adaptor destructor unrefs its tableau and disconnects/destroys
00511   // its glarea widget
00512   delete adaptor;
00513 
00514   // capes - returning FALSE automagically cancels this callback
00515   return FALSE;
00516 }
00517 
00518 extern "C" {
00519   gint timeout_callback(gpointer data)
00520   {
00521     vgui_gtk2_adaptor_callback_data* cd = static_cast<vgui_gtk2_adaptor_callback_data*> (data);
00522     vgui_event e(vgui_TIMER);
00523     e.timer_id = cd->name;
00524     cd->adapt->dispatch_to_tableau(e);
00525 
00526     // return non-zero value to reuse the timer
00527     return true;
00528   }
00529 }