wayland: Add basic hidpi support

Advertise the scale factor on the output and transform pointer and damage
events as well as input and opaque regions for clients
that scale up by themselves i.e use set_buffer_scale.

We do not scale any 'legacy' apps yet.

https://bugzilla.gnome.org/show_bug.cgi?id=728902
This commit is contained in:
Adel Gadllah 2014-04-26 10:27:34 +02:00
parent 5d310e06ba
commit 31c925c602
5 changed files with 111 additions and 7 deletions

View File

@ -73,6 +73,7 @@ struct _MetaOutput
int width_mm;
int height_mm;
CoglSubpixelOrder subpixel_order;
int scale;
MetaMonitorMode *preferred_mode;
MetaMonitorMode **modes;

View File

@ -29,6 +29,8 @@
#include "meta-wayland-private.h"
#include "meta-monitor-manager.h"
#include <string.h>
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);

View File

@ -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

View File

@ -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);

View File

@ -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;