aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/bar.c
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-28 23:04:20 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-03-29 22:11:08 -0400
commitcab1352801b62d1b8a12ca1c995cb24445ce4bc9 (patch)
treebc67373916c06d48700c4f69b8c2470a2f86887f /swaybar/bar.c
parentAllow sway IPC clients to fall back to i3 socket (diff)
downloadsway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.gz
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.zst
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.zip
Start port of swaybar to layer shell
This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly.
Diffstat (limited to 'swaybar/bar.c')
-rw-r--r--swaybar/bar.c444
1 files changed, 100 insertions, 344 deletions
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f12923a8..e1d594b4 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,390 +1,146 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <assert.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <poll.h>
6#include <signal.h>
2#include <stdlib.h> 7#include <stdlib.h>
3#include <unistd.h>
4#include <string.h> 8#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <sys/wait.h> 9#include <sys/wait.h>
8#include <signal.h> 10#include <unistd.h>
9#include <poll.h> 11#include <wayland-client.h>
10#ifdef __FreeBSD__ 12#include <wlr/util/log.h>
11#include <dev/evdev/input-event-codes.h>
12#else
13#include <linux/input-event-codes.h>
14#endif
15#ifdef ENABLE_TRAY
16#include <dbus/dbus.h>
17#include "swaybar/tray/sni_watcher.h"
18#include "swaybar/tray/tray.h"
19#include "swaybar/tray/sni.h"
20#endif
21#include "swaybar/ipc.h"
22#include "swaybar/render.h" 13#include "swaybar/render.h"
23#include "swaybar/config.h" 14#include "swaybar/config.h"
24#include "swaybar/status_line.h"
25#include "swaybar/event_loop.h" 15#include "swaybar/event_loop.h"
26#include "swaybar/bar.h" 16#include "swaybar/bar.h"
27#include "ipc-client.h"
28#include "list.h" 17#include "list.h"
29#include "log.h" 18#include "pango.h"
19#include "pool-buffer.h"
20#include "wlr-layer-shell-unstable-v1-client-protocol.h"
30 21
31static void bar_init(struct bar *bar) { 22static void bar_init(struct swaybar *bar) {
32 bar->config = init_config(); 23 bar->config = init_config();
33 bar->status = init_status_line(); 24 wl_list_init(&bar->outputs);
34 bar->outputs = create_list();
35} 25}
36 26
37static void spawn_status_cmd_proc(struct bar *bar) { 27struct swaybar_output *new_output(const char *name) {
38 if (bar->config->status_command) { 28 struct swaybar_output *output = malloc(sizeof(struct swaybar_output));
39 int pipe_read_fd[2];
40 int pipe_write_fd[2];
41
42 if (pipe(pipe_read_fd) != 0) {
43 sway_log(L_ERROR, "Unable to create pipes for status_command fork");
44 return;
45 }
46 if (pipe(pipe_write_fd) != 0) {
47 sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)");
48 close(pipe_read_fd[0]);
49 close(pipe_read_fd[1]);
50 return;
51 }
52
53 bar->status_command_pid = fork();
54 if (bar->status_command_pid == 0) {
55 close(pipe_read_fd[0]);
56 dup2(pipe_read_fd[1], STDOUT_FILENO);
57 close(pipe_read_fd[1]);
58
59 dup2(pipe_write_fd[0], STDIN_FILENO);
60 close(pipe_write_fd[0]);
61 close(pipe_write_fd[1]);
62
63 char *const cmd[] = {
64 "sh",
65 "-c",
66 bar->config->status_command,
67 NULL,
68 };
69 execvp(cmd[0], cmd);
70 return;
71 }
72
73 close(pipe_read_fd[1]);
74 bar->status_read_fd = pipe_read_fd[0];
75 fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK);
76
77 close(pipe_write_fd[0]);
78 bar->status_write_fd = pipe_write_fd[1];
79 fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK);
80 }
81}
82
83struct output *new_output(const char *name) {
84 struct output *output = malloc(sizeof(struct output));
85 output->name = strdup(name); 29 output->name = strdup(name);
86 output->window = NULL;
87 output->registry = NULL;
88 output->workspaces = create_list();
89#ifdef ENABLE_TRAY
90 output->items = create_list();
91#endif
92 return output; 30 return output;
93} 31}
94 32
95static void mouse_button_notify(struct window *window, int x, int y, 33static void layer_surface_configure(void *data,
96 uint32_t button, uint32_t state_w) { 34 struct zwlr_layer_surface_v1 *surface,
97 sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w); 35 uint32_t serial, uint32_t width, uint32_t height) {
98 if (!state_w) { 36 struct swaybar_output *output = data;
99 return; 37 output->width = width;
100 } 38 output->height = height;
101 39 zwlr_layer_surface_v1_ack_configure(surface, serial);
102 struct output *clicked_output = NULL; 40 render_frame(output->bar, output);
103 for (int i = 0; i < swaybar.outputs->length; i++) {
104 struct output *output = swaybar.outputs->items[i];
105 if (window == output->window) {
106 clicked_output = output;
107 break;
108 }
109 }
110
111 if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) {
112 return;
113 }
114
115 double button_x = 0.5;
116 for (int i = 0; i < clicked_output->workspaces->length; i++) {
117 struct workspace *workspace = clicked_output->workspaces->items[i];
118 int button_width, button_height;
119
120 workspace_button_size(window, workspace->name, &button_width, &button_height);
121
122 button_x += button_width;
123 if (x <= button_x) {
124 ipc_send_workspace_command(workspace->name);
125 break;
126 }
127 }
128
129 switch (button) {
130 case BTN_LEFT:
131 status_line_mouse_event(&swaybar, x, y, 1);
132 break;
133 case BTN_MIDDLE:
134 status_line_mouse_event(&swaybar, x, y, 2);
135 break;
136 case BTN_RIGHT:
137 status_line_mouse_event(&swaybar, x, y, 3);
138 break;
139 }
140
141#ifdef ENABLE_TRAY
142 tray_mouse_event(clicked_output, x, y, button, state_w);
143#endif
144
145} 41}
146 42
147static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { 43static void layer_surface_closed(void *_output,
148 sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down"); 44 struct zwlr_layer_surface_v1 *surface) {
149 45 // TODO: Deal with hotplugging
150 // If there are status blocks and click_events are enabled 46 struct swaybar_output *output = output;
151 // check if the position is within the status area and if so 47 zwlr_layer_surface_v1_destroy(output->layer_surface);
152 // tell the status line to output the event and skip workspace 48 wl_surface_destroy(output->surface);
153 // switching below. 49}
154 int num_blocks = swaybar.status->block_line->length;
155 if (swaybar.status->click_events && num_blocks > 0) {
156 struct status_block *first_block = swaybar.status->block_line->items[0];
157 int x = window->pointer_input.last_x;
158 int y = window->pointer_input.last_y;
159 if (x > first_block->x) {
160 if (direction == SCROLL_UP) {
161 status_line_mouse_event(&swaybar, x, y, 4);
162 } else {
163 status_line_mouse_event(&swaybar, x, y, 5);
164 }
165 return;
166 }
167 }
168 50
169 if (!swaybar.config->wrap_scroll) { 51struct zwlr_layer_surface_v1_listener layer_surface_listener = {
170 // Find output this window lives on 52 .configure = layer_surface_configure,
171 int i; 53 .closed = layer_surface_closed,
172 struct output *output = NULL; 54};
173 for (i = 0; i < swaybar.outputs->length; ++i) { 55
174 output = swaybar.outputs->items[i]; 56static void handle_global(void *data, struct wl_registry *registry,
175 if (output->window == window) { 57 uint32_t name, const char *interface, uint32_t version) {
176 break; 58 struct swaybar *bar = data;
177 } 59 if (strcmp(interface, wl_compositor_interface.name) == 0) {
178 } 60 bar->compositor = wl_registry_bind(registry, name,
179 if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) { 61 &wl_compositor_interface, 1);
180 return; 62 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
181 } 63 bar->shm = wl_registry_bind(registry, name,
182 int focused = -1; 64 &wl_shm_interface, 1);
183 for (i = 0; i < output->workspaces->length; ++i) { 65 } else if (strcmp(interface, wl_output_interface.name) == 0) {
184 struct workspace *ws = output->workspaces->items[i]; 66 static int idx = 0;
185 if (ws->focused) { 67 struct swaybar_output *output =
186 focused = i; 68 calloc(1, sizeof(struct swaybar_output));
187 break; 69 output->bar = bar;
188 } 70 output->output = wl_registry_bind(registry, name,
189 } 71 &wl_output_interface, 1);
190 if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) { 72 output->idx = idx++;
191 return; 73 wl_list_insert(&bar->outputs, &output->link);
192 } 74 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
193 if ((focused == 0 && direction == SCROLL_UP) || 75 bar->layer_shell = wl_registry_bind(
194 (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) { 76 registry, name, &zwlr_layer_shell_v1_interface, 1);
195 // Do not wrap
196 return;
197 }
198 } 77 }
78}
199 79
200 const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output"; 80static void handle_global_remove(void *data, struct wl_registry *registry,
201 ipc_send_workspace_command(workspace_name); 81 uint32_t name) {
82 // who cares
202} 83}
203 84
204void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { 85static const struct wl_registry_listener registry_listener = {
205 /* initialize bar with default values */ 86 .global = handle_global,
206 bar_init(bar); 87 .global_remove = handle_global_remove,
88};
207 89
208 /* Initialize event loop lists */ 90void bar_setup(struct swaybar *bar,
91 const char *socket_path, const char *bar_id) {
92 bar_init(bar);
209 init_event_loop(); 93 init_event_loop();
210 94
211 /* connect to sway ipc */ 95 assert(bar->display = wl_display_connect(NULL));
212 bar->ipc_socketfd = ipc_open_socket(socket_path); 96
213 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 97 struct wl_registry *registry = wl_display_get_registry(bar->display);
214 98 wl_registry_add_listener(registry, &registry_listener, bar);
215 ipc_bar_init(bar, bar_id); 99 wl_display_roundtrip(bar->display);
216 100 assert(bar->compositor && bar->layer_shell && bar->shm);
217 int i; 101
218 for (i = 0; i < bar->outputs->length; ++i) { 102 // TODO: we might not necessarily be meant to do all of the outputs
219 struct output *bar_output = bar->outputs->items[i]; 103 struct swaybar_output *output;
220 104 wl_list_for_each(output, &bar->outputs, link) {
221 bar_output->registry = registry_poll(); 105 assert(output->surface = wl_compositor_create_surface(bar->compositor));
222 106 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
223 if (!bar_output->registry->desktop_shell) { 107 bar->layer_shell, output->surface, output->output,
224 sway_abort("swaybar requires the compositor to support the desktop-shell extension."); 108 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
225 } 109 assert(output->layer_surface);
226 110 zwlr_layer_surface_v1_add_listener(output->layer_surface,
227 struct output_state *output = bar_output->registry->outputs->items[bar_output->idx]; 111 &layer_surface_listener, output);
228 112 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
229 bar_output->window = window_setup(bar_output->registry,
230 output->width / output->scale, 30, output->scale, false);
231 if (!bar_output->window) {
232 sway_abort("Failed to create window.");
233 }
234 desktop_shell_set_panel(bar_output->registry->desktop_shell,
235 output->output, bar_output->window->surface);
236 desktop_shell_set_panel_position(bar_output->registry->desktop_shell,
237 bar->config->position); 113 bar->config->position);
238 114 render_frame(bar, output);
239 window_make_shell(bar_output->window);
240
241 /* set font */
242 bar_output->window->font = bar->config->font;
243
244 /* set mouse event callbacks */
245 bar_output->window->pointer_input.notify_button = mouse_button_notify;
246 bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify;
247
248 /* set window height */
249 set_window_height(bar_output->window, bar->config->height);
250 } 115 }
251 /* spawn status command */
252 spawn_status_cmd_proc(bar);
253
254#ifdef ENABLE_TRAY
255 init_tray(bar);
256#endif
257} 116}
258 117
259bool dirty = true; 118static void display_in(int fd, short mask, void *_bar) {
260 119 struct swaybar *bar = (struct swaybar *)_bar;
261static void respond_ipc(int fd, short mask, void *_bar) { 120 if (wl_display_dispatch(bar->display) == -1) {
262 struct bar *bar = (struct bar *)_bar; 121 wlr_log(L_ERROR, "failed to dispatch wl: %d", errno);
263 sway_log(L_DEBUG, "Got IPC event.");
264 dirty = handle_ipc_event(bar);
265}
266
267static void respond_command(int fd, short mask, void *_bar) {
268 struct bar *bar = (struct bar *)_bar;
269 dirty = handle_status_line(bar);
270}
271
272static void respond_output(int fd, short mask, void *_output) {
273 struct output *output = (struct output *)_output;
274 if (wl_display_dispatch(output->registry->display) == -1) {
275 sway_log(L_ERROR, "failed to dispatch wl: %d", errno);
276 } 122 }
277} 123}
278 124
279void bar_run(struct bar *bar) { 125void bar_run(struct swaybar *bar) {
280 add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar); 126 add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar);
281 add_event(bar->status_read_fd, POLLIN, respond_command, bar);
282
283 int i;
284 for (i = 0; i < bar->outputs->length; ++i) {
285 struct output *output = bar->outputs->items[i];
286 add_event(wl_display_get_fd(output->registry->display),
287 POLLIN, respond_output, output);
288 }
289
290 while (1) { 127 while (1) {
291 if (dirty) {
292 int i;
293 for (i = 0; i < bar->outputs->length; ++i) {
294 struct output *output = bar->outputs->items[i];
295 if (window_prerender(output->window) && output->window->cairo) {
296 render(output, bar->config, bar->status);
297 window_render(output->window);
298 wl_display_flush(output->registry->display);
299 }
300 }
301 }
302
303 dirty = false;
304
305 event_loop_poll(); 128 event_loop_poll();
306#ifdef ENABLE_TRAY
307 dispatch_dbus();
308#endif
309 } 129 }
310} 130}
311 131
312void free_workspaces(list_t *workspaces) { 132static void free_outputs(struct wl_list *list) {
313 int i; 133 struct swaybar_output *output, *tmp;
314 for (i = 0; i < workspaces->length; ++i) { 134 wl_list_for_each_safe(output, tmp, list, link) {
315 struct workspace *ws = workspaces->items[i]; 135 wl_list_remove(&output->link);
316 free(ws->name); 136 free(output->name);
317 free(ws); 137 free(output);
318 } 138 }
319 list_free(workspaces);
320} 139}
321 140
322static void free_output(struct output *output) { 141void bar_teardown(struct swaybar *bar) {
323 window_teardown(output->window); 142 free_outputs(&bar->outputs);
324 if (output->registry) {
325 registry_teardown(output->registry);
326 }
327
328 free(output->name);
329
330 if (output->workspaces) {
331 free_workspaces(output->workspaces);
332 }
333
334 free(output);
335}
336
337static void free_outputs(list_t *outputs) {
338 int i;
339 for (i = 0; i < outputs->length; ++i) {
340 free_output(outputs->items[i]);
341 }
342 list_free(outputs);
343}
344
345static void terminate_status_command(pid_t pid) {
346 if (pid) {
347 // terminate status_command process
348 int ret = kill(pid, SIGTERM);
349 if (ret != 0) {
350 sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid);
351 } else {
352 int status;
353 waitpid(pid, &status, 0);
354 }
355 }
356}
357
358void bar_teardown(struct bar *bar) {
359 if (bar->config) { 143 if (bar->config) {
360 free_config(bar->config); 144 free_config(bar->config);
361 } 145 }
362
363 if (bar->outputs) {
364 free_outputs(bar->outputs);
365 }
366
367 if (bar->status) {
368 free_status_line(bar->status);
369 }
370
371 /* close sockets/pipes */
372 if (bar->status_read_fd) {
373 close(bar->status_read_fd);
374 }
375
376 if (bar->status_write_fd) {
377 close(bar->status_write_fd);
378 }
379
380 if (bar->ipc_socketfd) {
381 close(bar->ipc_socketfd);
382 }
383
384 if (bar->ipc_event_socketfd) {
385 close(bar->ipc_event_socketfd);
386 }
387
388 /* terminate status command process */
389 terminate_status_command(bar->status_command_pid);
390} 146}