GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
sdl.hpp
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 #ifndef GLVIS_SDL_HPP
13 #define GLVIS_SDL_HPP
14 
15 #include <string>
16 #include <memory>
17 #include <functional>
18 #include <map>
19 #include <mutex>
20 #include <condition_variable>
21 #include <deque>
22 #include "gl/renderer.hpp"
23 #include "sdl_helper.hpp"
24 
25 struct EventInfo
26 {
27  GLint mouse_x;
28  GLint mouse_y;
29  SDL_Keymod keymod;
30 };
31 
32 typedef void (*TouchDelegate)(SDL_MultiGestureEvent&);
33 typedef void (*MouseDelegate)(EventInfo*);
34 typedef std::function<void(GLenum)> KeyDelegate;
35 typedef void (*WindowDelegate)(int, int);
36 typedef void (*Delegate)();
37 typedef bool (*IdleDelegate)();
38 
39 class SdlMainThread;
40 
41 class SdlWindow
42 {
43 private:
44  friend class SdlMainThread;
45  struct Handle
46  {
47  SDL_Window * hwnd{nullptr};
48  SDL_GLContext gl_ctx{};
49 
50  Handle() = default;
51 
52  Handle(const std::string& title, int x, int y, int w, int h,
53  Uint32 wndflags);
54 
55  ~Handle();
56 
57  Handle(Handle&& other)
58  : hwnd(other.hwnd), gl_ctx(other.gl_ctx)
59  {
60  other.hwnd = nullptr;
61  other.gl_ctx = 0;
62  }
63 
64  Handle& operator= (Handle&& other)
65  {
66  std::swap(hwnd, other.hwnd);
67  std::swap(gl_ctx, other.gl_ctx);
68  return *this;
69  }
70 
71  bool isInitialized() const
72  {
73  return (hwnd != nullptr && gl_ctx != 0);
74  }
75  };
76 
77  int window_id = -1;
78  Handle handle;
79  std::unique_ptr<gl3::MeshRenderer> renderer;
80  static const int high_dpi_threshold = 144;
81  // The display is high-dpi when:
82  // - SDL's "screen coordinates" sizes are different from the pixel sizes, or
83  // - either the horizontal or the vertical dpi, as returned by getDpi(),
84  // is >= high_dpi_threshold, defined above.
85  bool high_dpi = false;
86  // Ratio of SDL's "screen coordinates" to GLVis' "screen coordinates":
87  // - set to 1 on non-high-dpi displays,
88  // - set to 1 on high-dpi displays where SDL's "screen coordinates" sizes are
89  // different from the pixels sizes (e.g. Mac retina displays),
90  // - set to 2 on other high-dpi displays, so that GLVis can always work with
91  // scaled "screen coordinates" on all high-dpi displays.
92  float pixel_scale_x = 1.0f, pixel_scale_y = 1.0f;
93 
94  bool running;
95 
96  IdleDelegate onIdle{nullptr};
97  Delegate onExpose{nullptr};
98  WindowDelegate onReshape{nullptr};
99  std::map<int, KeyDelegate> onKeyDown;
100  std::map<int, MouseDelegate> onMouseDown;
101  std::map<int, MouseDelegate> onMouseUp;
102  std::map<int, MouseDelegate> onMouseMove;
103  TouchDelegate onTouchPinch{nullptr};
104  TouchDelegate onTouchRotate{nullptr};
105 
106  bool ctrlDown{false};
107 
108 #ifdef __EMSCRIPTEN__
109  std::string canvas_id_;
110 #endif
111 
112  enum class RenderState
113  {
114  // window displayed is fully current (no events or backbuffer updates pending)
115  Updated,
116  // events issued which may require a call to MyExpose
118  // back buffer updated by MyExpose, now awaiting swap to be displayed on window
120  };
121 
122  RenderState wnd_state{RenderState::Updated};
123 
124  bool update_before_expose{false};
125 
126  //bool requiresExpose;
127  bool takeScreenshot{false};
128  std::string screenshot_file;
129  bool screenshot_convert;
130 
131  // internal event handlers
132  void windowEvent(SDL_WindowEvent& ew);
133  void motionEvent(SDL_MouseMotionEvent& em);
134  void mouseEventDown(SDL_MouseButtonEvent& eb);
135  void mouseEventUp(SDL_MouseButtonEvent& eb);
136  void keyEvent(SDL_Keysym& ks);
137  void keyEvent(char c);
138  void multiGestureEvent(SDL_MultiGestureEvent & e);
139 
140  bool is_multithreaded{true};
141 
142  bool call_idle_func{false};
143 
144  // Hand off events to the SdlWindow. Intended to be called by the main SDL
145  // thread in MainThread::MainLoop().
146  void queueEvents(std::vector<SDL_Event> events)
147  {
148  {
149  std::lock_guard<std::mutex> evt_guard{event_mutex};
150  waiting_events.insert(waiting_events.end(), events.begin(), events.end());
151  }
152  if (is_multithreaded)
153  {
154  events_available.notify_all();
155  }
156  }
157 
158  std::string saved_keys;
159 
160  std::condition_variable events_available;
161  std::mutex event_mutex;
162  // The window-specific events collected by the main event thread.
163  std::deque<SDL_Event> waiting_events;
164 public:
165  SdlWindow();
166  ~SdlWindow();
167 
168  // Initializes SDL2 and starts the main loop. Should be called from the main
169  // thread only.
170  static void StartSDL(bool server_mode);
171 
174  bool createWindow(const char * title, int x, int y, int w, int h,
175  bool legacyGlOnly);
176 
178  void mainLoop();
179  void mainIter();
180 
181  // Called by worker threads in GLVisCommand::signal()
182  void signalLoop();
183 
184  void setOnIdle(IdleDelegate func) { onIdle = func; }
185  void setOnExpose(Delegate func) { onExpose = func; }
186  void setOnReshape(WindowDelegate func) { onReshape = func; }
187 
188  void setOnKeyDown(int key, Delegate func)
189  {
190  onKeyDown[key] = [func](GLenum) { func(); };
191  }
192  void setOnKeyDown(int key, KeyDelegate func) { onKeyDown[key] = func; }
193 
194  void setOnMouseDown(int btn, MouseDelegate func) { onMouseDown[btn] = func; }
195  void setOnMouseUp(int btn, MouseDelegate func) { onMouseUp[btn] = func; }
196  void setOnMouseMove(int btn, MouseDelegate func) { onMouseMove[btn] = func; }
197 
198  void setTouchPinchCallback(TouchDelegate cb) { onTouchPinch = cb; }
199  void setTouchRotateCallback(TouchDelegate cb) { onTouchRotate = cb; }
200 
201  void clearEvents()
202  {
203  onIdle = nullptr;
204  onExpose = nullptr;
205  onReshape = nullptr;
206  onKeyDown.clear();
207  onMouseUp.clear();
208  onMouseDown.clear();
209  onMouseMove.clear();
210  }
211 
212  void callKeyDown(SDL_Keycode k, Uint16 mod=0)
213  {
214  if (onKeyDown[k])
215  {
216  onKeyDown[k](mod);
217  }
218  }
219 
220  void getWindowSize(int& w, int& h);
221  void getGLDrawSize(int& w, int& h);
222  void getDpi(int& wdpi, int& hdpi);
224  bool isHighDpi() const { return high_dpi; }
225 
226  gl3::MeshRenderer& getRenderer() { return *renderer.get(); }
227  void setWindowTitle(std::string& title);
228  void setWindowTitle(const char* title);
229  void setWindowSize(int w, int h);
230  void setWindowPos(int x, int y);
231 
232  void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE);
233  void signalExpose() { wnd_state = RenderState::ExposePending; }
234  void signalSwap() { wnd_state = RenderState::SwapPending; }
235  void signalQuit() { running = false; }
236 
238  std::string getSavedKeys() const { return saved_keys; }
239 
241  void screenshot(std::string filename, bool convert = false)
242  {
243  takeScreenshot = true;
244  screenshot_file = filename;
245  screenshot_convert = convert;
246  // Queue up an expose, so Screenshot() can pull image from the back
247  // buffer
248  signalExpose();
249  }
250 
251  void swapBuffer();
252 
253  operator bool() { return handle.isInitialized(); }
254  bool isWindowInitialized() { return handle.isInitialized(); }
256  bool isGlInitialized();
257 
258  bool isSwapPending() { return wnd_state == RenderState::SwapPending; }
259  bool isExposePending() { return wnd_state == RenderState::ExposePending; }
260 
261 #ifdef __EMSCRIPTEN__
262  std::string getCanvasId() const { return canvas_id_; }
263  void setCanvasId(std::string canvas_id) { canvas_id_ = canvas_id; }
264 #endif
265 };
266 
267 #endif
void signalQuit()
Definition: sdl.hpp:235
void signalExpose()
Definition: sdl.hpp:233
void signalKeyDown(SDL_Keycode k, SDL_Keymod m=KMOD_NONE)
Definition: sdl.cpp:630
void signalSwap()
Definition: sdl.hpp:234
void(* WindowDelegate)(int, int)
Definition: sdl.hpp:35
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
bool isExposePending()
Definition: sdl.hpp:259
void mainLoop()
Runs the window loop.
Definition: sdl.cpp:483
gl3::MeshRenderer & getRenderer()
Definition: sdl.hpp:226
~SdlWindow()
Definition: sdl.cpp:185
SDL_Keymod keymod
Definition: sdl.hpp:29
void setTouchRotateCallback(TouchDelegate cb)
Definition: sdl.hpp:199
void setWindowTitle(std::string &title)
Definition: sdl.cpp:601
void signalLoop()
Definition: sdl.cpp:524
void setOnKeyDown(int key, KeyDelegate func)
Definition: sdl.hpp:192
void setWindowPos(int x, int y)
Definition: sdl.cpp:618
void setOnReshape(WindowDelegate func)
Definition: sdl.hpp:186
void swapBuffer()
Definition: sdl.cpp:649
bool isWindowInitialized()
Definition: sdl.hpp:254
GLint mouse_y
Definition: sdl.hpp:28
void screenshot(std::string filename, bool convert=false)
Queues a screenshot to be taken.
Definition: sdl.hpp:241
void getWindowSize(int &w, int &h)
Definition: sdl.cpp:534
void setOnMouseDown(int btn, MouseDelegate func)
Definition: sdl.hpp:194
void setCanvasId(std::string canvas_id)
Definition: sdl.hpp:263
void(* MouseDelegate)(EventInfo *)
Definition: sdl.hpp:33
void callKeyDown(SDL_Keycode k, Uint16 mod=0)
Definition: sdl.hpp:212
static void StartSDL(bool server_mode)
Definition: sdl.cpp:83
void setOnMouseMove(int btn, MouseDelegate func)
Definition: sdl.hpp:196
std::string getSavedKeys() const
Returns the keyboard events that have been logged by the window.
Definition: sdl.hpp:238
void clearEvents()
Definition: sdl.hpp:201
void setTouchPinchCallback(TouchDelegate cb)
Definition: sdl.hpp:198
bool isHighDpi() const
This property is set by createWindow().
Definition: sdl.hpp:224
bool(* IdleDelegate)()
Definition: sdl.hpp:37
void setOnExpose(Delegate func)
Definition: sdl.hpp:185
GLint mouse_x
Definition: sdl.hpp:27
void(* Delegate)()
Definition: sdl.hpp:36
bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly)
Definition: sdl.cpp:90
void(* TouchDelegate)(SDL_MultiGestureEvent &)
Definition: sdl.hpp:32
void setOnIdle(IdleDelegate func)
Definition: sdl.hpp:184
std::function< void(GLenum)> KeyDelegate
Definition: sdl.hpp:34
void setOnKeyDown(int key, Delegate func)
Definition: sdl.hpp:188
SdlWindow()
Definition: sdl.cpp:81
bool isSwapPending()
Definition: sdl.hpp:258
void getDpi(int &wdpi, int &hdpi)
Definition: sdl.cpp:568
std::string getCanvasId() const
Definition: sdl.hpp:262
void mainIter()
Definition: sdl.cpp:362
void setOnMouseUp(int btn, MouseDelegate func)
Definition: sdl.hpp:195
void setWindowSize(int w, int h)
Definition: sdl.cpp:611