GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
sdl_main.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 <future>
13 #include <thread>
14 #include <climits>
15 
16 #include "sdl_main.hpp"
17 #include "sdl_helper.hpp"
18 #include "logo.hpp"
19 
20 #ifdef SDL_VIDEO_DRIVER_COCOA
21 #include "sdl_mac.hpp"
22 #endif
23 
24 #ifdef GLVIS_DEBUG
25 #define PRINT_DEBUG(s) std::cerr << s
26 #else
27 #define PRINT_DEBUG(s) {}
28 #endif
29 
30 extern int GetMultisample();
31 extern bool wndUseHiDPI;
32 
33 struct SdlMainThread::CreateWindowCmd
34 {
35  SdlWindow* wnd;
36  std::string title;
37  int x, y, w, h;
38  bool legacy_gl_only;
39  promise<Handle> out_handle;
40 };
41 
42 struct SdlMainThread::SdlCtrlCommand
43 {
44  SdlCmdType type {SdlCmdType::None};
45 
46  const Handle* handle {nullptr};
47  CreateWindowCmd* cmd_create;
48  Handle cmd_delete;
49  string cmd_title;
50  pair<int, int> cmd_set_size;
51  pair<int, int> cmd_set_position;
52  // Promise object for signaling completion of the command on the main thread
53  promise<void> finished;
54 };
55 
57 {
58  SDL_version sdl_ver;
59  SDL_GetVersion(&sdl_ver);
60  PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor
61  << "." << (int)sdl_ver.patch << std::endl);
62 
63 #ifdef SDL_VIDEO_DRIVER_COCOA
64  if (SDL_VERSIONNUM(sdl_ver.major, sdl_ver.minor, sdl_ver.patch)
65  < SDL_VERSIONNUM(2, 0, 14))
66  {
67  std::cerr << "Warning: your current version of SDL ("
68  << (int)sdl_ver.major << "." << (int)sdl_ver.minor << "."
69  << (int)sdl_ver.patch
70  << ") may be unsupported in a future version of GLVis on macOS."
71  << std::endl;
72  std::cerr << "If possible, upgrade to SDL version 2.0.14 or newer."
73  << std::endl;
74  }
75 #endif
76 
77  if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
78  {
79  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0)
80  {
81  std::cerr << "FATAL: Failed to initialize SDL: " << SDL_GetError() << endl;
82  return;
83  }
84  SDL_EnableScreenSaver();
85  }
86 
87  sdl_init = true;
88 #ifdef __EMSCRIPTEN__
90 #endif
91 }
92 
94 {
95  SDL_Quit();
96 }
97 
98 void SdlMainThread::MainLoop(bool server)
99 {
100  if (!sdl_init) { return; }
101 
102  this->server_mode = server;
103  if (server_mode)
104  {
105  // Create a hidden window for dock icon and suppressing SDL_QUIT on last
106  // vis window closing
107  int flags = SDL_WINDOW_HIDDEN
108  | SDL_WINDOW_BORDERLESS
109  | SDL_WINDOW_ALLOW_HIGHDPI;
110  SDL_Window* bg_wnd_handle = SDL_CreateWindow("GLVis",
111  SDL_WINDOWPOS_CENTERED,
112  SDL_WINDOWPOS_CENTERED,
113  1,
114  1,
115  flags);
116  setWindowIcon(bg_wnd_handle);
117  bg_wnd.reset(bg_wnd_handle);
118  }
119  while (1)
120  {
121  // Process all pending window commands
122  vector<SdlCtrlCommand> pending_cmds;
123  {
124  lock_guard<mutex> cmd_lock{window_cmd_mtx};
125  pending_cmds = std::move(window_cmds);
126  }
127 
128  for (SdlCtrlCommand& cmd : pending_cmds)
129  {
130  handleWindowCmdImpl(cmd);
131  }
132  // Check if all windows have been closed, and we're not in server mode.
133  // Exiting via shortcut 'q' won't generate an SDL_QUIT event that can be
134  // handled in DispatchSDLEvents().
135  if (num_windows == 0 && !server_mode)
136  {
137  terminating = true;
138  }
139 
141 
142  // At this point, we're ready to start cleaning up
143  if (terminating)
144  {
145  // Signal worker threads of all remaining open windows to terminate
146  for (auto wnds : hwnd_to_window)
147  {
148  SDL_Event sdl_quit;
149  sdl_quit.type = SDL_QUIT;
150  wnds.second->queueEvents({sdl_quit});
151  }
152  while (num_windows > 0)
153  {
154  // Process pending destroy window commands
155  vector<SdlCtrlCommand> pending_destroys;
156  {
157  lock_guard<mutex> cmd_lock{window_cmd_mtx};
158  pending_destroys = std::move(window_cmds);
159  }
160  for (SdlCtrlCommand& cmd : pending_destroys)
161  {
162  if (cmd.type == SdlCmdType::Delete)
163  {
164  handleWindowCmdImpl(cmd);
165  }
166  }
167  }
168  // Exit main loop
169  break;
170  }
171 
172  // Wait for the next event (without consuming CPU cycles, if possible)
173  // See also: SdlWindow::signalLoop()
174  if (platform)
175  {
176  platform->WaitEvent();
177  }
178  else
179  {
180  if (!SDL_PollEvent(nullptr))
181  {
182  std::this_thread::sleep_for(std::chrono::milliseconds(8));
183  }
184  }
185  }
186 }
187 
189 {
190  SDL_Event e;
191  while (SDL_PollEvent(&e))
192  {
193  unsigned int destWindow = UINT_MAX;
194  bool sendToAll = false;
195  switch (e.type)
196  {
197  case SDL_QUIT:
198  terminating = true;
199  break;
200  case SDL_WINDOWEVENT:
201  destWindow = getWindowID(e.window);
202  if (server_mode && (destWindow == SDL_GetWindowID(bg_wnd.get())))
203  {
204  handleBackgroundWindowEvent(e.window);
205  }
206  break;
207  case SDL_FINGERDOWN:
208  fingers.insert(e.tfinger.fingerId);
209  if (fingers.size() >= 2) { disable_mouse = true; }
210  break;
211  case SDL_FINGERUP:
212  fingers.erase(e.tfinger.fingerId);
213  if (fingers.size() < 2) { disable_mouse = false; }
214  break;
215  case SDL_MULTIGESTURE:
216  // No window ID is provided with multigesture events. We'll
217  // have to use focus status within the windows themselves in
218  // order to resolve the correct target.
219  sendToAll = true;
220  break;
221  case SDL_KEYDOWN:
222  case SDL_KEYUP:
223  destWindow = getWindowID(e.key);
224  break;
225  case SDL_TEXTINPUT:
226  destWindow = getWindowID(e.text);
227  break;
228  case SDL_MOUSEMOTION:
229  if (!disable_mouse) { destWindow = getWindowID(e.motion); }
230  break;
231  case SDL_MOUSEBUTTONDOWN:
232  case SDL_MOUSEBUTTONUP:
233  destWindow = getWindowID(e.button);
234  break;
235  }
236  if (sendToAll == true)
237  {
238  for (auto wnds : hwnd_to_window)
239  {
240  int windowId = wnds.first;
241  wnd_events[windowId].emplace_back(e);
242  }
243  }
244  else if (destWindow != UINT_MAX)
245  {
246  wnd_events[destWindow].emplace_back(e);
247  }
248  }
249  // Send events to window worker threads
250  for (auto wnds : hwnd_to_window)
251  {
252  int windowId = wnds.first;
253  SdlWindow* wnd = wnds.second;
254  if (!wnd_events[windowId].empty())
255  {
256  wnd->queueEvents(std::move(wnd_events[windowId]));
257  }
258  }
259 }
260 
261 void SdlMainThread::handleBackgroundWindowEvent(SDL_WindowEvent e)
262 {
263  // Background window should always stay hidden.
264  if (e.event == SDL_WINDOWEVENT_SHOWN)
265  {
266  SDL_HideWindow(bg_wnd.get());
267  }
268 }
269 
270 SdlMainThread::Handle SdlMainThread::GetHandle(SdlWindow* wnd,
271  const std::string& title,
272  int x, int y, int w, int h, bool legacyGlOnly)
273 {
274  if (!sdl_init)
275  {
276  return Handle{};
277  }
278 
279  CreateWindowCmd cmd_create = { wnd, title, x, y, w, h, legacyGlOnly, {} };
280  future<Handle> res_handle = cmd_create.out_handle.get_future();
281 
282  SdlCtrlCommand main_thread_cmd;
283  main_thread_cmd.type = SdlCmdType::Create;
284  main_thread_cmd.cmd_create = &cmd_create;
285 
286  queueWindowEvent(std::move(main_thread_cmd));
287 
288  Handle out_hnd = res_handle.get();
289  if (out_hnd.isInitialized())
290  {
291  // Make the OpenGL context current on the worker thread.
292  // Since SDL calls aren't guaranteed to be thread-safe, we guard
293  // the call to SDL_GL_MakeCurrent.
294  lock_guard<mutex> ctx_lock{gl_ctx_mtx};
295 #ifdef SDL_VIDEO_DRIVER_COCOA
296  // TODO: Temporary workaround - after merge, everyone should update to
297  // latest SDL
298  SdlCocoaPlatform* mac_platform
299  = dynamic_cast<SdlCocoaPlatform*>(platform.get());
300  if (mac_platform && mac_platform->UseThreadWorkaround())
301  {
302  int wnd_id = SDL_GetWindowID(out_hnd.hwnd);
303  mac_platform->SetCurrentContext(wnd_id);
304  }
305  else
306  {
307  SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx);
308  }
309 #else
310  SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx);
311 #endif
312  }
313  return out_hnd;
314 }
315 
316 void SdlMainThread::DeleteHandle(Handle to_delete)
317 {
318  if (to_delete.isInitialized())
319  {
320  {
321  lock_guard<mutex> ctx_lock{gl_ctx_mtx};
322  // Unbinding the context before deletion seems to resolve some issues
323  // with thread-local storage on Wayland/EGL.
324  SDL_GL_MakeCurrent(to_delete.hwnd, nullptr);
325  }
326  SdlCtrlCommand main_thread_cmd;
327  main_thread_cmd.type = SdlCmdType::Delete;
328  main_thread_cmd.cmd_delete = std::move(to_delete);
329 
330  queueWindowEvent(std::move(main_thread_cmd));
331  }
332 }
333 
334 void SdlMainThread::SetWindowTitle(const Handle& handle, std::string title)
335 {
336  if (handle.isInitialized())
337  {
338  SdlCtrlCommand main_thread_cmd;
339  main_thread_cmd.type = SdlCmdType::SetTitle;
340  main_thread_cmd.handle = &handle;
341  main_thread_cmd.cmd_title = std::move(title);
342 
343  queueWindowEvent(std::move(main_thread_cmd));
344  }
345 }
346 
347 void SdlMainThread::SetWindowSize(const Handle& handle, int w, int h)
348 {
349  if (handle.isInitialized())
350  {
351  SdlCtrlCommand main_thread_cmd;
352  main_thread_cmd.type = SdlCmdType::SetSize;
353  main_thread_cmd.handle = &handle;
354  main_thread_cmd.cmd_set_size = {w, h};
355 
356  queueWindowEvent(std::move(main_thread_cmd), true);
357  }
358 }
359 
360 void SdlMainThread::SetWindowPosition(const Handle& handle, int x, int y)
361 {
362  if (handle.isInitialized())
363  {
364  SdlCtrlCommand main_thread_cmd;
365  main_thread_cmd.type = SdlCmdType::SetPosition;
366  main_thread_cmd.handle = &handle;
367  main_thread_cmd.cmd_set_position = {x, y};
368 
369  queueWindowEvent(std::move(main_thread_cmd), true);
370  }
371 }
372 
373 void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd, bool sync)
374 {
375  future<void> wait_complete;
376  if (sdl_multithread)
377  {
378  if (sync)
379  {
380  wait_complete = cmd.finished.get_future();
381  }
382  // queue up our event
383  {
384  lock_guard<mutex> req_lock{window_cmd_mtx};
385  window_cmds.emplace_back(std::move(cmd));
386  }
387  // wake up the main thread to handle our event
388  SendEvent();
389 
390  if (sync) { wait_complete.get(); }
391  }
392  else
393  {
394  // call the underlying SDL command immediately
395  handleWindowCmdImpl(cmd);
396  }
397 }
398 
399 void SdlMainThread::handleWindowCmdImpl(SdlCtrlCommand& cmd)
400 {
401  switch (cmd.type)
402  {
403  case SdlCmdType::Create:
404  createWindowImpl(*cmd.cmd_create);
405  break;
406  case SdlCmdType::Delete:
407  if (cmd.cmd_delete.isInitialized())
408  {
409  Handle to_delete = std::move(cmd.cmd_delete);
410  if (platform)
411  {
412  platform->UnregisterWindow(to_delete.hwnd);
413  }
414  int wnd_id = SDL_GetWindowID(to_delete.hwnd);
415  hwnd_to_window.erase(wnd_id);
416  wnd_events.erase(wnd_id);
417  num_windows--;
418  }
419  break;
421  SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str());
422  break;
423  case SdlCmdType::SetSize:
424  SDL_SetWindowSize(cmd.handle->hwnd,
425  cmd.cmd_set_size.first,
426  cmd.cmd_set_size.second);
427  break;
429  SDL_SetWindowPosition(cmd.handle->hwnd,
430  cmd.cmd_set_position.first,
431  cmd.cmd_set_position.second + title_height_offset);
432  break;
433  default:
434  cerr << "Error in main thread: unknown window control command.\n";
435  break;
436  }
437  // Signal completion of the command, in case worker thread is waiting.
438  cmd.finished.set_value();
439 }
440 
441 void SdlMainThread::setWindowIcon(SDL_Window* hwnd)
442 {
443 #ifdef GLVIS_USE_LOGO
444  const int PixelStride = 4;
445  int stride = (int) sqrt(logo_rgba_len / PixelStride);
446  if (unsigned(stride * stride * PixelStride) != logo_rgba_len)
447  {
448  cerr << "Unable to set window logo: icon size not square" << endl;
449  }
450  else
451  {
452  SDL_Surface* iconSurf =
453  SDL_CreateRGBSurfaceFrom(logo_rgba,
454  stride, stride, // height, width
455  8 * PixelStride, // depth
456  stride * PixelStride, // pitch
457  0x000000FF,
458  0x0000FF00,
459  0x00FF0000,
460  0xFF000000);
461  if (iconSurf)
462  {
463  SDL_SetWindowIcon(hwnd, iconSurf);
464  SDL_FreeSurface(iconSurf);
465  }
466  else
467  {
468  PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl);
469  }
470  }
471 #endif // GLVIS_USE_LOGO
472 }
473 
474 void SdlMainThread::probeGLContextSupport(bool legacyGlOnly)
475 {
476  Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
477 #ifndef __EMSCRIPTEN__
478  if (!legacyGlOnly)
479  {
480  // Try and probe for a core/compatibility context. Needed for Mac OS X,
481  // which will only support OpenGL 2.1 if you don't create a core context.
482  PRINT_DEBUG("Testing if OpenGL core profile window can be created..." << flush);
483  // Try to create core profile context first
484  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
485  {
486  Handle testCore("",
487  SDL_WINDOWPOS_UNDEFINED,
488  SDL_WINDOWPOS_UNDEFINED,
489  100, 100, win_flags_hidden);
490  if (testCore.isInitialized())
491  {
492  PRINT_DEBUG("success!" << endl);
493  return;
494  }
495  }
496 
497  PRINT_DEBUG("failed." << endl);
498  PRINT_DEBUG("Testing if OpenGL compatibility profile window can be created..."
499  << flush);
500  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
501  SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
502  {
503  Handle testCompat("",
504  SDL_WINDOWPOS_UNDEFINED,
505  SDL_WINDOWPOS_UNDEFINED,
506  100, 100, win_flags_hidden);
507  if (testCompat.isInitialized())
508  {
509  PRINT_DEBUG("success!" << endl);
510  return;
511  }
512  }
513  PRINT_DEBUG("failed." << endl);
514  }
515  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
516  if (GetMultisample() > 0)
517  {
518  SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1);
519  SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, GetMultisample());
520  PRINT_DEBUG("Testing OpenGL with default profile and antialiasing..." << endl);
521  {
522  Handle testMsaa("",
523  SDL_WINDOWPOS_UNDEFINED,
524  SDL_WINDOWPOS_UNDEFINED,
525  100, 100, win_flags_hidden);
526  if (testMsaa.isInitialized())
527  {
528  PRINT_DEBUG("success!" << endl);
529  return;
530  }
531  }
532  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
533  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
534  PRINT_DEBUG("failed." << endl);
535  }
536  PRINT_DEBUG("Opening OpenGL window with no flags.");
537 #else
538  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
539  SDL_GL_CONTEXT_PROFILE_ES);
540  PRINT_DEBUG("Testing for WebGL 2.0 context availability..." << flush);
541  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
542  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
543  {
544  Handle testWebGl2("",
545  SDL_WINDOWPOS_UNDEFINED,
546  SDL_WINDOWPOS_UNDEFINED,
547  100, 100, win_flags_hidden);
548  if (testWebGl2.isInitialized())
549  {
550  PRINT_DEBUG("success!" << endl);
551  return;
552  }
553  }
554  PRINT_DEBUG("failed." << endl);
555  PRINT_DEBUG("Testing for WebGL 1.0 context availability..." << flush);
556  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
557  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
558  // Note that WebGL 2 support controllable antialiasing, while WebGL 1 only
559  // supports requesting multisampling at context creation time. The browser
560  // is free to ignore this flag.
561  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
562  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, GetMultisample());
563  {
564  Handle testWebGl("",
565  SDL_WINDOWPOS_UNDEFINED,
566  SDL_WINDOWPOS_UNDEFINED,
567  100, 100, win_flags_hidden);
568  if (testWebGl.isInitialized())
569  {
570  PRINT_DEBUG("success!" << endl);
571  return;
572  }
573  }
574 #endif
575 }
576 
577 const int default_dpi = 72;
578 void SdlMainThread::getDpi(const Handle& handle, int& w, int& h)
579 {
580  w = default_dpi;
581  h = default_dpi;
582  if (!handle.isInitialized())
583  {
584  PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl);
585  return;
586  }
587  int disp = SDL_GetWindowDisplayIndex(handle.hwnd);
588  if (disp < 0)
589  {
590  PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError()
591  << endl);
592  PRINT_DEBUG("returning default dpi of " << default_dpi << endl);
593  return;
594  }
595 
596  float f_w, f_h;
597  if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h))
598  {
599  PRINT_DEBUG("warning: problem getting dpi, setting to " << default_dpi
600  << ": " << SDL_GetError() << endl);
601  }
602  else
603  {
604  PRINT_DEBUG("Screen DPI: w = " << f_w << " ppi, h = " << f_h << " ppi");
605  w = f_w + 0.5f;
606  h = f_h + 0.5f;
607  PRINT_DEBUG(" (" << w << " x " << h << ')' << endl);
608  }
609 }
610 
611 void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd)
612 {
613  Uint32 win_flags = SDL_WINDOW_OPENGL;
614  // Hide window until we adjust its size for high-dpi displays
615  win_flags |= SDL_WINDOW_HIDDEN;
616 #ifndef __EMSCRIPTEN__
617  win_flags |= SDL_WINDOW_RESIZABLE;
618 #endif
619  if (wndUseHiDPI)
620  {
621  win_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
622  }
623  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1);
624  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24);
625  probeGLContextSupport(cmd.legacy_gl_only);
626  Handle new_handle(cmd.title,
627  cmd.x, cmd.y + title_height_offset,
628  cmd.w, cmd.h, win_flags);
629 
630  // at this point, window should be up
631  if (!new_handle.isInitialized())
632  {
633  PRINT_DEBUG("failed." << endl);
634  cerr << "FATAL: window and/or OpenGL context creation failed." << endl;
635  // If not in server mode, this was the only window we were going to open.
636  if (!server_mode) { terminating = true; }
637  return;
638  }
639  else
640  {
641  PRINT_DEBUG("Handle for window created." << endl);
642  }
643 
644 #ifndef __EMSCRIPTEN__
645  SDL_GL_SetSwapInterval(0);
646  glEnable(GL_DEBUG_OUTPUT);
647 #endif
648 
649  // Register window internally in the main thread so it can receive events
650  int wnd_id = SDL_GetWindowID(new_handle.hwnd);
651  hwnd_to_window[wnd_id] = cmd.wnd;
652 
653  {
654  lock_guard<mutex> platform_lk{event_mtx};
655  if (!platform)
656  {
657  platform = SdlNativePlatform::Create(new_handle.hwnd);
658  try_create_platform = true;
659  }
660  event_cv.notify_all();
661  }
662  if (platform)
663  {
664  platform->RegisterWindow(new_handle.hwnd);
665  }
666 
667  setWindowIcon(new_handle.hwnd);
668 
669  if (num_windows == -1)
670  {
671  // Get the window titlebar height after creating the first window.
672  // Window coordinates are based on the client area, so placing a window
673  // at (0, 0) will hide the title bar on Windows.
674  SDL_GetWindowBordersSize(new_handle.hwnd, &title_height_offset, nullptr,
675  nullptr, nullptr);
676  SDL_SetWindowPosition(new_handle.hwnd, cmd.x, cmd.y + title_height_offset);
677  }
678 
679  // Detect if we are using a high-dpi display and resize the window unless it
680  // was already resized by SDL's underlying backend.
681  {
682  SdlWindow* wnd = cmd.wnd;
683  int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi;
684  // SDL_GetWindowSize() -- size in "screen coordinates"
685  SDL_GetWindowSize(new_handle.hwnd, &scr_w, &scr_h);
686  // SDL_GL_GetDrawableSize() -- size in pixels
687  SDL_GL_GetDrawableSize(new_handle.hwnd, &pix_w, &pix_h);
688  wnd->high_dpi = false;
689  wnd->pixel_scale_x = wnd->pixel_scale_y = 1.0f;
690  float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f;
691  // If "screen" and "pixel" sizes are different, assume high-dpi and no
692  // need to scale the window.
693  if (scr_w == pix_w && scr_h == pix_h)
694  {
695  getDpi(new_handle, wdpi, hdpi);
696  if (std::max(wdpi, hdpi) >= SdlWindow::high_dpi_threshold)
697  {
698  wnd->high_dpi = true;
699  wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f;
700  // the following two calls use 'pixel_scale_*'
701  SDL_SetWindowSize(new_handle.hwnd,
702  wnd->pixel_scale_x*cmd.w,
703  wnd->pixel_scale_y*cmd.h);
704  bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(cmd.x) ||
705  SDL_WINDOWPOS_ISCENTERED(cmd.x);
706  bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(cmd.y) ||
707  SDL_WINDOWPOS_ISCENTERED(cmd.y);
708  SDL_SetWindowPosition(new_handle.hwnd,
709  uc_x ? cmd.x : wnd->pixel_scale_x*cmd.x,
710  uc_y ? cmd.y : wnd->pixel_scale_y*cmd.y);
711  }
712  }
713  else
714  {
715  wnd->high_dpi = true;
716  // keep 'pixel_scale_*' = 1, scaling is done inside SDL
717  sdl_pixel_scale_x = float(pix_w)/scr_w;
718  sdl_pixel_scale_y = float(pix_h)/scr_h;
719  }
720  if (wnd->high_dpi)
721  {
722  cout << "High-dpi display detected: using window scaling: "
723  << sdl_pixel_scale_x*wnd->pixel_scale_x << " x "
724  << sdl_pixel_scale_y*wnd->pixel_scale_y << endl;
725  }
726  }
727 
728  // Unset GL context in this thread
729  {
730  lock_guard<mutex> ctx_lock{gl_ctx_mtx};
731 #ifdef SDL_VIDEO_DRIVER_COCOA
732  // TODO: Temporary workaround - after merge, everyone should update to
733  // latest SDL
734  SdlCocoaPlatform* mac_platform
735  = dynamic_cast<SdlCocoaPlatform*>(platform.get());
736  if (mac_platform && mac_platform->UseThreadWorkaround())
737  {
738  mac_platform->ClearCurrentContext(wnd_id);
739  }
740  else
741  {
742  SDL_GL_MakeCurrent(new_handle.hwnd, nullptr);
743  }
744 #else
745  SDL_GL_MakeCurrent(new_handle.hwnd, nullptr);
746 #endif
747  }
748 
749  SDL_ShowWindow(new_handle.hwnd);
750  SDL_RaiseWindow(new_handle.hwnd);
751  if (num_windows == -1)
752  {
753  num_windows = 0;
754  }
755  num_windows++;
756  cmd.out_handle.set_value(std::move(new_handle));
757 }
void ClearCurrentContext(int wnd_id)
Definition: sdl_mac.mm:90
bool UseThreadWorkaround() const
Definition: sdl_mac.mm:66
void SetCurrentContext(int wnd_id)
Definition: sdl_mac.mm:100
thread_local SdlWindow * wnd
Definition: aux_vis.cpp:50
const int default_dpi
Definition: sdl.cpp:88
bool wndUseHiDPI
Definition: aux_vis.cpp:52
void SetWindowTitle(const Handle &handle, std::string title)
Definition: sdl_main.cpp:334
int GetMultisample()
Definition: aux_vis.cpp:1573
Handle GetHandle(SdlWindow *wnd, const std::string &title, int x, int y, int w, int h, bool legacyGlOnly)
Definition: sdl_main.cpp:270
unsigned char logo_rgba[]
void MainLoop(bool server_mode)
Definition: sdl_main.cpp:98
void SetWindowPosition(const Handle &handle, int x, int y)
Definition: sdl_main.cpp:360
void SetSingleThread()
Definition: sdl_main.hpp:30
static std::unique_ptr< SdlNativePlatform > Create(SDL_Window *window)
Definition: sdl_helper.cpp:29
void DeleteHandle(Handle to_delete)
Definition: sdl_main.cpp:316
void DispatchSDLEvents()
Definition: sdl_main.cpp:188
void SetWindowSize(const Handle &handle, int w, int h)
Definition: sdl_main.cpp:347
unsigned int logo_rgba_len