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