core/vgui/impl/gtk/vgui_gtk_adaptor.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/gtk/vgui_gtk_adaptor.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief  See vgui_gtk_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_gtk_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 <gtkgl/gtkglarea.h>
00025 
00026 #include <vgui/vgui_gl.h>
00027 #include <vgui/vgui_popup_params.h>
00028 #include <vgui/internals/vgui_overlay_helper.h>
00029 #include "vgui_gtk_utils.h"
00030 #include "vgui_gtk_window.h"
00031 
00032 static bool debug = false;
00033 vgui_menu vgui_gtk_adaptor::last_popup;
00034 
00035 extern "C" { static gint timeout_callback(gpointer); }
00036 
00037 //--------------------------------------------------------------------------------
00038 //: Constructors
00039 vgui_gtk_adaptor::vgui_gtk_adaptor(vgui_gtk_window* win)
00040   : widget(0),
00041     win_(win),
00042     ovl_helper(0),
00043     last_mouse_x(0),
00044     last_mouse_y(0)
00045 {
00046   widget = gtk_gl_area_new_vargs(0/*NULL*/,         // no sharing
00047                                  GDK_GL_RGBA,
00048                                  GDK_GL_DOUBLEBUFFER,
00049                                  GDK_GL_RED_SIZE, 8,
00050                                  GDK_GL_GREEN_SIZE, 8,
00051                                  GDK_GL_BLUE_SIZE, 8,
00052                                  //GDK_GL_ALPHA_SIZE, 8,
00053                                  GDK_GL_DEPTH_SIZE,1,
00054                                  GDK_GL_NONE); // last argument must be GDK_GL_NONE
00055   if (!widget) {
00056     widget = gtk_gl_area_new_vargs(0/*NULL*/,         // no sharing
00057                                    GDK_GL_RGBA,
00058                                    GDK_GL_DOUBLEBUFFER,
00059                                    GDK_GL_RED_SIZE, 5,
00060                                    GDK_GL_GREEN_SIZE, 6,
00061                                    GDK_GL_BLUE_SIZE, 5,
00062                                    GDK_GL_DEPTH_SIZE,1,
00063                                    GDK_GL_NONE); // last argument must be GDK_GL_NONE
00064   }
00065 
00066   if (!widget) {
00067     widget = gtk_gl_area_new_vargs(0/*NULL*/,         // no sharing
00068                                    GDK_GL_RGBA,
00069                                    GDK_GL_DOUBLEBUFFER,
00070                                    GDK_GL_DEPTH_SIZE,1,
00071                                    GDK_GL_NONE); // last argument must be GDK_GL_NONE
00072   }
00073 
00074   if (!widget) {
00075     vcl_cerr << __FILE__ << " : Could not get a GL visual!\n";
00076     vcl_abort();
00077   }
00078 
00079   // Since we need to access the widget from time to time (e.g. to
00080   // make this OpenGL context the current context), we need to keep a
00081   // reference to the widget.
00082   gtk_object_ref( GTK_OBJECT(widget) );
00083 
00084   gtk_widget_set_events(widget,
00085                         GDK_EXPOSURE_MASK |
00086                         GDK_POINTER_MOTION_MASK |
00087                         GDK_POINTER_MOTION_HINT_MASK |
00088                         GDK_BUTTON_PRESS_MASK |
00089                         GDK_BUTTON_RELEASE_MASK |
00090                         GDK_KEY_PRESS_MASK |
00091                         GDK_KEY_RELEASE_MASK |
00092                         GDK_ENTER_NOTIFY_MASK |
00093                         GDK_LEAVE_NOTIFY_MASK);
00094 
00095   gtk_signal_connect(GTK_OBJECT(widget), "event", GTK_SIGNAL_FUNC(handle), this);
00096 
00097   GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
00098 
00099   redraw_requested = false;
00100   destroy_requested = false;
00101 }
00102 
00103 //: Destructor
00104 vgui_gtk_adaptor::~vgui_gtk_adaptor()
00105 {
00106   if (ovl_helper)
00107     delete ovl_helper;
00108   ovl_helper = 0;
00109 
00110   glFlush();
00111   gtk_object_unref( GTK_OBJECT(widget) );
00112   widget = 0; // to detect potential bugs
00113 }
00114 
00115 
00116 vgui_window* vgui_gtk_adaptor::get_window() const
00117 {
00118   return win_;
00119 }
00120 
00121 void vgui_gtk_adaptor::swap_buffers()
00122 {
00123   make_current();
00124   gtk_gl_area_swapbuffers(GTK_GL_AREA(widget));
00125 }
00126 
00127 void vgui_gtk_adaptor::make_current()
00128 {
00129   assert(gtk_gl_area_make_current(GTK_GL_AREA(widget)));
00130 }
00131 
00132 void vgui_gtk_adaptor::post_redraw()
00133 {
00134   if (!redraw_requested) {
00135     redraw_requested = true;
00136     gtk_idle_add(idle_callback_for_redraw, this);
00137   }
00138 }
00139 
00140 void vgui_gtk_adaptor::post_overlay_redraw()
00141 {
00142   if (!ovl_helper)
00143     ovl_helper = new vgui_overlay_helper(this);
00144   ovl_helper->post_overlay_redraw();
00145 }
00146 
00147 //: gtk will pass this structure to the timer callback.
00148 typedef struct
00149 {
00150   vgui_gtk_adaptor *adapt;
00151   int name;
00152 } vgui_gtk_adaptor_callback_data;
00153 
00154 //: timeout is in milliseconds
00155 void vgui_gtk_adaptor::post_timer(float timeout, int name)
00156 {
00157   vgui_gtk_adaptor_callback_data *cd = new vgui_gtk_adaptor_callback_data; // <*> acquire
00158   cd->adapt = this;
00159   cd->name = name;
00160 
00161   gint id = gtk_timeout_add(int(timeout),
00162                             timeout_callback,
00163                             cd);
00164 
00165   // add them to timer map
00166   internal_timer i( id, (void*)cd );
00167   timers_.insert( vcl_pair<int, internal_timer>(name, i) );
00168 }
00169 
00170 //: timeout is in milliseconds
00171 void vgui_gtk_adaptor::kill_timer(int name)
00172 {
00173   vcl_map<int, internal_timer>::iterator it
00174     = timers_.find( name );
00175   if ( it == timers_.end() )  // if such timer does not exist
00176     return;
00177 
00178   internal_timer timer;
00179   timer = (*it).second;
00180   // remove timer
00181   gtk_timeout_remove(timer.real_id_);
00182   // remove callback ptr
00183   delete  (vgui_gtk_adaptor_callback_data*)(timer.callback_ptr_);
00184 
00185   // remove timer from map
00186   timers_.erase(it);
00187 }
00188 
00189 void vgui_gtk_adaptor::post_destroy()
00190 {
00191   if (!destroy_requested) {
00192     destroy_requested = true;
00193     gtk_idle_add(idle_callback_for_destroy, this);
00194   }
00195 }
00196 
00197 void vgui_gtk_adaptor::set_default_popup(vgui_menu)
00198 {
00199   vcl_cerr << "vgui_gtk_adaptor::set_default_popup\n";
00200 }
00201 
00202 vgui_menu vgui_gtk_adaptor::get_popup()
00203 {
00204   vcl_cerr << "vgui_gtk_adaptor::get_popup\n";
00205   return vgui_menu();
00206 }
00207 
00208 gint vgui_gtk_adaptor::handle(GtkWidget *widget,
00209                               GdkEvent *gev,
00210                               gpointer context)
00211 {
00212   vgui_gtk_adaptor* adaptor = (vgui_gtk_adaptor*) context;
00213 
00214   bool ret_value = TRUE;
00215   if (vgui_gtk_utils::is_modifier(gev))
00216     ret_value = FALSE;
00217 
00218   vgui_event event;
00219 
00220   GdkEventType type = gev->type;
00221 
00222   if (type==GDK_EXPOSE || type==GDK_MAP) {
00223     adaptor->draw();
00224     return TRUE;
00225   }
00226   else if (type==GDK_CONFIGURE) {
00227     adaptor->reshape();
00228     return TRUE;
00229   }
00230   else if (type==GDK_MOTION_NOTIFY) {
00231     event.type = vgui_MOTION;
00232     GdkEventMotion *e = (GdkEventMotion*)gev;
00233     if (e->is_hint) {
00234       int x,y;
00235       GdkModifierType state;
00236       gdk_window_get_pointer(e->window, &x, &y, &state);
00237       vgui_gtk_utils::set_modifiers(event, state);
00238       vgui_gtk_utils::set_coordinates(event, x, y);
00239     } else {
00240       vgui_gtk_utils::set_modifiers(event,e->state);
00241       vgui_gtk_utils::set_coordinates(event,e->x, e->y);
00242     }
00243     adaptor->last_mouse_x = event.wx;
00244     adaptor->last_mouse_y = event.wy;
00245   }
00246   else if (type==GDK_BUTTON_PRESS) {
00247     event.type = vgui_BUTTON_DOWN;
00248     GdkEventButton *e = (GdkEventButton*)gev;
00249     event.button = vgui_gtk_utils::translate_button(e->button);
00250     vgui_gtk_utils::set_modifiers(event,e->state);
00251     vgui_gtk_utils::set_coordinates(event,e->x, e->y);
00252     adaptor->last_mouse_x = event.wx;
00253     adaptor->last_mouse_y = event.wy;
00254   }
00255   else if (type==GDK_BUTTON_RELEASE) {
00256     event.type = vgui_BUTTON_UP;
00257     GdkEventButton *e = (GdkEventButton*)gev;
00258     event.button = vgui_gtk_utils::translate_button(e->button);
00259     vgui_gtk_utils::set_modifiers(event,e->state);
00260     vgui_gtk_utils::set_coordinates(event,e->x, e->y);
00261     adaptor->last_mouse_x = event.wx;
00262     adaptor->last_mouse_y = event.wy;
00263   }
00264   else if (type==GDK_KEY_PRESS) {
00265     event.type = vgui_KEY_PRESS;
00266     GdkEventKey *e = (GdkEventKey*)gev;
00267     event.set_key( vgui_gtk_utils::translate_key(e));
00268     event.ascii_char = vgui_gtk_utils::translate_key(e);
00269     vgui_gtk_utils::set_modifiers(event,e->state);
00270     event.wx = adaptor->last_mouse_x;
00271     event.wy = adaptor->last_mouse_y;
00272   }
00273   else if (type==GDK_KEY_RELEASE) {
00274     event.type = vgui_KEY_RELEASE;
00275     GdkEventKey *e = (GdkEventKey*)gev;
00276     event.set_key( vgui_gtk_utils::translate_key(e));
00277     event.ascii_char = vgui_gtk_utils::translate_key(e);
00278     vgui_gtk_utils::set_modifiers(event,e->state);
00279     event.wx = adaptor->last_mouse_x;
00280     event.wy = adaptor->last_mouse_y;
00281   }
00282   else if (type==GDK_ENTER_NOTIFY) {
00283     event.type = vgui_ENTER;
00284     gtk_widget_grab_focus(GTK_WIDGET(widget));
00285   }
00286   else if (type==GDK_LEAVE_NOTIFY) {
00287     event.type = vgui_LEAVE;
00288   }
00289   else {
00290     event.type = vgui_OTHER;
00291   }
00292 
00293   if (event.type == vgui_BUTTON_DOWN &&
00294       event.button == adaptor->popup_button &&
00295       event.modifier == adaptor->popup_modifier)
00296   {
00297     GdkEventButton *bevent = (GdkEventButton *)gev;
00298 
00299     GtkWidget *popup_menu = gtk_menu_new ();    /* Don't need to show menus */
00300 
00301     vgui_popup_params params;
00302     params.x = event.wx;
00303     params.y = event.wy;
00304 
00305     // fsm - assign the popup menu to 'last_popup' to ensure the
00306     // commands stay in scope for the lifetime of the gtk popup.
00307     adaptor->last_popup = adaptor->get_total_popup(params);
00308 
00309     vgui_gtk_utils::set_menu(popup_menu, adaptor->last_popup, false);
00310     gtk_menu_popup(GTK_MENU(popup_menu), 0/*NULL*/, 0/*NULL*/, 0/*NULL*/, 0/*NULL*/,
00311                    bevent->button, bevent->time);
00312     return TRUE;
00313   }
00314 
00315   if (debug) vcl_cerr << "vgui_event " << event << vcl_endl;
00316   // Only send events to the tableau if the widget is mapped; that is,
00317   // only when an OpenGL context exists.
00318   if ( GTK_WIDGET_MAPPED(widget) ) {
00319     if (adaptor->ovl_helper)
00320       adaptor->ovl_helper->dispatch(event);
00321     else
00322       adaptor->dispatch_to_tableau(event);
00323   } else {
00324     vcl_cerr << __FILE__ << ": error: event " << event
00325              << " while GL area was not mapped\n";
00326   }
00327 
00328   return ret_value;
00329 }
00330 
00331 
00332 void vgui_gtk_adaptor::reshape()
00333 {
00334   width = widget->allocation.width;
00335   height = widget->allocation.height;
00336 
00337   // Only send events to the tableau if the widget is mapped; that is,
00338   // only when an OpenGL context exists.
00339   if ( GTK_WIDGET_MAPPED(widget) ) {
00340     make_current();
00341     if (ovl_helper)
00342       ovl_helper->dispatch(vgui_RESHAPE);
00343     else
00344       dispatch_to_tableau(vgui_RESHAPE);
00345   }
00346 }
00347 
00348 
00349 //--------------------------------------------------------------------------------
00350 //: This is overriding the gtk draw() method.
00351 void vgui_gtk_adaptor::draw()
00352 {
00353   if (debug) vcl_cerr << "vgui_gtk_adaptor::draw\n";
00354   if ( GTK_WIDGET_MAPPED(widget) ) {
00355     make_current();
00356     glDrawBuffer(GL_BACK);
00357     if (ovl_helper)
00358       ovl_helper->dispatch(vgui_DRAW);
00359     else {
00360       dispatch_to_tableau(vgui_DRAW);
00361       swap_buffers();
00362     }
00363   }
00364 }
00365 
00366 
00367 gint vgui_gtk_adaptor::idle_callback_for_redraw(gpointer data)
00368 {
00369   vgui_gtk_adaptor *adaptor = static_cast<vgui_gtk_adaptor*>(data);
00370 
00371   adaptor->draw();
00372 
00373   adaptor->redraw_requested = false;
00374 
00375   // capes - returning FALSE automagically cancels this callback
00376   return FALSE;
00377 }
00378 
00379 // Callback setup by post_destroy. First notifies tableau of the impending
00380 // destruction. Then deletes the adaptor and its associated window.
00381 gint vgui_gtk_adaptor::idle_callback_for_destroy(gpointer data)
00382 {
00383   vgui_gtk_adaptor *adaptor = static_cast<vgui_gtk_adaptor*>(data);
00384 
00385   adaptor->dispatch_to_tableau(vgui_DESTROY);
00386 
00387   vgui_window* win = adaptor->get_window();
00388 
00389   // The adaptor destructor unrefs its tableau and disconnects/destroys
00390   // its glarea widget
00391   delete adaptor;
00392 
00393   // If we know the parent window then delete it now.
00394   if (win)
00395     delete win;
00396   else
00397     vcl_cerr << __FILE__ " : parent vgui_gtk_window is unknown, so cannot destroy!\n";
00398 
00399   // capes - returning FALSE automagically cancels this callback
00400   return FALSE;
00401 }
00402 
00403 extern "C" {
00404 
00405 gint timeout_callback(gpointer data)
00406 {
00407   vgui_gtk_adaptor_callback_data* cd = static_cast<vgui_gtk_adaptor_callback_data*> (data);
00408   vgui_event e(vgui_TIMER);
00409   e.timer_id = cd->name;
00410   cd->adapt->dispatch_to_tableau(e);
00411 
00412   delete cd;                             // <*> release
00413 
00414   // capes - returning FALSE automagically cancels this callback
00415   return FALSE;
00416 }
00417 
00418 } // extern "C"