diyac/diyac.c

268 lines
8.9 KiB
C
Raw Normal View History

2024-03-24 23:21:34 +01:00
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
2024-03-30 00:18:51 +01:00
#include <stdlib.h>
2024-03-24 23:21:34 +01:00
#include <unistd.h>
2024-03-30 00:18:51 +01:00
#include <getopt.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
2024-03-24 23:21:34 +01:00
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_compositor.h>
2024-03-30 00:18:51 +01:00
#include <wlr/types/wlr_subcompositor.h>
2024-03-24 23:21:34 +01:00
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_xcursor_manager.h>
2024-03-30 00:18:51 +01:00
#include "diyac.h"
#include "output.h"
#include "xdg.h"
#include "cursor.h"
#include "seat.h"
2024-03-24 23:21:34 +01:00
2024-03-30 00:18:51 +01:00
void diyac_focus_toplevel(struct diyac_toplevel *toplevel, struct wlr_surface *surface)
{
2024-03-24 23:21:34 +01:00
/* Note: this function only deals with keyboard focus. */
2024-03-30 00:18:51 +01:00
if (toplevel == NULL)
{
2024-03-24 23:21:34 +01:00
return;
}
2024-03-30 00:18:51 +01:00
struct diyac_server *server = toplevel->server;
struct wlr_seat *seat = server->seat.wlr_seat;
2024-03-24 23:21:34 +01:00
struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
2024-03-30 00:18:51 +01:00
if (prev_surface == surface)
{
2024-03-24 23:21:34 +01:00
/* Don't re-focus an already focused surface. */
return;
}
2024-03-30 00:18:51 +01:00
if (prev_surface)
{
2024-03-24 23:21:34 +01:00
/*
* Deactivate the previously focused surface. This lets the client know
* it no longer has focus and the client will repaint accordingly, e.g.
* stop displaying a caret.
*/
struct wlr_xdg_toplevel *prev_toplevel =
wlr_xdg_toplevel_try_from_wlr_surface(prev_surface);
2024-03-30 00:18:51 +01:00
if (prev_toplevel != NULL)
{
2024-03-24 23:21:34 +01:00
wlr_xdg_toplevel_set_activated(prev_toplevel, false);
}
}
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
/* 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(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.
*/
2024-03-30 00:18:51 +01:00
if (keyboard != NULL)
{
2024-03-24 23:21:34 +01:00
wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface,
2024-03-30 00:18:51 +01:00
keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
2024-03-24 23:21:34 +01:00
}
}
2024-03-30 00:18:51 +01:00
struct diyac_toplevel *diyac_toplevel_at(
struct diyac_server *server, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy)
{
2024-03-24 23:21:34 +01:00
/* 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
2024-03-30 00:18:51 +01:00
* surface in the surface tree of a diyac_toplevel. */
2024-03-24 23:21:34 +01:00
struct wlr_scene_node *node = wlr_scene_node_at(
&server->scene->tree.node, lx, ly, sx, sy);
2024-03-30 00:18:51 +01:00
if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER)
{
2024-03-24 23:21:34 +01:00
return NULL;
}
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
struct wlr_scene_surface *scene_surface =
wlr_scene_surface_try_from_buffer(scene_buffer);
2024-03-30 00:18:51 +01:00
if (!scene_surface)
{
2024-03-24 23:21:34 +01:00
return NULL;
}
*surface = scene_surface->surface;
2024-03-30 00:18:51 +01:00
/* Find the node corresponding to the diyac_toplevel at the root of this
2024-03-24 23:21:34 +01:00
* surface tree, it is the only one for which we set the data field. */
struct wlr_scene_tree *tree = node->parent;
2024-03-30 00:18:51 +01:00
while (tree != NULL && tree->node.data == NULL)
{
2024-03-24 23:21:34 +01:00
tree = tree->node.parent;
}
return tree->node.data;
}
2024-03-30 00:18:51 +01:00
int main(int argc, char *argv[])
{
wlr_log_init(WLR_INFO, NULL);
2024-03-24 23:21:34 +01:00
char *startup_cmd = NULL;
int c;
2024-03-30 00:18:51 +01:00
while ((c = getopt(argc, argv, "s:h")) != -1)
{
switch (c)
{
2024-03-24 23:21:34 +01:00
case 's':
startup_cmd = optarg;
break;
default:
printf("Usage: %s [-s startup command]\n", argv[0]);
return 0;
}
}
2024-03-30 00:18:51 +01:00
if (optind < argc)
{
2024-03-24 23:21:34 +01:00
printf("Usage: %s [-s startup command]\n", argv[0]);
return 0;
}
2024-03-30 00:18:51 +01:00
struct diyac_server server = {0};
2024-03-24 23:21:34 +01:00
/* 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();
/* The backend is a wlroots feature which abstracts the underlying input and
* 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, NULL);
2024-03-30 00:18:51 +01:00
if (server.backend == NULL)
{
2024-03-24 23:21:34 +01:00
wlr_log(WLR_ERROR, "failed to create wlr_backend");
return 1;
}
/* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
* can also specify a renderer using the WLR_RENDERER env var.
* The renderer is responsible for defining the various pixel formats it
* supports for shared memory, this configures that for clients. */
server.renderer = wlr_renderer_autocreate(server.backend);
2024-03-30 00:18:51 +01:00
if (server.renderer == NULL)
{
2024-03-24 23:21:34 +01:00
wlr_log(WLR_ERROR, "failed to create wlr_renderer");
return 1;
}
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
/* Autocreates an allocator for us.
* 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,
2024-03-30 00:18:51 +01:00
server.renderer);
if (server.allocator == NULL)
{
2024-03-24 23:21:34 +01:00
wlr_log(WLR_ERROR, "failed to create wlr_allocator");
return 1;
}
/* This creates some hands-off wlroots interfaces. The compositor is
* necessary for clients to allocate surfaces, the subcompositor allows to
* assign the role of subsurfaces to surfaces and the data device manager
* handles the clipboard. Each of these wlroots interfaces has room for you
* 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, 5, server.renderer);
wlr_subcompositor_create(server.wl_display);
wlr_data_device_manager_create(server.wl_display);
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server.output_layout = wlr_output_layout_create();
/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server.outputs);
2024-03-30 00:18:51 +01:00
server.new_output.notify = diyac_server_new_output;
2024-03-24 23:21:34 +01:00
wl_signal_add(&server.backend->events.new_output, &server.new_output);
/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
* is add things that should be rendered to the scene graph at the proper
* positions and then call wlr_scene_output_commit() to render a frame if
* necessary.
*/
server.scene = wlr_scene_create();
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
* https://drewdevault.com/2018/07/29/Wayland-shells.html.
*/
wl_list_init(&server.toplevels);
server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3);
2024-03-30 00:18:51 +01:00
server.new_xdg_surface.notify = diyac_new_xdg_surface;
2024-03-24 23:21:34 +01:00
wl_signal_add(&server.xdg_shell->events.new_surface,
2024-03-30 00:18:51 +01:00
&server.new_xdg_surface);
2024-03-24 23:21:34 +01:00
2024-03-30 00:18:51 +01:00
/**
* TODO
2024-03-24 23:21:34 +01:00
*
2024-03-30 00:18:51 +01:00
server.new_layer_surface.notify = server_new_layer_surface;
wl_signal_add(&server.layer_shell->events.new_surface,
&server.new_layer_surface);
*/
diyac_init_cursor_manager(&server);
2024-03-24 23:21:34 +01:00
/*
* Configures a seat, which is a single "seat" at which a user sits and
* operates the computer. This conceptually includes up to one keyboard,
* pointer, touch, and drawing tablet device. We also rig up a listener to
* let us know when new input devices are available on the backend.
*/
2024-03-30 00:18:51 +01:00
wl_list_init(&server.seat.keyboards);
diyac_init_seat(&server);
2024-03-24 23:21:34 +01:00
/* Add a Unix socket to the Wayland display. */
const char *socket = wl_display_add_socket_auto(server.wl_display);
2024-03-30 00:18:51 +01:00
if (!socket)
{
2024-03-24 23:21:34 +01:00
wlr_backend_destroy(server.backend);
return 1;
}
/* Start the backend. This will enumerate outputs and inputs, become the DRM
* master, etc */
2024-03-30 00:18:51 +01:00
if (!wlr_backend_start(server.backend))
{
2024-03-24 23:21:34 +01:00
wlr_backend_destroy(server.backend);
wl_display_destroy(server.wl_display);
return 1;
}
/* Set the WAYLAND_DISPLAY environment variable to our socket and run the
* startup command if requested. */
setenv("WAYLAND_DISPLAY", socket, true);
2024-03-30 00:18:51 +01:00
if (startup_cmd)
{
if (fork() == 0)
{
2024-03-24 23:21:34 +01:00
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
}
}
/* Run the Wayland event loop. This does not return until you exit the
* compositor. Starting the backend rigged up all of the necessary event
* loop configuration to listen to libinput events, DRM events, generate
* frame events at the refresh rate, and so on. */
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
socket);
wl_display_run(server.wl_display);
/* 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);
2024-03-30 00:18:51 +01:00
wlr_xcursor_manager_destroy(server.seat.cursor_mgr);
2024-03-24 23:21:34 +01:00
wlr_output_layout_destroy(server.output_layout);
wl_display_destroy(server.wl_display);
return 0;
}