2
* Copyright (C) 1998 Janne L.b��f <jlof@mail.student.oulu.fi>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of the
7
* License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the Free
16
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
* Modified on 10th June 2002, for porting this program
20
* to the 'gtkglext-0.1.0` extension of gtk-2.0.
22
* Alif Wahid, <awah005@users.sourceforge.net>
25
* Improved mouse operation.
27
* Naofumi Yasufuku <naofumi@users.sourceforge.net>
30
* Adapted for gfsview. May 2004.
32
* Stephane Popinet <popinet@users.sf.net>
38
#include <sys/types.h>
44
#include <gdk/gdkkeysyms.h>
47
#include <gtk/gtkgl.h>
49
#define SN_API_NOT_YET_FROZEN
52
#if defined(__APPLE__)
53
# include <OpenGL/gl.h>
54
# include <OpenGL/glu.h>
62
#include "gl/trackball.h"
63
#include "glade/interface.h"
64
#include "glade/support.h"
65
#include "glade/callbacks.h"
67
static void pick (GtkWidget * widget, int winx, int winy, gboolean motion)
69
GLdouble model[16], proj[16];
74
glGetDoublev (GL_MODELVIEW_MATRIX, model);
75
glGetDoublev (GL_PROJECTION_MATRIX, proj);
76
glGetIntegerv (GL_VIEWPORT, viewport);
77
winy = widget->allocation.height - winy;
78
g_return_if_fail (gluUnProject (winx, winy, 0., model, proj, viewport, &x, &y, &z));
79
r.a.x = x; r.a.y = y; r.a.z = z;
80
g_return_if_fail (gluUnProject (winx, winy, 1., model, proj, viewport, &x, &y, &z));
81
r.b.x = x; r.b.y = y; r.b.z = z;
83
gfk_gl_view_pick (lookup_widget (widget, "view"), &r, motion);
86
static GList * get_symmetries (GtkTreeModel * list)
89
gboolean valid = gtk_tree_model_get_iter_first (list, &iter);
90
GList * symmetry = NULL;
96
gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
97
if (visible && GFK_IS_GL_SYMMETRY (gl))
98
symmetry = g_list_append (symmetry, gl->gl);
99
valid = gtk_tree_model_iter_next (list, &iter);
105
expose(GtkWidget * widget,
106
GdkEventExpose * event)
108
GdkGLContext * glcontext = gtk_widget_get_gl_context (widget);
109
GdkGLDrawable * gldrawable = gtk_widget_get_gl_drawable (widget);
110
GfsGlViewParams * info = g_object_get_data (G_OBJECT (widget), "GfsGlViewParams");
112
/* draw only last expose */
113
if (event->count > 0)
116
if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
119
/* basic initialization */
120
if (info->do_init == TRUE) {
122
info->do_init = FALSE;
125
glMatrixMode (GL_PROJECTION);
127
GfsDomain * domain = g_object_get_data (G_OBJECT (widget), "sim");
128
GtkWidget * view = lookup_widget (widget, "view");
129
GtkTreeModel * list = gtk_tree_view_get_model (GTK_TREE_VIEW (lookup_widget (view, "gl_list")));
130
GList * symmetries = get_symmetries (list);
131
gdouble max = gfs_gl_domain_extent (domain, symmetries);
132
g_list_free (symmetries);
133
gluPerspective (info->fov, widget->allocation.width/(float) widget->allocation.height,
135
glMatrixMode (GL_MODELVIEW);
139
glTranslatef (info->tx, info->ty, - (1. + max));
140
gfs_gl_add_quats (info->dquat, info->quat, info->quat);
142
gfs_gl_build_rotmatrix (m, info->quat);
143
glMultMatrixf (&m[0][0]);
144
glScalef (info->sx, info->sy, info->sz);
147
glClearColor (info->bg.r, info->bg.g, info->bg.b, 1);
148
glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
150
gfk_gl_view_draw (view, GFSGL_SCREEN);
152
/* swap backbuffer to front */
153
if (gdk_gl_drawable_is_double_buffered (gldrawable))
154
gdk_gl_drawable_swap_buffers (gldrawable);
158
gdk_gl_drawable_gl_end (gldrawable);
164
configure(GtkWidget *widget,
165
GdkEventConfigure *event)
167
GdkGLContext *glcontext;
168
GdkGLDrawable *gldrawable;
170
g_return_val_if_fail(widget && event, FALSE);
172
glcontext = gtk_widget_get_gl_context(widget);
173
gldrawable = gtk_widget_get_gl_drawable(widget);
175
/*** OpenGL BEGIN ***/
176
if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
179
glViewport (0, 0, widget->allocation.width, widget->allocation.height);
181
gdk_gl_drawable_gl_end(gldrawable);
187
destroy(GtkWidget *widget)
189
/* delete mesh info */
190
GfsGlViewParams *info = (GfsGlViewParams*)g_object_get_data(G_OBJECT(widget), "GfsGlViewParams");
196
button_press(GtkWidget *widget,
197
GdkEventButton *event)
199
if (event->button == 1 && event->state & GDK_CONTROL_MASK) {
200
pick (widget, event->x, event->y, FALSE);
204
GfsGlViewParams * info = g_object_get_data (G_OBJECT (widget), "GfsGlViewParams");
206
info->dquat[0] = 0.0;
207
info->dquat[1] = 0.0;
208
info->dquat[2] = 0.0;
209
info->dquat[3] = 1.0;
211
/* beginning of drag, reset mouse position */
212
info->beginx = event->x;
213
info->beginy = event->y;
221
button_release(GtkWidget *widget,
222
GdkEventButton *event)
224
GfsGlViewParams *info = (GfsGlViewParams*)g_object_get_data(G_OBJECT(widget), "GfsGlViewParams");
226
info->dquat[0] = 0.0;
227
info->dquat[1] = 0.0;
228
info->dquat[2] = 0.0;
229
info->dquat[3] = 1.0;
233
info->motion = FALSE;
234
if (info->res != info->base_res)
235
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
241
motion_notify(GtkWidget *widget,
242
GdkEventMotion *event)
246
GdkModifierType state = 0;
248
gboolean redraw = FALSE;
249
GfsGlViewParams *info = (GfsGlViewParams*)g_object_get_data(G_OBJECT(widget), "GfsGlViewParams");
252
gdk_window_get_pointer(event->window, &x, &y, &state);
256
state = event->state;
259
width = widget->allocation.width;
260
height = widget->allocation.height;
262
if (state & GDK_CONTROL_MASK && state & GDK_BUTTON1_MASK) {
263
if (x >= 0 && y >= 0 && x < width && y < height)
264
pick (widget, x, y, TRUE);
268
if (state & GDK_BUTTON1_MASK) {
269
/* drag in progress, simulate trackball */
270
gfs_gl_trackball( info->dquat,
271
(2.0*info->beginx - width) / width,
272
( height - 2.0*info->beginy) / height,
273
( 2.0*x - width) / width,
274
( height - 2.0*y) / height );
276
if (!(event->state & GDK_SHIFT_MASK)) {
277
/* constrain the rotations in the plane */
278
info->dquat[0] = info->dquat[1] = 0.;
279
gdouble n = sqrt (info->dquat[2]*info->dquat[2] + info->dquat[3]*info->dquat[3]);
280
info->dquat[2] /= n; info->dquat[3] /= n;
283
info->dx = x - info->beginx;
284
info->dy = y - info->beginy;
286
/* orientation has changed, redraw mesh */
290
if (state & GDK_BUTTON2_MASK) {
292
info->fov += (0.1 + 3.*info->fov)*(y - info->beginy)/height;
293
if (info->fov > 100.)
295
else if (info->fov < 0.01)
297
/* zoom has changed, redraw mesh */
301
if (state & GDK_BUTTON3_MASK) {
303
info->tx += (x - info->beginx)/width*0.02*(0.01 + 3.*info->fov);
304
info->ty -= (y - info->beginy)/height*0.02*(0.01 + 3.*info->fov);
306
/* translate has changed, redraw mesh */
314
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
320
scroll_notify(GtkWidget *widget,
321
GdkEventScroll *event)
323
GfsGlViewParams *info = (GfsGlViewParams*)g_object_get_data(G_OBJECT(widget), "GfsGlViewParams");
325
switch (event->direction) {
327
info->fov -= 0.02*(0.1 + 3.*info->fov);
328
if (info->fov > 100.)
330
else if (info->fov < 0.01)
332
/* zoom has changed, redraw mesh */
333
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
335
case GDK_SCROLL_DOWN:
336
info->fov += 0.02*(0.1 + 3.*info->fov);
337
if (info->fov > 100.)
339
else if (info->fov < 0.01)
341
/* zoom has changed, redraw mesh */
342
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
351
key_press_event(GtkWidget *widget,
354
GfsGlViewParams *info = (GfsGlViewParams*)g_object_get_data(G_OBJECT(widget), "GfsGlViewParams");
356
switch (event->keyval) {
358
info->fov -= 0.02*(0.1 + 3.*info->fov);
359
if (info->fov > 100.)
361
else if (info->fov < 0.01)
363
/* zoom has changed, redraw mesh */
364
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
368
info->fov += 0.02*(0.1 + 3.*info->fov);
369
if (info->fov > 100.)
371
else if (info->fov < 0.01)
373
/* zoom has changed, redraw mesh */
374
gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
384
static GtkWidget * gl_area_new (GdkGLConfig * glconfig)
388
/* create new OpenGL widget */
389
glarea = gtk_drawing_area_new ();
390
if (glarea == NULL) {
391
fprintf (stderr, "gfsview: cannot create GtkDrawingArea widget\n");
395
/* Set OpenGL-capability to the widget. */
396
gtk_widget_set_gl_capability (GTK_WIDGET (glarea),
402
/* set up events and signals for OpenGL widget */
403
gtk_widget_set_events (glarea,
405
GDK_BUTTON_PRESS_MASK|
406
GDK_BUTTON_RELEASE_MASK|
407
GDK_POINTER_MOTION_MASK|
408
GDK_POINTER_MOTION_HINT_MASK);
410
g_signal_connect (G_OBJECT (glarea), "expose_event",
411
G_CALLBACK (expose), NULL);
412
g_signal_connect (G_OBJECT (glarea), "motion_notify_event",
413
G_CALLBACK (motion_notify), NULL);
414
g_signal_connect (G_OBJECT (glarea), "button_press_event",
415
G_CALLBACK (button_press), NULL);
416
g_signal_connect (G_OBJECT (glarea), "button_release_event",
417
G_CALLBACK (button_release), NULL);
418
g_signal_connect (G_OBJECT (glarea), "scroll_event",
419
G_CALLBACK (scroll_notify), NULL);
420
g_signal_connect (G_OBJECT (glarea), "configure_event",
421
G_CALLBACK (configure), NULL);
422
g_signal_connect (G_OBJECT (glarea), "destroy",
423
G_CALLBACK (destroy), NULL);
424
g_signal_connect (G_OBJECT (glarea), "key_press_event",
425
G_CALLBACK (key_press_event), glarea);
432
gboolean survive_broken_pipe;
435
G_LOCK_DEFINE (main_loop_started);
437
static void send_scripting_message (GfkScriptingEvent event, GtkWidget * view, gpointer data)
439
G_LOCK (scripting_pending);
440
GfkScriptingMessage * msg = g_malloc (sizeof (GfkScriptingMessage));
444
if (!g_idle_add (gfk_receive_scripting_message, msg)) {
445
g_warning ("could not send scripting message");
448
G_UNLOCK (scripting_pending);
452
static gpointer scripting (ScriptingArgs * s)
454
gboolean scripting_off;
457
G_LOCK (main_loop_started); /* make sure main loop has been started */
458
G_UNLOCK (main_loop_started);
462
while (select (1, &rfds, NULL, NULL, NULL) > 0) {
463
GtsFile * fp = gts_file_new (stdin);
465
while (fp->type != GTS_ERROR) {
467
if (!s->survive_broken_pipe) {
468
G_LOCK (scripting_pending); /* wait for pending scripting events */
469
gdk_threads_enter ();
471
gdk_threads_leave ();
475
else if (fp->type == '\n')
476
gts_file_next_token (fp);
477
else if (fp->type == GTS_INT || !strcmp (fp->token->str, "GModule")) {
478
GfsSimulation * sim = gfs_simulation_read (fp);
483
G_LOCK (gfk_gl_scripting);
484
scripting_off = !gfk_gl_scripting;
485
G_UNLOCK (gfk_gl_scripting);
487
gts_object_destroy (GTS_OBJECT (sim));
489
gfs_simulation_init (sim);
491
gdk_threads_enter ();
492
gfk_gl_view_set_simulation (s->view, sim, "<stdin>");
493
gdk_threads_leave ();
496
else if (fp->type == GTS_STRING) {
497
if (!strcmp (fp->token->str, "Save") || !strcmp (fp->token->str, "Append")) {
498
gboolean append = !strcmp (fp->token->str, "Append");
499
GfsOutputFile * out = NULL;
503
gts_file_next_token (fp);
504
if (fp->type != GTS_STRING) {
505
gts_file_error (fp, "expecting a string (filename)");
508
fname = g_strdup (fp->token->str);
509
gts_file_next_token (fp);
510
p = g_malloc (sizeof (GfsGl2PSParams));
511
gfs_gl2ps_params_read (p, fp);
512
if (fp->type == GTS_ERROR) {
518
G_LOCK (gfk_gl_scripting);
519
scripting_off = !gfk_gl_scripting;
520
G_UNLOCK (gfk_gl_scripting);
522
if (!scripting_off) {
524
if ((out = gfs_output_file_open (fp->token->str, "w")))
528
p->fp = (!strcmp (fname, "stdout") ? stdout :
529
!strcmp (fname, "stderr") ? stderr :
532
fprintf (stderr, "gfsview: <stdin>: cannot open file `%s'\n", fname);
534
send_scripting_message (append ? GFS_APPEND_EVENT : GFS_SAVE_EVENT, s->view, p);
535
/* p is freed by the receiver of the message */
538
/* Append mode, just free memory, do not close file */
540
gfs_output_file_close (out);
547
else if (!strcmp (fp->token->str, "View")) {
548
G_LOCK (gfk_gl_scripting);
549
scripting_off = !gfk_gl_scripting;
550
G_UNLOCK (gfk_gl_scripting);
552
gdk_threads_enter ();
553
if (!gfk_gl_view_read_parameters (s->view, fp, scripting_off)) {
554
gdk_threads_leave ();
557
gdk_threads_leave ();
559
else if (!strcmp (fp->token->str, "Clear")) {
560
gdk_threads_enter ();
561
gfk_gl_view_clear (s->view);
562
gdk_threads_leave ();
563
gts_file_next_token (fp);
565
else if (!strcmp (fp->token->str, "Echo")) {
566
gts_file_next_token (fp);
567
if (fp->type != '{') {
568
gts_file_error (fp, "expecting an opening brace");
571
GString * echo = g_string_new ("");
572
guint scope = fp->scope_max;
573
gint c = gts_file_getc (fp);
574
while (c != EOF && fp->scope > scope) {
575
g_string_append_c (echo, c);
576
c = gts_file_getc (fp);
578
if (fp->scope != scope) {
579
g_string_free (echo, TRUE);
580
gts_file_error (fp, "parse error");
583
gts_file_next_token (fp);
585
send_scripting_message (GFS_ECHO_EVENT, s->view, echo->str);
586
/* echo->str is freed by the receiver of the message */
587
g_string_free (echo, FALSE);
590
gts_file_error (fp, "unknown keyword `%s'", fp->token->str);
595
gts_file_error (fp, "expecting an integer got %d", fp->type);
599
gdk_threads_enter ();
600
gfk_gl_view_set_scripting (s->view, FALSE);
601
GtkWidget * msg = gtk_message_dialog_new (GTK_WINDOW (s->view), 0,
604
"GfsView: error in scripting thread\n"
606
"Scripting is disabled",
607
fp->line, fp->pos, fp->error);
608
gtk_dialog_run (GTK_DIALOG (msg));
609
gtk_widget_destroy (msg);
610
gdk_threads_leave ();
611
gts_file_destroy (fp);
612
while (fgetc (stdin) != EOF)
618
static void error_trap_push (SnDisplay * display,
623
static void error_trap_pop (SnDisplay * display,
626
XSync (xdisplay, False); /* get all errors out of the queue */
629
static gboolean unlock_main_loop (gpointer data)
631
G_UNLOCK (main_loop_started);
635
int main (int argc, char * argv[])
637
GdkGLConfig * glconfig;
642
/* initialize multithreading */
643
g_thread_init (NULL);
648
gtk_init (&argc, &argv);
650
add_pixmap_directory (PACKAGE_DATA_DIR "/pixmaps");
652
/* initialize gtkglext */
653
gtk_gl_init(&argc, &argv);
656
gfs_init (&argc, &argv);
658
/* OpenGL drivers seem to often generate floating-point
659
exceptions... turn them off so that people don't blame
661
gfs_disable_floating_point_exceptions ();
664
s.survive_broken_pipe = FALSE;
666
static struct option long_options[] = {
667
{"survive-broken-pipe", no_argument, NULL, 's'},
668
{"help", no_argument, NULL, 'h'},
669
{"version", no_argument, NULL, 'V'},
672
int option_index = 0;
673
switch ((c = getopt_long (argc, argv, "hVs", long_options, &option_index))) {
674
case 's': /* survive-broken-pipe */
675
s.survive_broken_pipe = TRUE;
679
"Usage: gfsview [OPTION] FILE1 FILE2 ...\n"
680
"The Gerris flow solver visualisation tool.\n"
682
" -s --survive-broken-pipe GfsView will not terminate\n"
683
" if the standard input pipe is broken\n"
684
" -h --help display this help and exit\n"
685
" -V --version output version information and exit\n"
687
"Reports bugs to %s\n",
689
return 0; /* success */
691
case 'V': /* version */
693
"gfsview: %dD version %s\n",
694
FTT_DIMENSION, VERSION);
695
return 0; /* succes */
697
case '?': /* wrong options */
698
fprintf (stderr, "Try `gfsview --help' for more information.\n");
699
return 1; /* failure */
703
/* Configure OpenGL-capable visual. */
705
/* Try double-buffered visual */
706
glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
709
if (glconfig == NULL) {
710
g_print ("*** Cannot find the double-buffered visual.\n");
711
g_print ("*** Trying single-buffered visual.\n");
713
/* Try single-buffered visual */
714
glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
716
if (glconfig == NULL) {
717
g_print ("*** No appropriate OpenGL-capable visual found.\n");
722
/* startup notification */
723
Display * xdisplay = GDK_DISPLAY ();
724
SnDisplay * display = sn_display_new (xdisplay, error_trap_push, error_trap_pop);
726
launched = sn_launchee_context_new_from_environment (display, DefaultScreen (xdisplay));
729
glarea = gl_area_new (glconfig);
730
gtk_widget_show (glarea);
731
s.view = gfk_gl_view (glarea);
733
/* Register scripting thread */
734
G_LOCK (main_loop_started);
735
if (!isatty (STDIN_FILENO) && g_thread_supported ()) {
737
if (g_thread_create ((GThreadFunc) scripting, &s, FALSE, &error))
738
gfk_gl_view_set_scripting (s.view, TRUE);
740
GtkWidget * msg = gtk_message_dialog_new (NULL, 0,
743
"GfsView could not start scripting thread:\n\n"
745
"Scripting is disabled\n",
747
gtk_dialog_run (GTK_DIALOG (msg));
748
gtk_widget_destroy (msg);
749
g_clear_error (&error);
753
/* Read files on command line */
754
gtk_widget_show (s.view);
755
for (c = optind; c < argc; c++)
756
gfk_gl_simulation_read (argv[c], s.view, TRUE);
758
/* startup finished */
760
sn_launchee_context_complete (launched);
762
g_timeout_add (0, unlock_main_loop, NULL);