1 /**************************************************************************
2  * coolwave2.c
3  *
4  * Copyright (c) 2002 Alif Wahid <awah005@users.sourceforge.net>
5  *
6  * A program to demonstrate GtkGLExt. It's a modified version
7  * of the old IrisGL demo 'newave', first ported to OpenGL and
8  * Glut by Erik Larsen. Now I have modified it to use Gtk and GtkGLExt
9  * comprehensively along with ofcourse OpenGL.
10  *
11  * This program is in the public domain and you are using it at
12  * your own risk.
13  *
14  **************************************************************************/
15 
16 /*
17  * Modified by Naofumi Yasufuku  <naofumi@users.sourceforge.net>
18  */
19 
20 /* September, 2003.
21  *
22  * A slightly different version from the coolwave.c example program. Here
23  * the primary goal is to test how well GtkGLExt works with GTK's
24  * ability to display a window in fullscreen mode. It's quite nice to have
25  * a 3D animation displayed in fullscreen mode!
26  *
27  * So just use the menu items to change between the fullscreen and normal
28  * size mode.
29  *
30  * Alif Wahid.
31  */
32 
33 /**************************************************************************
34  * Header file inclusions.
35  **************************************************************************/
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <math.h>
40 
41 #include <gtk/gtk.h>
42 #include <gdk/gdkkeysyms.h>
43 
44 #include <gtk/gtkgl.h>
45 /*** Use OpenGL extensions. ***/
46 #include <gdk/gdkglglext.h>
47 
48 #ifdef G_OS_WIN32
49 #define WIN32_LEAN_AND_MEAN 1
50 #include <windows.h>
51 #endif
52 
53 #include <GL/gl.h>
54 #include <GL/glu.h>
55 
56 
57 /**************************************************************************
58  * The following section contains all the macro definitions.
59  **************************************************************************/
60 
61 #define DEFAULT_WIDTH  300
62 #define DEFAULT_HEIGHT 200
63 #define DEFAULT_TITLE  "CoolWave2"
64 
65 #define TIMEOUT_INTERVAL 10
66 
67 #define MAXGRID 64
68 #define SQRTOFTWOINV (1.0 / 1.414213562)
69 
70 
71 /**************************************************************************
72  * Global variable declarations.
73  **************************************************************************/
74 
75 static gboolean animate = TRUE;
76 
77 static int grid = (MAXGRID/2);
78 static int beginX, beginY;
79 
80 static float force[MAXGRID][MAXGRID];
81 static float veloc[MAXGRID][MAXGRID];
82 static float posit[MAXGRID][MAXGRID];
83 
84 static float dt = 0.008;
85 static float sphi = 90.0;
86 static float stheta = 45.0;
87 static float sdepth = 5.0/4.0 * (MAXGRID/2);
88 static float zNear = (MAXGRID/2)/10.0;
89 static float zFar = (MAXGRID/2)*3.0;
90 static float aspect = 5.0/4.0;
91 
92 static float lightPosition[4] = {0.0, 0.0, 1.0, 1.0};
93 
94 
95 /**************************************************************************
96  * The following section contains the function prototype declarations.
97  **************************************************************************/
98 
99 static void         timeout_add       (GtkWidget   *widget);
100 static void         timeout_remove    (GtkWidget   *widget);
101 
102 static void         toggle_animation  (GtkWidget   *widget);
103 static void         init_wireframe    (GtkWidget   *widget);
104 
105 static GdkGLConfig *configure_gl      (void);
106 
107 static GtkWidget   *create_popup_menu (GtkWidget   *window, GtkWidget   *drawing_area);
108 static GtkWidget   *create_window     (GdkGLConfig *glconfig);
109 
110 
111 /**************************************************************************
112  * The waving functions.
113  **************************************************************************/
114 
getforce(void)115 void getforce (void)
116 {
117   int i=0, j=0;
118   float d;
119 
120   for(i=0;i<grid;i++)
121     {
122       for(j=0;j<grid;j++)
123 	{
124 	  force[i][j]=0.0;
125 	}
126     }
127 
128   for(i=2;i<grid-2;i++)
129     {
130       for(j=2;j<grid-2;j++)
131 	{
132 	  d=posit[i][j]-posit[i][j-1];
133 	  force[i][j] -= d;
134 	  force[i][j-1] += d;
135 
136 	  d=posit[i][j]-posit[i-1][j];
137 	  force[i][j] -= d;
138 	  force[i-1][j] += d;
139 
140 	  d= (posit[i][j]-posit[i][j+1]);
141 	  force[i][j] -= d ;
142 	  force[i][j+1] += d;
143 
144 	  d= (posit[i][j]-posit[i+1][j]);
145 	  force[i][j] -= d ;
146 	  force[i+1][j] += d;
147 
148 	  d= (posit[i][j]-posit[i+1][j+1])*SQRTOFTWOINV;
149 	  force[i][j] -= d ;
150 	  force[i+1][j+1] += d;
151 
152 	  d= (posit[i][j]-posit[i-1][j-1])*SQRTOFTWOINV;
153 	  force[i][j] -= d ;
154 	  force[i-1][j-1] += d;
155 
156 	  d= (posit[i][j]-posit[i+1][j-1])*SQRTOFTWOINV;
157 	  force[i][j] -= d ;
158 	  force[i+1][j-1] += d;
159 
160 	  d= (posit[i][j]-posit[i-1][j+1])*SQRTOFTWOINV;
161 	  force[i][j] -= d ;
162 	  force[i- 1][j+1] += d;
163 	}
164     }
165 }
166 
getvelocity(void)167 void getvelocity (void)
168 {
169   int i=0, j=0;
170 
171   for(i=0;i<grid;i++)
172     {
173       for(j=0;j<grid;j++) veloc[i][j]+=force[i][j] * dt;
174     }
175 }
176 
getposition(void)177 void getposition (void)
178 {
179   int i=0, j=0;
180 
181   for(i=0;i<grid;i++)
182     {
183       for(j=0;j<grid;j++) posit[i][j]+=veloc[i][j];
184     }
185 }
186 
drawWireframe(void)187 void drawWireframe (void)
188 {
189   int i=0, j=0;
190 
191   glColor3f(1.0, 1.0, 1.0);
192 
193   for(i=0;i<grid;i++)
194     {
195       glBegin(GL_LINE_STRIP);
196       for(j=0;j<grid;j++) glVertex3f((float)i,(float)j,(float)posit[i][j]);
197       glEnd();
198     }
199 
200   for(i=0;i<grid;i++)
201     {
202       glBegin(GL_LINE_STRIP);
203       for(j=0;j<grid;j++) glVertex3f((float)j,(float)i,(float)posit[j][i]);
204       glEnd();
205     }
206 }
207 
resetWireframe(void)208 void resetWireframe (void)
209 {
210   int i=0, j=0;
211 
212   for(i=0;i<grid;i++)
213     {
214       for(j=0;j<grid;j++)
215 	{
216 	  force[i][j]=0.0;
217 	  veloc[i][j]=0.0;
218 
219 	  posit[i][j]= 	(sin(G_PI*2 * ((float)i/(float)grid)) +
220 			 sin(G_PI*2 * ((float)j/(float)grid)))* grid/6.0;
221 
222 	  if (i==0||j==0||i==grid-1||j==grid-1) posit[i][j]=0.0;
223 	}
224     }
225 }
226 
227 
228 /**************************************************************************
229  * The following section contains all the callback function definitions.
230  **************************************************************************/
231 
232 /***
233  *** The "realize" signal handler. All the OpenGL initialization
234  *** should be performed here, such as default background colour,
235  *** certain states etc.
236  ***/
237 static void
realize(GtkWidget * widget,gpointer data)238 realize (GtkWidget *widget,
239 	 gpointer   data)
240 {
241   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
242   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
243 
244   GdkGLProc proc = NULL;
245 
246   /*** OpenGL BEGIN ***/
247   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
248     return;
249 
250   /* glPolygonOffsetEXT */
251   proc = gdk_gl_get_glPolygonOffsetEXT ();
252   if (proc == NULL)
253     {
254       /* glPolygonOffset */
255       proc = gdk_gl_get_proc_address ("glPolygonOffset");
256       if (proc == NULL)
257 	{
258 	  g_print ("Sorry, glPolygonOffset() is not supported by this renderer.\n");
259 	  exit (1);
260 	}
261     }
262 
263   glEnable (GL_DEPTH_TEST);
264   glDepthFunc (GL_LEQUAL);
265   glClearColor (0.0, 0.0, 0.0, 0.0);
266   gdk_gl_glPolygonOffsetEXT (proc, 1.0, 1.0);
267   glEnable (GL_CULL_FACE);
268   glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
269   glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
270   glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
271   glEnable (GL_COLOR_MATERIAL);
272   glColorMaterial (GL_FRONT, GL_DIFFUSE);
273   glLightfv (GL_LIGHT0, GL_POSITION, lightPosition);
274   glEnable (GL_LIGHT0);
275   glShadeModel (GL_FLAT);
276   glDisable (GL_LIGHTING);
277 
278   resetWireframe ();
279 
280   gdk_gl_drawable_gl_end (gldrawable);
281   /*** OpenGL END ***/
282 
283   return;
284 }
285 
286 /***
287  *** The "configure_event" signal handler. Any processing required when
288  *** the OpenGL-capable drawing area is re-configured should be done here.
289  *** Almost always it will be used to resize the OpenGL viewport when
290  *** the window is resized.
291  ***/
292 static gboolean
configure_event(GtkWidget * widget,GdkEventConfigure * event,gpointer data)293 configure_event (GtkWidget         *widget,
294 		 GdkEventConfigure *event,
295 		 gpointer           data)
296 {
297   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
298   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
299 
300   GLfloat w = widget->allocation.width;
301   GLfloat h = widget->allocation.height;
302 
303   /*** OpenGL BEGIN ***/
304   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
305     return FALSE;
306 
307   aspect = (float)w/(float)h;
308   glViewport (0, 0, w, h);
309 
310   gdk_gl_drawable_gl_end (gldrawable);
311   /*** OpenGL END ***/
312 
313   return TRUE;
314 }
315 
316 /***
317  *** The "expose_event" signal handler. All the OpenGL re-drawing should
318  *** be done here. This is repeatedly called as the painting routine
319  *** every time the 'expose'/'draw' event is signalled.
320  ***/
321 static gboolean
expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer data)322 expose_event (GtkWidget      *widget,
323 	      GdkEventExpose *event,
324 	      gpointer        data)
325 {
326   GdkGLContext *glcontext = gtk_widget_get_gl_context(widget);
327   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget);
328 
329   /*** OpenGL BEGIN ***/
330   if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
331     return FALSE;
332 
333   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
334 
335   glMatrixMode (GL_PROJECTION);
336   glLoadIdentity ();
337   gluPerspective (64.0, aspect, zNear, zFar);
338   glMatrixMode (GL_MODELVIEW);
339   glLoadIdentity ();
340 
341   glTranslatef (0.0,0.0,-sdepth);
342   glRotatef (-stheta, 1.0, 0.0, 0.0);
343   glRotatef (sphi, 0.0, 0.0, 1.0);
344   glTranslatef (-(float)((grid+1)/2-1), -(float)((grid+1)/2-1), 0.0);
345 
346   drawWireframe ();
347 
348   /* Swap buffers */
349   if (gdk_gl_drawable_is_double_buffered (gldrawable))
350     gdk_gl_drawable_swap_buffers (gldrawable);
351   else
352     glFlush ();
353 
354   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
355 
356   gdk_gl_drawable_gl_end (gldrawable);
357   /*** OpenGL END ***/
358 
359   return TRUE;
360 }
361 
362 /***
363  *** The timeout function. Often in animations,
364  *** timeout functions are suitable for continous
365  *** frame updates.
366  ***/
367 static gboolean
timeout(GtkWidget * widget)368 timeout (GtkWidget *widget)
369 {
370   getforce ();
371   getvelocity ();
372   getposition ();
373 
374   /* Invalidate the whole window. */
375   gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
376 
377   /* Update synchronously (fast). */
378   gdk_window_process_updates (widget->window, FALSE);
379 
380   return TRUE;
381 }
382 
383 /***
384  *** The "motion_notify_event" signal handler. Any processing required when
385  *** the OpenGL-capable drawing area is under drag motion should be done here.
386  ***/
387 static gboolean
motion_notify_event(GtkWidget * widget,GdkEventMotion * event,gpointer data)388 motion_notify_event (GtkWidget      *widget,
389 		     GdkEventMotion *event,
390 		     gpointer        data)
391 {
392   gboolean redraw = FALSE;
393 
394   if (event->state & GDK_BUTTON1_MASK)
395     {
396       sphi += (float)(event->x - beginX) / 4.0;
397       stheta += (float)(beginY - event->y) / 4.0;
398 
399       redraw = TRUE;
400     }
401 
402   if (event->state & GDK_BUTTON2_MASK)
403     {
404       sdepth -= ((event->y - beginY)/(widget->allocation.height))*(MAXGRID/2);
405 
406       redraw = TRUE;
407     }
408 
409   beginX = event->x;
410   beginY = event->y;
411 
412   if (redraw && !animate)
413     gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
414 
415   return TRUE;
416 }
417 
418 /***
419  *** The "button_press_event" signal handler. Any processing required when
420  *** mouse buttons (only left and middle buttons) are pressed on the OpenGL-
421  *** capable drawing area should be done here.
422  ***/
423 static gboolean
button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer data)424 button_press_event (GtkWidget      *widget,
425 		    GdkEventButton *event,
426 		    gpointer        data)
427 {
428   if (event->button == 1)
429     {
430       beginX = event->x;
431       beginY = event->y;
432       return TRUE;
433     }
434 
435   if (event->button == 2)
436     {
437       beginX = event->x;
438       beginY = event->y;
439       return TRUE;
440     }
441 
442   return FALSE;
443 }
444 
445 /* For popup menu. */
446 static gboolean
button_press_event_popup_menu(GtkWidget * widget,GdkEventButton * event,gpointer data)447 button_press_event_popup_menu (GtkWidget      *widget,
448 			       GdkEventButton *event,
449 			       gpointer        data)
450 {
451   if (event->button == 3)
452     {
453       /* Popup menu. */
454       gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
455 		      event->button, event->time);
456       return TRUE;
457     }
458 
459   return FALSE;
460 }
461 
462 /***
463  *** The "key_press_event" signal handler. Any processing required when key
464  *** presses occur should be done here.
465  ***/
466 
467 static gboolean
key_press_event(GtkWidget * widget,GdkEventKey * event,gpointer data)468 key_press_event (GtkWidget   *widget,
469 		 GdkEventKey *event,
470 		 gpointer     data)
471 {
472   switch (event->keyval)
473     {
474     case GDK_r:
475       init_wireframe (widget);
476       break;
477 
478     case GDK_a:
479       toggle_animation (widget);
480       break;
481 
482     case GDK_w:
483       if (!animate)
484 	timeout (widget);
485       break;
486 
487     case GDK_plus:
488       sdepth -= 2.0;
489       break;
490 
491     case GDK_minus:
492       sdepth += 2.0;
493       break;
494 
495     case GDK_Escape:
496       gtk_main_quit ();
497       break;
498 
499     default:
500       return FALSE;
501     }
502 
503   if (!animate)
504     gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
505 
506   return TRUE;
507 }
508 
509 /***
510  *** The "unrealize" signal handler. Any processing required when
511  *** the OpenGL-capable window is unrealized should be done here.
512  ***/
513 static void
unrealize(GtkWidget * widget,gpointer data)514 unrealize (GtkWidget *widget,
515 	   gpointer   data)
516 {
517   /*** Fill in the details here ***/
518 
519 }
520 
521 
522 /**************************************************************************
523  * The following section contains the timeout function management routines.
524  **************************************************************************/
525 
526 /***
527  *** Helper functions to add or remove the timeout function.
528  ***/
529 
530 static guint timeout_id = 0;
531 
532 static void
timeout_add(GtkWidget * widget)533 timeout_add (GtkWidget *widget)
534 {
535   if (timeout_id == 0)
536     {
537       timeout_id = g_timeout_add (TIMEOUT_INTERVAL,
538                                   (GSourceFunc) timeout,
539                                   widget);
540     }
541 }
542 
543 static void
timeout_remove(GtkWidget * widget)544 timeout_remove (GtkWidget *widget)
545 {
546   if (timeout_id != 0)
547     {
548       g_source_remove (timeout_id);
549       timeout_id = 0;
550     }
551 }
552 
553 /***
554  *** The "map_event" signal handler. Any processing required when the
555  *** OpenGL-capable drawing area is mapped should be done here.
556  ***/
557 static gboolean
map_event(GtkWidget * widget,GdkEvent * event,gpointer data)558 map_event (GtkWidget *widget,
559 	   GdkEvent  *event,
560 	   gpointer   data)
561 {
562   if (animate)
563     timeout_add (widget);
564 
565   return TRUE;
566 }
567 
568 /***
569  *** The "unmap_event" signal handler. Any processing required when the
570  *** OpenGL-capable drawing area is unmapped should be done here.
571  ***/
572 static gboolean
unmap_event(GtkWidget * widget,GdkEvent * event,gpointer data)573 unmap_event (GtkWidget *widget,
574 	     GdkEvent  *event,
575 	     gpointer   data)
576 {
577   timeout_remove (widget);
578 
579   return TRUE;
580 }
581 
582 /***
583  *** The "visibility_notify_event" signal handler. Any processing required
584  *** when the OpenGL-capable drawing area is visually obscured should be
585  *** done here.
586  ***/
587 static gboolean
visibility_notify_event(GtkWidget * widget,GdkEventVisibility * event,gpointer data)588 visibility_notify_event (GtkWidget          *widget,
589 			 GdkEventVisibility *event,
590 			 gpointer            data)
591 {
592   if (animate)
593     {
594       if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
595 	timeout_remove (widget);
596       else
597 	timeout_add (widget);
598     }
599 
600   return TRUE;
601 }
602 
603 
604 /**************************************************************************
605  * The following section contains some miscellaneous utility functions.
606  **************************************************************************/
607 
608 /***
609  *** Toggle animation.
610  ***/
611 static void
toggle_animation(GtkWidget * widget)612 toggle_animation (GtkWidget *widget)
613 {
614   animate = !animate;
615 
616   if (animate)
617     {
618       timeout_add (widget);
619     }
620   else
621     {
622       timeout_remove (widget);
623       gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
624     }
625 }
626 
627 /***
628  *** Init wireframe model.
629  ***/
630 static void
init_wireframe(GtkWidget * widget)631 init_wireframe (GtkWidget *widget)
632 {
633   resetWireframe ();
634   gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
635 }
636 
637 
638 /**************************************************************************
639  * The following section contains the GUI building function definitions.
640  **************************************************************************/
641 
642 /* Two callbacks for the popup menu items Fullscreen and Unfullscreen.
643  * These are just making sure that the user can't select the same menu
644  * item among these, twice in a row by making the current item selected
645  * insensitive. GTK+-2.2.3 on my WinXP box seems to ignore any calls to
646  * unfullscreen if the user has selected fullscreen on a window that's
647  * already fullscreen.
648  */
fullscreen_window(GtkWidget * current,gpointer other)649 static void fullscreen_window (GtkWidget *current, gpointer other)
650 {
651   GtkWidget *window = g_object_get_data(G_OBJECT(current), "window");
652   if (window)
653     {
654 	  gtk_widget_set_sensitive(GTK_WIDGET(current), FALSE);
655 	  gtk_widget_set_sensitive(GTK_WIDGET(other), TRUE);
656 	  gtk_window_fullscreen(GTK_WINDOW(window));
657     }
658 }
659 
unfullscreen_window(GtkWidget * current,gpointer other)660 static void unfullscreen_window (GtkWidget *current, gpointer other)
661 {
662   GtkWidget *window = g_object_get_data(G_OBJECT(current), "window");
663   if (window)
664     {
665 	  gtk_widget_set_sensitive(GTK_WIDGET(current), FALSE);
666 	  gtk_widget_set_sensitive(GTK_WIDGET(other), TRUE);
667 	  gtk_window_unfullscreen(GTK_WINDOW(window));
668     }
669 }
670 
671 /***
672  *** Creates the popup menu to be displayed.
673  ***/
674 static GtkWidget *
create_popup_menu(GtkWidget * window,GtkWidget * drawing_area)675 create_popup_menu (GtkWidget *window, GtkWidget *drawing_area)
676 {
677   GtkWidget *menu;
678   GtkWidget *menu_item;
679   GtkWidget *full_item;
680   GtkWidget *unfull_item;
681 
682   menu = gtk_menu_new ();
683 
684   /* Toggle animation */
685   menu_item = gtk_menu_item_new_with_label ("Toggle Animation");
686   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
687   g_signal_connect_swapped (G_OBJECT (menu_item), "activate",
688 			    G_CALLBACK (toggle_animation), drawing_area);
689   gtk_widget_show (menu_item);
690 
691   /* Init wireframe model */
692   menu_item = gtk_menu_item_new_with_label ("Initialize");
693   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
694   g_signal_connect_swapped (G_OBJECT (menu_item), "activate",
695 			    G_CALLBACK (init_wireframe), drawing_area);
696   gtk_widget_show (menu_item);
697 
698   /* Fullscreen window. */
699   full_item = gtk_menu_item_new_with_label ("Fullscreen");
700   g_object_set_data(G_OBJECT(full_item), "window", window);
701   gtk_menu_shell_append (GTK_MENU_SHELL (menu), full_item);
702 
703   /* Un-fullscreen window. */
704   unfull_item = gtk_menu_item_new_with_label ("Unfullscreen");
705   g_object_set_data(G_OBJECT(unfull_item), "window", window);
706   gtk_menu_shell_append (GTK_MENU_SHELL (menu), unfull_item);
707   gtk_widget_set_sensitive(unfull_item, FALSE);
708 
709   /* Connect the fullscreen and unfullscreen callbacks. */
710   g_signal_connect (G_OBJECT (full_item), "activate",
711 			    G_CALLBACK (fullscreen_window), unfull_item);
712   g_signal_connect (G_OBJECT (unfull_item), "activate",
713 			    G_CALLBACK (unfullscreen_window), full_item);
714   gtk_widget_show (full_item);
715   gtk_widget_show (unfull_item);
716 
717   /* Quit */
718   menu_item = gtk_menu_item_new_with_label ("Quit");
719   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
720   g_signal_connect (G_OBJECT (menu_item), "activate",
721 		    G_CALLBACK (gtk_main_quit), NULL);
722   gtk_widget_show (menu_item);
723 
724   return menu;
725 }
726 
727 /***
728  *** Creates the simple application window with one
729  *** drawing area that has an OpenGL-capable visual.
730  ***/
731 static GtkWidget *
create_window(GdkGLConfig * glconfig)732 create_window (GdkGLConfig *glconfig)
733 {
734   GtkWidget *window;
735   GtkWidget *drawing_area;
736   GtkWidget *menu;
737 
738   /*
739    * Top-level window.
740    */
741 
742   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
743   gtk_window_set_title (GTK_WINDOW (window), DEFAULT_TITLE);
744 
745   /* Get automatically redrawn if any of their children changed allocation. */
746   gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE);
747 
748   /* Connect signal handlers to the window */
749   g_signal_connect (G_OBJECT (window), "delete_event",
750 		    G_CALLBACK (gtk_main_quit), NULL);
751 
752   /*
753    * Drawing area to draw OpenGL scene.
754    */
755 
756   drawing_area = gtk_drawing_area_new ();
757   gtk_widget_set_size_request (drawing_area, DEFAULT_WIDTH, DEFAULT_HEIGHT);
758 
759   /* Set OpenGL-capability to the widget */
760   gtk_widget_set_gl_capability (drawing_area,
761 				glconfig,
762 				NULL,
763 				TRUE,
764 				GDK_GL_RGBA_TYPE);
765 
766   gtk_widget_add_events (drawing_area,
767 			 GDK_BUTTON1_MOTION_MASK    |
768 			 GDK_BUTTON2_MOTION_MASK    |
769 			 GDK_BUTTON_PRESS_MASK      |
770 			 GDK_VISIBILITY_NOTIFY_MASK);
771 
772   /* Connect signal handlers to the drawing area */
773   g_signal_connect_after (G_OBJECT (drawing_area), "realize",
774                           G_CALLBACK (realize), NULL);
775   g_signal_connect (G_OBJECT (drawing_area), "configure_event",
776 		    G_CALLBACK (configure_event), NULL);
777   g_signal_connect (G_OBJECT (drawing_area), "expose_event",
778 		    G_CALLBACK (expose_event), NULL);
779 
780   g_signal_connect (G_OBJECT (drawing_area), "motion_notify_event",
781 		    G_CALLBACK (motion_notify_event), NULL);
782   g_signal_connect (G_OBJECT (drawing_area), "button_press_event",
783 		    G_CALLBACK (button_press_event), NULL);
784   g_signal_connect (G_OBJECT (drawing_area), "unrealize",
785 		    G_CALLBACK (unrealize), NULL);
786 
787   /* key_press_event handler for top-level window */
788   g_signal_connect_swapped (G_OBJECT (window), "key_press_event",
789 			    G_CALLBACK (key_press_event), drawing_area);
790 
791   /* For timeout function. */
792   g_signal_connect (G_OBJECT (drawing_area), "map_event",
793 		    G_CALLBACK (map_event), NULL);
794   g_signal_connect (G_OBJECT (drawing_area), "unmap_event",
795 		    G_CALLBACK (unmap_event), NULL);
796   g_signal_connect (G_OBJECT (drawing_area), "visibility_notify_event",
797 		    G_CALLBACK (visibility_notify_event), NULL);
798 
799   gtk_container_add (GTK_CONTAINER (window), drawing_area);
800   gtk_widget_show (drawing_area);
801 
802   /*
803    * Popup menu.
804    */
805 
806   menu = create_popup_menu (window, drawing_area);
807 
808   g_signal_connect_swapped (G_OBJECT (drawing_area), "button_press_event",
809 			    G_CALLBACK (button_press_event_popup_menu), menu);
810 
811   return window;
812 }
813 
814 
815 /**************************************************************************
816  * The following section contains utility function definitions.
817  **************************************************************************/
818 
819 /***
820  *** Configure the OpenGL framebuffer.
821  ***/
822 static GdkGLConfig *
configure_gl(void)823 configure_gl (void)
824 {
825   GdkGLConfig *glconfig;
826 
827   /* Try double-buffered visual */
828   glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB    |
829 					GDK_GL_MODE_DEPTH  |
830 					GDK_GL_MODE_DOUBLE);
831   if (glconfig == NULL)
832     {
833       g_print ("\n*** Cannot find the double-buffered visual.\n");
834       g_print ("\n*** Trying single-buffered visual.\n");
835 
836       /* Try single-buffered visual */
837       glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB   |
838 					    GDK_GL_MODE_DEPTH);
839       if (glconfig == NULL)
840 	{
841 	  g_print ("*** No appropriate OpenGL-capable visual found.\n");
842 	  exit (1);
843 	}
844     }
845 
846   return glconfig;
847 }
848 
849 
850 /**************************************************************************
851  * The main function is rather trivial.
852  **************************************************************************/
853 
854 int
main(int argc,char * argv[])855 main (int   argc,
856       char *argv[])
857 {
858   GtkWidget *window;
859   GdkGLConfig *glconfig;
860 
861   /* Initialize GTK. */
862   gtk_init (&argc, &argv);
863 
864   /* Initialize GtkGLExt. */
865   gtk_gl_init (&argc, &argv);
866 
867   /* Configure OpenGL framebuffer. */
868   glconfig = configure_gl ();
869 
870   /* Create and show the application window. */
871   window = create_window (glconfig);
872   gtk_widget_show (window);
873 
874   gtk_main ();
875 
876   return 0;
877 }
878 
879 
880 /**************************************************************************
881  * End of file.
882  **************************************************************************/
883