From 06479ba53507b7b8e4b9575fbaf6ec659c58fdc8 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 | 8 ++++- src/wl_monitor.c | 5 +++ src/wl_platform.h | 10 +++++- src/wl_window.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index a27bf6fc..cceef16b 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -36,6 +36,11 @@ #include +static inline int min(int n1, int n2) +{ + return n1 < n2 ? n1 : n2; +} + static void pointerHandleEnter(void* data, struct wl_pointer* pointer, uint32_t serial, @@ -381,7 +386,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 967f05e9..7c58c8f8 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 = { @@ -142,6 +145,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) monitor->wl.modes = calloc(4, sizeof(_GLFWvidmodeWayland)); monitor->wl.modesSize = 4; + monitor->wl.scale = 1; + monitor->wl.output = output; wl_output_add_listener(output, &output_listener, monitor); diff --git a/src/wl_platform.h b/src/wl_platform.h index 5b8c25d8..a734ec1e 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 713cda29..00ce7135 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,