source: trunk/deushax/gtk/glmapview.cpp @ 863

Last change on this file since 863 was 863, checked in by sam, 9 years ago

core: rename vec2i to ivec2 etc. to better match GLSL.

  • Property svn:keywords set to Id
File size: 8.8 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
16using namespace lol;
17
18#include "glmapview.h"
19
20static float const FPS = 30.0f;
21
22GlMapView::GlMapView(GtkContainer *in_container,
23                     GtkAdjustment *in_hadj,
24                     GtkAdjustment *in_vadj)
25  : hadj(in_hadj), vadj(in_vadj),
26    ticking(FALSE), panning(FALSE), destroyed(FALSE),
27    mapviewer(0),
28    xpan(0.0), ypan(0.0)
29{
30    /* Create new OpenGL widget */
31    int attrlist[] =
32    {
33        GDK_GL_RGBA,
34        GDK_GL_RED_SIZE, 1,
35        GDK_GL_GREEN_SIZE, 1,
36        GDK_GL_BLUE_SIZE, 1,
37        GDK_GL_DEPTH_SIZE, 16,
38        GDK_GL_DOUBLEBUFFER,
39        GDK_GL_NONE
40    };
41
42    glarea = gtk_gl_area_new(attrlist);
43    gtk_widget_set_usize(glarea, 400, 300);
44    gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK |
45                                  GDK_POINTER_MOTION_MASK |
46                                  GDK_BUTTON_PRESS_MASK |
47                                  GDK_BUTTON_RELEASE_MASK);
48    gtk_widget_set_can_focus(glarea, TRUE);
49
50    gtk_container_add(in_container, glarea);
51
52    /* We tick from the idle function instead of a timeout to avoid
53     * stealing time from the GTK loop when the callback time exceeds
54     * the timeout value. */
55    g_idle_add((GSourceFunc)IdleTickSignal, this);
56
57    /* FIXME: for completeness, the "hierarchy_changed" signal should be
58     * intercepted in case we ever get reparented. */
59    g_signal_connect(GTK_OBJECT(glarea), "realize",
60                     GTK_SIGNAL_FUNC(SetupSignal), this);
61    g_signal_connect(GTK_OBJECT(glarea), "expose_event",
62                     GTK_SIGNAL_FUNC(DrawSignal), this);
63    g_signal_connect(GTK_OBJECT(glarea), "configure_event",
64                     GTK_SIGNAL_FUNC(ReshapeSignal), this);
65
66    g_signal_connect(GTK_OBJECT(glarea), "button_press_event",
67                     GTK_SIGNAL_FUNC(MouseButtonSignal), this);
68    g_signal_connect(GTK_OBJECT(glarea), "button_release_event",
69                     GTK_SIGNAL_FUNC(MouseButtonSignal), this);
70    g_signal_connect(GTK_OBJECT(glarea), "motion_notify_event",
71                     GTK_SIGNAL_FUNC(MouseMotionSignal), this);
72
73    g_signal_connect(GTK_OBJECT(glarea), "key_press_event",
74                     GTK_SIGNAL_FUNC(KeyPressSignal), this);
75}
76
77GlMapView::~GlMapView()
78{
79    Shutdown();
80}
81
82void GlMapView::LoadMap(char const *path)
83{
84    // FIXME: detect when the map viewer is killed
85    mapviewer = new MapViewer(path);
86    Ticker::Ref(mapviewer);
87
88    UpdateAdjustments();
89}
90
91void GlMapView::CloseMap()
92{
93    if (mapviewer)
94        Ticker::Unref(mapviewer);
95    mapviewer = NULL;
96
97    UpdateAdjustments();
98}
99
100gboolean GlMapView::IdleTick()
101{
102    if (Ticker::Finished())
103    {
104        /* Release the hijacked exit sequence */
105        gtk_main_quit();
106        return FALSE;
107    }
108
109    if (!ticking)
110    {
111        ticking = TRUE;
112
113        if (mapviewer)
114            mapviewer->SetPOV(gtk_adjustment_get_value(hadj),
115                              mapviewer->GetHeight() - glarea->allocation.height
116                               - gtk_adjustment_get_value(vadj));
117
118        /* Tick the game */
119        Ticker::TickGame();
120    }
121
122    gtk_widget_draw(GTK_WIDGET(glarea), NULL);
123
124    return TRUE;
125}
126
127gboolean GlMapView::Setup()
128{
129    /* Hook to the toplevel's unmap signal */
130    GtkWidget *toplevel = gtk_widget_get_toplevel(glarea);
131    if (g_signal_handler_find(toplevel, G_SIGNAL_MATCH_DATA,
132                              0, 0, NULL, NULL, this) == 0)
133        g_signal_connect(GTK_OBJECT(toplevel), "delete_event",
134                         GTK_SIGNAL_FUNC(DeleteSignal), this);
135
136    /* Set up display */
137    gtk_widget_grab_focus(glarea);
138    if (gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
139    {
140        Ticker::Setup(FPS);
141        Video::Setup(ivec2(glarea->allocation.width,
142                           glarea->allocation.height));
143    }
144
145    UpdateAdjustments();
146
147    return TRUE;
148}
149
150/* This method is called when gtk_main_quit() is called, or when the toplevel
151 * widget is destroyed, or both. */
152gboolean GlMapView::Shutdown()
153{
154    /* Make sure we don't initiate shutdown twice. It could happen if we
155     * got the delete signal first, then the gtk_main_quit() signal. In
156     * that case, ignore the latter. */
157    if (!destroyed)
158    {
159        destroyed = TRUE;
160        CloseMap();
161        Ticker::Shutdown();
162        gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE);
163        /* Hijack the exit sequence by adding another level of gtk_main,
164         * because returning too soon may leave us no time to clean up. */
165        gtk_main();
166    }
167    return FALSE;
168}
169
170gboolean GlMapView::Draw(GdkEventExpose *e)
171{
172    if (e->count > 0)
173        return TRUE;
174
175    /* OpenGL functions can be called only if make_current returns true */
176    if (ticking && gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
177    {
178        ticking = FALSE;
179
180        /* Tick the renderer, show the frame and clamp to desired framerate */
181        Ticker::TickDraw();
182        gtk_gl_area_swapbuffers(GTK_GL_AREA(glarea));
183        while (g_main_context_iteration(NULL, FALSE))
184            ;
185
186        /* FIXME: do some GTK stuff instead of waiting for so long */
187        Ticker::ClampFps();
188    }
189
190    return TRUE;
191}
192
193void GlMapView::Scroll(double dx, double dy)
194{
195    gtk_adjustment_set_value(hadj, gtk_adjustment_get_value(hadj) + dx);
196    gtk_adjustment_set_value(vadj, gtk_adjustment_get_value(vadj) + dy);
197
198    UpdateAdjustments();
199}
200
201void GlMapView::UpdateAdjustments()
202{
203    float w = mapviewer ? mapviewer->GetWidth() : glarea->allocation.width;
204    float h = mapviewer ? mapviewer->GetHeight() : glarea->allocation.height;
205
206    /* Manage adjustments */
207    struct { GtkAdjustment *adj; float map_size, sw_size; } s[2] =
208    {
209        { hadj, w, glarea->allocation.width },
210        { vadj, h, glarea->allocation.height },
211    };
212
213    for (int i = 0; i < 2; i++)
214    {
215        gtk_adjustment_set_lower(s[i].adj, 0);
216        gtk_adjustment_set_upper(s[i].adj, s[i].map_size);
217        gtk_adjustment_set_step_increment(s[i].adj, 1);
218        gtk_adjustment_set_page_increment(s[i].adj, s[i].sw_size);
219        gtk_adjustment_set_page_size(s[i].adj, s[i].sw_size);
220
221        float val = gtk_adjustment_get_value(s[i].adj);
222        if (val + s[i].sw_size > s[i].map_size)
223        {
224            gtk_adjustment_set_value(s[i].adj,
225                                     s[i].map_size - s[i].sw_size);
226            gtk_adjustment_value_changed(s[i].adj);
227        }
228    }
229}
230
231gboolean GlMapView::MouseButton(GdkEventButton *e)
232{
233    if (e->type == GDK_BUTTON_PRESS && e->button == 2)
234    {
235        panning = TRUE;
236        xpan = e->x;
237        ypan = e->y;
238        GdkCursor *cursor = gdk_cursor_new(GDK_HAND1);
239        gdk_window_set_cursor(glarea->window, cursor);
240        gdk_cursor_unref(cursor);
241        return FALSE;
242    }
243    else if (e->type == GDK_BUTTON_RELEASE && e->button == 2)
244    {
245        panning = FALSE;
246        gdk_window_set_cursor(glarea->window, NULL);
247        return FALSE;
248    }
249
250    return TRUE;
251}
252
253gboolean GlMapView::MouseMotion(GdkEventMotion *e)
254{
255    if (panning)
256    {
257        Scroll(xpan - e->x, ypan - e->y);
258        xpan = e->x;
259        ypan = e->y;
260        return TRUE;
261    }
262
263    return FALSE;
264}
265
266gboolean GlMapView::KeyPress(GdkEventKey *e)
267{
268    switch (e->keyval)
269    {
270    case GDK_Up:    Scroll(  0.0, -10.0); return TRUE;
271    case GDK_Down:  Scroll(  0.0,  10.0); return TRUE;
272    case GDK_Left:  Scroll(-10.0,   0.0); return TRUE;
273    case GDK_Right: Scroll( 10.0,   0.0); return TRUE;
274    }
275
276    return FALSE;
277}
278
279/* Private signal slots */
280gboolean GlMapView::IdleTickSignal(GlMapView *that)
281{
282    return that->IdleTick();
283}
284
285gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
286{
287    (void)w;
288    return that->Setup();
289}
290
291gboolean GlMapView::DeleteSignal(GtkWidget *w, GdkEvent *e,
292                                 GlMapView *that)
293{
294    /* XXX: w is not glarea, it's the top level window */
295    (void)w;
296    (void)e;
297    return that->Shutdown();
298}
299
300gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e,
301                               GlMapView *that)
302{
303    (void)w;
304    return that->Draw(e);
305}
306
307gboolean GlMapView::ReshapeSignal(GtkWidget *w, GdkEventConfigure *e,
308                                  GlMapView *that)
309{
310    (void)w;
311    (void)e;
312    return that->Setup();
313}
314
315gboolean GlMapView::MouseButtonSignal(GtkWidget *w, GdkEventButton *e,
316                                      GlMapView *that)
317{
318    (void)w;
319    return that->MouseButton(e);
320}
321
322gboolean GlMapView::MouseMotionSignal(GtkWidget *w, GdkEventMotion *e,
323                                      GlMapView *that)
324{
325    (void)w;
326    return that->MouseMotion(e);
327}
328
329gboolean GlMapView::KeyPressSignal(GtkWidget *w, GdkEventKey *e,
330                                   GlMapView *that)
331{
332    (void)w;
333    return that->KeyPress(e);
334}
335
Note: See TracBrowser for help on using the repository browser.