diff --git a/seat.c b/seat.c index da7a900..addceb1 100644 --- a/seat.c +++ b/seat.c @@ -27,6 +27,9 @@ #include "seat.h" #include "server.h" #include "view.h" +#if CAGE_HAS_XWAYLAND +#include "xwayland.h" +#endif static void drag_icon_update_position(struct cg_drag_icon *drag_icon); @@ -707,9 +710,11 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) } #if CAGE_HAS_XWAYLAND - if (view->type == CAGE_XWAYLAND_VIEW && - !wlr_xwayland_or_surface_wants_focus(view->xwayland_surface)) { - return; + if (view->type == CAGE_XWAYLAND_VIEW) { + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + if (!wlr_xwayland_or_surface_wants_focus(xwayland_view->xwayland_surface)) { + return; + } } #endif diff --git a/view.c b/view.c index 2b4fc69..1bea75f 100644 --- a/view.c +++ b/view.c @@ -24,14 +24,14 @@ char * view_get_title(struct cg_view *view) { - const char *title = view->get_title(view); + const char *title = view->impl->get_title(view); return strndup(title, strlen(title)); } void view_activate(struct cg_view *view, bool activate) { - view->activate(view, activate); + view->impl->activate(view, activate); } static void @@ -41,7 +41,7 @@ view_maximize(struct cg_view *view) int output_width, output_height; wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height); - view->maximize(view, output_width, output_height); + view->impl->maximize(view, output_width, output_height); } static void @@ -53,29 +53,28 @@ view_center(struct cg_view *view) wlr_output_transformed_resolution(output, &output_width, &output_height); int width, height; - view->get_geometry(view, &width, &height); + view->impl->get_geometry(view, &width, &height); view->x = (output_width - width) / 2; view->y = (output_height - height) / 2; } void -view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, - void *data) +view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) { - view->for_each_surface(view, iterator, data); + view->impl->for_each_surface(view, iterator, data); } struct wlr_surface * view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y) { - return view->wlr_surface_at(view, sx, sy, sub_x, sub_y); + return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y); } bool view_is_primary(struct cg_view *view) { - return view->is_primary(view); + return view->impl->is_primary(view); } bool @@ -83,7 +82,7 @@ view_has_children(struct cg_server *server, struct cg_view *parent) { struct cg_view *child; wl_list_for_each(child, &server->views, link) { - if (parent != child && parent->is_parent(parent, child)) { + if (parent != child && parent->impl->is_parent(parent, child)) { return true; } } @@ -122,50 +121,32 @@ void view_destroy(struct cg_view *view) { struct cg_server *server = view->server; - bool mapped = true; - -#if CAGE_HAS_XWAYLAND - /* Some applications that aren't yet Wayland-native or - otherwise "special" (e.g. Firefox Nightly and Google - Chrome/Chromium) spawn an XWayland surface upon startup - that is almost immediately closed again. This makes Cage - think there are no views left, which results in it - exiting. However, after this initial (unmapped) surface, - the "real" application surface is opened. This leads to - these applications' startup sequences being interrupted by - Cage exiting. Hence, to work around this issue, Cage checks - whether an XWayland surface has been mapped and exits only - if 1) the XWayland surface has been mapped and 2) this was - the last surface Cage manages. */ - if (view->type == CAGE_XWAYLAND_VIEW) { - mapped = view->xwayland_surface->mapped; - } -#endif if (view->wlr_surface != NULL) { view_unmap(view); } - free(view); + + view->impl->destroy(view); /* If there is a previous view in the list, focus that. */ bool empty = wl_list_empty(&server->views); if (!empty) { struct cg_view *prev = wl_container_of(server->views.next, prev, link); seat_set_focus(server->seat, prev); - } else if (mapped) { + } else { /* The list is empty and the last view has been mapped, so we can safely exit. */ wl_display_terminate(server->wl_display); } } -struct cg_view * -view_create(struct cg_server *server) +void +view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, + const struct cg_view_impl *impl) { - struct cg_view *view = calloc(1, sizeof(struct cg_view)); - view->server = server; - return view; + view->type = type; + view->impl = impl; } struct cg_view * diff --git a/view.h b/view.h index f6645b7..3c016c0 100644 --- a/view.h +++ b/view.h @@ -28,23 +28,15 @@ struct cg_view { int x, y; enum cg_view_type type; - union { - struct wlr_xdg_surface *xdg_surface; -#if CAGE_HAS_XWAYLAND - struct wlr_xwayland_surface *xwayland_surface; -#endif - }; - - struct wl_listener destroy; - struct wl_listener unmap; - struct wl_listener map; - // TODO: allow applications to go to fullscreen from maximized? - // struct wl_listener request_fullscreen; + const struct cg_view_impl *impl; +}; +struct cg_view_impl { char *(*get_title)(struct cg_view *view); void (*activate)(struct cg_view *view, bool activate); void (*maximize)(struct cg_view *view, int output_width, int output_height); void (*get_geometry)(struct cg_view *view, int *width_out, int *height_out); + void (*destroy)(struct cg_view *view); void (*for_each_surface)(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data); struct wlr_surface *(*wlr_surface_at)(struct cg_view *view, double sx, double sy, @@ -64,7 +56,8 @@ void view_position(struct cg_view *view); void view_unmap(struct cg_view *view); void view_map(struct cg_view *view, struct wlr_surface *surface); void view_destroy(struct cg_view *view); -struct cg_view *view_create(struct cg_server *server); +void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, + const struct cg_view_impl *impl); struct cg_view *view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface); diff --git a/xdg_shell.c b/xdg_shell.c index 93fa97f..5b8ff03 100644 --- a/xdg_shell.c +++ b/xdg_shell.c @@ -7,60 +7,81 @@ */ #include +#include #include #include #include +#include #include "server.h" #include "view.h" #include "xdg_shell.h" +static struct cg_xdg_shell_view * +xdg_shell_view_from_view(struct cg_view *view) +{ + return (struct cg_xdg_shell_view *) view; +} + static char * get_title(struct cg_view *view) { - return view->xdg_surface->toplevel->title; + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + return xdg_shell_view->xdg_surface->toplevel->title; } static void activate(struct cg_view *view, bool activate) { - wlr_xdg_toplevel_set_activated(view->xdg_surface, activate); + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate); } static void maximize(struct cg_view *view, int output_width, int output_height) { - wlr_xdg_toplevel_set_size(view->xdg_surface, output_width, output_height); - wlr_xdg_toplevel_set_maximized(view->xdg_surface, true); + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, output_width, output_height); + wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_surface, true); +} + +static void +destroy(struct cg_view *view) +{ + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + free(xdg_shell_view); } static void get_geometry(struct cg_view *view, int *width_out, int *height_out) { + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); struct wlr_box geom; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geom); + wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom); *width_out = geom.width; *height_out = geom.height; } static void -for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, - void *data) +for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) { - wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, data); + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator, data); } static struct wlr_surface * wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y) { - return wlr_xdg_surface_surface_at(view->xdg_surface, sx, sy, sub_x, sub_y); + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y); } static bool is_primary(struct cg_view *view) { - struct wlr_xdg_surface *parent = view->xdg_surface->toplevel->parent; + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + struct wlr_xdg_surface *parent = xdg_shell_view->xdg_surface->toplevel->parent; /* FIXME: role is 0? */ return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */ } @@ -71,35 +92,55 @@ is_parent(struct cg_view *parent, struct cg_view *child) if (child->type != CAGE_XDG_SHELL_VIEW) { return false; } - return child->xdg_surface->toplevel->parent == parent->xdg_surface; + struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent); + struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child); + return _child->xdg_surface->toplevel->parent == _parent->xdg_surface; } static void handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, unmap); + struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); + struct cg_view *view = &xdg_shell_view->view; + view_unmap(view); } static void handle_xdg_shell_surface_map(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, map); - view_map(view, view->xdg_surface->surface); + struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map); + struct cg_view *view = &xdg_shell_view->view; + + view_map(view, xdg_shell_view->xdg_surface->surface); } static void handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, destroy); + struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); + struct cg_view *view = &xdg_shell_view->view; - wl_list_remove(&view->map.link); - wl_list_remove(&view->unmap.link); - wl_list_remove(&view->destroy.link); + wl_list_remove(&xdg_shell_view->map.link); + wl_list_remove(&xdg_shell_view->unmap.link); + wl_list_remove(&xdg_shell_view->destroy.link); + xdg_shell_view->xdg_surface = NULL; view_destroy(view); } +static const struct cg_view_impl xdg_shell_view_impl = { + .get_title = get_title, + .get_geometry = get_geometry, + .is_primary = is_primary, + .is_parent = is_parent, + .activate = activate, + .maximize = maximize, + .destroy = destroy, + .for_each_surface = for_each_surface, + .wlr_surface_at = wlr_surface_at, +}; + void handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) { @@ -110,23 +151,19 @@ handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) return; } - struct cg_view *view = view_create(server); - view->type = CAGE_XDG_SHELL_VIEW; - view->xdg_surface = xdg_surface; + struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view)); + if (!xdg_shell_view) { + wlr_log(WLR_ERROR, "Failed to allocate XDG Shell view"); + return; + } - view->map.notify = handle_xdg_shell_surface_map; - wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = handle_xdg_shell_surface_unmap; - wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = handle_xdg_shell_surface_destroy; - wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl); + xdg_shell_view->xdg_surface = xdg_surface; - view->get_title = get_title; - view->activate = activate; - view->maximize = maximize; - view->get_geometry = get_geometry; - view->for_each_surface = for_each_surface; - view->wlr_surface_at = wlr_surface_at; - view->is_primary = is_primary; - view->is_parent = is_parent; + xdg_shell_view->map.notify = handle_xdg_shell_surface_map; + wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); + xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap; + wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); + xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy; + wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); } diff --git a/xdg_shell.h b/xdg_shell.h index 25bcc12..89ec5f3 100644 --- a/xdg_shell.h +++ b/xdg_shell.h @@ -2,6 +2,20 @@ #define CG_XDG_SHELL_H #include +#include + +#include "view.h" + +struct cg_xdg_shell_view { + struct cg_view view; + struct wlr_xdg_surface *xdg_surface; + + struct wl_listener destroy; + struct wl_listener unmap; + struct wl_listener map; + // TODO: allow applications to go to fullscreen from maximized? + // struct wl_listener request_fullscreen; +}; void handle_xdg_shell_surface_new(struct wl_listener *listener, void *data); diff --git a/xwayland.c b/xwayland.c index 03a75aa..c1cde85 100644 --- a/xwayland.c +++ b/xwayland.c @@ -7,43 +7,61 @@ */ #include +#include #include #include #include +#include #include "server.h" #include "view.h" #include "xwayland.h" +struct cg_xwayland_view * +xwayland_view_from_view(struct cg_view *view) +{ + return (struct cg_xwayland_view *) view; +} + static char * get_title(struct cg_view *view) { - return view->xwayland_surface->title; + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + return xwayland_view->xwayland_surface->title; } static void activate(struct cg_view *view, bool activate) { - wlr_xwayland_surface_activate(view->xwayland_surface, activate); + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + wlr_xwayland_surface_activate(xwayland_view->xwayland_surface, activate); } static void maximize(struct cg_view *view, int output_width, int output_height) { - wlr_xwayland_surface_configure(view->xwayland_surface, 0, 0, output_width, output_height); - wlr_xwayland_surface_set_maximized(view->xwayland_surface, true); + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, 0, 0, output_width, output_height); + wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true); +} + +static void +destroy(struct cg_view *view) +{ + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + free(xwayland_view); } static void get_geometry(struct cg_view *view, int *width_out, int *height_out) { - *width_out = view->xwayland_surface->surface->current.width; - *height_out = view->xwayland_surface->surface->current.height; + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + *width_out = xwayland_view->xwayland_surface->surface->current.width; + *height_out = xwayland_view->xwayland_surface->surface->current.height; } static void -for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, - void *data) +for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) { wlr_surface_for_each_surface(view->wlr_surface, iterator, data); } @@ -57,7 +75,8 @@ wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double static bool is_primary(struct cg_view *view) { - struct wlr_xwayland_surface *parent = view->xwayland_surface->parent; + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + struct wlr_xwayland_surface *parent = xwayland_view->xwayland_surface->parent; return parent == NULL; } @@ -67,58 +86,74 @@ is_parent(struct cg_view *parent, struct cg_view *child) if (child->type != CAGE_XWAYLAND_VIEW) { return false; } - return child->xwayland_surface->parent == parent->xwayland_surface; + struct cg_xwayland_view *_parent = xwayland_view_from_view(parent); + struct cg_xwayland_view *_child = xwayland_view_from_view(child); + return _child->xwayland_surface->parent == _parent->xwayland_surface; } static void handle_xwayland_surface_unmap(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, unmap); + struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap); + struct cg_view *view = &xwayland_view->view; + view_unmap(view); } static void handle_xwayland_surface_map(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, map); - view_map(view, view->xwayland_surface->surface); + struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, map); + struct cg_view *view = &xwayland_view->view; + + view_map(view, xwayland_view->xwayland_surface->surface); } static void handle_xwayland_surface_destroy(struct wl_listener *listener, void *data) { - struct cg_view *view = wl_container_of(listener, view, destroy); + struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy); + struct cg_view *view = &xwayland_view->view; - wl_list_remove(&view->map.link); - wl_list_remove(&view->unmap.link); - wl_list_remove(&view->destroy.link); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); + wl_list_remove(&xwayland_view->destroy.link); + xwayland_view->xwayland_surface = NULL; view_destroy(view); } +static const struct cg_view_impl xwayland_view_impl = { + .get_title = get_title, + .get_geometry = get_geometry, + .is_primary = is_primary, + .is_parent = is_parent, + .activate = activate, + .maximize = maximize, + .destroy = destroy, + .for_each_surface = for_each_surface, + .wlr_surface_at = wlr_surface_at, +}; + void handle_xwayland_surface_new(struct wl_listener *listener, void *data) { struct cg_server *server = wl_container_of(listener, server, new_xwayland_surface); struct wlr_xwayland_surface *xwayland_surface = data; - struct cg_view *view = view_create(server); - view->type = CAGE_XWAYLAND_VIEW; - view->xwayland_surface = xwayland_surface; + struct cg_xwayland_view *xwayland_view = calloc(1, sizeof(struct cg_xwayland_view)); + if (!xwayland_view) { + wlr_log(WLR_ERROR, "Failed to allocate XWayland view"); + return; + } - view->map.notify = handle_xwayland_surface_map; - wl_signal_add(&xwayland_surface->events.map, &view->map); - view->unmap.notify = handle_xwayland_surface_unmap; - wl_signal_add(&xwayland_surface->events.unmap, &view->unmap); - view->destroy.notify = handle_xwayland_surface_destroy; - wl_signal_add(&xwayland_surface->events.destroy, &view->destroy); + view_init(&xwayland_view->view, server, CAGE_XWAYLAND_VIEW, &xwayland_view_impl); + xwayland_view->xwayland_surface = xwayland_surface; - view->get_title = get_title; - view->activate = activate; - view->maximize = maximize; - view->get_geometry = get_geometry; - view->for_each_surface = for_each_surface; - view->wlr_surface_at = wlr_surface_at; - view->is_primary = is_primary; - view->is_parent = is_parent; + xwayland_view->map.notify = handle_xwayland_surface_map; + wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map); + xwayland_view->unmap.notify = handle_xwayland_surface_unmap; + wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap); + xwayland_view->destroy.notify = handle_xwayland_surface_destroy; + wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy); } diff --git a/xwayland.h b/xwayland.h index 8ca1700..e8ec9f9 100644 --- a/xwayland.h +++ b/xwayland.h @@ -2,7 +2,22 @@ #define CG_XWAYLAND_H #include +#include +#include "view.h" + +struct cg_xwayland_view { + struct cg_view view; + struct wlr_xwayland_surface *xwayland_surface; + + struct wl_listener destroy; + struct wl_listener unmap; + struct wl_listener map; + // TODO: allow applications to go to fullscreen from maximized? + // struct wl_listener request_fullscreen; +}; + +struct cg_xwayland_view *xwayland_view_from_view(struct cg_view *view); void handle_xwayland_surface_new(struct wl_listener *listener, void *data); #endif