diff --git a/include/labwc.h b/include/labwc.h index 204c952d..8c2b5fc4 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -52,6 +52,7 @@ struct server { struct wlr_xwayland *xwayland; struct wl_listener new_xwayland_surface; struct wl_list views; + struct wl_list unmanaged_surfaces; struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; @@ -78,10 +79,12 @@ struct server { struct wl_list outputs; struct wl_listener new_output; - /* For use in cycle (alt-tab) mode */ + /* Set when in cycle (alt-tab) mode */ struct view *cycle_view; }; +extern struct server server; + struct output { struct wl_list link; struct server *server; @@ -137,6 +140,18 @@ struct view { struct wl_listener request_configure; }; +struct xwayland_unmanaged { + struct wlr_xwayland_surface *xwayland_surface; + struct wl_list link; + int lx, ly; + + struct wl_listener request_configure; + struct wl_listener commit; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; +}; + struct keyboard { struct wl_list link; struct server *server; @@ -150,6 +165,7 @@ void xdg_toplevel_decoration(struct wl_listener *listener, void *data); void xdg_surface_new(struct wl_listener *listener, void *data); void xwl_surface_new(struct wl_listener *listener, void *data); +void xwayland_unmanaged_create(struct wlr_xwayland_surface *xsurface); void view_init_position(struct view *view); /** @@ -169,6 +185,8 @@ struct view *view_at(struct server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy, int *view_area); +void seat_focus_surface(struct wlr_surface *surface); + void interactive_begin(struct view *view, enum cursor_mode mode, uint32_t edges); diff --git a/src/meson.build b/src/meson.build index 3b8cabe9..26cd31ec 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,9 +7,11 @@ labwc_sources = files( 'keyboard.c', 'main.c', 'output.c', + 'seat.c', 'server.c', 'view.c', 'xdg.c', + 'xwayland-unmanaged.c', 'xwl.c', ) diff --git a/src/output.c b/src/output.c index 756b2cdb..9b150abc 100644 --- a/src/output.c +++ b/src/output.c @@ -88,7 +88,7 @@ static void render_decorations(struct wlr_output *output, struct view *view) struct render_data { struct wlr_output *output; struct wlr_renderer *renderer; - struct view *view; + int lx, ly; struct timespec *when; }; @@ -98,7 +98,6 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, /* This function is called for every surface that needs to be rendered. */ struct render_data *rdata = data; - struct view *view = rdata->view; struct wlr_output *output = rdata->output; /* We first obtain a wlr_texture, which is a GPU resource. wlroots @@ -117,10 +116,9 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, * display might have layout coordinates of 2000,100. We need to * translate that to output-local coordinates, or (2000 - 1920). */ double ox = 0, oy = 0; - wlr_output_layout_output_coords(view->server->output_layout, output, - &ox, &oy); - ox += view->x + sx; - oy += view->y + sy; + wlr_output_layout_output_coords(server.output_layout, output, &ox, &oy); + ox += rdata->lx + sx; + oy += rdata->ly + sy; /* TODO: Support HiDPI */ struct wlr_box box = { @@ -188,22 +186,21 @@ void output_frame(struct wl_listener *listener, void *data) * backwards. */ struct view *view; wl_list_for_each_reverse (view, &output->server->views, link) { - if (!view->mapped) { - /* An unmapped view should not be rendered. */ + if (!view->mapped) continue; - } + struct render_data rdata = { .output = output->wlr_output, - .view = view, + .lx = view->x, + .ly = view->y, .renderer = renderer, .when = &now, }; render_decorations(output->wlr_output, view); - /* This calls our render_surface function for each surface among - * the xdg_surface's toplevel and popups. */ if (view->type == LAB_XDG_SHELL_VIEW) { + /* render each xdg toplevel and popup surface */ wlr_xdg_surface_for_each_surface( view->xdg_surface, render_surface, &rdata); } else if (view->type == LAB_XWAYLAND_VIEW) { @@ -215,6 +212,21 @@ void output_frame(struct wl_listener *listener, void *data) /* If in cycle (alt-tab) mode, highlight selected view */ render_cycle_box(output); + /* Render xwayland override_redirect surfaces */ + struct xwayland_unmanaged *unmanaged; + wl_list_for_each_reverse (unmanaged, &server.unmanaged_surfaces, link) { + struct render_data rdata = { + .output = output->wlr_output, + .lx = unmanaged->lx, + .ly = unmanaged->ly, + .renderer = renderer, + .when = &now, + }; + + struct wlr_surface *s = unmanaged->xwayland_surface->surface; + render_surface(s, 0, 0, &rdata); + } + /* Hardware cursors are rendered by the GPU on a separate plane, and can * be moved around without re-rendering what's beneath them - which is * more efficient. However, not all hardware supports hardware cursors. diff --git a/src/seat.c b/src/seat.c new file mode 100644 index 00000000..62e4aca6 --- /dev/null +++ b/src/seat.c @@ -0,0 +1,11 @@ +#include "labwc.h" + +void seat_focus_surface(struct wlr_surface *surface) +{ + if (!surface) { + wlr_seat_keyboard_notify_clear_focus(server.seat); + return; + } + /* TODO: add keyboard stuff */ + wlr_seat_keyboard_notify_enter(server.seat, surface, NULL, 0, NULL); +} diff --git a/src/server.c b/src/server.c index 6db7e1c7..b144e1c4 100644 --- a/src/server.c +++ b/src/server.c @@ -105,6 +105,7 @@ void server_init(struct server *server) wlr_renderer_init_wl_display(server->renderer, server->wl_display); wl_list_init(&server->views); + wl_list_init(&server->unmanaged_surfaces); wl_list_init(&server->outputs); /* diff --git a/src/view.c b/src/view.c index 8f8e6c7a..d4034f47 100644 --- a/src/view.c +++ b/src/view.c @@ -150,6 +150,8 @@ void view_focus(struct view *view) /* Note: this function only deals with keyboard focus. */ if (!view) return; + + /* TODO: messy - sort out */ if (!view->mapped) { view->impl->map(view); return; diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c new file mode 100644 index 00000000..2d8d0e4d --- /dev/null +++ b/src/xwayland-unmanaged.c @@ -0,0 +1,86 @@ +#include "labwc.h" + +static void unmanaged_handle_request_configure(struct wl_listener *listener, + void *data) +{ + struct xwayland_unmanaged *unmanaged = + wl_container_of(listener, unmanaged, request_configure); + struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; + struct wlr_xwayland_surface_configure_event *ev = data; + wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, + ev->height); +} + +static void unmanaged_handle_commit(struct wl_listener *listener, void *data) +{ + struct xwayland_unmanaged *unmanaged = + wl_container_of(listener, unmanaged, commit); + struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; + unmanaged->lx = xsurface->x; + unmanaged->ly = xsurface->y; +} + +static void unmanaged_handle_map(struct wl_listener *listener, void *data) +{ + struct xwayland_unmanaged *unmanaged = + wl_container_of(listener, unmanaged, map); + struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; + + wl_list_insert(server.unmanaged_surfaces.prev, &unmanaged->link); + + wl_signal_add(&xsurface->surface->events.commit, &unmanaged->commit); + unmanaged->commit.notify = unmanaged_handle_commit; + + unmanaged->lx = xsurface->x; + unmanaged->ly = xsurface->y; + + if (wlr_xwayland_or_surface_wants_focus(xsurface)) + seat_focus_surface(xsurface->surface); +} + +static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) +{ + struct xwayland_unmanaged *unmanaged = + wl_container_of(listener, unmanaged, unmap); + struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; + wl_list_remove(&unmanaged->link); + wl_list_remove(&unmanaged->commit.link); + + if (server.seat->keyboard_state.focused_surface == xsurface->surface) { + struct xwayland_unmanaged *u; + wl_list_for_each (u, &server.unmanaged_surfaces, link) { + struct wlr_xwayland_surface *prev = u->xwayland_surface; + if (!wlr_xwayland_or_surface_wants_focus(prev)) + continue; + seat_focus_surface(prev->surface); + return; + } + } +} + +static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) +{ + struct xwayland_unmanaged *unmanaged = + wl_container_of(listener, unmanaged, destroy); + wl_list_remove(&unmanaged->map.link); + wl_list_remove(&unmanaged->unmap.link); + wl_list_remove(&unmanaged->destroy.link); + free(unmanaged); +} + +void xwayland_unmanaged_create(struct wlr_xwayland_surface *xsurface) +{ + struct xwayland_unmanaged *unmanaged; + unmanaged = calloc(1, sizeof(struct xwayland_unmanaged)); + unmanaged->xwayland_surface = xsurface; + wl_signal_add(&xsurface->events.request_configure, + &unmanaged->request_configure); + unmanaged->request_configure.notify = + unmanaged_handle_request_configure; + wl_signal_add(&xsurface->events.map, &unmanaged->map); + unmanaged->map.notify = unmanaged_handle_map; + wl_signal_add(&xsurface->events.unmap, &unmanaged->unmap); + unmanaged->unmap.notify = unmanaged_handle_unmap; + wl_signal_add(&xsurface->events.destroy, &unmanaged->destroy); + unmanaged->destroy.notify = unmanaged_handle_destroy; +} diff --git a/src/xwl.c b/src/xwl.c index 3b9dae5a..bd426047 100644 --- a/src/xwl.c +++ b/src/xwl.c @@ -79,10 +79,7 @@ static void map(struct view *view) } view->been_mapped = true; - /* - * Add commit listener here, because xwayland map/unmap can change - * the wlr_surface - */ + /* Add commit here, as xwayland map/unmap can change the wlr_surface */ wl_signal_add(&view->xwayland_surface->surface->events.commit, &view->commit); view->commit.notify = handle_commit; @@ -108,22 +105,31 @@ void xwl_surface_new(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_xwayland_surface); - struct wlr_xwayland_surface *xwayland_surface = data; - wlr_xwayland_surface_ping(xwayland_surface); + struct wlr_xwayland_surface *xsurface = data; + wlr_xwayland_surface_ping(xsurface); + + /* + * We do not create 'views' for xwayland override_redirect surfaces, + * but add them to server.unmanaged_surfaces so that we can render them + */ + if (xsurface->override_redirect) { + xwayland_unmanaged_create(xsurface); + return; + } struct view *view = calloc(1, sizeof(struct view)); view->server = server; view->type = LAB_XWAYLAND_VIEW; view->impl = &xwl_view_impl; - view->xwayland_surface = xwayland_surface; + view->xwayland_surface = xsurface; view->map.notify = handle_map; - wl_signal_add(&xwayland_surface->events.map, &view->map); + wl_signal_add(&xsurface->events.map, &view->map); view->unmap.notify = handle_unmap; - wl_signal_add(&xwayland_surface->events.unmap, &view->unmap); + wl_signal_add(&xsurface->events.unmap, &view->unmap); view->destroy.notify = handle_destroy; - wl_signal_add(&xwayland_surface->events.destroy, &view->destroy); + wl_signal_add(&xsurface->events.destroy, &view->destroy); view->request_configure.notify = handle_request_configure; - wl_signal_add(&xwayland_surface->events.request_configure, + wl_signal_add(&xsurface->events.request_configure, &view->request_configure); }