comparison gbwm.c @ 7:180e42b65105 1.0

Finalizing
author Atarwn Gard <a@qwa.su>
date Sun, 12 Oct 2025 21:57:17 +0500
parents f1f332156693
children
comparison
equal deleted inserted replaced
6:49a8b21b0f95 7:180e42b65105
5 #include <X11/Xutil.h> 5 #include <X11/Xutil.h>
6 #include <X11/keysym.h> 6 #include <X11/keysym.h>
7 #include <X11/cursorfont.h> 7 #include <X11/cursorfont.h>
8 #include <stdio.h> 8 #include <stdio.h>
9 #include <stdlib.h> 9 #include <stdlib.h>
10 #include <stdarg.h>
10 #include <string.h> 11 #include <string.h>
11 #include <unistd.h> 12 #include <unistd.h>
12 #include <signal.h> 13 #include <signal.h>
13 #include <sys/wait.h> 14 #include <sys/wait.h>
14 15
15 typedef struct Client Client; 16 typedef struct Client Client;
16 struct Client { 17 struct Client {
17 Window win; 18 Window win;
18 int x, y, w, h; 19 int x, y, w, h;
19 int saved_x, saved_y, saved_w, saved_h; // Saved position before fullscreen 20 int saved_x, saved_y, saved_w, saved_h; // Saved position before fullscreen
20 int isfullscreen; 21 int isfullscreen;
21 int workspace; 22 int workspace;
22 Client *next; 23 Client *next;
23 }; 24 };
24 25
25 typedef union { 26 typedef union {
26 int i; 27 int i;
27 unsigned int ui; 28 unsigned int ui;
28 float f; 29 float f;
29 const void *v; 30 const void *v;
30 } Arg; 31 } Arg;
31 32
32 typedef struct { 33 typedef struct {
33 unsigned int mod; 34 unsigned int mod;
34 KeySym keysym; 35 KeySym keysym;
35 void (*func)(const Arg *); 36 void (*func)(const Arg *);
36 const Arg arg; 37 const Arg arg;
37 } Key; 38 } Key;
38 39
39 static Display *dpy; 40 static Display *dpy;
40 static Window root; 41 static Window root;
41 static Client *workspaces[9] = {NULL}; // 9 workspaces 42 static Client *workspaces[9] = {NULL}; // 9 workspaces
77 78
78 #include "config.h" 79 #include "config.h"
79 80
80 // Event handlers 81 // Event handlers
81 static void buttonpress(XEvent *e) { 82 static void buttonpress(XEvent *e) {
82 for (Client *c = workspaces[current_ws]; c; c = c->next) 83 for (Client *c = workspaces[current_ws]; c; c = c->next)
83 if (c->win == e->xbutton.subwindow) { 84 if (c->win == e->xbutton.subwindow) {
84 focus(c); 85 focus(c);
85 break; 86 break;
86 } 87 }
87 } 88 }
88 89
89 static void clientmessage(XEvent *e) { 90 static void clientmessage(XEvent *e) {
90 XClientMessageEvent *cme = &e->xclient; 91 XClientMessageEvent *cme = &e->xclient;
91 Client *c; 92 Client *c;
92 for (c = workspaces[current_ws]; c; c = c->next) 93 for (c = workspaces[current_ws]; c; c = c->next)
93 if (c->win == cme->window) 94 if (c->win == cme->window)
94 break; 95 break;
95 if (!c) 96 if (!c)
96 return; 97 return;
97 98
98 if (cme->message_type == wm_state && cme->data.l[1] == (long)XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False)) { 99 if (cme->message_type == wm_state && cme->data.l[1] == (long)XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False)) {
99 setfullscreen(c, cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->isfullscreen)); 100 setfullscreen(c, cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->isfullscreen));
100 } 101 }
101 } 102 }
102 103
103 static void maprequest(XEvent *e) { 104 static void maprequest(XEvent *e) {
104 XWindowAttributes wa; 105 XWindowAttributes wa;
105 if (!XGetWindowAttributes(dpy, e->xmaprequest.window, &wa)) return; 106 if (!XGetWindowAttributes(dpy, e->xmaprequest.window, &wa)) return;
106 if (wa.override_redirect) return; 107 if (wa.override_redirect) return;
107 108
108 Client *c = calloc(1, sizeof(Client)); 109 Client *c = calloc(1, sizeof(Client));
109 c->win = e->xmaprequest.window; 110 c->win = e->xmaprequest.window;
110 c->workspace = current_ws; 111 c->workspace = current_ws;
111 c->next = workspaces[current_ws]; 112 c->next = workspaces[current_ws];
112 workspaces[current_ws] = c; 113 workspaces[current_ws] = c;
113 114
114 // ICCCM setup 115 // ICCCM setup
115 XSetWindowBorderWidth(dpy, c->win, border_width); 116 XSetWindowBorderWidth(dpy, c->win, border_width);
116 XSelectInput(dpy, c->win, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); 117 XSelectInput(dpy, c->win, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask);
117 118
118 // Set WM_STATE 119 // Set WM_STATE
119 long data[] = { NormalState, None }; 120 long data[] = { NormalState, None };
120 XChangeProperty(dpy, c->win, wm_state, wm_state, 32, PropModeReplace, (unsigned char *)data, 2); 121 XChangeProperty(dpy, c->win, wm_state, wm_state, 32, PropModeReplace, (unsigned char *)data, 2);
121 122
122 XMapWindow(dpy, c->win); 123 XMapWindow(dpy, c->win);
123 focus(c); 124 focus(c);
124 arrange(); 125 arrange();
125 } 126 }
126 127
127 static void removeclient(Window win) { 128 static void removeclient(Window win) {
128 Client *c, **prev; 129 Client *c, **prev;
129 for (prev = &workspaces[current_ws]; (c = *prev); prev = &c->next) { 130 for (prev = &workspaces[current_ws]; (c = *prev); prev = &c->next) {
130 if (c->win == win) { 131 if (c->win == win) {
131 *prev = c->next; 132 *prev = c->next;
132 if (focused == c) { 133 if (focused == c) {
133 focused = workspaces[current_ws]; 134 focused = workspaces[current_ws];
134 if (focused) 135 if (focused)
135 focus(focused); 136 focus(focused);
136 } 137 }
137 free(c); 138 free(c);
138 arrange(); 139 arrange();
139 return; 140 return;
140 } 141 }
141 } 142 }
142 } 143 }
143 144
144 static void unmapnotify(XEvent *e) { 145 static void unmapnotify(XEvent *e) {
145 removeclient(e->xunmap.window); 146 removeclient(e->xunmap.window);
146 } 147 }
147 148
148 static void destroynotify(XEvent *e) { 149 static void destroynotify(XEvent *e) {
149 removeclient(e->xdestroywindow.window); 150 removeclient(e->xdestroywindow.window);
150 } 151 }
151 152
152 static void enternotify(XEvent *e) { 153 static void enternotify(XEvent *e) {
153 if (e->xcrossing.mode != NotifyNormal || e->xcrossing.detail == NotifyInferior) 154 if (e->xcrossing.mode != NotifyNormal || e->xcrossing.detail == NotifyInferior)
154 return; 155 return;
155 for (Client *c = workspaces[current_ws]; c; c = c->next) 156 for (Client *c = workspaces[current_ws]; c; c = c->next)
156 if (c->win == e->xcrossing.window) { 157 if (c->win == e->xcrossing.window) {
157 focus(c); 158 focus(c);
158 break; 159 break;
159 } 160 }
160 } 161 }
161 162
162 static void expose(XEvent *e) { 163 static void expose(XEvent *e) {
163 if (e->xexpose.window == overlay_win && overlay_mode) { 164 if (e->xexpose.window == overlay_win && overlay_mode) {
164 draw_overlay(); 165 draw_overlay();
165 } 166 }
166 } 167 }
167 168
168 static void keypress(XEvent *e) { 169 static void keypress(XEvent *e) {
169 if (overlay_mode) { 170 if (overlay_mode) {
170 KeySym k = XLookupKeysym(&e->xkey, 0); 171 KeySym k = XLookupKeysym(&e->xkey, 0);
171 if (k == XK_Escape) { 172 if (k == XK_Escape) {
172 hide_overlay(); 173 hide_overlay();
173 return; 174 return;
174 } 175 }
175 176
176 char ch = 0; 177 char ch = 0;
177 if (k >= '0' && k <= '9') ch = (char)k; 178 if (k >= '0' && k <= '9') ch = (char)k;
178 else if (k >= 'a' && k <= 'z') ch = (char)k; 179 else if (k >= 'a' && k <= 'z') ch = (char)k;
179 else if (k >= 'A' && k <= 'Z') ch = (char)(k - 'A' + 'a'); 180 else if (k >= 'A' && k <= 'Z') ch = (char)(k - 'A' + 'a');
180 else return; 181 else return;
181 182
182 int found = 0; 183 int found = 0;
183 for (int r = 0; r < GRID_ROWS && !found; r++) 184 for (int r = 0; r < GRID_ROWS && !found; r++)
184 for (int c = 0; c < GRID_COLS && !found; c++) 185 for (int c = 0; c < GRID_COLS && !found; c++)
185 if (grid_chars[r][c] == ch) found = 1; 186 if (grid_chars[r][c] == ch) found = 1;
186 if (!found) return; 187 if (!found) return;
187 188
188 if (overlay_input[0] == 0) { 189 if (overlay_input[0] == 0) {
189 overlay_input[0] = ch; 190 overlay_input[0] = ch;
190 draw_overlay(); 191 draw_overlay();
191 } else if (overlay_input[1] == 0) { 192 } else if (overlay_input[1] == 0) {
192 overlay_input[1] = ch; 193 overlay_input[1] = ch;
193 draw_overlay(); 194 draw_overlay();
194 usleep(150000); 195 usleep(150000);
195 process_overlay_input(); 196 process_overlay_input();
196 hide_overlay(); 197 hide_overlay();
197 } 198 }
198 return; 199 return;
199 } 200 }
200 201
201 KeySym keysym = XLookupKeysym(&e->xkey, 0); 202 KeySym keysym = XLookupKeysym(&e->xkey, 0);
202 unsigned int state = e->xkey.state & ~(LockMask | Mod2Mask); 203 unsigned int state = e->xkey.state & ~(LockMask | Mod2Mask);
203 204
204 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) { 205 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) {
205 if (keysym == keys[i].keysym && state == keys[i].mod && keys[i].func) { 206 if (keysym == keys[i].keysym && state == keys[i].mod && keys[i].func) {
206 keys[i].func(&keys[i].arg); 207 keys[i].func(&keys[i].arg);
207 return; 208 return;
208 } 209 }
209 } 210 }
210 } 211 }
211 212
212 // Core logic 213 // Core logic
213 static void resize(Client *c, int x, int y, int w, int h) { 214 static void resize(Client *c, int x, int y, int w, int h) {
214 c->x = x; c->y = y; c->w = w; c->h = h; 215 c->x = x; c->y = y; c->w = w; c->h = h;
215 XMoveResizeWindow(dpy, c->win, x, y, w, h); 216 XMoveResizeWindow(dpy, c->win, x, y, w, h);
216 } 217 }
217 218
218 static void updateborder(Client *c) { 219 static void updateborder(Client *c) {
219 XSetWindowBorder(dpy, c->win, c == focused ? border_focused : border_normal); 220 XSetWindowBorder(dpy, c->win, c == focused ? border_focused : border_normal);
220 } 221 }
221 222
222 static void focus(Client *c) { 223 static void focus(Client *c) {
223 if (!c) return; 224 if (!c) return;
224 225
225 Client *old = focused; 226 Client *old = focused;
226 focused = c; 227 focused = c;
227 228
228 if (old && old != c) 229 if (old && old != c)
229 updateborder(old); 230 updateborder(old);
230 231
231 updateborder(c); 232 updateborder(c);
232 XRaiseWindow(dpy, c->win); 233 XRaiseWindow(dpy, c->win);
233 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 234 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
234 sendevent(c, wm_take_focus); 235 sendevent(c, wm_take_focus);
235 } 236 }
236 237
237 static int is_cell_free(int r, int c, int cell_w, int cell_h) { 238 static int is_cell_free(int r, int c, int cell_w, int cell_h) {
238 int cell_x = padding + c * (cell_w + padding); 239 int cell_x = padding + c * (cell_w + padding);
239 int cell_y = padding + r * (cell_h + padding); 240 int cell_y = padding + r * (cell_h + padding);
240 241
241 // Check if any window overlaps with this cell 242 // Check if any window overlaps with this cell
242 for (Client *cl = workspaces[current_ws]; cl; cl = cl->next) { 243 for (Client *cl = workspaces[current_ws]; cl; cl = cl->next) {
243 if (cl->isfullscreen) continue; 244 if (cl->isfullscreen) continue;
244 245
245 // Check for overlap 246 // Check for overlap
246 int cl_right = cl->x + cl->w; 247 int cl_right = cl->x + cl->w;
247 int cl_bottom = cl->y + cl->h; 248 int cl_bottom = cl->y + cl->h;
248 int cell_right = cell_x + cell_w; 249 int cell_right = cell_x + cell_w;
249 int cell_bottom = cell_y + cell_h; 250 int cell_bottom = cell_y + cell_h;
250 251
251 // If rectangles overlap 252 // If rectangles overlap
252 if (!(cl_right <= cell_x || cl->x >= cell_right || 253 if (!(cl_right <= cell_x || cl->x >= cell_right ||
253 cl_bottom <= cell_y || cl->y >= cell_bottom)) { 254 cl_bottom <= cell_y || cl->y >= cell_bottom)) {
254 return 0; // Cell is occupied 255 return 0; // Cell is occupied
255 } 256 }
256 } 257 }
257 258
258 return 1; // Cell is free 259 return 1; // Cell is free
259 } 260 }
260 261
261 static void find_next_free_cell(int *out_r, int *out_c) { 262 static void find_next_free_cell(int *out_r, int *out_c) {
262 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS; 263 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS;
263 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS; 264 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS;
264 265
265 // First pass: look for any completely free cell 266 // First pass: look for any completely free cell
266 for (int r = 0; r < GRID_ROWS; r++) { 267 for (int r = 0; r < GRID_ROWS; r++) {
267 for (int c = 0; c < GRID_COLS; c++) { 268 for (int c = 0; c < GRID_COLS; c++) {
268 if (is_cell_free(r, c, cell_w, cell_h)) { 269 if (is_cell_free(r, c, cell_w, cell_h)) {
269 *out_r = r; 270 *out_r = r;
270 *out_c = c; 271 *out_c = c;
271 return; 272 return;
272 } 273 }
273 } 274 }
274 } 275 }
275 276
276 // Second pass: no free space found, check top-left for 1x1 windows 277 // Second pass: no free space found, check top-left for 1x1 windows
277 int cell_x = padding; 278 int cell_x = padding;
278 int cell_y = padding; 279 int cell_y = padding;
279 280
280 for (Client *cl = workspaces[current_ws]; cl; cl = cl->next) { 281 for (Client *cl = workspaces[current_ws]; cl; cl = cl->next) {
281 if (cl->isfullscreen) continue; 282 if (cl->isfullscreen) continue;
282 if (cl->x == cell_x && cl->y == cell_y && 283 if (cl->x == cell_x && cl->y == cell_y &&
283 cl->w == cell_w && cl->h == cell_h) { 284 cl->w == cell_w && cl->h == cell_h) {
284 // Found a 1x1 window at top-left, find next free cell 285 // Found a 1x1 window at top-left, find next free cell
285 for (int r = 0; r < GRID_ROWS; r++) { 286 for (int r = 0; r < GRID_ROWS; r++) {
286 for (int c = 0; c < GRID_COLS; c++) { 287 for (int c = 0; c < GRID_COLS; c++) {
287 int check_x = padding + c * (cell_w + padding); 288 int check_x = padding + c * (cell_w + padding);
288 int check_y = padding + r * (cell_h + padding); 289 int check_y = padding + r * (cell_h + padding);
289 290
290 int found_1x1 = 0; 291 int found_1x1 = 0;
291 for (Client *check = workspaces[current_ws]; check; check = check->next) { 292 for (Client *check = workspaces[current_ws]; check; check = check->next) {
292 if (check->isfullscreen) continue; 293 if (check->isfullscreen) continue;
293 if (check->x == check_x && check->y == check_y && 294 if (check->x == check_x && check->y == check_y &&
294 check->w == cell_w && check->h == cell_h) { 295 check->w == cell_w && check->h == cell_h) {
295 found_1x1 = 1; 296 found_1x1 = 1;
296 break; 297 break;
297 } 298 }
298 } 299 }
299 300
300 if (!found_1x1) { 301 if (!found_1x1) {
301 *out_r = r; 302 *out_r = r;
302 *out_c = c; 303 *out_c = c;
303 return; 304 return;
304 } 305 }
305 } 306 }
306 } 307 }
307 break; 308 break;
308 } 309 }
309 } 310 }
310 311
311 // Fallback to top-left 312 // Fallback to top-left
312 *out_r = 0; 313 *out_r = 0;
313 *out_c = 0; 314 *out_c = 0;
314 } 315 }
315 316
316 static void arrange(void) { 317 static void arrange(void) {
317 if (!workspaces[current_ws]) return; 318 if (!workspaces[current_ws]) return;
318 319
319 if (!focused) focused = workspaces[current_ws]; 320 if (!focused) focused = workspaces[current_ws];
320 321
321 if (focused->isfullscreen) { 322 if (focused->isfullscreen) {
322 return; 323 return;
323 } 324 }
324 325
325 // Default window location - find next free cell 326 // Default window location - find next free cell
326 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS; 327 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS;
327 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS; 328 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS;
328 329
329 if (focused->w == 0 || focused->h == 0) { 330 if (focused->w == 0 || focused->h == 0) {
330 int r, c; 331 int r, c;
331 find_next_free_cell(&r, &c); 332 find_next_free_cell(&r, &c);
332 int x = padding + c * (cell_w + padding); 333 int x = padding + c * (cell_w + padding);
333 int y = padding + r * (cell_h + padding); 334 int y = padding + r * (cell_h + padding);
334 resize(focused, x, y, cell_w, cell_h); 335 resize(focused, x, y, cell_w, cell_h);
335 } 336 }
336 337
337 // Update all borders 338 // Update all borders
338 for (Client *c = workspaces[current_ws]; c; c = c->next) 339 for (Client *c = workspaces[current_ws]; c; c = c->next)
339 updateborder(c); 340 updateborder(c);
340 } 341 }
341 342
342 static void draw_overlay(void) { 343 static void draw_overlay(void) {
343 if (!overlay_win) return; 344 if (!overlay_win) return;
344 345
345 XClearWindow(dpy, overlay_win); 346 XClearWindow(dpy, overlay_win);
346 347
347 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS; 348 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS;
348 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS; 349 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS;
349 350
350 int r1 = -1, c1 = -1, r2 = -1, c2 = -1; 351 int r1 = -1, c1 = -1, r2 = -1, c2 = -1;
351 if (overlay_input[0]) { 352 if (overlay_input[0]) {
352 for (int r = 0; r < GRID_ROWS; r++) 353 for (int r = 0; r < GRID_ROWS; r++)
353 for (int c = 0; c < GRID_COLS; c++) 354 for (int c = 0; c < GRID_COLS; c++)
354 if (grid_chars[r][c] == overlay_input[0]) { r1 = r; c1 = c; } 355 if (grid_chars[r][c] == overlay_input[0]) { r1 = r; c1 = c; }
355 } 356 }
356 if (overlay_input[1]) { 357 if (overlay_input[1]) {
357 for (int r = 0; r < GRID_ROWS; r++) 358 for (int r = 0; r < GRID_ROWS; r++)
358 for (int c = 0; c < GRID_COLS; c++) 359 for (int c = 0; c < GRID_COLS; c++)
359 if (grid_chars[r][c] == overlay_input[1]) { r2 = r; c2 = c; } 360 if (grid_chars[r][c] == overlay_input[1]) { r2 = r; c2 = c; }
360 } 361 }
361 362
362 for (int r = 0; r < GRID_ROWS; r++) { 363 for (int r = 0; r < GRID_ROWS; r++) {
363 for (int c = 0; c < GRID_COLS; c++) { 364 for (int c = 0; c < GRID_COLS; c++) {
364 int x = padding + c * (cell_w + padding); 365 int x = padding + c * (cell_w + padding);
365 int y = padding + r * (cell_h + padding); 366 int y = padding + r * (cell_h + padding);
366 367
367 int is_selected = 0; 368 int is_selected = 0;
368 if (r1 >= 0 && c1 >= 0) { 369 if (r1 >= 0 && c1 >= 0) {
369 if (r2 >= 0 && c2 >= 0) { 370 if (r2 >= 0 && c2 >= 0) {
370 int min_r = r1 < r2 ? r1 : r2; 371 int min_r = r1 < r2 ? r1 : r2;
371 int max_r = r1 > r2 ? r1 : r2; 372 int max_r = r1 > r2 ? r1 : r2;
372 int min_c = c1 < c2 ? c1 : c2; 373 int min_c = c1 < c2 ? c1 : c2;
373 int max_c = c1 > c2 ? c1 : c2; 374 int max_c = c1 > c2 ? c1 : c2;
374 if (r >= min_r && r <= max_r && c >= min_c && c <= max_c) 375 if (r >= min_r && r <= max_r && c >= min_c && c <= max_c)
375 is_selected = 1; 376 is_selected = 1;
376 } else if (r == r1 && c == c1) { 377 } else if (r == r1 && c == c1) {
377 is_selected = 1; 378 is_selected = 1;
378 } 379 }
379 } 380 }
380 381
381 if (is_selected) { 382 if (is_selected) {
382 XSetForeground(dpy, gc, xft_col_sel.pixel); 383 XSetForeground(dpy, gc, xft_col_sel.pixel);
383 XFillRectangle(dpy, overlay_win, gc, x, y, cell_w, cell_h); 384 XFillRectangle(dpy, overlay_win, gc, x, y, cell_w, cell_h);
384 } 385 }
385 386
386 XSetForeground(dpy, gc, xft_col_fg.pixel); 387 XSetForeground(dpy, gc, xft_col_fg.pixel);
387 XDrawRectangle(dpy, overlay_win, gc, x, y, cell_w, cell_h); 388 XDrawRectangle(dpy, overlay_win, gc, x, y, cell_w, cell_h);
388 389
389 if (font && xftdraw) { 390 if (font && xftdraw) {
390 char txt[2] = {grid_chars[r][c], 0}; 391 char txt[2] = {grid_chars[r][c], 0};
391 XGlyphInfo extents; 392 XGlyphInfo extents;
392 XftTextExtentsUtf8(dpy, font, (FcChar8*)txt, strlen(txt), &extents); 393 XftTextExtentsUtf8(dpy, font, (FcChar8*)txt, strlen(txt), &extents);
393 394
394 int tx = x + (cell_w - extents.width) / 2; 395 int tx = x + (cell_w - extents.width) / 2;
395 int ty = y + (cell_h - extents.height) / 2 + extents.y; 396 int ty = y + (cell_h - extents.height) / 2 + extents.y;
396 397
397 XftDrawStringUtf8(xftdraw, &xft_col_fg, font, tx, ty, 398 XftDrawStringUtf8(xftdraw, &xft_col_fg, font, tx, ty,
398 (FcChar8*)txt, strlen(txt)); 399 (FcChar8*)txt, strlen(txt));
399 } 400 }
400 } 401 }
401 } 402 }
402 403
403 if (overlay_input[0] || overlay_input[1]) { 404 if (overlay_input[0] || overlay_input[1]) {
404 char status[64]; 405 char status[64];
405 snprintf(status, sizeof(status), "Input: %c%c", 406 snprintf(status, sizeof(status), "Input: %c%c",
406 overlay_input[0] ? overlay_input[0] : ' ', 407 overlay_input[0] ? overlay_input[0] : ' ',
407 overlay_input[1] ? overlay_input[1] : ' '); 408 overlay_input[1] ? overlay_input[1] : ' ');
408 409
409 if (font && xftdraw) { 410 if (font && xftdraw) {
410 XftDrawStringUtf8(xftdraw, &xft_col_fg, font, 20, sh - 20, 411 XftDrawStringUtf8(xftdraw, &xft_col_fg, font, 20, sh - 20,
411 (FcChar8*)status, strlen(status)); 412 (FcChar8*)status, strlen(status));
412 } 413 }
413 } 414 }
414 415
415 XFlush(dpy); 416 XFlush(dpy);
416 } 417 }
417 418
418 static void enter_overlay(const Arg *arg) { 419 static void enter_overlay(const Arg *arg) {
419 if (!focused) return; 420 if (!focused) return;
420 421
421 overlay_mode = 1; 422 overlay_mode = 1;
422 memset(overlay_input, 0, sizeof(overlay_input)); 423 memset(overlay_input, 0, sizeof(overlay_input));
423 424
424 if (!overlay_win) { 425 if (!overlay_win) {
425 XSetWindowAttributes wa = { 426 XSetWindowAttributes wa = {
426 .override_redirect = True, 427 .override_redirect = True,
427 .background_pixel = xft_col_bg.pixel, 428 .background_pixel = xft_col_bg.pixel,
428 .event_mask = ExposureMask | KeyPressMask 429 .event_mask = ExposureMask | KeyPressMask
429 }; 430 };
430 overlay_win = XCreateWindow(dpy, root, 0, 0, sw, sh, 0, 431 overlay_win = XCreateWindow(dpy, root, 0, 0, sw, sh, 0,
431 CopyFromParent, InputOutput, CopyFromParent, 432 CopyFromParent, InputOutput, CopyFromParent,
432 CWOverrideRedirect | CWBackPixel | CWEventMask, &wa); 433 CWOverrideRedirect | CWBackPixel | CWEventMask, &wa);
433 434
434 gc = XCreateGC(dpy, overlay_win, 0, NULL); 435 gc = XCreateGC(dpy, overlay_win, 0, NULL);
435 436
436 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); 437 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
437 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy)); 438 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
438 xftdraw = XftDrawCreate(dpy, overlay_win, visual, cmap); 439 xftdraw = XftDrawCreate(dpy, overlay_win, visual, cmap);
439 440
440 unsigned long opacity = (unsigned long)(0.85 * 0xffffffff); 441 unsigned long opacity = (unsigned long)(0.85 * 0xffffffff);
441 Atom atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); 442 Atom atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
442 XChangeProperty(dpy, overlay_win, atom, XA_CARDINAL, 32, 443 XChangeProperty(dpy, overlay_win, atom, XA_CARDINAL, 32,
443 PropModeReplace, (unsigned char *)&opacity, 1); 444 PropModeReplace, (unsigned char *)&opacity, 1);
444 } 445 }
445 446
446 XMapRaised(dpy, overlay_win); 447 XMapRaised(dpy, overlay_win);
447 XSetInputFocus(dpy, overlay_win, RevertToPointerRoot, CurrentTime); 448 XSetInputFocus(dpy, overlay_win, RevertToPointerRoot, CurrentTime);
448 draw_overlay(); 449 draw_overlay();
449 } 450 }
450 451
451 static void hide_overlay(void) { 452 static void hide_overlay(void) {
452 overlay_mode = 0; 453 overlay_mode = 0;
453 memset(overlay_input, 0, sizeof(overlay_input)); 454 memset(overlay_input, 0, sizeof(overlay_input));
454 if (overlay_win) { 455 if (overlay_win) {
455 XUnmapWindow(dpy, overlay_win); 456 XUnmapWindow(dpy, overlay_win);
456 } 457 }
457 if (focused) { 458 if (focused) {
458 XSetInputFocus(dpy, focused->win, RevertToPointerRoot, CurrentTime); 459 XSetInputFocus(dpy, focused->win, RevertToPointerRoot, CurrentTime);
459 } 460 }
460 } 461 }
461 462
462 static void process_overlay_input(void) { 463 static void process_overlay_input(void) {
463 if (!focused || overlay_input[0] == 0 || overlay_input[1] == 0) return; 464 if (!focused || overlay_input[0] == 0 || overlay_input[1] == 0) return;
464 465
465 int r1 = -1, c1 = -1, r2 = -1, c2 = -1; 466 int r1 = -1, c1 = -1, r2 = -1, c2 = -1;
466 for (int r = 0; r < GRID_ROWS; r++) { 467 for (int r = 0; r < GRID_ROWS; r++) {
467 for (int c = 0; c < GRID_COLS; c++) { 468 for (int c = 0; c < GRID_COLS; c++) {
468 if (grid_chars[r][c] == overlay_input[0]) { r1 = r; c1 = c; } 469 if (grid_chars[r][c] == overlay_input[0]) { r1 = r; c1 = c; }
469 if (grid_chars[r][c] == overlay_input[1]) { r2 = r; c2 = c; } 470 if (grid_chars[r][c] == overlay_input[1]) { r2 = r; c2 = c; }
470 } 471 }
471 } 472 }
472 if (r1 == -1 || r2 == -1) return; 473 if (r1 == -1 || r2 == -1) return;
473 474
474 if (r1 > r2) { int t = r1; r1 = r2; r2 = t; } 475 if (r1 > r2) { int t = r1; r1 = r2; r2 = t; }
475 if (c1 > c2) { int t = c1; c1 = c2; c2 = t; } 476 if (c1 > c2) { int t = c1; c1 = c2; c2 = t; }
476 477
477 int cols_span = c2 - c1 + 1; 478 int cols_span = c2 - c1 + 1;
478 int rows_span = r2 - r1 + 1; 479 int rows_span = r2 - r1 + 1;
479 480
480 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS; 481 int cell_w = (sw - padding * (GRID_COLS + 1)) / GRID_COLS;
481 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS; 482 int cell_h = (sh - padding * (GRID_ROWS + 1)) / GRID_ROWS;
482 483
483 int x = padding + c1 * (cell_w + padding); 484 int x = padding + c1 * (cell_w + padding);
484 int y = padding + r1 * (cell_h + padding); 485 int y = padding + r1 * (cell_h + padding);
485 int w = cols_span * cell_w + (cols_span - 1) * padding; 486 int w = cols_span * cell_w + (cols_span - 1) * padding;
486 int h = rows_span * cell_h + (rows_span - 1) * padding; 487 int h = rows_span * cell_h + (rows_span - 1) * padding;
487 488
488 resize(focused, x, y, w, h); 489 resize(focused, x, y, w, h);
489 } 490 }
490 491
491 // Workspace functions 492 // Workspace functions
492 static void switchws(const Arg *arg) { 493 static void switchws(const Arg *arg) {
493 int ws = arg->i; 494 int ws = arg->i;
494 if (ws < 0 || ws >= 9 || ws == current_ws) return; 495 if (ws < 0 || ws >= 9 || ws == current_ws) return;
495 496
496 current_ws = ws; 497 current_ws = ws;
497 498
498 // Hide all windows from all workspaces 499 // Hide all windows from all workspaces
499 for (int i = 0; i < 9; i++) { 500 for (int i = 0; i < 9; i++) {
500 for (Client *c = workspaces[i]; c; c = c->next) { 501 for (Client *c = workspaces[i]; c; c = c->next) {
501 XUnmapWindow(dpy, c->win); 502 XUnmapWindow(dpy, c->win);
502 } 503 }
503 } 504 }
504 505
505 // Show current workspace windows 506 // Show current workspace windows
506 for (Client *c = workspaces[current_ws]; c; c = c->next) { 507 for (Client *c = workspaces[current_ws]; c; c = c->next) {
507 XMapWindow(dpy, c->win); 508 XMapWindow(dpy, c->win);
508 } 509 }
509 510
510 focused = workspaces[current_ws]; 511 focused = workspaces[current_ws];
511 if (focused) focus(focused); 512 if (focused) focus(focused);
512 arrange(); 513 arrange();
513 } 514 }
514 515
515 static void movewin_to_ws(const Arg *arg) { 516 static void movewin_to_ws(const Arg *arg) {
516 int ws = arg->i; 517 int ws = arg->i;
517 if (!focused || ws < 0 || ws >= 9 || ws == current_ws) return; 518 if (!focused || ws < 0 || ws >= 9 || ws == current_ws) return;
518 519
519 Client *moving = focused; 520 Client *moving = focused;
520 521
521 // Remove from current workspace 522 // Remove from current workspace
522 Client **prev; 523 Client **prev;
523 for (prev = &workspaces[current_ws]; *prev; prev = &(*prev)->next) { 524 for (prev = &workspaces[current_ws]; *prev; prev = &(*prev)->next) {
524 if (*prev == moving) { 525 if (*prev == moving) {
525 *prev = moving->next; 526 *prev = moving->next;
526 break; 527 break;
527 } 528 }
528 } 529 }
529 530
530 // Add to target workspace 531 // Add to target workspace
531 moving->workspace = ws; 532 moving->workspace = ws;
532 moving->next = workspaces[ws]; 533 moving->next = workspaces[ws];
533 workspaces[ws] = moving; 534 workspaces[ws] = moving;
534 moving->isfullscreen = 0; // Reset fullscreen state 535 moving->isfullscreen = 0; // Reset fullscreen state
535 536
536 // Hide the window we just moved 537 // Hide the window we just moved
537 XUnmapWindow(dpy, moving->win); 538 XUnmapWindow(dpy, moving->win);
538 539
539 // Update focus to next available window in current workspace 540 // Update focus to next available window in current workspace
540 focused = workspaces[current_ws]; 541 focused = workspaces[current_ws];
541 if (focused) { 542 if (focused) {
542 focus(focused); 543 focus(focused);
543 } else { 544 } else {
544 focused = NULL; 545 focused = NULL;
545 } 546 }
546 547
547 // Re-arrange current workspace 548 // Re-arrange current workspace
548 arrange(); 549 arrange();
549 } 550 }
550 551
551 // Action functions 552 // Action functions
552 static int sendevent(Client *c, Atom proto) { 553 static int sendevent(Client *c, Atom proto) {
553 int n; 554 int n;
554 Atom *protocols; 555 Atom *protocols;
555 int exists = 0; 556 int exists = 0;
556 XEvent ev; 557 XEvent ev;
557 558
558 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 559 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
559 while (!exists && n--) 560 while (!exists && n--)
560 exists = protocols[n] == proto; 561 exists = protocols[n] == proto;
561 XFree(protocols); 562 XFree(protocols);
562 } 563 }
563 if (exists) { 564 if (exists) {
564 ev.type = ClientMessage; 565 ev.type = ClientMessage;
565 ev.xclient.window = c->win; 566 ev.xclient.window = c->win;
566 ev.xclient.message_type = wm_protocols; 567 ev.xclient.message_type = wm_protocols;
567 ev.xclient.format = 32; 568 ev.xclient.format = 32;
568 ev.xclient.data.l[0] = proto; 569 ev.xclient.data.l[0] = proto;
569 ev.xclient.data.l[1] = CurrentTime; 570 ev.xclient.data.l[1] = CurrentTime;
570 XSendEvent(dpy, c->win, False, NoEventMask, &ev); 571 XSendEvent(dpy, c->win, False, NoEventMask, &ev);
571 } 572 }
572 return exists; 573 return exists;
573 } 574 }
574 575
575 static void killclient(const Arg *arg) { 576 static void killclient(const Arg *arg) {
576 if (!focused) return; 577 if (!focused) return;
577 if (!sendevent(focused, wm_delete_window)) { 578 if (!sendevent(focused, wm_delete_window)) {
578 XGrabServer(dpy); 579 XGrabServer(dpy);
579 XSetCloseDownMode(dpy, DestroyAll); 580 XSetCloseDownMode(dpy, DestroyAll);
580 XKillClient(dpy, focused->win); 581 XKillClient(dpy, focused->win);
581 XSync(dpy, False); 582 XSync(dpy, False);
582 XUngrabServer(dpy); 583 XUngrabServer(dpy);
583 } 584 }
584 } 585 }
585 586
586 static void setfullscreen(Client *c, int fullscreen) { 587 static void setfullscreen(Client *c, int fullscreen) {
587 if (!c) return; 588 if (!c) return;
588 589
589 if (fullscreen && !c->isfullscreen) { 590 if (fullscreen && !c->isfullscreen) {
590 // Save current position before going fullscreen 591 // Save current position before going fullscreen
591 c->saved_x = c->x; 592 c->saved_x = c->x;
592 c->saved_y = c->y; 593 c->saved_y = c->y;
593 c->saved_w = c->w; 594 c->saved_w = c->w;
594 c->saved_h = c->h; 595 c->saved_h = c->h;
595 596
596 c->isfullscreen = 1; 597 c->isfullscreen = 1;
597 598
598 // Remove border and set to full screen 599 // Remove border and set to full screen
599 XSetWindowBorderWidth(dpy, c->win, 0); 600 XSetWindowBorderWidth(dpy, c->win, 0);
600 resize(c, 0, 0, sw, sh); 601 resize(c, 0, 0, sw, sh);
601 XRaiseWindow(dpy, c->win); 602 XRaiseWindow(dpy, c->win);
602 603
603 } else if (!fullscreen && c->isfullscreen) { 604 } else if (!fullscreen && c->isfullscreen) {
604 // Restore saved position 605 // Restore saved position
605 c->isfullscreen = 0; 606 c->isfullscreen = 0;
606 607
607 // Restore border 608 // Restore border
608 XSetWindowBorderWidth(dpy, c->win, border_width); 609 XSetWindowBorderWidth(dpy, c->win, border_width);
609 610
610 // Restore original position 611 // Restore original position
611 resize(c, c->saved_x, c->saved_y, c->saved_w, c->saved_h); 612 resize(c, c->saved_x, c->saved_y, c->saved_w, c->saved_h);
612 } 613 }
613 614
614 // Update _NET_WM_STATE 615 // Update _NET_WM_STATE
615 XEvent ev; 616 XEvent ev;
616 ev.type = ClientMessage; 617 ev.type = ClientMessage;
617 ev.xclient.window = c->win; 618 ev.xclient.window = c->win;
618 ev.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False); 619 ev.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False);
619 ev.xclient.format = 32; 620 ev.xclient.format = 32;
620 ev.xclient.data.l[0] = fullscreen ? 1 : 0; 621 ev.xclient.data.l[0] = fullscreen ? 1 : 0;
621 ev.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 622 ev.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
622 ev.xclient.data.l[2] = 0; 623 ev.xclient.data.l[2] = 0;
623 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, &ev); 624 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
624 } 625 }
625 626
626 static void toggle_fullscreen(const Arg *arg) { 627 static void toggle_fullscreen(const Arg *arg) {
627 if (!focused) return; 628 if (!focused) return;
628 setfullscreen(focused, !focused->isfullscreen); 629 setfullscreen(focused, !focused->isfullscreen);
629 } 630 }
630 631
631 static void spawn(const Arg *arg) { 632 static void spawn(const Arg *arg) {
632 if (fork() == 0) { 633 if (fork() == 0) {
633 if (dpy) 634 if (dpy)
634 close(ConnectionNumber(dpy)); 635 close(ConnectionNumber(dpy));
635 setsid(); 636 setsid();
636 execvp(((char **)arg->v)[0], (char **)arg->v); 637 execvp(((char **)arg->v)[0], (char **)arg->v);
637 fprintf(stderr, "eowm: execvp %s failed\n", ((char **)arg->v)[0]); 638 fprintf(stderr, "eowm: execvp %s failed\n", ((char **)arg->v)[0]);
638 exit(1); 639 exit(1);
639 } 640 }
640 } 641 }
641 642
642 static void quit(const Arg *arg) { 643 static void quit(const Arg *arg) {
643 exit(0); 644 exit(0);
644 } 645 }
645 646
646 static void cycle_focus(const Arg *arg) { 647 static void cycle_focus(const Arg *arg) {
647 if (!workspaces[current_ws]) return; 648 if (!workspaces[current_ws]) return;
648 649
649 if (!focused) { 650 if (!focused) {
650 focus(workspaces[current_ws]); 651 focus(workspaces[current_ws]);
651 return; 652 return;
652 } 653 }
653 654
654 Client *next = focused->next; 655 Client *next = focused->next;
655 if (!next) next = workspaces[current_ws]; 656 if (!next) next = workspaces[current_ws];
656 657
657 focus(next); 658 focus(next);
658 } 659 }
659 660
660 static void grabkeys(void) { 661 static void grabkeys(void) {
661 XUngrabKey(dpy, AnyKey, AnyModifier, root); 662 XUngrabKey(dpy, AnyKey, AnyModifier, root);
662 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) { 663 for (unsigned int i = 0; i < sizeof(keys) / sizeof(Key); i++) {
663 KeyCode code = XKeysymToKeycode(dpy, keys[i].keysym); 664 KeyCode code = XKeysymToKeycode(dpy, keys[i].keysym);
664 if (code) { 665 if (code) {
665 XGrabKey(dpy, code, keys[i].mod, root, True, 666 XGrabKey(dpy, code, keys[i].mod, root, True,
666 GrabModeAsync, GrabModeAsync); 667 GrabModeAsync, GrabModeAsync);
667 XGrabKey(dpy, code, keys[i].mod | Mod2Mask, root, True, 668 XGrabKey(dpy, code, keys[i].mod | Mod2Mask, root, True,
668 GrabModeAsync, GrabModeAsync); 669 GrabModeAsync, GrabModeAsync);
669 } 670 }
670 } 671 }
671 } 672 }
672 673
673 static void sigchld(int s) { 674 static void sigchld(int s) {
674 (void)s; 675 (void)s;
675 while (waitpid(-1, NULL, WNOHANG) > 0); 676 while (waitpid(-1, NULL, WNOHANG) > 0);
676 } 677 }
677 678
678 int xerror_handler(Display *dpy, XErrorEvent *ee) { 679 int xerror_handler(Display *dpy, XErrorEvent *ee) {
679 return 0; 680 return 0;
680 } 681 }
681 682
682 static void setup_colors(void) { 683 static void setup_colors(void) {
683 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); 684 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
684 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy)); 685 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
685 686
686 XftColorAllocName(dpy, visual, cmap, col_bg, &xft_col_bg); 687 XftColorAllocName(dpy, visual, cmap, col_bg, &xft_col_bg);
687 XftColorAllocName(dpy, visual, cmap, col_fg, &xft_col_fg); 688 XftColorAllocName(dpy, visual, cmap, col_fg, &xft_col_fg);
688 XftColorAllocName(dpy, visual, cmap, col_sel, &xft_col_sel); 689 XftColorAllocName(dpy, visual, cmap, col_sel, &xft_col_sel);
689 690
690 font = XftFontOpenName(dpy, DefaultScreen(dpy), overlay_font); 691 font = XftFontOpenName(dpy, DefaultScreen(dpy), overlay_font);
691 692
692 // Allocate border colors 693 // Allocate border colors
693 XColor color; 694 XColor color;
694 XParseColor(dpy, cmap, col_border_normal, &color); 695 XParseColor(dpy, cmap, col_border_normal, &color);
695 XAllocColor(dpy, cmap, &color); 696 XAllocColor(dpy, cmap, &color);
696 border_normal = color.pixel; 697 border_normal = color.pixel;
697 698
698 XParseColor(dpy, cmap, col_border_focused, &color); 699 XParseColor(dpy, cmap, col_border_focused, &color);
699 XAllocColor(dpy, cmap, &color); 700 XAllocColor(dpy, cmap, &color);
700 border_focused = color.pixel; 701 border_focused = color.pixel;
701 } 702 }
702 703
703 static void setup_icccm(void) { 704 static void setup_icccm(void) {
704 wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); 705 wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
705 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 706 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
706 wm_state = XInternAtom(dpy, "WM_STATE", False); 707 wm_state = XInternAtom(dpy, "WM_STATE", False);
707 wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 708 wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
708 } 709 }
709 710
710 static void setrootbackground(void) { 711 static void setrootbackground(void) {
711 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy)); 712 Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
712 XColor color; 713 XColor color;
713 714
714 if (XParseColor(dpy, cmap, root_bg, &color) && 715 if (XParseColor(dpy, cmap, root_bg, &color) &&
715 XAllocColor(dpy, cmap, &color)) { 716 XAllocColor(dpy, cmap, &color)) {
716 XSetWindowBackground(dpy, root, color.pixel); 717 XSetWindowBackground(dpy, root, color.pixel);
717 XClearWindow(dpy, root); 718 XClearWindow(dpy, root);
718 } 719 }
719 } 720 }
720 721
721 int main(void) { 722 void die(const char *fmt, ...) {
722 signal(SIGCHLD, sigchld); 723 va_list ap;
723 if (!(dpy = XOpenDisplay(NULL))) { 724 va_start(ap, fmt);
724 fprintf(stderr, "eowm: cannot open display\n"); 725 vfprintf(stderr, fmt, ap);
725 exit(1); 726 va_end(ap);
726 } 727 fputc('\n', stderr);
727 XSetErrorHandler(xerror_handler); 728 exit(1);
728 729 }
729 sw = DisplayWidth(dpy, DefaultScreen(dpy)); 730
730 sh = DisplayHeight(dpy, DefaultScreen(dpy)); 731 int main(int argc, char *argv[]) {
731 root = RootWindow(dpy, DefaultScreen(dpy)); 732 if (argc == 2 && !strcmp("-v", argv[1]))
732 Cursor cursor = XCreateFontCursor(dpy, XC_left_ptr); 733 die("gbwm v"VERSION);
733 XDefineCursor(dpy, root, cursor); 734 else if (argc != 1)
734 735 die("Usage: gbwm [-v]");
735 setup_colors(); 736 if (!getenv("DISPLAY"))
736 setrootbackground(); 737 die("DISPLAY environment variable not set");
737 setup_icccm(); 738 if (!(dpy = XOpenDisplay(NULL)))
738 739 die("cannot open X11 display (is X running?)");
739 XSelectInput(dpy, root, 740
740 SubstructureRedirectMask | SubstructureNotifyMask | 741 signal(SIGCHLD, sigchld);
741 EnterWindowMask | LeaveWindowMask | FocusChangeMask); 742 if (!(dpy = XOpenDisplay(NULL))) {
742 743 fprintf(stderr, "eowm: cannot open display\n");
743 grabkeys(); 744 exit(1);
744 745 }
745 XEvent ev; 746 XSetErrorHandler(xerror_handler);
746 while (1) { 747
747 XNextEvent(dpy, &ev); 748 sw = DisplayWidth(dpy, DefaultScreen(dpy));
748 switch (ev.type) { 749 sh = DisplayHeight(dpy, DefaultScreen(dpy));
749 case ButtonPress: buttonpress(&ev); break; 750 root = RootWindow(dpy, DefaultScreen(dpy));
750 case ClientMessage: clientmessage(&ev); break; 751 Cursor cursor = XCreateFontCursor(dpy, XC_left_ptr);
751 case MapRequest: maprequest(&ev); break; 752 XDefineCursor(dpy, root, cursor);
752 case UnmapNotify: unmapnotify(&ev); break; 753
753 case DestroyNotify: destroynotify(&ev); break; 754 setup_colors();
754 case EnterNotify: enternotify(&ev); break; 755 setrootbackground();
755 case KeyPress: keypress(&ev); break; 756 setup_icccm();
756 case Expose: expose(&ev); break; 757
757 } 758 XSelectInput(dpy, root,
758 } 759 SubstructureRedirectMask | SubstructureNotifyMask |
759 760 EnterWindowMask | LeaveWindowMask | FocusChangeMask);
760 XCloseDisplay(dpy); 761
761 return 0; 762 grabkeys();
762 } 763
764 XEvent ev;
765 while (1) {
766 XNextEvent(dpy, &ev);
767 switch (ev.type) {
768 case ButtonPress: buttonpress(&ev); break;
769 case ClientMessage: clientmessage(&ev); break;
770 case MapRequest: maprequest(&ev); break;
771 case UnmapNotify: unmapnotify(&ev); break;
772 case DestroyNotify: destroynotify(&ev); break;
773 case EnterNotify: enternotify(&ev); break;
774 case KeyPress: keypress(&ev); break;
775 case Expose: expose(&ev); break;
776 }
777 }
778
779 XCloseDisplay(dpy);
780 return 0;
781 }