From ae3185c560cd2a08ad2c3c2103ce40d20d6569d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 10 Oct 2015 16:35:06 +0100 Subject: [PATCH] Wayland: Implement HiDPI support Windows now keep track of the monitors they are on, so we can calculate the best scaling factor for them, by using the maximum of each of the monitors. The compositor scales down the buffer automatically when it is on a lower density monitor, instead of the previous way where it was scaling up the buffer on higher density monitors, which makes the application look much better on those ones. --- src/wl_init.c | 3 +- src/wl_monitor.c | 3 ++ src/wl_platform.h | 10 +++++- src/wl_window.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index a27bf6fc9..cac929529 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -381,7 +381,8 @@ static void registryHandleGlobal(void* data, if (strcmp(interface, "wl_compositor") == 0) { _glfw.wl.compositor = - wl_registry_bind(registry, name, &wl_compositor_interface, 1); + wl_registry_bind(registry, name, &wl_compositor_interface, + min(3, version)); } else if (strcmp(interface, "wl_shm") == 0) { diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 967f05e9f..ad78d4ff7 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -97,6 +97,9 @@ static void scale(void* data, struct wl_output* output, int32_t factor) { + struct _GLFWmonitor *monitor = data; + + monitor->wl.scale = factor; } static const struct wl_output_listener output_listener = { diff --git a/src/wl_platform.h b/src/wl_platform.h index 5b8c25d8a..a734ec1e7 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -65,8 +65,16 @@ typedef struct _GLFWwindowWayland struct wl_egl_window* native; struct wl_shell_surface* shell_surface; struct wl_callback* callback; + _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; + + // We need to track the monitors the window spans on to calculate the + // optimal scaling factor. + int scale; + _GLFWmonitor** monitors; + int monitorsCount; + int monitorsSize; } _GLFWwindowWayland; @@ -123,7 +131,7 @@ typedef struct _GLFWmonitorWayland int x; int y; - + int scale; } _GLFWmonitorWayland; diff --git a/src/wl_window.c b/src/wl_window.c index 713cda29e..00ce7135a 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -72,6 +72,79 @@ static const struct wl_shell_surface_listener shellSurfaceListener = { handlePopupDone }; +static void checkScaleChange(_GLFWwindow* window) +{ + int scaledWidth, scaledHeight; + int scale = 1; + int i; + int monitorScale; + + // Get the scale factor from the highest scale monitor. + for (i = 0; i < window->wl.monitorsCount; ++i) + { + monitorScale = window->wl.monitors[i]->wl.scale; + if (scale < monitorScale) + scale = monitorScale; + } + + // Only change the framebuffer size if the scale changed. + if (scale != window->wl.scale) + { + window->wl.scale = scale; + scaledWidth = window->wl.width * scale; + scaledHeight = window->wl.height * scale; + wl_surface_set_buffer_scale(window->wl.surface, scale); + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + } +} + +static void handleEnter(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + + if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) + { + ++window->wl.monitorsSize; + window->wl.monitors = + realloc(window->wl.monitors, + window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + } + + window->wl.monitors[window->wl.monitorsCount++] = monitor; + + checkScaleChange(window); +} + +static void handleLeave(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + GLFWbool found; + int i; + + for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) + { + if (monitor == window->wl.monitors[i]) + found = GLFW_TRUE; + if (found) + window->wl.monitors[i] = window->wl.monitors[i + 1]; + } + window->wl.monitors[--window->wl.monitorsCount] = NULL; + + checkScaleChange(window); +} + +static const struct wl_surface_listener surfaceListener = { + handleEnter, + handleLeave +}; + static GLFWbool createSurface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { @@ -79,6 +152,10 @@ static GLFWbool createSurface(_GLFWwindow* window, if (!window->wl.surface) return GLFW_FALSE; + wl_surface_add_listener(window->wl.surface, + &surfaceListener, + window); + wl_surface_set_user_data(window->wl.surface, window); window->wl.native = wl_egl_window_create(window->wl.surface, @@ -98,6 +175,7 @@ static GLFWbool createSurface(_GLFWwindow* window, window->wl.width = wndconfig->width; window->wl.height = wndconfig->height; + window->wl.scale = 1; return GLFW_TRUE; } @@ -262,6 +340,10 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, window->wl.currentCursor = NULL; + window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); + window->wl.monitorsCount = 0; + window->wl.monitorsSize = 1; + return GLFW_TRUE; } @@ -288,6 +370,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->wl.surface) wl_surface_destroy(window->wl.surface); + + free(window->wl.monitors); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) @@ -322,9 +406,12 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { - wl_egl_window_resize(window->wl.native, width, height, 0, 0); + int scaledWidth = width * window->wl.scale; + int scaledHeight = height * window->wl.scale; window->wl.width = width; window->wl.height = height; + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, @@ -344,6 +431,8 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); + *width *= window->wl.scale; + *height *= window->wl.scale; } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,