| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sommelier.h" |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <wayland-client.h> |
| |
| #include "aura-shell-client-protocol.h" |
| |
| #define MAX_OUTPUT_SCALE 2 |
| |
| #define INCH_IN_MM 25.4 |
| |
| double sl_output_aura_scale_factor_to_double(int scale_factor) { |
| // Aura scale factor is an enum that for all currently know values |
| // is a scale value multipled by 1000. For example, enum value for |
| // 1.25 scale factor is 1250. |
| return scale_factor / 1000.0; |
| } |
| |
| void sl_output_get_host_output_state(struct sl_host_output* host, |
| int* scale, |
| int* physical_width, |
| int* physical_height, |
| int* width, |
| int* height) { |
| double preferred_scale = |
| sl_output_aura_scale_factor_to_double(host->preferred_scale); |
| double current_scale = |
| sl_output_aura_scale_factor_to_double(host->current_scale); |
| double ideal_scale_factor = 1.0; |
| double scale_factor = host->scale_factor; |
| |
| // Use the scale factor we received from aura shell protocol when available. |
| if (host->ctx->aura_shell) { |
| double device_scale_factor = |
| sl_output_aura_scale_factor_to_double(host->device_scale_factor); |
| |
| ideal_scale_factor = device_scale_factor * preferred_scale; |
| scale_factor = device_scale_factor * current_scale; |
| } |
| |
| // Always use scale=1 and adjust geometry and mode based on ideal |
| // scale factor for Xwayland client. For other clients, pick an optimal |
| // scale and adjust geometry and mode based on it. |
| if (host->ctx->xwayland) { |
| if (scale) |
| *scale = 1; |
| *physical_width = host->physical_width * ideal_scale_factor / scale_factor; |
| *physical_height = |
| host->physical_height * ideal_scale_factor / scale_factor; |
| *width = host->width * host->ctx->scale / scale_factor; |
| *height = host->height * host->ctx->scale / scale_factor; |
| } else { |
| int s = MIN(ceil(scale_factor / host->ctx->scale), MAX_OUTPUT_SCALE); |
| |
| if (scale) |
| *scale = s; |
| *physical_width = host->physical_width; |
| *physical_height = host->physical_height; |
| *width = host->width * host->ctx->scale * s / scale_factor; |
| *height = host->height * host->ctx->scale * s / scale_factor; |
| } |
| |
| if (host->ctx->dpi.size) { |
| int dpi = (*width * INCH_IN_MM) / *physical_width; |
| int adjusted_dpi = *((int*)host->ctx->dpi.data); |
| double mmpd; |
| int* p; |
| |
| wl_array_for_each(p, &host->ctx->dpi) { |
| if (*p > dpi) |
| break; |
| |
| adjusted_dpi = *p; |
| } |
| |
| mmpd = INCH_IN_MM / adjusted_dpi; |
| *physical_width = *width * mmpd + 0.5; |
| *physical_height = *height * mmpd + 0.5; |
| } |
| } |
| |
| void sl_output_send_host_output_state(struct sl_host_output* host) { |
| int scale; |
| int physical_width; |
| int physical_height; |
| int width; |
| int height; |
| |
| sl_output_get_host_output_state(host, &scale, &physical_width, |
| &physical_height, &width, &height); |
| |
| // Use density of internal display for all Xwayland outputs. X11 clients |
| // typically lack support for dynamically changing density so it's |
| // preferred to always use the density of the internal display. |
| if (host->ctx->xwayland) { |
| struct sl_host_output* output; |
| |
| wl_list_for_each(output, &host->ctx->host_outputs, link) { |
| if (output->internal) { |
| int internal_width; |
| int internal_height; |
| |
| sl_output_get_host_output_state(output, NULL, &physical_width, |
| &physical_height, &internal_width, |
| &internal_height); |
| |
| physical_width = (physical_width * width) / internal_width; |
| physical_height = (physical_height * height) / internal_height; |
| break; |
| } |
| } |
| } |
| |
| // X/Y are best left at origin as managed X windows are kept centered on |
| // the root window. The result is that all outputs are overlapping and |
| // pointer events can always be dispatched to the visible region of the |
| // window. |
| wl_output_send_geometry(host->resource, 0, 0, physical_width, physical_height, |
| host->subpixel, host->make, host->model, |
| host->transform); |
| wl_output_send_mode(host->resource, host->flags | WL_OUTPUT_MODE_CURRENT, |
| width, height, host->refresh); |
| if (wl_resource_get_version(host->resource) >= WL_OUTPUT_SCALE_SINCE_VERSION) |
| wl_output_send_scale(host->resource, scale); |
| if (wl_resource_get_version(host->resource) >= WL_OUTPUT_DONE_SINCE_VERSION) |
| wl_output_send_done(host->resource); |
| } |
| |
| static void sl_output_geometry(void* data, |
| struct wl_output* output, |
| int x, |
| int y, |
| int physical_width, |
| int physical_height, |
| int subpixel, |
| const char* make, |
| const char* model, |
| int transform) { |
| struct sl_host_output* host = wl_output_get_user_data(output); |
| |
| host->x = x; |
| host->y = y; |
| host->physical_width = physical_width; |
| host->physical_height = physical_height; |
| host->subpixel = subpixel; |
| free(host->model); |
| host->model = strdup(model); |
| free(host->make); |
| host->make = strdup(make); |
| host->transform = transform; |
| } |
| |
| static void sl_output_mode(void* data, |
| struct wl_output* output, |
| uint32_t flags, |
| int width, |
| int height, |
| int refresh) { |
| struct sl_host_output* host = wl_output_get_user_data(output); |
| |
| host->flags = flags; |
| host->width = width; |
| host->height = height; |
| host->refresh = refresh; |
| } |
| |
| static void sl_output_done(void* data, struct wl_output* output) { |
| struct sl_host_output* host = wl_output_get_user_data(output); |
| |
| // Early out if scale is expected but not yet know. |
| if (host->expecting_scale) |
| return; |
| |
| sl_output_send_host_output_state(host); |
| |
| // Expect scale if aura output exists. |
| if (host->aura_output) |
| host->expecting_scale = 1; |
| } |
| |
| static void sl_output_scale(void* data, |
| struct wl_output* output, |
| int32_t scale_factor) { |
| struct sl_host_output* host = wl_output_get_user_data(output); |
| |
| host->scale_factor = scale_factor; |
| } |
| |
| static const struct wl_output_listener sl_output_listener = { |
| sl_output_geometry, sl_output_mode, sl_output_done, sl_output_scale}; |
| |
| static void sl_aura_output_scale(void* data, |
| struct zaura_output* output, |
| uint32_t flags, |
| uint32_t scale) { |
| struct sl_host_output* host = zaura_output_get_user_data(output); |
| |
| switch (scale) { |
| case ZAURA_OUTPUT_SCALE_FACTOR_0400: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0500: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0550: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0600: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0625: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0650: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0700: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0750: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0800: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0850: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0900: |
| case ZAURA_OUTPUT_SCALE_FACTOR_0950: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1000: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1050: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1100: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1150: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1125: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1200: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1250: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1300: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1400: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1450: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1500: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1600: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1750: |
| case ZAURA_OUTPUT_SCALE_FACTOR_1800: |
| case ZAURA_OUTPUT_SCALE_FACTOR_2000: |
| case ZAURA_OUTPUT_SCALE_FACTOR_2200: |
| case ZAURA_OUTPUT_SCALE_FACTOR_2250: |
| case ZAURA_OUTPUT_SCALE_FACTOR_2500: |
| case ZAURA_OUTPUT_SCALE_FACTOR_2750: |
| case ZAURA_OUTPUT_SCALE_FACTOR_3000: |
| case ZAURA_OUTPUT_SCALE_FACTOR_3500: |
| case ZAURA_OUTPUT_SCALE_FACTOR_4000: |
| case ZAURA_OUTPUT_SCALE_FACTOR_4500: |
| case ZAURA_OUTPUT_SCALE_FACTOR_5000: |
| break; |
| default: |
| fprintf(stderr, "warning: unknown scale factor: %d\n", scale); |
| break; |
| } |
| |
| if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT) |
| host->current_scale = scale; |
| if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED) |
| host->preferred_scale = scale; |
| |
| host->expecting_scale = 0; |
| } |
| |
| static void sl_aura_output_connection(void* data, |
| struct zaura_output* output, |
| uint32_t connection) { |
| struct sl_host_output* host = zaura_output_get_user_data(output); |
| |
| host->internal = connection == ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL; |
| } |
| |
| static void sl_aura_output_device_scale_factor(void* data, |
| struct zaura_output* output, |
| uint32_t device_scale_factor) { |
| struct sl_host_output* host = zaura_output_get_user_data(output); |
| |
| host->device_scale_factor = device_scale_factor; |
| } |
| |
| static const struct zaura_output_listener sl_aura_output_listener = { |
| sl_aura_output_scale, sl_aura_output_connection, |
| sl_aura_output_device_scale_factor}; |
| |
| static void sl_destroy_host_output(struct wl_resource* resource) { |
| struct sl_host_output* host = wl_resource_get_user_data(resource); |
| |
| if (host->aura_output) |
| zaura_output_destroy(host->aura_output); |
| if (wl_output_get_version(host->proxy) >= WL_OUTPUT_RELEASE_SINCE_VERSION) { |
| wl_output_release(host->proxy); |
| } else { |
| wl_output_destroy(host->proxy); |
| } |
| wl_resource_set_user_data(resource, NULL); |
| wl_list_remove(&host->link); |
| free(host->make); |
| free(host->model); |
| free(host); |
| } |
| |
| static void sl_bind_host_output(struct wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| struct sl_output* output = (struct sl_output*)data; |
| struct sl_context* ctx = output->ctx; |
| struct sl_host_output* host; |
| |
| host = malloc(sizeof(*host)); |
| assert(host); |
| host->ctx = ctx; |
| host->resource = wl_resource_create(client, &wl_output_interface, |
| MIN(version, output->version), id); |
| wl_resource_set_implementation(host->resource, NULL, host, |
| sl_destroy_host_output); |
| host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display), |
| output->id, &wl_output_interface, |
| wl_resource_get_version(host->resource)); |
| wl_output_set_user_data(host->proxy, host); |
| wl_output_add_listener(host->proxy, &sl_output_listener, host); |
| host->aura_output = NULL; |
| // We assume that first output is internal by default. |
| host->internal = wl_list_empty(&ctx->host_outputs); |
| host->x = 0; |
| host->y = 0; |
| host->physical_width = 0; |
| host->physical_height = 0; |
| host->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
| host->make = strdup("unknown"); |
| host->model = strdup("unknown"); |
| host->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
| host->flags = 0; |
| host->width = 1024; |
| host->height = 768; |
| host->refresh = 60000; |
| host->scale_factor = 1; |
| host->current_scale = 1000; |
| host->preferred_scale = 1000; |
| host->device_scale_factor = 1000; |
| host->expecting_scale = 0; |
| wl_list_insert(ctx->host_outputs.prev, &host->link); |
| if (ctx->aura_shell) { |
| host->expecting_scale = 1; |
| host->internal = 0; |
| host->aura_output = |
| zaura_shell_get_aura_output(ctx->aura_shell->internal, host->proxy); |
| zaura_output_set_user_data(host->aura_output, host); |
| zaura_output_add_listener(host->aura_output, &sl_aura_output_listener, |
| host); |
| } |
| } |
| |
| struct sl_global* sl_output_global_create(struct sl_output* output) { |
| return sl_global_create(output->ctx, &wl_output_interface, output->version, |
| output, sl_bind_host_output); |
| } |