Mercurial > yawc
diff src/yawc.c @ 0:f0372d3356b4 default tip
initial import
| author | Yasuri <linuxoverwindows@charliesmp.net> |
|---|---|
| date | Sun, 30 Aug 2026 12:47:10 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/yawc.c Sun Aug 30 12:47:10 2026 -0500 @@ -0,0 +1,648 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "nein_cursor.h" +#include <swc.h> +#include <wayland-server.h> +#include <wayland-util.h> +#include <xkbcommon/xkbcommon-keysyms.h> + +#include "config.h" +#include "types.h" +#include "util.h" +#include "yawc.h" + +static void focus(struct client* c); +static void apply_decor(struct client* c, bool active); +static struct client* first_client(struct screen* s); +static bool is_ws_client(const struct client* c, const struct screen* s); +static void on_screen_destroy(void* data); +static void on_win_destroy(void* data); +static void on_win_entered(void* data); +static void on_win_metadata_changed(void* data); +static void run_autostart(void); +static void setup(void); +static void setup_binds(void); +static void setup_cursor(void); +static void sync_window_visibility(void); + +struct wm wm; +const struct swc_manager manager = { + .new_screen = new_screen, .new_window = new_window, .new_device = new_device, +}; +struct swc_window_handler window_handler = { + .destroy = on_win_destroy, + .title_changed = on_win_metadata_changed, + .app_id_changed = on_win_metadata_changed, + .entered = on_win_entered, +}; +struct swc_screen_handler screen_handler = { + .destroy = on_screen_destroy, +}; + +static void focus(struct client* c) +{ + if (wm.sel_client) { + swc_window_set_border( + wm.sel_client->win, + cfg.border_col_normal, cfg.border_width, + cfg.border_col_normal_outer, cfg.border_width_outer + ); + } + + if (c) { + swc_window_set_border( + c->win, + cfg.border_col_active, cfg.border_width, + cfg.border_col_active_outer, cfg.border_width_outer + ); + } + + swc_window_focus(c ? c->win : NULL); + wm.sel_client = c; +} + +static void apply_decor(struct client* c, bool active) +{ + (void)active; + + if (!c) + return; + swc_window_set_decor(c->win, NULL); +} + +static struct client* first_client(struct screen* s) +{ + struct client* c; + + wl_list_for_each(c, &wm.clients, link) { + if (is_ws_client(c, s)) + return c; + } + + return NULL; +} + +static bool is_ws_client(const struct client* c, const struct screen* s) +{ + return c && c->ws == wm.ws && (!s || c->scr == s); +} + +static void on_screen_destroy(void* data) +{ + struct screen* s = data; + + if (!s) + return; + + wl_list_remove(&s->link); + + if (wm.sel_screen == s) { + if (wl_list_empty(&wm.screens)) + wm.sel_screen = NULL; + else + wm.sel_screen = wl_container_of(wm.screens.next, wm.sel_screen, link); + } + + free(s); +} + +static void on_win_destroy(void* data) +{ + struct client* c = data; + struct client* next; + + if (!c) + return; + + if (wm.grab.active && wm.grab.c == c) { + wm.grab.active = false; + wm.grab.c = NULL; + } + + if (wm.sel_client == c) { + wm.sel_client = NULL; + } + + wl_list_remove(&c->link); + free(c); + + next = first_client(wm.sel_screen); + if (!next) + next = first_client(NULL); + focus(next); +} + +static void on_win_entered(void* data) +{ + if (wm.grab.active) + return; + + struct client* c = data; + if (!is_ws_client(c, NULL)) + return; + + focus(c); +} + +static void on_win_metadata_changed(void* data) +{ + struct client* c = data; + + if (!c) + return; + + apply_decor(c, c == wm.sel_client); +} +static void setup_cursor(void) +{ + const struct nein_cursor_meta *arrow = &nein_cursor_metadata[NEIN_CURSOR_WHITEARROW]; + + swc_set_cursor_mode(SWC_CURSOR_MODE_COMPOSITOR); + swc_set_cursor_image(SWC_CURSOR_DEFAULT, + &nein_cursor_data[arrow->offset], + arrow->width, arrow->height, + arrow->hotspot_x, arrow->hotspot_y); +} +static void setup(void) +{ + /* display */ + wm.dpy = wl_display_create(); + if (!wm.dpy) + die(EXIT_FAILURE, "wl_display_create failed"); + + /* variables */ + wl_list_init(&wm.screens); + wl_list_init(&wm.clients); + wm.sel_client = NULL; + wm.sel_screen = NULL; + wm.grab.active = false; + wm.grab.resize = false; + wm.grab.c = NULL; + wm.ws = 1; + + /* event loop */ + wm.ev_loop = wl_display_get_event_loop(wm.dpy); + if (!swc_initialize(wm.dpy, wm.ev_loop, &manager)) + die(EXIT_FAILURE, "swc_initialize failed\n"); + + swc_repeat_rate = cfg.repeat_rate; + swc_repeat_delay = cfg.repeat_delay; + setup_cursor(); + setup_binds(); + + /* display socket */ + const char* sock; + sock = wl_display_add_socket_auto(wm.dpy); + if (!sock) + die(EXIT_FAILURE, "wl_display_add_socket_auto failed\n"); + setenv("WAYLAND_DISPLAY", sock, 1); + _log(stderr, "WAYLAND_DISPLAY=%s\n", sock); + + run_autostart(); + + /* signals */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGQUIT, sig_handler); +} + +static void run_autostart(void) +{ + const char* shell = getenv("SHELL"); + + if (!shell || !shell[0]) + shell = "/bin/sh"; + + for (size_t i = 0; i < LENGTH(autostart); i++) { + const char* cmd = autostart[i]; + pid_t pid; + + if (!cmd || !cmd[0]) + continue; + + pid = fork(); + if (pid < 0) { + _log(stderr, "autostart fork failed for: %s\n", cmd); + continue; + } + + if (pid == 0) { + setsid(); + execl(shell, shell, "-c", cmd, (char*)NULL); + _exit(127); + } + } +} + +static void setup_binds(void) +{ + for (size_t i = 0; i < LENGTH(binds); i++) { + const struct bind* b = &binds[i]; + swc_add_binding(b->type, b->mods, b->ksym, b->fn, (void*)&b->arg); + } +} + +static void sync_window_visibility(void) +{ + struct client* c; + + wl_list_for_each(c, &wm.clients, link) { + if (c->ws == wm.ws) + swc_window_show(c->win); + else + swc_window_hide(c->win); + } +} + +void focus_next(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + (void)data; + (void)time; + (void)value; + + struct client* c = NULL; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (wl_list_empty(&wm.clients)) + return; + + if (!wm.sel_client || !is_ws_client(wm.sel_client, wm.sel_screen)) { + c = first_client(wm.sel_screen); + if (!c) + c = first_client(NULL); + focus(c); + return; + } + + struct wl_list* start = wm.sel_client->link.next; + struct wl_list* it = start; + + do { + if (it == &wm.clients) + it = wm.clients.next; + if (it == &wm.clients) + break; + + c = wl_container_of(it, c, link); + if (is_ws_client(c, wm.sel_screen)) { + focus(c); + return; + } + it = it->next; + } while (it != start); + + c = first_client(wm.sel_screen); + if (!c) + c = first_client(NULL); + focus(c); +} + +void focus_prev(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + struct client* c = NULL; + + (void)data; + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (wl_list_empty(&wm.clients)) + return; + + if (!wm.sel_client || !is_ws_client(wm.sel_client, wm.sel_screen)) { + c = first_client(wm.sel_screen); + if (!c) + c = first_client(NULL); + focus(c); + return; + } + + struct wl_list* start = wm.sel_client->link.prev; + struct wl_list* it = start; + + do { + if (it == &wm.clients) + it = wm.clients.prev; + if (it == &wm.clients) + break; + + c = wl_container_of(it, c, link); + if (is_ws_client(c, wm.sel_screen)) { + focus(c); + return; + } + it = it->prev; + } while (it != start); + + c = first_client(wm.sel_screen); + if (!c) + c = first_client(NULL); + focus(c); +} + +void kill_sel(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + (void)data; + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (!wm.sel_client) + return; + + swc_window_close(wm.sel_client->win); +} + +void fullscreen(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + struct swc_rectangle geom; + + (void)data; + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (!wm.sel_client) + return; + + if (wm.sel_client->fullscreen) { + wm.sel_client->fullscreen = false; + swc_window_set_stacked(wm.sel_client->win); + apply_decor(wm.sel_client, true); + + if (wm.sel_client->w > 0 && wm.sel_client->h > 0) { + geom.x = wm.sel_client->x; + geom.y = wm.sel_client->y; + geom.width = wm.sel_client->w; + geom.height = wm.sel_client->h; + swc_window_set_geometry(wm.sel_client->win, &geom); + } + return; + } + + if (!wm.sel_client->scr || !wm.sel_client->scr->scr) + return; + + if (swc_window_get_geometry(wm.sel_client->win, &geom)) { + wm.sel_client->x = geom.x; + wm.sel_client->y = geom.y; + wm.sel_client->w = geom.width; + wm.sel_client->h = geom.height; + } + + wm.sel_client->fullscreen = true; + apply_decor(wm.sel_client, true); + swc_window_set_fullscreen(wm.sel_client->win, wm.sel_client->scr->scr); +} + +void mouse_move(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + (void)data; + (void)time; + (void)value; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (!wm.sel_client) + return; + + if (!wm.sel_client->floating) { + wm.sel_client->floating = true; + swc_window_set_stacked(wm.sel_client->win); + } + + wm.grab.active = true; + wm.grab.resize = false; + wm.grab.c = wm.sel_client; + + swc_window_begin_move(wm.grab.c->win); + } + else { + if (!wm.grab.active || wm.grab.resize || !wm.grab.c) + return; + + swc_window_end_move(wm.grab.c->win); + + wm.grab.active = false; + wm.grab.c = NULL; + } +} + +void mouse_resize(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + (void)data; + (void)time; + (void)value; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (!wm.sel_client) + return; + + if (!wm.sel_client->floating) { + wm.sel_client->floating = true; + swc_window_set_stacked(wm.sel_client->win); + } + + wm.grab.active = true; + wm.grab.resize = true; + wm.grab.c = wm.sel_client; + + swc_window_begin_resize( + wm.grab.c->win, + SWC_WINDOW_EDGE_RIGHT | SWC_WINDOW_EDGE_BOTTOM + ); + } + else { + if (!wm.grab.active || !wm.grab.resize || !wm.grab.c) + return; + + swc_window_end_resize(wm.grab.c->win); + + wm.grab.active = false; + wm.grab.c = NULL; + } +} + +void new_screen(struct swc_screen* scr) +{ + struct screen* s; + + s = malloc(sizeof(*s)); + if (!s) + die(EXIT_FAILURE, "new screen calloc failed"); + + s->scr = scr; + + s->x = 0; + s->y = 0; + s->w = 0; + s->h = 0; + + wl_list_insert(&wm.screens, &s->link); + + if (!wm.sel_screen) + wm.sel_screen = s; + + swc_screen_set_handler(scr, &screen_handler, s); + + _log(stderr, "new_screen=%p\n", (void*)scr); +} + +void new_window(struct swc_window* win) +{ + struct client* c; + + c = malloc(sizeof(*c)); + if (!c) + die(EXIT_FAILURE, "malloc client failed"); + + win->motion_throttle_ms = 1000 / cfg.motion_throttle_hz; + win->min_width = 1; + win->min_height = 1; + win->max_width = 0; + win->max_height = 0; + + c->win = win; + c->scr = wm.sel_screen; + c->mapped = 0; + c->floating = true; + c->fullscreen = 0; + c->ws = wm.ws; + c->x = 0; + c->y = 0; + c->w = 0; + c->h = 0; + + wl_list_insert(&wm.clients, &c->link); + swc_window_set_handler(win, &window_handler, c); + swc_window_set_stacked(win); + apply_decor(c, false); + { + /* swc reports cursor coordinates in wl_fixed_t (24.8) units */ + int32_t cx_fixed = 0; + int32_t cy_fixed = 0; + + if (swc_cursor_position(&cx_fixed, &cy_fixed)) { + int32_t cx = cx_fixed / 256; + int32_t cy = cy_fixed / 256; + + swc_window_set_position( + win, + cx - (int32_t)(cfg.spawn_w / 2), + cy - (int32_t)(cfg.spawn_h / 2) + ); + } + } + swc_window_show(win); + focus(c); + + _log(stderr, "new_window=%p\n", (void*)win); +} + +void new_device(struct libinput_device* dev) +{ + (void)dev; +} + +void quit(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + (void)data; + (void)time; + (void)value; + (void)state; + + wl_display_terminate(wm.dpy); +} + +void spawn(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + union arg* a = data; + char* const* cmd = (char* const*)a->v; + + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (fork() == 0) { + execvp(cmd[0], cmd); + _exit(127); + } +} + +void workspace_goto(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + union arg* a = data; + struct client* c; + + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (a->ui < 1 || a->ui > 9 || a->ui == wm.ws) + return; + + wm.ws = a->ui; + sync_window_visibility(); + + c = first_client(wm.sel_screen); + if (!c) + c = first_client(NULL); + focus(c); +} + +void workspace_moveto(void* data, uint32_t time, uint32_t value, uint32_t state) +{ + union arg* a = data; + struct client* c; + struct client* next; + + (void)time; + (void)value; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + if (!wm.sel_client) + return; + + if (a->ui < 1 || a->ui > 9) + return; + + c = wm.sel_client; + if (c->ws == a->ui) + return; + + c->ws = a->ui; + if (c->ws == wm.ws) + swc_window_show(c->win); + else + swc_window_hide(c->win); + + next = first_client(wm.sel_screen); + if (!next) + next = first_client(NULL); + focus(next); +} + +int main(void) +{ + setup(); + wl_display_run(wm.dpy); + swc_finalize(); + wl_display_destroy(wm.dpy); + return EXIT_SUCCESS; +}
