feat: support switchbind

This commit is contained in:
DreamMaoMao 2025-09-05 16:17:07 +08:00
parent cf3e9905f3
commit afbf1cc22a
2 changed files with 266 additions and 83 deletions

View file

@ -114,6 +114,12 @@ typedef struct {
Arg arg; Arg arg;
} AxisBinding; } AxisBinding;
typedef struct {
unsigned int fold;
void (*func)(const Arg *);
Arg arg;
} SwitchBinding;
typedef struct { typedef struct {
unsigned int mod; unsigned int mod;
unsigned int motion; unsigned int motion;
@ -262,6 +268,9 @@ typedef struct {
AxisBinding *axis_bindings; AxisBinding *axis_bindings;
int axis_bindings_count; int axis_bindings_count;
SwitchBinding *switch_bindings;
int switch_bindings_count;
GestureBinding *gesture_bindings; GestureBinding *gesture_bindings;
int gesture_bindings_count; int gesture_bindings_count;
@ -390,6 +399,25 @@ int parse_direction(const char *str) {
} }
} }
int parse_fold_state(const char *str) {
// 将输入字符串转换为小写
char lowerStr[10];
int i = 0;
while (str[i] && i < 9) {
lowerStr[i] = tolower(str[i]);
i++;
}
lowerStr[i] = '\0';
// 根据转换后的小写字符串返回对应的枚举值
if (strcmp(lowerStr, "fold") == 0) {
return FOLD;
} else if (strcmp(lowerStr, "unfold") == 0) {
return UNFOLD;
} else {
return INVALIDFOLD;
}
}
long int parse_color(const char *hex_str) { long int parse_color(const char *hex_str) {
char *endptr; char *endptr;
long int hex_num = strtol(hex_str, &endptr, 16); long int hex_num = strtol(hex_str, &endptr, 16);
@ -1768,6 +1796,62 @@ void parse_config_line(Config *config, const char *line) {
config->axis_bindings_count++; config->axis_bindings_count++;
} }
} else if (strncmp(key, "switchbind", 10) == 0) {
config->switch_bindings = realloc(config->switch_bindings,
(config->switch_bindings_count + 1) *
sizeof(SwitchBinding));
if (!config->switch_bindings) {
fprintf(stderr,
"Error: Failed to allocate memory for switch bindings\n");
return;
}
SwitchBinding *binding =
&config->switch_bindings[config->switch_bindings_count];
memset(binding, 0, sizeof(SwitchBinding));
char fold_str[256], func_name[256],
arg_value[256] = "none", arg_value2[256] = "none",
arg_value3[256] = "none", arg_value4[256] = "none",
arg_value5[256] = "none";
if (sscanf(value, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]",
fold_str, func_name, arg_value, arg_value2, arg_value3,
arg_value4, arg_value5) < 3) {
fprintf(stderr, "Error: Invalid switchbind format: %s\n", value);
return;
}
trim_whitespace(fold_str);
trim_whitespace(func_name);
trim_whitespace(arg_value);
trim_whitespace(arg_value2);
trim_whitespace(arg_value3);
trim_whitespace(arg_value4);
trim_whitespace(arg_value5);
binding->fold = parse_fold_state(fold_str);
binding->func =
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
arg_value3, arg_value4, arg_value5);
if (!binding->func) {
if (binding->arg.v) {
free(binding->arg.v);
binding->arg.v = NULL;
}
if (binding->arg.v2) {
free(binding->arg.v2);
binding->arg.v2 = NULL;
}
if (binding->arg.v3) {
free(binding->arg.v3);
binding->arg.v3 = NULL;
}
fprintf(stderr, "Error: Unknown function in switchbind: %s\n",
func_name);
} else {
config->switch_bindings_count++;
}
} else if (strncmp(key, "gesturebind", 11) == 0) { } else if (strncmp(key, "gesturebind", 11) == 0) {
config->gesture_bindings = realloc( config->gesture_bindings = realloc(
config->gesture_bindings, config->gesture_bindings,
@ -2020,6 +2104,27 @@ void free_config(void) {
config.axis_bindings_count = 0; config.axis_bindings_count = 0;
} }
// 释放 switch_bindings
if (config.switch_bindings) {
for (i = 0; i < config.switch_bindings_count; i++) {
if (config.switch_bindings[i].arg.v) {
free((void *)config.switch_bindings[i].arg.v);
config.switch_bindings[i].arg.v = NULL;
}
if (config.switch_bindings[i].arg.v2) {
free((void *)config.switch_bindings[i].arg.v2);
config.switch_bindings[i].arg.v2 = NULL;
}
if (config.switch_bindings[i].arg.v3) {
free((void *)config.switch_bindings[i].arg.v3);
config.switch_bindings[i].arg.v3 = NULL;
}
}
free(config.switch_bindings);
config.switch_bindings = NULL;
config.switch_bindings_count = 0;
}
// 释放 gesture_bindings // 释放 gesture_bindings
if (config.gesture_bindings) { if (config.gesture_bindings) {
for (i = 0; i < config.gesture_bindings_count; i++) { for (i = 0; i < config.gesture_bindings_count; i++) {
@ -2318,8 +2423,8 @@ void set_value_default() {
config.inhibit_regardless_of_visibility = config.inhibit_regardless_of_visibility =
inhibit_regardless_of_visibility; /* 1 means idle inhibitors will inhibit_regardless_of_visibility; /* 1 means idle inhibitors will
disable idle tracking even if it's surface disable idle tracking even if it's
isn't visible surface isn't visible
*/ */
config.borderpx = borderpx; config.borderpx = borderpx;
@ -2418,6 +2523,8 @@ void parse_config(void) {
config.mouse_bindings_count = 0; config.mouse_bindings_count = 0;
config.axis_bindings = NULL; config.axis_bindings = NULL;
config.axis_bindings_count = 0; config.axis_bindings_count = 0;
config.switch_bindings = NULL;
config.switch_bindings_count = 0;
config.gesture_bindings = NULL; config.gesture_bindings = NULL;
config.gesture_bindings_count = 0; config.gesture_bindings_count = 0;
config.exec = NULL; config.exec = NULL;

View file

@ -64,6 +64,7 @@
#include <wlr/types/wlr_session_lock_v1.h> #include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h> #include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h> #include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_switch.h>
#include <wlr/types/wlr_viewporter.h> #include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h> #include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h> #include <wlr/types/wlr_virtual_pointer_v1.h>
@ -155,7 +156,7 @@ enum {
#endif #endif
enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */
enum { NONE, OPEN, MOVE, CLOSE, TAG }; enum { NONE, OPEN, MOVE, CLOSE, TAG };
enum { UNFOLD, FOLD, INVALIDFOLD };
struct dvec2 { struct dvec2 {
double x, y; double x, y;
}; };
@ -197,6 +198,12 @@ struct input_device {
struct wl_listener destroy_listener; // 用于监听设备销毁事件 struct wl_listener destroy_listener; // 用于监听设备销毁事件
}; };
typedef struct {
struct wl_list link;
struct wlr_switch *wlr_switch;
struct wl_listener toggle;
} Switch;
struct dwl_animation { struct dwl_animation {
bool should_animate; bool should_animate;
bool running; bool running;
@ -515,6 +522,9 @@ static void createnotify(struct wl_listener *listener, void *data);
static void createpointer(struct wlr_pointer *pointer); static void createpointer(struct wlr_pointer *pointer);
static void configure_pointer(struct libinput_device *device); static void configure_pointer(struct libinput_device *device);
static void destroypointer(struct wl_listener *listener, void *data); static void destroypointer(struct wl_listener *listener, void *data);
static void createswitch(struct wlr_switch *switch_device);
static void switch_toggle(struct wl_listener *listener, void *data);
static void cleanupswitches();
static void createpointerconstraint(struct wl_listener *listener, void *data); static void createpointerconstraint(struct wl_listener *listener, void *data);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
static void commitpopup(struct wl_listener *listener, void *data); static void commitpopup(struct wl_listener *listener, void *data);
@ -742,6 +752,7 @@ static struct wlr_seat *seat;
static KeyboardGroup *kb_group; static KeyboardGroup *kb_group;
static struct wl_list keyboards; static struct wl_list keyboards;
static struct wl_list pointers; static struct wl_list pointers;
static struct wl_list switches;
static unsigned int cursor_mode; static unsigned int cursor_mode;
static Client *grabc; static Client *grabc;
static int grabcx, grabcy; /* client-relative */ static int grabcx, grabcy; /* client-relative */
@ -1998,6 +2009,8 @@ void cleanup(void) {
dwl_im_relay_finish(dwl_input_method_relay); dwl_im_relay_finish(dwl_input_method_relay);
cleanupswitches();
/* If it's not destroyed manually it will cause a use-after-free of /* If it's not destroyed manually it will cause a use-after-free of
* wlr_seat. Destroy it until it's fixed in the wlroots side */ * wlr_seat. Destroy it until it's fixed in the wlroots side */
wlr_backend_destroy(backend); wlr_backend_destroy(backend);
@ -2703,6 +2716,50 @@ void createpointer(struct wlr_pointer *pointer) {
wlr_cursor_attach_input_device(cursor, &pointer->base); wlr_cursor_attach_input_device(cursor, &pointer->base);
} }
void switch_toggle(struct wl_listener *listener, void *data) {
// 获取包含监听器的结构体
Switch *sw = wl_container_of(listener, sw, toggle);
// 处理切换事件
struct wlr_switch_toggle_event *event = data;
SwitchBinding *a;
int ji;
for (ji = 0; ji < config.switch_bindings_count; ji++) {
if (config.switch_bindings_count < 1)
break;
a = &config.switch_bindings[ji];
if (event->switch_state == a->fold && a->func) {
a->func(&a->arg);
return;
}
}
}
void createswitch(struct wlr_switch *switch_device) {
Switch *sw = calloc(1, sizeof(Switch));
sw->wlr_switch = switch_device;
sw->toggle.notify = switch_toggle;
wl_signal_add(&switch_device->events.toggle, &sw->toggle);
// 添加到全局列表(可选,用于统一管理)
wl_list_insert(&switches, &sw->link);
}
void cleanupswitches() {
Switch *sw, *tmp;
wl_list_for_each_safe(sw, tmp, &switches, link) {
// 移除事件监听
wl_list_remove(&sw->toggle.link);
// 从列表中移除
wl_list_remove(&sw->link);
// 释放内存
free(sw);
}
}
void createpointerconstraint(struct wl_listener *listener, void *data) { void createpointerconstraint(struct wl_listener *listener, void *data) {
PointerConstraint *pointer_constraint = PointerConstraint *pointer_constraint =
ecalloc(1, sizeof(*pointer_constraint)); ecalloc(1, sizeof(*pointer_constraint));
@ -2732,8 +2789,9 @@ void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
void cursorframe(struct wl_listener *listener, void *data) { void cursorframe(struct wl_listener *listener, void *data) {
/* This event is forwarded by the cursor when a pointer emits an frame /* This event is forwarded by the cursor when a pointer emits an frame
* event. Frame events are sent after regular pointer events to group * event. Frame events are sent after regular pointer events to group
* multiple events together. For instance, two axis events may happen at the * multiple events together. For instance, two axis events may happen at
* same time, in which case a frame event won't be sent in between. */ * the same time, in which case a frame event won't be sent in between.
*/
/* Notify the client with pointer focus of the frame event. */ /* Notify the client with pointer focus of the frame event. */
wlr_seat_pointer_notify_frame(seat); wlr_seat_pointer_notify_frame(seat);
} }
@ -2761,7 +2819,8 @@ void destroydragicon(struct wl_listener *listener, void *data) {
void destroyidleinhibitor(struct wl_listener *listener, void *data) { void destroyidleinhibitor(struct wl_listener *listener, void *data) {
/* `data` is the wlr_surface of the idle inhibitor being destroyed, /* `data` is the wlr_surface of the idle inhibitor being destroyed,
* at this point the idle inhibitor is still in the list of the manager */ * at this point the idle inhibitor is still in the list of the manager
*/
checkidleinhibitor(wlr_surface_get_root_surface(data)); checkidleinhibitor(wlr_surface_get_root_surface(data));
wl_list_remove(&listener->link); wl_list_remove(&listener->link);
free(listener); free(listener);
@ -2947,10 +3006,10 @@ void focusclient(Client *c, int lift) {
/* Deactivate old client if focus is changing */ /* Deactivate old client if focus is changing */
if (old_keyboard_focus_surface && if (old_keyboard_focus_surface &&
(!c || client_surface(c) != old_keyboard_focus_surface)) { (!c || client_surface(c) != old_keyboard_focus_surface)) {
/* If an exclusive_focus layer is focused, don't focus or activate the /* If an exclusive_focus layer is focused, don't focus or activate
* client, but only update its position in fstack to render its border * the client, but only update its position in fstack to render its
* with focuscolor and focus it after the exclusive_focus layer is * border with focuscolor and focus it after the exclusive_focus
* closed. */ * layer is closed. */
Client *w = NULL; Client *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
int type = int type =
@ -2961,9 +3020,9 @@ void focusclient(Client *c, int lift) {
return; return;
} else if (w && w == exclusive_focus && client_wants_focus(w)) { } else if (w && w == exclusive_focus && client_wants_focus(w)) {
return; return;
/* Don't deactivate old_keyboard_focus_surface client if the new one /* Don't deactivate old_keyboard_focus_surface client if the new
* wants focus, as this causes issues with winecfg and probably * one wants focus, as this causes issues with winecfg and
* other clients */ * probably other clients */
} else if (w && !client_is_unmanaged(w) && } else if (w && !client_is_unmanaged(w) &&
(!c || !client_wants_focus(c))) { (!c || !client_wants_focus(c))) {
setborder_color(w); setborder_color(w);
@ -3026,6 +3085,9 @@ void inputdevice(struct wl_listener *listener, void *data) {
case WLR_INPUT_DEVICE_POINTER: case WLR_INPUT_DEVICE_POINTER:
createpointer(wlr_pointer_from_input_device(device)); createpointer(wlr_pointer_from_input_device(device));
break; break;
case WLR_INPUT_DEVICE_SWITCH:
createswitch(wlr_switch_from_input_device(device));
break;
default: default:
/* TODO handle other input device types */ /* TODO handle other input device types */
break; break;
@ -3033,7 +3095,8 @@ void inputdevice(struct wl_listener *listener, void *data) {
/* We need to let the wlr_seat know what our capabilities are, which is /* We need to let the wlr_seat know what our capabilities are, which is
* communiciated to the client. In dwl we always have a cursor, even if * communiciated to the client. In dwl we always have a cursor, even if
* there are no pointer devices, so we always include that capability. */ * there are no pointer devices, so we always include that capability.
*/
/* TODO do we actually require a cursor? */ /* TODO do we actually require a cursor? */
caps = WL_SEAT_CAPABILITY_POINTER; caps = WL_SEAT_CAPABILITY_POINTER;
if (!wl_list_empty(&kb_group->wlr_group->devices)) if (!wl_list_empty(&kb_group->wlr_group->devices))
@ -3061,8 +3124,8 @@ int // 17
keybinding(unsigned int mods, xkb_keysym_t sym, unsigned int keycode) { keybinding(unsigned int mods, xkb_keysym_t sym, unsigned int keycode) {
/* /*
* Here we handle compositor keybindings. This is when the compositor is * Here we handle compositor keybindings. This is when the compositor is
* processing keys, rather than passing them on to the client for its own * processing keys, rather than passing them on to the client for its
* processing. * own processing.
*/ */
int handled = 0; int handled = 0;
const KeyBinding *k; const KeyBinding *k;
@ -3208,8 +3271,8 @@ void keypress(struct wl_listener *listener, void *data) {
return; return;
/* don't pass when popup is focused /* don't pass when popup is focused
* this is better than having popups (like fuzzel or wmenu) closing while * this is better than having popups (like fuzzel or wmenu) closing
* typing in a passed keybind */ * while typing in a passed keybind */
pass = (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) || pass = (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) ||
!last_surface !last_surface
#ifdef XWAYLAND #ifdef XWAYLAND
@ -3355,7 +3418,9 @@ mapnotify(struct wl_listener *listener, void *data) {
c->image_capture_scene_surface = wlr_scene_surface_create( c->image_capture_scene_surface = wlr_scene_surface_create(
&c->image_capture_scene->tree, client_surface(c)); &c->image_capture_scene->tree, client_surface(c));
/* Handle unmanaged clients first so we can return prior create borders */ /* Handle unmanaged clients first so we can return prior create borders
*/
if (client_is_unmanaged(c)) { if (client_is_unmanaged(c)) {
/* Unmanaged clients always are floating */ /* Unmanaged clients always are floating */
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
@ -3424,7 +3489,8 @@ maximizenotify(struct wl_listener *listener, void *data) {
* Since xdg-shell protocol v5 we should ignore request of unsupported * Since xdg-shell protocol v5 we should ignore request of unsupported
* capabilities, just schedule a empty configure when the client uses <5 * capabilities, just schedule a empty configure when the client uses <5
* protocol version * protocol version
* wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ * wlr_xdg_surface_schedule_configure() is used to send an empty reply.
*/
// Client *c = wl_container_of(listener, c, maximize); // Client *c = wl_container_of(listener, c, maximize);
// if (wl_resource_get_version(c->surface.xdg->toplevel->resource) // if (wl_resource_get_version(c->surface.xdg->toplevel->resource)
// < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
@ -3470,7 +3536,8 @@ minimizenotify(struct wl_listener *listener, void *data) {
* Since xdg-shell protocol v5 we should ignore request of unsupported * Since xdg-shell protocol v5 we should ignore request of unsupported
* capabilities, just schedule a empty configure when the client uses <5 * capabilities, just schedule a empty configure when the client uses <5
* protocol version * protocol version
* wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ * wlr_xdg_surface_schedule_configure() is used to send an empty reply.
*/
// Client *c = wl_container_of(listener, c, maximize); // Client *c = wl_container_of(listener, c, maximize);
// if (wl_resource_get_version(c->surface.xdg->toplevel->resource) // if (wl_resource_get_version(c->surface.xdg->toplevel->resource)
// < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
@ -3492,12 +3559,12 @@ minimizenotify(struct wl_listener *listener, void *data) {
} }
void motionabsolute(struct wl_listener *listener, void *data) { void motionabsolute(struct wl_listener *listener, void *data) {
/* This event is forwarded by the cursor when a pointer emits an _absolute_ /* This event is forwarded by the cursor when a pointer emits an
* motion event, from 0..1 on each axis. This happens, for example, when * _absolute_ motion event, from 0..1 on each axis. This happens, for
* wlroots is running under a Wayland window rather than KMS+DRM, and you * example, when wlroots is running under a Wayland window rather than
* move the mouse over the window. You could enter the window from any edge, * KMS+DRM, and you move the mouse over the window. You could enter the
* so we have to warp the mouse there. There is also some hardware which * window from any edge, so we have to warp the mouse there. There is
* emits these events. */ * also some hardware which emits these events. */
struct wlr_pointer_motion_absolute_event *event = data; struct wlr_pointer_motion_absolute_event *event = data;
double lx, ly, dx, dy; double lx, ly, dx, dy;
@ -3598,9 +3665,9 @@ void motionnotify(unsigned int time, struct wlr_input_device *device, double dx,
return; return;
} }
/* If there's no client surface under the cursor, set the cursor image to a /* If there's no client surface under the cursor, set the cursor image
* default. This is what makes the cursor image appear when you move it * to a default. This is what makes the cursor image appear when you
* off of a client or over its border. */ * move it off of a client or over its border. */
if (!surface && !seat->drag && !cursor_hidden) if (!surface && !seat->drag && !cursor_hidden)
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
@ -3642,14 +3709,14 @@ void motionnotify(unsigned int time, struct wlr_input_device *device, double dx,
} }
void motionrelative(struct wl_listener *listener, void *data) { void motionrelative(struct wl_listener *listener, void *data) {
/* This event is forwarded by the cursor when a pointer emits a _relative_ /* This event is forwarded by the cursor when a pointer emits a
* pointer motion event (i.e. a delta) */ * _relative_ pointer motion event (i.e. a delta) */
struct wlr_pointer_motion_event *event = data; struct wlr_pointer_motion_event *event = data;
/* The cursor doesn't move unless we tell it to. The cursor automatically /* The cursor doesn't move unless we tell it to. The cursor
* handles constraining the motion to the output layout, as well as any * automatically handles constraining the motion to the output layout,
* special configuration applied for the specific input device which * as well as any special configuration applied for the specific input
* generated the event. You can pass NULL for the device if you want to move * device which generated the event. You can pass NULL for the device if
* the cursor around without any input. */ * you want to move the cursor around without any input. */
if (check_trackpad_disabled(event->pointer)) { if (check_trackpad_disabled(event->pointer)) {
return; return;
@ -3701,8 +3768,8 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) {
Monitor *m = wlr_output->data; Monitor *m = wlr_output->data;
struct wlr_output_state state; struct wlr_output_state state;
/* Ensure displays previously disabled by wlr-output-power-management-v1 /* Ensure displays previously disabled by
* are properly handled*/ * wlr-output-power-management-v1 are properly handled*/
m->asleep = 0; m->asleep = 0;
wlr_output_state_init(&state); wlr_output_state_init(&state);
@ -3967,13 +4034,13 @@ run(char *startup_cmd) {
die("startup: display_add_socket_auto"); die("startup: display_add_socket_auto");
setenv("WAYLAND_DISPLAY", socket, 1); setenv("WAYLAND_DISPLAY", socket, 1);
/* Start the backend. This will enumerate outputs and inputs, become the DRM /* Start the backend. This will enumerate outputs and inputs, become the
* master, etc */ * DRM master, etc */
if (!wlr_backend_start(backend)) if (!wlr_backend_start(backend))
die("startup: backend_start"); die("startup: backend_start");
/* Now that the socket exists and the backend is started, run the startup /* Now that the socket exists and the backend is started, run the
* command */ * startup command */
if (!startup_cmd) if (!startup_cmd)
startup_cmd = get_autostart_path(autostart_temp_path, startup_cmd = get_autostart_path(autostart_temp_path,
sizeof(autostart_temp_path)); sizeof(autostart_temp_path));
@ -4003,8 +4070,8 @@ run(char *startup_cmd) {
printstatus(); printstatus();
/* At this point the outputs are initialized, choose initial selmon based on /* At this point the outputs are initialized, choose initial selmon
* cursor position, and set default cursor image */ * based on cursor position, and set default cursor image */
selmon = xytomon(cursor->x, cursor->y); selmon = xytomon(cursor->x, cursor->y);
/* TODO hack to get cursor to display in its initial location (100, 100) /* TODO hack to get cursor to display in its initial location (100, 100)
@ -4027,11 +4094,13 @@ run(char *startup_cmd) {
} }
void setcursor(struct wl_listener *listener, void *data) { void setcursor(struct wl_listener *listener, void *data) {
/* This event is raised by the seat when a client provides a cursor image */ /* This event is raised by the seat when a client provides a cursor
* image */
struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_pointer_request_set_cursor_event *event = data;
/* If we're "grabbing" the cursor, don't use the client's image, we will /* If we're "grabbing" the cursor, don't use the client's image, we will
* restore it after "grabbing" sending a leave event, followed by a enter * restore it after "grabbing" sending a leave event, followed by a
* event, which will result in the client requesting set the cursor surface * enter event, which will result in the client requesting set the
* cursor surface
*/ */
if (cursor_mode != CurNormal && cursor_mode != CurPressed) if (cursor_mode != CurNormal && cursor_mode != CurPressed)
return; return;
@ -4436,14 +4505,14 @@ void setup(void) {
dpy = wl_display_create(); dpy = wl_display_create();
event_loop = wl_display_get_event_loop(dpy); event_loop = wl_display_get_event_loop(dpy);
pointer_manager = wlr_relative_pointer_manager_v1_create(dpy); pointer_manager = wlr_relative_pointer_manager_v1_create(dpy);
/* The backend is a wlroots feature which abstracts the underlying input and /* The backend is a wlroots feature which abstracts the underlying input
* output hardware. The autocreate option will choose the most suitable * and output hardware. The autocreate option will choose the most
* backend based on the current environment, such as opening an X11 window * suitable backend based on the current environment, such as opening an
* if an X11 server is running. The NULL argument here optionally allows you * X11 window if an X11 server is running. The NULL argument here
* to pass in a custom renderer if wlr_renderer doesn't meet your needs. The * optionally allows you to pass in a custom renderer if wlr_renderer
* backend uses the renderer, for example, to fall back to software cursors * doesn't meet your needs. The backend uses the renderer, for example,
* if the backend does not support hardware cursors (some older GPUs * to fall back to software cursors if the backend does not support
* don't). */ * hardware cursors (some older GPUs don't). */
if (!(backend = wlr_backend_autocreate(event_loop, &session))) if (!(backend = wlr_backend_autocreate(event_loop, &session)))
die("couldn't create backend"); die("couldn't create backend");
@ -4471,8 +4540,8 @@ void setup(void) {
/* Create shm, drm and linux_dmabuf interfaces by ourselves. /* Create shm, drm and linux_dmabuf interfaces by ourselves.
* The simplest way is call: * The simplest way is call:
* wlr_renderer_init_wl_display(drw); * wlr_renderer_init_wl_display(drw);
* but we need to create manually the linux_dmabuf interface to integrate it * but we need to create manually the linux_dmabuf interface to
* with wlr_scene. */ * integrate it with wlr_scene. */
wlr_renderer_init_wl_shm(drw, dpy); wlr_renderer_init_wl_shm(drw, dpy);
if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) { if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
@ -4490,11 +4559,11 @@ void setup(void) {
die("couldn't create allocator"); die("couldn't create allocator");
/* This creates some hands-off wlroots interfaces. The compositor is /* This creates some hands-off wlroots interfaces. The compositor is
* necessary for clients to allocate surfaces and the data device manager * necessary for clients to allocate surfaces and the data device
* handles the clipboard. Each of these wlroots interfaces has room for you * manager handles the clipboard. Each of these wlroots interfaces has
* to dig your fingers in and play with their behavior if you want. Note * room for you to dig your fingers in and play with their behavior if
* that the clients cannot set the selection directly without compositor * you want. Note that the clients cannot set the selection directly
* approval, see the setsel() function. */ * without compositor approval, see the setsel() function. */
compositor = wlr_compositor_create(dpy, 6, drw); compositor = wlr_compositor_create(dpy, 6, drw);
wlr_export_dmabuf_manager_v1_create(dpy); wlr_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy);
@ -4536,8 +4605,8 @@ void setup(void) {
wl_signal_add(&output_layout->events.change, &layout_change); wl_signal_add(&output_layout->events.change, &layout_change);
wlr_xdg_output_manager_v1_create(dpy, output_layout); wlr_xdg_output_manager_v1_create(dpy, output_layout);
/* Configure a listener to be notified when new outputs are available on the /* Configure a listener to be notified when new outputs are available on
* backend. */ * the backend. */
wl_list_init(&mons); wl_list_init(&mons);
wl_signal_add(&backend->events.new_output, &new_output); wl_signal_add(&backend->events.new_output, &new_output);
@ -4594,23 +4663,25 @@ void setup(void) {
wlr_cursor_attach_output_layout(cursor, output_layout); wlr_cursor_attach_output_layout(cursor, output_layout);
/* Creates an xcursor manager, another wlroots utility which loads up /* Creates an xcursor manager, another wlroots utility which loads up
* Xcursor themes to source cursor images from and makes sure that cursor * Xcursor themes to source cursor images from and makes sure that
* images are available at all scale factors on the screen (necessary for * cursor images are available at all scale factors on the screen
* HiDPI support). Scaled cursors will be loaded with each output. */ * (necessary for HiDPI support). Scaled cursors will be loaded with
* each output. */
// cursor_mgr = wlr_xcursor_manager_create(cursor_theme, 24); // cursor_mgr = wlr_xcursor_manager_create(cursor_theme, 24);
cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size); cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size);
/* /*
* wlr_cursor *only* displays an image on screen. It does not move around * wlr_cursor *only* displays an image on screen. It does not move
* when the pointer moves. However, we can attach input devices to it, and * around when the pointer moves. However, we can attach input devices
* it will generate aggregate events for all of them. In these events, we * to it, and it will generate aggregate events for all of them. In
* can choose how we want to process them, forwarding them to clients and * these events, we can choose how we want to process them, forwarding
* moving the cursor around. More detail on this process is described in my * them to clients and moving the cursor around. More detail on this
* input handling blog post: * process is described in my input handling blog post:
* *
* https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
* *
* And more comments are sprinkled throughout the notify functions above. * And more comments are sprinkled throughout the notify functions
* above.
*/ */
wl_signal_add(&cursor->events.motion, &cursor_motion); wl_signal_add(&cursor->events.motion, &cursor_motion);
wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
@ -4628,11 +4699,12 @@ void setup(void) {
/* /*
* Configures a seat, which is a single "seat" at which a user sits and * Configures a seat, which is a single "seat" at which a user sits and
* operates the computer. This conceptually includes up to one keyboard, * operates the computer. This conceptually includes up to one keyboard,
* pointer, touch, and drawing tablet device. We also rig up a listener to * pointer, touch, and drawing tablet device. We also rig up a listener
* let us know when new input devices are available on the backend. * to let us know when new input devices are available on the backend.
*/ */
wl_list_init(&keyboards); wl_list_init(&keyboards);
wl_list_init(&pointers); wl_list_init(&pointers);
wl_list_init(&switches);
wl_signal_add(&backend->events.new_input, &new_input_device); wl_signal_add(&backend->events.new_input, &new_input_device);
virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
@ -4913,7 +4985,8 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
} }
void unmapnotify(struct wl_listener *listener, void *data) { void unmapnotify(struct wl_listener *listener, void *data) {
/* Called when the surface is unmapped, and should no longer be shown. */ /* Called when the surface is unmapped, and should no longer be shown.
*/
Client *c = wl_container_of(listener, c, unmap); Client *c = wl_container_of(listener, c, unmap);
Monitor *m; Monitor *m;
c->iskilling = 1; c->iskilling = 1;
@ -5026,7 +5099,8 @@ void updatemons(struct wl_listener *listener, void *data) {
config_head = config_head =
wlr_output_configuration_head_v1_create(config, m->wlr_output); wlr_output_configuration_head_v1_create(config, m->wlr_output);
config_head->state.enabled = 0; config_head->state.enabled = 0;
/* Remove this output from the layout to avoid cursor enter inside it */ /* Remove this output from the layout to avoid cursor enter inside
* it */
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
closemon(m); closemon(m);
m->m = m->w = (struct wlr_box){0}; m->m = m->w = (struct wlr_box){0};
@ -5101,7 +5175,8 @@ void updatemons(struct wl_listener *listener, void *data) {
resize(c, m->m, 0); resize(c, m->m, 0);
/* Try to re-set the gamma LUT when updating monitors, /* Try to re-set the gamma LUT when updating monitors,
* it's only really needed when enabling a disabled output, but meh. */ * it's only really needed when enabling a disabled output, but meh.
*/
m->gamma_lut_changed = 1; m->gamma_lut_changed = 1;
config_head->state.x = m->m.x; config_head->state.x = m->m.x;
@ -5128,8 +5203,8 @@ void updatemons(struct wl_listener *listener, void *data) {
/* FIXME: figure out why the cursor image is at 0,0 after turning all /* FIXME: figure out why the cursor image is at 0,0 after turning all
* the monitors on. * the monitors on.
* Move the cursor image where it used to be. It does not generate a * Move the cursor image where it used to be. It does not generate a
* wl_pointer.motion event for the clients, it's only the image what it's * wl_pointer.motion event for the clients, it's only the image what
* at the wrong position after all. */ * it's at the wrong position after all. */
wlr_cursor_move(cursor, NULL, 0, 0); wlr_cursor_move(cursor, NULL, 0, 0);
wlr_output_manager_v1_set_configuration(output_mgr, config); wlr_output_manager_v1_set_configuration(output_mgr, config);
@ -5423,7 +5498,8 @@ int main(int argc, char *argv[]) {
if (optind < argc) if (optind < argc)
goto usage; goto usage;
/* Wayland requires XDG_RUNTIME_DIR for creating its communications socket /* Wayland requires XDG_RUNTIME_DIR for creating its communications
* socket
*/ */
if (!getenv("XDG_RUNTIME_DIR")) if (!getenv("XDG_RUNTIME_DIR"))
die("XDG_RUNTIME_DIR must be set"); die("XDG_RUNTIME_DIR must be set");