Thanks to visit codestin.com
Credit goes to glvis.github.io

GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
sdl.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-443271.
4 //
5 // This file is part of the GLVis visualization tool and library. For more
6 // information and source code availability see https://glvis.org.
7 //
8 // GLVis is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include <iostream>
13 #include "aux_vis.hpp"
14 #include "gl/renderer_core.hpp"
15 #include "gl/renderer_ff.hpp"
16 #include "sdl.hpp"
17 #include "sdl_main.hpp"
18 #ifdef __EMSCRIPTEN__
19 #include <emscripten.h>
20 #include <emscripten/html5.h>
21 #endif
22 
23 #ifdef SDL_VIDEO_DRIVER_COCOA
24 #include "sdl_mac.hpp"
25 #endif
26 
27 using std::cerr;
28 using std::endl;
29 
30 #ifdef GLVIS_DEBUG
31 #define PRINT_DEBUG(s) std::cerr << s
32 #else
33 #define PRINT_DEBUG(s) {}
34 #endif
35 
36 extern int GetMultisample();
37 
38 SdlWindow::Handle::Handle(const std::string& title, int x, int y, int w, int h,
39  Uint32 wndflags)
40  : hwnd(nullptr)
41  , gl_ctx(0)
42 {
43  hwnd = SDL_CreateWindow(title.c_str(), x, y, w, h, wndflags);
44  if (!hwnd)
45  {
46  PRINT_DEBUG("SDL window creation failed with error: "
47  << SDL_GetError() << endl);
48  return;
49  }
50  gl_ctx = SDL_GL_CreateContext(hwnd);
51  if (!gl_ctx)
52  {
53  PRINT_DEBUG("OpenGL context creation failed with error: "
54  << SDL_GetError() << endl);
55  }
56 }
57 
58 SdlWindow::Handle::~Handle()
59 {
60  if (gl_ctx)
61  {
62  SDL_GL_DeleteContext(gl_ctx);
63  }
64  if (hwnd)
65  {
66  SDL_DestroyWindow(hwnd);
67  }
68 }
69 
71 {
72  static SdlMainThread inst;
73  return inst;
74 }
75 
77 {
78  return (handle.gl_ctx != 0);
79 }
80 
82 
83 void SdlWindow::StartSDL(bool server_mode)
84 {
85  GetMainThread().MainLoop(server_mode);
86 }
87 
88 const int default_dpi = 72;
89 
90 bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h,
91  bool legacyGlOnly)
92 {
93 #ifdef __EMSCRIPTEN__
94  is_multithreaded = false;
95 #endif
96  // create a new SDL window
97  handle = GetMainThread().GetHandle(this, title, x, y, w, h, legacyGlOnly);
98 
99  // at this point, window should be up
100  if (!handle.isInitialized())
101  {
102  return false;
103  }
104 
105  window_id = SDL_GetWindowID(handle.hwnd);
106 
107  GLenum err = glewInit();
108 #ifdef GLEW_ERROR_NO_GLX_DISPLAY
109  // NOTE: Hacky workaround for Wayland initialization failure
110  // See https://github.com/nigels-com/glew/issues/172
111  if (err == GLEW_ERROR_NO_GLX_DISPLAY)
112  {
113  cerr << "GLEW: No GLX display found. If you are using Wayland this can "
114  << "be ignored." << endl;
115  err = GLEW_OK;
116  }
117 #endif
118  if (err != GLEW_OK)
119  {
120  cerr << "FATAL: Failed to initialize GLEW: "
121  << glewGetErrorString(err) << endl;
122  return false;
123  }
124 
125  // print versions
126  PRINT_DEBUG("Using GLEW " << glewGetString(GLEW_VERSION) << std::endl);
127  PRINT_DEBUG("Using GL " << glGetString(GL_VERSION) << std::endl);
128 
129  renderer.reset(new gl3::MeshRenderer);
130  renderer->setSamplesMSAA(GetMultisample());
131 #ifndef __EMSCRIPTEN__
132  if (!GLEW_VERSION_1_1)
133  {
134  cerr << "FATAL: Minimum of OpenGL 1.1 is required." << endl;
135  return false;
136  }
137  if (!GLEW_VERSION_1_3)
138  {
139  // Multitexturing was introduced into the core OpenGL specification in
140  // version 1.3; for versions before, we need to load the functions from
141  // the ARB_multitexture extension.
142  if (GLEW_ARB_multitexture)
143  {
144  glActiveTexture = glActiveTextureARB;
145  glClientActiveTexture = glClientActiveTextureARB;
146  glMultiTexCoord2f = glMultiTexCoord2fARB;
147  }
148  else
149  {
150  cerr << "FATAL: Missing OpenGL multitexture support." << endl;
151  return false;
152  }
153  }
154  if (!GLEW_VERSION_3_0 && GLEW_EXT_transform_feedback)
155  {
156  glBindBufferBase = glBindBufferBaseEXT;
157  // Use an explicit typecast to suppress an error from inconsistent types
158  // that are present in older versions of GLEW.
159  glTransformFeedbackVaryings =
160  (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)glTransformFeedbackVaryingsEXT;
161  glBeginTransformFeedback = glBeginTransformFeedbackEXT;
162  glEndTransformFeedback = glEndTransformFeedbackEXT;
163  }
164  if (!legacyGlOnly && (GLEW_VERSION_3_0
165  || (GLEW_VERSION_2_0 && GLEW_EXT_transform_feedback)))
166  {
167  // We require both shaders and transform feedback EXT_transform_feedback
168  // was made core in OpenGL 3.0
169  PRINT_DEBUG("Loading CoreGLDevice..." << endl);
170  renderer->setDevice<gl3::CoreGLDevice>();
171  }
172  else
173  {
174  PRINT_DEBUG("Shader support missing, loading FFGLDevice..." << endl);
175  renderer->setDevice<gl3::FFGLDevice>();
176  }
177 
178 #else
179  renderer->setDevice<gl3::CoreGLDevice>();
180 #endif
181 
182  return true;
183 }
184 
186 {
187  // Let the main SDL thread delete the handles
188  GetMainThread().DeleteHandle(std::move(handle));
189 }
190 
191 void SdlWindow::windowEvent(SDL_WindowEvent& ew)
192 {
193  switch (ew.event)
194  {
195  case SDL_WINDOWEVENT_EXPOSED:
196  case SDL_WINDOWEVENT_RESIZED:
197  update_before_expose = true;
198  if (onExpose)
199  {
200  wnd_state = RenderState::ExposePending;
201  }
202  break;
203  case SDL_WINDOWEVENT_CLOSE:
204  running = false;
205  break;
206  case SDL_WINDOWEVENT_MOVED:
207  update_before_expose = true;
208  break;
209  default:
210  break;
211  }
212 }
213 
214 void SdlWindow::motionEvent(SDL_MouseMotionEvent& em)
215 {
216  EventInfo info =
217  {
218  em.x, em.y,
219  SDL_GetModState()
220  };
221  if (em.state & SDL_BUTTON_LMASK)
222  {
223  if (onMouseMove[SDL_BUTTON_LEFT])
224  {
225  onMouseMove[SDL_BUTTON_LEFT](&info);
226  }
227  }
228  else if (em.state & SDL_BUTTON_RMASK)
229  {
230  if (onMouseMove[SDL_BUTTON_RIGHT])
231  {
232  onMouseMove[SDL_BUTTON_RIGHT](&info);
233  }
234  }
235  else if (em.state & SDL_BUTTON_MMASK)
236  {
237  if (onMouseMove[SDL_BUTTON_MIDDLE])
238  {
239  onMouseMove[SDL_BUTTON_MIDDLE](&info);
240  }
241  }
242 }
243 
244 void SdlWindow::mouseEventDown(SDL_MouseButtonEvent& eb)
245 {
246  if (onMouseDown[eb.button])
247  {
248  EventInfo info =
249  {
250  eb.x, eb.y,
251  SDL_GetModState()
252  };
253  onMouseDown[eb.button](&info);
254  }
255 }
256 
257 void SdlWindow::mouseEventUp(SDL_MouseButtonEvent& eb)
258 {
259  if (onMouseUp[eb.button])
260  {
261  EventInfo info =
262  {
263  eb.x, eb.y,
264  SDL_GetModState()
265  };
266  onMouseUp[eb.button](&info);
267  }
268 }
269 
270 void SdlWindow::keyEvent(SDL_Keysym& ks)
271 {
272  bool handled = false;
273  if (ks.sym >= 128 || ks.sym < 32)
274  {
275  if (onKeyDown[ks.sym])
276  {
277  onKeyDown[ks.sym](ks.mod);
278  handled = true;
279  }
280  }
281  else if (ks.sym < 256 && std::isdigit(ks.sym))
282  {
283  if (!(SDL_GetModState() & KMOD_SHIFT))
284  {
285  // handle number key event here
286  onKeyDown[ks.sym](ks.mod);
287  handled = true;
288  }
289  }
290  else if (ctrlDown)
291  {
292  if (onKeyDown[ks.sym])
293  {
294  onKeyDown[ks.sym](ks.mod);
295  handled = true;
296  }
297  }
298  if (ks.sym == SDLK_RCTRL || ks.sym == SDLK_LCTRL)
299  {
300  ctrlDown = true;
301  }
302  if (handled)
303  {
304  bool isAlt = ks.mod & (KMOD_ALT);
305  bool isCtrl = ks.mod & (KMOD_CTRL);
306  saved_keys += "[";
307  if (isCtrl) { saved_keys += "C-"; }
308  if (isAlt) { saved_keys += "Alt-"; }
309  if (ks.sym < 256 && std::isalpha(ks.sym))
310  {
311  // key with corresponding text output
312  char c = ks.sym;
313  if (!(ks.mod & KMOD_SHIFT)) { c = std::tolower(c); }
314  saved_keys += c;
315  }
316  else
317  {
318  saved_keys += SDL_GetKeyName(ks.sym);
319  }
320  saved_keys += "]";
321  }
322 }
323 
324 void SdlWindow::keyEvent(char c)
325 {
326  if (!std::isdigit(c) && onKeyDown[c])
327  {
328  SDL_Keymod mods = SDL_GetModState();
329  bool isAlt = mods & (KMOD_ALT);
330  bool isCtrl = mods & (KMOD_CTRL);
331  onKeyDown[c](mods);
332  if (isAlt || isCtrl)
333  {
334  saved_keys += "[";
335  if (isCtrl) { saved_keys += "C-"; }
336  if (isAlt) { saved_keys += "Alt-"; }
337  }
338  saved_keys += c;
339  if (isAlt || isCtrl)
340  {
341  saved_keys += "]";
342  }
343  }
344 }
345 
346 void SdlWindow::multiGestureEvent(SDL_MultiGestureEvent & e)
347 {
348  if (e.numFingers == 2)
349  {
350  if (onTouchPinch && fabs(e.dDist) > 0.00002)
351  {
352  onTouchPinch(e);
353  }
354 
355  if (onTouchRotate)
356  {
357  onTouchRotate(e);
358  }
359  }
360 }
361 
363 {
364  if (!is_multithreaded)
365  {
366  // Pull events from GetMainThread() object
368  }
369  bool events_pending = false;
370  bool sleep = false;
371  {
372  lock_guard<mutex> evt_guard{event_mutex};
373  events_pending = !waiting_events.empty();
374  }
375  if (events_pending)
376  {
377  bool keep_going;
378  do
379  {
380  SDL_Event e;
381  // Fetch next event from the queue
382  {
383  lock_guard<mutex> evt_guard{event_mutex};
384  e = waiting_events.front();
385  waiting_events.pop_front();
386  events_pending = !waiting_events.empty();
387  }
388  keep_going = false;
389  switch (e.type)
390  {
391  case SDL_QUIT:
392  running = false;
393  break;
394  case SDL_WINDOWEVENT:
395  windowEvent(e.window);
396  if (wnd_state != RenderState::ExposePending)
397  {
398  keep_going = true;
399  }
400  break;
401  case SDL_MULTIGESTURE:
402  multiGestureEvent(e.mgesture);
403  keep_going = true;
404  break;
405  case SDL_KEYDOWN:
406  keyEvent(e.key.keysym);
407  break;
408  case SDL_KEYUP:
409  if (e.key.keysym.sym == SDLK_LCTRL
410  || e.key.keysym.sym == SDLK_RCTRL)
411  {
412  ctrlDown = false;
413  }
414  break;
415  case SDL_TEXTINPUT:
416  keyEvent(e.text.text[0]);
417  break;
418  case SDL_MOUSEMOTION:
419  motionEvent(e.motion);
420  // continue processing events
421  keep_going = true;
422  break;
423  case SDL_MOUSEBUTTONDOWN:
424  mouseEventDown(e.button);
425  break;
426  case SDL_MOUSEBUTTONUP:
427  mouseEventUp(e.button);
428  break;
429  }
430  }
431  while (keep_going && events_pending);
432  }
433  else if (onIdle)
434  {
435  {
436  unique_lock<mutex> event_lock{event_mutex};
437  call_idle_func = false;
438  }
439  sleep = onIdle();
440  }
441  else
442  {
443  // No actions performed this iteration.
444  sleep = true;
445  }
446  if (wnd_state == RenderState::ExposePending)
447  {
448 #ifdef SDL_VIDEO_DRIVER_COCOA
449  if (update_before_expose)
450  {
451  // On SDL, when the OpenGL context is on a separate thread from the
452  // main thread, the call to [NSOpenGLContext update] after a resize or
453  // move event is only scheduled for after the next swap event. Any
454  // rendering/OpenGL commands occuring before this update will be
455  // corrupted.
456  //
457  // To avoid this issue, we just call [NSOpenGLContext update]
458  // immediately before the expose event.
459  SdlCocoaPlatform* platform =
460  dynamic_cast<SdlCocoaPlatform*>(GetMainThread().GetPlatform());
461  if (platform)
462  {
463  platform->ContextUpdate();
464  }
465  update_before_expose = false;
466  }
467 #endif
468  onExpose();
469  wnd_state = RenderState::SwapPending;
470  }
471  else if (is_multithreaded && sleep)
472  {
473  // No updates to vis, wait for next wakeup event from glvis_command or WM
474  unique_lock<mutex> event_lock{event_mutex};
475  events_available.wait(event_lock, [this]()
476  {
477  // Sleep until events from WM or glvis_command can be handled
478  return !waiting_events.empty() || call_idle_func;
479  });
480  }
481 }
482 
484 {
485  running = true;
486 #ifdef __EMSCRIPTEN__
487  emscripten_set_main_loop_arg([](void* arg)
488  {
489  ((SdlWindow*) arg)->mainIter();
490  }, this, 0, true);
491 #else
492  while (running)
493  {
494  mainIter();
495  if (takeScreenshot)
496  {
497  Screenshot(screenshot_file.c_str(), screenshot_convert);
498  takeScreenshot = false;
499  }
500  if (wnd_state == RenderState::SwapPending)
501  {
502 #ifdef SDL_VIDEO_DRIVER_COCOA
503  // TODO: Temporary workaround - after merge, everyone should update to
504  // latest SDL
505  SdlCocoaPlatform* mac_platform
506  = dynamic_cast<SdlCocoaPlatform*>(GetMainThread().GetPlatform());
507  if (mac_platform && mac_platform->UseThreadWorkaround())
508  {
509  mac_platform->SwapWindow();
510  }
511  else
512  {
513  SDL_GL_SwapWindow(handle.hwnd);
514  }
515 #else
516  SDL_GL_SwapWindow(handle.hwnd);
517 #endif
518  wnd_state = RenderState::Updated;
519  }
520  }
521 #endif
522 }
523 
525 {
526  // Note: not executed from the main thread
527  {
528  lock_guard<mutex> evt_guard{event_mutex};
529  call_idle_func = true;
530  }
531  events_available.notify_all();
532 }
533 
534 void SdlWindow::getWindowSize(int& w, int& h)
535 {
536  w = 0;
537  h = 0;
538  if (handle.isInitialized())
539  {
540 #ifdef __EMSCRIPTEN__
541  if (canvas_id_.empty())
542  {
543  std::cerr << "error: id is undefined: " << canvas_id_ << std::endl;
544  return;
545  }
546  double dw, dh;
547  auto err = emscripten_get_element_css_size(canvas_id_.c_str(), &dw, &dh);
548  w = int(dw);
549  h = int(dh);
550  if (err != EMSCRIPTEN_RESULT_SUCCESS)
551  {
552  std::cerr << "error (emscripten_get_element_css_size): " << err << std::endl;
553  return;
554  }
555 #else
556  SDL_GetWindowSize(handle.hwnd, &w, &h);
557 #endif
558  w /= pixel_scale_x;
559  h /= pixel_scale_y;
560  }
561 }
562 
563 void SdlWindow::getGLDrawSize(int& w, int& h)
564 {
565  SDL_GL_GetDrawableSize(handle.hwnd, &w, &h);
566 }
567 
568 void SdlWindow::getDpi(int& w, int& h)
569 {
570  w = default_dpi;
571  h = default_dpi;
572  if (!handle.isInitialized())
573  {
574  PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl);
575  return;
576  }
577  int disp = SDL_GetWindowDisplayIndex(handle.hwnd);
578  if (disp < 0)
579  {
580  PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError()
581  << endl);
582  PRINT_DEBUG("returning default dpi of " << default_dpi << endl);
583  return;
584  }
585 
586  float f_w, f_h;
587  if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h))
588  {
589  PRINT_DEBUG("warning: problem getting dpi, setting to " << default_dpi
590  << ": " << SDL_GetError() << endl);
591  }
592  else
593  {
594  PRINT_DEBUG("Screen DPI: w = " << f_w << " ppi, h = " << f_h << " ppi");
595  w = f_w + 0.5f;
596  h = f_h + 0.5f;
597  PRINT_DEBUG(" (" << w << " x " << h << ')' << endl);
598  }
599 }
600 
601 void SdlWindow::setWindowTitle(std::string& title)
602 {
603  setWindowTitle(title.c_str());
604 }
605 
606 void SdlWindow::setWindowTitle(const char * title)
607 {
608  GetMainThread().SetWindowTitle(handle, title);
609 }
610 
611 void SdlWindow::setWindowSize(int w, int h)
612 {
613  GetMainThread().SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h);
614  update_before_expose = true;
615 
616 }
617 
618 void SdlWindow::setWindowPos(int x, int y)
619 {
620  bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(x) ||
621  SDL_WINDOWPOS_ISCENTERED(x);
622  bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) ||
623  SDL_WINDOWPOS_ISCENTERED(y);
625  uc_x ? x : pixel_scale_x*x,
626  uc_y ? y : pixel_scale_y*y);
627  update_before_expose = true;
628 }
629 
630 void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m)
631 {
632  SDL_Event event;
633  if (k >= 32 && k < 128)
634  {
635  event.type = SDL_TEXTINPUT;
636  event.text.windowID = window_id;
637  event.text.text[0] = k;
638  }
639  else
640  {
641  event.type = SDL_KEYDOWN;
642  event.key.windowID = window_id;
643  event.key.keysym.sym = k;
644  event.key.keysym.mod = m;
645  }
646  queueEvents({ event });
647 }
648 
650 {
651  SDL_GL_SwapWindow(handle.hwnd);
652 }
SdlMainThread & GetMainThread()
Definition: sdl.cpp:70
void signalKeyDown(SDL_Keycode k, SDL_Keymod m=KMOD_NONE)
Definition: sdl.cpp:630
void SwapWindow()
Definition: sdl_mac.mm:111
bool UseThreadWorkaround() const
Definition: sdl_mac.mm:66
int Screenshot(const char *fname, bool convert)
Take a screenshot using libtiff, libpng or sdl2.
Definition: aux_vis.cpp:970
void getGLDrawSize(int &w, int &h)
Definition: sdl.cpp:563
bool isGlInitialized()
Returns true if the OpenGL context was successfully initialized.
Definition: sdl.cpp:76
void mainLoop()
Runs the window loop.
Definition: sdl.cpp:483
const int default_dpi
Definition: sdl.cpp:88
~SdlWindow()
Definition: sdl.cpp:185
void setWindowTitle(std::string &title)
Definition: sdl.cpp:601
void signalLoop()
Definition: sdl.cpp:524
void setWindowPos(int x, int y)
Definition: sdl.cpp:618
void SetWindowTitle(const Handle &handle, std::string title)
Definition: sdl_main.cpp:334
int GetMultisample()
Definition: aux_vis.cpp:1573
void swapBuffer()
Definition: sdl.cpp:649
void getWindowSize(int &w, int &h)
Definition: sdl.cpp:534
SdlNativePlatform * GetPlatform() const
Definition: sdl_main.hpp:60
Handle GetHandle(SdlWindow *wnd, const std::string &title, int x, int y, int w, int h, bool legacyGlOnly)
Definition: sdl_main.cpp:270
static void StartSDL(bool server_mode)
Definition: sdl.cpp:83
void MainLoop(bool server_mode)
Definition: sdl_main.cpp:98
void SetWindowPosition(const Handle &handle, int x, int y)
Definition: sdl_main.cpp:360
bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly)
Definition: sdl.cpp:90
void DeleteHandle(Handle to_delete)
Definition: sdl_main.cpp:316
string screenshot_file
Definition: glvis_driver.py:45
void DispatchSDLEvents()
Definition: sdl_main.cpp:188
void SetWindowSize(const Handle &handle, int w, int h)
Definition: sdl_main.cpp:347
void ContextUpdate()
Definition: sdl_mac.mm:80
SdlWindow()
Definition: sdl.cpp:81
void getDpi(int &wdpi, int &hdpi)
Definition: sdl.cpp:568
void mainIter()
Definition: sdl.cpp:362
void setWindowSize(int w, int h)
Definition: sdl.cpp:611