#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include "xdg.h" #include "cursor.h" static void begin_interactive(struct diyac_toplevel *toplevel, enum diyac_cursor_mode mode, uint32_t edges) { /* This function sets up an interactive move or resize operation, where the * compositor stops propegating pointer events to clients and instead * consumes them itself, to move or resize windows. */ struct diyac_server *server = toplevel->server; struct wlr_surface *focused_surface = server->seat.wlr_seat->pointer_state.focused_surface; if (toplevel->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } server->grabbed_toplevel = toplevel; server->seat.cursor_mode = mode; if (mode == DIYAC_CURSOR_MOVE) { server->grab_x = server->seat.cursor->x - toplevel->scene_tree->node.x; server->grab_y = server->seat.cursor->y - toplevel->scene_tree->node.y; } else { struct wlr_box geo_box; wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); double border_x = (toplevel->scene_tree->node.x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); double border_y = (toplevel->scene_tree->node.y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->seat.cursor->x - border_x; server->grab_y = server->seat.cursor->y - border_y; server->grab_geobox = geo_box; server->grab_geobox.x += toplevel->scene_tree->node.x; server->grab_geobox.y += toplevel->scene_tree->node.y; server->resize_edges = edges; } } static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, map); wl_list_insert(&toplevel->server->toplevels, &toplevel->link); diyac_focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); } static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); /* Reset the cursor mode if the grabbed toplevel was unmapped. */ if (toplevel == toplevel->server->grabbed_toplevel) { diyac_reset_cursor_mode(toplevel->server); } wl_list_remove(&toplevel->link); } static void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive * move, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); begin_interactive(toplevel, DIYAC_CURSOR_MOVE, 0); } static void xdg_toplevel_request_resize( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive * resize, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct wlr_xdg_toplevel_resize_event *event = data; struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); begin_interactive(toplevel, DIYAC_CURSOR_RESIZE, event->edges); } static void xdg_toplevel_request_maximize( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to maximize itself, * typically because the user clicked on the maximize button on * client-side decorations. diyac doesn't support maximization, but * to conform to xdg-shell protocol we still must send a configure. * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize); // wlr_wl_output* output = get_wl_output_from_surface(struct wlr_wl_backend *wl, // struct wl_surface *surface); wlr_log(WLR_ERROR, "request maximize %dx%d", toplevel->output->width, toplevel->output->height); wlr_scene_node_set_position(&toplevel->scene_tree->node,0,0); wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->output->width, toplevel->output->height); //wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void xdg_toplevel_request_fullscreen( struct wl_listener *listener, void *data) { /* Just as with request_maximize, we must send a configure here. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_fullscreen); wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); wl_list_remove(&toplevel->request_maximize.link); wl_list_remove(&toplevel->request_fullscreen.link); free(toplevel); } void diyac_new_xdg_surface(struct wl_listener *listener, void *data) { /* This event is raised when wlr_xdg_shell receives a new xdg surface from a * client, either a toplevel (application window) or popup. */ struct diyac_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; /* We must add xdg popups to the scene graph so they get rendered. The * wlroots scene graph provides a helper for this, but to use it we must * provide the proper parent scene node of the xdg popup. To enable this, * we always set the user data field of xdg_surfaces to the corresponding * scene node. */ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); assert(parent != NULL); struct wlr_scene_tree *parent_tree = parent->data; xdg_surface->data = wlr_scene_xdg_surface_create( parent_tree, xdg_surface); return; } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); /* Allocate a diyac_toplevel for this surface */ struct diyac_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->server = server; toplevel->xdg_toplevel = xdg_surface->toplevel; toplevel->scene_tree = wlr_scene_xdg_surface_create( &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); toplevel->scene_tree->node.data = toplevel; xdg_surface->data = toplevel->scene_tree; struct wlr_output *output = wlr_output_layout_output_at( server->output_layout, server->seat.cursor->x, server->seat.cursor->y); toplevel->output = output; if (!output) { wlr_log(WLR_ERROR, "No output available to assign layer surface"); } /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); toplevel->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); toplevel->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); /* cotd */ struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; toplevel->request_move.notify = xdg_toplevel_request_move; wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); toplevel->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); toplevel->request_maximize.notify = xdg_toplevel_request_maximize; wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize); toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); }