3#include <SDL2/SDL_syswm.h>
5#include <SDL2/SDL_vulkan.h>
18#include "common/config.h"
20#ifdef EVENGINE_ANDROID
21#include "android/android.h"
28#if defined(EVENGINE_WINDOWS)
33#include "macosx/macosx.h"
43Uint32 messageBoxFlag(
const std::string&
type) {
44 if (
type ==
"error")
return SDL_MESSAGEBOX_ERROR;
45 if (
type ==
"warning")
return SDL_MESSAGEBOX_WARNING;
46 return SDL_MESSAGEBOX_INFORMATION;
52 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
53 throw Exception(
"Could not initialize SDL video subsystem (%s)", SDL_GetError());
71 f.minwidth = std::max(
f.minwidth, (uint16_t)1);
72 f.minheight = std::max(
f.minheight, (uint16_t)1);
75 f.display =
static_cast<uint8_t
>(std::min(std::max(
int(
f.display), 0),
getDisplayCount() - 1));
76 if (
f.width == 0 ||
f.height == 0) {
77 SDL_DisplayMode mode = {};
78 SDL_GetDesktopDisplayMode(
f.display, &mode);
84#ifndef EVENGINE_WEBGPU
85 sdlflags = SDL_WINDOW_VULKAN;
94#ifdef EVENGINE_ANDROID
95 bool fullscreen =
f.fullscreen;
98 f.desktop_mode =
true;
103 sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
105 sdlflags |= SDL_WINDOW_FULLSCREEN;
106 SDL_DisplayMode mode = {0, width, height, 0,
nullptr};
109 if (SDL_GetClosestDisplayMode(
f.display, &mode, &mode) ==
nullptr) {
113 if (SDL_GetDisplayMode(
f.display, 0, &mode) < 0)
return false;
121 if (
f.resizable) sdlflags |= SDL_WINDOW_RESIZABLE;
122 if (
f.borderless) sdlflags |= SDL_WINDOW_BORDERLESS;
123 if (
f.high_dpi) sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
124 if (
f.always_on_top) sdlflags |= SDL_WINDOW_ALWAYS_ON_TOP;
129 if (
f.use_position) {
131 SDL_Rect displaybounds = {};
132 SDL_GetDisplayBounds(
f.display, &displaybounds);
133 x += displaybounds.x;
134 y += displaybounds.y;
137 x =
y = SDL_WINDOWPOS_CENTERED_DISPLAY(
f.display);
139 x =
y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(
f.display);
146 if (!createWindowAndContext(
x,
y,
f.width,
f.height, sdlflags,
f.msaa,
f.stencil,
f.depth))
return false;
150 if (!iconRgba.empty())
setIconRGBA(iconRgba.data(), iconWidth, iconHeight);
156 SDL_SetWindowMinimumSize(window,
f.minwidth,
f.minheight);
158 if (
f.use_position ||
f.centered) SDL_SetWindowPosition(window,
x,
y);
160 SDL_RaiseWindow(window);
164#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS)
169 SDL_GetWindowSize(window, &lw, &lh);
170 if (lw > 0 && lh > 0) {
171 f.width =
static_cast<uint16_t
>(lw);
172 f.height =
static_cast<uint16_t
>(lh);
181#ifdef EVENGINE_WEBGPU
184 SDL_GetWindowSize(window, &pw, &ph);
186 SDL_Vulkan_GetDrawableSize(window, &pw, &ph);
188 if (pw <= 0 || ph <= 0) {
196 surfaceHost->initWithWindow(window);
198 surfaceHost->setViewportSize(
f.width,
f.height, pw, ph);
207 return setFullscreenInternal(fullscreen,
true);
211 return setFullscreenInternal(fullscreen,
false);
214bool Window::setFullscreenInternal(
bool fullscreen,
bool desktop_mode) {
215 if (!window)
return false;
228 sdlflags = SDL_WINDOW_FULLSCREEN_DESKTOP;
230 sdlflags = SDL_WINDOW_FULLSCREEN;
232 SDL_DisplayMode mode = {};
233 mode.w = windowWidth;
234 mode.h = windowHeight;
236 SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
237 SDL_SetWindowDisplayMode(window, &mode);
241#ifdef EVENGINE_ANDROID
242 android::setImmersive(fullscreen);
247 SDL_ShowWindow(window);
248 SDL_RaiseWindow(window);
251 int rc = SDL_SetWindowFullscreen(window, sdlflags);
255 rc = SDL_SetWindowFullscreen(window, sdlflags);
263 if (!fullscreen) SDL_SetWindowMinimumSize(window, settings.
minwidth, settings.
minheight);
275 if (window) SDL_SetWindowTitle(window, title.c_str());
282 int count = SDL_GetNumVideoDisplays();
283 if (display < 0 || display >= count) display = 0;
284 SDL_Rect bounds = {};
285 SDL_GetDisplayBounds(display, &bounds);
286 SDL_SetWindowPosition(window, bounds.x +
x, bounds.y +
y);
289 settings.
display =
static_cast<uint8_t
>(display);
301 SDL_GetWindowPosition(window, &wx, &wy);
302 display = SDL_GetWindowDisplayIndex(window);
303 if (display < 0) display = 0;
304 SDL_Rect bounds = {};
305 SDL_GetDisplayBounds(display, &bounds);
315 return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) != 0;
318 return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0;
321 return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0;
324 return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_FOCUS) != 0;
327 return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0
328 && (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) == 0;
332 settings.
vsync =
static_cast<uint8_t
>(vsync < 0 ? 0 : (vsync > 255 ? 255 : vsync));
342 if (!window || windowWidth <= 0)
return 1.0;
343 return double(pixelWidth) / double(windowWidth);
347 if (!window)
return 1.0;
353 if (!
x || !
y)
return;
360 if (!
x || !
y)
return;
362 if (
s == 0.0)
return;
368 if (!
x || !
y)
return;
370 if (
s == 0.0)
return;
376 if (!
x || !
y)
return;
386 return s == 0.0 ?
x :
x /
s;
400 int n = SDL_GetNumVideoDisplays();
401 return n < 0 ? 0 :
n;
406 const char*
n = SDL_GetDisplayName(display);
407 return n ? std::string(
n) : std::string();
412 switch (SDL_GetDisplayOrientation(display)) {
413 case SDL_ORIENTATION_LANDSCAPE:
return "landscape";
414 case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
return "landscapeFlipped";
415 case SDL_ORIENTATION_PORTRAIT:
return "portrait";
416 case SDL_ORIENTATION_PORTRAIT_FLIPPED:
return "portraitFlipped";
417 default:
return "unknown";
422 std::vector<WindowSize> out;
424 int modes = SDL_GetNumDisplayModes(display);
425 for (
int i = 0; i < modes; ++i) {
426 SDL_DisplayMode mode = {};
427 if (SDL_GetDisplayMode(display, i, &mode) == 0) {
431 if (std::find(out.begin(), out.end(),
s) == out.end()) out.push_back(
s);
440 SDL_DisplayMode mode = {};
441 if (SDL_GetDesktopDisplayMode(display, &mode) == 0) {
448 const std::string&
type,
bool attachToWindow) {
449 SDL_Window*
parent = (attachToWindow && window) ? window :
nullptr;
450 return SDL_ShowSimpleMessageBox(messageBoxFlag(
type),
title.c_str(), message.c_str(),
parent) == 0;
454 if (
data.buttons.empty())
return -1;
455 std::vector<SDL_MessageBoxButtonData> buttons;
456 buttons.reserve(
data.buttons.size());
457 for (
size_t i = 0; i <
data.buttons.size(); ++i) {
459 if (
int(i) ==
data.enterButtonIndex) flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
460 if (
int(i) ==
data.escapeButtonIndex) flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
461 buttons.push_back({flags, int(i),
data.buttons[i].c_str()});
463 SDL_MessageBoxData sdl = {};
464 sdl.flags = messageBoxFlag(
data.type);
465 sdl.window = (
data.attachToWindow && window) ? window :
nullptr;
466 sdl.title =
data.title.c_str();
467 sdl.message =
data.message.c_str();
468 sdl.numbuttons = int(buttons.size());
469 sdl.buttons = buttons.data();
470 sdl.colorScheme =
nullptr;
472 if (SDL_ShowMessageBox(&sdl, &buttonid) < 0)
return -1;
477#ifdef EVENGINE_MACOSX
478 eve::macosx::requestAttention(continuous);
481#if SDL_VERSION_ATLEAST(2, 0, 16)
482 SDL_FlashWindow(window, continuous ? SDL_FLASH_UNTIL_FOCUSED : SDL_FLASH_BRIEFLY);
488 if (!rgba ||
width <= 0 ||
height <= 0)
return false;
490#if SDL_BYTEORDER == SDL_BIG_ENDIAN
491 const Uint32 rmask = 0xFF000000;
492 const Uint32 gmask = 0x00FF0000;
493 const Uint32 bmask = 0x0000FF00;
494 const Uint32 amask = 0x000000FF;
496 const Uint32 rmask = 0x000000FF;
497 const Uint32 gmask = 0x0000FF00;
498 const Uint32 bmask = 0x00FF0000;
499 const Uint32 amask = 0xFF000000;
502 SDL_Surface* surface =
503 SDL_CreateRGBSurfaceFrom(
const_cast<uint8_t*
>(rgba),
width,
height, 32,
width * 4, rmask, gmask, bmask, amask);
505 throw Exception(
"Could not create window icon surface: %s", SDL_GetError());
509 SDL_SetWindowIcon(window, surface);
511 SDL_FreeSurface(surface);
513 iconRgba.assign(rgba, rgba +
static_cast<size_t>(
width) *
height * 4);
517#ifdef EVENGINE_MACOSX
525bool Window::createWindowAndContext(
int x,
int y,
int w,
int h, Uint32 windowflags,
int msaa,
bool stencil,
int depth) {
526#ifdef EVENGINE_MACOSX
533 const std::string bundledVulkanDir = eve::macosx::bootstrapBundledVulkan();
534 if (!bundledVulkanDir.empty()) {
535 const std::string loader = bundledVulkanDir +
"/libvulkan.1.dylib";
536 if (SDL_Vulkan_LoadLibrary(loader.c_str()) < 0)
537 std::fprintf(stderr,
"eve: failed to load bundled Vulkan loader %s (%s)\n",
538 loader.c_str(), SDL_GetError());
542 window = SDL_CreateWindow(title.c_str(),
x,
y,
w,
h, windowflags);
545 throw Exception(
"Window Create Failed: %s", SDL_GetError());
560 SDL_DestroyWindow(window);
565 SDL_FlushEvent(SDL_WINDOWEVENT);
568 pixelWidth = pixelHeight = 0;
569 windowWidth = windowHeight = 0;
574 settings = newsettings;
576 SDL_GetWindowSize(window, &windowWidth, &windowHeight);
577#ifdef EVENGINE_WEBGPU
578 SDL_GetWindowSize(window, &pixelWidth, &pixelHeight);
580 SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight);
583 if (updateGraphicsViewport && window) {
585 surfaceHost->setViewportSize(windowWidth, windowHeight, pixelWidth, pixelHeight);
bool hasMouseFocus() const override
True when the window has mouse focus.
bool setFullscreenExclusive(bool fullscreen) override
Switches between exclusive fullscreen and windowed mode.
bool isMaximized() const override
bool isVisible() const override
void setVSync(int vsync) override
Enables/disables vertical sync (0 = off, 1 = on).
int getVSync() const override
int showMessageBoxData(const MessageBoxData &data) override
Shows a configurable message box; returns the pressed button index.
int getHeight() const override
void setSize(int width, int height) override
Requests a new logical window size.
double getDPIScale() const override
Current UI/logical DPI scale (1.0 on non-retina displays).
int getPixelHeight() const override
std::string getDisplayOrientation(int display) const override
Display orientation string (e.g. "landscape").
bool hasFocus() const override
True when the window has keyboard focus.
double fromPixels(double x) const override
int getWidth() const override
bool isMinimized() const override
bool setWindowSettings(WindowSettings settings) override
Applies a full WindowSettings struct (recreates the window when needed).
void setWindowTitle(const std::string &title) override
Sets the window title shown in the OS title bar.
void fromPixelsXY(double px, double py, double &wx, double &wy) const override
void requestAttention(bool continuous) override
Requests OS attention (flashing taskbar); continuous repeats until focused.
bool showMessageBox(const std::string &title, const std::string &message, const std::string &type, bool attachToWindow) override
Shows a modal message box ("info", "warning", or "error").
std::vector< WindowSize > getFullscreenSizes(int display) const override
Fullscreen sizes supported by a display.
void updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport)
Used by event backend on resize to refresh drawable size / viewport.
void windowToDPICoords(double *x, double *y) const override
Converts window logical coordinates to UI-DPI coordinates.
double getNativeDPIScale() const override
Native DPI scale reported by the OS for this window.
void pixelToWindowCoords(double *x, double *y) const override
Converts framebuffer pixel coordinates to window logical coordinates.
int getPixelWidth() const override
Framebuffer (pixel) dimensions of the window.
bool isOpen() const override
double toPixels(double x) const override
Scales a logical length to UI pixels (and back).
int getDisplayCount() const override
Number of displays attached to the system.
void setPosition(int x, int y, int display) override
Moves the window to a position in logical units on the given display.
bool setFullscreenDesktop(bool fullscreen) override
Switches between desktop fullscreen and windowed mode.
void toPixelsXY(double wx, double wy, double &px, double &py) const override
Vector variants of the coordinate conversions above.
bool setIconRGBA(const uint8_t *rgba, int width, int height) override
Sets the window icon from tightly packed RGBA8 pixels (w*h*4 bytes).
std::string getDisplayName(int display) const override
Human-readable display name for a display index.
void getPosition(int &x, int &y, int &display) override
Reads the current window position and display index.
void DPIToWindowCoords(double *x, double *y) const override
Converts UI-DPI coordinates to window logical coordinates.
void windowToPixelCoords(double *x, double *y) const override
Converts window logical coordinates to framebuffer pixel coordinates.
WindowSettings getWindowSettings() override
void getDesktopDimensions(int display, int &width, int &height) const override
Desktop resolution of a display.
void close() override
Closes and destroys the underlying window.
void minimize() override
Minimizes / maximizes / restores the window.
const std::string & getWindowTitle() const override
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
Display settings for window creation/resize. width/height == 0 selects the desktop display mode size.