aboutsummaryrefslogtreecommitdiffstats
path: root/swaynag/nagbar.c
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-07-27 11:19:42 -0400
committerLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-08-01 22:47:54 -0400
commita4f7bf23b21d0d838a8a19261d5fd69719003a03 (patch)
treee46a226d8c3ded2d93e1933323263b6756199a68 /swaynag/nagbar.c
parentSupport a detailed message in swaynagbar (diff)
downloadsway-a4f7bf23b21d0d838a8a19261d5fd69719003a03.tar.gz
sway-a4f7bf23b21d0d838a8a19261d5fd69719003a03.tar.zst
sway-a4f7bf23b21d0d838a8a19261d5fd69719003a03.zip
Address first round review for swaynag
Diffstat (limited to 'swaynag/nagbar.c')
-rw-r--r--swaynag/nagbar.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/swaynag/nagbar.c b/swaynag/nagbar.c
new file mode 100644
index 00000000..6647e8c2
--- /dev/null
+++ b/swaynag/nagbar.c
@@ -0,0 +1,420 @@
1#define _XOPEN_SOURCE 500
2#include <assert.h>
3#include <sys/stat.h>
4#include <sys/wait.h>
5#include <wayland-client.h>
6#include <wayland-cursor.h>
7#include "log.h"
8#include "list.h"
9#include "swaynag/nagbar.h"
10#include "swaynag/render.h"
11#include "wlr-layer-shell-unstable-v1-client-protocol.h"
12
13static void nop() {
14 // Intentionally left blank
15}
16
17static bool terminal_execute(char *terminal, char *command) {
18 char fname[] = "/tmp/swaynagXXXXXX";
19 FILE *tmp= fdopen(mkstemp(fname), "w");
20 if (!tmp) {
21 wlr_log(WLR_ERROR, "Failed to create temp script");
22 return false;
23 }
24 wlr_log(WLR_DEBUG, "Created temp script: %s", fname);
25 fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command);
26 fclose(tmp);
27 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR);
28 char cmd[strlen(terminal) + strlen(" -e ") + strlen(fname) + 1];
29 sprintf(cmd, "%s -e %s", terminal, fname);
30 execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
31 return true;
32}
33
34static void nagbar_button_execute(struct sway_nagbar *nagbar,
35 struct sway_nagbar_button *button) {
36 wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action);
37 if (button->type == NAGBAR_ACTION_DISMISS) {
38 nagbar->run_display = false;
39 } else if (button->type == NAGBAR_ACTION_EXPAND) {
40 nagbar->details.visible = !nagbar->details.visible;
41 render_frame(nagbar);
42 } else {
43 if (fork() == 0) {
44 // Child process. Will be used to prevent zombie processes
45 setsid();
46 if (fork() == 0) {
47 // Child of the child. Will be reparented to the init process
48 char *terminal = getenv("TERMINAL");
49 if (terminal && strlen(terminal)) {
50 wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal);
51 if (!terminal_execute(terminal, button->action)) {
52 nagbar_destroy(nagbar);
53 exit(EXIT_FAILURE);
54 }
55 } else {
56 wlr_log(WLR_DEBUG, "$TERMINAL not found. Running directly");
57 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL);
58 }
59 }
60 exit(EXIT_SUCCESS);
61 }
62 }
63 wait(0);
64}
65
66static void layer_surface_configure(void *data,
67 struct zwlr_layer_surface_v1 *surface,
68 uint32_t serial, uint32_t width, uint32_t height) {
69 struct sway_nagbar *nagbar = data;
70 nagbar->width = width;
71 nagbar->height = height;
72 zwlr_layer_surface_v1_ack_configure(surface, serial);
73 render_frame(nagbar);
74}
75
76static void layer_surface_closed(void *data,
77 struct zwlr_layer_surface_v1 *surface) {
78 struct sway_nagbar *nagbar = data;
79 nagbar_destroy(nagbar);
80}
81
82static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
83 .configure = layer_surface_configure,
84 .closed = layer_surface_closed,
85};
86
87static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
88 uint32_t serial, struct wl_surface *surface,
89 wl_fixed_t surface_x, wl_fixed_t surface_y) {
90 struct sway_nagbar *nagbar = data;
91 struct sway_nagbar_pointer *pointer = &nagbar->pointer;
92 wl_surface_set_buffer_scale(pointer->cursor_surface, nagbar->scale);
93 wl_surface_attach(pointer->cursor_surface,
94 wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
95 wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface,
96 pointer->cursor_image->hotspot_x / nagbar->scale,
97 pointer->cursor_image->hotspot_y / nagbar->scale);
98 wl_surface_commit(pointer->cursor_surface);
99}
100
101static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
102 uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
103 struct sway_nagbar *nagbar = data;
104 nagbar->pointer.x = wl_fixed_to_int(surface_x);
105 nagbar->pointer.y = wl_fixed_to_int(surface_y);
106}
107
108static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
109 uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
110 struct sway_nagbar *nagbar = data;
111
112 if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
113 return;
114 }
115
116 double x = nagbar->pointer.x * nagbar->scale;
117 double y = nagbar->pointer.y * nagbar->scale;
118 for (int i = 0; i < nagbar->buttons->length; i++) {
119 struct sway_nagbar_button *nagbutton = nagbar->buttons->items[i];
120 if (x >= nagbutton->x
121 && y >= nagbutton->y
122 && x < nagbutton->x + nagbutton->width
123 && y < nagbutton->y + nagbutton->height) {
124 nagbar_button_execute(nagbar, nagbutton);
125 return;
126 }
127 }
128
129 if (nagbar->details.visible &&
130 nagbar->details.total_lines != nagbar->details.visible_lines) {
131 struct sway_nagbar_button button_up = nagbar->details.button_up;
132 if (x >= button_up.x
133 && y >= button_up.y
134 && x < button_up.x + button_up.width
135 && y < button_up.y + button_up.height
136 && nagbar->details.offset > 0) {
137 nagbar->details.offset--;
138 render_frame(nagbar);
139 return;
140 }
141
142 struct sway_nagbar_button button_down = nagbar->details.button_down;
143 int bot = nagbar->details.total_lines - nagbar->details.visible_lines;
144 if (x >= button_down.x
145 && y >= button_down.y
146 && x < button_down.x + button_down.width
147 && y < button_down.y + button_down.height
148 && nagbar->details.offset < bot) {
149 nagbar->details.offset++;
150 render_frame(nagbar);
151 return;
152 }
153 }
154}
155
156static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
157 uint32_t time, uint32_t axis, wl_fixed_t value) {
158 struct sway_nagbar *nagbar = data;
159 if (!nagbar->details.visible
160 || nagbar->pointer.x < nagbar->details.x
161 || nagbar->pointer.y < nagbar->details.y
162 || nagbar->pointer.x >= nagbar->details.x + nagbar->details.width
163 || nagbar->pointer.y >= nagbar->details.y + nagbar->details.height
164 || nagbar->details.total_lines == nagbar->details.visible_lines) {
165 return;
166 }
167
168 int direction = wl_fixed_to_int(value);
169 int bot = nagbar->details.total_lines - nagbar->details.visible_lines;
170 if (direction < 0 && nagbar->details.offset > 0) {
171 nagbar->details.offset--;
172 } else if (direction > 0 && nagbar->details.offset < bot) {
173 nagbar->details.offset++;
174 }
175
176 render_frame(nagbar);
177}
178
179static struct wl_pointer_listener pointer_listener = {
180 .enter = wl_pointer_enter,
181 .leave = nop,
182 .motion = wl_pointer_motion,
183 .button = wl_pointer_button,
184 .axis = wl_pointer_axis,
185 .frame = nop,
186 .axis_source = nop,
187 .axis_stop = nop,
188 .axis_discrete = nop,
189};
190
191static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
192 enum wl_seat_capability caps) {
193 struct sway_nagbar *nagbar = data;
194 if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
195 nagbar->pointer.pointer = wl_seat_get_pointer(wl_seat);
196 wl_pointer_add_listener(nagbar->pointer.pointer, &pointer_listener,
197 nagbar);
198 }
199}
200
201const struct wl_seat_listener seat_listener = {
202 .capabilities = seat_handle_capabilities,
203 .name = nop,
204};
205
206static void output_scale(void *data, struct wl_output *output,
207 int32_t factor) {
208 struct sway_nagbar *nagbar = data;
209 nagbar->scale = factor;
210 render_frame(nagbar);
211}
212
213static struct wl_output_listener output_listener = {
214 .geometry = nop,
215 .mode = nop,
216 .done = nop,
217 .scale = output_scale,
218};
219
220struct output_state {
221 struct wl_output *wl_output;
222 uint32_t wl_name;
223 struct zxdg_output_v1 *xdg_output;
224 struct sway_nagbar *nagbar;
225};
226
227static void xdg_output_handle_name(void *data,
228 struct zxdg_output_v1 *xdg_output, const char *name) {
229 struct output_state *state = data;
230 char *outname = state->nagbar->output.name;
231 wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname);
232 if ((!outname && !state->nagbar->output.wl_output)
233 || (name && outname && strcmp(name, outname) == 0)) {
234 wlr_log(WLR_DEBUG, "Using output %s", name);
235 state->nagbar->output.wl_output = state->wl_output;
236 state->nagbar->output.wl_name = state->wl_name;
237 wl_output_add_listener(state->nagbar->output.wl_output,
238 &output_listener, state->nagbar);
239 wl_display_roundtrip(state->nagbar->display);
240 zxdg_output_v1_destroy(state->xdg_output);
241 } else {
242 zxdg_output_v1_destroy(state->xdg_output);
243 wl_output_destroy(state->wl_output);
244 }
245 state->nagbar->querying_outputs--;
246 free(state);
247}
248
249static struct zxdg_output_v1_listener xdg_output_listener = {
250 .logical_position = nop,
251 .logical_size = nop,
252 .done = nop,
253 .name = xdg_output_handle_name,
254 .description = nop,
255};
256
257static void handle_global(void *data, struct wl_registry *registry,
258 uint32_t name, const char *interface, uint32_t version) {
259 struct sway_nagbar *nagbar = data;
260 if (strcmp(interface, wl_compositor_interface.name) == 0) {
261 nagbar->compositor = wl_registry_bind(registry, name,
262 &wl_compositor_interface, 3);
263 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
264 nagbar->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
265 wl_seat_add_listener(nagbar->seat, &seat_listener, nagbar);
266 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
267 nagbar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
268 } else if (strcmp(interface, wl_output_interface.name) == 0) {
269 if (!nagbar->output.wl_output && nagbar->xdg_output_manager) {
270 nagbar->querying_outputs++;
271 struct output_state *state =
272 calloc(1, sizeof(struct output_state));
273 state->nagbar = nagbar;
274 state->wl_output = wl_registry_bind(registry, name,
275 &wl_output_interface, 3);
276 state->wl_name = name;
277 state->xdg_output = zxdg_output_manager_v1_get_xdg_output(
278 nagbar->xdg_output_manager, state->wl_output);
279 zxdg_output_v1_add_listener(state->xdg_output,
280 &xdg_output_listener, state);
281 } else if (!nagbar->output.wl_output && !nagbar->xdg_output_manager) {
282 wlr_log(WLR_ERROR, "Warning: zxdg_output_manager_v1 not supported."
283 " Falling back to first detected output");
284 nagbar->output.wl_output = wl_registry_bind(registry, name,
285 &wl_output_interface, 3);
286 nagbar->output.wl_name = name;
287 wl_output_add_listener(nagbar->output.wl_output,
288 &output_listener, nagbar);
289 }
290 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
291 nagbar->layer_shell = wl_registry_bind(
292 registry, name, &zwlr_layer_shell_v1_interface, 1);
293 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0
294 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
295 nagbar->xdg_output_manager = wl_registry_bind(registry, name,
296 &zxdg_output_manager_v1_interface,
297 ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
298 }
299}
300
301static void handle_global_remove(void *data, struct wl_registry *registry,
302 uint32_t name) {
303 struct sway_nagbar *nagbar = data;
304 if (nagbar->output.wl_name == name) {
305 nagbar->run_display = false;
306 }
307}
308
309static const struct wl_registry_listener registry_listener = {
310 .global = handle_global,
311 .global_remove = handle_global_remove,
312};
313
314void nagbar_setup(struct sway_nagbar *nagbar) {
315 nagbar->display = wl_display_connect(NULL);
316 assert(nagbar->display);
317
318 nagbar->scale = 1;
319
320 struct wl_registry *registry = wl_display_get_registry(nagbar->display);
321 wl_registry_add_listener(registry, &registry_listener, nagbar);
322 wl_display_roundtrip(nagbar->display);
323 assert(nagbar->compositor && nagbar->layer_shell && nagbar->shm);
324
325 while (nagbar->querying_outputs > 0) {
326 wl_display_roundtrip(nagbar->display);
327 }
328
329 if (!nagbar->output.wl_output) {
330 if (nagbar->output.name) {
331 wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name);
332 } else {
333 wlr_log(WLR_ERROR, "No outputs detected");
334 }
335 nagbar_destroy(nagbar);
336 exit(EXIT_FAILURE);
337 }
338
339 struct sway_nagbar_pointer *pointer = &nagbar->pointer;
340 int scale = nagbar->scale < 1 ? 1 : nagbar->scale;
341 pointer->cursor_theme = wl_cursor_theme_load(
342 NULL, 24 * scale, nagbar->shm);
343 assert(pointer->cursor_theme);
344 struct wl_cursor *cursor =
345 wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
346 assert(cursor);
347 pointer->cursor_image = cursor->images[0];
348 pointer->cursor_surface = wl_compositor_create_surface(nagbar->compositor);
349 assert(pointer->cursor_surface);
350
351 nagbar->surface = wl_compositor_create_surface(nagbar->compositor);
352 assert(nagbar->surface);
353 nagbar->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
354 nagbar->layer_shell, nagbar->surface, nagbar->output.wl_output,
355 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag");
356 assert(nagbar->layer_surface);
357 zwlr_layer_surface_v1_add_listener(nagbar->layer_surface,
358 &layer_surface_listener, nagbar);
359 zwlr_layer_surface_v1_set_anchor(nagbar->layer_surface, nagbar->anchors);
360
361 wl_registry_destroy(registry);
362}
363
364void nagbar_run(struct sway_nagbar *nagbar) {
365 nagbar->run_display = true;
366 render_frame(nagbar);
367 while (nagbar->run_display && wl_display_dispatch(nagbar->display) != -1) {
368 // This is intentionally left blank
369 }
370}
371
372void nagbar_destroy(struct sway_nagbar *nagbar) {
373 nagbar->run_display = false;
374
375 free(nagbar->message);
376 free(nagbar->font);
377 while (nagbar->buttons->length) {
378 struct sway_nagbar_button *button = nagbar->buttons->items[0];
379 list_del(nagbar->buttons, 0);
380 free(button->text);
381 free(button->action);
382 free(button);
383 }
384 list_free(nagbar->buttons);
385 free(nagbar->details.message);
386 free(nagbar->details.button_up.text);
387 free(nagbar->details.button_down.text);
388
389 if (nagbar->layer_surface) {
390 zwlr_layer_surface_v1_destroy(nagbar->layer_surface);
391 }
392
393 if (nagbar->surface) {
394 wl_surface_destroy(nagbar->surface);
395 }
396
397 if (nagbar->output.wl_output) {
398 wl_output_destroy(nagbar->output.wl_output);
399 }
400
401 if (&nagbar->buffers[0]) {
402 destroy_buffer(&nagbar->buffers[0]);
403 }
404
405 if (&nagbar->buffers[1]) {
406 destroy_buffer(&nagbar->buffers[1]);
407 }
408
409 if (nagbar->compositor) {
410 wl_compositor_destroy(nagbar->compositor);
411 }
412
413 if (nagbar->shm) {
414 wl_shm_destroy(nagbar->shm);
415 }
416
417 if (nagbar->display) {
418 wl_display_disconnect(nagbar->display);
419 }
420}