From 3dc5caa6a756a5739cad5e99a2d839fac12ff1fe Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Dec 2019 16:10:11 +0100 Subject: [PATCH] Support multiple outputs Outputs are arranged in a horizontal layout in the order they are created in by wlroots. Maximized xdg_shell views will span all outputs, like the global fullscreen mode in sway. Fixes #87 --- cage.c | 1 + output.c | 57 ++++++++++++++++++++++++++++++++--------------------- output.h | 2 ++ seat.c | 30 ++++++++++++++++++++++++---- server.h | 2 +- view.c | 34 ++++++++++++++++++++++++-------- xdg_shell.c | 8 +++++--- 7 files changed, 95 insertions(+), 39 deletions(-) diff --git a/cage.c b/cage.c index 4b74305..5a4a855 100644 --- a/cage.c +++ b/cage.c @@ -220,6 +220,7 @@ main(int argc, char *argv[]) wlr_renderer_init_wl_display(renderer, server.wl_display); wl_list_init(&server.views); + wl_list_init(&server.outputs); server.output_layout = wlr_output_layout_create(); if (!server.output_layout) { diff --git a/output.c b/output.c index e5b545b..973f9e4 100644 --- a/output.c +++ b/output.c @@ -319,12 +319,20 @@ output_destroy(struct cg_output *output) wl_list_remove(&output->transform.link); wl_list_remove(&output->damage_frame.link); wl_list_remove(&output->damage_destroy.link); - free(output); - server->output = NULL; + wl_list_remove(&output->link); - /* Since there is no use in continuing without our (single) - * output, terminate. */ - wl_display_terminate(server->wl_display); + wlr_output_layout_remove(server->output_layout, output->wlr_output); + + struct cg_view *view; + wl_list_for_each(view, &output->server->views, link) { + view_position(view); + } + + free(output); + + if (wl_list_empty(&server->outputs)) { + wl_display_terminate(server->wl_display); + } } static void @@ -354,29 +362,32 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_output_set_mode(wlr_output, preferred_mode); } - server->output = calloc(1, sizeof(struct cg_output)); - server->output->wlr_output = wlr_output; - server->output->server = server; - server->output->damage = wlr_output_damage_create(wlr_output); + struct cg_output *output = calloc(1, sizeof(struct cg_output)); + output->wlr_output = wlr_output; + output->server = server; + output->damage = wlr_output_damage_create(wlr_output); + wl_list_insert(&server->outputs, &output->link); - server->output->mode.notify = handle_output_mode; - wl_signal_add(&wlr_output->events.mode, &server->output->mode); - server->output->transform.notify = handle_output_transform; - wl_signal_add(&wlr_output->events.transform, &server->output->transform); - server->output->destroy.notify = handle_output_destroy; - wl_signal_add(&wlr_output->events.destroy, &server->output->destroy); - server->output->damage_frame.notify = handle_output_damage_frame; - wl_signal_add(&server->output->damage->events.frame, &server->output->damage_frame); - server->output->damage_destroy.notify = handle_output_damage_destroy; - wl_signal_add(&server->output->damage->events.destroy, &server->output->damage_destroy); + output->mode.notify = handle_output_mode; + wl_signal_add(&wlr_output->events.mode, &output->mode); + output->transform.notify = handle_output_transform; + wl_signal_add(&wlr_output->events.transform, &output->transform); + output->destroy.notify = handle_output_destroy; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + output->damage_frame.notify = handle_output_damage_frame; + wl_signal_add(&output->damage->events.frame, &output->damage_frame); + output->damage_destroy.notify = handle_output_damage_destroy; + wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); + + struct cg_view *view; + wl_list_for_each(view, &output->server->views, link) { + view_position(view); + } wlr_output_set_transform(wlr_output, server->output_transform); wlr_output_layout_add_auto(server->output_layout, wlr_output); - /* Disconnect the signal now, because we only use one static output. */ - wl_list_remove(&server->new_output.link); - if (wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) { wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name, @@ -385,7 +396,7 @@ handle_new_output(struct wl_listener *listener, void *data) /* Place the cursor in the center of the screen. */ wlr_cursor_warp(server->seat->cursor, NULL, wlr_output->width / 2, wlr_output->height / 2); - wlr_output_damage_add_whole(server->output->damage); + wlr_output_damage_add_whole(output->damage); } void diff --git a/output.h b/output.h index 9237c67..563e503 100644 --- a/output.h +++ b/output.h @@ -19,6 +19,8 @@ struct cg_output { struct wl_listener destroy; struct wl_listener damage_frame; struct wl_listener damage_destroy; + + struct wl_list link; }; void handle_new_output(struct wl_listener *listener, void *data); diff --git a/seat.c b/seat.c index 9402ec6..c955d85 100644 --- a/seat.c +++ b/seat.c @@ -158,7 +158,15 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) touch->destroy.notify = handle_touch_destroy; wl_signal_add(&touch->device->events.destroy, &touch->destroy); - wlr_cursor_map_input_to_output(seat->cursor, device, seat->server->output->wlr_output); + if (device->output_name != NULL) { + struct cg_output *output; + wl_list_for_each(output, &seat->server->outputs, link) { + if (strcmp(device->output_name, output->wlr_output->name) == 0) { + wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output); + break; + } + } + } } static void @@ -192,7 +200,15 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) pointer->destroy.notify = handle_pointer_destroy; wl_signal_add(&device->events.destroy, &pointer->destroy); - wlr_cursor_map_input_to_output(seat->cursor, device, seat->server->output->wlr_output); + if (device->output_name != NULL) { + struct cg_output *output; + wl_list_for_each(output, &seat->server->outputs, link) { + if (strcmp(device->output_name, output->wlr_output->name) == 0) { + wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output); + break; + } + } + } } static void @@ -568,7 +584,10 @@ handle_cursor_motion(struct wl_listener *listener, void *data) static void drag_icon_damage(struct cg_drag_icon *drag_icon) { - output_damage_drag_icon(drag_icon->seat->server->output, drag_icon); + struct cg_output *output; + wl_list_for_each(output, &drag_icon->seat->server->outputs, link) { + output_damage_drag_icon(output, drag_icon); + } } static void @@ -835,7 +854,10 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) view_activate(view, true); char *title = view_get_title(view); - output_set_window_title(server->output, title); + struct cg_output *output; + wl_list_for_each(output, &server->outputs, link) { + output_set_window_title(output, title); + } free(title); struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wlr_seat); diff --git a/server.h b/server.h index ce43d5b..d05dabf 100644 --- a/server.h +++ b/server.h @@ -29,7 +29,7 @@ struct cg_server { struct wl_list inhibitors; struct wlr_output_layout *output_layout; - struct cg_output *output; + struct wl_list outputs; struct wl_listener new_output; struct wl_listener xdg_toplevel_decoration; diff --git a/view.c b/view.c index cc2f4b0..36a69d0 100644 --- a/view.c +++ b/view.c @@ -138,13 +138,19 @@ view_is_transient_for(struct cg_view *child, struct cg_view *parent) { void view_damage_surface(struct cg_view *view) { - output_damage_view_surface(view->server->output, view); + struct cg_output *output; + wl_list_for_each(output, &view->server->outputs, link) { + output_damage_view_surface(output, view); + } } void view_damage_whole(struct cg_view *view) { - output_damage_view_whole(view->server->output, view); + struct cg_output *output; + wl_list_for_each(output, &view->server->outputs, link) { + output_damage_view_whole(output, view); + } } void @@ -153,23 +159,35 @@ view_activate(struct cg_view *view, bool activate) view->impl->activate(view, activate); } +static void +get_view_output_dimensions(struct cg_view *view, int *output_width, int *output_height) +{ + *output_width = 0; + *output_height = 0; + struct cg_output *output; + wl_list_for_each(output, &view->server->outputs, link) { + int h, w; + wlr_output_transformed_resolution(output->wlr_output, &w, &h); + *output_width += w; + if (h > *output_height) { + *output_height = h; + } + } +} + static void view_maximize(struct cg_view *view) { - struct cg_output *output = view->server->output; int output_width, output_height; - - wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height); + get_view_output_dimensions(view, &output_width, &output_height); view->impl->maximize(view, output_width, output_height); } static void view_center(struct cg_view *view) { - struct wlr_output *output = view->server->output->wlr_output; - int output_width, output_height; - wlr_output_transformed_resolution(output, &output_width, &output_height); + get_view_output_dimensions(view, &output_width, &output_height); int width, height; view->impl->get_geometry(view, &width, &height); diff --git a/xdg_shell.c b/xdg_shell.c index 5f2c9fd..a5c439e 100644 --- a/xdg_shell.c +++ b/xdg_shell.c @@ -93,10 +93,12 @@ static void popup_unconstrain(struct cg_xdg_popup *popup) { struct cg_view *view = popup->view_child.view; - struct wlr_output *output = view->server->output->wlr_output; - struct wlr_output_layout *output_layout = view->server->output_layout; + struct cg_server *server = view->server; + struct wlr_box *popup_box = &popup->wlr_popup->geometry; - struct wlr_box *output_box = wlr_output_layout_get_box(output_layout, output); + struct wlr_output_layout *output_layout = server->output_layout; + struct wlr_output *wlr_output = wlr_output_layout_output_at(output_layout, view->x + popup_box->x, view->y + popup_box->y); + struct wlr_box *output_box = wlr_output_layout_get_box(output_layout, wlr_output); struct wlr_box output_toplevel_box = { .x = output_box->x - view->x,