1651 lines
41 KiB
C
1651 lines
41 KiB
C
#include <hikari/server.h>
|
|
|
|
#include <errno.h>
|
|
#include <libinput.h>
|
|
#include <unistd.h>
|
|
|
|
#include <wlr/backend.h>
|
|
#include <wlr/backend/headless.h>
|
|
#include <wlr/backend/libinput.h>
|
|
#include <wlr/backend/session.h>
|
|
#include <wlr/render/allocator.h>
|
|
#include <wlr/render/wlr_renderer.h>
|
|
#include <wlr/types/wlr_compositor.h>
|
|
#include <wlr/types/wlr_data_control_v1.h>
|
|
#include <wlr/types/wlr_data_device.h>
|
|
#include <wlr/types/wlr_input_device.h>
|
|
#include <wlr/types/wlr_keyboard.h>
|
|
#include <wlr/types/wlr_output_layout.h>
|
|
#include <wlr/types/wlr_primary_selection.h>
|
|
#include <wlr/types/wlr_primary_selection_v1.h>
|
|
#include <wlr/types/wlr_seat.h>
|
|
#include <wlr/types/wlr_server_decoration.h>
|
|
#include <wlr/types/wlr_xdg_output_v1.h>
|
|
#include <wlr/types/wlr_xdg_shell.h>
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
#include <wlr/types/wlr_layer_shell_v1.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_GAMMACONTROL
|
|
#include <wlr/types/wlr_gamma_control_v1.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SCREENCOPY
|
|
#include <wlr/types/wlr_screencopy_v1.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
#include <wlr/xwayland.h>
|
|
#endif
|
|
|
|
#include <hikari/border.h>
|
|
#include <hikari/command.h>
|
|
#include <hikari/configuration.h>
|
|
#include <hikari/decoration.h>
|
|
#include <hikari/exec.h>
|
|
#include <hikari/keyboard.h>
|
|
#include <hikari/layout.h>
|
|
#include <hikari/mark.h>
|
|
#include <hikari/memory.h>
|
|
#include <hikari/output.h>
|
|
#include <hikari/pointer.h>
|
|
#include <hikari/pointer_config.h>
|
|
#include <hikari/sheet.h>
|
|
#include <hikari/switch.h>
|
|
#include <hikari/workspace.h>
|
|
#include <hikari/xdg_view.h>
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
#include <hikari/xwayland_unmanaged_view.h>
|
|
#include <hikari/xwayland_view.h>
|
|
#endif
|
|
|
|
static void
|
|
add_pointer(struct hikari_server *server, struct wlr_input_device *device)
|
|
{
|
|
struct hikari_pointer *pointer = hikari_malloc(sizeof(struct hikari_pointer));
|
|
hikari_pointer_init(pointer, device);
|
|
|
|
struct hikari_pointer_config *pointer_config =
|
|
hikari_configuration_resolve_pointer_config(
|
|
hikari_configuration, device->name);
|
|
|
|
if (pointer_config != NULL) {
|
|
hikari_pointer_configure(pointer, pointer_config);
|
|
}
|
|
|
|
wlr_cursor_attach_input_device(server->cursor.wlr_cursor, device);
|
|
wlr_cursor_map_input_to_output(server->cursor.wlr_cursor, device, NULL);
|
|
}
|
|
|
|
static void
|
|
add_keyboard(struct hikari_server *server, struct wlr_input_device *device)
|
|
{
|
|
struct hikari_keyboard *keyboard =
|
|
hikari_malloc(sizeof(struct hikari_keyboard));
|
|
|
|
hikari_keyboard_init(keyboard, device);
|
|
|
|
struct hikari_keyboard_config *keyboard_config =
|
|
hikari_configuration_resolve_keyboard_config(
|
|
hikari_configuration, device->name);
|
|
|
|
assert(keyboard_config != NULL);
|
|
hikari_keyboard_configure(keyboard, keyboard_config);
|
|
|
|
hikari_keyboard_configure_bindings(
|
|
keyboard, &hikari_configuration->keyboard_binding_configs);
|
|
}
|
|
|
|
static void
|
|
add_switch(struct hikari_server *server, struct wlr_input_device *device)
|
|
{
|
|
struct hikari_switch *swtch = hikari_malloc(sizeof(struct hikari_switch));
|
|
|
|
hikari_switch_init(swtch, device);
|
|
|
|
struct hikari_switch_config *switch_config =
|
|
hikari_configuration_resolve_switch_config(
|
|
hikari_configuration, device->name);
|
|
|
|
if (switch_config != NULL) {
|
|
hikari_switch_configure(swtch, switch_config);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_input(struct hikari_server *server, struct wlr_input_device *device)
|
|
{
|
|
|
|
switch (device->type) {
|
|
case WLR_INPUT_DEVICE_KEYBOARD:
|
|
add_keyboard(server, device);
|
|
break;
|
|
|
|
case WLR_INPUT_DEVICE_POINTER:
|
|
add_pointer(server, device);
|
|
break;
|
|
|
|
case WLR_INPUT_DEVICE_SWITCH:
|
|
add_switch(server, device);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
|
|
if (!wl_list_empty(&server->keyboards)) {
|
|
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
|
|
}
|
|
wlr_seat_set_capabilities(server->seat, caps);
|
|
|
|
if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0) {
|
|
hikari_cursor_reset_image(&server->cursor);
|
|
}
|
|
}
|
|
|
|
static void
|
|
new_input_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server = wl_container_of(listener, server, new_input);
|
|
struct wlr_input_device *device = data;
|
|
|
|
add_input(server, device);
|
|
}
|
|
|
|
#ifdef HAVE_VIRTUAL_INPUT
|
|
static void
|
|
new_virtual_keyboard_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, new_virtual_keyboard);
|
|
struct wlr_virtual_keyboard_v1 *keyboard = data;
|
|
struct wlr_input_device *device = &keyboard->input_device;
|
|
|
|
add_input(server, device);
|
|
}
|
|
|
|
static void
|
|
setup_virtual_keyboard(struct hikari_server *server)
|
|
{
|
|
server->virtual_keyboard =
|
|
wlr_virtual_keyboard_manager_v1_create(server->display);
|
|
wl_signal_add(&server->virtual_keyboard->events.new_virtual_keyboard,
|
|
&server->new_virtual_keyboard);
|
|
server->new_virtual_keyboard.notify = new_virtual_keyboard_handler;
|
|
}
|
|
|
|
static void
|
|
new_virtual_pointer_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, new_virtual_pointer);
|
|
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
|
|
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
|
|
struct wlr_input_device *device = &pointer->input_device;
|
|
|
|
add_input(server, device);
|
|
|
|
if (event->suggested_output) {
|
|
wlr_cursor_map_to_output(
|
|
server->cursor.wlr_cursor, event->suggested_output);
|
|
}
|
|
}
|
|
|
|
static void
|
|
setup_virtual_pointer(struct hikari_server *server)
|
|
{
|
|
server->virtual_pointer =
|
|
wlr_virtual_pointer_manager_v1_create(server->display);
|
|
wl_signal_add(&server->virtual_pointer->events.new_virtual_pointer,
|
|
&server->new_virtual_pointer);
|
|
server->new_virtual_pointer.notify = new_virtual_pointer_handler;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
new_output_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server = wl_container_of(listener, server, new_output);
|
|
|
|
assert(server == &hikari_server);
|
|
|
|
struct wlr_output *wlr_output = data;
|
|
struct hikari_output *output = hikari_malloc(sizeof(struct hikari_output));
|
|
|
|
if (!wlr_output_init_render(
|
|
wlr_output, server->allocator, server->renderer)) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
hikari_output_init(output, wlr_output);
|
|
hikari_cursor_reset_image(&server->cursor);
|
|
}
|
|
|
|
static bool
|
|
surface_at(struct hikari_node *node,
|
|
double ox,
|
|
double oy,
|
|
struct wlr_surface **surface,
|
|
double *sx,
|
|
double *sy)
|
|
{
|
|
double out_sx, out_sy;
|
|
|
|
struct wlr_surface *out_surface =
|
|
hikari_node_surface_at(node, ox, oy, &out_sx, &out_sy);
|
|
|
|
if (out_surface != NULL) {
|
|
*sx = out_sx;
|
|
*sy = out_sy;
|
|
*surface = out_surface;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
static bool
|
|
layer_at(struct wl_list *layers,
|
|
double ox,
|
|
double oy,
|
|
struct wlr_surface **surface,
|
|
double *sx,
|
|
double *sy,
|
|
struct hikari_node **node)
|
|
{
|
|
double out_sx, out_sy;
|
|
|
|
struct hikari_layer *layer;
|
|
wl_list_for_each (layer, layers, layer_surfaces) {
|
|
struct hikari_node *out_node = (struct hikari_node *)layer;
|
|
|
|
struct wlr_surface *out_surface =
|
|
hikari_node_surface_at(out_node, ox, oy, &out_sx, &out_sy);
|
|
|
|
if (out_surface != NULL) {
|
|
*sx = out_sx;
|
|
*sy = out_sy;
|
|
*surface = out_surface;
|
|
*node = out_node;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
topmost_of(struct wl_list *layers,
|
|
double ox,
|
|
double oy,
|
|
struct wlr_surface **surface,
|
|
double *sx,
|
|
double *sy,
|
|
struct hikari_node **node)
|
|
{
|
|
double out_sx, out_sy;
|
|
|
|
struct hikari_layer *layer;
|
|
wl_list_for_each (layer, layers, layer_surfaces) {
|
|
struct hikari_node *out_node = (struct hikari_node *)layer;
|
|
|
|
struct wlr_layer_surface_v1_state *state = &layer->surface->current;
|
|
|
|
struct wlr_surface *out_surface =
|
|
hikari_node_surface_at(out_node, ox, oy, &out_sx, &out_sy);
|
|
|
|
if (state->keyboard_interactive) {
|
|
if (out_surface != NULL) {
|
|
*surface = out_surface;
|
|
} else {
|
|
*surface = layer->surface->surface;
|
|
}
|
|
|
|
*sx = out_sx;
|
|
*sy = out_sy;
|
|
*node = out_node;
|
|
|
|
return true;
|
|
} else if (out_surface != NULL) {
|
|
*surface = out_surface;
|
|
|
|
*sx = out_sx;
|
|
*sy = out_sy;
|
|
*node = out_node;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static struct hikari_node *
|
|
node_at(double lx,
|
|
double ly,
|
|
struct wlr_surface **surface,
|
|
struct hikari_workspace **workspace,
|
|
double *sx,
|
|
double *sy)
|
|
{
|
|
assert(hikari_server.workspace != NULL);
|
|
|
|
struct wlr_output *wlr_output =
|
|
wlr_output_layout_output_at(hikari_server.output_layout, lx, ly);
|
|
|
|
if (wlr_output == NULL) {
|
|
*workspace = hikari_server.workspace;
|
|
return NULL;
|
|
}
|
|
|
|
struct hikari_output *output = wlr_output->data;
|
|
struct hikari_workspace *output_workspace = output->workspace;
|
|
|
|
*workspace = output_workspace;
|
|
|
|
struct hikari_node *node;
|
|
double ox = lx - output->geometry.x;
|
|
double oy = ly - output->geometry.y;
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
if (topmost_of(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
|
|
ox,
|
|
oy,
|
|
surface,
|
|
sx,
|
|
sy,
|
|
&node)) {
|
|
return node;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL;
|
|
wl_list_for_each (xwayland_unmanaged_view,
|
|
&output->unmanaged_xwayland_views,
|
|
unmanaged_output_views) {
|
|
node = (struct hikari_node *)xwayland_unmanaged_view;
|
|
|
|
if (surface_at(node, ox, oy, surface, sx, sy)) {
|
|
return node;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
if (topmost_of(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
|
|
ox,
|
|
oy,
|
|
surface,
|
|
sx,
|
|
sy,
|
|
&node)) {
|
|
return node;
|
|
}
|
|
#endif
|
|
|
|
struct hikari_view *view = NULL;
|
|
wl_list_for_each (view, &output_workspace->views, workspace_views) {
|
|
node = (struct hikari_node *)view;
|
|
|
|
if (surface_at(node, ox, oy, surface, sx, sy)) {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
if (layer_at(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
|
|
ox,
|
|
oy,
|
|
surface,
|
|
sx,
|
|
sy,
|
|
&node)) {
|
|
return node;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct hikari_node *
|
|
hikari_server_node_at(double x,
|
|
double y,
|
|
struct wlr_surface **surface,
|
|
struct hikari_workspace **workspace,
|
|
double *sx,
|
|
double *sy)
|
|
{
|
|
return node_at(x, y, surface, workspace, sx, sy);
|
|
}
|
|
|
|
void
|
|
hikari_server_cursor_focus(void)
|
|
{
|
|
struct timespec now;
|
|
uint32_t time_msec = (uint32_t)clock_gettime(CLOCK_MONOTONIC, &now);
|
|
hikari_server.mode->cursor_move(time_msec);
|
|
}
|
|
|
|
static void
|
|
request_set_primary_selection_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, request_set_primary_selection);
|
|
|
|
struct wlr_seat_request_set_primary_selection_event *event = data;
|
|
|
|
// CAN FAIL WITH NULL POINTER. HOW?
|
|
wlr_seat_set_primary_selection(server->seat, event->source, event->serial);
|
|
}
|
|
|
|
static void
|
|
request_set_selection_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, request_set_selection);
|
|
|
|
struct wlr_seat_request_set_selection_event *event = data;
|
|
|
|
wlr_seat_set_selection(server->seat, event->source, event->serial);
|
|
}
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
static void
|
|
new_xwayland_surface_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, new_xwayland_surface);
|
|
|
|
struct wlr_xwayland_surface *wlr_xwayland_surface = data;
|
|
|
|
struct hikari_workspace *workspace = server->workspace;
|
|
|
|
if (wlr_xwayland_surface->override_redirect) {
|
|
struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view =
|
|
hikari_malloc(sizeof(struct hikari_xwayland_unmanaged_view));
|
|
|
|
hikari_xwayland_unmanaged_view_init(
|
|
xwayland_unmanaged_view, wlr_xwayland_surface, workspace);
|
|
} else {
|
|
struct hikari_xwayland_view *xwayland_view =
|
|
hikari_malloc(sizeof(struct hikari_xwayland_view));
|
|
|
|
hikari_xwayland_view_init(xwayland_view, wlr_xwayland_surface, workspace);
|
|
}
|
|
}
|
|
|
|
static void
|
|
setup_xwayland(struct hikari_server *server)
|
|
{
|
|
server->xwayland =
|
|
wlr_xwayland_create(server->display, server->compositor, true);
|
|
|
|
server->new_xwayland_surface.notify = new_xwayland_surface_handler;
|
|
wl_signal_add(
|
|
&server->xwayland->events.new_surface, &server->new_xwayland_surface);
|
|
|
|
setenv("DISPLAY", server->xwayland->display_name, true);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
setup_cursor(struct hikari_server *server)
|
|
{
|
|
hikari_cursor_init(&hikari_server.cursor, server->output_layout);
|
|
|
|
hikari_cursor_configure_bindings(
|
|
&hikari_server.cursor, &hikari_configuration->mouse_binding_configs);
|
|
}
|
|
|
|
static void
|
|
server_decoration_mode_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_view_decoration *decoration =
|
|
wl_container_of(listener, decoration, mode);
|
|
struct hikari_view *view = decoration->view;
|
|
|
|
view->use_csd = decoration->wlr_decoration->mode ==
|
|
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
|
|
|
|
if (view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_NONE;
|
|
hikari_output_damage_whole(hikari_server.workspace->output);
|
|
}
|
|
}
|
|
|
|
static void
|
|
server_decoration_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_server_decoration *wlr_decoration = data;
|
|
struct hikari_view *view =
|
|
wl_container_of(wlr_decoration->surface, view, surface);
|
|
struct wlr_xdg_surface *xdg_surface =
|
|
wlr_xdg_surface_from_wlr_surface(wlr_decoration->surface);
|
|
struct hikari_xdg_view *xdg_view = xdg_surface->data;
|
|
|
|
if (xdg_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
wl_signal_add(&wlr_decoration->events.mode, &xdg_view->view.decoration.mode);
|
|
xdg_view->view.decoration.mode.notify = server_decoration_mode_handler;
|
|
|
|
xdg_view->view.decoration.wlr_decoration = wlr_decoration;
|
|
xdg_view->view.decoration.view = &xdg_view->view;
|
|
}
|
|
|
|
static void
|
|
new_toplevel_decoration_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration = data;
|
|
|
|
struct hikari_decoration *decoration =
|
|
hikari_malloc(sizeof(struct hikari_decoration));
|
|
|
|
hikari_decoration_init(decoration, wlr_decoration);
|
|
}
|
|
|
|
static void
|
|
setup_decorations(struct hikari_server *server)
|
|
{
|
|
server->decoration_manager =
|
|
wlr_server_decoration_manager_create(server->display);
|
|
|
|
wlr_server_decoration_manager_set_default_mode(
|
|
server->decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
|
|
|
wl_signal_add(&server->decoration_manager->events.new_decoration,
|
|
&server->new_decoration);
|
|
server->new_decoration.notify = server_decoration_handler;
|
|
|
|
server->xdg_decoration_manager =
|
|
wlr_xdg_decoration_manager_v1_create(server->display);
|
|
wl_signal_add(&server->xdg_decoration_manager->events.new_toplevel_decoration,
|
|
&server->new_toplevel_decoration);
|
|
server->new_toplevel_decoration.notify = new_toplevel_decoration_handler;
|
|
}
|
|
|
|
static void
|
|
start_drag_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_surface *surface;
|
|
struct hikari_workspace *workspace;
|
|
double sx, sy;
|
|
|
|
struct hikari_node *node = node_at(hikari_server.cursor.wlr_cursor->x,
|
|
hikari_server.cursor.wlr_cursor->y,
|
|
&surface,
|
|
&workspace,
|
|
&sx,
|
|
&sy);
|
|
|
|
if (node != NULL) {
|
|
hikari_dnd_mode_enter();
|
|
}
|
|
}
|
|
|
|
static void
|
|
request_start_drag_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, request_start_drag);
|
|
struct wlr_seat_request_start_drag_event *event = data;
|
|
|
|
if (wlr_seat_validate_pointer_grab_serial(
|
|
server->seat, event->origin, event->serial)) {
|
|
wlr_seat_start_pointer_drag(server->seat, event->drag, event->serial);
|
|
} else {
|
|
wlr_data_source_destroy(event->drag->source);
|
|
}
|
|
}
|
|
|
|
static void
|
|
setup_selection(struct hikari_server *server)
|
|
{
|
|
wlr_data_control_manager_v1_create(server->display);
|
|
|
|
wlr_primary_selection_v1_device_manager_create(server->display);
|
|
|
|
server->seat = wlr_seat_create(server->display, "seat0");
|
|
assert(server->seat != NULL);
|
|
|
|
server->request_set_primary_selection.notify =
|
|
request_set_primary_selection_handler;
|
|
wl_signal_add(&server->seat->events.request_set_primary_selection,
|
|
&server->request_set_primary_selection);
|
|
|
|
server->request_set_selection.notify = request_set_selection_handler;
|
|
wl_signal_add(&server->seat->events.request_set_selection,
|
|
&server->request_set_selection);
|
|
|
|
server->request_start_drag.notify = request_start_drag_handler;
|
|
wl_signal_add(
|
|
&server->seat->events.request_start_drag, &server->request_start_drag);
|
|
|
|
server->start_drag.notify = start_drag_handler;
|
|
wl_signal_add(&server->seat->events.start_drag, &server->start_drag);
|
|
}
|
|
|
|
static void
|
|
new_xdg_surface_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, new_xdg_surface);
|
|
|
|
struct wlr_xdg_surface *xdg_surface = data;
|
|
|
|
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_xdg_view *xdg_view =
|
|
hikari_malloc(sizeof(struct hikari_xdg_view));
|
|
|
|
hikari_xdg_view_init(xdg_view, xdg_surface, server->workspace);
|
|
}
|
|
|
|
static void
|
|
setup_xdg_shell(struct hikari_server *server)
|
|
{
|
|
server->xdg_shell = wlr_xdg_shell_create(server->display);
|
|
|
|
server->new_xdg_surface.notify = new_xdg_surface_handler;
|
|
wl_signal_add(
|
|
&server->xdg_shell->events.new_surface, &server->new_xdg_surface);
|
|
}
|
|
|
|
#ifdef HAVE_LAYERSHELL
|
|
static void
|
|
new_layer_shell_surface_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_layer_surface_v1 *wlr_layer_surface =
|
|
(struct wlr_layer_surface_v1 *)data;
|
|
struct hikari_layer *layer = hikari_malloc(sizeof(struct hikari_layer));
|
|
|
|
hikari_layer_init(layer, wlr_layer_surface);
|
|
}
|
|
|
|
static void
|
|
setup_layer_shell(struct hikari_server *server)
|
|
{
|
|
server->layer_shell = wlr_layer_shell_v1_create(server->display);
|
|
|
|
wl_signal_add(&server->layer_shell->events.new_surface,
|
|
&server->new_layer_shell_surface);
|
|
server->new_layer_shell_surface.notify = new_layer_shell_surface_handler;
|
|
}
|
|
#endif
|
|
|
|
struct hikari_server hikari_server;
|
|
|
|
static void
|
|
output_layout_change_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_server *server =
|
|
wl_container_of(listener, server, output_layout_change);
|
|
|
|
struct hikari_output *output;
|
|
wl_list_for_each (output, &server->outputs, server_outputs) {
|
|
struct wlr_output *wlr_output = output->wlr_output;
|
|
struct wlr_box *output_box =
|
|
wlr_output_layout_get_box(hikari_server.output_layout, wlr_output);
|
|
|
|
output->geometry.x = output_box->x;
|
|
output->geometry.y = output_box->y;
|
|
output->geometry.width = output_box->width;
|
|
output->geometry.height = output_box->height;
|
|
|
|
struct hikari_output_config *output_config =
|
|
hikari_configuration_resolve_output_config(
|
|
hikari_configuration, wlr_output->name);
|
|
|
|
if (output_config != NULL) {
|
|
hikari_output_load_background(output,
|
|
output_config->background.value,
|
|
output_config->background_fit.value);
|
|
}
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
hikari_output_rearrange_xwayland_views(output);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static bool
|
|
drop_privileges(struct hikari_server *server)
|
|
{
|
|
if (getuid() != geteuid() || getgid() != getegid()) {
|
|
if (setuid(getuid()) != 0 || setgid(getgid()) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (geteuid() == 0 || getegid() == 0) {
|
|
fprintf(stderr, "running as root is prohibited\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
hikari_server_prepare_privileged(void)
|
|
{
|
|
bool success = false;
|
|
struct hikari_server *server = &hikari_server;
|
|
|
|
server->display = wl_display_create();
|
|
if (server->display == NULL) {
|
|
fprintf(stderr, "error: could not create display\n");
|
|
goto done;
|
|
}
|
|
|
|
server->event_loop = wl_display_get_event_loop(server->display);
|
|
if (server->event_loop == NULL) {
|
|
fprintf(stderr, "error: could not create event loop\n");
|
|
goto done;
|
|
}
|
|
|
|
server->backend = wlr_backend_autocreate(server->display);
|
|
if (server->backend == NULL) {
|
|
fprintf(stderr, "error: could not create backend\n");
|
|
goto done;
|
|
}
|
|
|
|
success = true;
|
|
|
|
done:
|
|
if (!drop_privileges(server) || !success) {
|
|
if (server->display != NULL) {
|
|
wl_display_destroy(server->display);
|
|
}
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
init_noop_output(struct hikari_server *server)
|
|
{
|
|
server->noop_backend = wlr_headless_backend_create(server->display);
|
|
|
|
struct wlr_output *wlr_output =
|
|
wlr_headless_add_output(server->noop_backend, 800, 600);
|
|
struct hikari_output *noop_output =
|
|
hikari_malloc(sizeof(struct hikari_output));
|
|
|
|
server->noop_output = noop_output;
|
|
hikari_output_init(noop_output, wlr_output);
|
|
|
|
hikari_server.workspace = noop_output->workspace;
|
|
hikari_cursor_activate(&hikari_server.cursor);
|
|
hikari_server.mode = (struct hikari_mode *)&hikari_server.normal_mode;
|
|
}
|
|
|
|
static void
|
|
server_init(struct hikari_server *server, char *config_path)
|
|
{
|
|
#ifndef NDEBUG
|
|
server->track_damage = false;
|
|
#endif
|
|
server->shutdown_timer = NULL;
|
|
server->config_path = config_path;
|
|
|
|
hikari_configuration = hikari_malloc(sizeof(struct hikari_configuration));
|
|
|
|
hikari_configuration_init(hikari_configuration);
|
|
|
|
if (!hikari_configuration_load(hikari_configuration, config_path)) {
|
|
hikari_configuration_fini(hikari_configuration);
|
|
hikari_free(hikari_configuration);
|
|
|
|
wl_display_destroy(server->display);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
server->keyboard_state.modifiers = 0;
|
|
server->keyboard_state.mod_released = false;
|
|
server->keyboard_state.mod_changed = false;
|
|
server->keyboard_state.mod_pressed = false;
|
|
|
|
server->cycling = false;
|
|
server->workspace = NULL;
|
|
|
|
hikari_indicator_init(
|
|
&server->indicator, hikari_configuration->indicator_selected);
|
|
|
|
wl_list_init(&server->outputs);
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
server->renderer = wlr_renderer_autocreate(server->backend);
|
|
if (server->renderer == NULL) {
|
|
wl_display_destroy(server->display);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
wlr_renderer_init_wl_display(server->renderer, server->display);
|
|
|
|
server->allocator =
|
|
wlr_allocator_autocreate(server->backend, server->renderer);
|
|
if (server->allocator == NULL) {
|
|
wl_display_destroy(server->display);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
server->socket = wl_display_add_socket_auto(server->display);
|
|
if (server->socket == NULL) {
|
|
wl_display_destroy(server->display);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
setenv("WAYLAND_DISPLAY", server->socket, true);
|
|
|
|
server->compositor = wlr_compositor_create(server->display, server->renderer);
|
|
|
|
server->data_device_manager = wlr_data_device_manager_create(server->display);
|
|
|
|
server->new_input.notify = new_input_handler;
|
|
wl_signal_add(&server->backend->events.new_input, &server->new_input);
|
|
|
|
server->output_layout = wlr_output_layout_create();
|
|
server->output_manager =
|
|
wlr_xdg_output_manager_v1_create(server->display, server->output_layout);
|
|
|
|
server->output_layout_change.notify = output_layout_change_handler;
|
|
wl_signal_add(
|
|
&server->output_layout->events.change, &server->output_layout_change);
|
|
|
|
server->new_output.notify = new_output_handler;
|
|
wl_signal_add(&server->backend->events.new_output, &server->new_output);
|
|
|
|
#ifdef HAVE_GAMMACONTROL
|
|
wlr_gamma_control_manager_v1_create(server->display);
|
|
#endif
|
|
|
|
#ifdef HAVE_SCREENCOPY
|
|
wlr_screencopy_manager_v1_create(server->display);
|
|
#endif
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
setup_xwayland(server);
|
|
#endif
|
|
setup_cursor(server);
|
|
#ifdef HAVE_VIRTUAL_INPUT
|
|
setup_virtual_keyboard(server);
|
|
setup_virtual_pointer(server);
|
|
#endif
|
|
setup_decorations(server);
|
|
setup_selection(server);
|
|
setup_xdg_shell(server);
|
|
#ifdef HAVE_LAYERSHELL
|
|
setup_layer_shell(server);
|
|
#endif
|
|
|
|
wl_list_init(&server->pointers);
|
|
wl_list_init(&server->keyboards);
|
|
wl_list_init(&server->switches);
|
|
|
|
wl_list_init(&server->groups);
|
|
wl_list_init(&server->visible_groups);
|
|
wl_list_init(&server->visible_views);
|
|
|
|
hikari_dnd_mode_init(&server->dnd_mode);
|
|
hikari_group_assign_mode_init(&server->group_assign_mode);
|
|
hikari_input_grab_mode_init(&server->input_grab_mode);
|
|
hikari_layout_select_mode_init(&server->layout_select_mode);
|
|
hikari_lock_mode_init(&server->lock_mode);
|
|
hikari_mark_assign_mode_init(&server->mark_assign_mode);
|
|
hikari_mark_select_mode_init(&server->mark_select_mode);
|
|
hikari_move_mode_init(&server->move_mode);
|
|
hikari_normal_mode_init(&server->normal_mode);
|
|
hikari_resize_mode_init(&server->resize_mode);
|
|
hikari_sheet_assign_mode_init(&server->sheet_assign_mode);
|
|
|
|
hikari_marks_init();
|
|
|
|
init_noop_output(server);
|
|
}
|
|
|
|
static void
|
|
sig_handler(int signal)
|
|
{
|
|
hikari_server_terminate(NULL);
|
|
}
|
|
|
|
static void
|
|
run_autostart(char *autostart)
|
|
{
|
|
hikari_command_execute(autostart);
|
|
free(autostart);
|
|
}
|
|
|
|
void
|
|
hikari_server_start(char *config_path, char *autostart)
|
|
{
|
|
server_init(&hikari_server, config_path);
|
|
signal(SIGTERM, sig_handler);
|
|
|
|
wlr_backend_start(hikari_server.backend);
|
|
|
|
if (autostart != NULL) {
|
|
run_autostart(autostart);
|
|
}
|
|
|
|
wl_display_run(hikari_server.display);
|
|
}
|
|
|
|
static int
|
|
shutdown_handler(void *data)
|
|
{
|
|
struct hikari_server *server = &hikari_server;
|
|
|
|
if (server->shutdown_timer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
struct hikari_output *output;
|
|
wl_list_for_each (output, &server->outputs, server_outputs) {
|
|
if (!wl_list_empty(&output->views)) {
|
|
wl_event_source_timer_update(server->shutdown_timer, 1000);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
wl_display_terminate(hikari_server.display);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
destroy_shutdown_timer(struct hikari_server *server)
|
|
{
|
|
wl_event_source_timer_update(server->shutdown_timer, 0);
|
|
wl_event_source_remove(server->shutdown_timer);
|
|
|
|
server->shutdown_timer = NULL;
|
|
}
|
|
|
|
void
|
|
hikari_server_terminate(void *arg)
|
|
{
|
|
struct hikari_server *server = &hikari_server;
|
|
|
|
if (server->shutdown_timer != NULL) {
|
|
destroy_shutdown_timer(server);
|
|
wl_display_terminate(server->display);
|
|
return;
|
|
}
|
|
|
|
struct hikari_output *output;
|
|
wl_list_for_each (output, &server->outputs, server_outputs) {
|
|
struct hikari_view *view, *view_temp;
|
|
wl_list_for_each_safe (view, view_temp, &output->views, output_views) {
|
|
hikari_view_quit(view);
|
|
}
|
|
}
|
|
|
|
server->shutdown_timer =
|
|
wl_event_loop_add_timer(server->event_loop, shutdown_handler, NULL);
|
|
|
|
wl_event_source_timer_update(server->shutdown_timer, 100);
|
|
}
|
|
|
|
void
|
|
hikari_server_stop(void)
|
|
{
|
|
struct hikari_server *server = &hikari_server;
|
|
|
|
wl_list_remove(&server->new_output.link);
|
|
wl_list_remove(&server->new_input.link);
|
|
wl_list_remove(&server->new_xdg_surface.link);
|
|
wl_list_remove(&server->request_set_primary_selection.link);
|
|
wl_list_remove(&server->request_start_drag.link);
|
|
wl_list_remove(&server->start_drag.link);
|
|
wl_list_remove(&server->output_layout_change.link);
|
|
#ifdef HAVE_XWAYLAND
|
|
wl_list_remove(&server->new_xwayland_surface.link);
|
|
#endif
|
|
|
|
if (server->shutdown_timer != NULL) {
|
|
destroy_shutdown_timer(server);
|
|
}
|
|
|
|
hikari_cursor_fini(&server->cursor);
|
|
hikari_indicator_fini(&server->indicator);
|
|
|
|
hikari_lock_mode_fini(&server->lock_mode);
|
|
hikari_mark_assign_mode_fini(&server->mark_assign_mode);
|
|
|
|
wl_display_destroy_clients(server->display);
|
|
|
|
#if HAVE_XWAYLAND
|
|
wlr_xwayland_destroy(server->xwayland);
|
|
#endif
|
|
|
|
wlr_seat_destroy(server->seat);
|
|
wl_display_destroy(server->display);
|
|
wlr_output_layout_destroy(server->output_layout);
|
|
|
|
hikari_configuration_fini(hikari_configuration);
|
|
hikari_free(hikari_configuration);
|
|
hikari_marks_fini();
|
|
|
|
free(server->config_path);
|
|
}
|
|
|
|
struct hikari_group *
|
|
hikari_server_find_group(const char *group_name)
|
|
{
|
|
struct hikari_group *group;
|
|
wl_list_for_each (group, &hikari_server.groups, server_groups) {
|
|
if (!strcmp(group_name, group->name)) {
|
|
return group;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct hikari_group *
|
|
hikari_server_find_or_create_group(const char *group_name)
|
|
{
|
|
struct hikari_group *group = hikari_server_find_group(group_name);
|
|
|
|
if (group == NULL) {
|
|
group = hikari_malloc(sizeof(struct hikari_group));
|
|
hikari_group_init(group, group_name);
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
void
|
|
hikari_server_lock(void *arg)
|
|
{
|
|
hikari_lock_mode_enter();
|
|
}
|
|
|
|
void
|
|
hikari_server_reload(void *arg)
|
|
{
|
|
hikari_configuration_reload(hikari_server.config_path);
|
|
}
|
|
|
|
#define CYCLE_VIEW(name, link) \
|
|
static struct hikari_view *cycle_##name##_view(void) \
|
|
{ \
|
|
struct hikari_server *server = &hikari_server; \
|
|
\
|
|
if (wl_list_empty(&server->visible_views)) { \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
struct hikari_view *focus_view = server->workspace->focus_view; \
|
|
\
|
|
struct hikari_view *view; \
|
|
if (focus_view == NULL) { \
|
|
view = wl_container_of( \
|
|
server->visible_views.link, view, visible_server_views); \
|
|
} else { \
|
|
struct wl_list *link = focus_view->visible_server_views.link; \
|
|
if (link != &server->visible_views) { \
|
|
view = wl_container_of(link, view, visible_server_views); \
|
|
} else { \
|
|
view = wl_container_of( \
|
|
server->visible_views.link, view, visible_server_views); \
|
|
} \
|
|
} \
|
|
\
|
|
return view; \
|
|
} \
|
|
\
|
|
void hikari_server_cycle_##name##_view(void *arg) \
|
|
{ \
|
|
struct hikari_view *view; \
|
|
\
|
|
hikari_server_set_cycling(); \
|
|
view = cycle_##name##_view(); \
|
|
\
|
|
if (view != NULL && view != hikari_server.workspace->focus_view) { \
|
|
hikari_workspace_focus_view(view->sheet->workspace, view); \
|
|
} \
|
|
}
|
|
|
|
CYCLE_VIEW(next, prev)
|
|
CYCLE_VIEW(prev, next)
|
|
#undef CYCLE_VIEW
|
|
|
|
#define CYCLE_ACTION(n) \
|
|
void hikari_server_cycle_##n(void *arg) \
|
|
{ \
|
|
struct hikari_view *view; \
|
|
\
|
|
hikari_server_set_cycling(); \
|
|
view = hikari_workspace_##n(hikari_server.workspace); \
|
|
\
|
|
if (view != NULL && view != hikari_server.workspace->focus_view) { \
|
|
hikari_workspace_focus_view(view->sheet->workspace, view); \
|
|
} \
|
|
}
|
|
|
|
CYCLE_ACTION(first_group_view)
|
|
CYCLE_ACTION(last_group_view)
|
|
CYCLE_ACTION(next_group_view)
|
|
CYCLE_ACTION(prev_group_view)
|
|
CYCLE_ACTION(next_layout_view)
|
|
CYCLE_ACTION(prev_layout_view)
|
|
CYCLE_ACTION(first_layout_view)
|
|
CYCLE_ACTION(last_layout_view)
|
|
CYCLE_ACTION(next_group)
|
|
CYCLE_ACTION(prev_group)
|
|
#undef CYCLE_ACTION
|
|
|
|
#define CYCLE_WORKSPACE(link) \
|
|
void hikari_server_cycle_##link##_workspace(void *arg) \
|
|
{ \
|
|
struct hikari_workspace *workspace = hikari_server.workspace; \
|
|
struct hikari_workspace *link = hikari_workspace_##link(workspace); \
|
|
\
|
|
if (workspace != link) { \
|
|
hikari_server_set_cycling(); \
|
|
\
|
|
struct hikari_view *view = hikari_workspace_first_view(link); \
|
|
if (view != NULL) { \
|
|
hikari_workspace_focus_view(link, view); \
|
|
hikari_view_center_cursor(view); \
|
|
} else { \
|
|
hikari_workspace_center_cursor(link); \
|
|
hikari_server_cursor_focus(); \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
CYCLE_WORKSPACE(next)
|
|
CYCLE_WORKSPACE(prev)
|
|
#undef CYCLE_WORKSPACE
|
|
|
|
static void
|
|
update_indication(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(view->group != NULL);
|
|
|
|
hikari_group_damage(view->group);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_normal_mode(void *arg)
|
|
{
|
|
struct hikari_server *server = &hikari_server;
|
|
|
|
hikari_cursor_reset_image(&server->cursor);
|
|
|
|
hikari_normal_mode_enter();
|
|
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_sheet_assign_mode(void *arg)
|
|
{
|
|
assert(hikari_server.workspace != NULL);
|
|
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_view *focus_view = workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
update_indication(focus_view);
|
|
|
|
hikari_sheet_assign_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_move_mode(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
update_indication(focus_view);
|
|
|
|
hikari_move_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_resize_mode(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
update_indication(focus_view);
|
|
|
|
hikari_resize_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_group_assign_mode(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
hikari_group_assign_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_input_grab_mode(void *arg)
|
|
{
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_view *focus_view = workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
update_indication(focus_view);
|
|
|
|
hikari_input_grab_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_mark_select_mode(void *arg)
|
|
{
|
|
hikari_mark_select_mode_enter(false);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_mark_select_switch_mode(void *arg)
|
|
{
|
|
hikari_mark_select_mode_enter(true);
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_layout_select_mode(void *arg)
|
|
{
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_view *focus_view = workspace->focus_view;
|
|
|
|
if (focus_view != NULL) {
|
|
update_indication(focus_view);
|
|
}
|
|
|
|
hikari_layout_select_mode_enter();
|
|
}
|
|
|
|
void
|
|
hikari_server_enter_mark_assign_mode(void *arg)
|
|
{
|
|
assert(hikari_server.workspace != NULL);
|
|
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_view *focus_view = workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
update_indication(focus_view);
|
|
|
|
hikari_mark_assign_mode_enter(focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_execute_command(void *arg)
|
|
{
|
|
const char *command = arg;
|
|
hikari_command_execute(command);
|
|
}
|
|
|
|
void
|
|
hikari_server_reset_sheet_layout(void *arg)
|
|
{
|
|
struct hikari_layout *layout = hikari_server.workspace->sheet->layout;
|
|
|
|
if (layout == NULL) {
|
|
return;
|
|
}
|
|
|
|
hikari_layout_reset(layout);
|
|
}
|
|
|
|
void
|
|
hikari_server_layout_restack_append(void *arg)
|
|
{
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_sheet *sheet = workspace->sheet;
|
|
struct hikari_layout *layout = sheet->layout;
|
|
|
|
if (layout == NULL) {
|
|
struct hikari_split *split = hikari_sheet_default_split(sheet);
|
|
|
|
if (split != NULL) {
|
|
hikari_sheet_apply_split(sheet, split);
|
|
}
|
|
} else {
|
|
hikari_workspace_display_sheet_current(workspace);
|
|
hikari_layout_restack_append(layout);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_layout_restack_prepend(void *arg)
|
|
{
|
|
struct hikari_workspace *workspace = hikari_server.workspace;
|
|
struct hikari_sheet *sheet = workspace->sheet;
|
|
struct hikari_layout *layout = sheet->layout;
|
|
|
|
if (layout == NULL) {
|
|
struct hikari_split *split = hikari_sheet_default_split(sheet);
|
|
|
|
if (split != NULL) {
|
|
hikari_sheet_apply_split(sheet, split);
|
|
}
|
|
} else {
|
|
hikari_workspace_display_sheet_current(workspace);
|
|
hikari_layout_restack_prepend(layout);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_layout_sheet(void *arg)
|
|
{
|
|
char layout_register = (intptr_t)arg;
|
|
|
|
struct hikari_split *split =
|
|
hikari_configuration_lookup_layout(hikari_configuration, layout_register);
|
|
|
|
if (split != NULL) {
|
|
hikari_workspace_apply_split(hikari_server.workspace, split);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_session_change_vt(void *arg)
|
|
{
|
|
const intptr_t vt = (intptr_t)arg;
|
|
assert(vt >= 1 && vt <= 12);
|
|
|
|
struct wlr_session *session = wlr_backend_get_session(hikari_server.backend);
|
|
if (session != NULL) {
|
|
wlr_session_change_vt(session, vt);
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_marked_view(struct hikari_view *view, struct hikari_mark *mark)
|
|
{
|
|
if (view != NULL) {
|
|
if (hikari_view_is_hidden(view)) {
|
|
hikari_view_show(view);
|
|
} else {
|
|
hikari_view_raise(view);
|
|
}
|
|
|
|
hikari_view_center_cursor(view);
|
|
hikari_server_cursor_focus();
|
|
} else {
|
|
char *command = hikari_configuration->execs[mark->nr].command;
|
|
|
|
if (command != NULL) {
|
|
hikari_command_execute(command);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_show_mark(void *arg)
|
|
{
|
|
assert(arg != NULL);
|
|
|
|
struct hikari_mark *mark = (struct hikari_mark *)arg;
|
|
struct hikari_view *view = mark->view;
|
|
|
|
show_marked_view(view, mark);
|
|
}
|
|
|
|
void
|
|
hikari_server_switch_to_mark(void *arg)
|
|
{
|
|
assert(arg != NULL);
|
|
|
|
struct hikari_mark *mark = (struct hikari_mark *)arg;
|
|
struct hikari_view *view = mark->view;
|
|
|
|
if (view != NULL && view->sheet->workspace->sheet != view->sheet) {
|
|
hikari_workspace_switch_sheet(view->sheet->workspace, view->sheet);
|
|
}
|
|
|
|
show_marked_view(view, mark);
|
|
}
|
|
|
|
void
|
|
hikari_server_migrate_focus_view(
|
|
struct hikari_output *output, double lx, double ly, bool center)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
assert(focus_view != NULL);
|
|
|
|
struct hikari_sheet *sheet = output->workspace->sheet;
|
|
|
|
hikari_view_migrate(focus_view,
|
|
sheet,
|
|
lx - output->geometry.x,
|
|
ly - output->geometry.y,
|
|
center);
|
|
|
|
hikari_indicator_update_sheet(
|
|
&hikari_server.indicator, output, sheet, focus_view->flags);
|
|
|
|
hikari_server.workspace->focus_view = NULL;
|
|
hikari_server.workspace = output->workspace;
|
|
hikari_server.workspace->focus_view = focus_view;
|
|
}
|
|
|
|
static void
|
|
move_view(int dx, int dy)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_output *view_output = focus_view->output;
|
|
struct wlr_box *geometry = hikari_view_geometry(focus_view);
|
|
|
|
double lx = view_output->geometry.x + geometry->x + dx;
|
|
double ly = view_output->geometry.y + geometry->y + dy;
|
|
|
|
struct wlr_output *wlr_output =
|
|
wlr_output_layout_output_at(hikari_server.output_layout, lx, ly);
|
|
|
|
hikari_server_set_cycling();
|
|
|
|
if (wlr_output == NULL || wlr_output->data == view_output) {
|
|
hikari_view_move(focus_view, dx, dy);
|
|
} else {
|
|
hikari_server_migrate_focus_view(wlr_output->data, lx, ly, false);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_move_view_up(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_view(0, -step);
|
|
}
|
|
|
|
void
|
|
hikari_server_move_view_down(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_view(0, step);
|
|
}
|
|
|
|
void
|
|
hikari_server_move_view_left(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_view(-step, 0);
|
|
}
|
|
|
|
void
|
|
hikari_server_move_view_right(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_view(step, 0);
|
|
}
|
|
|
|
static void
|
|
move_resize_view(int dx, int dy, int dwidth, int dheight)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_output *view_output = focus_view->output;
|
|
struct wlr_box *geometry = hikari_view_geometry(focus_view);
|
|
|
|
double lx = view_output->geometry.x + geometry->x + dy;
|
|
double ly = view_output->geometry.y + geometry->y + dy;
|
|
|
|
struct wlr_output *wlr_output =
|
|
wlr_output_layout_output_at(hikari_server.output_layout, lx, ly);
|
|
|
|
hikari_server_set_cycling();
|
|
|
|
if (wlr_output == NULL || wlr_output->data == view_output) {
|
|
hikari_view_move_resize(focus_view, dx, dy, dwidth, dheight);
|
|
} else {
|
|
hikari_server_migrate_focus_view(wlr_output->data, lx, ly, false);
|
|
hikari_view_resize(focus_view, dheight, dwidth);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_server_decrease_view_size_down(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_resize_view(0, step, 0, -step);
|
|
}
|
|
|
|
void
|
|
hikari_server_decrease_view_size_right(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_resize_view(step, 0, -step, 0);
|
|
}
|
|
|
|
void
|
|
hikari_server_increase_view_size_up(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_resize_view(0, -step, 0, step);
|
|
}
|
|
|
|
void
|
|
hikari_server_increase_view_size_left(void *arg)
|
|
{
|
|
const int step = hikari_configuration->step;
|
|
move_resize_view(-step, 0, step, 0);
|
|
}
|
|
|
|
void
|
|
hikari_server_lower_group(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_group *group = focus_view->group;
|
|
|
|
hikari_group_lower(group, focus_view);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
void
|
|
hikari_server_raise_group(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_group *group = focus_view->group;
|
|
|
|
hikari_group_raise(group, focus_view);
|
|
}
|
|
|
|
void
|
|
hikari_server_only_group(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_group *group = focus_view->group;
|
|
|
|
struct hikari_output *output;
|
|
wl_list_for_each (output, &hikari_server.outputs, server_outputs) {
|
|
hikari_workspace_clear(output->workspace);
|
|
}
|
|
|
|
hikari_group_show(group);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
void
|
|
hikari_server_hide_group(void *arg)
|
|
{
|
|
struct hikari_view *focus_view = hikari_server.workspace->focus_view;
|
|
|
|
if (focus_view == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_group *group = focus_view->group;
|
|
assert(group != NULL);
|
|
|
|
hikari_group_hide(group);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
void
|
|
hikari_server_toggle_damage_tracking(void *arg)
|
|
{
|
|
hikari_server.track_damage = !hikari_server.track_damage;
|
|
|
|
struct hikari_output *output = NULL;
|
|
wl_list_for_each (output, &hikari_server.outputs, server_outputs) {
|
|
hikari_output_damage_whole(output);
|
|
}
|
|
}
|
|
#endif
|