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

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

Store the frame number in the Ticker instead of duplicating the information
in each class that uses it.

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