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

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

Minor simplification to the GTK exit handling.

  • Property svn:keywords set to Id
File size: 7.9 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
54    /* We must divert gtk_main_quit() to release our resources when the
55     * GL widget is still realised. We'll call gtk_main_quit() when we
56     * are sure that everything has been cleaned. */
57    gtk_quit_add(0, (GtkFunction)ShutdownSignal, this);
58
59    gtk_signal_connect(GTK_OBJECT(glarea), "realize",
60                       GTK_SIGNAL_FUNC(SetupSignal), this);
61    gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
62                       GTK_SIGNAL_FUNC(DrawSignal), this);
63    gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
64                       GTK_SIGNAL_FUNC(ReshapeSignal), this);
65
66    gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event",
67                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
68    gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event",
69                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
70    gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event",
71                       GTK_SIGNAL_FUNC(MouseMotionSignal), this);
72
73    gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event",
74                       GTK_SIGNAL_FUNC(KeyPressSignal), this);
75}
76
77void GlMapView::LoadMap(char const *path)
78{
79    // FIXME: detect when the map viewer is killed
80    mapviewer = new MapViewer(path);
81    Ticker::Ref(mapviewer);
82
83    UpdateAdjustments();
84}
85
86void GlMapView::CloseMap()
87{
88    if (mapviewer)
89        Ticker::Unref(mapviewer);
90    mapviewer = NULL;
91
92    UpdateAdjustments();
93}
94
95gboolean GlMapView::IdleTick()
96{
97    if (Ticker::Finished())
98    {
99        gtk_main_quit();
100        return FALSE;
101    }
102
103    // FIXME: do not do anything if the previous tick was too recent?
104    ticking = TRUE;
105
106    if (mapviewer)
107        mapviewer->SetPOV(gtk_adjustment_get_value(hadj),
108                          gtk_adjustment_get_value(vadj));
109
110    /* Tick the game */
111    Ticker::TickGame();
112
113    gtk_widget_draw(GTK_WIDGET(glarea), NULL);
114
115    return TRUE;
116}
117
118gboolean GlMapView::Setup()
119{
120    /* Set up display */
121    gtk_widget_grab_focus(glarea);
122    if (gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
123        Video::Setup(glarea->allocation.width, glarea->allocation.height);
124
125    UpdateAdjustments();
126
127    return TRUE;
128}
129
130gboolean GlMapView::Shutdown()
131{
132    CloseMap();
133    Ticker::Shutdown();
134    /* Hijack the exit process by adding another level of gtk_main */
135    gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE);
136    gtk_main();
137    return TRUE;
138}
139
140gboolean GlMapView::Draw(GdkEventExpose *e)
141{
142    if (e->count > 0)
143        return TRUE;
144
145    /* OpenGL functions can be called only if make_current returns true */
146    if (ticking && gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
147    {
148        ticking = FALSE;
149
150        /* Clear the screen, tick the renderer, show the frame and
151         * clamp to desired framerate */
152        Video::Clear();
153        Ticker::TickDraw();
154        gtk_gl_area_swapbuffers(GTK_GL_AREA(glarea));
155        while (g_main_context_iteration(NULL, FALSE))
156            ;
157        Ticker::ClampFps(1000.0f / FPS);
158    }
159
160    return TRUE;
161}
162
163void GlMapView::Scroll(double dx, double dy)
164{
165    gtk_adjustment_set_value(hadj, gtk_adjustment_get_value(hadj) + dx);
166    gtk_adjustment_set_value(vadj, gtk_adjustment_get_value(vadj) + dy);
167
168    UpdateAdjustments();
169}
170
171void GlMapView::UpdateAdjustments()
172{
173    float w = mapviewer ? mapviewer->GetWidth() : glarea->allocation.width;
174    float h = mapviewer ? mapviewer->GetHeight() : glarea->allocation.height;
175
176    /* Manage adjustments */
177    struct { GtkAdjustment *adj; float map_size, sw_size; } s[2] =
178    {
179        { hadj, w, glarea->allocation.width },
180        { vadj, h, glarea->allocation.height },
181    };
182
183    for (int i = 0; i < 2; i++)
184    {
185        gtk_adjustment_set_lower(s[i].adj, 0);
186        gtk_adjustment_set_upper(s[i].adj, s[i].map_size);
187        gtk_adjustment_set_step_increment(s[i].adj, 1);
188        gtk_adjustment_set_page_increment(s[i].adj, s[i].sw_size);
189        gtk_adjustment_set_page_size(s[i].adj, s[i].sw_size);
190
191        float val = gtk_adjustment_get_value(s[i].adj);
192        if (val + s[i].sw_size > s[i].map_size)
193        {
194            gtk_adjustment_set_value(s[i].adj,
195                                     s[i].map_size - s[i].sw_size);
196            gtk_adjustment_value_changed(s[i].adj);
197        }
198    }
199}
200
201gboolean GlMapView::MouseButton(GdkEventButton *e)
202{
203    if (e->type == GDK_BUTTON_PRESS && e->button == 2)
204    {
205        panning = TRUE;
206        xpan = e->x;
207        ypan = e->y;
208        GdkCursor *cursor = gdk_cursor_new(GDK_HAND1);
209        gdk_window_set_cursor(glarea->window, cursor);
210        gdk_cursor_unref(cursor);
211        return FALSE;
212    }
213    else if (e->type == GDK_BUTTON_RELEASE && e->button == 2)
214    {
215        panning = FALSE;
216        gdk_window_set_cursor(glarea->window, NULL);
217        return FALSE;
218    }
219
220    return TRUE;
221}
222
223gboolean GlMapView::MouseMotion(GdkEventMotion *e)
224{
225    if (panning)
226    {
227        Scroll(xpan - e->x, ypan - e->y);
228        xpan = e->x;
229        ypan = e->y;
230        return TRUE;
231    }
232
233    return FALSE;
234}
235
236gboolean GlMapView::KeyPress(GdkEventKey *e)
237{
238    switch (e->keyval)
239    {
240    case GDK_Up:    Scroll(  0.0, -10.0); return TRUE;
241    case GDK_Down:  Scroll(  0.0,  10.0); return TRUE;
242    case GDK_Left:  Scroll(-10.0,   0.0); return TRUE;
243    case GDK_Right: Scroll( 10.0,   0.0); return TRUE;
244    }
245
246    return FALSE;
247}
248
249/* Private signal slots */
250gboolean GlMapView::IdleTickSignal(GlMapView *that)
251{
252    return that->IdleTick();
253}
254
255gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
256{
257    (void)w;
258    return that->Setup();
259}
260
261gboolean GlMapView::ShutdownSignal(GlMapView *that)
262{
263    return that->Shutdown();
264}
265
266gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e,
267                               GlMapView *that)
268{
269    (void)w;
270    return that->Draw(e);
271}
272
273gboolean GlMapView::ReshapeSignal(GtkWidget *w, GdkEventConfigure *e,
274                                  GlMapView *that)
275{
276    (void)w;
277    (void)e;
278    return that->Setup();
279}
280
281gboolean GlMapView::MouseButtonSignal(GtkWidget *w, GdkEventButton *e,
282                                      GlMapView *that)
283{
284    (void)w;
285    return that->MouseButton(e);
286}
287
288gboolean GlMapView::MouseMotionSignal(GtkWidget *w, GdkEventMotion *e,
289                                      GlMapView *that)
290{
291    (void)w;
292    return that->MouseMotion(e);
293}
294
295gboolean GlMapView::KeyPressSignal(GtkWidget *w, GdkEventKey *e,
296                                   GlMapView *that)
297{
298    (void)w;
299    return that->KeyPress(e);
300}
301
Note: See TracBrowser for help on using the repository browser.