comparison gbwm.c @ 8:0e5c535e379e default tip

Move cursor with focus
author Atarwn Gard <a@qwa.su>
date Mon, 13 Oct 2025 09:49:19 +0500
parents 180e42b65105
children
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++) {