main 0uppy/slgro / source / slgro.c
  1#include <signal.h>
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <string.h>
  5#include <unistd.h>
  6#include <sys/wait.h>
  7
  8#include <swc.h>
  9#include <wayland-server.h>
 10#include <wayland-util.h>
 11#include <xkbcommon/xkbcommon-keysyms.h>
 12
 13/* this is for lua-config.c !!! awwa!!awaw!!waw!! */
 14extern struct config cfg;
 15extern struct bind *binds;
 16extern size_t nbinds;
 17extern void load_config(void);
 18
 19#include "include/types.h"
 20#include "include/util.h"
 21#include "include/slgro.h"
 22
 23static void focus(struct client* c);
 24static struct client* first_client(struct screen* s);
 25static bool is_ws_client(const struct client* c, const struct screen* s);
 26static void on_screen_destroy(void* data);
 27static void on_win_destroy(void* data);
 28static void on_win_entered(void* data);
 29static void setup(void);
 30static void setup_binds(void);
 31static void sync_window_visibility(void);
 32static void apply_decor(struct client* c, bool active);
 33static const char* decorstring(struct client* c, char* buf, size_t size);
 34
 35#define WHEN_PRESSED(state) \
 36    do { if ((state) != WL_KEYBOARD_KEY_STATE_PRESSED) return; } while (0)
 37
 38#define BIND_EVENT(time, value, state) \
 39    (void)(time); (void)(value); \
 40    WHEN_PRESSED(state);
 41
 42#define BIND_ACTION(data, time, value, state) \
 43    BIND_EVENT(time, value, state); \
 44    if (!wm.sel_client) return; \
 45    union arg* a = (data)
 46
 47/* read geometry from client window; returns true if successful */
 48static bool get_geometry(struct client *c, struct swc_rectangle *geom)
 49{
 50    if (!c || !c->win)
 51        return false;
 52
 53    return swc_window_get_geometry(c->win, geom);
 54}
 55
 56/* adjust geometry fields and write back */
 57static void adjust_geom(struct client *c, int dx, int dy, int dw, int dh)
 58{
 59    struct swc_rectangle geom;
 60    if (!get_geometry(c, &geom)) return;
 61
 62    geom.x += dx;
 63    geom.y += dy;
 64
 65    int new_w = (int)geom.width + dw;
 66    int new_h = (int)geom.height + dh;
 67    geom.width = (new_w < 1) ? 1 : (uint32_t)new_w;
 68    geom.height = (new_h < 1) ? 1 : (uint32_t)new_h;
 69
 70    swc_window_set_geometry(c->win, &geom);
 71}
 72
 73static const char* decorstring(struct client* c, char* buf, size_t size)
 74{
 75    char path[64];
 76    FILE* fp;
 77    pid_t pid;
 78    size_t len;
 79
 80    if (!c || !buf || size == 0)
 81        return NULL;
 82
 83    pid = swc_window_get_pid(c->win);
 84    if (pid <= 0)
 85        return NULL;
 86
 87    snprintf(path, sizeof(path), "/proc/%lld/comm", (long long)pid);
 88    fp = fopen(path, "r");
 89    if (!fp)
 90        return NULL;
 91
 92    if (!fgets(buf, (int)size, fp)) {
 93        fclose(fp);
 94        return NULL;
 95    }
 96
 97    fclose(fp);
 98    len = strcspn(buf, "\n");
 99    buf[len] = '\0';
100    return buf[0] ? buf : NULL;
101}
102
103static void apply_decor(struct client* c, bool active)
104{
105    char process_name[256];
106    const char* title;
107    struct swc_decor decor;
108
109    if (!c)
110        return;
111
112    if (c->fullscreen) {
113        swc_window_set_decor(c->win, NULL);
114        return;
115    }
116
117    decor = cfg.decor;
118    title = decorstring(c, process_name, sizeof(process_name));
119    if (title)
120        decor.title.string = title;
121    swc_window_set_decor(c->win, &decor);
122}
123
124struct wm wm;
125const struct swc_manager manager = {
126    .new_screen = new_screen, .new_window = new_window, .new_device = new_device,
127};
128struct swc_window_handler window_handler = {
129    .destroy = on_win_destroy, .entered = on_win_entered,
130};
131struct swc_screen_handler screen_handler = {
132    .destroy = on_screen_destroy,
133};
134
135static struct client* get_focus_candidate(struct screen* s)
136{
137    struct client* c = first_client(s);
138    if (!c && s) c = first_client(NULL);
139    return c;
140}
141
142static void focus(struct client* c)
143{
144    if (wm.sel_client && wm.sel_client != c) {
145        swc_window_set_border(wm.sel_client->win, cfg.border_col_normal, cfg.border_width, 0, 0);
146        apply_decor(wm.sel_client, false);
147    }
148    if (c) {
149        swc_window_set_border(c->win, cfg.border_col_active, cfg.border_width, 0, 0);
150        apply_decor(c, true);
151    }
152
153    swc_window_focus(c ? c->win : NULL);
154    wm.sel_client = c;
155}
156
157static struct client* first_client(struct screen* s)
158{
159    struct client* c;
160
161    wl_list_for_each(c, &wm.clients, link) {
162        if (is_ws_client(c, s))
163            return c;
164    }
165
166    return NULL;
167}
168
169static bool is_ws_client(const struct client* c, const struct screen* s)
170{
171    return c && c->ws == wm.ws && (!s || c->scr == s);
172}
173
174static void on_screen_destroy(void* data)
175{
176    struct screen* s = data;
177
178    if (!s)
179        return;
180
181    wl_list_remove(&s->link);
182
183    if (wm.sel_screen == s) {
184        wm.sel_screen = wl_list_empty(&wm.screens)
185            ? NULL
186            : wl_container_of(wm.screens.next, wm.sel_screen, link);
187    }
188
189    free(s);
190}
191
192static void on_win_destroy(void* data)
193{
194    struct client* c = data;
195    if (!c) return;
196
197    if (wm.sel_client == c)
198        wm.sel_client = NULL;
199
200    wl_list_remove(&c->link);
201    free(c);
202
203    focus(get_focus_candidate(wm.sel_screen));
204}
205
206static void on_win_entered(void* data)
207{
208    struct client* c = data;
209    if (!is_ws_client(c, NULL))
210        return;
211
212    focus(c);
213}
214
215static void setup(void)
216{
217    /* display */
218    load_config();
219    wm.dpy = wl_display_create();
220    if (!wm.dpy)
221        die(EXIT_FAILURE, "wl_display_create failed");
222
223    /* variables */
224    wl_list_init(&wm.screens);
225    wl_list_init(&wm.clients);
226
227    wm.sel_client = NULL;
228    wm.sel_screen = NULL;
229    wm.ws = 1;
230
231    /* event loop */
232    wm.ev_loop = wl_display_get_event_loop(wm.dpy);
233    if (!swc_initialize(wm.dpy, wm.ev_loop, &manager))
234        die(EXIT_FAILURE, "swc_initialize failed\n");
235
236    setup_binds();
237
238    /* display socket */
239    const char* sock;
240    sock = wl_display_add_socket_auto(wm.dpy);
241    if (!sock)
242        die(EXIT_FAILURE, "wl_display_add_socket_auto failed\n");
243
244    setenv("WAYLAND_DISPLAY", sock, 1);
245    _log(stderr, "WAYLAND_DISPLAY=%s\n", sock);
246
247    /* signals */
248    signal(SIGINT,  sig_handler);
249    signal(SIGTERM, sig_handler);
250    signal(SIGQUIT, sig_handler);
251}
252
253static void setup_binds(void)
254{
255    for (size_t i = 0; i < nbinds; i++) {
256        const struct bind* b = &binds[i];
257        swc_add_binding(b->type, b->mods, b->ksym, b->fn, (void*)&b->arg);
258    }
259}
260
261static void sync_window_visibility(void)
262{
263    struct client* c;
264
265    wl_list_for_each(c, &wm.clients, link) {
266        if (c->ws == wm.ws)
267            swc_window_show(c->win);
268        else
269            swc_window_hide(c->win);
270    }
271}
272
273/* focus next/prev helpers */
274static void focus_walk_direction(bool forward)
275{
276    if (wl_list_empty(&wm.clients)) return;
277
278    if (!is_ws_client(wm.sel_client, wm.sel_screen)) {
279        focus(get_focus_candidate(wm.sel_screen));
280        return;
281    }
282
283    struct wl_list *start =
284        forward ? wm.sel_client->link.next
285                : wm.sel_client->link.prev;
286
287    struct wl_list *it = start;
288    struct client *c = NULL;
289
290    do {
291        if (it == &wm.clients)
292            it = forward ? wm.clients.next : wm.clients.prev;
293
294        if (it == &wm.clients) break;
295
296        c = wl_container_of(it, c, link);
297        if (is_ws_client(c, wm.sel_screen)) {
298            focus(c);
299            return;
300        }
301
302        it = forward ? it->next : it->prev;
303    } while (it != start);
304
305    focus(get_focus_candidate(wm.sel_screen));
306}
307
308void focus_next(void* data, uint32_t time, uint32_t value, uint32_t state)
309{
310    (void)data; BIND_EVENT(time, value, state);
311    focus_walk_direction(true);
312}
313
314void kill_sel(void* data, uint32_t time, uint32_t value, uint32_t state)
315{
316    (void)data; BIND_EVENT(time, value, state);
317
318    if (wm.sel_client)
319        swc_window_close(wm.sel_client->win);
320}
321
322void fullscreen(void* data, uint32_t time, uint32_t value, uint32_t state)
323{
324    (void)data; BIND_EVENT(time, value, state);
325    if (!wm.sel_client) return;
326
327    struct client *c = wm.sel_client;
328    struct swc_rectangle geom;
329
330    if (c->fullscreen) {
331        c->fullscreen = false;
332        swc_window_set_stacked(c->win);
333        apply_decor(c, true);
334
335        if (c->w > 0 && c->h > 0) {
336            geom = (struct swc_rectangle){ c->x, c->y, c->w, c->h };
337            swc_window_set_geometry(c->win, &geom);
338        }
339        return;
340    }
341
342    if (!c->scr || !c->scr->scr) return;
343
344    if (get_geometry(c, &geom)) {
345        c->x = geom.x;
346        c->y = geom.y;
347        c->w = geom.width;
348        c->h = geom.height;
349    }
350
351    c->fullscreen = true;
352    apply_decor(c, true);
353    swc_window_set_fullscreen(c->win, c->scr->scr);
354}
355
356void kb_move_x(void* data, uint32_t time, uint32_t value, uint32_t state)
357{
358    BIND_ACTION(data, time, value, state);
359    adjust_geom(wm.sel_client, a->i, 0, 0, 0);
360}
361
362void kb_move_y(void* data, uint32_t time, uint32_t value, uint32_t state)
363{
364    BIND_ACTION(data, time, value, state);
365    adjust_geom(wm.sel_client, 0, a->i, 0, 0);
366}
367
368void kb_resize_width(void* data, uint32_t time, uint32_t value, uint32_t state)
369{
370    BIND_ACTION(data, time, value, state);
371    adjust_geom(wm.sel_client, 0, 0, a->i, 0);
372}
373
374void kb_resize_height(void* data, uint32_t time, uint32_t value, uint32_t state)
375{
376    BIND_ACTION(data, time, value, state);
377    adjust_geom(wm.sel_client, 0, 0, 0, a->i);
378}
379
380static void apply_grid_geometry(struct client *c, float x_frac, float w_frac)
381{
382    if (!c || !c->scr || !c->scr->scr) return;
383
384    struct swc_rectangle geom;
385    struct swc_rectangle *ug = &c->scr->scr->usable_geometry;
386
387    geom.x = ug->x + (ug->width * x_frac);
388    geom.y = ug->y;
389    geom.width = ug->width * w_frac;
390    geom.height = ug->height;
391
392    swc_window_set_geometry(c->win, &geom);
393}
394
395void center_window(void* data, uint32_t time, uint32_t value, uint32_t state)
396{
397    (void)data; BIND_EVENT(time, value, state);
398    if (!wm.sel_client->scr || !wm.sel_client->scr->scr) return;
399
400    struct swc_rectangle geom;
401    if (!get_geometry(wm.sel_client, &geom)) return;
402
403    struct swc_rectangle *ug = &wm.sel_client->scr->scr->usable_geometry;
404    geom.x = ug->x + (ug->width - geom.width) / 2;
405    geom.y = ug->y + (ug->height - geom.height) / 2;
406
407    swc_window_set_geometry(wm.sel_client->win, &geom);
408}
409
410void snap_left_half(void* data, uint32_t time, uint32_t value, uint32_t state)
411{
412    (void)data; BIND_EVENT(time, value, state);
413    apply_grid_geometry(wm.sel_client, 0.0f, 0.5f);
414}
415
416void snap_right_half(void* data, uint32_t time, uint32_t value, uint32_t state)
417{
418    (void)data; BIND_EVENT(time, value, state);
419    apply_grid_geometry(wm.sel_client, 0.5f, 0.5f);
420}
421
422void new_screen(struct swc_screen* scr)
423{
424    struct screen* s = calloc(1, sizeof(*s));
425    if (!s) die(EXIT_FAILURE, "new screen calloc failed");
426
427    s->scr = scr;
428    wl_list_insert(&wm.screens, &s->link);
429
430    if (!wm.sel_screen) wm.sel_screen = s;
431
432    swc_screen_set_handler(scr, &screen_handler, s);
433    _log(stderr, "new_screen=%p\n", (void*)scr);
434}
435
436void new_window(struct swc_window* win)
437{
438    struct client* c = calloc(1, sizeof(*c));
439    if (!c) die(EXIT_FAILURE, "calloc client failed");
440
441    win->motion_throttle_ms = 1000 / cfg.motion_throttle_hz;
442    win->min_width = 1;
443    win->min_height = 1;
444
445    c->win = win;
446    c->scr = wm.sel_screen;
447    c->ws = wm.ws;
448
449    wl_list_insert(&wm.clients, &c->link);
450    swc_window_set_handler(win, &window_handler, c);
451    swc_window_set_stacked(win);
452    apply_decor(c, false);
453
454    int32_t cx = 0, cy = 0;
455    if (swc_cursor_position(&cx, &cy))
456        swc_window_set_position(win, cx / 256, cy / 256);
457
458    swc_window_show(win);
459    focus(c);
460
461    _log(stderr, "new_window=%p\n", (void*)win);
462}
463
464void new_device(struct libinput_device* dev)
465{
466    (void)dev;
467}
468
469void quit(void* data, uint32_t time, uint32_t value, uint32_t state)
470{
471    (void)data; BIND_EVENT(time, value, state);
472    wl_display_terminate(wm.dpy);
473}
474
475void spawn(void* data, uint32_t time, uint32_t value, uint32_t state)
476{
477    BIND_EVENT(time, value, state);
478
479    pid_t pid = fork();
480    if (pid == 0) {
481        setsid();
482        if (fork() == 0) {
483            union arg* a = data;
484            char* const* cmd = (char* const*)a->v;
485
486            execvp(cmd[0], cmd);
487            _exit(127);
488        }
489        _exit(0);
490    } else if (pid > 0) {
491        waitpid(pid, NULL, 0);
492    }
493}
494
495void workspace_goto(void* data, uint32_t time, uint32_t value, uint32_t state)
496{
497    union arg* a = data;
498    BIND_EVENT(time, value, state);
499
500    if (a->ui < 1 || a->ui > 9 || a->ui == wm.ws) return;
501
502    wm.ws = a->ui;
503    sync_window_visibility();
504
505    focus(get_focus_candidate(wm.sel_screen));
506}
507
508void workspace_moveto(void* data, uint32_t time, uint32_t value, uint32_t state)
509{
510    BIND_ACTION(data, time, value, state);
511    if (a->ui < 1 || a->ui > 9 || wm.sel_client->ws == a->ui) return;
512
513    struct client* c = wm.sel_client;
514    c->ws = a->ui;
515
516    if (c->ws == wm.ws)
517        swc_window_show(c->win);
518    else
519        swc_window_hide(c->win);
520
521    focus(get_focus_candidate(wm.sel_screen));
522}
523
524int main(void)
525{
526    setup();
527    wl_display_run(wm.dpy);
528    swc_finalize();
529    wl_display_destroy(wm.dpy);
530    return EXIT_SUCCESS;
531}