Mercurial > gbwm
comparison gbwm.c @ 8:0e5c535e379e
Move cursor with focus
| author | Atarwn Gard <a@qwa.su> | 
|---|---|
| date | Mon, 13 Oct 2025 09:49:19 +0500 | 
| parents | 180e42b65105 | 
| children | 1d9dacd8fe55 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 7:180e42b65105 | 8:0e5c535e379e | 
|---|---|
| 1 #define _POSIX_C_SOURCE 200809L | |
| 1 /* gbwm - grid-based tiling window manager */ | 2 /* gbwm - grid-based tiling window manager */ | 
| 2 #include <X11/Xlib.h> | 3 #include <X11/Xlib.h> | 
| 3 #include <X11/Xatom.h> | 4 #include <X11/Xatom.h> | 
| 4 #include <X11/Xft/Xft.h> | 5 #include <X11/Xft/Xft.h> | 
| 5 #include <X11/Xutil.h> | 6 #include <X11/Xutil.h> | 
| 6 #include <X11/keysym.h> | 7 #include <X11/keysym.h> | 
| 7 #include <X11/cursorfont.h> | 8 #include <X11/cursorfont.h> | 
| 9 #include <X11/extensions/XTest.h> | |
| 8 #include <stdio.h> | 10 #include <stdio.h> | 
| 9 #include <stdlib.h> | 11 #include <stdlib.h> | 
| 10 #include <stdarg.h> | 12 #include <stdarg.h> | 
| 11 #include <string.h> | 13 #include <string.h> | 
| 12 #include <unistd.h> | 14 #include <unistd.h> | 
| 13 #include <signal.h> | 15 #include <signal.h> | 
| 14 #include <sys/wait.h> | 16 #include <sys/wait.h> | 
| 17 #include <time.h> | |
| 15 | 18 | 
| 16 typedef struct Client Client; | 19 typedef struct Client Client; | 
| 17 struct Client { | 20 struct Client { | 
| 18 Window win; | 21 Window win; | 
| 19 int x, y, w, h; | 22 int x, y, w, h; | 
| 56 static Atom wm_protocols, wm_delete_window, wm_state, wm_take_focus; | 59 static Atom wm_protocols, wm_delete_window, wm_state, wm_take_focus; | 
| 57 | 60 | 
| 58 // Forward decls | 61 // Forward decls | 
| 59 static void arrange(void); | 62 static void arrange(void); | 
| 60 static void resize(Client *c, int x, int y, int w, int h); | 63 static void resize(Client *c, int x, int y, int w, int h); | 
| 61 static void focus(Client *c); | 64 static void focus(Client *c, int warp); | 
| 62 static void spawn(const Arg *arg); | 65 static void spawn(const Arg *arg); | 
| 63 static void killclient(const Arg *arg); | 66 static void killclient(const Arg *arg); | 
| 64 static void toggle_fullscreen(const Arg *arg); | 67 static void toggle_fullscreen(const Arg *arg); | 
| 65 static void enter_overlay(const Arg *arg); | 68 static void enter_overlay(const Arg *arg); | 
| 66 static void process_overlay_input(void); | 69 static void process_overlay_input(void); | 
| 73 static int sendevent(Client *c, Atom proto); | 76 static int sendevent(Client *c, Atom proto); | 
| 74 static void updateborder(Client *c); | 77 static void updateborder(Client *c); | 
| 75 static void find_next_free_cell(int *out_r, int *out_c); | 78 static void find_next_free_cell(int *out_r, int *out_c); | 
| 76 static void switchws(const Arg *arg); | 79 static void switchws(const Arg *arg); | 
| 77 static void movewin_to_ws(const Arg *arg); | 80 static void movewin_to_ws(const Arg *arg); | 
| 81 static void die(const char *fmt, ...); | |
| 78 | 82 | 
| 79 #include "config.h" | 83 #include "config.h" | 
| 80 | 84 | 
| 81 // Event handlers | 85 // Event handlers | 
| 82 static void buttonpress(XEvent *e) { | 86 static void buttonpress(XEvent *e) { | 
| 83 for (Client *c = workspaces[current_ws]; c; c = c->next) | 87 for (Client *c = workspaces[current_ws]; c; c = c->next) | 
| 84 if (c->win == e->xbutton.subwindow) { | 88 if (c->win == e->xbutton.subwindow) { | 
| 85 focus(c); | 89 focus(c, 1); | 
| 86 break; | 90 break; | 
| 87 } | 91 } | 
| 88 } | 92 } | 
| 89 | 93 | 
| 90 static void clientmessage(XEvent *e) { | 94 static void clientmessage(XEvent *e) { | 
| 119 // Set WM_STATE | 123 // Set WM_STATE | 
| 120 long data[] = { NormalState, None }; | 124 long data[] = { NormalState, None }; | 
| 121 XChangeProperty(dpy, c->win, wm_state, wm_state, 32, PropModeReplace, (unsigned char *)data, 2); | 125 XChangeProperty(dpy, c->win, wm_state, wm_state, 32, PropModeReplace, (unsigned char *)data, 2); | 
| 122 | 126 | 
| 123 XMapWindow(dpy, c->win); | 127 XMapWindow(dpy, c->win); | 
| 124 focus(c); | 128 focus(c, 1); | 
| 125 arrange(); | 129 arrange(); | 
| 126 } | 130 } | 
| 127 | 131 | 
| 128 static void removeclient(Window win) { | 132 static void removeclient(Window win) { | 
| 129 Client *c, **prev; | 133 Client *c, **prev; | 
| 131 if (c->win == win) { | 135 if (c->win == win) { | 
| 132 *prev = c->next; | 136 *prev = c->next; | 
| 133 if (focused == c) { | 137 if (focused == c) { | 
| 134 focused = workspaces[current_ws]; | 138 focused = workspaces[current_ws]; | 
| 135 if (focused) | 139 if (focused) | 
| 136 focus(focused); | 140 focus(focused, 1); | 
| 137 } | 141 } | 
| 138 free(c); | 142 free(c); | 
| 139 arrange(); | 143 arrange(); | 
| 140 return; | 144 return; | 
| 141 } | 145 } | 
| 153 static void enternotify(XEvent *e) { | 157 static void enternotify(XEvent *e) { | 
| 154 if (e->xcrossing.mode != NotifyNormal || e->xcrossing.detail == NotifyInferior) | 158 if (e->xcrossing.mode != NotifyNormal || e->xcrossing.detail == NotifyInferior) | 
| 155 return; | 159 return; | 
| 156 for (Client *c = workspaces[current_ws]; c; c = c->next) | 160 for (Client *c = workspaces[current_ws]; c; c = c->next) | 
| 157 if (c->win == e->xcrossing.window) { | 161 if (c->win == e->xcrossing.window) { | 
| 158 focus(c); | 162 focus(c, 0); | 
| 159 break; | 163 break; | 
| 160 } | 164 } | 
| 161 } | 165 } | 
| 162 | 166 | 
| 163 static void expose(XEvent *e) { | 167 static void expose(XEvent *e) { | 
| 190 overlay_input[0] = ch; | 194 overlay_input[0] = ch; | 
| 191 draw_overlay(); | 195 draw_overlay(); | 
| 192 } else if (overlay_input[1] == 0) { | 196 } else if (overlay_input[1] == 0) { | 
| 193 overlay_input[1] = ch; | 197 overlay_input[1] = ch; | 
| 194 draw_overlay(); | 198 draw_overlay(); | 
| 195 usleep(150000); | 199 struct timespec ts = { | 
| 200 .tv_sec = 0, | |
| 201 .tv_nsec = 150000 * 1000 | |
| 202 }; | |
| 203 nanosleep(&ts, NULL); | |
| 196 process_overlay_input(); | 204 process_overlay_input(); | 
| 197 hide_overlay(); | 205 hide_overlay(); | 
| 198 } | 206 } | 
| 199 return; | 207 return; | 
| 200 } | 208 } | 
| 218 | 226 | 
| 219 static void updateborder(Client *c) { | 227 static void updateborder(Client *c) { | 
| 220 XSetWindowBorder(dpy, c->win, c == focused ? border_focused : border_normal); | 228 XSetWindowBorder(dpy, c->win, c == focused ? border_focused : border_normal); | 
| 221 } | 229 } | 
| 222 | 230 | 
| 223 static void focus(Client *c) { | 231 static void focus(Client *c, int warp) { | 
| 224 if (!c) return; | 232 if (!c) return; | 
| 225 | 233 | 
| 226 Client *old = focused; | 234 Client *old = focused; | 
| 227 focused = c; | 235 focused = c; | 
| 228 | 236 | 
| 231 | 239 | 
| 232 updateborder(c); | 240 updateborder(c); | 
| 233 XRaiseWindow(dpy, c->win); | 241 XRaiseWindow(dpy, c->win); | 
| 234 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | 242 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | 
| 235 sendevent(c, wm_take_focus); | 243 sendevent(c, wm_take_focus); | 
| 244 | |
| 245 if (warp && !c->isfullscreen) { | |
| 246 int cursor_x = c->x + c->w - 16; | |
| 247 int cursor_y = c->y + c->h - 16; | |
| 248 if (cursor_x < 0) cursor_x = 0; | |
| 249 if (cursor_y < 0) cursor_y = 0; | |
| 250 if (cursor_x >= sw) cursor_x = sw - 1; | |
| 251 if (cursor_y >= sh) cursor_y = sh - 1; | |
| 252 | |
| 253 XWarpPointer(dpy, None, root, 0, 0, 0, 0, cursor_x, cursor_y); | |
| 254 } | |
| 236 } | 255 } | 
| 237 | 256 | 
| 238 static int is_cell_free(int r, int c, int cell_w, int cell_h) { | 257 static int is_cell_free(int r, int c, int cell_w, int cell_h) { | 
| 239 int cell_x = padding + c * (cell_w + padding); | 258 int cell_x = padding + c * (cell_w + padding); | 
| 240 int cell_y = padding + r * (cell_h + padding); | 259 int cell_y = padding + r * (cell_h + padding); | 
| 485 int y = padding + r1 * (cell_h + padding); | 504 int y = padding + r1 * (cell_h + padding); | 
| 486 int w = cols_span * cell_w + (cols_span - 1) * padding; | 505 int w = cols_span * cell_w + (cols_span - 1) * padding; | 
| 487 int h = rows_span * cell_h + (rows_span - 1) * padding; | 506 int h = rows_span * cell_h + (rows_span - 1) * padding; | 
| 488 | 507 | 
| 489 resize(focused, x, y, w, h); | 508 resize(focused, x, y, w, h); | 
| 509 if (focused) focus(focused, 1); | |
| 490 } | 510 } | 
| 491 | 511 | 
| 492 // Workspace functions | 512 // Workspace functions | 
| 493 static void switchws(const Arg *arg) { | 513 static void switchws(const Arg *arg) { | 
| 494 int ws = arg->i; | 514 int ws = arg->i; | 
| 507 for (Client *c = workspaces[current_ws]; c; c = c->next) { | 527 for (Client *c = workspaces[current_ws]; c; c = c->next) { | 
| 508 XMapWindow(dpy, c->win); | 528 XMapWindow(dpy, c->win); | 
| 509 } | 529 } | 
| 510 | 530 | 
| 511 focused = workspaces[current_ws]; | 531 focused = workspaces[current_ws]; | 
| 512 if (focused) focus(focused); | 532 if (focused) focus(focused, 1); | 
| 513 arrange(); | 533 arrange(); | 
| 514 } | 534 } | 
| 515 | 535 | 
| 516 static void movewin_to_ws(const Arg *arg) { | 536 static void movewin_to_ws(const Arg *arg) { | 
| 517 int ws = arg->i; | 537 int ws = arg->i; | 
| 538 XUnmapWindow(dpy, moving->win); | 558 XUnmapWindow(dpy, moving->win); | 
| 539 | 559 | 
| 540 // Update focus to next available window in current workspace | 560 // Update focus to next available window in current workspace | 
| 541 focused = workspaces[current_ws]; | 561 focused = workspaces[current_ws]; | 
| 542 if (focused) { | 562 if (focused) { | 
| 543 focus(focused); | 563 focus(focused, 0); | 
| 544 } else { | 564 } else { | 
| 545 focused = NULL; | 565 focused = NULL; | 
| 546 } | 566 } | 
| 547 | 567 | 
| 548 // Re-arrange current workspace | 568 // Re-arrange current workspace | 
| 633 if (fork() == 0) { | 653 if (fork() == 0) { | 
| 634 if (dpy) | 654 if (dpy) | 
| 635 close(ConnectionNumber(dpy)); | 655 close(ConnectionNumber(dpy)); | 
| 636 setsid(); | 656 setsid(); | 
| 637 execvp(((char **)arg->v)[0], (char **)arg->v); | 657 execvp(((char **)arg->v)[0], (char **)arg->v); | 
| 638 fprintf(stderr, "eowm: execvp %s failed\n", ((char **)arg->v)[0]); | 658 die("execvp %s failed", ((char **)arg->v)[0]); | 
| 639 exit(1); | |
| 640 } | 659 } | 
| 641 } | 660 } | 
| 642 | 661 | 
| 643 static void quit(const Arg *arg) { | 662 static void quit(const Arg *arg) { | 
| 644 exit(0); | 663 exit(0); | 
| 646 | 665 | 
| 647 static void cycle_focus(const Arg *arg) { | 666 static void cycle_focus(const Arg *arg) { | 
| 648 if (!workspaces[current_ws]) return; | 667 if (!workspaces[current_ws]) return; | 
| 649 | 668 | 
| 650 if (!focused) { | 669 if (!focused) { | 
| 651 focus(workspaces[current_ws]); | 670 focus(workspaces[current_ws], 1); | 
| 652 return; | 671 return; | 
| 653 } | 672 } | 
| 654 | 673 | 
| 655 Client *next = focused->next; | 674 Client *next = focused->next; | 
| 656 if (!next) next = workspaces[current_ws]; | 675 if (!next) next = workspaces[current_ws]; | 
| 657 | 676 | 
| 658 focus(next); | 677 focus(next, 1); | 
| 659 } | 678 } | 
| 660 | 679 | 
| 661 static void grabkeys(void) { | 680 static void grabkeys(void) { | 
| 662 XUngrabKey(dpy, AnyKey, AnyModifier, root); | 681 XUngrabKey(dpy, AnyKey, AnyModifier, root); | 
| 663 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) { | 682 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) { |