diff --git a/include/zen/screen/compositor.h b/include/zen/screen/compositor.h index 9ff177a9..6778b04a 100644 --- a/include/zen/screen/compositor.h +++ b/include/zen/screen/compositor.h @@ -3,6 +3,7 @@ #include #include #include +#include struct zn_screen_compositor { struct wl_display *display; @@ -10,8 +11,11 @@ struct zn_screen_compositor { // These objects will be automatically destroyed when wl_display is destroyed struct wlr_compositor *compositor; struct wlr_xdg_shell *xdg_shell; + struct wlr_xwayland *xwayland; struct wl_listener xdg_shell_new_surface_listener; + struct wl_listener xwayland_ready_listener; + struct wl_listener xwayland_new_surface_listener; }; struct zn_screen_compositor *zn_screen_compositor_create( diff --git a/include/zen/screen/xwayland.h b/include/zen/screen/xwayland.h new file mode 100644 index 00000000..771825f5 --- /dev/null +++ b/include/zen/screen/xwayland.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +struct zn_view; + +struct zn_xwayland_view { + struct wlr_xwayland_surface *xwayland_surface; // nonnull + + struct zn_view *view; // null if not mapped + + struct wl_listener map_listener; + struct wl_listener unmap_listener; + struct wl_listener move_listener; + struct wl_listener resize_listener; + struct wl_listener maximize_listener; + struct wl_listener wlr_xwayland_surface_destroy_listener; +}; + +struct zn_xwayland_view *zn_xwayland_view_create( + struct wlr_xwayland_surface *xwayland_surface); diff --git a/include/zen/view.h b/include/zen/view.h index b3375d2b..a25c60bd 100644 --- a/include/zen/view.h +++ b/include/zen/view.h @@ -2,6 +2,7 @@ #include #include +#include #include "zen/appearance/view.h" @@ -17,8 +18,11 @@ struct zn_view_interface { uint32_t (*get_current_configure_serial)(struct zn_view *view); uint32_t (*set_size)(struct zn_view *view, double width, double height); uint32_t (*set_maximized)(struct zn_view *view, bool maximized); + void (*set_position)(struct zn_view *view, double x, double y); // nulable void (*set_activated)(struct zn_view *view, bool activated); - uint32_t (*schedule_configure)(struct zn_view *view); + void (*restack)( + struct zn_view *view, enum xcb_stack_mode_t mode); // nullable + uint32_t (*schedule_configure)(struct zn_view *view); // nullable void (*close_popups)(struct zn_view *view); void (*for_each_popup_surface)(struct zn_view *view, wlr_surface_iterator_func_t iterator, void *user_data); @@ -46,6 +50,7 @@ struct zn_view { struct { bool maximized; + bool maximize_status_changed; uint32_t changed_serial; struct wlr_fbox reset_box; } maximize_status; diff --git a/zen/meson.build b/zen/meson.build index a44572c6..1df0772e 100644 --- a/zen/meson.build +++ b/zen/meson.build @@ -29,6 +29,7 @@ _zen_srcs = [ 'screen/renderer.c', 'screen/xdg-popup.c', 'screen/xdg-toplevel.c', + 'screen/xwayland.c', 'screen/cursor-grab/default.c', 'screen/cursor-grab/down.c', 'screen/cursor-grab/move.c', diff --git a/zen/screen/compositor.c b/zen/screen/compositor.c index a9a787da..ca03fbfd 100644 --- a/zen/screen/compositor.c +++ b/zen/screen/compositor.c @@ -2,7 +2,22 @@ #include +#include "zen/input/input-manager.h" #include "zen/screen/xdg-toplevel.h" +#include "zen/screen/xwayland.h" +#include "zen/server.h" + +static void +zn_screen_compositor_handle_xwayland_ready( + struct wl_listener *listener, void *data) +{ + UNUSED(data); + struct zn_screen_compositor *self = + zn_container_of(listener, self, xwayland_ready_listener); + + struct zn_server *server = zn_server_get_singleton(); + wlr_xwayland_set_seat(self->xwayland, server->input_manager->seat->wlr_seat); +} static void zn_screen_compositor_handle_xdg_shell_new_surface( @@ -22,6 +37,16 @@ zn_screen_compositor_handle_xdg_shell_new_surface( // TODO: handle other roles } +static void +zn_screen_compositor_handle_xwayland_new_surface( + struct wl_listener *listener, void *data) +{ + UNUSED(listener); + struct wlr_xwayland_surface *xwayland_surface = data; + wlr_xwayland_surface_ping(xwayland_surface); + (void)zn_xwayland_view_create(xwayland_surface); +} + struct zn_screen_compositor * zn_screen_compositor_create( struct wl_display *display, struct wlr_renderer *renderer) @@ -48,11 +73,27 @@ zn_screen_compositor_create( goto err_free; } + self->xwayland = wlr_xwayland_create(display, self->compositor, true); + if (self->xwayland == NULL) { + zn_error("Failed to create a wlr_xwayland"); + goto err_free; + } + setenv("DISPLAY", self->xwayland->display_name, true); + + self->xwayland_ready_listener.notify = + zn_screen_compositor_handle_xwayland_ready; + wl_signal_add(&self->xwayland->events.ready, &self->xwayland_ready_listener); + self->xdg_shell_new_surface_listener.notify = zn_screen_compositor_handle_xdg_shell_new_surface; wl_signal_add(&self->xdg_shell->events.new_surface, &self->xdg_shell_new_surface_listener); + self->xwayland_new_surface_listener.notify = + zn_screen_compositor_handle_xwayland_new_surface; + wl_signal_add(&self->xwayland->events.new_surface, + &self->xwayland_new_surface_listener); + return self; err_free: @@ -65,6 +106,9 @@ zn_screen_compositor_create( void zn_screen_compositor_destroy(struct zn_screen_compositor *self) { + wl_list_remove(&self->xwayland_ready_listener.link); + wl_list_remove(&self->xwayland_new_surface_listener.link); wl_list_remove(&self->xdg_shell_new_surface_listener.link); + wlr_xwayland_destroy(self->xwayland); free(self); } diff --git a/zen/screen/xwayland.c b/zen/screen/xwayland.c new file mode 100644 index 00000000..18c20487 --- /dev/null +++ b/zen/screen/xwayland.c @@ -0,0 +1,229 @@ +#include "zen/screen/xwayland.h" + +#include + +#include "zen/screen/cursor-grab/move.h" +#include "zen/screen/cursor-grab/resize.h" +#include "zen/server.h" +#include "zen/view.h" + +static void zn_xwayland_view_destroy(struct zn_xwayland_view *self); + +static struct wlr_surface * +zn_xwayland_view_impl_get_wlr_surface_at(struct zn_view *view, double view_sx, + double view_sy, double *surface_x, double *surface_y) +{ + struct zn_xwayland_view *self = view->user_data; + + return wlr_surface_surface_at( + self->xwayland_surface->surface, view_sx, view_sy, surface_x, surface_y); +} + +static void +zn_xwayland_view_impl_get_window_geom(struct zn_view *view, struct wlr_box *box) +{ + struct zn_xwayland_view *self = view->user_data; + box->x = 0; + box->y = 0; + box->width = self->xwayland_surface->width; + box->height = self->xwayland_surface->height; +} + +static uint32_t +zn_xwayland_view_impl_get_current_configure_serial(struct zn_view *view) +{ + UNUSED(view); + return 0; +} + +static uint32_t +zn_xwayland_view_impl_set_size( + struct zn_view *view, double width, double height) +{ + struct zn_xwayland_view *self = view->user_data; + + wlr_xwayland_surface_configure(self->xwayland_surface, + self->xwayland_surface->x, self->xwayland_surface->y, width, height); + + return 0; +} + +static void +zn_xwayland_view_impl_set_position(struct zn_view *view, double x, double y) +{ + struct zn_xwayland_view *self = view->user_data; + + wlr_xwayland_surface_configure(self->xwayland_surface, x, y, + self->xwayland_surface->width, self->xwayland_surface->height); +} + +static uint32_t +zn_xwayland_view_impl_set_maximized(struct zn_view *view, bool maximized) +{ + struct zn_xwayland_view *self = view->user_data; + + wlr_xwayland_surface_set_maximized(self->xwayland_surface, maximized); + + return 0; +} + +static void +zn_xwayland_view_impl_set_activated(struct zn_view *view, bool activated) +{ + struct zn_xwayland_view *self = view->user_data; + + wlr_xwayland_surface_activate(self->xwayland_surface, activated); +} + +static void +zn_xwayland_view_impl_restack(struct zn_view *view, enum xcb_stack_mode_t mode) +{ + struct zn_xwayland_view *self = view->user_data; + wlr_xwayland_surface_restack(self->xwayland_surface, NULL, mode); +} + +static const struct zn_view_interface zn_xwayland_view_impl = { + .get_wlr_surface_at = zn_xwayland_view_impl_get_wlr_surface_at, + .get_window_geom = zn_xwayland_view_impl_get_window_geom, + .get_current_configure_serial = + zn_xwayland_view_impl_get_current_configure_serial, + .set_size = zn_xwayland_view_impl_set_size, + .set_maximized = zn_xwayland_view_impl_set_maximized, + .set_position = zn_xwayland_view_impl_set_position, + .set_activated = zn_xwayland_view_impl_set_activated, + .restack = zn_xwayland_view_impl_restack, +}; + +static void +zn_xwayland_view_handle_move(struct wl_listener *listener, void *data) +{ + UNUSED(data); + struct zn_xwayland_view *self = + zn_container_of(listener, self, move_listener); + struct zn_server *server = zn_server_get_singleton(); + + zn_move_cursor_grab_start(server->scene->cursor, self->view); +} + +static void +zn_xwayland_view_handle_resize(struct wl_listener *listener, void *data) +{ + struct zn_xwayland_view *self = + zn_container_of(listener, self, resize_listener); + struct wlr_xwayland_resize_event *event = data; + struct zn_server *server = zn_server_get_singleton(); + + zn_resize_cursor_grab_start(server->scene->cursor, self->view, event->edges); +} + +static void +zn_xwayland_view_handle_maximize(struct wl_listener *listener, void *data) +{ + UNUSED(data); + struct zn_xwayland_view *self = + zn_container_of(listener, self, maximize_listener); + if (!self->view) { + return; + } + zn_view_set_maximized(self->view, !self->view->maximize_status.maximized); +} + +static void +zn_xwayland_view_handle_map(struct wl_listener *listener, void *data) +{ + UNUSED(data); + + struct zn_server *server = zn_server_get_singleton(); + struct zn_xwayland_view *self = zn_container_of(listener, self, map_listener); + + if (!zn_assert(self->view == NULL, "xwayland has already mapped")) { + return; + } + + self->view = zn_view_create( + self->xwayland_surface->surface, &zn_xwayland_view_impl, self); + zn_scene_new_view(server->scene, self->view); +} + +static void +zn_xwayland_view_handle_unmap(struct wl_listener *listener, void *data) +{ + UNUSED(data); + + struct zn_xwayland_view *self = + zn_container_of(listener, self, unmap_listener); + + if (!zn_assert(self->view, "xwayland has not mapped yet")) { + return; + } + + zn_view_destroy(self->view); + self->view = NULL; +} + +static void +zn_xwayland_view_handle_xwayland_surface_destroy( + struct wl_listener *listener, void *data) +{ + UNUSED(data); + + struct zn_xwayland_view *self = + zn_container_of(listener, self, wlr_xwayland_surface_destroy_listener); + + zn_xwayland_view_destroy(self); +} + +struct zn_xwayland_view * +zn_xwayland_view_create(struct wlr_xwayland_surface *xwayland_surface) +{ + struct zn_xwayland_view *self; + + self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->xwayland_surface = xwayland_surface; + self->view = NULL; + + self->wlr_xwayland_surface_destroy_listener.notify = + zn_xwayland_view_handle_xwayland_surface_destroy; + wl_signal_add(&xwayland_surface->events.destroy, + &self->wlr_xwayland_surface_destroy_listener); + + self->map_listener.notify = zn_xwayland_view_handle_map; + wl_signal_add(&xwayland_surface->events.map, &self->map_listener); + + self->unmap_listener.notify = zn_xwayland_view_handle_unmap; + wl_signal_add(&xwayland_surface->events.unmap, &self->unmap_listener); + + self->move_listener.notify = zn_xwayland_view_handle_move; + wl_signal_add(&xwayland_surface->events.request_move, &self->move_listener); + + self->resize_listener.notify = zn_xwayland_view_handle_resize; + wl_signal_add( + &xwayland_surface->events.request_resize, &self->resize_listener); + + self->maximize_listener.notify = zn_xwayland_view_handle_maximize; + wl_signal_add( + &xwayland_surface->events.request_maximize, &self->maximize_listener); + + return self; + +err: + return NULL; +} + +static void +zn_xwayland_view_destroy(struct zn_xwayland_view *self) +{ + wl_list_remove(&self->unmap_listener.link); + wl_list_remove(&self->map_listener.link); + wl_list_remove(&self->move_listener.link); + wl_list_remove(&self->resize_listener.link); + wl_list_remove(&self->maximize_listener.link); + wl_list_remove(&self->wlr_xwayland_surface_destroy_listener.link); + if (self->view) zn_view_destroy(self->view); + free(self); +} diff --git a/zen/server.c b/zen/server.c index 64feda96..c52c1dc5 100644 --- a/zen/server.c +++ b/zen/server.c @@ -240,6 +240,7 @@ zn_server_create(struct wl_display *display) setenv("WAYLAND_DISPLAY", self->socket, true); xdg = getenv("XDG_RUNTIME_DIR"); zn_debug("WAYLAND_DISPLAY=%s", self->socket); + zn_debug("DISPLAY=%s", self->screen_compositor->xwayland->display_name); zn_debug("XDG_RUNTIME_DIR=%s", xdg); self->shell = zn_shell_create(self->display, self->scene); diff --git a/zen/view.c b/zen/view.c index 11428e30..3b21d132 100644 --- a/zen/view.c +++ b/zen/view.c @@ -118,16 +118,19 @@ zn_view_handle_commit(struct wl_listener *listener, void *data) pixman_region32_fini(&damage); // FIXME: add damages of synced subsurfaces - const uint32_t last_serial = self->impl->get_current_configure_serial(self); - if (self->maximize_status.changed_serial == last_serial) { - self->maximize_status.changed_serial = 0; - if (self->maximize_status.maximized) { - zn_view_move(self, self->board, 0, 0); - } else { - zn_view_move(self, self->board, self->maximize_status.reset_box.x, - self->maximize_status.reset_box.y); + if (self->maximize_status.maximize_status_changed) { + if (self->maximize_status.changed_serial == last_serial || + last_serial == 0) { + self->maximize_status.maximize_status_changed = false; + self->maximize_status.changed_serial = 0; + if (self->maximize_status.maximized) { + zn_view_move(self, self->board, 0, 0); + } else { + zn_view_move(self, self->board, self->maximize_status.reset_box.x, + self->maximize_status.reset_box.y); + } } } @@ -143,7 +146,7 @@ zn_view_handle_commit(struct wl_listener *listener, void *data) } zn_view_move(self, self->board, self->x + dx, self->y + dy); - if (self->resize_status.last_serial == last_serial) { + if (self->resize_status.last_serial == last_serial || last_serial == 0) { self->resize_status.resizing = false; } } @@ -213,6 +216,10 @@ zn_view_bring_to_front(struct zn_view *self) wl_list_insert(self->board->view_list.prev, &self->board_link); zn_board_rearrange_view(self->board); + if (self->impl->restack) { + self->impl->restack(self, XCB_STACK_MODE_ABOVE); + } + zn_view_damage_whole(self); } @@ -238,6 +245,10 @@ zn_view_move(struct zn_view *self, struct zn_board *board, double x, double y) self->x = x; self->y = y; + if (self->impl->set_position) { + self->impl->set_position(self, x, y); + } + self->board = board; zn_view_damage_whole(self); @@ -249,7 +260,9 @@ void zn_view_set_maximized(struct zn_view *self, bool maximized) { if (!self->board || self->maximize_status.maximized == maximized) { - self->impl->schedule_configure(self); + if (self->impl->schedule_configure) { + self->impl->schedule_configure(self); + } return; } @@ -266,6 +279,7 @@ zn_view_set_maximized(struct zn_view *self, bool maximized) self->maximize_status.changed_serial = self->impl->set_size(self, width, height); self->maximize_status.maximized = maximized; + self->maximize_status.maximize_status_changed = true; } struct zn_view *