// // Deus Hax (working title) // Copyright (c) 2010 Sam Hocevar // #if defined HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "core.h" #include "glmapview.h" static float const FPS = 30.0f; GlMapView::GlMapView(GtkBuilder *builder) : hadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_hadj"))), vadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_vadj"))), ticking(FALSE), panning(FALSE), mapviewer(0), xpan(0.0), ypan(0.0) { /* Create new OpenGL widget */ int attrlist[] = { GDK_GL_RGBA, GDK_GL_RED_SIZE, 1, GDK_GL_GREEN_SIZE, 1, GDK_GL_BLUE_SIZE, 1, GDK_GL_DEPTH_SIZE, 16, GDK_GL_DOUBLEBUFFER, GDK_GL_NONE }; glarea = gtk_gl_area_new(attrlist); gtk_widget_set_usize(glarea, 400, 300); gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_widget_set_can_focus(glarea, TRUE); GtkContainer *cont = GTK_CONTAINER(gtk_builder_get_object(builder, "gl_container")); gtk_container_add(cont, glarea); /* We tick from the idle function instead of a timeout to avoid * stealing time from the GTK loop when the callback time exceeds * the timeout value. */ g_idle_add((GSourceFunc)IdleTickSignal, this); /* We must divert gtk_main_quit() to release our resources when the * GL widget is still realised. We'll call gtk_main_quit() when we * are sure that everything has been cleaned. */ gtk_quit_add(0, (GtkFunction)ShutdownSignal, this); gtk_signal_connect(GTK_OBJECT(glarea), "realize", GTK_SIGNAL_FUNC(SetupSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", GTK_SIGNAL_FUNC(DrawSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", GTK_SIGNAL_FUNC(ReshapeSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event", GTK_SIGNAL_FUNC(MouseButtonSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event", GTK_SIGNAL_FUNC(MouseButtonSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event", GTK_SIGNAL_FUNC(MouseMotionSignal), this); gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event", GTK_SIGNAL_FUNC(KeyPressSignal), this); } void GlMapView::LoadMap(char const *path) { // FIXME: detect when the map viewer is killed mapviewer = new MapViewer(path); Ticker::Ref(mapviewer); UpdateAdjustments(); } void GlMapView::CloseMap() { if (mapviewer) Ticker::Unref(mapviewer); mapviewer = NULL; UpdateAdjustments(); } gboolean GlMapView::IdleTick() { if (Ticker::Finished()) { gtk_main_quit(); return FALSE; } ticking = TRUE; if (mapviewer) mapviewer->SetPOV(gtk_adjustment_get_value(hadj), mapviewer->GetHeight() - glarea->allocation.height - gtk_adjustment_get_value(vadj)); /* Tick the game */ Ticker::TickGame(); gtk_widget_draw(GTK_WIDGET(glarea), NULL); return TRUE; } gboolean GlMapView::Setup() { /* Set up display */ gtk_widget_grab_focus(glarea); if (gtk_gl_area_make_current(GTK_GL_AREA(glarea))) Video::Setup(glarea->allocation.width, glarea->allocation.height); UpdateAdjustments(); return TRUE; } gboolean GlMapView::Shutdown() { CloseMap(); Ticker::Shutdown(); /* Hijack the exit process by adding another level of gtk_main */ gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE); gtk_main(); return TRUE; } gboolean GlMapView::Draw(GdkEventExpose *e) { if (e->count > 0) return TRUE; /* OpenGL functions can be called only if make_current returns true */ if (ticking && gtk_gl_area_make_current(GTK_GL_AREA(glarea))) { ticking = FALSE; /* Clear the screen, tick the renderer, show the frame and * clamp to desired framerate */ Video::Clear(); Ticker::TickDraw(); gtk_gl_area_swapbuffers(GTK_GL_AREA(glarea)); while (g_main_context_iteration(NULL, FALSE)) ; // FIXME: do some GTK stuff in here Ticker::ClampFps(1000.0f / FPS); } return TRUE; } void GlMapView::Scroll(double dx, double dy) { gtk_adjustment_set_value(hadj, gtk_adjustment_get_value(hadj) + dx); gtk_adjustment_set_value(vadj, gtk_adjustment_get_value(vadj) + dy); UpdateAdjustments(); } void GlMapView::UpdateAdjustments() { float w = mapviewer ? mapviewer->GetWidth() : glarea->allocation.width; float h = mapviewer ? mapviewer->GetHeight() : glarea->allocation.height; /* Manage adjustments */ struct { GtkAdjustment *adj; float map_size, sw_size; } s[2] = { { hadj, w, glarea->allocation.width }, { vadj, h, glarea->allocation.height }, }; for (int i = 0; i < 2; i++) { gtk_adjustment_set_lower(s[i].adj, 0); gtk_adjustment_set_upper(s[i].adj, s[i].map_size); gtk_adjustment_set_step_increment(s[i].adj, 1); gtk_adjustment_set_page_increment(s[i].adj, s[i].sw_size); gtk_adjustment_set_page_size(s[i].adj, s[i].sw_size); float val = gtk_adjustment_get_value(s[i].adj); if (val + s[i].sw_size > s[i].map_size) { gtk_adjustment_set_value(s[i].adj, s[i].map_size - s[i].sw_size); gtk_adjustment_value_changed(s[i].adj); } } } gboolean GlMapView::MouseButton(GdkEventButton *e) { if (e->type == GDK_BUTTON_PRESS && e->button == 2) { panning = TRUE; xpan = e->x; ypan = e->y; GdkCursor *cursor = gdk_cursor_new(GDK_HAND1); gdk_window_set_cursor(glarea->window, cursor); gdk_cursor_unref(cursor); return FALSE; } else if (e->type == GDK_BUTTON_RELEASE && e->button == 2) { panning = FALSE; gdk_window_set_cursor(glarea->window, NULL); return FALSE; } return TRUE; } gboolean GlMapView::MouseMotion(GdkEventMotion *e) { if (panning) { Scroll(xpan - e->x, ypan - e->y); xpan = e->x; ypan = e->y; return TRUE; } return FALSE; } gboolean GlMapView::KeyPress(GdkEventKey *e) { switch (e->keyval) { case GDK_Up: Scroll( 0.0, -10.0); return TRUE; case GDK_Down: Scroll( 0.0, 10.0); return TRUE; case GDK_Left: Scroll(-10.0, 0.0); return TRUE; case GDK_Right: Scroll( 10.0, 0.0); return TRUE; } return FALSE; } /* Private signal slots */ gboolean GlMapView::IdleTickSignal(GlMapView *that) { return that->IdleTick(); } gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that) { (void)w; return that->Setup(); } gboolean GlMapView::ShutdownSignal(GlMapView *that) { return that->Shutdown(); } gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e, GlMapView *that) { (void)w; return that->Draw(e); } gboolean GlMapView::ReshapeSignal(GtkWidget *w, GdkEventConfigure *e, GlMapView *that) { (void)w; (void)e; return that->Setup(); } gboolean GlMapView::MouseButtonSignal(GtkWidget *w, GdkEventButton *e, GlMapView *that) { (void)w; return that->MouseButton(e); } gboolean GlMapView::MouseMotionSignal(GtkWidget *w, GdkEventMotion *e, GlMapView *that) { (void)w; return that->MouseMotion(e); } gboolean GlMapView::KeyPressSignal(GtkWidget *w, GdkEventKey *e, GlMapView *that) { (void)w; return that->KeyPress(e); }