You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

305 line
8.0 KiB

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