1 // GTK_WND.CPP
2
3 // Copyright (C) 2005 Tommi Hassinen.
4
5 // This package is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9
10 // This package is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this package; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 /*################################################################################################*/
20
21 #include "gtk_wnd.h"
22
23 #include "gtk_app.h"
24 #include "local_i18n.h"
25
26 #include <GL/gl.h>
27 #include <GL/glu.h>
28
29 /*################################################################################################*/
30
31 vector<gtk_wnd *> gtk_wnd::iv;
32
gtk_wnd(bool det_flag)33 gtk_wnd::gtk_wnd(bool det_flag) :
34 base_wnd()
35 {
36 iv.push_back(this);
37
38 view_widget = NULL;
39
40 detached = NULL;
41
42 label_widget = NULL;
43 popupmenu = NULL;
44
45 GdkGLConfigMode mode = (GdkGLConfigMode) 0;
46 GdkGLConfig * glconfig = NULL;
47
48 mode = (GdkGLConfigMode) (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE);
49 glconfig = gdk_gl_config_new_by_mode(mode); // try a double-buffered visual...
50
51 if (glconfig == NULL)
52 {
53 g_print(_("*** Cannot find the double-buffered visual.\n"));
54 g_print(_("*** Trying single-buffered visual.\n"));
55
56 mode = (GdkGLConfigMode) (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
57 glconfig = gdk_gl_config_new_by_mode(mode); // try a single-buffered visual...
58
59 if (glconfig == NULL)
60 {
61 g_print(_("*** No appropriate OpenGL-capable visual found.\n"));
62 exit(EXIT_FAILURE);
63 }
64 }
65
66 g_print (_("\nOpenGL visual configurations :\n\n"));
67 g_print ("gdk_gl_config_is_rgba (glconfig) = %s\n", gdk_gl_config_is_rgba (glconfig) ? "TRUE" : "FALSE");
68 g_print ("gdk_gl_config_is_double_buffered (glconfig) = %s\n", gdk_gl_config_is_double_buffered (glconfig) ? "TRUE" : "FALSE");
69 g_print ("gdk_gl_config_is_stereo (glconfig) = %s\n", gdk_gl_config_is_stereo (glconfig) ? "TRUE" : "FALSE");
70 g_print ("gdk_gl_config_has_alpha (glconfig) = %s\n", gdk_gl_config_has_alpha (glconfig) ? "TRUE" : "FALSE");
71 g_print ("gdk_gl_config_has_depth_buffer (glconfig) = %s\n", gdk_gl_config_has_depth_buffer (glconfig) ? "TRUE" : "FALSE");
72 g_print ("gdk_gl_config_has_stencil_buffer (glconfig) = %s\n", gdk_gl_config_has_stencil_buffer (glconfig) ? "TRUE" : "FALSE");
73 g_print ("gdk_gl_config_has_accum_buffer (glconfig) = %s\n", gdk_gl_config_has_accum_buffer (glconfig) ? "TRUE" : "FALSE");
74 g_print ("\n");
75
76 view_widget = gtk_drawing_area_new();
77 gtk_widget_set_size_request(view_widget, 100, 100); // minimum size...
78
79 gtk_widget_set_gl_capability(view_widget, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
80
81 int events = GDK_EXPOSURE_MASK;
82 events |= GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
83 events |= GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
84 gtk_widget_set_events(GTK_WIDGET(view_widget), events);
85
86 gtk_signal_connect(GTK_OBJECT(view_widget), "expose_event", GTK_SIGNAL_FUNC(ExposeHandler), NULL);
87 gtk_signal_connect(GTK_OBJECT(view_widget), "button_press_event", GTK_SIGNAL_FUNC(ButtonHandler), NULL);
88 gtk_signal_connect(GTK_OBJECT(view_widget), "button_release_event", GTK_SIGNAL_FUNC(ButtonHandler), NULL);
89 gtk_signal_connect(GTK_OBJECT(view_widget), "motion_notify_event", GTK_SIGNAL_FUNC(MotionNotifyHandler), NULL);
90 gtk_signal_connect(GTK_OBJECT(view_widget), "configure_event", GTK_SIGNAL_FUNC(ConfigureHandler), NULL);
91
92 gtk_signal_connect_after(GTK_OBJECT(view_widget), "realize", GTK_SIGNAL_FUNC(RealizeHandler), NULL); // after!!!
93
94 gtk_widget_show(GTK_WIDGET(view_widget));
95
96 if (det_flag)
97 {
98 detached = gtk_window_new(GTK_WINDOW_TOPLEVEL);
99
100 gtk_window_set_default_size(GTK_WINDOW(detached), 500, 300);
101 gtk_app::GetAppX()->SetTransientForMainWnd(GTK_WINDOW(detached));
102
103 // we could set the window title here, but at this stage
104 // we are always unlinked and cannot get the title text...
105
106 gtk_container_add(GTK_CONTAINER(detached), view_widget);
107 gtk_signal_connect(GTK_OBJECT(detached), "delete_event", GTK_SIGNAL_FUNC(gtk_wnd::DetachedDeleteHandler), NULL);
108
109 gtk_widget_show(detached);
110 }
111 }
112
~gtk_wnd(void)113 gtk_wnd::~gtk_wnd(void)
114 {
115 if (detached != NULL)
116 {
117 gtk_widget_destroy(GTK_WIDGET(detached));
118 detached = NULL;
119
120 // gtk_widget_destroy(GTK_WIDGET(view_widget)); // ???crash???
121 view_widget = NULL; // already destroyed as a child widget?
122 }
123 else
124 {
125 // gtk_widget_destroy(GTK_WIDGET(view_widget)); // ???crash???
126 view_widget = NULL; // already destroyed as a child widget?
127 }
128
129 // remove the record from iv...
130 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131
132 unsigned int i = 0;
133 while (i < iv.size())
134 {
135 if (iv[i] == this) break;
136 else i++;
137 }
138
139 if (i < iv.size())
140 {
141 iv.erase(iv.begin() + i);
142 }
143 else
144 {
145 assertion_failed(__FILE__, __LINE__, "removal from iv failed!");
146 }
147 }
148
iv_Find(GtkWidget * vw)149 gtk_wnd * gtk_wnd::iv_Find(GtkWidget * vw)
150 {
151 for (unsigned int i = 0;i < iv.size();i++)
152 {
153 if (iv[i]->view_widget == vw) return iv[i];
154 }
155
156 return NULL;
157 }
158
RealizeHandler(GtkWidget * widget,gpointer)159 void gtk_wnd::RealizeHandler(GtkWidget * widget, gpointer)
160 {
161 //cout << "DEBUG : gtk_wnd::RealizeHandler()" << endl;
162 ////////////////////////////////////////////////////////////
163
164 // realize signal will occur when the widget is created.
165
166 gtk_wnd * w = iv_Find(widget);
167 if (w == NULL) cout << "DEBUG : gtk_wnd::RealizeHandler() : iv_Find() failed!" << endl;
168 else
169 {
170 w->SetRealized();
171
172 // here it would be natural to do the things like these:
173 // SetCurrent() + GetClient()->InitGL() + RequestUpdate(false).
174
175 // the problem is that usually GetClient() will return NULL at
176 // this stage ; if this is the case, then do nothing here and
177 // handle the problem elsewhere (see base_wcl::LinkWnd()).
178
179 // this is really a timing issue since we do not know at which
180 // point the widget is going to be realized...
181
182 if (w->GetClient() != NULL && !w->GetInitialized())
183 {
184 // usually this stuff is skipped...
185 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
186
187 w->SetInitialized();
188
189 // w->SetCurrent(); no begin/end here...
190 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
191
192 GdkGLContext * glcontext = gtk_widget_get_gl_context(w->view_widget);
193 GdkGLDrawable * gldrawable = gtk_widget_get_gl_drawable(w->view_widget);
194
195 if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
196 {
197 g_print("DEBUG : gtk_wnd::RealizeHandler() : gdk_gl_drawable_make_current() failed.\n");
198 }
199 else
200 {
201 gdk_gl_drawable_gl_begin(gldrawable, glcontext);
202
203 w->GetClient()->InitGL();
204
205 gdk_gl_drawable_gl_end(gldrawable);
206 }
207
208 w->RequestUpdate(false);
209 }
210 }
211
212 // no return value for this event?!?!?!
213 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
214 }
215
ExposeHandler(GtkWidget * widget,GdkEventExpose * event)216 gint gtk_wnd::ExposeHandler(GtkWidget * widget, GdkEventExpose * event)
217 {
218 //cout << "DEBUG : gtk_wnd::ExposeHandler()" << endl;
219 ////////////////////////////////////////////////////////////
220
221 // expose_event will occur when the window should be re-drawn.
222 // IS SOMETHING WRONG IN HERE??? see the comments on configure-handler...
223 // IS SOMETHING WRONG IN HERE??? see the comments on configure-handler...
224 // IS SOMETHING WRONG IN HERE??? see the comments on configure-handler...
225
226 if (event->count > 0) return TRUE; // draw only last expose.
227
228 gtk_wnd * w = iv_Find(widget);
229 if (w == NULL) cout << "DEBUG : gtk_wnd::ButtonHandler() : iv_Find() failed!" << endl;
230 else
231 {
232 if (!w->GetRealized())
233 {
234 assertion_failed(__FILE__, __LINE__, "NOT REALIZED!");
235 }
236
237 if (!w->GetInitialized())
238 {
239 assertion_failed(__FILE__, __LINE__, "NOT INITIALIZED!");
240 }
241
242 base_wcl * wcl = w->GetClient();
243 if (wcl != NULL)
244 {
245 GdkGLContext * glcontext = gtk_widget_get_gl_context(w->view_widget);
246 GdkGLDrawable * gldrawable = gtk_widget_get_gl_drawable(w->view_widget);
247
248 if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
249 {
250 g_print("DEBUG : gtk_wnd::ExposeHandler() : gdk_gl_drawable_make_current() failed.\n");
251 }
252 else
253 {
254 gdk_gl_drawable_gl_begin(gldrawable, glcontext);
255
256 wcl->UpdateWnd();
257
258 if (gdk_gl_drawable_is_double_buffered(gldrawable))
259 {
260 gdk_gl_drawable_swap_buffers(gldrawable);
261 }
262 else
263 {
264 glFlush();
265 }
266
267 gdk_gl_drawable_gl_end(gldrawable);
268 }
269 }
270 }
271
272 // return TRUE; // which one this should be???
273 return FALSE; // which one this should be??? scribble.c example used this.
274 }
275
276 int button_event_lost_counter = 0;
277
ButtonHandler(GtkWidget * widget,GdkEventButton * eb)278 gint gtk_wnd::ButtonHandler(GtkWidget * widget, GdkEventButton * eb)
279 {
280 //cout << "DEBUG : gtk_wnd::ButtonHandler()" << endl;
281 ////////////////////////////////////////////////////////////
282
283 // button_press/release_event(s) are triggered by mouse events.
284
285 gtk_wnd * w = iv_Find(widget);
286 if (w == NULL) cout << "DEBUG : gtk_wnd::ButtonHandler() : iv_Find() failed!" << endl;
287 else
288 {
289 if (!w->GetRealized())
290 {
291 assertion_failed(__FILE__, __LINE__, "NOT REALIZED!");
292 }
293
294 mouseinfo::mi_button tmpb;
295 i32s tmps1;
296
297 switch (eb->button)
298 {
299 case 1:
300 tmpb = mouseinfo::bLeft;
301 tmps1 = GDK_BUTTON1_MASK;
302 break;
303
304 case 3:
305 tmpb = mouseinfo::bRight;
306 tmps1 = GDK_BUTTON3_MASK;
307 break;
308
309 default:
310 tmpb = mouseinfo::bMiddle;
311 tmps1 = GDK_BUTTON2_MASK;
312 }
313
314 mouseinfo::mi_state tmps2 = (eb->state & tmps1) ? mouseinfo::sUp : mouseinfo::sDown;
315
316 if (tmps2 == mouseinfo::sDown)
317 {
318 if (mouseinfo::button == mouseinfo::bNone)
319 {
320 if (tmpb == mouseinfo::bRight)
321 {
322 if (project::background_job_running) return TRUE;
323
324 // the popup menu is created here. pointer to the gtk_drawing_area
325 // widget is given as "user_data", and it is also passed to the popup
326 // hander callback function (instead of the original value).
327
328 if (w->popupmenu != NULL)
329 {
330 gtk_menu_popup(GTK_MENU(w->popupmenu), NULL, NULL, NULL, NULL, eb->button, eb->time);
331 }
332
333 return TRUE;
334 }
335
336 mouseinfo::button = tmpb;
337
338 mouseinfo::shift_down = (eb->state & GDK_SHIFT_MASK) ? true : false;
339 mouseinfo::ctrl_down = (eb->state & GDK_CONTROL_MASK) ? true : false;
340
341 mouseinfo::state = mouseinfo::sDown;
342
343 // cout << "DEBUG : button_event_D " << mouseinfo::button << " " << mouseinfo::state << endl;
344 w->GetClient()->ButtonEvent((i32s) eb->x, (i32s) eb->y);
345
346 // this is for exceptions, see below...
347 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
348 button_event_lost_counter = 0;
349 }
350 }
351 else
352 {
353 if (mouseinfo::button == mouseinfo::bLeft && tmpb != mouseinfo::bLeft) return TRUE;
354 if (mouseinfo::button == mouseinfo::bMiddle && tmpb != mouseinfo::bMiddle) return TRUE;
355 if (mouseinfo::button == mouseinfo::bRight && tmpb != mouseinfo::bRight) return TRUE;
356
357 mouseinfo::state = mouseinfo::sUp;
358
359 // cout << "DEBUG : button_event_U " << mouseinfo::button << " " << mouseinfo::state << endl;
360 w->GetClient()->ButtonEvent((i32s) eb->x, (i32s) eb->y);
361
362 mouseinfo::button = mouseinfo::bNone;
363 }
364 }
365
366 return TRUE;
367 }
368
MotionNotifyHandler(GtkWidget * widget,GdkEventMotion * event)369 gint gtk_wnd::MotionNotifyHandler(GtkWidget * widget, GdkEventMotion * event)
370 {
371 //cout << "DEBUG : gtk_wnd::MotionNotifyHandler()" << endl;
372 ////////////////////////////////////////////////////////////
373
374 // mouse movements will trigger the events here...
375
376 int x; int y; GdkModifierType mbstate;
377 if (event->is_hint) gdk_window_get_pointer(event->window, & x, & y, & mbstate);
378 else { x = (int) event->x; y = (int) event->y; mbstate = (GdkModifierType) event->state; }
379
380 // here it is good to check if we have lost a "mouse button up" message.
381 // it can happen if a user moves the mouse outside to the graphics window,
382 // and then changes the mousebutton state.
383
384 // if we think that a mouse button should be down, but GTK+ says it's not,
385 // then immediately send a "mouse button down" message...
386
387 bool no_buttons_down = !(mbstate & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK));
388 if (no_buttons_down && mouseinfo::button != mouseinfo::bNone)
389 {
390 button_event_lost_counter++;
391 if (button_event_lost_counter > 1)
392 {
393 gtk_wnd * w = iv_Find(widget);
394 if (w == NULL) cout << "DEBUG : gtk_wnd::MotionNotifyHandler() : iv_Find() failed!" << endl;
395 else
396 {
397 cout << "DEBUG : WARNING ; a mouse-button-up event was lost!" << endl;
398
399 mouseinfo::state = mouseinfo::sUp;
400
401 // cout << "DEBUG : button_event_U " << mouseinfo::button << " " << mouseinfo::state << endl;
402 w->GetClient()->ButtonEvent((i32s) x, (i32s) y);
403
404 mouseinfo::button = mouseinfo::bNone;
405 }
406 }
407 }
408
409 // the normal operation starts here...
410 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
411
412 if (mouseinfo::button != mouseinfo::bNone)
413 {
414 gtk_wnd * w = iv_Find(widget);
415 if (w == NULL) cout << "DEBUG : gtk_wnd::MotionNotifyHandler() : iv_Find() failed!" << endl;
416 else
417 {
418 // cout << "DEBUG : motion_event " << mouseinfo::button << " " << mouseinfo::state << endl;
419 w->GetClient()->MotionEvent(x, y);
420 }
421 }
422
423 return TRUE;
424 }
425
ConfigureHandler(GtkWidget * widget,GdkEventConfigure *)426 gint gtk_wnd::ConfigureHandler(GtkWidget * widget, GdkEventConfigure *)
427 {
428 //cout << "DEBUG : gtk_wnd::ConfigureHandler()" << endl;
429 ////////////////////////////////////////////////////////////
430
431 // configure_event will occur at each window size change,
432 // and when the window is initially created.
433
434 gtk_wnd * w = iv_Find(widget);
435 if (w != NULL)
436 {
437 w->SetWidth(widget->allocation.width);
438 w->SetHeight(widget->allocation.height);
439 }
440 else cout << "DEBUG : iv_Find() failed at gtk_wnd::ConfigureHandler()." << endl;
441
442 // the screen is NOT always properly updated after a configure-event.
443 // sometimes it gets rendered 100% fine, but sometimes only partially
444 // or sometimes not at all (leaving a blank screen). it is possible to
445 // do w->RequestUpdate(false); here but it does not affect the result...
446
447 return TRUE;
448 }
449
DetachedDeleteHandler(GtkWidget *,GdkEvent *)450 gint gtk_wnd::DetachedDeleteHandler(GtkWidget *, GdkEvent *)
451 {
452 // when we create detached view windows as GTK_WINDOW_TOPLEVEL, the
453 // window will have the "close" button at titlebar. now if the user
454 // presses the "close" button, the window-closing sequence will start.
455 // we will grab the resulting delete_event here and return TRUE, that
456 // will deny the user's request to close the window. the user should
457 // use the stardard popup-way of closing the window...
458
459 return TRUE;
460 }
461
GetViewWidget(void)462 GtkWidget * gtk_wnd::GetViewWidget(void)
463 {
464 return view_widget;
465 }
466
IsDetached(void)467 bool gtk_wnd::IsDetached(void)
468 {
469 return (detached != NULL);
470 }
471
RequestUpdate(bool directly)472 void gtk_wnd::RequestUpdate(bool directly)
473 {
474 if (directly)
475 {
476 gdk_window_invalidate_rect(view_widget->window, & view_widget->allocation, FALSE);
477 gdk_window_process_updates(view_widget->window, FALSE);
478 }
479 else
480 {
481 // this is the original ; replaced 20061006 TH
482 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
483 // gtk_widget_queue_draw(view_widget);
484
485 gdk_window_invalidate_rect(view_widget->window, & view_widget->allocation, FALSE);
486 }
487 }
488
RequestResize(int,int)489 void gtk_wnd::RequestResize(int, int)
490 {
491 cout << "DEBUG : gtk_wnd::RequestResize() not yet implemented!" << endl;
492 }
493
SetCurrent(void)494 bool gtk_wnd::SetCurrent(void)
495 {
496 GdkGLContext * glcontext = gtk_widget_get_gl_context(view_widget);
497 GdkGLDrawable * gldrawable = gtk_widget_get_gl_drawable(view_widget);
498
499 if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
500 {
501 g_print("DEBUG : gtk_wnd::SetCurrent() : gdk_gl_drawable_make_current() failed.\n");
502 return false;
503 }
504 else
505 {
506 return true;
507 }
508 }
509
TitleChanged(void)510 void gtk_wnd::TitleChanged(void)
511 {
512 if (GetClient() == NULL)
513 {
514 assertion_failed(__FILE__, __LINE__, "wnd is unlinked.");
515 }
516
517 if (detached != NULL)
518 {
519 gtk_window_set_title(GTK_WINDOW(detached), GetClient()->GetTitle());
520 }
521 else
522 {
523 //GtkWidget * oldlabel = label_widget; // not needed??? 20061115 TH
524
525 label_widget = gtk_label_new(GetClient()->GetTitle());
526 gtk_widget_show(label_widget);
527
528 gtk_app::GetAppX()->SetTabTitleNB(view_widget, label_widget);
529
530 //if (oldlabel != NULL) gtk_widget_destroy(oldlabel); // not needed??? 20061115 TH
531 }
532 }
533
534 /*################################################################################################*/
535
536 // eof
537