diff options
Diffstat (limited to 'swaybar/input.c')
-rw-r--r-- | swaybar/input.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/swaybar/input.c b/swaybar/input.c new file mode 100644 index 00000000..31137f44 --- /dev/null +++ b/swaybar/input.c | |||
@@ -0,0 +1,286 @@ | |||
1 | #include <assert.h> | ||
2 | #ifdef __FreeBSD__ | ||
3 | #include <dev/evdev/input-event-codes.h> | ||
4 | #else | ||
5 | #include <linux/input-event-codes.h> | ||
6 | #endif | ||
7 | #include <wayland-client.h> | ||
8 | #include <wayland-cursor.h> | ||
9 | #include <wlr/util/log.h> | ||
10 | #include "list.h" | ||
11 | #include "log.h" | ||
12 | #include "swaybar/bar.h" | ||
13 | #include "swaybar/config.h" | ||
14 | #include "swaybar/input.h" | ||
15 | #include "swaybar/ipc.h" | ||
16 | |||
17 | void free_hotspots(struct wl_list *list) { | ||
18 | struct swaybar_hotspot *hotspot, *tmp; | ||
19 | wl_list_for_each_safe(hotspot, tmp, list, link) { | ||
20 | wl_list_remove(&hotspot->link); | ||
21 | if (hotspot->destroy) { | ||
22 | hotspot->destroy(hotspot->data); | ||
23 | } | ||
24 | free(hotspot); | ||
25 | } | ||
26 | } | ||
27 | |||
28 | static enum x11_button wl_button_to_x11_button(uint32_t button) { | ||
29 | switch (button) { | ||
30 | case BTN_LEFT: | ||
31 | return LEFT; | ||
32 | case BTN_MIDDLE: | ||
33 | return MIDDLE; | ||
34 | case BTN_RIGHT: | ||
35 | return RIGHT; | ||
36 | case BTN_SIDE: | ||
37 | return BACK; | ||
38 | case BTN_EXTRA: | ||
39 | return FORWARD; | ||
40 | default: | ||
41 | return NONE; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | static enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) { | ||
46 | switch (axis) { | ||
47 | case WL_POINTER_AXIS_VERTICAL_SCROLL: | ||
48 | return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; | ||
49 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: | ||
50 | return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; | ||
51 | default: | ||
52 | wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); | ||
53 | return NONE; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | ||
58 | uint32_t serial, struct wl_surface *surface, | ||
59 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
60 | struct swaybar *bar = data; | ||
61 | struct swaybar_pointer *pointer = &bar->pointer; | ||
62 | struct swaybar_output *output; | ||
63 | wl_list_for_each(output, &bar->outputs, link) { | ||
64 | if (output->surface == surface) { | ||
65 | pointer->current = output; | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | int max_scale = 1; | ||
70 | struct swaybar_output *_output; | ||
71 | wl_list_for_each(_output, &bar->outputs, link) { | ||
72 | if (_output->scale > max_scale) { | ||
73 | max_scale = _output->scale; | ||
74 | } | ||
75 | } | ||
76 | wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale); | ||
77 | wl_surface_attach(pointer->cursor_surface, | ||
78 | wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); | ||
79 | wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, | ||
80 | pointer->cursor_image->hotspot_x / max_scale, | ||
81 | pointer->cursor_image->hotspot_y / max_scale); | ||
82 | wl_surface_commit(pointer->cursor_surface); | ||
83 | } | ||
84 | |||
85 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | ||
86 | uint32_t serial, struct wl_surface *surface) { | ||
87 | struct swaybar *bar = data; | ||
88 | bar->pointer.current = NULL; | ||
89 | } | ||
90 | |||
91 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | ||
92 | uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
93 | struct swaybar *bar = data; | ||
94 | bar->pointer.x = wl_fixed_to_int(surface_x); | ||
95 | bar->pointer.y = wl_fixed_to_int(surface_y); | ||
96 | } | ||
97 | |||
98 | static bool check_bindings(struct swaybar *bar, uint32_t x11_button, | ||
99 | uint32_t state) { | ||
100 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; | ||
101 | for (int i = 0; i < bar->config->bindings->length; i++) { | ||
102 | struct swaybar_binding *binding = bar->config->bindings->items[i]; | ||
103 | if (binding->button == x11_button && binding->release == released) { | ||
104 | ipc_execute_binding(bar, binding); | ||
105 | return true; | ||
106 | } | ||
107 | } | ||
108 | return false; | ||
109 | } | ||
110 | |||
111 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | ||
112 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { | ||
113 | struct swaybar *bar = data; | ||
114 | struct swaybar_pointer *pointer = &bar->pointer; | ||
115 | struct swaybar_output *output = pointer->current; | ||
116 | if (!sway_assert(output, "button with no active output")) { | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | if (check_bindings(bar, wl_button_to_x11_button(button), state)) { | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | if (state != WL_POINTER_BUTTON_STATE_PRESSED) { | ||
125 | return; | ||
126 | } | ||
127 | struct swaybar_hotspot *hotspot; | ||
128 | wl_list_for_each(hotspot, &output->hotspots, link) { | ||
129 | double x = pointer->x * output->scale; | ||
130 | double y = pointer->y * output->scale; | ||
131 | if (x >= hotspot->x | ||
132 | && y >= hotspot->y | ||
133 | && x < hotspot->x + hotspot->width | ||
134 | && y < hotspot->y + hotspot->height) { | ||
135 | if (HOTSPOT_IGNORE == hotspot->callback(output, pointer->x, pointer->y, | ||
136 | wl_button_to_x11_button(button), hotspot->data)) { | ||
137 | return; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | ||
144 | uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
145 | struct swaybar *bar = data; | ||
146 | struct swaybar_pointer *pointer = &bar->pointer; | ||
147 | struct swaybar_output *output = pointer->current; | ||
148 | if (!sway_assert(output, "axis with no active output")) { | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | // If there is a button press binding, execute it, skip default behavior, | ||
153 | // and check button release bindings | ||
154 | if (check_bindings(bar, wl_axis_to_x11_button(axis, value), | ||
155 | WL_POINTER_BUTTON_STATE_PRESSED)) { | ||
156 | check_bindings(bar, wl_axis_to_x11_button(axis, value), | ||
157 | WL_POINTER_BUTTON_STATE_RELEASED); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | struct swaybar_hotspot *hotspot; | ||
162 | wl_list_for_each(hotspot, &output->hotspots, link) { | ||
163 | double x = pointer->x * output->scale; | ||
164 | double y = pointer->y * output->scale; | ||
165 | if (x >= hotspot->x | ||
166 | && y >= hotspot->y | ||
167 | && x < hotspot->x + hotspot->width | ||
168 | && y < hotspot->y + hotspot->height) { | ||
169 | if (HOTSPOT_IGNORE == hotspot->callback( | ||
170 | output, pointer->x, pointer->y, | ||
171 | wl_axis_to_x11_button(axis, value), hotspot->data)) { | ||
172 | return; | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | double amt = wl_fixed_to_double(value); | ||
178 | if (amt == 0.0) { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | // last doesn't actually need initialization, | ||
183 | // but gcc (7.3.1) is too dumb to figure it out | ||
184 | struct swaybar_workspace *first = NULL; | ||
185 | struct swaybar_workspace *active = NULL; | ||
186 | struct swaybar_workspace *last = NULL; | ||
187 | |||
188 | struct swaybar_workspace *iter; | ||
189 | wl_list_for_each(iter, &output->workspaces, link) { | ||
190 | if (!first) { | ||
191 | first = iter; | ||
192 | } | ||
193 | |||
194 | if (iter->visible) { | ||
195 | active = iter; | ||
196 | } | ||
197 | |||
198 | last = iter; | ||
199 | } | ||
200 | |||
201 | if (!sway_assert(active, "axis with null workspace")) { | ||
202 | return; | ||
203 | } | ||
204 | |||
205 | struct swaybar_workspace *new; | ||
206 | |||
207 | if (amt > 0.0) { | ||
208 | if (active == first) { | ||
209 | if (!bar->config->wrap_scroll) { | ||
210 | return; | ||
211 | } | ||
212 | new = last; | ||
213 | } | ||
214 | |||
215 | new = wl_container_of(active->link.prev, new, link); | ||
216 | } else { | ||
217 | if (active == last) { | ||
218 | if (!bar->config->wrap_scroll) { | ||
219 | return; | ||
220 | } | ||
221 | new = first; | ||
222 | } | ||
223 | |||
224 | new = wl_container_of(active->link.next, new, link); | ||
225 | } | ||
226 | |||
227 | ipc_send_workspace_command(bar, new->name); | ||
228 | |||
229 | // Check button release bindings | ||
230 | check_bindings(bar, wl_axis_to_x11_button(axis, value), | ||
231 | WL_POINTER_BUTTON_STATE_RELEASED); | ||
232 | } | ||
233 | |||
234 | static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { | ||
235 | // Who cares | ||
236 | } | ||
237 | |||
238 | static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, | ||
239 | uint32_t axis_source) { | ||
240 | // Who cares | ||
241 | } | ||
242 | |||
243 | static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, | ||
244 | uint32_t time, uint32_t axis) { | ||
245 | // Who cares | ||
246 | } | ||
247 | |||
248 | static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, | ||
249 | uint32_t axis, int32_t discrete) { | ||
250 | // Who cares | ||
251 | } | ||
252 | |||
253 | struct wl_pointer_listener pointer_listener = { | ||
254 | .enter = wl_pointer_enter, | ||
255 | .leave = wl_pointer_leave, | ||
256 | .motion = wl_pointer_motion, | ||
257 | .button = wl_pointer_button, | ||
258 | .axis = wl_pointer_axis, | ||
259 | .frame = wl_pointer_frame, | ||
260 | .axis_source = wl_pointer_axis_source, | ||
261 | .axis_stop = wl_pointer_axis_stop, | ||
262 | .axis_discrete = wl_pointer_axis_discrete, | ||
263 | }; | ||
264 | |||
265 | static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, | ||
266 | enum wl_seat_capability caps) { | ||
267 | struct swaybar *bar = data; | ||
268 | if (bar->pointer.pointer != NULL) { | ||
269 | wl_pointer_release(bar->pointer.pointer); | ||
270 | bar->pointer.pointer = NULL; | ||
271 | } | ||
272 | if ((caps & WL_SEAT_CAPABILITY_POINTER)) { | ||
273 | bar->pointer.pointer = wl_seat_get_pointer(wl_seat); | ||
274 | wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | static void seat_handle_name(void *data, struct wl_seat *wl_seat, | ||
279 | const char *name) { | ||
280 | // Who cares | ||
281 | } | ||
282 | |||
283 | const struct wl_seat_listener seat_listener = { | ||
284 | .capabilities = seat_handle_capabilities, | ||
285 | .name = seat_handle_name, | ||
286 | }; | ||