aboutsummaryrefslogtreecommitdiffstats
path: root/swaynagbar/nagbar.c
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-07-25 21:57:19 -0400
committerLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-08-01 22:47:54 -0400
commit88bc4b528ef3e1af3598b513dd5c1572dd09ec23 (patch)
tree535543a1e5bc6d59c96af249f3d3c9953a68a2aa /swaynagbar/nagbar.c
parentArrange output in arrange_layers and commit dirty (diff)
downloadsway-88bc4b528ef3e1af3598b513dd5c1572dd09ec23.tar.gz
sway-88bc4b528ef3e1af3598b513dd5c1572dd09ec23.tar.zst
sway-88bc4b528ef3e1af3598b513dd5c1572dd09ec23.zip
Implements swaynagbar
Diffstat (limited to 'swaynagbar/nagbar.c')
-rw-r--r--swaynagbar/nagbar.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/swaynagbar/nagbar.c b/swaynagbar/nagbar.c
new file mode 100644
index 00000000..22e5aff4
--- /dev/null
+++ b/swaynagbar/nagbar.c
@@ -0,0 +1,364 @@
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 "swaynagbar/nagbar.h"
10#include "swaynagbar/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/swaynagbarXXXXXX";
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->action) {
38 nagbar->run_display = false;
39 } else {
40 if (fork() == 0) {
41 // Child process. Will be used to prevent zombie processes
42 setsid();
43 if (fork() == 0) {
44 // Child of the child. Will be reparented to the init process
45 char *terminal = getenv("TERMINAL");
46 if (terminal && strlen(terminal)) {
47 wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal);
48 if (!terminal_execute(terminal, button->action)) {
49 nagbar_destroy(nagbar);
50 exit(EXIT_FAILURE);
51 }
52 } else {
53 wlr_log(WLR_DEBUG, "$TERMINAL not found. Running directly");
54 execl("/bin/sh", "/bin/sh", "-c", button->action, NULL);
55 }
56 }
57 exit(EXIT_SUCCESS);
58 }
59 }
60 wait(0);
61}
62
63static void layer_surface_configure(void *data,
64 struct zwlr_layer_surface_v1 *surface,
65 uint32_t serial, uint32_t width, uint32_t height) {
66 struct sway_nagbar *nagbar = data;
67 nagbar->width = width;
68 nagbar->height = height;
69 zwlr_layer_surface_v1_ack_configure(surface, serial);
70 render_frame(nagbar);
71}
72
73static void layer_surface_closed(void *data,
74 struct zwlr_layer_surface_v1 *surface) {
75 struct sway_nagbar *nagbar = data;
76 nagbar_destroy(nagbar);
77}
78
79static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
80 .configure = layer_surface_configure,
81 .closed = layer_surface_closed,
82};
83
84static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
85 uint32_t serial, struct wl_surface *surface,
86 wl_fixed_t surface_x, wl_fixed_t surface_y) {
87 struct sway_nagbar *nagbar = data;
88 struct sway_nagbar_pointer *pointer = &nagbar->pointer;
89 wl_surface_set_buffer_scale(pointer->cursor_surface, nagbar->scale);
90 wl_surface_attach(pointer->cursor_surface,
91 wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
92 wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface,
93 pointer->cursor_image->hotspot_x / nagbar->scale,
94 pointer->cursor_image->hotspot_y / nagbar->scale);
95 wl_surface_commit(pointer->cursor_surface);
96}
97
98static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
99 uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
100 struct sway_nagbar *nagbar = data;
101 nagbar->pointer.x = wl_fixed_to_int(surface_x);
102 nagbar->pointer.y = wl_fixed_to_int(surface_y);
103}
104
105static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
106 uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
107 struct sway_nagbar *nagbar = data;
108
109 if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
110 return;
111 }
112
113 double x = nagbar->pointer.x * nagbar->scale;
114 double y = nagbar->pointer.y * nagbar->scale;
115 for (int i = 0; i < nagbar->buttons->length; i++) {
116 struct sway_nagbar_button *nagbutton = nagbar->buttons->items[i];
117 if (x >= nagbutton->x
118 && y >= nagbutton->y
119 && x < nagbutton->x + nagbutton->width
120 && y < nagbutton->y + nagbutton->height) {
121 nagbar_button_execute(nagbar, nagbutton);
122 }
123 }
124}
125
126static struct wl_pointer_listener pointer_listener = {
127 .enter = wl_pointer_enter,
128 .leave = nop,
129 .motion = wl_pointer_motion,
130 .button = wl_pointer_button,
131 .axis = nop,
132 .frame = nop,
133 .axis_source = nop,
134 .axis_stop = nop,
135 .axis_discrete = nop,
136};
137
138static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
139 enum wl_seat_capability caps) {
140 struct sway_nagbar *nagbar = data;
141 if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
142 nagbar->pointer.pointer = wl_seat_get_pointer(wl_seat);
143 wl_pointer_add_listener(nagbar->pointer.pointer, &pointer_listener,
144 nagbar);
145 }
146}
147
148const struct wl_seat_listener seat_listener = {
149 .capabilities = seat_handle_capabilities,
150 .name = nop,
151};
152
153static void output_scale(void *data, struct wl_output *output,
154 int32_t factor) {
155 struct sway_nagbar *nagbar = data;
156 nagbar->scale = factor;
157 render_frame(nagbar);
158}
159
160static struct wl_output_listener output_listener = {
161 .geometry = nop,
162 .mode = nop,
163 .done = nop,
164 .scale = output_scale,
165};
166
167struct output_state {
168 struct wl_output *wl_output;
169 uint32_t wl_name;
170 struct zxdg_output_v1 *xdg_output;
171 struct sway_nagbar *nagbar;
172};
173
174static void xdg_output_handle_name(void *data,
175 struct zxdg_output_v1 *xdg_output, const char *name) {
176 struct output_state *state = data;
177 char *outname = state->nagbar->output.name;
178 wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname);
179 if ((!outname && !state->nagbar->output.wl_output)
180 || (name && outname && strcmp(name, outname) == 0)) {
181 wlr_log(WLR_DEBUG, "Using output %s", name);
182 state->nagbar->output.wl_output = state->wl_output;
183 state->nagbar->output.wl_name = state->wl_name;
184 wl_output_add_listener(state->nagbar->output.wl_output,
185 &output_listener, state->nagbar);
186 wl_display_roundtrip(state->nagbar->display);
187 zxdg_output_v1_destroy(state->xdg_output);
188 } else {
189 zxdg_output_v1_destroy(state->xdg_output);
190 wl_output_destroy(state->wl_output);
191 }
192 state->nagbar->querying_outputs--;
193 free(state);
194}
195
196static struct zxdg_output_v1_listener xdg_output_listener = {
197 .logical_position = nop,
198 .logical_size = nop,
199 .done = nop,
200 .name = xdg_output_handle_name,
201 .description = nop,
202};
203
204static void handle_global(void *data, struct wl_registry *registry,
205 uint32_t name, const char *interface, uint32_t version) {
206 struct sway_nagbar *nagbar = data;
207 if (strcmp(interface, wl_compositor_interface.name) == 0) {
208 nagbar->compositor = wl_registry_bind(registry, name,
209 &wl_compositor_interface, 3);
210 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
211 nagbar->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
212 wl_seat_add_listener(nagbar->seat, &seat_listener, nagbar);
213 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
214 nagbar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
215 } else if (strcmp(interface, wl_output_interface.name) == 0) {
216 if (!nagbar->output.wl_output && nagbar->xdg_output_manager) {
217 nagbar->querying_outputs++;
218 struct output_state *state =
219 calloc(1, sizeof(struct output_state));
220 state->nagbar = nagbar;
221 state->wl_output = wl_registry_bind(registry, name,
222 &wl_output_interface, 3);
223 state->wl_name = name;
224 state->xdg_output = zxdg_output_manager_v1_get_xdg_output(
225 nagbar->xdg_output_manager, state->wl_output);
226 zxdg_output_v1_add_listener(state->xdg_output,
227 &xdg_output_listener, state);
228 } else if (!nagbar->output.wl_output && !nagbar->xdg_output_manager) {
229 wlr_log(WLR_ERROR, "Warning: zxdg_output_manager_v1 not supported."
230 " Falling back to first detected output");
231 nagbar->output.wl_output = wl_registry_bind(registry, name,
232 &wl_output_interface, 3);
233 nagbar->output.wl_name = name;
234 wl_output_add_listener(nagbar->output.wl_output,
235 &output_listener, nagbar);
236 }
237 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
238 nagbar->layer_shell = wl_registry_bind(
239 registry, name, &zwlr_layer_shell_v1_interface, 1);
240 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0
241 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
242 nagbar->xdg_output_manager = wl_registry_bind(registry, name,
243 &zxdg_output_manager_v1_interface,
244 ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
245 }
246}
247
248static void handle_global_remove(void *data, struct wl_registry *registry,
249 uint32_t name) {
250 struct sway_nagbar *nagbar = data;
251 if (nagbar->output.wl_name == name) {
252 nagbar->run_display = false;
253 }
254}
255
256static const struct wl_registry_listener registry_listener = {
257 .global = handle_global,
258 .global_remove = handle_global_remove,
259};
260
261void nagbar_setup(struct sway_nagbar *nagbar) {
262 nagbar->display = wl_display_connect(NULL);
263 assert(nagbar->display);
264
265 nagbar->scale = 1;
266
267 struct wl_registry *registry = wl_display_get_registry(nagbar->display);
268 wl_registry_add_listener(registry, &registry_listener, nagbar);
269 wl_display_roundtrip(nagbar->display);
270 assert(nagbar->compositor && nagbar->layer_shell && nagbar->shm);
271
272 while (nagbar->querying_outputs > 0) {
273 wl_display_roundtrip(nagbar->display);
274 }
275
276 if (!nagbar->output.wl_output) {
277 if (nagbar->output.name) {
278 wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name);
279 } else {
280 wlr_log(WLR_ERROR, "No outputs detected");
281 }
282 nagbar_destroy(nagbar);
283 exit(EXIT_FAILURE);
284 }
285
286 struct sway_nagbar_pointer *pointer = &nagbar->pointer;
287 int scale = nagbar->scale < 1 ? 1 : nagbar->scale;
288 pointer->cursor_theme = wl_cursor_theme_load(
289 NULL, 24 * scale, nagbar->shm);
290 assert(pointer->cursor_theme);
291 struct wl_cursor *cursor =
292 wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
293 assert(cursor);
294 pointer->cursor_image = cursor->images[0];
295 pointer->cursor_surface = wl_compositor_create_surface(nagbar->compositor);
296 assert(pointer->cursor_surface);
297
298 nagbar->surface = wl_compositor_create_surface(nagbar->compositor);
299 assert(nagbar->surface);
300 nagbar->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
301 nagbar->layer_shell, nagbar->surface, nagbar->output.wl_output,
302 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "nagbar");
303 assert(nagbar->layer_surface);
304 zwlr_layer_surface_v1_add_listener(nagbar->layer_surface,
305 &layer_surface_listener, nagbar);
306 zwlr_layer_surface_v1_set_anchor(nagbar->layer_surface, nagbar->anchors);
307
308 wl_registry_destroy(registry);
309}
310
311void nagbar_run(struct sway_nagbar *nagbar) {
312 nagbar->run_display = true;
313 render_frame(nagbar);
314 while (nagbar->run_display && wl_display_dispatch(nagbar->display) != -1) {
315 // This is intentionally left blank
316 }
317}
318
319void nagbar_destroy(struct sway_nagbar *nagbar) {
320 nagbar->run_display = false;
321
322 free(nagbar->message);
323 free(nagbar->font);
324 while (nagbar->buttons->length) {
325 struct sway_nagbar_button *button = nagbar->buttons->items[0];
326 list_del(nagbar->buttons, 0);
327 free(button->text);
328 free(button->action);
329 free(button);
330 }
331 list_free(nagbar->buttons);
332
333 if (nagbar->layer_surface) {
334 zwlr_layer_surface_v1_destroy(nagbar->layer_surface);
335 }
336
337 if (nagbar->surface) {
338 wl_surface_destroy(nagbar->surface);
339 }
340
341 if (nagbar->output.wl_output) {
342 wl_output_destroy(nagbar->output.wl_output);
343 }
344
345 if (&nagbar->buffers[0]) {
346 destroy_buffer(&nagbar->buffers[0]);
347 }
348
349 if (&nagbar->buffers[1]) {
350 destroy_buffer(&nagbar->buffers[1]);
351 }
352
353 if (nagbar->compositor) {
354 wl_compositor_destroy(nagbar->compositor);
355 }
356
357 if (nagbar->shm) {
358 wl_shm_destroy(nagbar->shm);
359 }
360
361 if (nagbar->display) {
362 wl_display_disconnect(nagbar->display);
363 }
364}