source: trunk/src/gtk/glmapview.cpp @ 170

Last change on this file since 170 was 170, checked in by sam, 10 years ago

Properly implement program termination, including in the GTK program.

  • Property svn:keywords set to Id
File size: 7.7 KB
Line 
1//
2// Deus Hax (working title)
3// Copyright (c) 2010 Sam Hocevar <sam@hocevar.net>
4//
5
6#if defined HAVE_CONFIG_H
7#   include "config.h"
8#endif
9
10#include <gtk/gtk.h>
11#include <gtkgl/gtkglarea.h>
12#include <gdk/gdkkeysyms.h>
13
14#include "core.h"
15#include "glmapview.h"
16
17static float const FPS = 30.0f;
18
19GlMapView::GlMapView(GtkBuilder *builder)
20  : hadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_hadj"))),
21    vadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_vadj"))),
22    ticking(FALSE), panning(FALSE),
23    mapviewer(0),
24    xpan(0.0), ypan(0.0)
25{
26    /* Create new OpenGL widget */
27    int attrlist[] =
28    {
29        GDK_GL_RGBA,
30        GDK_GL_RED_SIZE, 1,
31        GDK_GL_GREEN_SIZE, 1,
32        GDK_GL_BLUE_SIZE, 1,
33        GDK_GL_DOUBLEBUFFER,
34        GDK_GL_NONE
35    };
36
37    glarea = gtk_gl_area_new(attrlist);
38    gtk_widget_set_usize(glarea, 400, 300);
39    gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK |
40                                  GDK_POINTER_MOTION_MASK |
41                                  GDK_BUTTON_PRESS_MASK |
42                                  GDK_BUTTON_RELEASE_MASK);
43    gtk_widget_set_can_focus(glarea, TRUE);
44
45    GtkContainer *cont = GTK_CONTAINER(gtk_builder_get_object(builder,
46                                                              "gl_container"));
47    gtk_container_add(cont, glarea);
48
49    /* We tick from the idle function instead of a timeout to avoid
50     * stealing time from the GTK loop when the callback time exceeds
51     * the timeout value. */
52    g_idle_add((GSourceFunc)IdleTickSignal, this);
53    gtk_quit_add(0, (GtkFunction)ShutdownSignal, this);
54
55    gtk_signal_connect(GTK_OBJECT(glarea), "realize",
56                       GTK_SIGNAL_FUNC(SetupSignal), this);
57    gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
58                       GTK_SIGNAL_FUNC(DrawSignal), this);
59    gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
60                       GTK_SIGNAL_FUNC(ReshapeSignal), this);
61
62    gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event",
63                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
64    gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event",
65                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
66    gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event",
67                       GTK_SIGNAL_FUNC(MouseMotionSignal), this);
68
69    gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event",
70                       GTK_SIGNAL_FUNC(KeyPressSignal), this);
71}
72
73void GlMapView::LoadMap(char const *path)
74{
75    // FIXME: detect when the map viewer is killed
76    mapviewer = new MapViewer(path);
77    Ticker::Ref(mapviewer);
78
79    UpdateAdjustments();
80}
81
82void GlMapView::CloseMap()
83{
84    if (mapviewer)
85        Ticker::Unref(mapviewer);
86    mapviewer = NULL;
87
88    UpdateAdjustments();
89}
90
91gboolean GlMapView::IdleTick()
92{
93    if (Ticker::Finished())
94    {
95        gtk_main_quit();
96        return FALSE;
97    }
98
99    // FIXME: do not do anything if the previous tick was too recent?
100    ticking = TRUE;
101
102    if (mapviewer)
103        mapviewer->SetPOV(gtk_adjustment_get_value(hadj),
104                          gtk_adjustment_get_value(vadj));
105
106    /* Tick the game */
107    Ticker::TickGame();
108
109    gtk_widget_draw(GTK_WIDGET(glarea), NULL);
110
111    return TRUE;
112}
113
114gboolean GlMapView::Setup()
115{
116    /* Set up display */
117    gtk_widget_grab_focus(glarea);
118    if (gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
119        Video::Setup(glarea->allocation.width, glarea->allocation.height);
120
121    UpdateAdjustments();
122
123    return TRUE;
124}
125
126gboolean GlMapView::Shutdown()
127{
128    CloseMap();
129    Ticker::Shutdown();
130    /* Hijack the exit process by adding another level of gtk_main */
131    gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE);
132    gtk_main();
133    return TRUE;
134}
135
136gboolean GlMapView::Draw(GdkEventExpose *e)
137{
138    if (e->count > 0)
139        return TRUE;
140
141    /* OpenGL functions can be called only if make_current returns true */
142    if (ticking && gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
143    {
144        ticking = FALSE;
145
146        /* Clear the screen, tick the renderer, show the frame and
147         * clamp to desired framerate */
148        Video::Clear();
149        Ticker::TickDraw();
150        gtk_gl_area_swapbuffers(GTK_GL_AREA(glarea));
151        while (g_main_context_iteration(NULL, FALSE))
152            ;
153        Ticker::ClampFps(1000.0f / FPS);
154    }
155
156    return TRUE;
157}
158
159void GlMapView::Scroll(double dx, double dy)
160{
161    gtk_adjustment_set_value(hadj, gtk_adjustment_get_value(hadj) + dx);
162    gtk_adjustment_set_value(vadj, gtk_adjustment_get_value(vadj) + dy);
163
164    UpdateAdjustments();
165}
166
167void GlMapView::UpdateAdjustments()
168{
169    float w = mapviewer ? mapviewer->GetWidth() : glarea->allocation.width;
170    float h = mapviewer ? mapviewer->GetHeight() : glarea->allocation.height;
171
172    /* Manage adjustments */
173    struct { GtkAdjustment *adj; float map_size, sw_size; } s[2] =
174    {
175        { hadj, w, glarea->allocation.width },
176        { vadj, h, glarea->allocation.height },
177    };
178
179    for (int i = 0; i < 2; i++)
180    {
181        gtk_adjustment_set_lower(s[i].adj, 0);
182        gtk_adjustment_set_upper(s[i].adj, s[i].map_size);
183        gtk_adjustment_set_step_increment(s[i].adj, 1);
184        gtk_adjustment_set_page_increment(s[i].adj, s[i].sw_size);
185        gtk_adjustment_set_page_size(s[i].adj, s[i].sw_size);
186
187        float val = gtk_adjustment_get_value(s[i].adj);
188        if (val + s[i].sw_size > s[i].map_size)
189        {
190            gtk_adjustment_set_value(s[i].adj,
191                                     s[i].map_size - s[i].sw_size);
192            gtk_adjustment_value_changed(s[i].adj);
193        }
194    }
195}
196
197gboolean GlMapView::MouseButton(GdkEventButton *e)
198{
199    if (e->type == GDK_BUTTON_PRESS && e->button == 2)
200    {
201        panning = TRUE;
202        xpan = e->x;
203        ypan = e->y;
204        GdkCursor *cursor = gdk_cursor_new(GDK_HAND1);
205        gdk_window_set_cursor(glarea->window, cursor);
206        gdk_cursor_unref(cursor);
207        return FALSE;
208    }
209    else if (e->type == GDK_BUTTON_RELEASE && e->button == 2)
210    {
211        panning = FALSE;
212        gdk_window_set_cursor(glarea->window, NULL);
213        return FALSE;
214    }
215
216    return TRUE;
217}
218
219gboolean GlMapView::MouseMotion(GdkEventMotion *e)
220{
221    if (panning)
222    {
223        Scroll(xpan - e->x, ypan - e->y);
224        xpan = e->x;
225        ypan = e->y;
226        return TRUE;
227    }
228
229    return FALSE;
230}
231
232gboolean GlMapView::KeyPress(GdkEventKey *e)
233{
234    switch (e->keyval)
235    {
236    case GDK_Up:    Scroll(  0.0, -10.0); return TRUE;
237    case GDK_Down:  Scroll(  0.0,  10.0); return TRUE;
238    case GDK_Left:  Scroll(-10.0,   0.0); return TRUE;
239    case GDK_Right: Scroll( 10.0,   0.0); return TRUE;
240    }
241
242    return FALSE;
243}
244
245/* Private signal slots */
246gboolean GlMapView::IdleTickSignal(GlMapView *that)
247{
248    return that->IdleTick();
249}
250
251gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
252{
253    (void)w;
254    return that->Setup();
255}
256
257gboolean GlMapView::ShutdownSignal(GlMapView *that)
258{
259    return that->Shutdown();
260}
261
262gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e,
263                               GlMapView *that)
264{
265    (void)w;
266    return that->Draw(e);
267}
268
269gboolean GlMapView::ReshapeSignal(GtkWidget *w, GdkEventConfigure *e,
270                                  GlMapView *that)
271{
272    (void)w;
273    (void)e;
274    return that->Setup();
275}
276
277gboolean GlMapView::MouseButtonSignal(GtkWidget *w, GdkEventButton *e,
278                                      GlMapView *that)
279{
280    (void)w;
281    return that->MouseButton(e);
282}
283
284gboolean GlMapView::MouseMotionSignal(GtkWidget *w, GdkEventMotion *e,
285                                      GlMapView *that)
286{
287    (void)w;
288    return that->MouseMotion(e);
289}
290
291gboolean GlMapView::KeyPressSignal(GtkWidget *w, GdkEventKey *e,
292                                   GlMapView *that)
293{
294    (void)w;
295    return that->KeyPress(e);
296}
297
Note: See TracBrowser for help on using the repository browser.