Mercurial > yawc
view 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 source
#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; }
