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