From f084d82e5948daa331ff741072ce81d24018feea Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 3 Jul 2020 12:37:37 +0800 Subject: [PATCH 25/98] backend-drm: Support controlling output dynamically Use config file to set output's rotate/down-scale/size/pos/mode/off/ freeze/input/display-rectangle and prefer/primary output. Default config file is "/tmp/.weston_drm.conf", can override with "WESTON_DRM_CONFIG" environment. Supported configs format is "output::", for example: echo "output:DSI-1:state=off" >> /tmp/.weston_drm.conf echo "output:DSI-1:state=on" >> /tmp/.weston_drm.conf echo "output:DSI-1:state=detect" >> /tmp/.weston_drm.conf echo "output:DSI-1:off" >> /tmp/.weston_drm.conf echo "output:eDP-1:freeze" >> /tmp/.weston_drm.conf echo "output:DSI-1:offscreen" >> /tmp/.weston_drm.conf echo "output:all:rotate90" >> /tmp/.weston_drm.conf echo "output:all:rect=<100,20,1636,2068>" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:mode=800x600" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:pos=100,200" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:size=1920x1080" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:prefer" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:primary" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:down-scale=0.5" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:input=*" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:input=" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:input=event6" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:input=goodix*" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:input=goodix-ts" >> /tmp/.weston_drm.conf echo "output:HDMI-A-1:refresh" >> /tmp/.weston_drm.conf Signed-off-by: Jeffy Chen --- desktop-shell/shell.c | 70 +++++- frontend/main.c | 18 ++ include/libweston/libweston.h | 21 ++ libweston/backend-drm/drm-internal.h | 36 +++- libweston/backend-drm/drm.c | 311 ++++++++++++++++++++++++++- libweston/backend-drm/kms.c | 12 +- libweston/backend-drm/modes.c | 16 +- libweston/compositor.c | 88 ++++++-- libweston/libinput-seat.c | 45 ++++ libweston/libweston-internal.h | 4 + libweston/pixman-renderer.c | 29 +++ libweston/renderer-gl/gl-renderer.c | 3 + libweston/shell-utils/shell-utils.c | 17 +- 13 files changed, 627 insertions(+), 43 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 127cf1906..e7dca8fd2 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -48,6 +48,9 @@ #define DEFAULT_NUM_WORKSPACES 1 #define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200 +static void +set_maximized_position(struct desktop_shell *shell, struct shell_surface *shsurf); + struct focus_state { struct desktop_shell *shell; struct weston_seat *seat; @@ -268,22 +271,31 @@ set_shsurf_size_maximized_or_fullscreen(struct shell_surface *shsurf, { int width = 0; int height = 0; - if (!weston_output_valid(shsurf->output)) + if (!weston_output_valid(shsurf->output)) { + struct weston_coord_global pos; + pos.c = weston_coord(0, 0); + weston_view_set_position(shsurf->view, pos); return; + } if (fullscreen_requested) { width = shsurf->output->width; height = shsurf->output->height; + + weston_view_set_position(shsurf->view, shsurf->output->pos); } else if (max_requested) { /* take the panels into considerations */ get_maximized_size(shsurf, &width, &height); + + set_maximized_position(shsurf->shell, 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) + if (shsurf->shell->compositor->view_list_needs_rebuild || + (shsurf->committed_width != width && + shsurf->committed_height != height)) shsurf->output->resizing = true; } @@ -972,6 +984,12 @@ touch_move_grab_motion(struct weston_touch_grab *grab, if (!shsurf || !shsurf->desktop_surface || !move->active) return; + /* Ignore pinned output when grabbing. */ + if (shsurf->view && shsurf->view->pinned_output) { + free(shsurf->view->pinned_output); + shsurf->view->pinned_output = NULL; + } + pos = weston_coord_global_add(grab->touch->grab_pos, move->delta); pos.c = weston_coord_truncate(pos.c); weston_view_set_position(shsurf->view, pos); @@ -1098,6 +1116,12 @@ move_grab_motion(struct weston_pointer_grab *grab, if (!shsurf || !shsurf->desktop_surface) return; + /* Ignore pinned output when grabbing. */ + if (shsurf->view && shsurf->view->pinned_output) { + free(shsurf->view->pinned_output); + shsurf->view->pinned_output = NULL; + } + pos = constrain_position(move); weston_view_set_position(shsurf->view, pos); } @@ -1223,6 +1247,12 @@ tablet_tool_move_grab_motion(struct weston_tablet_tool_grab *grab, if (!shsurf) return; + /* Ignore pinned output when grabbing. */ + if (shsurf->view && shsurf->view->pinned_output) { + free(shsurf->view->pinned_output); + shsurf->view->pinned_output = NULL; + } + pos.c.x += wl_fixed_to_double(move->dx); pos.c.y += wl_fixed_to_double(move->dy); weston_view_set_position(shsurf->view, pos); @@ -1715,8 +1745,6 @@ shell_surface_set_output(struct shell_surface *shsurf, shsurf->output = output; else if (es->output) shsurf->output = es->output; - else - shsurf->output = weston_shell_utils_get_default_output(es->compositor); if (shsurf->output_destroy_listener.notify) { wl_list_remove(&shsurf->output_destroy_listener.link); @@ -4080,6 +4108,7 @@ weston_view_set_initial_position(struct weston_view *view, int32_t range_x, range_y; int32_t x, y; struct weston_output *output, *target_output = NULL; + struct weston_output *preferred_output = NULL; struct weston_seat *seat; pixman_rectangle32_t area; struct weston_coord_global pos; @@ -4106,16 +4135,20 @@ weston_view_set_initial_position(struct weston_view *view, } } - wl_list_for_each(output, &compositor->output_list, link) { + wl_list_for_each_reverse(output, &compositor->output_list, link) { if (!weston_output_valid(output)) continue; - if (weston_output_contains_coord(output, pos)) { + if (weston_output_preferred(output)) + preferred_output = output; + + if (weston_output_contains_coord(output, pos)) target_output = output; - break; - } } + if (preferred_output) + target_output = preferred_output; + if (!target_output) { pos.c = weston_coord(10 + random() % 400, 10 + random() % 400); @@ -4536,6 +4569,12 @@ shell_reposition_view_on_output_change(struct weston_view *view) if (!visible) { struct weston_coord_global pos; + if (ec->pin_output && view->pinned_output) + return; + + if (shsurf->state.fullscreen || shsurf->state.maximized) + return; + first_output = container_of(ec->output_list.next, struct weston_output, link); @@ -4651,6 +4690,12 @@ handle_output_resized(struct wl_listener *listener, void *data) if (!sh_output) return; + 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); + } + handle_output_resized_shsurfs(shell); shell_resize_surface_to_output(shell, sh_output->background_surface, output); @@ -4704,7 +4749,9 @@ handle_output_move_layer(struct desktop_shell *shell, pos = weston_coord_global_add( weston_view_get_pos_offset_global(view), output->move); - weston_view_set_position(view, pos); + if (pixman_region32_contains_point(&output->region, + pos.c.x, pos.c.y, NULL)) + weston_view_set_position(view, pos); } } @@ -4732,6 +4779,9 @@ handle_output_move(struct wl_listener *listener, void *data) handle_output_move_layer(shell, &shell->background_layer, data); handle_output_move_layer(shell, &shell->panel_layer, data); } + + /* HACK: For updating fullscreen/maximized views' position */ + handle_output_resized_shsurfs(shell); } static void diff --git a/frontend/main.c b/frontend/main.c index f0d12a48c..009f0d22b 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -2366,6 +2366,20 @@ drm_backend_output_configure(struct weston_output *output, } free(s); + weston_config_section_get_string(section, "pos", &s, NULL); + if (s) { + if (sscanf(s, "%lf,%lf", &output->pos.c.x, &output->pos.c.y) == 2) + output->fixed_position = true; + free(s); + } + + weston_config_section_get_string(section, "size", &s, NULL); + if (s) { + if (sscanf(s, "%dx%d", &output->width, &output->height) == 2) + output->fixed_size = true; + free(s); + } + if (api->set_mode(output, mode, modeline) < 0) { weston_log("Cannot configure output \"%s\" using weston_drm_output_api.\n", output->name); @@ -4639,6 +4653,10 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) wet.compositor->output_flow = WESTON_OUTPUT_FLOW_SAME_AS; } + buf = getenv("WESTON_OUTPUT_PIN"); + if (buf && buf[0] == '1') + wet.compositor->pin_output = true; + protocol_scope = weston_log_ctx_add_log_scope(log_ctx, "proto", "Wayland protocol dump for all clients.\n", diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index b98ee5022..e9e74ba79 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -34,6 +34,7 @@ extern "C" { #include #include +#include #include #include #include @@ -718,6 +719,11 @@ struct weston_output { bool resizing; bool unavailable; + + bool fixed_position; + bool fixed_size; + + double down_scale; }; #define weston_output_valid(o) \ ((o) && !(o)->destroying && !(o)->unavailable) @@ -1645,6 +1651,8 @@ struct weston_compositor { bool block_output_resizing; enum weston_output_flow output_flow; + + bool pin_output; }; struct weston_solid_buffer_values { @@ -1858,6 +1866,9 @@ struct weston_view { struct weston_output *output; struct wl_listener output_destroy_listener; + /* Pinned to this output. */ + char *pinned_output; + /* * A more complete representation of all outputs this surface is * displayed on. @@ -2862,4 +2873,14 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor); } #endif +static inline bool +weston_output_preferred(struct weston_output *output) { + const char *preferred_output = getenv("WESTON_OUTPUT_PREFERRED"); + + if (!weston_output_valid(output) || !preferred_output) + return false; + + return !strcmp(output->name, preferred_output); +} + #endif diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 0d537db76..477f411c5 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -119,6 +119,9 @@ /* Min duration between drm outputs update requests, to avoid glith */ #define DRM_MIN_UPDATE_MS 1000 +#define WESTON_DRM_CONFIG_FILE "/tmp/.weston_drm.conf" +#define DRM_CONFIG_UPDATE_MS 100 + /** * Represents the values of an enum-type KMS property */ @@ -272,7 +275,6 @@ struct drm_backend { 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; @@ -285,6 +287,9 @@ struct drm_backend { int virtual_height; bool mirror_mode; + + struct wl_event_source *config_timer; + struct stat config_stat; }; struct drm_mode { @@ -518,6 +523,12 @@ struct drm_writeback { struct weston_drm_format_array formats; }; +enum drm_head_state { + DRM_HEAD_DETECT, + DRM_HEAD_OFF, + DRM_HEAD_ON, +}; + struct drm_head { struct weston_head base; struct drm_connector connector; @@ -533,6 +544,8 @@ struct drm_head { void *display_data; /**< EDID or DisplayID blob */ size_t display_data_len; /**< bytes */ + + enum drm_head_state state; }; struct drm_crtc { @@ -628,7 +641,12 @@ struct drm_output { bool is_mirror; + bool freezing; + bool offscreen; + pixman_box32_t plane_bounds; + + uint32_t original_transform; }; void @@ -747,6 +765,13 @@ drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list); void drm_output_print_modes(struct drm_output *output); +struct drm_mode * +drm_output_choose_initial_mode(struct drm_device *device, + struct drm_output *output, + enum weston_drm_backend_output_mode mode, + const char *modeline, + const drmModeModeInfo *current_mode); + int drm_output_set_mode(struct weston_output *base, enum weston_drm_backend_output_mode mode, @@ -964,3 +989,12 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) return NULL; } #endif + +inline static bool +drm_head_is_connected(struct drm_head *head) +{ + if (!head || !head->connector.conn) + return false; + + return head->connector.conn->connection == DRM_MODE_CONNECTED; +} diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index f3dcf4b9a..e75524051 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,8 @@ static const char default_seat[] = "seat0"; +static int config_timer_handler(void *data); + static inline bool drm_head_is_external(struct drm_head *head) { @@ -726,8 +729,8 @@ got_fb: goto out; } - sw = fb->width; - sh = fb->height; + sw = fb->width * output->base.down_scale; + sh = fb->height * output->base.down_scale; dx = output->plane_bounds.x1; dy = output->plane_bounds.y1; @@ -1074,6 +1077,14 @@ drm_output_repaint(struct weston_output *output_base) assert(!output->state_last); + if (output->freezing) { + int64_t refresh_nsec = + millihz_to_nsec(output_base->current_mode->refresh); + timespec_add_nsec(&output_base->next_repaint, + &output_base->next_repaint, refresh_nsec); + return 1; + } + /* If planes have been disabled in the core, we might not have * hit assign_planes at all, so might not have valid output state * here. */ @@ -1082,7 +1093,7 @@ drm_output_repaint(struct weston_output *output_base) state = drm_output_state_duplicate(output->state_cur, pending_state, DRM_OUTPUT_STATE_CLEAR_PLANES); - state->dpms = WESTON_DPMS_ON; + state->dpms = output->offscreen ? WESTON_DPMS_OFF : WESTON_DPMS_ON; cursor_state = drm_output_state_get_existing_plane(state, output->cursor_plane); @@ -2743,6 +2754,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->original_transform = output->base.transform; + output->state_invalid = true; if (device->atomic_modeset) @@ -3690,17 +3703,18 @@ drm_backend_update_connectors(struct drm_device *device, 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)) + if (head->state == DRM_HEAD_OFF || + (head->state != DRM_HEAD_ON && + !drm_head_is_connected(head))) + continue; + + if (!(*match)(b, head)) continue; weston_head_set_connection_status(base, true); @@ -3908,6 +3922,7 @@ drm_shutdown(struct weston_backend *backend) udev_input_destroy(&b->input); + wl_event_source_remove(b->config_timer); wl_event_source_remove(b->hotplug_timer); wl_event_source_remove(b->udev_drm_source); wl_event_source_remove(b->drm_source); @@ -4459,6 +4474,10 @@ output_create_notify(struct wl_listener *listener, void *data) /* NOTE: It might trigger output resized signal too early */ drm_backend_update_outputs(b); + + /* Force reload config */ + memset(&b->config_stat, 0, sizeof(b->config_stat)); + config_timer_handler(b); } static const struct weston_drm_output_api api = { @@ -4469,6 +4488,278 @@ static const struct weston_drm_output_api api = { drm_output_set_content_type, }; +static void +drm_output_rotate(struct drm_output *output, int rotate) +{ + uint32_t transform = output->original_transform; + + /* Hacky way to rotate transform */ + transform = (transform / 4) * 4 + (transform + rotate) % 4; + + if (output->base.transform == transform) + return; + + weston_output_set_transform(&output->base, transform); + weston_output_damage(&output->base); +} + +static void +drm_output_modeset(struct drm_output *output, const char *modeline) +{ + struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_head *head = + to_drm_head(weston_output_get_first_head(&output->base)); + struct drm_mode *mode; + struct timespec now; + + /* Unable to switch mode, let's retry later */ + if (output->page_flip_pending || output->atomic_complete_pending) { + memset(&b->config_stat, 0, sizeof(b->config_stat)); + return; + } + + mode = drm_output_choose_initial_mode(b->drm, output, + WESTON_DRM_BACKEND_OUTPUT_PREFERRED, + modeline, + &head->inherited_mode); + + weston_output_mode_set_native(&output->base, &mode->base, + output->base.current_scale); + weston_output_damage(&output->base); + + mode = to_drm_mode(output->base.current_mode); + + weston_log("Output %s changed to %dx%d@%d for mode(%s)\n", + output->base.name, + mode->mode_info.hdisplay, mode->mode_info.vdisplay, + mode->mode_info.vrefresh, + modeline); + + weston_compositor_read_presentation_clock(b->compositor, &now); + b->last_update_ms = timespec_to_msec(&now); +} + +static void +drm_output_set_size(struct drm_output *output, const int w, const int h) +{ + struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = output->device; + struct weston_mode *mode; + + if (output->base.fixed_size && + output->base.current_mode->width == w && + output->base.current_mode->height == h) + return; + + wl_list_for_each(mode, &output->base.mode_list, link) { + mode->width = w; + mode->height = h; + } + + output->base.fixed_size = true; + + weston_output_set_transform(&output->base, output->base.transform); + + if (b->compositor->renderer->type == WESTON_RENDERER_PIXMAN) { + drm_output_fini_pixman(output); + if (drm_output_init_pixman(output, b) < 0) + weston_log("failed to init output pixman state with " + "new mode\n"); + } else { + drm_output_fini_egl(output); + if (drm_output_init_egl(output, b) < 0) + weston_log("failed to init output egl state with " + "new mode"); + } + + drm_output_print_modes(output); + + if (device->atomic_modeset) + weston_output_update_capture_info(&output->base, + WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, + output->base.current_mode->width, + output->base.current_mode->height, + pixel_format_get_info(output->format->format)); +} + +static void +config_handle_output(struct drm_backend *b, const char *name, + const char *config) +{ + struct weston_output *base_output; + struct weston_head *base_head; + struct drm_output *output; + struct drm_head *head; + bool is_all = !strcmp(name, "all"); + + if (!is_all) { + if (!strcmp(config, "primary")) { + setenv("WESTON_DRM_PRIMARY", name, 1); + hotplug_timer_handler(b->drm); + return; + } else if (!strcmp(config, "prefer")) { + setenv("WESTON_OUTPUT_PREFERRED", name, 1); + return; + } else if (!strncmp(config, "input=", strlen("input="))) { + weston_input_bind_output(b->compositor, + name, + config + strlen("input=")); + return; + } + } + + wl_list_for_each(base_head, &b->compositor->head_list, compositor_link) { + head = to_drm_head(base_head); + if (!head || (!is_all && strcmp(name, base_head->name))) + continue; + + /* Handle forcing state */ + if (!strncmp(config, "state=", strlen("state="))) { + const char *state = config + strlen("state="); + if (!strcmp(state, "on")) { + head->state = DRM_HEAD_ON; + hotplug_timer_handler(b->drm); + } else if (!strcmp(state, "off")) { + head->state = DRM_HEAD_OFF; + hotplug_timer_handler(b->drm); + } else if (!strcmp(state, "detect")) { + head->state = DRM_HEAD_DETECT; + hotplug_timer_handler(b->drm); + } + continue; + } + + base_output = base_head->output; + if (!base_output) + continue; + + output = to_drm_output(base_output); + if (!strncmp(config, "rotate", strlen("rotate"))) { + int rotate = atoi(config + strlen("rotate")) / 90; + drm_output_rotate(output, rotate); + } else if (!strncmp(config, "mode=", strlen("mode="))) { + drm_output_modeset(output, config + strlen("mode=")); + } else if (!strcmp(config, "freeze")) { + output->freezing = true; + } else if (!strcmp(config, "offscreen")) { + output->offscreen = true; + if (!output->is_virtual) + weston_output_power_off(base_output); + } else if (!strcmp(config, "off")) { + output->freezing = true; + output->offscreen = true; + if (!output->is_virtual) + weston_output_power_off(base_output); + } else if (!strcmp(config, "unfreeze") || + !strcmp(config, "on")) { + output->freezing = false; + output->offscreen = false; + if (!output->is_virtual) + weston_output_power_on(base_output); + } else if (!strncmp(config, "down-scale=", + strlen("down-scale="))) { + double down_scale = + atof(config + strlen("down-scale=")); + if (down_scale == base_output->down_scale || + down_scale < 0.125 || down_scale > 1) + continue; + + base_output->down_scale = down_scale; + weston_output_damage(base_output); + } else if (!strncmp(config, "size=", strlen("size="))) { + int w, h; + + if (sscanf(config, "size=%dx%d", &w, &h) != 2) + continue; + + drm_output_set_size(output, w, h); + } else if (!strncmp(config, "pos=", strlen("pos="))) { + struct weston_coord_global pos; + int x, y; + + if (sscanf(config, "pos=%d,%d", &x, &y) != 2) + continue; + + pos.c = weston_coord(x, y); + weston_output_move(base_output, pos); + base_output->fixed_position = true; + + weston_compositor_reflow_outputs(b->compositor); + } else if (!strncmp(config, "rect=", strlen("rect="))) { + int x1, y1, x2, y2, ret; + + ret = sscanf(config, "rect=<%d,%d,%d,%d>", + &x1, &y1, &x2, &y2); + if (ret != 4) + continue; + + output->plane_bounds.x1 = x1; + output->plane_bounds.y1 = y1; + output->plane_bounds.x2 = x2; + output->plane_bounds.y2 = y2; + weston_output_schedule_repaint(base_output); + } else if (!strcmp(config, "refresh")) { + output->state_invalid = true; + weston_output_damage(base_output); + } + } +} + +static int +config_timer_handler(void *data) +{ +#define MAX_CONF_LEN 512 +#define _STR(x) #x +#define STR(x) _STR(x) + + struct drm_backend *b = data; + struct stat st, *old_st = &b->config_stat; + char type[MAX_CONF_LEN], key[MAX_CONF_LEN], value[MAX_CONF_LEN]; + const char *config_file; + FILE *conf_fp; + + wl_event_source_timer_update(b->config_timer, DRM_CONFIG_UPDATE_MS); + + config_file = getenv("WESTON_DRM_CONFIG"); + if (!config_file) + config_file = WESTON_DRM_CONFIG_FILE; + + if (stat(config_file, &st) < 0) + return 0; + + if (st.st_size == old_st->st_size && st.st_ino == old_st->st_ino && + st.st_mtime && st.st_mtime == old_st->st_mtime) { +#ifdef __USE_XOPEN2K8 + if (st.st_mtim.tv_nsec == old_st->st_mtim.tv_nsec) + return 0; +#else + if (st.st_mtimensec == old_st->st_mtimensec) + return 0; +#endif + } + + conf_fp = fopen(config_file, "r"); + if (!conf_fp) + return 0; + + *old_st = st; + + /** + * Parse configs, formated with :: + * For example: "output:all:rotate90" + */ + while (3 == fscanf(conf_fp, + "%" STR(MAX_CONF_LEN) "[^:]:" + "%" STR(MAX_CONF_LEN) "[^:]:" + "%" STR(MAX_CONF_LEN) "[^\n]%*c", type, key, value)) { + if (!strcmp(type, "output")) + config_handle_output(b, key, value); + } + + fclose(conf_fp); + return 0; +} + enum drm_head_mode { DRM_HEAD_MODE_DEFAULT, DRM_HEAD_MODE_PRIMARY, @@ -4531,6 +4822,7 @@ drm_late_init(struct weston_backend *backend) { struct drm_backend *b = container_of(backend, struct drm_backend, base); hotplug_timer_handler(b->drm); + config_timer_handler(b); } static struct drm_backend * @@ -4831,6 +5123,9 @@ drm_backend_create(struct weston_compositor *compositor, b->hotplug_timer = wl_event_loop_add_timer(loop, hotplug_timer_handler, b->drm); + b->config_timer = + wl_event_loop_add_timer(loop, config_timer_handler, b); + return b; err_udev_monitor: diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index f974c904b..2dbe0ee0b 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -860,6 +860,11 @@ drm_output_apply_state_legacy(struct drm_output_state *state) bool scaling; wl_list_for_each(head, &output->base.head_list, base.output_link) { + if (!drm_head_is_connected(head)) { + output->state_invalid = true; + continue; + } + assert(n_conn < MAX_CLONED_CONNECTORS); connectors[n_conn++] = head->connector.connector_id; } @@ -876,7 +881,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) } } - if (state->dpms != WESTON_DPMS_ON) { + if (!n_conn || state->dpms != WESTON_DPMS_ON) { if (output->cursor_plane) { ret = drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0); @@ -1334,6 +1339,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state, /* No need for the DPMS property, since it is implicit in * routing and CRTC activity. */ wl_list_for_each(head, &output->base.head_list, base.output_link) { + if (!drm_head_is_connected(head)) { + output->state_invalid = true; + continue; + } + ret |= connector_add_prop(req, &head->connector, WDRM_CONNECTOR_CRTC_ID, crtc->crtc_id); diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index 4f17d7bb7..c94354030 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -470,15 +470,19 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) if (mode == NULL) return NULL; - mode->base.flags = 0; - mode->base.width = info->hdisplay; - mode->base.height = info->vdisplay; - - if (b->virtual_width && b->virtual_height) { + if (output->base.fixed_size) { + mode->base.width = output->base.width; + mode->base.height = output->base.height; + } else if (b->virtual_width && b->virtual_height) { mode->base.width = b->virtual_width; mode->base.height = b->virtual_height; + } else { + mode->base.width = info->hdisplay; + mode->base.height = info->vdisplay; } + mode->base.flags = 0; + mode->base.refresh = drm_refresh_rate_mHz(info); mode->mode_info = *info; mode->blob_id = 0; @@ -669,7 +673,7 @@ update_head_from_connector(struct drm_head *head) * @param current_mode Mode currently being displayed on this output * @returns A mode from the output's mode list, or NULL if none available */ -static struct drm_mode * +struct drm_mode * drm_output_choose_initial_mode(struct drm_device *device, struct drm_output *output, enum weston_drm_backend_output_mode mode, diff --git a/libweston/compositor.c b/libweston/compositor.c index e7e8f12a1..031391293 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -432,6 +432,24 @@ weston_compositor_is_static_layer(struct weston_layer *layer) } } +static bool +weston_compositor_is_system_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_LOCK: + case WESTON_LAYER_POSITION_CURSOR: + 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. @@ -1547,6 +1565,7 @@ get_view_layer(struct weston_view *view) static void weston_surface_assign_output(struct weston_surface *es) { + struct weston_compositor *ec = es->compositor; struct weston_output *new_output; struct weston_view *view; pixman_region32_t region; @@ -1598,10 +1617,11 @@ weston_surface_assign_output(struct weston_surface *es) continue; } - /* All else being equal, prefer the primary backend */ - if (area == max && new_output && - view->output->backend == es->compositor->primary_backend) { - new_output = view->output; + /* All else being equal, prefer the preferred or primary backend */ + if (area == max && new_output) { + if (weston_output_preferred(view->output) || + view->output->backend == ec->primary_backend) + new_output = view->output; } } pixman_region32_fini(®ion); @@ -1635,21 +1655,24 @@ weston_view_assign_output(struct weston_view *ev) pixman_region32_t region; uint32_t new_output_area, area, mask; pixman_box32_t *e; + struct weston_layer *layer = get_view_layer(ev); /* The static views should bind to the specific output */ - if (weston_compositor_is_static_layer(get_view_layer(ev))) { + if (weston_compositor_is_static_layer(layer)) { 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); + if (output && !output->destroying) { + new_output = output; + mask = 1u << output->id; + } else { + new_output = NULL; + mask = 0; + } - weston_surface_assign_output(ev->surface); - return; + goto out; } new_output = NULL; @@ -1671,6 +1694,17 @@ weston_view_assign_output(struct weston_view *ev) mask |= 1u << output->id; + /* Pinned to a specific output */ + if (ec->pin_output && ev->pinned_output) { + if (!strcmp(output->name, ev->pinned_output)) { + new_output = output; + break; + } + + /* Ignore other outputs */ + continue; + } + /* Regardless of what we have now, even if it's off, a turned * off output is not better. */ @@ -1686,14 +1720,27 @@ weston_view_assign_output(struct weston_view *ev) continue; } - /* All else being equal, prefer the primary backend */ - if (new_output && new_output_area == area && - output->backend == ec->primary_backend) { - new_output = output; + /* All else being equal, prefer the preferred or primary backend */ + if (new_output && new_output_area == area) { + if (weston_output_preferred(output) || + output->backend == ec->primary_backend) + new_output = output; } } pixman_region32_fini(®ion); + if (ec->pin_output && layer && + !weston_compositor_is_system_layer(layer)) { + /* Pin non-system view to new output */ + if (!ev->pinned_output && new_output) + ev->pinned_output = strdup(new_output->name); + + /* Don't show pinned view on other outputs */ + if (ev->pinned_output && !new_output) + mask = 0; + } + +out: weston_view_set_output(ev, new_output); ev->output_mask = mask; @@ -2757,6 +2804,9 @@ weston_view_destroy(struct weston_view *view) wl_list_remove(&view->surface_link); + if (view->pinned_output) + free(view->pinned_output); + free(view); } @@ -7366,7 +7416,8 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor) wl_list_for_each(head, &output->head_list, output_link) weston_head_update_global(head); - if (!weston_output_valid(output) || output->mirror_of) + if (!weston_output_valid(output) || output->fixed_position || + output->mirror_of) continue; pos.c = weston_coord(next_x, next_y); @@ -7868,6 +7919,9 @@ weston_output_set_transform(struct weston_output *output, weston_compositor_reflow_outputs(output->compositor); + wl_signal_emit(&output->compositor->output_resized_signal, + output); + /* Notify clients of the change for output transform. */ wl_list_for_each(head, &output->head_list, output_link) { wl_resource_for_each(resource, &head->resource_list) { @@ -8225,6 +8279,8 @@ weston_output_init(struct weston_output *output, /* Can't use -1 on uint32_t and 0 is valid enum value */ output->transform = UINT32_MAX; + output->down_scale = 1.0f; + pixman_region32_init(&output->region); wl_list_init(&output->mode_list); diff --git a/libweston/libinput-seat.c b/libweston/libinput-seat.c index 4d3ab67c4..4f33f9bb8 100644 --- a/libweston/libinput-seat.c +++ b/libweston/libinput-seat.c @@ -536,3 +536,48 @@ udev_seat_get_named(struct udev_input *input, const char *seat_name) return udev_seat_create(input, seat_name); } + +void +weston_input_bind_output(struct weston_compositor *compositor, const char *output_name, const char *match) +{ + struct evdev_device *device; + struct udev_seat *seat; + const char *sysname, *name; + int len = strlen(match); + int clear = !len; + + /* Handle pattern match */ + if (len && match[len - 1] == '*') + len--; + + wl_list_for_each(seat, &compositor->seat_list, base.link) { + wl_list_for_each(device, &seat->devices_list, link) { + if (clear) { + /* Clear all bounded inputs */ + if (!device->output_name || + strcmp(device->output_name, output_name)) + continue; + + free(device->output_name); + device->output_name = NULL; + continue; + } + + sysname = libinput_device_get_sysname(device->device); + name = libinput_device_get_name(device->device); + + if (!len || !strncmp(name, match, len) || + !strncmp(sysname, match, len)) { + if (device->output_name) { + free(device->output_name); + device->output_name = NULL; + } + + if (output_name) + device->output_name = strdup(output_name); + } + } + + udev_seat_update_output(seat); + } +} diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 0cdb9e2d7..0935da89e 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -305,6 +305,10 @@ weston_compositor_xkb_destroy(struct weston_compositor *ec); int weston_input_init(struct weston_compositor *compositor); +void +weston_input_bind_output(struct weston_compositor *compositor, + const char *output_name, const char *match); + /* weston_output */ void diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 15401eb51..dedfb440f 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -353,6 +353,26 @@ repaint_region(struct weston_paint_node *pnode, else filter = PIXMAN_FILTER_NEAREST; + if (output->down_scale != 1.0f) { + struct weston_matrix matrix; + pixman_region32_t clip; + + weston_matrix_init(&matrix); + weston_matrix_scale(&matrix, output->down_scale, + output->down_scale, 1); + + pixman_region32_init(&clip); + weston_matrix_transform_region(&clip, &matrix, repaint_output); + + pixman_image_set_clip_region32(target_image, &clip); + + weston_matrix_init(&matrix); + weston_matrix_scale(&matrix, 1.0f / output->down_scale, + 1.0f / output->down_scale, 1); + weston_matrix_multiply(&matrix, &pnode->output_to_buffer_matrix); + weston_matrix_to_pixman_transform(&transform, &matrix); + } + if (ps->buffer_ref.buffer) wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer); @@ -564,6 +584,15 @@ copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region) weston_region_global_to_output(&output_region, output, &output_region); + if (output->down_scale != 1.0f) { + struct weston_matrix matrix; + weston_matrix_init(&matrix); + weston_matrix_scale(&matrix, output->down_scale, + output->down_scale, 1); + weston_matrix_transform_region(&output_region, + &matrix, &output_region); + } + pixman_image_set_clip_region32 (po->hw_buffer, &output_region); pixman_region32_fini(&output_region); diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index b688e8f0d..a27aa843a 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2307,6 +2307,9 @@ gl_renderer_repaint_output(struct weston_output *output, /* Calculate the global GL matrix */ go->output_matrix = output->matrix; + + weston_matrix_scale(&go->output_matrix, + output->down_scale, output->down_scale, 1); weston_matrix_translate(&go->output_matrix, -(go->area.width / 2.0), -(go->area.height / 2.0), 0); diff --git a/libweston/shell-utils/shell-utils.c b/libweston/shell-utils/shell-utils.c index 5587d44e9..c120ee743 100644 --- a/libweston/shell-utils/shell-utils.c +++ b/libweston/shell-utils/shell-utils.c @@ -43,9 +43,17 @@ WL_EXPORT struct weston_output * weston_shell_utils_get_default_output(struct weston_compositor *compositor) { + struct weston_output *output; + if (wl_list_empty(&compositor->output_list)) return NULL; + /* HACK: Return preferred output when available */ + wl_list_for_each(output, &compositor->output_list, link) { + if (weston_output_preferred(output)) + return output; + } + return container_of(compositor->output_list.next, struct weston_output, link); } @@ -57,8 +65,15 @@ WL_EXPORT struct weston_output * weston_shell_utils_get_focused_output(struct weston_compositor *compositor) { struct weston_seat *seat; - struct weston_output *output = NULL; + struct weston_output *output; + + /* HACK: Return preferred output when available */ + wl_list_for_each(output, &compositor->output_list, link) { + if (weston_output_preferred(output)) + return output; + } + output = NULL; wl_list_for_each(seat, &compositor->seat_list, link) { struct weston_touch *touch = weston_seat_get_touch(seat); struct weston_pointer *pointer = weston_seat_get_pointer(seat); -- 2.20.1