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++) { |