diff options
author | Erik Reider <[email protected]> | 2023-12-30 11:25:16 +0100 |
---|---|---|
committer | Erik Reider <[email protected]> | 2024-01-02 11:21:58 +0100 |
commit | 0b52aa9d137b03017313e028accc92dc5d536440 (patch) | |
tree | 06649993526e0a339fff34f0ae8b4c8ec2fa4d13 /tinywl | |
parent | b929a2bbadf467864796ad4ec90882ce86cfebff (diff) |
Initial rebase without effects
Diffstat (limited to 'tinywl')
-rw-r--r-- | tinywl/tinywl.c | 407 |
1 files changed, 195 insertions, 212 deletions
diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 56091e3..adc7b38 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -42,10 +42,11 @@ struct tinywl_server { struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_scene *scene; + struct wlr_scene_output_layout *scene_layout; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; - struct wl_list views; + struct wl_list toplevels; struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; @@ -61,7 +62,7 @@ struct tinywl_server { struct wl_listener request_set_selection; struct wl_list keyboards; enum tinywl_cursor_mode cursor_mode; - struct tinywl_view *grabbed_view; + struct tinywl_toplevel *grabbed_toplevel; double grab_x, grab_y; struct wlr_box grab_geobox; uint32_t resize_edges; @@ -76,10 +77,11 @@ struct tinywl_output { struct tinywl_server *server; struct wlr_output *wlr_output; struct wl_listener frame; + struct wl_listener request_state; struct wl_listener destroy; }; -struct tinywl_view { +struct tinywl_toplevel { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_toplevel *xdg_toplevel; @@ -91,9 +93,6 @@ struct tinywl_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; - int x, y; - - struct wlr_addon addon; float opacity; int corner_radius; @@ -110,50 +109,12 @@ struct tinywl_keyboard { struct wl_listener destroy; }; -static void tinywl_view_destroy(struct tinywl_view *view) { - wl_list_remove(&view->map.link); - wl_list_remove(&view->unmap.link); - wl_list_remove(&view->destroy.link); - wl_list_remove(&view->request_move.link); - wl_list_remove(&view->request_resize.link); - wl_list_remove(&view->request_maximize.link); - wl_list_remove(&view->request_fullscreen.link); - - free(view); -} - -static void tinywl_view_addon_handle_destroy(struct wlr_addon *addon) { - struct tinywl_view *view = wl_container_of(addon, view, addon); - - tinywl_view_destroy(view); -} -static const struct wlr_addon_interface tinywl_view_addon_impl = { - .name = "tinywl_view", - .destroy = tinywl_view_addon_handle_destroy, -}; - -static void tinywl_view_addon_assign(struct tinywl_view *view, struct wlr_addon_set *addons, - const void * owner) { - wlr_addon_init(&view->addon, addons, owner, &tinywl_view_addon_impl); -} - -static struct tinywl_view *tinywl_view_addon_get(struct wlr_addon_set *addons, - const void * owner) { - struct wlr_addon *addon = - wlr_addon_find(addons, owner, &tinywl_view_addon_impl); - if (addon == NULL) { - return NULL; - } - struct tinywl_view *view = wl_container_of(addon, view, addon); - return view; -} - -static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { +static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ - if (view == NULL) { + if (toplevel == NULL) { return; } - struct tinywl_server *server = view->server; + struct tinywl_server *server = toplevel->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; if (prev_surface == surface) { @@ -166,25 +127,26 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { * it no longer has focus and the client will repaint accordingly, e.g. * stop displaying a caret. */ - struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( - seat->keyboard_state.focused_surface); - assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - wlr_xdg_toplevel_set_activated(previous->toplevel, false); + struct wlr_xdg_toplevel *prev_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); + if (prev_toplevel != NULL) { + wlr_xdg_toplevel_set_activated(prev_toplevel, false); + } } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - /* Move the view to the front */ - wlr_scene_node_raise_to_top(&view->scene_tree->node); - wl_list_remove(&view->link); - wl_list_insert(&server->views, &view->link); + /* Move the toplevel to the front */ + wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); + wl_list_remove(&toplevel->link); + wl_list_insert(&server->toplevels, &toplevel->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true); + wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ if (keyboard != NULL) { - wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, + wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } @@ -220,13 +182,13 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { wl_display_terminate(server->wl_display); break; case XKB_KEY_F1: - /* Cycle to the next view */ - if (wl_list_length(&server->views) < 2) { + /* Cycle to the next toplevel */ + if (wl_list_length(&server->toplevels) < 2) { break; } - struct tinywl_view *next_view = wl_container_of( - server->views.prev, next_view, link); - focus_view(next_view, next_view->xdg_toplevel->base->surface); + struct tinywl_toplevel *next_toplevel = + wl_container_of(server->toplevels.prev, next_toplevel, link); + focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); break; default: return false; @@ -287,8 +249,7 @@ static void server_new_keyboard(struct tinywl_server *server, struct wlr_input_device *device) { struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); - struct tinywl_keyboard *keyboard = - calloc(1, sizeof(struct tinywl_keyboard)); + struct tinywl_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->server = server; keyboard->wlr_keyboard = wlr_keyboard; @@ -382,12 +343,12 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static struct tinywl_view *desktop_view_at( +static struct tinywl_toplevel *desktop_toplevel_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { /* This returns the topmost node in the scene at the given layout coords. - * we only care about surface nodes as we are specifically looking for a - * surface in the surface tree of a tinywl_view. */ + * We only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_toplevel. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->tree.node, lx, ly, sx, sy); if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { @@ -395,13 +356,13 @@ static struct tinywl_view *desktop_view_at( } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_buffer(scene_buffer); + wlr_scene_surface_try_from_buffer(scene_buffer); if (!scene_surface) { return NULL; } *surface = scene_surface->surface; - /* Find the node corresponding to the tinywl_view at the root of this + /* Find the node corresponding to the tinywl_toplevel at the root of this * surface tree, it is the only one for which we set the data field. */ struct wlr_scene_tree *tree = node->parent; while (tree != NULL && tree->node.data == NULL) { @@ -413,29 +374,29 @@ static struct tinywl_view *desktop_view_at( static void reset_cursor_mode(struct tinywl_server *server) { /* Reset the cursor mode to passthrough. */ server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - server->grabbed_view = NULL; + server->grabbed_toplevel = NULL; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { - /* Move the grabbed view to the new position. */ - struct tinywl_view *view = server->grabbed_view; - view->x = server->cursor->x - server->grab_x; - view->y = server->cursor->y - server->grab_y; - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); + /* Move the grabbed toplevel to the new position. */ + struct tinywl_toplevel *toplevel = server->grabbed_toplevel; + wlr_scene_node_set_position(&toplevel->scene_tree->node, + server->cursor->x - server->grab_x, + server->cursor->y - server->grab_y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { /* - * Resizing the grabbed view can be a little bit complicated, because we - * could be resizing from any corner or edge. This not only resizes the view - * on one or two axes, but can also move the view if you resize from the top - * or left edges (or top-left corner). + * Resizing the grabbed toplevel can be a little bit complicated, because we + * could be resizing from any corner or edge. This not only resizes the + * toplevel on one or two axes, but can also move the toplevel if you resize + * from the top or left edges (or top-left corner). * - * Note that I took some shortcuts here. In a more fleshed-out compositor, - * you'd wait for the client to prepare a buffer at the new size, then - * commit any movement that was prepared. + * Note that some shortcuts are taken here. In a more fleshed-out + * compositor, you'd wait for the client to prepare a buffer at the new + * size, then commit any movement that was prepared. */ - struct tinywl_view *view = server->grabbed_view; + struct tinywl_toplevel *toplevel = server->grabbed_toplevel; double border_x = server->cursor->x - server->grab_x; double border_y = server->cursor->y - server->grab_y; int new_left = server->grab_geobox.x; @@ -467,14 +428,13 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); - view->x = new_left - geo_box.x; - view->y = new_top - geo_box.y; - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); + wlr_scene_node_set_position(&toplevel->scene_tree->node, + new_left - geo_box.x, new_top - geo_box.y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; - wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { @@ -487,18 +447,17 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { return; } - /* Otherwise, find the view under the pointer and send the event along. */ + /* Otherwise, find the toplevel under the pointer and send the event along. */ double sx, sy; struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; - struct tinywl_view *view = desktop_view_at(server, + struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); - if (!view) { - /* If there's no view under the cursor, set the cursor image to a + if (!toplevel) { + /* If there's no toplevel under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it - * around the screen, not over any views. */ - wlr_xcursor_manager_set_cursor_image( - server->cursor_mgr, "left_ptr", server->cursor); + * around the screen, not over any toplevels. */ + wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); } if (surface) { /* @@ -564,14 +523,14 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface = NULL; - struct tinywl_view *view = desktop_view_at(server, + struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); } else { /* Focus that client if the button was _pressed_ */ - focus_view(view, surface); + focus_toplevel(toplevel, surface); } } @@ -599,47 +558,45 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { } static void output_configure_scene(struct wlr_scene_node *node, - struct tinywl_view *view) { + struct tinywl_toplevel *toplevel) { if (!node->enabled) { return; } - struct tinywl_view *_view = tinywl_view_addon_get(&node->addons, node); - if (_view) { - view = _view; + struct tinywl_toplevel *_toplevel = node->data; + if (_toplevel) { + toplevel = _toplevel; } if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface * scene_surface - = wlr_scene_surface_from_buffer(buffer); + struct wlr_scene_surface * scene_surface = + wlr_scene_surface_try_from_buffer(buffer); if (!scene_surface) { return; } - struct wlr_xdg_surface *xdg_surface = NULL; - if (wlr_surface_is_xdg_surface(scene_surface->surface)) { - xdg_surface = wlr_xdg_surface_from_wlr_surface(scene_surface->surface); - } + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_try_from_wlr_surface(scene_surface->surface); - if (view && + if (toplevel && xdg_surface && xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { // TODO: Be able to set whole decoration_data instead of calling // each individually? - wlr_scene_buffer_set_opacity(buffer, view->opacity); + wlr_scene_buffer_set_opacity(buffer, toplevel->opacity); - if (!wlr_surface_is_subsurface(xdg_surface->surface)) { - wlr_scene_buffer_set_corner_radius(buffer, view->corner_radius); - wlr_scene_buffer_set_shadow_data(buffer, view->shadow_data); + if (!wlr_subsurface_try_from_wlr_surface(xdg_surface->surface)) { + wlr_scene_buffer_set_corner_radius(buffer, toplevel->corner_radius); + wlr_scene_buffer_set_shadow_data(buffer, toplevel->shadow_data); } } } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *tree = wl_container_of(node, tree, node); struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { - output_configure_scene(node, view); + output_configure_scene(node, toplevel); } } } @@ -656,17 +613,27 @@ static void output_frame(struct wl_listener *listener, void *data) { output_configure_scene(&scene_output->scene->tree.node, NULL); /* Render the scene if needed and commit the output */ - wlr_scene_output_commit(scene_output); + wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(scene_output, &now); } +static void output_request_state(struct wl_listener *listener, void *data) { + /* This function is called when the backend requests a new state for + * the output. For example, Wayland and X11 backends request a new mode + * when the output window is resized. */ + struct tinywl_output *output = wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + static void output_destroy(struct wl_listener *listener, void *data) { struct tinywl_output *output = wl_container_of(listener, output, destroy); wl_list_remove(&output->frame.link); + wl_list_remove(&output->request_state.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); free(output); @@ -683,30 +650,39 @@ static void server_new_output(struct wl_listener *listener, void *data) { * and our renderer. Must be done once, before commiting the output */ wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* The output may be disabled, switch it on. */ + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We * just pick the monitor's preferred mode, a more sophisticated compositor * would let the user configure it. */ - if (!wl_list_empty(&wlr_output->modes)) { - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - wlr_output_set_mode(wlr_output, mode); - wlr_output_enable(wlr_output, true); - if (!wlr_output_commit(wlr_output)) { - return; - } + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode != NULL) { + wlr_output_state_set_mode(&state, mode); } + /* Atomically applies the new output state. */ + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + /* Allocates and configures our state for this output */ - struct tinywl_output *output = - calloc(1, sizeof(struct tinywl_output)); + struct tinywl_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; - /* Sets up a listener for the frame notify event. */ + + /* Sets up a listener for the frame event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); - /* Sets up a listener for the destroy notify event. */ + /* Sets up a listener for the state request event. */ + output->request_state.notify = output_request_state; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + + /* Sets up a listener for the destroy event. */ output->destroy.notify = output_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); @@ -721,70 +697,81 @@ static void server_new_output(struct wl_listener *listener, void *data) { * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ - wlr_output_layout_add_auto(server->output_layout, wlr_output); + struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, + wlr_output); + struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); + wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); } static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ - struct tinywl_view *view = wl_container_of(listener, view, map); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map); - wl_list_insert(&view->server->views, &view->link); + wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - focus_view(view, view->xdg_toplevel->base->surface); + 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 tinywl_view *view = wl_container_of(listener, view, unmap); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); - /* Reset the cursor mode if the grabbed view was unmapped. */ - if (view == view->server->grabbed_view) { - reset_cursor_mode(view->server); + /* Reset the cursor mode if the grabbed toplevel was unmapped. */ + if (toplevel == toplevel->server->grabbed_toplevel) { + reset_cursor_mode(toplevel->server); } - wl_list_remove(&view->link); + wl_list_remove(&toplevel->link); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { - /* Called when the surface is destroyed and should never be shown again. */ - struct tinywl_view *view = wl_container_of(listener, view, destroy); - - tinywl_view_destroy(view); + /* Called when the xdg_toplevel is destroyed. */ + struct tinywl_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); } -static void begin_interactive(struct tinywl_view *view, +static void begin_interactive(struct tinywl_toplevel *toplevel, enum tinywl_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 tinywl_server *server = view->server; + struct tinywl_server *server = toplevel->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_toplevel->base->surface != + if (toplevel->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } - server->grabbed_view = view; + server->grabbed_toplevel = toplevel; server->cursor_mode = mode; if (mode == TINYWL_CURSOR_MOVE) { - server->grab_x = server->cursor->x - view->x; - server->grab_y = server->cursor->y - view->y; + server->grab_x = server->cursor->x - toplevel->scene_tree->node.x; + server->grab_y = server->cursor->y - toplevel->scene_tree->node.y; } else { struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); - double border_x = (view->x + geo_box.x) + + double border_x = (toplevel->scene_tree->node.x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (view->y + geo_box.y) + + double border_y = (toplevel->scene_tree->node.y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; server->grab_geobox = geo_box; - server->grab_geobox.x += view->x; - server->grab_geobox.y += view->y; + server->grab_geobox.x += toplevel->scene_tree->node.x; + server->grab_geobox.y += toplevel->scene_tree->node.y; server->resize_edges = edges; } @@ -797,8 +784,8 @@ static void xdg_toplevel_request_move( * 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 tinywl_view *view = wl_container_of(listener, view, request_move); - begin_interactive(view, TINYWL_CURSOR_MOVE, 0); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); + begin_interactive(toplevel, TINYWL_CURSOR_MOVE, 0); } static void xdg_toplevel_request_resize( @@ -809,8 +796,8 @@ static void xdg_toplevel_request_resize( * 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 tinywl_view *view = wl_container_of(listener, view, request_resize); - begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); + begin_interactive(toplevel, TINYWL_CURSOR_RESIZE, event->edges); } static void xdg_toplevel_request_maximize( @@ -820,17 +807,17 @@ static void xdg_toplevel_request_maximize( * client-side decorations. tinywl 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 tinywl_view *view = - wl_container_of(listener, view, request_maximize); - wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct tinywl_toplevel *toplevel = + wl_container_of(listener, toplevel, request_maximize); + 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 tinywl_view *view = - wl_container_of(listener, view, request_fullscreen); - wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct tinywl_toplevel *toplevel = + wl_container_of(listener, toplevel, request_fullscreen); + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void server_new_xdg_surface(struct wl_listener *listener, void *data) { @@ -846,8 +833,9 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { * 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_from_wlr_surface( - xdg_surface->popup->parent); + 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); @@ -855,46 +843,42 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - /* Allocate a tinywl_view for this surface */ - struct tinywl_view *view = - calloc(1, sizeof(struct tinywl_view)); - view->server = server; - view->xdg_toplevel = xdg_surface->toplevel; - view->scene_tree = wlr_scene_xdg_surface_create( - &view->server->scene->tree, view->xdg_toplevel->base); - view->scene_tree->node.data = view; - xdg_surface->data = view->scene_tree; - - /* Add the view to the trees addon */ - tinywl_view_addon_assign(view, &view->scene_tree->node.addons, &view->scene_tree->node); + /* Allocate a tinywl_toplevel for this surface */ + struct tinywl_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; /* Set the scene_nodes decoration data */ - view->opacity = 1; - view->corner_radius = 20; - view->shadow_data = shadow_data_get_default(); - view->shadow_data.enabled = true; - memcpy(view->shadow_data.color, (float[]) {1.0f, 0.0f, 0.0f, 1.0f}, sizeof(float[4])); + toplevel->opacity = 1; + toplevel->corner_radius = 20; + toplevel->shadow_data = shadow_data_get_default(); + toplevel->shadow_data.enabled = true; + memcpy(toplevel->shadow_data.color, (float[]) {1.0f, 0.0f, 0.0f, 1.0f}, sizeof(float[4])); /* Listen to the various events it can emit */ - view->map.notify = xdg_toplevel_map; - wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = xdg_toplevel_unmap; - wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = xdg_toplevel_destroy; - wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + 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 *toplevel = xdg_surface->toplevel; - view->request_move.notify = xdg_toplevel_request_move; - wl_signal_add(&toplevel->events.request_move, &view->request_move); - view->request_resize.notify = xdg_toplevel_request_resize; - wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - view->request_maximize.notify = xdg_toplevel_request_maximize; - wl_signal_add(&toplevel->events.request_maximize, - &view->request_maximize); - view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; - wl_signal_add(&toplevel->events.request_fullscreen, - &view->request_fullscreen); + 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); } int main(int argc, char *argv[]) { @@ -917,7 +901,7 @@ int main(int argc, char *argv[]) { return 0; } - struct tinywl_server server; + struct tinywl_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); @@ -925,7 +909,7 @@ int main(int argc, char *argv[]) { * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ - server.backend = wlr_backend_autocreate(server.wl_display); + server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (server.backend == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; @@ -947,7 +931,7 @@ int main(int argc, char *argv[]) { * The allocator is the bridge between the renderer and the backend. It * handles the buffer creation, allowing wlroots to render onto the * screen */ - server.allocator = wlr_allocator_autocreate(server.backend, + server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); if (server.allocator == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_allocator"); @@ -961,7 +945,7 @@ int main(int argc, char *argv[]) { * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ - wlr_compositor_create(server.wl_display, server.renderer); + wlr_compositor_create(server.wl_display, 5, server.renderer); wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); @@ -982,15 +966,13 @@ int main(int argc, char *argv[]) { * necessary. */ server.scene = wlr_scene_create(); - wlr_scene_attach_output_layout(server.scene, server.output_layout); + server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is - * used for application windows. For more detail on shells, refer to my - * article: - * - * https://drewdevault.com/2018/07/29/Wayland-shells.html + * used for application windows. For more detail on shells, refer to + * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ - wl_list_init(&server.views); + wl_list_init(&server.toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, @@ -1006,19 +988,16 @@ int main(int argc, char *argv[]) { /* Creates an xcursor manager, another wlroots utility which loads up * Xcursor themes to source cursor images from and makes sure that cursor * images are available at all scale factors on the screen (necessary for - * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */ + * HiDPI support). */ server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); - wlr_xcursor_manager_load(server.cursor_mgr, 1); /* * wlr_cursor *only* displays an image on screen. It does not move around * when the pointer moves. However, we can attach input devices to it, and * it will generate aggregate events for all of them. In these events, we * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * - * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * moving the cursor around. More detail on this process is described in + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html. * * And more comments are sprinkled throughout the notify functions above. */ @@ -1083,8 +1062,12 @@ int main(int argc, char *argv[]) { socket); wl_display_run(server.wl_display); - /* Once wl_display_run returns, we shut down the server. */ + /* Once wl_display_run returns, we destroy all clients then shut down the + * server. */ wl_display_destroy_clients(server.wl_display); + wlr_scene_node_destroy(&server.scene->tree.node); + wlr_xcursor_manager_destroy(server.cursor_mgr); + wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); return 0; } |