533 行
17 KiB
C
533 行
17 KiB
C
#include "bar.h"
|
|
#include "config.h"
|
|
#include "dwl-ipc-unstable-v2-protocol.h"
|
|
#include "event.h"
|
|
#include "log.h"
|
|
#include "render.h"
|
|
#include "util.h"
|
|
#include "main.h"
|
|
#include "input.h"
|
|
#include "xdg-shell-protocol.h"
|
|
#include "wlr-layer-shell-unstable-v1-protocol.h"
|
|
#include <math.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <sys/stat.h>
|
|
#include <wayland-client-core.h>
|
|
#include <wayland-client-protocol.h>
|
|
#include <wayland-client.h>
|
|
#include <wayland-util.h>
|
|
|
|
static void check_global(void *global, const char *name);
|
|
static void check_globals(void);
|
|
static void cleanup(void);
|
|
static void display_in(int fd, short mask, void *data);
|
|
static void fifo_handle(const char *line);
|
|
static void fifo_in(int fd, short mask, void *data);
|
|
static void fifo_setup(void);
|
|
static void monitor_destroy(struct Monitor *monitor);
|
|
struct Monitor *monitor_from_surface(const struct wl_surface *surface);
|
|
static void monitor_initialize(struct Monitor *monitor);
|
|
static void monitor_update(struct Monitor *monitor);
|
|
static void pipe_in(int fd, short mask, void *data);
|
|
static void registry_global_add(void *data, struct wl_registry *registry, uint32_t name,
|
|
const char *interface, uint32_t version);
|
|
static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name);
|
|
static void run(void);
|
|
static void set_cloexec(int fd);
|
|
static void setup(void);
|
|
static void sigaction_handler(int _);
|
|
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial);
|
|
static void zdwl_ipc_manager_layout(void *data, struct zdwl_ipc_manager_v2 *zdwl_ipc_manager_v2, const char *name);
|
|
static void zdwl_ipc_manager_tags(void *data, struct zdwl_ipc_manager_v2 *zdwl_ipc_manager_v2, uint32_t amount);
|
|
static void zdwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t active);
|
|
static void zdwl_ipc_output_appid(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *appid);
|
|
static void zdwl_ipc_output_floating(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t is_floating);
|
|
static void zdwl_ipc_output_frame(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2);
|
|
static void zdwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t is_fullscreen);
|
|
static void zdwl_ipc_output_layout(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t layout);
|
|
static void zdwl_ipc_output_layout_symbol(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *layout);
|
|
static void zdwl_ipc_output_tag(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2,
|
|
uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused);
|
|
static void zdwl_ipc_output_title(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *title);
|
|
static void zdwl_ipc_output_toggle_visibility(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2);
|
|
|
|
static struct xdg_wm_base *base;
|
|
struct wl_compositor *compositor;
|
|
static struct wl_display *display;
|
|
static int display_fd;
|
|
static struct zdwl_ipc_manager_v2 *dwl_manager = NULL;
|
|
static struct Events *events;
|
|
static int fifo_fd;
|
|
static char *fifo_path;
|
|
int layoutcount;
|
|
static struct wl_list monitors; // struct Monitor*
|
|
static struct zxdg_output_manager_v1 *output_manager;
|
|
static const struct wl_registry_listener registry_listener = {
|
|
.global = registry_global_add,
|
|
.global_remove = registry_global_remove,
|
|
};
|
|
static int running = 0;
|
|
static struct wl_list seats; // struct Seat*
|
|
static int self_pipe[2];
|
|
struct zwlr_layer_shell_v1 *shell;
|
|
struct wl_shm *shm;
|
|
static int tagcount;
|
|
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|
.ping = xdg_wm_base_ping,
|
|
};
|
|
static const struct zdwl_ipc_manager_v2_listener zdwl_manager_listener = {
|
|
.layout = zdwl_ipc_manager_layout,
|
|
.tags = zdwl_ipc_manager_tags,
|
|
};
|
|
static const struct zdwl_ipc_output_v2_listener zdwl_output_listener = {
|
|
.active = zdwl_ipc_output_active,
|
|
.appid = zdwl_ipc_output_appid,
|
|
.floating = zdwl_ipc_output_floating,
|
|
.frame = zdwl_ipc_output_frame,
|
|
.fullscreen = zdwl_ipc_output_fullscreen,
|
|
.layout = zdwl_ipc_output_layout,
|
|
.layout_symbol = zdwl_ipc_output_layout_symbol,
|
|
.tag = zdwl_ipc_output_tag,
|
|
.title = zdwl_ipc_output_title,
|
|
.toggle_visibility = zdwl_ipc_output_toggle_visibility,
|
|
};
|
|
|
|
void check_global(void *global, const char *name) {
|
|
if (global)
|
|
return;
|
|
panic("Wayland compositor did not export: %s", name);
|
|
}
|
|
|
|
void check_globals(void) {
|
|
check_global(base, "xdg_wm_base");
|
|
check_global(compositor, "wl_compositor");
|
|
check_global(dwl_manager, "zdwl_ipc_manager_v2");
|
|
check_global(shell, "zwlr_layer_shell_v1");
|
|
check_global(shm, "wl_shm");
|
|
}
|
|
|
|
void cleanup(void) {
|
|
xdg_wm_base_destroy(base);
|
|
wl_compositor_destroy(compositor);
|
|
close(fifo_fd);
|
|
unlink(fifo_path);
|
|
free(fifo_path);
|
|
zwlr_layer_shell_v1_destroy(shell);
|
|
zdwl_ipc_manager_v2_destroy(dwl_manager);
|
|
wl_shm_destroy(shm);
|
|
events_destroy(events);
|
|
log_destroy();
|
|
|
|
struct Monitor *monitor, *tmp_monitor;
|
|
wl_list_for_each_safe(monitor, tmp_monitor, &monitors, link)
|
|
monitor_destroy(monitor);
|
|
|
|
struct Seat *seat, *tmp_seat;
|
|
wl_list_for_each_safe(seat, tmp_seat, &seats, link)
|
|
seat_destroy(seat);
|
|
|
|
wl_display_disconnect(display);
|
|
}
|
|
|
|
void display_in(int fd, short mask, void *data) {
|
|
if (mask & (POLLHUP | POLLERR) ||
|
|
wl_display_dispatch(display) == -1) {
|
|
running = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void fifo_handle(const char *line) {
|
|
char *command;
|
|
unsigned long loc = 0;
|
|
|
|
command = to_delimiter(line, &loc, ' ');
|
|
|
|
if (STRING_EQUAL(command, "status")) {
|
|
char *status = to_delimiter(line, &loc, '\n');
|
|
struct Monitor *pos;
|
|
wl_list_for_each(pos, &monitors, link) {
|
|
bar_set_status(pos->bar, status);
|
|
pipeline_invalidate(pos->pipeline);
|
|
}
|
|
free(status);
|
|
}
|
|
|
|
free(command);
|
|
}
|
|
|
|
void fifo_in(int fd, short mask, void *data) {
|
|
if (mask & POLLERR) {
|
|
events_remove(events, fd);
|
|
char *default_status = string_create("dwl %.1f", VERSION);
|
|
struct Monitor *pos;
|
|
wl_list_for_each(pos, &monitors, link) {
|
|
bar_set_status(pos->bar, default_status);
|
|
pipeline_invalidate(pos->pipeline);
|
|
}
|
|
free(default_status);
|
|
return;
|
|
}
|
|
|
|
int new_fd = dup(fd);
|
|
FILE *fifo_file = fdopen(new_fd, "r");
|
|
char *buffer = NULL;
|
|
size_t size = 0;
|
|
while (1) {
|
|
if (getline(&buffer, &size, fifo_file) == -1)
|
|
break;
|
|
|
|
fifo_handle(buffer);
|
|
}
|
|
free(buffer);
|
|
fclose(fifo_file);
|
|
close(new_fd);
|
|
|
|
}
|
|
|
|
void fifo_setup(void) {
|
|
int result, i;
|
|
char *runtime_path = getenv("XDG_RUNTIME_DIR");
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
fifo_path = string_create("%s/dwl-bar-%d", runtime_path, i);
|
|
|
|
result = mkfifo(fifo_path, 0666);
|
|
if (result < 0) {
|
|
if (errno != EEXIST)
|
|
panic("mkfifo");
|
|
|
|
continue;
|
|
}
|
|
|
|
if ((fifo_fd = open(fifo_path, O_CLOEXEC | O_RDONLY | O_NONBLOCK)) < 0)
|
|
panic("open fifo");
|
|
|
|
return;
|
|
}
|
|
|
|
panic("setup fifo"); /* If we get here then we couldn't setup the fifo */
|
|
}
|
|
|
|
void monitor_destroy(struct Monitor *monitor) {
|
|
if (!monitor)
|
|
return;
|
|
|
|
if (wl_output_get_version(monitor->wl_output) >= WL_OUTPUT_RELEASE_SINCE_VERSION)
|
|
wl_output_release(monitor->wl_output);
|
|
wl_output_release(monitor->wl_output);
|
|
zdwl_ipc_output_v2_destroy(monitor->dwl_output);
|
|
list_elements_destroy(monitor->hotspots, free);
|
|
pipeline_destroy(monitor->pipeline);
|
|
bar_destroy(monitor->bar);
|
|
free(monitor);
|
|
}
|
|
|
|
struct Monitor *monitor_from_surface(const struct wl_surface *surface) {
|
|
struct Monitor *pos;
|
|
wl_list_for_each(pos, &monitors, link) {
|
|
if (pos->pipeline->surface == surface)
|
|
return pos;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void monitor_initialize(struct Monitor *monitor) {
|
|
if (!monitor) return;
|
|
|
|
monitor->desired_visibility = show_bar;
|
|
monitor->hotspots = list_create(1);
|
|
monitor->pipeline = pipeline_create();
|
|
monitor->bar = bar_create(monitor->hotspots, monitor->pipeline);
|
|
if (!monitor->pipeline || !monitor->bar)
|
|
panic("Failed to create a pipline or bar for a monitor");
|
|
monitor_update(monitor);
|
|
}
|
|
|
|
void monitor_update(struct Monitor *monitor) {
|
|
if (!monitor)
|
|
return;
|
|
|
|
if (!pipeline_is_visible(monitor->pipeline) && monitor->desired_visibility) {
|
|
pipeline_show(monitor->pipeline, monitor->wl_output);
|
|
return;
|
|
}
|
|
|
|
pipeline_invalidate(monitor->pipeline);
|
|
}
|
|
|
|
void monitors_update(void) {
|
|
struct Monitor *monitor;
|
|
wl_list_for_each(monitor, &monitors, link) {
|
|
monitor_update(monitor);
|
|
}
|
|
}
|
|
|
|
void pipe_in(int fd, short mask, void *data) {
|
|
running = 0;
|
|
}
|
|
|
|
void registry_global_add(void *data, struct wl_registry *registry, uint32_t name,
|
|
const char *interface, uint32_t version) {
|
|
if (STRING_EQUAL(interface, wl_compositor_interface.name))
|
|
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
|
|
else if (STRING_EQUAL(interface, wl_output_interface.name)) {
|
|
struct Monitor *monitor = ecalloc(1, sizeof(*monitor));
|
|
monitor->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1);
|
|
monitor->wl_name = name;
|
|
monitor->dwl_output = NULL;
|
|
|
|
wl_list_insert(&monitors, &monitor->link);
|
|
|
|
if (!dwl_manager) return;
|
|
|
|
monitor->dwl_output = zdwl_ipc_manager_v2_get_output(dwl_manager, monitor->wl_output);
|
|
zdwl_ipc_output_v2_add_listener(monitor->dwl_output, &zdwl_output_listener, monitor);
|
|
|
|
if (!running) return;
|
|
monitor_initialize(monitor);
|
|
}
|
|
else if (STRING_EQUAL(interface, wl_seat_interface.name)) {
|
|
struct Seat *seat = ecalloc(1, sizeof(*seat));
|
|
seat->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
|
|
seat->wl_name = name;
|
|
seat->pointer = NULL;
|
|
seat->touch = NULL;
|
|
wl_list_insert(&seats, &seat->link);
|
|
wl_seat_add_listener(seat->seat, &seat_listener, seat);
|
|
}
|
|
else if (STRING_EQUAL(interface, wl_shm_interface.name))
|
|
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
else if (STRING_EQUAL(interface, xdg_wm_base_interface.name)) {
|
|
base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 2);
|
|
xdg_wm_base_add_listener(base, &xdg_wm_base_listener, NULL);
|
|
}
|
|
else if (STRING_EQUAL(interface, zdwl_ipc_manager_v2_interface.name)) {
|
|
dwl_manager = wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 2);
|
|
zdwl_ipc_manager_v2_add_listener(dwl_manager, &zdwl_manager_listener, NULL);
|
|
|
|
struct Monitor *monitor;
|
|
wl_list_for_each(monitor, &monitors, link) {
|
|
if (monitor->dwl_output) continue;
|
|
|
|
monitor->dwl_output = zdwl_ipc_manager_v2_get_output(dwl_manager, monitor->wl_output);
|
|
zdwl_ipc_output_v2_add_listener(monitor->dwl_output, &zdwl_output_listener, monitor);
|
|
}
|
|
}
|
|
else if (STRING_EQUAL(interface, zwlr_layer_shell_v1_interface.name))
|
|
shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 4);
|
|
}
|
|
|
|
void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
|
|
struct Monitor *monitor, *tmp_monitor;
|
|
wl_list_for_each_safe(monitor, tmp_monitor, &monitors, link) {
|
|
if (monitor->wl_name != name) continue;
|
|
wl_list_remove(&monitor->link);
|
|
monitor_destroy(monitor);
|
|
}
|
|
|
|
struct Seat *seat, *tmp_seat;
|
|
wl_list_for_each_safe(seat, tmp_seat, &seats, link) {
|
|
if (seat->wl_name != name) continue;
|
|
wl_list_remove(&seat->link);
|
|
seat_destroy(seat);
|
|
}
|
|
}
|
|
|
|
void run(void) {
|
|
running = 1;
|
|
|
|
while (running) {
|
|
wl_display_dispatch_pending(display);
|
|
if (wl_display_flush(display) == -1 && errno != EAGAIN)
|
|
break;
|
|
|
|
events_poll(events);
|
|
}
|
|
}
|
|
|
|
void set_cloexec(int fd) {
|
|
int flags = fcntl(fd, F_GETFD);
|
|
if (flags == -1)
|
|
panic("F_GETFD");
|
|
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
|
|
panic("FD_SETFD");
|
|
}
|
|
|
|
void setup(void) {
|
|
if (pipe(self_pipe) == -1)
|
|
panic("pipe");
|
|
|
|
set_cloexec(self_pipe[0]);
|
|
set_cloexec(self_pipe[1]);
|
|
|
|
static struct sigaction sighandle;
|
|
static struct sigaction child_sigaction;
|
|
|
|
sighandle.sa_handler = &sigaction_handler;
|
|
child_sigaction.sa_handler = SIG_IGN;
|
|
|
|
if (sigaction(SIGTERM, &sighandle, NULL) < 0)
|
|
panic("sigaction SIGTERM");
|
|
if (sigaction(SIGINT, &sighandle, NULL) < 0)
|
|
panic("sigaction SIGINT");
|
|
if (sigaction(SIGCHLD, &child_sigaction, NULL) < 0)
|
|
panic("sigaction SIGCHLD");
|
|
|
|
display = wl_display_connect(NULL);
|
|
if (!display)
|
|
panic("Failed to connect to Wayland compositor.");
|
|
display_fd = wl_display_get_fd(display);
|
|
|
|
wl_list_init(&seats);
|
|
wl_list_init(&monitors);
|
|
|
|
struct wl_registry *registry = wl_display_get_registry(display);
|
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
wl_display_roundtrip(display);
|
|
|
|
fifo_setup();
|
|
|
|
check_globals();
|
|
|
|
wl_display_roundtrip(display);
|
|
|
|
if (tagcount != LENGTH(tags))
|
|
panic("We do not have the same amount of tags as dwl! Please check config.def.h!");
|
|
|
|
struct Monitor *monitor;
|
|
wl_list_for_each(monitor, &monitors, link) {
|
|
monitor_initialize(monitor);
|
|
}
|
|
|
|
if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0)
|
|
panic("STDIN_FILENO O_NONBLOCK");
|
|
|
|
events = events_create();
|
|
events_add(events, display_fd, POLLIN, NULL, display_in);
|
|
events_add(events, self_pipe[0], POLLIN, NULL, pipe_in);
|
|
events_add(events, fifo_fd, POLLIN, NULL, fifo_in);
|
|
}
|
|
|
|
void sigaction_handler(int _) {
|
|
if (write(self_pipe[1], "0", 1) < 0)
|
|
panic("sigaction_handler");
|
|
}
|
|
|
|
void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) {
|
|
xdg_wm_base_pong(xdg_wm_base, serial);
|
|
}
|
|
|
|
void zdwl_ipc_manager_layout(void *data, struct zdwl_ipc_manager_v2 *zdwl_ipc_manager_v2, const char *name) {
|
|
layoutcount++;
|
|
}
|
|
|
|
void zdwl_ipc_manager_tags(void *data, struct zdwl_ipc_manager_v2 *zdwl_ipc_manager_v2, uint32_t amount) {
|
|
tagcount = amount;
|
|
}
|
|
|
|
void zdwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t active) {
|
|
struct Monitor *monitor = data;
|
|
bar_set_active(monitor->bar, active);
|
|
}
|
|
|
|
void zdwl_ipc_output_appid(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *appid) {
|
|
/* Nop */
|
|
}
|
|
|
|
void zdwl_ipc_output_floating(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t is_floating) {
|
|
struct Monitor *monitor = data;
|
|
bar_set_floating(monitor->bar, is_floating);
|
|
}
|
|
|
|
void zdwl_ipc_output_frame(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2) {
|
|
struct Monitor *monitor = data;
|
|
monitor_update(monitor);
|
|
}
|
|
|
|
void zdwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t is_fullscreen) {
|
|
/* Nop */
|
|
}
|
|
|
|
void zdwl_ipc_output_layout(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t layout) {
|
|
struct Monitor *monitor = data;
|
|
monitor->layout = layout;
|
|
}
|
|
|
|
void zdwl_ipc_output_layout_symbol(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *layout) {
|
|
struct Monitor *monitor = data;
|
|
bar_set_layout(monitor->bar, layout);
|
|
}
|
|
|
|
void zdwl_ipc_output_tag(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) {
|
|
struct Monitor *monitor = data;
|
|
bar_set_tag(monitor->bar, tag, state, clients ? 1 : 0, focused);
|
|
monitor->tags = (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) ? monitor->tags | (1 << tag) : monitor->tags & ~(1 << tag);
|
|
}
|
|
|
|
void zdwl_ipc_output_title(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2, const char *title) {
|
|
struct Monitor *monitor = data;
|
|
bar_set_title(monitor->bar, title);
|
|
}
|
|
|
|
void zdwl_ipc_output_toggle_visibility(void *data, struct zdwl_ipc_output_v2 *zdwl_ipc_output_v2) {
|
|
struct Monitor *monitor = data;
|
|
monitor->desired_visibility ^= 1;
|
|
pipeline_hide(monitor->pipeline);
|
|
monitor_update(monitor);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int opt;
|
|
while((opt = getopt(argc, argv, "l")) != -1) {
|
|
switch (opt) {
|
|
case 'l':
|
|
if (!setup_log())
|
|
panic("Failed to setup logging");
|
|
break;
|
|
case 'h':
|
|
printf("Usage: %s [-h] [-v]\n", argv[0]);
|
|
exit(EXIT_SUCCESS);
|
|
case 'v':
|
|
printf("%s %.1f\n", argv[0], VERSION);
|
|
exit(EXIT_SUCCESS);
|
|
case '?':
|
|
printf("Invalid Argument\n");
|
|
printf("Usage: %s [-h] [-v] [-l]\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
setup();
|
|
run();
|
|
cleanup();
|
|
}
|
|
|
|
void panic(const char *fmt, ...) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
fprintf(stderr, "[dwl-bar] panic: ");
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
|
|
fputc(' ', stderr);
|
|
perror(NULL);
|
|
|
|
} else {
|
|
fputc('\n', stderr);
|
|
}
|
|
|
|
cleanup();
|
|
exit(EXIT_FAILURE);
|
|
}
|