diff --git a/src/backends/meta-monitor-manager.h b/src/backends/meta-monitor-manager.h index 56dcb7dbf..b5bc927a5 100644 --- a/src/backends/meta-monitor-manager.h +++ b/src/backends/meta-monitor-manager.h @@ -73,6 +73,7 @@ struct _MetaOutput int width_mm; int height_mm; CoglSubpixelOrder subpixel_order; + int scale; MetaMonitorMode *preferred_mode; MetaMonitorMode **modes; diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c index 8d02cd92c..bef2b46d1 100644 --- a/src/wayland/meta-wayland-outputs.c +++ b/src/wayland/meta-wayland-outputs.c @@ -29,6 +29,8 @@ #include "meta-wayland-private.h" #include "meta-monitor-manager.h" +#include + typedef struct { MetaOutput *output; struct wl_global *global; @@ -38,6 +40,17 @@ typedef struct { GList *resources; } MetaWaylandOutput; +/* The minimum resolution at which we turn on a window-scale of 2 */ +#define HIDPI_LIMIT 192 + +/* The minimum screen height at which we turn on a window-scale of 2; + * below this there just isn't enough vertical real estate for GNOME + * apps to work, and it's better to just be tiny */ +#define HIDPI_MIN_HEIGHT 1200 + +/* From http://en.wikipedia.org/wiki/4K_resolution#Resolutions_of_common_formats */ +#define SMALLEST_4K_WIDTH 3656 + static void output_resource_destroy (struct wl_resource *res) { @@ -47,6 +60,37 @@ output_resource_destroy (struct wl_resource *res) wayland_output->resources = g_list_remove (wayland_output->resources, res); } +/* Based on code from gnome-settings-daemon */ +static int +compute_scale (MetaOutput *output) +{ + int scale = 1; + + /* Scaling makes no sense */ + if (output->crtc->rect.width < HIDPI_MIN_HEIGHT) + goto out; + + /* 4K TV */ + if (output->name != NULL && strstr(output->name, "HDMI") != NULL && + output->crtc->rect.width >= SMALLEST_4K_WIDTH) + goto out; + + if (output->width_mm > 0 && output->height_mm > 0) + { + double dpi_x, dpi_y; + dpi_x = (double)output->crtc->rect.width / (output->width_mm / 25.4); + dpi_y = (double)output->crtc->rect.height / (output->height_mm / 25.4); + /* We don't completely trust these values so both + must be high, and never pick higher ratio than + 2 automatically */ + if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT) + scale = 2; + } + +out: + return scale; +} + static void bind_output (struct wl_client *client, void *data, @@ -97,6 +141,11 @@ bind_output (struct wl_client *client, (int)output->crtc->current_mode->height, (int)output->crtc->current_mode->refresh_rate); + output->scale = compute_scale (output); + wl_resource_post_event (resource, + WL_OUTPUT_SCALE, + output->scale); + if (version >= META_WL_OUTPUT_HAS_DONE) wl_resource_post_event (resource, WL_OUTPUT_DONE); diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 388f92eca..5a3a09cff 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -724,8 +724,8 @@ meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, clutter_actor_transform_stage_point (CLUTTER_ACTOR (surface->surface_actor), pos.x, pos.y, &xf, &yf); - *sx = wl_fixed_from_double (xf); - *sy = wl_fixed_from_double (yf); + *sx = wl_fixed_from_double (xf) / surface->scale; + *sy = wl_fixed_from_double (yf) / surface->scale; } void diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 0c7cfdbcd..7696ff4d7 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -108,6 +108,7 @@ surface_process_damage (MetaWaylandSurface *surface, { int i, n_rectangles; cairo_rectangle_int_t buffer_rect; + int scale = surface->scale; /* Damage without a buffer makes no sense so ignore that, otherwise we would crash */ if (!surface->buffer) @@ -129,7 +130,7 @@ surface_process_damage (MetaWaylandSurface *surface, cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); meta_surface_actor_process_damage (surface->surface_actor, - rect.x, rect.y, rect.width, rect.height); + rect.x * scale, rect.y * scale, rect.width * scale, rect.height * scale); } } @@ -206,6 +207,7 @@ pending_state_init (MetaWaylandPendingState *state) state->buffer = NULL; state->dx = 0; state->dy = 0; + state->scale = 0; state->input_region = NULL; state->opaque_region = NULL; @@ -287,6 +289,36 @@ parent_surface_committed (gpointer data, gpointer user_data) subsurface_parent_surface_committed (data); } +static cairo_region_t* +scale_region (cairo_region_t *region, int scale) +{ + int n_rects, i; + cairo_rectangle_int_t *rects; + cairo_region_t *scaled_region; + + if (scale == 1) + return region; + + n_rects = cairo_region_num_rectangles (region); + + rects = g_malloc (sizeof(cairo_rectangle_int_t) * n_rects); + for (i = 0; i < n_rects; i++) + { + cairo_region_get_rectangle (region, i, &rects[i]); + rects[i].x *= scale; + rects[i].y *= scale; + rects[i].width *= scale; + rects[i].height *= scale; + } + + scaled_region = cairo_region_create_rectangles (rects, n_rects); + + g_free (rects); + cairo_region_destroy (region); + + return scaled_region; +} + static void commit_pending_state (MetaWaylandSurface *surface, MetaWaylandPendingState *pending) @@ -316,13 +348,22 @@ commit_pending_state (MetaWaylandSurface *surface, } } + if (pending->scale > 0) + surface->scale = pending->scale; + if (!cairo_region_is_empty (pending->damage)) surface_process_damage (surface, pending->damage); if (pending->opaque_region) - meta_surface_actor_set_opaque_region (surface->surface_actor, pending->opaque_region); + { + pending->opaque_region = scale_region (pending->opaque_region, surface->scale); + meta_surface_actor_set_opaque_region (surface->surface_actor, pending->opaque_region); + } if (pending->input_region) - meta_surface_actor_set_input_region (surface->surface_actor, pending->input_region); + { + pending->input_region = scale_region (pending->input_region, surface->scale); + meta_surface_actor_set_input_region (surface->surface_actor, pending->input_region); + } if (surface == compositor->seat->pointer.cursor_surface) cursor_surface_commit (surface, pending); @@ -500,8 +541,11 @@ wl_surface_set_buffer_scale (struct wl_client *client, struct wl_resource *resource, int scale) { - if (scale != 1) - g_warning ("TODO: support set_buffer_scale request"); + MetaWaylandSurface *surface = wl_resource_get_user_data (resource); + if (scale > 0) + surface->pending.scale = scale; + else + g_warning ("Trying to set invalid buffer_scale of %d\n", scale); } const struct wl_surface_interface meta_wayland_wl_surface_interface = { @@ -593,6 +637,7 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface); surface->compositor = compositor; + surface->scale = 1; surface->resource = wl_resource_create (client, &wl_surface_interface, MIN (META_WL_SURFACE_VERSION, wl_resource_get_version (compositor_resource)), id); @@ -1684,6 +1729,12 @@ meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, int new_width, int new_height) { + /* new_width and new_height comes from window->rect, + * which is based on the buffer size, not the surface + * size. The configure event requires surface size. */ + new_width /= surface->scale; + new_height /= surface->scale; + if (surface->xdg_surface.resource) xdg_surface_send_configure (surface->xdg_surface.resource, new_width, new_height); diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index fc744e81e..91a0b1057 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -50,6 +50,8 @@ typedef struct int32_t dx; int32_t dy; + int scale; + /* wl_surface.damage */ cairo_region_t *damage; @@ -79,6 +81,7 @@ struct _MetaWaylandSurface MetaWaylandSurfaceExtension wl_shell_surface; MetaWaylandSurfaceExtension gtk_surface; MetaWaylandSurfaceExtension subsurface; + int scale; MetaWaylandBuffer *buffer; struct wl_listener buffer_destroy_listener;