From 63d6a9fe6500baa7137a972443789a7c2c283ad2 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Tue, 23 Jun 2020 10:05:48 +0800 Subject: [PATCH 21/98] backend-drm: Support selecting monitors Using these environments: WESTON_DRM_PRIMARY: Specify primary connector. WESTON_DRM_SINGLE_HEAD: Force using single connector. WESTON_DRM_HEAD_FALLBACK: Fallback to any available connector if none matched. WESTON_DRM_HEAD_FALLBACK_ALL: Fallback to all available connector if none matched. WESTON_DRM_PREFER_EXTERNAL_DUAL: Prefer external connectors, and also enable internal ones. WESTON_DRM_PREFER_EXTERNAL: Prefer external connectors, and disable internal ones if any matched. WESTON_DRM_HEAD_MODE: default(match primary->internal->external) primary(match primary only) internal(match primary->internal) external(match primary->external) external-dual(match primary->external->internal) Signed-off-by: Jeffy Chen --- desktop-shell/shell.c | 84 +++++-- frontend/main.c | 22 +- include/libweston/config-parser.h | 1 + include/libweston/libweston.h | 7 + kiosk-shell/kiosk-shell.c | 17 ++ libweston/backend-drm/drm-internal.h | 21 ++ libweston/backend-drm/drm.c | 361 +++++++++++++++++++++++---- libweston/backend-drm/kms.c | 22 +- libweston/backend.h | 2 + libweston/compositor.c | 84 ++++++- 10 files changed, 535 insertions(+), 86 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 38ddb4aa6..333218dd0 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -104,6 +104,7 @@ struct shell_surface { struct weston_surface *wsurface_anim_fade; struct weston_view *wview_anim_fade; int32_t last_width, last_height; + int32_t committed_width, committed_height; struct desktop_shell *shell; @@ -134,7 +135,7 @@ struct shell_surface { bool fullscreen; bool maximized; bool lowered; - } state; + } state, requested; struct { bool is_set; @@ -267,11 +268,12 @@ set_shsurf_size_maximized_or_fullscreen(struct shell_surface *shsurf, { int width = 0; int height = 0; + if (!shsurf->output) + return; + if (fullscreen_requested) { - if (shsurf->output) { - width = shsurf->output->width; - height = shsurf->output->height; - } + width = shsurf->output->width; + height = shsurf->output->height; } else if (max_requested) { /* take the panels into considerations */ get_maximized_size(shsurf, &width, &height); @@ -279,6 +281,10 @@ set_shsurf_size_maximized_or_fullscreen(struct shell_surface *shsurf, /* (0, 0) means we're back from one of the maximized/fullcreen states */ weston_desktop_surface_set_size(shsurf->desktop_surface, width, height); + + if (shsurf->committed_width != width && + shsurf->committed_height != height) + shsurf->output->resizing = true; } static void @@ -1882,7 +1888,8 @@ shell_set_view_fullscreen(struct shell_surface *shsurf) weston_view_move_to_layer(shsurf->view, &shsurf->shell->fullscreen_layer.view_list); - weston_shell_utils_center_on_output(shsurf->view, shsurf->fullscreen_output); + + weston_shell_utils_center_on_output(shsurf->view, shsurf->output); if (!shsurf->fullscreen.black_view) { shsurf->fullscreen.black_view = @@ -2309,6 +2316,9 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, bool was_fullscreen; bool was_maximized; + shsurf->committed_width = surface->width; + shsurf->committed_height = surface->height; + if (!weston_surface_has_content(surface) && weston_surface_is_unmapping(surface) && shsurf->state.fullscreen) { @@ -2427,6 +2437,7 @@ set_fullscreen(struct shell_surface *shsurf, bool fullscreen, struct weston_surface *surface = weston_desktop_surface_get_surface(shsurf->desktop_surface); + shsurf->requested.fullscreen = fullscreen; weston_desktop_surface_set_fullscreen(desktop_surface, fullscreen); if (fullscreen) { /* handle clients launching in fullscreen */ @@ -2591,6 +2602,7 @@ set_maximized(struct shell_surface *shsurf, bool maximized) weston_desktop_surface_set_orientation(shsurf->desktop_surface, WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE); } + shsurf->requested.maximized = maximized; weston_desktop_surface_set_maximized(desktop_surface, maximized); set_shsurf_size_maximized_or_fullscreen(shsurf, maximized, false); } @@ -2786,6 +2798,8 @@ background_committed(struct weston_surface *es, weston_surface_map(es); assert(wl_list_empty(&es->views)); sh_output->background_view = weston_view_create(es); + weston_view_set_output(sh_output->background_view, + sh_output->output); } assert(sh_output->background_view); @@ -2898,6 +2912,8 @@ panel_committed(struct weston_surface *es, weston_view_move_to_layer(sh_output->panel_view, &shell->panel_layer.view_list); + + weston_view_set_output(sh_output->panel_view, output); } assert(sh_output->panel_view); @@ -3822,6 +3838,9 @@ shell_fade_done(struct weston_view_animation *animation, void *data) { struct desktop_shell *shell = data; + if (!shell->fade.curtain) + return; + shell->fade.animation = NULL; switch (shell->fade.type) { case FADE_IN: @@ -3876,6 +3895,7 @@ shell_fade_create_view(struct desktop_shell *shell) x2 = MAX(x2, op->pos.c.x + op->width); y2 = MAX(y2, op->pos.c.y + op->height); } + curtain_params.pos.c.x = x1; curtain_params.pos.c.y = y1; curtain_params.width = x2 - x1; @@ -4507,6 +4527,9 @@ shell_reposition_view_on_output_change(struct weston_view *view) if (!shsurf) return; + if (shsurf->fullscreen_output && shsurf->fullscreen_output->destroying) + shsurf->fullscreen_output = NULL; + if (!visible) { struct weston_coord_global pos; @@ -4520,16 +4543,7 @@ shell_reposition_view_on_output_change(struct weston_view *view) weston_view_set_position(view, pos); } else { weston_view_geometry_dirty(view); - - if (shsurf->state.maximized || - shsurf->state.fullscreen) - return; } - - - shsurf->saved_position_valid = false; - set_maximized(shsurf, false); - set_fullscreen(shsurf, false, NULL); } void @@ -4599,16 +4613,18 @@ static void handle_output_resized_shsurfs(struct desktop_shell *shell) { struct shell_surface *shsurf; + struct shell_output *shell_output; + + wl_list_for_each(shell_output, &shell->output_list, link) + shell_output->output->resizing = false; wl_list_for_each(shsurf, &shell->shsurf_list, link) { struct weston_desktop_surface *dsurface = shsurf->desktop_surface; if (dsurface) { - bool is_maximized = - weston_desktop_surface_get_maximized(dsurface); - bool is_fullscreen = - weston_desktop_surface_get_fullscreen(dsurface); + bool is_maximized = shsurf->requested.maximized; + bool is_fullscreen = shsurf->requested.fullscreen; if (is_maximized || is_fullscreen) { set_shsurf_size_maximized_or_fullscreen(shsurf, @@ -4628,6 +4644,10 @@ handle_output_resized(struct wl_listener *listener, void *data) struct weston_output *output = (struct weston_output *)data; struct shell_output *sh_output = find_shell_output_from_weston_output(shell, output); + /* HACK: The resized signal might be eariler than the created signal */ + if (!sh_output) + return; + handle_output_resized_shsurfs(shell); shell_resize_surface_to_output(shell, sh_output->background_surface, output); @@ -4688,12 +4708,27 @@ handle_output_move_layer(struct desktop_shell *shell, static void handle_output_move(struct wl_listener *listener, void *data) { + struct weston_output *output = data; + struct weston_compositor *compositor = output->compositor; struct desktop_shell *shell; shell = container_of(listener, struct desktop_shell, output_move_listener); - shell_for_each_layer(shell, handle_output_move_layer, data); + if (shell->lock_surface) { + struct weston_coord_surface offset = + weston_coord_surface(0, 0, shell->lock_surface); + shell->lock_surface->committed(shell->lock_surface, offset); + } + + /* Only move normal layers for non-default output */ + if (output != weston_shell_utils_get_default_output(compositor)) { + shell_for_each_layer(shell, handle_output_move_layer, data); + } else { + handle_output_move_layer(shell, &shell->lock_layer, data); + handle_output_move_layer(shell, &shell->background_layer, data); + handle_output_move_layer(shell, &shell->panel_layer, data); + } } static void @@ -4786,8 +4821,11 @@ shell_destroy(struct wl_listener *listener, void *data) shell->fade.animation = NULL; } - if (shell->fade.curtain) - weston_shell_utils_curtain_destroy(shell->fade.curtain); + if (shell->fade.curtain) { + struct weston_curtain *curtain = shell->fade.curtain; + shell->fade.curtain = NULL; + weston_shell_utils_curtain_destroy(curtain); + } if (shell->fade.startup_timer) wl_event_source_remove(shell->fade.startup_timer); @@ -5032,5 +5070,7 @@ wet_shell_init(struct weston_compositor *ec, clock_gettime(CLOCK_MONOTONIC, &shell->startup_time); + ec->block_output_resizing = true; + return 0; } diff --git a/frontend/main.c b/frontend/main.c index 3220629e4..f0d12a48c 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -2766,7 +2766,7 @@ drm_head_prepare_enable(struct wet_compositor *wet, char *output_name = NULL; char *mode = NULL; - section = drm_config_find_controlling_output_section(wet->config, name); + section = head->section; if (section) { /* skip outputs that are explicitly off, or non-desktop and not * explicitly enabled. The backend turns them off automatically. @@ -2796,11 +2796,10 @@ static bool drm_head_should_force_enable(struct wet_compositor *wet, struct weston_head *head) { - const char *name = weston_head_get_name(head); struct weston_config_section *section; bool force; - section = drm_config_find_controlling_output_section(wet->config, name); + section = head->section; if (!section) return false; @@ -2999,6 +2998,21 @@ drm_head_disable(struct weston_head *head) wet_output_destroy(output); } +static bool +drm_head_update_output_section(struct weston_head *head) +{ + struct weston_compositor *compositor = head->compositor; + struct wet_compositor *wet = to_wet_compositor(compositor); + const char *name = weston_head_get_name(head); + + if (head->section) + return true; + + head->section = + drm_config_find_controlling_output_section(wet->config, name); + return !!head->section; +} + static void drm_heads_changed(struct wl_listener *listener, void *arg) { @@ -3016,6 +3030,8 @@ drm_heads_changed(struct wl_listener *listener, void *arg) * output. */ while ((head = wet_backend_iterate_heads(wet, wb, head))) { + drm_head_update_output_section(head); + connected = weston_head_is_connected(head); enabled = weston_head_is_enabled(head); changed = weston_head_is_device_changed(head); diff --git a/include/libweston/config-parser.h b/include/libweston/config-parser.h index 8ed14a3b5..e05a90b5b 100644 --- a/include/libweston/config-parser.h +++ b/include/libweston/config-parser.h @@ -32,6 +32,7 @@ extern "C" { #include #include +#include #define WESTON_CONFIG_FILE_ENV_VAR "WESTON_CONFIG_FILE" diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 77b44baa2..be3cbdaaa 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -516,6 +516,8 @@ struct weston_head { * When a client uses this request, we add the wl_resource we create to * this list. */ struct wl_list cm_output_resource_list; + + struct weston_config_section *section; /**< config section **/ }; enum weston_output_power_state { @@ -711,6 +713,9 @@ struct weston_output { * mirror-of key in [output] section. */ struct weston_output *mirror_of; + + /* Resizing maximized or fullscreen surfaces */ + bool resizing; }; enum weston_pointer_motion_mask { @@ -1633,6 +1638,8 @@ struct weston_compositor { struct wl_signal ask_auth; } output_capture; + bool block_output_resizing; + enum weston_output_flow output_flow; }; diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 099478d9a..440e36e13 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -447,6 +447,10 @@ kiosk_shell_surface_reconfigure_for_output(struct kiosk_shell_surface *shsurf) weston_desktop_surface_set_size(desktop_surface, shsurf->output->width, shsurf->output->height); + + if (shsurf->last_width != shsurf->output->width || + shsurf->last_height != shsurf->output->height) + shsurf->output->resizing = true; } weston_shell_utils_center_on_output(shsurf->view, shsurf->output); @@ -1072,6 +1076,11 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, shsurf->last_width = surface->width; shsurf->last_height = surface->height; + + if (is_fullscreen && shsurf->output && + (shsurf->last_width != shsurf->output->width || + shsurf->last_height != shsurf->output->height)) + shsurf->output->resizing = true; } static void @@ -1337,6 +1346,12 @@ kiosk_shell_handle_output_resized(struct wl_listener *listener, void *data) kiosk_shell_find_shell_output(shell, output); struct weston_view *view; + /* HACK: The resized signal might be eariler than the created signal */ + if (!shoutput) + return; + + output->resizing = false; + kiosk_shell_output_recreate_background(shoutput); wl_list_for_each(view, &shell->normal_layer.view_list.link, @@ -1542,5 +1557,7 @@ wet_shell_init(struct weston_compositor *ec, kiosk_shell_add_bindings(shell); + ec->block_output_resizing = true; + return 0; } diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index d00dc6f3b..f8f3d93cb 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -116,6 +116,8 @@ #define MAX_CLONED_CONNECTORS 4 +/* Min duration between drm outputs update requests, to avoid glith */ +#define DRM_MIN_UPDATE_MS 1000 /** * Represents the values of an enum-type KMS property @@ -188,6 +190,7 @@ struct drm_device { int fd; char *filename; dev_t devnum; + char *syspath; } drm; /* Track the GEM handles if the device does not have a gbm device, which @@ -234,6 +237,10 @@ struct drm_device { struct wl_list link; }; +struct drm_head; +struct drm_backend; +typedef bool (*drm_head_match_t) (struct drm_backend *, struct drm_head *); + struct drm_backend { struct weston_backend base; struct weston_compositor *compositor; @@ -261,6 +268,18 @@ struct drm_backend { bool has_underlay; struct weston_log_scope *debug; + + struct wl_event_source *hotplug_timer; + bool pending_update; + int64_t last_update_ms; + int64_t resize_freeze_ms; + + bool single_head; + bool head_fallback; + bool head_fallback_all; + drm_head_match_t *head_matches; + struct drm_head *primary_head; + struct wl_listener output_create_listener; }; struct drm_mode { @@ -590,6 +609,8 @@ struct drm_output { submit_frame_cb virtual_submit_frame; enum wdrm_content_type content_type; + + bool state_invalid; }; void diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 360a8657a..4a6e30b0c 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -48,6 +48,7 @@ #include +#include #include #include #include @@ -71,6 +72,45 @@ static const char default_seat[] = "seat0"; +static inline bool +drm_head_is_external(struct drm_head *head) +{ + drmModeConnector *conn = head->connector.conn; + switch (conn->connector_type) { + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_eDP: +#ifdef DRM_MODE_CONNECTOR_DSI + case DRM_MODE_CONNECTOR_DSI: +#endif + return false; + default: + return true; + } +}; + +static void +drm_backend_update_outputs(struct drm_backend *b) +{ + struct weston_output *primary; + + if (!b->primary_head) + return; + + primary = b->primary_head->base.output; + if (!primary) + return; + + wl_list_remove(&primary->link); + wl_list_insert(&b->compositor->output_list, &primary->link); + + weston_compositor_reflow_outputs(b->compositor); + weston_compositor_damage_all(b->compositor); + + /* Ensure maximized and fullscreen surfaces resized */ + if (b->compositor->block_output_resizing) + primary->resizing = true; +} + static void drm_backend_create_faked_zpos(struct drm_device *device) { @@ -1089,6 +1129,7 @@ drm_output_apply_mode(struct drm_output *output) * content. */ device->state_invalid = true; + output->state_invalid = true; fb_size.width = output->base.current_mode->width; fb_size.height = output->base.current_mode->height; @@ -1755,6 +1796,7 @@ drm_output_attach_head(struct weston_output *output_base, * will not clear the flag before this output is updated? */ device->state_invalid = true; + output->state_invalid = true; weston_output_schedule_repaint(output_base); @@ -2383,6 +2425,8 @@ drm_output_enable(struct weston_output *base) output->base.switch_mode = drm_output_switch_mode; output->base.set_gamma = drm_output_set_gamma; + output->state_invalid = true; + if (device->atomic_modeset) weston_output_update_capture_info(base, WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, base->current_mode->width, @@ -2776,8 +2820,7 @@ drm_head_create(struct drm_device *device, drmModeConnector *conn, head->backlight = NULL; } - if (conn->connector_type == DRM_MODE_CONNECTOR_LVDS || - conn->connector_type == DRM_MODE_CONNECTOR_eDP) + if (!drm_head_is_external(head)) weston_head_set_internal(&head->base); if (drm_head_read_current_setup(head, device) < 0) { @@ -3176,56 +3219,54 @@ drm_backend_add_connector(struct drm_device *device, drmModeConnector *conn, return ret; } -/** Find all connectors of the fd and create drm_head or drm_writeback objects - * (depending on the type of connector they are) for each of them - * - * These objects are added to the DRM-backend lists of heads and writebacks. - * - * @param device The DRM device structure - * @param drm_device udev device pointer - * @param resources The DRM resources, it is taken with drmModeGetResources - * @return 0 on success, -1 on failure - */ -static int -drm_backend_discover_connectors(struct drm_device *device, - struct udev_device *drm_device, - drmModeRes *resources) +static bool +resources_has_connector(drmModeRes *resources, uint32_t connector_id) { - drmModeConnector *conn; - int i, ret; + for (int i = 0; i < resources->count_connectors; i++) { + if (resources->connectors[i] == connector_id) + return true; + } - device->min_width = resources->min_width; - device->max_width = resources->max_width; - device->min_height = resources->min_height; - device->max_height = resources->max_height; + return false; +} - for (i = 0; i < resources->count_connectors; i++) { - uint32_t connector_id = resources->connectors[i]; +/* based on frontend/main.c#drm_head_prepare_enable() */ +static bool +drm_head_is_available(struct weston_head *head) +{ + struct weston_config_section *section; + char *mode = NULL; - conn = drmModeGetConnector(device->drm.fd, connector_id); - if (!conn) - continue; + section = head->section; + if (!section) + return true; - ret = drm_backend_add_connector(device, conn, drm_device); - if (ret < 0) - drmModeFreeConnector(conn); + /* skip outputs that are explicitly off, or non-desktop and not + * explicitly enabled. + */ + weston_config_section_get_string(section, "mode", &mode, NULL); + if (mode && strcmp(mode, "off") == 0) { + free(mode); + return false; } - return 0; + if (!mode && weston_head_is_non_desktop(head)) + return false; + + free(mode); + return true; } static bool -resources_has_connector(drmModeRes *resources, uint32_t connector_id) +drm_head_match_fallback(struct drm_backend *b, struct drm_head *head) { - for (int i = 0; i < resources->count_connectors; i++) { - if (resources->connectors[i] == connector_id) - return true; - } + if (b->head_fallback_all) + return true; - return false; + return b->head_fallback && !b->primary_head; } -static void +static int drm_backend_update_connectors(struct drm_device *device, struct udev_device *drm_device) { @@ -3235,15 +3276,22 @@ drm_backend_update_connectors(struct drm_device *device, struct weston_head *base, *base_next; struct drm_head *head; struct drm_writeback *writeback, *writeback_next; + drm_head_match_t *match = b->head_matches; + struct timespec now; uint32_t connector_id; int i, ret; resources = drmModeGetResources(device->drm.fd); if (!resources) { weston_log("drmModeGetResources failed\n"); - return; + return -1; } + device->min_width = resources->min_width; + device->max_width = resources->max_width; + device->min_height = resources->min_height; + device->max_height = resources->max_height; + /* collect new connectors that have appeared, e.g. MST */ for (i = 0; i < resources->count_connectors; i++) { connector_id = resources->connectors[i]; @@ -3308,6 +3356,62 @@ drm_backend_update_connectors(struct drm_device *device, } drmModeFreeResources(resources); + + /* Disconnect drm heads */ + wl_list_for_each_safe(base, base_next, + &b->compositor->head_list, compositor_link) { + if (base->backend == &b->base) + weston_head_set_connection_status(base, false); + } + + /* Re-connect matched heads and find primary head */ + b->primary_head = NULL; + while (*match) { + wl_list_for_each_safe(base, base_next, + &b->compositor->head_list, + compositor_link) { + drmModeConnector *conn; + + if (base->backend != &b->base || + !drm_head_is_available(base)) + continue; + + head = to_drm_head(base); + conn = head->connector.conn; + + if (conn->connection != DRM_MODE_CONNECTED || + !(*match)(b, head)) + continue; + + weston_head_set_connection_status(base, true); + + if (!b->primary_head) { + b->primary_head = head; + + /* Done the single-head match */ + if (b->single_head) + goto match_done; + } + } + + /* Done the fallback match */ + if (*match == drm_head_match_fallback) + goto match_done; + + match++; + + /* Try the fallback match */ + if (!match && !b->primary_head) + *match = drm_head_match_fallback; + } +match_done: + + drm_backend_update_outputs(b); + + weston_compositor_read_presentation_clock(b->compositor, &now); + b->last_update_ms = timespec_to_msec(&now); + + return 0; } static enum wdrm_connector_property @@ -3399,6 +3503,50 @@ udev_event_is_conn_prop_change(struct drm_backend *b, return 1; } +static void +udev_hotplug_event(struct drm_device *device, struct udev_device *udev_device) +{ + struct drm_backend *b = device->backend; + struct timespec now; + int64_t now_ms, next_ms; + + weston_compositor_read_presentation_clock(b->compositor, &now); + now_ms = timespec_to_msec(&now); + + /* Already have a pending request */ + if (b->pending_update) + return; + + next_ms = b->last_update_ms + DRM_MIN_UPDATE_MS; + if (next_ms <= now_ms) { + /* Long enough to trigger a new request */ + drm_backend_update_connectors(device, udev_device); + } else { + /* Too close to the last request, schedule a new one */ + b->pending_update = true; + wl_event_source_timer_update(b->hotplug_timer, + next_ms - now_ms); + } +} + +static int +hotplug_timer_handler(void *data) +{ + struct drm_device *device = data; + struct drm_backend *b = device->backend; + struct udev_device *udev_device; + struct udev *udev; + + udev = udev_monitor_get_udev(b->udev_monitor); + udev_device = udev_device_new_from_syspath(udev, device->drm.syspath); + + drm_backend_update_connectors(device, udev_device); + b->pending_update = false; + + udev_device_unref(udev_device); + return 0; +} + static int udev_drm_event(int fd, uint32_t mask, void *data) { @@ -3413,7 +3561,7 @@ udev_drm_event(int fd, uint32_t mask, void *data) if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id)) drm_backend_update_conn_props(b, b->drm, conn_id, prop_id); else - drm_backend_update_connectors(b->drm, event); + udev_hotplug_event(b->drm, event); } wl_list_for_each(device, &b->kms_list, link) { @@ -3440,6 +3588,7 @@ drm_shutdown(struct weston_backend *backend) udev_input_destroy(&b->input); + wl_event_source_remove(b->hotplug_timer); wl_event_source_remove(b->udev_drm_source); wl_event_source_remove(b->drm_source); @@ -3533,6 +3682,11 @@ session_notify(struct wl_listener *listener, void *data) weston_compositor_wake(compositor); weston_compositor_damage_all(compositor); device->state_invalid = true; + + wl_list_for_each(output, &compositor->output_list, link) + if (to_drm_output(output)) + to_drm_output(output)->state_invalid = true; + udev_input_enable(&b->input); } else { weston_log("deactivating session\n"); @@ -3939,7 +4093,7 @@ drm_device_create(struct drm_backend *backend, const char *name) create_sprites(device, res); wl_list_init(&device->writeback_connector_list); - if (drm_backend_discover_connectors(device, udev_device, res) < 0) { + if (drm_backend_update_connectors(device, udev_device) < 0) { weston_log("Failed to create heads for %s\n", device->drm.filename); goto err; } @@ -3977,6 +4131,16 @@ next: free(tokenize); } +static void +output_create_notify(struct wl_listener *listener, void *data) +{ + struct drm_backend *b = container_of(listener, struct drm_backend, + output_create_listener); + + /* NOTE: It might trigger output resized signal too early */ + drm_backend_update_outputs(b); +} + static const struct weston_drm_output_api api = { drm_output_set_mode, drm_output_set_gbm_format, @@ -3985,6 +4149,70 @@ static const struct weston_drm_output_api api = { drm_output_set_content_type, }; +enum drm_head_mode { + DRM_HEAD_MODE_DEFAULT, + DRM_HEAD_MODE_PRIMARY, + DRM_HEAD_MODE_INTERNAL, + DRM_HEAD_MODE_EXTERNAL, + DRM_HEAD_MODE_EXTERNAL_DUAL, +}; + +static bool +drm_head_match_primary(struct drm_backend *b, struct drm_head *head) +{ + const char *buf = getenv("WESTON_DRM_PRIMARY"); + return buf && !strcmp(buf, head->base.name); +} + +static bool +drm_head_match_external(struct drm_backend *b, struct drm_head *head) +{ + return drm_head_is_external(head); +} + +static bool +drm_head_match_internal(struct drm_backend *b, struct drm_head *head) +{ + return !drm_head_is_external(head); +} + +#define DRM_HEAD_MAX_MATCHES 5 +static drm_head_match_t drm_head_matches[][DRM_HEAD_MAX_MATCHES] = { + [DRM_HEAD_MODE_DEFAULT] = { + drm_head_match_primary, + drm_head_match_internal, + drm_head_match_external, + NULL, + }, + [DRM_HEAD_MODE_PRIMARY] = { + drm_head_match_primary, + NULL, + }, + [DRM_HEAD_MODE_INTERNAL] = { + drm_head_match_primary, + drm_head_match_internal, + NULL, + }, + [DRM_HEAD_MODE_EXTERNAL] = { + drm_head_match_primary, + drm_head_match_external, + NULL, + }, + [DRM_HEAD_MODE_EXTERNAL_DUAL] = { + drm_head_match_primary, + drm_head_match_external, + drm_head_match_internal, + NULL, + }, +}; + +static void +drm_late_init(struct weston_backend *backend) { + struct drm_backend *b = container_of(backend, struct drm_backend, base); + + hotplug_timer_handler(b->drm); +} + static struct drm_backend * drm_backend_create(struct weston_compositor *compositor, struct weston_drm_backend_config *config) @@ -3996,7 +4224,9 @@ drm_backend_create(struct weston_compositor *compositor, const char *seat_id = default_seat; const char *session_seat; struct weston_drm_format_array *scanout_formats; + enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT; drmModeRes *res; + char *buf; int ret; session_seat = getenv("XDG_SEAT"); @@ -4012,6 +4242,42 @@ drm_backend_create(struct weston_compositor *compositor, if (b == NULL) return NULL; + buf = getenv("WESTON_DRM_SINGLE_HEAD"); + if (buf && buf[0] == '1') + b->single_head = true; + + buf = getenv("WESTON_DRM_HEAD_FALLBACK"); + if (buf && buf[0] == '1') + b->head_fallback = true; + + buf = getenv("WESTON_DRM_HEAD_FALLBACK_ALL"); + if (buf && buf[0] == '1') + b->head_fallback_all = true; + + buf = getenv("WESTON_DRM_PREFER_EXTERNAL"); + if (buf && buf[0] == '1') { + head_mode = DRM_HEAD_MODE_EXTERNAL; + b->head_fallback = true; + } + + buf = getenv("WESTON_DRM_PREFER_EXTERNAL_DUAL"); + if (buf && buf[0] == '1') + head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; + + buf = getenv("WESTON_DRM_HEAD_MODE"); + if (buf) { + if (!strcmp(buf, "primary")) + head_mode = DRM_HEAD_MODE_PRIMARY; + else if (!strcmp(buf, "internal")) + head_mode = DRM_HEAD_MODE_INTERNAL; + else if (!strcmp(buf, "external")) + head_mode = DRM_HEAD_MODE_EXTERNAL; + else if (!strcmp(buf, "external-dual")) + head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; + } + + b->head_matches = drm_head_matches[head_mode]; + device = zalloc(sizeof *device); if (device == NULL) goto err_backend; @@ -4102,6 +4368,7 @@ drm_backend_create(struct weston_compositor *compositor, goto err_udev_dev; } + b->base.late_init = drm_late_init; b->base.shutdown = drm_shutdown; b->base.destroy = drm_destroy; b->base.repaint_begin = drm_repaint_begin; @@ -4136,10 +4403,6 @@ drm_backend_create(struct weston_compositor *compositor, } wl_list_init(&b->drm->writeback_connector_list); - if (drm_backend_discover_connectors(b->drm, drm_device, res) < 0) { - weston_log("Failed to create heads for %s\n", b->drm->drm.filename); - goto err_udev_input; - } drmModeFreeResources(res); @@ -4175,6 +4438,10 @@ drm_backend_create(struct weston_compositor *compositor, udev_device_unref(drm_device); + b->output_create_listener.notify = output_create_notify; + wl_signal_add(&b->compositor->output_created_signal, + &b->output_create_listener); + weston_compositor_add_debug_binding(compositor, KEY_O, planes_binding, b); weston_compositor_add_debug_binding(compositor, KEY_C, @@ -4231,6 +4498,9 @@ drm_backend_create(struct weston_compositor *compositor, goto err_udev_monitor; } + b->hotplug_timer = + wl_event_loop_add_timer(loop, hotplug_timer_handler, b->drm); + return b; err_udev_monitor: @@ -4238,7 +4508,6 @@ err_udev_monitor: udev_monitor_unref(b->udev_monitor); err_drm_source: wl_event_source_remove(b->drm_source); -err_udev_input: udev_input_destroy(&b->input); err_sprite: destroy_sprites(b->drm); diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index c6d997625..30a95199f 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -860,6 +860,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) scanout_state = drm_output_state_get_existing_plane(state, scanout_plane); + if (!scanout_state || !scanout_state->fb) + return 0; /* The legacy SetCrtc API doesn't allow us to do scaling, and the * legacy PageFlip API doesn't allow us to do clipping either. */ @@ -877,7 +879,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) assert(scanout_state->in_fence_fd == -1); mode = to_drm_mode(output->base.current_mode); - if (device->state_invalid || + if (output->state_invalid || !scanout_plane->state_cur->fb || scanout_plane->state_cur->fb->strides[0] != scanout_state->fb->strides[0]) { @@ -892,6 +894,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) goto err; } + output->state_invalid = false; + if (!output->deprecated_gamma_is_set) drm_output_reset_legacy_gamma(output); } @@ -1231,6 +1235,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state, *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } + if (output->state_invalid) { + drm_debug(b, "\t\t\t[atomic] output state invalid, modeset OK\n"); + *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + } + if (state->dpms == WESTON_DPMS_ON) { ret = drm_mode_ensure_blob(device, current_mode); if (ret != 0) @@ -1562,13 +1571,14 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, } wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, - link) + link) { + struct drm_output *output = output_state->output; drm_output_assign_state(output_state, mode); + output->state_invalid = false; + } device->state_invalid = false; - assert(wl_list_empty(&pending_state->output_list)); - out: drmModeAtomicFree(req); drm_pending_state_free(pending_state); @@ -1673,8 +1683,6 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) device->state_invalid = false; - assert(wl_list_empty(&pending_state->output_list)); - drm_pending_state_free(pending_state); return 0; @@ -1726,8 +1734,6 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state) device->state_invalid = false; - assert(wl_list_empty(&pending_state->output_list)); - drm_pending_state_free(pending_state); return 0; diff --git a/libweston/backend.h b/libweston/backend.h index 05a40f30b..acb3a8f2e 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -42,6 +42,8 @@ struct weston_hdr_metadata_type1; struct weston_backend { struct wl_list link; /**< in weston_compositor::backend_list */ + void (*late_init)(struct weston_backend *backend); + /** Bitfield of supported presentation clocks * * Bit positions correspond to system clock IDs. diff --git a/libweston/compositor.c b/libweston/compositor.c index 419d834a1..8bf6677b4 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -413,6 +413,25 @@ weston_paint_node_destroy(struct weston_paint_node *pnode) free(pnode); } +static struct weston_layer * +get_view_layer(struct weston_view *view); + +static bool +weston_compositor_is_static_layer(struct weston_layer *layer) +{ + if (!layer) + return false; + + switch (layer->position) { + case WESTON_LAYER_POSITION_BACKGROUND: + case WESTON_LAYER_POSITION_UI: + case WESTON_LAYER_POSITION_FADE: + return true; + default: + return false; + } +} + /** Send wl_output events for mode and scale changes * * \param head Send on all resources bound to this head. @@ -1617,6 +1636,22 @@ weston_view_assign_output(struct weston_view *ev) uint32_t new_output_area, area, mask; pixman_box32_t *e; + /* The static views should bind to the specific output */ + if (weston_compositor_is_static_layer(get_view_layer(ev))) { + struct weston_view *view = ev; + + while (view && !(output = view->output)) + view = view->geometry.parent; + + if (output && !output->destroying) + ev->output_mask = 1u << output->id; + else + weston_view_set_output(ev, NULL); + + weston_surface_assign_output(ev->surface); + return; + } + new_output = NULL; new_output_area = 0; mask = 0; @@ -3715,6 +3750,11 @@ weston_output_flush_damage_for_primary_plane(struct weston_output *output, WL_EXPORT void weston_output_schedule_repaint_reset(struct weston_output *output) { + if (output->idle_repaint_source) { + wl_event_source_remove(output->idle_repaint_source); + output->idle_repaint_source = NULL; + } + weston_output_put_back_feedback_list(output); output->repaint_status = REPAINT_NOT_SCHEDULED; TL_POINT(output->compositor, "core_repaint_exit_loop", @@ -3782,10 +3822,28 @@ weston_output_repaint(struct weston_output *output, struct timespec *now) output_accumulate_damage(output); - r = output->repaint(output); + if (output->resizing) { + /* Resize maximized or fullscreen views(not always success) */ + wl_signal_emit(&ec->output_resized_signal, output); + } + + if (output->resizing) { + int64_t refresh_nsec = + millihz_to_nsec(output->current_mode->refresh); + timespec_add_nsec(&output->next_repaint, + &output->next_repaint, refresh_nsec); + r = 1; + } else { + r = output->repaint(output); + } output->repaint_needed = false; - if (r == 0) { + + /* HACK: Retry repaint again */ + if (r > 0) { + weston_output_schedule_repaint(output); + r = 0; + } else if (r == 0) { output->repaint_status = REPAINT_AWAITING_COMPLETION; output->repainted = true; } @@ -3947,7 +4005,7 @@ output_repaint_timer_handler(void *data) struct weston_backend *backend; struct weston_output *output; struct timespec now; - int ret = 0; + int ret = 0, repainted = 0; if (!access(getenv("WESTON_FREEZE_DISPLAY") ? : "", F_OK)) { usleep(DEFAULT_REPAINT_WINDOW * 1000); @@ -3990,8 +4048,10 @@ output_repaint_timer_handler(void *data) ret = weston_output_repaint(output, &now); if (ret) break; + + repainted |= output->repainted; } - if (ret == 0) { + if (ret == 0 && repainted) { if (backend->repaint_flush) backend->repaint_flush(backend); } else { @@ -7308,6 +7368,8 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor) weston_output_set_position(output, output->mirror_of->pos); } + + compositor->view_list_needs_rebuild = true; } /** Transform a region from global to output coordinates @@ -7504,6 +7566,10 @@ weston_compositor_add_output(struct weston_compositor *compositor, weston_view_geometry_dirty_internal(view); compositor->view_list_needs_rebuild = true; + + /* Ensure maximized and fullscreen surfaces resized */ + if (compositor->block_output_resizing) + output->resizing = true; } /** Create a weston_coord_global from a point and a weston_output @@ -9835,6 +9901,10 @@ weston_compositor_backends_loaded(struct weston_compositor *compositor) return -1; } + wl_list_for_each(backend, &compositor->backend_list, link) + if (backend->late_init) + backend->late_init(backend); + return 0; } @@ -9852,10 +9922,10 @@ weston_compositor_backends_loaded(struct weston_compositor *compositor) * * \ingroup compositor */ -WL_EXPORT void + WL_EXPORT void weston_compositor_read_presentation_clock( - struct weston_compositor *compositor, - struct timespec *ts) + struct weston_compositor *compositor, + struct timespec *ts) { int ret; -- 2.20.1