GlfwWindow.cpp
Go to the documentation of this file.
1 #include "GlfwWindow.hpp"
2 #include <ivorium_config.hpp>
3 
4 #if IV_GLPLATFORM_GLFW
5 
7 
8 #include <GL/glew.h>
9 #include <GLFW/glfw3.h>
10 
11 #include <iomanip>
12 #include <unistd.h>
13 
14 namespace iv
15 {
16 
17 GlfwWindow::GlfwWindow( std::string const & window_title ) :
18  window( nullptr ),
19  listener( nullptr ),
20  _render_target(),
21  _time_last( glfwGetTime() ),
22  _focused( true ),
23  _current_character( 0 ),
24  _floating_fps( 0.0 )
25 {
26  // window hints
27  glfwWindowHint( GLFW_RESIZABLE, GLFW_TRUE );
28  glfwWindowHint( GLFW_SCALE_TO_MONITOR, GLFW_TRUE );
29  glfwWindowHint( GLFW_CENTER_CURSOR, GLFW_FALSE );
30  //glfwWindowHint( GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE );
31  //glfwWindowHint( GLFW_DECORATED, GLFW_FALSE );
32 
33  // framebuffer hints
34  glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
35  glfwWindowHint( GLFW_RED_BITS, 8 );
36  glfwWindowHint( GLFW_GREEN_BITS, 8 );
37  glfwWindowHint( GLFW_BLUE_BITS, 8 );
38  glfwWindowHint( GLFW_ALPHA_BITS, 8 );
39  glfwWindowHint( GLFW_SAMPLES, 1 );
40  //glfwWindowHint( GLFW_SRGB_CAPABLE, GLFW_TRUE );
41 
42  // monitor hints
43  //glfwWindowHint( GLFW_REFRESH_RATE, 60 );
44 
45  // context hints
46  glfwWindowHint( GLFW_CLIENT_API, GLFW_OPENGL_API );
47  glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
48  glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 2 );
49  glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE );
50  glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
51 
52  // create window
53  this->window = glfwCreateWindow( 365, 650, window_title.c_str(), NULL, NULL );
54  if( !this->window )
55  {
56  runtime_warning( SRC_INFO, "Call to glfwCreateWindow failed." );
57  return;
58  }
59 
60  // read window geometry
61  float xscale, yscale;
62  glfwGetWindowContentScale(window, &xscale, &yscale);
63  xscale = 1.0f;
64  yscale = 1.0f;
65 
67  glfwGetWindowSize(window, &geo.size.x, &geo.size.y);
68  geo.density = ( xscale + yscale ) / 2.0f;
69 
70  this->_render_target.set_geometry( geo );
71 
72  // opengl setup
73  glfwMakeContextCurrent( this->window );
74  glewExperimental = GL_TRUE;
75  glewInit();
76  this->setupGL();
77  glfwSetWindowSizeLimits( this->window, 50, 50, GLFW_DONT_CARE, GLFW_DONT_CARE );
78 
79  // set callbacks
80  glfwSetWindowUserPointer( this->window, reinterpret_cast< void * >( this ) );
81  glfwSetWindowFocusCallback( this->window, GlfwWindow::S_FocusCallback );
82  glfwSetWindowContentScaleCallback( this->window, GlfwWindow::S_ContentScaleCallback );
83  glfwSetWindowSizeCallback( this->window, GlfwWindow::S_SizeCallback );
84  glfwSetFramebufferSizeCallback( this->window, GlfwWindow::S_FBSizeCallback );
85  glfwSetKeyCallback( this->window, GlfwWindow::S_KeyCallback );
86  glfwSetMouseButtonCallback( this->window, GlfwWindow::S_MouseButtonCallback );
87  glfwSetScrollCallback( this->window, GlfwWindow::S_ScrollCallback );
88  glfwSetCharCallback( this->window, GlfwWindow::S_CharCallback );
89 }
90 
92 {
93  glfwDestroyWindow( this->window );
94 }
95 
97 {
98  return true;
99 }
100 
102 {
103  this->listener = listener;
104 }
105 
107 {
108  return this->_render_target.geometry();
109 }
110 
112 {
113  return &this->_render_target;
114 }
115 
117 {
118  while( !glfwWindowShouldClose( this->window ) )
119  {
120  // compute delta time
121  double time = glfwGetTime();
122  double delta = time - this->_time_last;
123  uint64_t delta_ms = delta * 1000.0;
124  this->_time_last = time;
125 
126  // show FPS
127  #ifndef NDEBUG
128  {
129  double fps = 1.0 / delta;
130  this->_floating_fps = this->_floating_fps * 0.9 + fps * 0.1;
131 
132  std::stringstream ss;
133  ss << std::setprecision( 1 ) << std::fixed << this->_floating_fps << " fps";
134  glfwSetWindowTitle( this->window, ss.str().c_str() );
135  }
136  #endif
137 
138  // events
139  glfwPollEvents();
140 
141  // update
142  this->listener->update( delta_ms );
143 
144  // render
145  this->render();
146 
147  //
148  double target_fps = double( glfwGetVideoMode( glfwGetPrimaryMonitor() )->refreshRate );
149  double frame_end = time + 1.0 / target_fps;
150 
151  // extra update
152  bool more_extras_needed = this->listener->extra_update(); // at least one extra update
153  while( more_extras_needed )
154  {
155  // check if we have time
156  if( glfwGetTime() >= frame_end )
157  break;
158 
159  // call second extra update
160  more_extras_needed = this->listener->extra_update();
161  }
162 
163  // sleep until next frame
164  double after_time = glfwGetTime();
165  double us_remaining = ( frame_end - glfwGetTime() - 0.0005 ) * 1000000.0f;
166  if( us_remaining > 0 )
167  usleep( (int)us_remaining );
168 
169  // precise sync
170  while( glfwGetTime() < frame_end );
171  }
172 }
173 
174 void GlfwWindow::render()
175 {
176  // render
177  glGetError(); // clear errors
178  glClear( GL_COLOR_BUFFER_BIT );
179 
180  this->_render_target.frame_setup();
181  this->listener->draw();
182  this->_render_target.frame_close();
183 
184  glfwSwapBuffers( this->window );
185 }
186 
187 void GlfwWindow::FocusCallback( int focused )
188 {
189  this->_focused = focused;
190  if( !focused )
191  this->listener->focus_lost();
192 }
193 
194 void GlfwWindow::ContentScaleCallback( float xscale, float yscale )
195 {
196  RenderTarget::Geometry geo = this->_render_target.geometry();
197  geo.density = ( xscale + yscale ) / 2.0f;
198  this->_render_target.set_geometry( geo );
199  this->listener->resized( geo );
200 }
201 
202 void GlfwWindow::SizeCallback( int width, int height )
203 {
204  RenderTarget::Geometry geo = this->_render_target.geometry();
205  geo.size.x = width;
206  geo.size.y = height;
207  this->_render_target.set_geometry( geo );
208  this->listener->resized( geo );
209 }
210 
211 void GlfwWindow::FBSizeCallback( int width, int height )
212 {
213  glViewport( 0, 0, width, height );
214 }
215 
216 //---- keyboard -----
217 void GlfwWindow::KeyCallback( int key, int scancode, int action, int mods )
218 {
219  auto ivkey = this->KeyboardKey_GlfwToIvorium( key );
220  if( !ivkey.has_value() )
221  return;
222 
223  Input::Type type;
224  switch( action )
225  {
226  case GLFW_PRESS: type = Input::Type::Press; break;
227  case GLFW_RELEASE: type = Input::Type::Release; break;
228  default: return;
229  }
230 
231  Input i( type, ivkey.value(), 0, this->_focused );
232  this->listener->input( &i );
233 }
234 
235 //---- mouse -----
236 void GlfwWindow::MouseButtonCallback( int button, int action, int mods )
237 {
238  auto ivbutton = this->MouseButton_GlfwToIvorium( button );
239  if( !ivbutton.has_value() )
240  return;
241 
242  Input::Type type;
243  switch( action )
244  {
245  case GLFW_PRESS: type = Input::Type::Press; break;
246  case GLFW_RELEASE: type = Input::Type::Release; break;
247  default: return;
248  }
249 
250  Input i( type, ivbutton.value(), 0, this->_focused );
251  this->listener->input( &i );
252 }
253 
254 void GlfwWindow::ScrollCallback( double xoffset, double yoffset )
255 {
256  while( xoffset > 0.5f )
257  {
258  Input i( Input::Type::Trigger, Input::Key::MouseScrollLeft, 0, this->_focused );
259  this->listener->input( &i );
260  xoffset -= 1.0f;
261  }
262 
263  while( xoffset < -0.5f )
264  {
266  this->listener->input( &i );
267  xoffset += 1.0f;
268  }
269 
270  while( yoffset > 0.5f )
271  {
272  Input i( Input::Type::Trigger, Input::Key::MouseScrollUp, 0, this->_focused );
273  this->listener->input( &i );
274  yoffset -= 1.0f;
275  }
276 
277  while( yoffset < -0.5f )
278  {
279  Input i( Input::Type::Trigger, Input::Key::MouseScrollDown, 0, this->_focused );
280  this->listener->input( &i );
281  yoffset += 1.0f;
282  }
283 }
284 
285 void GlfwWindow::CharCallback( unsigned int codepoint )
286 {
287  this->_current_character = codepoint;
288  Input i( Input::Type::Trigger, Input::Key::Character, 0, this->_focused );
289  this->listener->input( &i );
290  this->_current_character = 0;
291 }
292 
294 {
295  if( key >= Input::Key::MOUSE_BEGIN && key < Input::Key::MOUSE_END )
296  {
297  double xpos, ypos;
298  glfwGetCursorPos( this->window, &xpos, &ypos );
299  return int2( xpos, ypos );
300  }
301 
302  return int2( 0, 0 );
303 }
304 
305 float GlfwWindow::input_value( Input::Key key, int device_id )
306 {
307  return 0.0f;
308 }
309 
311 {
312  return this->_current_character;
313 }
314 
315 
316 //===========================================================================================
317 void GlfwWindow::S_FocusCallback( GLFWwindow * window, int focused ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->FocusCallback( focused ); }
318 void GlfwWindow::S_ContentScaleCallback( GLFWwindow * window, float xscale, float yscale ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->ContentScaleCallback( xscale, yscale ); }
319 void GlfwWindow::S_SizeCallback( GLFWwindow * window, int width, int height ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->SizeCallback( width, height ); }
320 void GlfwWindow::S_FBSizeCallback( GLFWwindow * window, int width, int height ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->FBSizeCallback( width, height ); }
321 void GlfwWindow::S_KeyCallback( GLFWwindow * window, int key, int scancode, int action, int mods ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->KeyCallback( key, scancode, action, mods ); }
322 void GlfwWindow::S_MouseButtonCallback( GLFWwindow * window, int button, int action, int mods ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->MouseButtonCallback( button, action, mods ); }
323 void GlfwWindow::S_ScrollCallback( GLFWwindow * window, double xoffset, double yoffset ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->ScrollCallback( xoffset, yoffset ); }
324 void GlfwWindow::S_CharCallback( GLFWwindow * window, unsigned int codepoint ) { reinterpret_cast< GlfwWindow * >( glfwGetWindowUserPointer( window ) )->CharCallback( codepoint ); }
325 
326 //===========================================================================================
327 std::optional< Input::Key > GlfwWindow::KeyboardKey_GlfwToIvorium( int key )
328 {
329  switch( key )
330  {
331  case GLFW_KEY_SPACE: return Input::Key::Space;
332  case GLFW_KEY_APOSTROPHE: return Input::Key::Apostrophe;
333  case GLFW_KEY_COMMA: return Input::Key::Comma;
334  case GLFW_KEY_MINUS: return Input::Key::Minus;
335  case GLFW_KEY_PERIOD: return Input::Key::Period;
336  case GLFW_KEY_SLASH: return Input::Key::Slash;
337  case GLFW_KEY_0: return Input::Key::Num_0;
338  case GLFW_KEY_1: return Input::Key::Num_1;
339  case GLFW_KEY_2: return Input::Key::Num_2;
340  case GLFW_KEY_3: return Input::Key::Num_3;
341  case GLFW_KEY_4: return Input::Key::Num_4;
342  case GLFW_KEY_5: return Input::Key::Num_5;
343  case GLFW_KEY_6: return Input::Key::Num_6;
344  case GLFW_KEY_7: return Input::Key::Num_7;
345  case GLFW_KEY_8: return Input::Key::Num_8;
346  case GLFW_KEY_9: return Input::Key::Num_9;
347  case GLFW_KEY_SEMICOLON: return Input::Key::Semicolon;
348  case GLFW_KEY_EQUAL: return Input::Key::Equal;
349  case GLFW_KEY_A: return Input::Key::Char_A;
350  case GLFW_KEY_B: return Input::Key::Char_B;
351  case GLFW_KEY_C: return Input::Key::Char_C;
352  case GLFW_KEY_D: return Input::Key::Char_D;
353  case GLFW_KEY_E: return Input::Key::Char_E;
354  case GLFW_KEY_F: return Input::Key::Char_F;
355  case GLFW_KEY_G: return Input::Key::Char_G;
356  case GLFW_KEY_H: return Input::Key::Char_H;
357  case GLFW_KEY_I: return Input::Key::Char_I;
358  case GLFW_KEY_J: return Input::Key::Char_J;
359  case GLFW_KEY_K: return Input::Key::Char_K;
360  case GLFW_KEY_L: return Input::Key::Char_L;
361  case GLFW_KEY_M: return Input::Key::Char_M;
362  case GLFW_KEY_N: return Input::Key::Char_N;
363  case GLFW_KEY_O: return Input::Key::Char_O;
364  case GLFW_KEY_P: return Input::Key::Char_P;
365  case GLFW_KEY_Q: return Input::Key::Char_Q;
366  case GLFW_KEY_R: return Input::Key::Char_R;
367  case GLFW_KEY_S: return Input::Key::Char_S;
368  case GLFW_KEY_T: return Input::Key::Char_T;
369  case GLFW_KEY_U: return Input::Key::Char_U;
370  case GLFW_KEY_V: return Input::Key::Char_V;
371  case GLFW_KEY_W: return Input::Key::Char_W;
372  case GLFW_KEY_X: return Input::Key::Char_X;
373  case GLFW_KEY_Y: return Input::Key::Char_Y;
374  case GLFW_KEY_Z: return Input::Key::Char_Z;
375  case GLFW_KEY_LEFT_BRACKET: return Input::Key::Left_Bracket;
376  case GLFW_KEY_BACKSLASH: return Input::Key::Backslash;
377  case GLFW_KEY_RIGHT_BRACKET:return Input::Key::Right_Bracket;
378  case GLFW_KEY_GRAVE_ACCENT: return Input::Key::Grave_Accent;
379  case GLFW_KEY_WORLD_1: return Input::Key::World_1;
380  case GLFW_KEY_WORLD_2: return Input::Key::World_2;
381  case GLFW_KEY_ESCAPE: return Input::Key::Escape;
382  case GLFW_KEY_ENTER: return Input::Key::Enter;
383  case GLFW_KEY_TAB: return Input::Key::Tab;
384  case GLFW_KEY_BACKSPACE: return Input::Key::Backspace;
385  case GLFW_KEY_INSERT: return Input::Key::Insert;
386  case GLFW_KEY_DELETE: return Input::Key::Delete;
387  case GLFW_KEY_RIGHT: return Input::Key::Right;
388  case GLFW_KEY_LEFT: return Input::Key::Left;
389  case GLFW_KEY_DOWN: return Input::Key::Down;
390  case GLFW_KEY_UP: return Input::Key::Up;
391  case GLFW_KEY_PAGE_UP: return Input::Key::Page_Up;
392  case GLFW_KEY_PAGE_DOWN: return Input::Key::Page_Down;
393  case GLFW_KEY_HOME: return Input::Key::Home;
394  case GLFW_KEY_END: return Input::Key::End;
395  case GLFW_KEY_CAPS_LOCK: return Input::Key::Caps_Lock;
396  case GLFW_KEY_SCROLL_LOCK: return Input::Key::Scroll_Lock;
397  case GLFW_KEY_NUM_LOCK: return Input::Key::Num_Lock;
398  case GLFW_KEY_PRINT_SCREEN: return Input::Key::Print_Screen;
399  case GLFW_KEY_PAUSE: return Input::Key::Pause;
400  case GLFW_KEY_F1: return Input::Key::F1;
401  case GLFW_KEY_F2: return Input::Key::F2;
402  case GLFW_KEY_F3: return Input::Key::F3;
403  case GLFW_KEY_F4: return Input::Key::F4;
404  case GLFW_KEY_F5: return Input::Key::F5;
405  case GLFW_KEY_F6: return Input::Key::F6;
406  case GLFW_KEY_F7: return Input::Key::F7;
407  case GLFW_KEY_F8: return Input::Key::F8;
408  case GLFW_KEY_F9: return Input::Key::F9;
409  case GLFW_KEY_F10: return Input::Key::F10;
410  case GLFW_KEY_F11: return Input::Key::F11;
411  case GLFW_KEY_F12: return Input::Key::F12;
412  case GLFW_KEY_F13: return Input::Key::F13;
413  case GLFW_KEY_F14: return Input::Key::F14;
414  case GLFW_KEY_F15: return Input::Key::F15;
415  case GLFW_KEY_F16: return Input::Key::F16;
416  case GLFW_KEY_F17: return Input::Key::F17;
417  case GLFW_KEY_F18: return Input::Key::F18;
418  case GLFW_KEY_F19: return Input::Key::F19;
419  case GLFW_KEY_F20: return Input::Key::F20;
420  case GLFW_KEY_F21: return Input::Key::F21;
421  case GLFW_KEY_F22: return Input::Key::F22;
422  case GLFW_KEY_F23: return Input::Key::F23;
423  case GLFW_KEY_F24: return Input::Key::F24;
424  case GLFW_KEY_F25: return Input::Key::F25;
425  case GLFW_KEY_KP_0: return Input::Key::KeyPad_0;
426  case GLFW_KEY_KP_1: return Input::Key::KeyPad_1;
427  case GLFW_KEY_KP_2: return Input::Key::KeyPad_2;
428  case GLFW_KEY_KP_3: return Input::Key::KeyPad_3;
429  case GLFW_KEY_KP_4: return Input::Key::KeyPad_4;
430  case GLFW_KEY_KP_5: return Input::Key::KeyPad_5;
431  case GLFW_KEY_KP_6: return Input::Key::KeyPad_6;
432  case GLFW_KEY_KP_7: return Input::Key::KeyPad_7;
433  case GLFW_KEY_KP_8: return Input::Key::KeyPad_8;
434  case GLFW_KEY_KP_9: return Input::Key::KeyPad_9;
435  case GLFW_KEY_KP_DECIMAL: return Input::Key::KeyPad_Decimal;
436  case GLFW_KEY_KP_DIVIDE: return Input::Key::KeyPad_Divide;
437  case GLFW_KEY_KP_MULTIPLY: return Input::Key::KeyPad_Multiply;
438  case GLFW_KEY_KP_SUBTRACT: return Input::Key::KeyPad_Subtract;
439  case GLFW_KEY_KP_ADD: return Input::Key::KeyPad_Add;
440  case GLFW_KEY_KP_ENTER: return Input::Key::KeyPad_Enter;
441  case GLFW_KEY_KP_EQUAL: return Input::Key::KeyPad_Equal;
442  case GLFW_KEY_LEFT_SHIFT: return Input::Key::Left_Shift;
443  case GLFW_KEY_LEFT_CONTROL: return Input::Key::Left_Control;
444  case GLFW_KEY_LEFT_ALT: return Input::Key::Left_Alt;
445  case GLFW_KEY_LEFT_SUPER: return Input::Key::Left_Super;
446  case GLFW_KEY_RIGHT_SHIFT: return Input::Key::Right_Shift;
447  case GLFW_KEY_RIGHT_CONTROL:return Input::Key::Right_Control;
448  case GLFW_KEY_RIGHT_ALT: return Input::Key::Right_Alt;
449  case GLFW_KEY_RIGHT_SUPER: return Input::Key::Right_Super;
450  case GLFW_KEY_MENU: return Input::Key::Menu;
451  default: return std::nullopt;
452  }
453 }
454 
455 std::optional< Input::Key > GlfwWindow::MouseButton_GlfwToIvorium( int button )
456 {
457  switch( button )
458  {
459  case GLFW_MOUSE_BUTTON_LEFT: return Input::Key::MouseLeft;
460  case GLFW_MOUSE_BUTTON_RIGHT: return Input::Key::MouseRight;
461  case GLFW_MOUSE_BUTTON_MIDDLE: return Input::Key::MouseMiddle;
462  case GLFW_MOUSE_BUTTON_4: return Input::Key::MouseBack;
463  case GLFW_MOUSE_BUTTON_5: return Input::Key::MouseForward;
464  default: return std::nullopt;
465  }
466 }
467 
468 }
469 
470 #endif