20 #ifdef SDL_VIDEO_DRIVER_COCOA
25 #define PRINT_DEBUG(s) std::cerr << s
27 #define PRINT_DEBUG(s) {}
33 struct SdlMainThread::CreateWindowCmd
39 promise<Handle> out_handle;
42 struct SdlMainThread::SdlCtrlCommand
46 const Handle* handle {
nullptr};
47 CreateWindowCmd* cmd_create;
50 pair<int, int> cmd_set_size;
51 pair<int, int> cmd_set_position;
53 promise<void> finished;
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);
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))
67 std::cerr <<
"Warning: your current version of SDL ("
68 << (int)sdl_ver.major <<
"." << (
int)sdl_ver.minor <<
"."
70 <<
") may be unsupported in a future version of GLVis on macOS."
72 std::cerr <<
"If possible, upgrade to SDL version 2.0.14 or newer."
77 if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
79 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0)
81 std::cerr <<
"FATAL: Failed to initialize SDL: " << SDL_GetError() << endl;
84 SDL_EnableScreenSaver();
100 if (!sdl_init) {
return; }
102 this->server_mode = server;
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,
116 setWindowIcon(bg_wnd_handle);
117 bg_wnd.reset(bg_wnd_handle);
122 vector<SdlCtrlCommand> pending_cmds;
124 lock_guard<mutex> cmd_lock{window_cmd_mtx};
125 pending_cmds = std::move(window_cmds);
128 for (SdlCtrlCommand& cmd : pending_cmds)
130 handleWindowCmdImpl(cmd);
135 if (num_windows == 0 && !server_mode)
146 for (
auto wnds : hwnd_to_window)
149 sdl_quit.type = SDL_QUIT;
150 wnds.second->queueEvents({sdl_quit});
152 while (num_windows > 0)
155 vector<SdlCtrlCommand> pending_destroys;
157 lock_guard<mutex> cmd_lock{window_cmd_mtx};
158 pending_destroys = std::move(window_cmds);
160 for (SdlCtrlCommand& cmd : pending_destroys)
164 handleWindowCmdImpl(cmd);
176 platform->WaitEvent();
180 if (!SDL_PollEvent(
nullptr))
182 std::this_thread::sleep_for(std::chrono::milliseconds(8));
191 while (SDL_PollEvent(&e))
193 unsigned int destWindow = UINT_MAX;
194 bool sendToAll =
false;
200 case SDL_WINDOWEVENT:
201 destWindow = getWindowID(e.window);
202 if (server_mode && (destWindow == SDL_GetWindowID(bg_wnd.get())))
204 handleBackgroundWindowEvent(e.window);
208 fingers.insert(e.tfinger.fingerId);
209 if (fingers.size() >= 2) { disable_mouse =
true; }
212 fingers.erase(e.tfinger.fingerId);
213 if (fingers.size() < 2) { disable_mouse =
false; }
215 case SDL_MULTIGESTURE:
223 destWindow = getWindowID(e.key);
226 destWindow = getWindowID(e.text);
228 case SDL_MOUSEMOTION:
229 if (!disable_mouse) { destWindow = getWindowID(e.motion); }
231 case SDL_MOUSEBUTTONDOWN:
232 case SDL_MOUSEBUTTONUP:
233 destWindow = getWindowID(e.button);
236 if (sendToAll ==
true)
238 for (
auto wnds : hwnd_to_window)
240 int windowId = wnds.first;
241 wnd_events[windowId].emplace_back(e);
244 else if (destWindow != UINT_MAX)
246 wnd_events[destWindow].emplace_back(e);
250 for (
auto wnds : hwnd_to_window)
252 int windowId = wnds.first;
254 if (!wnd_events[windowId].empty())
256 wnd->queueEvents(std::move(wnd_events[windowId]));
261 void SdlMainThread::handleBackgroundWindowEvent(SDL_WindowEvent e)
264 if (e.event == SDL_WINDOWEVENT_SHOWN)
266 SDL_HideWindow(bg_wnd.get());
271 const std::string& title,
272 int x,
int y,
int w,
int h,
bool legacyGlOnly)
279 CreateWindowCmd cmd_create = {
wnd, title, x, y, w, h, legacyGlOnly, {} };
280 future<Handle> res_handle = cmd_create.out_handle.get_future();
282 SdlCtrlCommand main_thread_cmd;
284 main_thread_cmd.cmd_create = &cmd_create;
286 queueWindowEvent(std::move(main_thread_cmd));
288 Handle out_hnd = res_handle.get();
289 if (out_hnd.isInitialized())
294 lock_guard<mutex> ctx_lock{gl_ctx_mtx};
295 #ifdef SDL_VIDEO_DRIVER_COCOA
302 int wnd_id = SDL_GetWindowID(out_hnd.hwnd);
307 SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx);
310 SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx);
318 if (to_delete.isInitialized())
321 lock_guard<mutex> ctx_lock{gl_ctx_mtx};
324 SDL_GL_MakeCurrent(to_delete.hwnd,
nullptr);
326 SdlCtrlCommand main_thread_cmd;
328 main_thread_cmd.cmd_delete = std::move(to_delete);
330 queueWindowEvent(std::move(main_thread_cmd));
336 if (handle.isInitialized())
338 SdlCtrlCommand main_thread_cmd;
340 main_thread_cmd.handle = &handle;
341 main_thread_cmd.cmd_title = std::move(title);
343 queueWindowEvent(std::move(main_thread_cmd));
349 if (handle.isInitialized())
351 SdlCtrlCommand main_thread_cmd;
353 main_thread_cmd.handle = &handle;
354 main_thread_cmd.cmd_set_size = {w, h};
356 queueWindowEvent(std::move(main_thread_cmd),
true);
362 if (handle.isInitialized())
364 SdlCtrlCommand main_thread_cmd;
366 main_thread_cmd.handle = &handle;
367 main_thread_cmd.cmd_set_position = {x, y};
369 queueWindowEvent(std::move(main_thread_cmd),
true);
373 void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd,
bool sync)
375 future<void> wait_complete;
380 wait_complete = cmd.finished.get_future();
384 lock_guard<mutex> req_lock{window_cmd_mtx};
385 window_cmds.emplace_back(std::move(cmd));
390 if (sync) { wait_complete.get(); }
395 handleWindowCmdImpl(cmd);
399 void SdlMainThread::handleWindowCmdImpl(SdlCtrlCommand& cmd)
404 createWindowImpl(*cmd.cmd_create);
407 if (cmd.cmd_delete.isInitialized())
409 Handle to_delete = std::move(cmd.cmd_delete);
412 platform->UnregisterWindow(to_delete.hwnd);
414 int wnd_id = SDL_GetWindowID(to_delete.hwnd);
415 hwnd_to_window.erase(wnd_id);
416 wnd_events.erase(wnd_id);
421 SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str());
424 SDL_SetWindowSize(cmd.handle->hwnd,
425 cmd.cmd_set_size.first,
426 cmd.cmd_set_size.second);
429 SDL_SetWindowPosition(cmd.handle->hwnd,
430 cmd.cmd_set_position.first,
431 cmd.cmd_set_position.second + title_height_offset);
434 cerr <<
"Error in main thread: unknown window control command.\n";
438 cmd.finished.set_value();
441 void SdlMainThread::setWindowIcon(SDL_Window* hwnd)
443 #ifdef GLVIS_USE_LOGO
444 const int PixelStride = 4;
446 if (
unsigned(stride * stride * PixelStride) !=
logo_rgba_len)
448 cerr <<
"Unable to set window logo: icon size not square" << endl;
452 SDL_Surface* iconSurf =
456 stride * PixelStride,
463 SDL_SetWindowIcon(hwnd, iconSurf);
464 SDL_FreeSurface(iconSurf);
468 PRINT_DEBUG(
"Unable to set window logo: " << SDL_GetError() << endl);
471 #endif // GLVIS_USE_LOGO
474 void SdlMainThread::probeGLContextSupport(
bool legacyGlOnly)
476 Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
477 #ifndef __EMSCRIPTEN__
482 PRINT_DEBUG(
"Testing if OpenGL core profile window can be created..." << flush);
484 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
487 SDL_WINDOWPOS_UNDEFINED,
488 SDL_WINDOWPOS_UNDEFINED,
489 100, 100, win_flags_hidden);
490 if (testCore.isInitialized())
492 PRINT_DEBUG(
"success!" << endl);
497 PRINT_DEBUG(
"failed." << endl);
498 PRINT_DEBUG(
"Testing if OpenGL compatibility profile window can be created..."
500 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
501 SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
503 Handle testCompat(
"",
504 SDL_WINDOWPOS_UNDEFINED,
505 SDL_WINDOWPOS_UNDEFINED,
506 100, 100, win_flags_hidden);
507 if (testCompat.isInitialized())
509 PRINT_DEBUG(
"success!" << endl);
513 PRINT_DEBUG(
"failed." << endl);
515 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
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);
523 SDL_WINDOWPOS_UNDEFINED,
524 SDL_WINDOWPOS_UNDEFINED,
525 100, 100, win_flags_hidden);
526 if (testMsaa.isInitialized())
528 PRINT_DEBUG(
"success!" << endl);
532 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
533 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
534 PRINT_DEBUG(
"failed." << endl);
536 PRINT_DEBUG(
"Opening OpenGL window with no flags.");
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);
544 Handle testWebGl2(
"",
545 SDL_WINDOWPOS_UNDEFINED,
546 SDL_WINDOWPOS_UNDEFINED,
547 100, 100, win_flags_hidden);
548 if (testWebGl2.isInitialized())
550 PRINT_DEBUG(
"success!" << endl);
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);
561 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
565 SDL_WINDOWPOS_UNDEFINED,
566 SDL_WINDOWPOS_UNDEFINED,
567 100, 100, win_flags_hidden);
568 if (testWebGl.isInitialized())
570 PRINT_DEBUG(
"success!" << endl);
578 void SdlMainThread::getDpi(
const Handle& handle,
int& w,
int& h)
582 if (!handle.isInitialized())
584 PRINT_DEBUG(
"warning: unable to get dpi: handle is null" << endl);
587 int disp = SDL_GetWindowDisplayIndex(handle.hwnd);
590 PRINT_DEBUG(
"warning: problem getting display index: " << SDL_GetError()
592 PRINT_DEBUG(
"returning default dpi of " << default_dpi << endl);
597 if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h))
599 PRINT_DEBUG(
"warning: problem getting dpi, setting to " << default_dpi
600 <<
": " << SDL_GetError() << endl);
604 PRINT_DEBUG(
"Screen DPI: w = " << f_w <<
" ppi, h = " << f_h <<
" ppi");
607 PRINT_DEBUG(
" (" << w <<
" x " << h <<
')' << endl);
611 void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd)
613 Uint32 win_flags = SDL_WINDOW_OPENGL;
615 win_flags |= SDL_WINDOW_HIDDEN;
616 #ifndef __EMSCRIPTEN__
617 win_flags |= SDL_WINDOW_RESIZABLE;
621 win_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
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);
631 if (!new_handle.isInitialized())
633 PRINT_DEBUG(
"failed." << endl);
634 cerr <<
"FATAL: window and/or OpenGL context creation failed." << endl;
636 if (!server_mode) { terminating =
true; }
641 PRINT_DEBUG(
"Handle for window created." << endl);
644 #ifndef __EMSCRIPTEN__
645 SDL_GL_SetSwapInterval(0);
646 glEnable(GL_DEBUG_OUTPUT);
650 int wnd_id = SDL_GetWindowID(new_handle.hwnd);
651 hwnd_to_window[wnd_id] = cmd.wnd;
654 lock_guard<mutex> platform_lk{event_mtx};
658 try_create_platform =
true;
660 event_cv.notify_all();
664 platform->RegisterWindow(new_handle.hwnd);
667 setWindowIcon(new_handle.hwnd);
669 if (num_windows == -1)
674 SDL_GetWindowBordersSize(new_handle.hwnd, &title_height_offset,
nullptr,
676 SDL_SetWindowPosition(new_handle.hwnd, cmd.x, cmd.y + title_height_offset);
683 int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi;
685 SDL_GetWindowSize(new_handle.hwnd, &scr_w, &scr_h);
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;
693 if (scr_w == pix_w && scr_h == pix_h)
695 getDpi(new_handle, wdpi, hdpi);
696 if (std::max(wdpi, hdpi) >= SdlWindow::high_dpi_threshold)
698 wnd->high_dpi =
true;
699 wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f;
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);
715 wnd->high_dpi =
true;
717 sdl_pixel_scale_x = float(pix_w)/scr_w;
718 sdl_pixel_scale_y = float(pix_h)/scr_h;
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;
730 lock_guard<mutex> ctx_lock{gl_ctx_mtx};
731 #ifdef SDL_VIDEO_DRIVER_COCOA
742 SDL_GL_MakeCurrent(new_handle.hwnd,
nullptr);
745 SDL_GL_MakeCurrent(new_handle.hwnd,
nullptr);
749 SDL_ShowWindow(new_handle.hwnd);
750 SDL_RaiseWindow(new_handle.hwnd);
751 if (num_windows == -1)
756 cmd.out_handle.set_value(std::move(new_handle));
thread_local SdlWindow * wnd
void SetWindowTitle(const Handle &handle, std::string title)
Handle GetHandle(SdlWindow *wnd, const std::string &title, int x, int y, int w, int h, bool legacyGlOnly)
unsigned char logo_rgba[]
void MainLoop(bool server_mode)
void SetWindowPosition(const Handle &handle, int x, int y)
void DeleteHandle(Handle to_delete)
void SetWindowSize(const Handle &handle, int w, int h)
unsigned int logo_rgba_len