view: add proper abstraction with interface

This commit is contained in:
Jente Hidskes 2019-01-30 17:01:16 +01:00
parent 24517922cc
commit 64b971a665
7 changed files with 201 additions and 121 deletions

11
seat.c
View file

@ -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

53
view.c
View file

@ -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 *

19
view.h
View file

@ -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);

View file

@ -7,60 +7,81 @@
*/
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#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);
}

View file

@ -2,6 +2,20 @@
#define CG_XDG_SHELL_H
#include <wayland-server.h>
#include <wlr/types/wlr_xdg_shell.h>
#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);

View file

@ -7,43 +7,61 @@
*/
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/xwayland.h>
#include <wlr/util/log.h>
#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);
}

View file

@ -2,7 +2,22 @@
#define CG_XWAYLAND_H
#include <wayland-server.h>
#include <wlr/xwayland.h>
#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