| Hash | Commit message | Author | Date | Files | + | - |
1 | commit a84c91b50526654f64c83bef6b9453917518e444 |
2 | Author: Connor Etherington <[email protected]> |
3 | Date: Thu Dec 22 00:48:34 2022 +0200 |
4 | |
5 | Auto-Commit Update 22.12.2022 - 00:48:34 |
6 | --- |
7 | AutoLinux-DWM.desktop | 6 + |
8 | AutoLinux.c | 2951 +++++++++++++++++++++++++++++++++++++++++++++++++ |
9 | LICENSE | 39 + |
10 | Makefile | 49 + |
11 | PKGBUILD | 29 + |
12 | config.h | 417 +++++++ |
13 | config.mk | 39 + |
14 | drw.c | 424 +++++++ |
15 | drw.h | 57 + |
16 | patch/bar_ltsymbol.c | 17 + |
17 | patch/bar_ltsymbol.h | 3 + |
18 | patch/bar_status.c | 19 + |
19 | patch/bar_status.h | 3 + |
20 | patch/bar_tags.c | 55 + |
21 | patch/bar_tags.h | 3 + |
22 | patch/bar_wintitle.c | 31 + |
23 | patch/bar_wintitle.h | 3 + |
24 | patch/include.c | 5 + |
25 | patch/include.h | 5 + |
26 | shiftview.c | 64 ++ |
27 | transient.c | 42 + |
28 | util.c | 35 + |
29 | util.h | 8 + |
30 | vanitygaps.c | 542 +++++++++ |
31 | 24 files changed, 4846 insertions(+) |
32 | |
33 | diff --git a/AutoLinux-DWM.desktop b/AutoLinux-DWM.desktop |
34 | new file mode 100644 |
35 | index 0000000..7a498cc |
36 | --- /dev/null |
37 | +++ b/AutoLinux-DWM.desktop |
38 | @@ -0,0 +1,6 @@ |
39 | +[Desktop Entry] |
40 | +Name=AutoLinux-AutoLinux |
41 | +Comment=Dynamic Window Manager |
42 | +Exec=AutoLinux |
43 | +Icon=AutoLinux |
44 | +Type=Application |
45 | diff --git a/AutoLinux.c b/AutoLinux.c |
46 | new file mode 100644 |
47 | index 0000000..da95f4a |
48 | --- /dev/null |
49 | +++ b/AutoLinux.c |
50 | @@ -0,0 +1,2951 @@ |
51 | +/* See LICENSE file for copyright and license details. |
52 | + * |
53 | + * dynamic window manager is designed like any other X client as well. It is |
54 | + * driven through handling X events. In contrast to other X clients, a window |
55 | + * manager selects for SubstructureRedirectMask on the root window, to receive |
56 | + * events about window (dis-)appearance. Only one X connection at a time is |
57 | + * allowed to select for this event mask. |
58 | + * |
59 | + * The event handlers of AutoLinux are organized in an array which is accessed |
60 | + * whenever a new event has been fetched. This allows event dispatching |
61 | + * in O(1) time. |
62 | + * |
63 | + * Each child of the root window is called a client, except windows which have |
64 | + * set the override_redirect flag. Clients are organized in a linked client |
65 | + * list on each monitor, the focus history is remembered through a stack list |
66 | + * on each monitor. Each client contains a bit array to indicate the tags of a |
67 | + * client. |
68 | + * |
69 | + * Keys and tagging rules are organized as arrays and defined in config.h. |
70 | + * |
71 | + * To understand everything else, start reading main(). |
72 | + */ |
73 | +#include <errno.h> |
74 | +#include <locale.h> |
75 | +#include <signal.h> |
76 | +#include <stdarg.h> |
77 | +#include <stdio.h> |
78 | +#include <stdlib.h> |
79 | +#include <string.h> |
80 | +#include <unistd.h> |
81 | +#include <sys/types.h> |
82 | +#include <sys/wait.h> |
83 | +#include <X11/cursorfont.h> |
84 | +#include <X11/keysym.h> |
85 | +#include <X11/Xatom.h> |
86 | +#include <X11/Xlib.h> |
87 | +#include <X11/Xproto.h> |
88 | +#include <X11/Xutil.h> |
89 | +#include <X11/Xresource.h> |
90 | +#ifdef XINERAMA |
91 | +#include <X11/extensions/Xinerama.h> |
92 | +#endif /* XINERAMA */ |
93 | +#include <X11/Xft/Xft.h> |
94 | +#include <X11/Xlib-xcb.h> |
95 | +#include <xcb/res.h> |
96 | + |
97 | +#include "drw.h" |
98 | +#include "util.h" |
99 | + |
100 | +/* macros */ |
101 | +#define BARRULES 20 |
102 | +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) |
103 | +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) |
104 | +#define GETINC(X) ((X) - 2000) |
105 | +#define INC(X) ((X) + 2000) |
106 | +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ |
107 | + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) |
108 | +#define ISINC(X) ((X) > 1000 && (X) < 3000) |
109 | +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) |
110 | +#define PREVSEL 3000 |
111 | +#define LENGTH(X) (sizeof X / sizeof X[0]) |
112 | +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) |
113 | +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) |
114 | +#define WIDTH(X) ((X)->w + 2 * (X)->bw) |
115 | +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) |
116 | +#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) |
117 | +#define TAGMASK ((1 << NUMTAGS) - 1) |
118 | +#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) |
119 | +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) |
120 | +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) |
121 | +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) |
122 | + |
123 | +/* enums */ |
124 | +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ |
125 | +enum { SchemeNorm, SchemeSel }; /* color schemes */ |
126 | +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, |
127 | + NetWMFullscreen, NetActiveWindow, NetWMWindowType, |
128 | + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ |
129 | +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ |
130 | +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, |
131 | + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ |
132 | +enum { |
133 | + BAR_ALIGN_LEFT, |
134 | + BAR_ALIGN_CENTER, |
135 | + BAR_ALIGN_RIGHT, |
136 | + BAR_ALIGN_LEFT_LEFT, |
137 | + BAR_ALIGN_LEFT_RIGHT, |
138 | + BAR_ALIGN_LEFT_CENTER, |
139 | + BAR_ALIGN_NONE, |
140 | + BAR_ALIGN_RIGHT_LEFT, |
141 | + BAR_ALIGN_RIGHT_RIGHT, |
142 | + BAR_ALIGN_RIGHT_CENTER, |
143 | + BAR_ALIGN_LAST |
144 | +}; /* bar alignment */ |
145 | + |
146 | +typedef union { |
147 | + int i; |
148 | + unsigned int ui; |
149 | + float f; |
150 | + const void *v; |
151 | +} Arg; |
152 | + |
153 | +typedef struct Monitor Monitor; |
154 | +typedef struct Bar Bar; |
155 | +struct Bar { |
156 | + Window win; |
157 | + Monitor *mon; |
158 | + Bar *next; |
159 | + int idx; |
160 | + int topbar; |
161 | + int bx, by, bw, bh; /* bar geometry */ |
162 | + int w[BARRULES]; // module width |
163 | + int x[BARRULES]; // module position |
164 | +}; |
165 | + |
166 | +typedef struct { |
167 | + int max_width; |
168 | +} BarWidthArg; |
169 | + |
170 | +typedef struct { |
171 | + int x; |
172 | + int w; |
173 | +} BarDrawArg; |
174 | + |
175 | +typedef struct { |
176 | + int rel_x; |
177 | + int rel_y; |
178 | + int rel_w; |
179 | + int rel_h; |
180 | +} BarClickArg; |
181 | + |
182 | +typedef struct { |
183 | + int monitor; |
184 | + int bar; |
185 | + int alignment; // see bar alignment enum |
186 | + int (*widthfunc)(Bar *bar, BarWidthArg *a); |
187 | + int (*drawfunc)(Bar *bar, BarDrawArg *a); |
188 | + int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); |
189 | + char *name; // for debugging |
190 | + int x, w; // position, width for internal use |
191 | +} BarRule; |
192 | + |
193 | +typedef struct { |
194 | + unsigned int click; |
195 | + unsigned int mask; |
196 | + unsigned int button; |
197 | + void (*func)(const Arg *arg); |
198 | + const Arg arg; |
199 | +} Button; |
200 | + |
201 | +typedef struct Client Client; |
202 | +struct Client { |
203 | + char name[256]; |
204 | + float mina, maxa; |
205 | + int x, y, w, h; |
206 | + int oldx, oldy, oldw, oldh; |
207 | + int basew, baseh, incw, inch, maxw, maxh, minw, minh; |
208 | + int bw, oldbw; |
209 | + unsigned int tags; |
210 | + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow, issticky; |
211 | + pid_t pid; |
212 | + Client *next; |
213 | + Client *snext; |
214 | + Client *swallowing; |
215 | + Monitor *mon; |
216 | + Window win; |
217 | +}; |
218 | + |
219 | +typedef struct { |
220 | + unsigned int mod; |
221 | + KeySym keysym; |
222 | + void (*func)(const Arg *); |
223 | + const Arg arg; |
224 | +} Key; |
225 | + |
226 | +typedef struct { |
227 | + const char *symbol; |
228 | + void (*arrange)(Monitor *); |
229 | +} Layout; |
230 | + |
231 | +struct Monitor { |
232 | + char ltsymbol[16]; |
233 | + float mfact; |
234 | + int nmaster; |
235 | + int num; |
236 | + int mx, my, mw, mh; /* screen size */ |
237 | + int wx, wy, ww, wh; /* window area */ |
238 | + int gappih; /* horizontal gap between windows */ |
239 | + int gappiv; /* vertical gap between windows */ |
240 | + int gappoh; /* horizontal outer gaps */ |
241 | + int gappov; /* vertical outer gaps */ |
242 | + unsigned int seltags; |
243 | + unsigned int sellt; |
244 | + unsigned int tagset[2]; |
245 | + int showbar; |
246 | + Client *clients; |
247 | + Client *sel; |
248 | + Client *stack; |
249 | + Monitor *next; |
250 | + Bar *bar; |
251 | + const Layout *lt[2]; |
252 | +}; |
253 | + |
254 | +typedef struct { |
255 | + const char *class; |
256 | + const char *instance; |
257 | + const char *title; |
258 | + unsigned int tags; |
259 | + int isfloating; |
260 | + int isterminal; |
261 | + int noswallow; |
262 | + int monitor; |
263 | +} Rule; |
264 | + |
265 | +/* Xresources preferences */ |
266 | +enum resource_type { |
267 | + STRING = 0, |
268 | + INTEGER = 1, |
269 | + FLOAT = 2 |
270 | +}; |
271 | + |
272 | +typedef struct { |
273 | + char *name; |
274 | + enum resource_type type; |
275 | + void *dst; |
276 | +} ResourcePref; |
277 | + |
278 | +/* function declarations */ |
279 | +static void applyrules(Client *c); |
280 | +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); |
281 | +static void arrange(Monitor *m); |
282 | +static void arrangemon(Monitor *m); |
283 | +static void attach(Client *c); |
284 | +static void attachstack(Client *c); |
285 | +static void buttonpress(XEvent *e); |
286 | +static void checkotherwm(void); |
287 | +static void cleanup(void); |
288 | +static void cleanupmon(Monitor *mon); |
289 | +static void clientmessage(XEvent *e); |
290 | +static void configure(Client *c); |
291 | +static void configurenotify(XEvent *e); |
292 | +static void configurerequest(XEvent *e); |
293 | +static void copyvalidchars(char *text, char *rawtext); |
294 | +static Monitor *createmon(void); |
295 | +static void destroynotify(XEvent *e); |
296 | +static void detach(Client *c); |
297 | +static void detachstack(Client *c); |
298 | +static Monitor *dirtomon(int dir); |
299 | +static void drawbar(Monitor *m); |
300 | +static void drawbars(void); |
301 | +static void drawbarwin(Bar *bar); |
302 | +static void enternotify(XEvent *e); |
303 | +static void expose(XEvent *e); |
304 | +static void focus(Client *c); |
305 | +static void focusin(XEvent *e); |
306 | +static void focusmon(const Arg *arg); |
307 | +static void focusstack(const Arg *arg); |
308 | +static Atom getatomprop(Client *c, Atom prop); |
309 | +static int getrootptr(int *x, int *y); |
310 | +static long getstate(Window w); |
311 | +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); |
312 | +static void grabbuttons(Client *c, int focused); |
313 | +static void grabkeys(void); |
314 | +static void incnmaster(const Arg *arg); |
315 | +static void keypress(XEvent *e); |
316 | +static void killclient(const Arg *arg); |
317 | +static void manage(Window w, XWindowAttributes *wa); |
318 | +static void mappingnotify(XEvent *e); |
319 | +static void maprequest(XEvent *e); |
320 | +static void monocle(Monitor *m); |
321 | +static void motionnotify(XEvent *e); |
322 | +static void movemouse(const Arg *arg); |
323 | +static Client *nexttiled(Client *c); |
324 | +static void pop(Client *); |
325 | +static void propertynotify(XEvent *e); |
326 | +static void pushstack(const Arg *arg); |
327 | +static void quit(const Arg *arg); |
328 | +static Monitor *recttomon(int x, int y, int w, int h); |
329 | +static void resize(Client *c, int x, int y, int w, int h, int interact); |
330 | +static void resizeclient(Client *c, int x, int y, int w, int h); |
331 | +static void resizemouse(const Arg *arg); |
332 | +static void restack(Monitor *m); |
333 | +static void run(void); |
334 | +static void runAutostart(void); |
335 | +static void scan(void); |
336 | +static void scratchpad_hide (); |
337 | +static _Bool scratchpad_last_showed_is_killed (void); |
338 | +static void scratchpad_remove (); |
339 | +static void scratchpad_show (); |
340 | +static void scratchpad_show_client (Client * c); |
341 | +static void scratchpad_show_first (void); |
342 | +static int sendevent(Client *c, Atom proto); |
343 | +static void sendmon(Client *c, Monitor *m); |
344 | +static void setclientstate(Client *c, long state); |
345 | +static void setfocus(Client *c); |
346 | +static void setfullscreen(Client *c, int fullscreen); |
347 | +static void setlayout(const Arg *arg); |
348 | +static void setmfact(const Arg *arg); |
349 | +static void setup(void); |
350 | +static void seturgent(Client *c, int urg); |
351 | +static void showhide(Client *c); |
352 | +static void sigchld(int unused); |
353 | +#ifndef __OpenBSD__ |
354 | +static int getAutoLinuxblockspid(); |
355 | +static void sigAutoLinuxblocks(const Arg *arg); |
356 | +#endif |
357 | +static void sighup(int unused); |
358 | +static void sigterm(int unused); |
359 | +static void spawn(const Arg *arg); |
360 | +static int stackpos(const Arg *arg); |
361 | +static void tag(const Arg *arg); |
362 | +static void tagmon(const Arg *arg); |
363 | +static void togglebar(const Arg *arg); |
364 | +static void togglefloating(const Arg *arg); |
365 | +static void togglescratch(const Arg *arg); |
366 | +static void togglesticky(const Arg *arg); |
367 | +static void togglefullscr(const Arg *arg); |
368 | +static void toggletag(const Arg *arg); |
369 | +static void toggleview(const Arg *arg); |
370 | +static void unfocus(Client *c, int setfocus); |
371 | +static void unmanage(Client *c, int destroyed); |
372 | +static void unmapnotify(XEvent *e); |
373 | +static void updatebarpos(Monitor *m); |
374 | +static void updatebars(void); |
375 | +static void updateclientlist(void); |
376 | +static int updategeom(void); |
377 | +static void updatenumlockmask(void); |
378 | +static void updatesizehints(Client *c); |
379 | +static void updatestatus(void); |
380 | +static void updatetitle(Client *c); |
381 | +static void updatewindowtype(Client *c); |
382 | +static void updatewmhints(Client *c); |
383 | +static void view(const Arg *arg); |
384 | +static Client *wintoclient(Window w); |
385 | +static Monitor *wintomon(Window w); |
386 | +static int xerror(Display *dpy, XErrorEvent *ee); |
387 | +static int xerrordummy(Display *dpy, XErrorEvent *ee); |
388 | +static int xerrorstart(Display *dpy, XErrorEvent *ee); |
389 | +static int leftrotatebits(const int n, const unsigned int d); |
390 | +static int rightrotatebits(const int n, const unsigned int d); |
391 | +static void rightrotview(const Arg *arg); |
392 | +static void leftrotview(const Arg *arg); |
393 | +static void zoom(const Arg *arg); |
394 | +static void load_xresources(void); |
395 | +static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst); |
396 | + |
397 | +static pid_t getparentprocess(pid_t p); |
398 | +static int isdescprocess(pid_t p, pid_t c); |
399 | +static Client *swallowingclient(Window w); |
400 | +static Client *termforwin(const Client *c); |
401 | +static pid_t winpid(Window w); |
402 | + |
403 | +#include "patch/include.h" |
404 | +/* variables */ |
405 | +static const char broken[] = "broken"; |
406 | +static char stext[256]; |
407 | +static char rawstext[256]; |
408 | +static int AutoLinuxblockssig; |
409 | +pid_t AutoLinuxblockspid = 0; |
410 | +static int screen; |
411 | +static int sw, sh; /* X display screen geometry width, height */ |
412 | +static int bh; /* bar geometry */ |
413 | +static int lrpad; /* sum of left and right padding for text */ |
414 | +static int vp; /* vertical padding for bar */ |
415 | +static int sp; /* side padding for bar */ |
416 | +static int (*xerrorxlib)(Display *, XErrorEvent *); |
417 | +static unsigned int numlockmask = 0; |
418 | +static void (*handler[LASTEvent]) (XEvent *) = { |
419 | + [ButtonPress] = buttonpress, |
420 | + [ClientMessage] = clientmessage, |
421 | + [ConfigureRequest] = configurerequest, |
422 | + [ConfigureNotify] = configurenotify, |
423 | + [DestroyNotify] = destroynotify, |
424 | + [EnterNotify] = enternotify, |
425 | + [Expose] = expose, |
426 | + [FocusIn] = focusin, |
427 | + [KeyPress] = keypress, |
428 | + [MappingNotify] = mappingnotify, |
429 | + [MapRequest] = maprequest, |
430 | + [MotionNotify] = motionnotify, |
431 | + [PropertyNotify] = propertynotify, |
432 | + [UnmapNotify] = unmapnotify |
433 | +}; |
434 | +static Atom wmatom[WMLast], netatom[NetLast]; |
435 | +static int restart = 0; |
436 | +static int running = 1; |
437 | +static Cur *cursor[CurLast]; |
438 | +static Clr **scheme; |
439 | +static Display *dpy; |
440 | +static Drw *drw; |
441 | +static Monitor *mons, *selmon; |
442 | +static Window root, wmcheckwin; |
443 | + |
444 | +/* scratchpad */ |
445 | +# define SCRATCHPAD_MASK (1u << sizeof tags / sizeof * tags) |
446 | +static Client * scratchpad_last_showed = NULL; |
447 | + |
448 | +static xcb_connection_t *xcon; |
449 | + |
450 | +/* configuration, allows nested code to access above variables */ |
451 | +#include "config.h" |
452 | + |
453 | +#include "patch/include.c" |
454 | + |
455 | +/* compile-time check if all tags fit into an unsigned int bit array. */ |
456 | +struct NumTags { char limitexceeded[LENGTH(tags) > 30 ? -1 : 1]; }; |
457 | + |
458 | +/* function implementations */ |
459 | +void |
460 | +applyrules(Client *c) |
461 | +{ |
462 | + const char *class, *instance; |
463 | + unsigned int i; |
464 | + const Rule *r; |
465 | + Monitor *m; |
466 | + XClassHint ch = { NULL, NULL }; |
467 | + |
468 | + /* rule matching */ |
469 | + c->isfloating = 0; |
470 | + c->tags = 0; |
471 | + XGetClassHint(dpy, c->win, &ch); |
472 | + class = ch.res_class ? ch.res_class : broken; |
473 | + instance = ch.res_name ? ch.res_name : broken; |
474 | + |
475 | + for (i = 0; i < LENGTH(rules); i++) { |
476 | + r = &rules[i]; |
477 | + if ((!r->title || strstr(c->name, r->title)) |
478 | + && (!r->class || strstr(class, r->class)) |
479 | + && (!r->instance || strstr(instance, r->instance))) |
480 | + { |
481 | + c->isterminal = r->isterminal; |
482 | + c->isfloating = r->isfloating; |
483 | + c->noswallow = r->noswallow; |
484 | + c->tags |= r->tags; |
485 | + if ((r->tags & SPTAGMASK) && r->isfloating) { |
486 | + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); |
487 | + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); |
488 | + } |
489 | + |
490 | + for (m = mons; m && m->num != r->monitor; m = m->next); |
491 | + if (m) |
492 | + c->mon = m; |
493 | + } |
494 | + } |
495 | + if (ch.res_class) |
496 | + XFree(ch.res_class); |
497 | + if (ch.res_name) |
498 | + XFree(ch.res_name); |
499 | + if (c->tags != SCRATCHPAD_MASK) |
500 | + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; |
501 | +} |
502 | + |
503 | +int |
504 | +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) |
505 | +{ |
506 | + int baseismin; |
507 | + Monitor *m = c->mon; |
508 | + |
509 | + /* set minimum possible */ |
510 | + *w = MAX(1, *w); |
511 | + *h = MAX(1, *h); |
512 | + if (interact) { |
513 | + if (*x > sw) |
514 | + *x = sw - WIDTH(c); |
515 | + if (*y > sh) |
516 | + *y = sh - HEIGHT(c); |
517 | + if (*x + *w + 2 * c->bw < 0) |
518 | + *x = 0; |
519 | + if (*y + *h + 2 * c->bw < 0) |
520 | + *y = 0; |
521 | + } else { |
522 | + if (*x >= m->wx + m->ww) |
523 | + *x = m->wx + m->ww - WIDTH(c); |
524 | + if (*y >= m->wy + m->wh) |
525 | + *y = m->wy + m->wh - HEIGHT(c); |
526 | + if (*x + *w + 2 * c->bw <= m->wx) |
527 | + *x = m->wx; |
528 | + if (*y + *h + 2 * c->bw <= m->wy) |
529 | + *y = m->wy; |
530 | + } |
531 | + if (*h < bh) |
532 | + *h = bh; |
533 | + if (*w < bh) |
534 | + *w = bh; |
535 | + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { |
536 | + /* see last two sentences in ICCCM 4.1.2.3 */ |
537 | + baseismin = c->basew == c->minw && c->baseh == c->minh; |
538 | + if (!baseismin) { /* temporarily remove base dimensions */ |
539 | + *w -= c->basew; |
540 | + *h -= c->baseh; |
541 | + } |
542 | + /* adjust for aspect limits */ |
543 | + if (c->mina > 0 && c->maxa > 0) { |
544 | + if (c->maxa < (float)*w / *h) |
545 | + *w = *h * c->maxa + 0.5; |
546 | + else if (c->mina < (float)*h / *w) |
547 | + *h = *w * c->mina + 0.5; |
548 | + } |
549 | + if (baseismin) { /* increment calculation requires this */ |
550 | + *w -= c->basew; |
551 | + *h -= c->baseh; |
552 | + } |
553 | + /* adjust for increment value */ |
554 | + if (c->incw) |
555 | + *w -= *w % c->incw; |
556 | + if (c->inch) |
557 | + *h -= *h % c->inch; |
558 | + /* restore base dimensions */ |
559 | + *w = MAX(*w + c->basew, c->minw); |
560 | + *h = MAX(*h + c->baseh, c->minh); |
561 | + if (c->maxw) |
562 | + *w = MIN(*w, c->maxw); |
563 | + if (c->maxh) |
564 | + *h = MIN(*h, c->maxh); |
565 | + } |
566 | + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; |
567 | +} |
568 | + |
569 | +void |
570 | +arrange(Monitor *m) |
571 | +{ |
572 | + if (m) |
573 | + showhide(m->stack); |
574 | + else for (m = mons; m; m = m->next) |
575 | + showhide(m->stack); |
576 | + if (m) { |
577 | + arrangemon(m); |
578 | + restack(m); |
579 | + } else for (m = mons; m; m = m->next) |
580 | + arrangemon(m); |
581 | +} |
582 | + |
583 | +void |
584 | +arrangemon(Monitor *m) |
585 | +{ |
586 | + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); |
587 | + if (m->lt[m->sellt]->arrange) |
588 | + m->lt[m->sellt]->arrange(m); |
589 | +} |
590 | + |
591 | +void |
592 | +attach(Client *c) |
593 | +{ |
594 | + c->next = c->mon->clients; |
595 | + c->mon->clients = c; |
596 | +} |
597 | + |
598 | +void |
599 | +attachstack(Client *c) |
600 | +{ |
601 | + c->snext = c->mon->stack; |
602 | + c->mon->stack = c; |
603 | +} |
604 | + |
605 | +void |
606 | +swallow(Client *p, Client *c) |
607 | +{ |
608 | + if (c->noswallow || c->isterminal) |
609 | + return; |
610 | + if (!swallowfloating && c->isfloating) |
611 | + return; |
612 | + |
613 | + detach(c); |
614 | + detachstack(c); |
615 | + |
616 | + setclientstate(c, WithdrawnState); |
617 | + XUnmapWindow(dpy, p->win); |
618 | + |
619 | + p->swallowing = c; |
620 | + c->mon = p->mon; |
621 | + |
622 | + Window w = p->win; |
623 | + p->win = c->win; |
624 | + c->win = w; |
625 | + updatetitle(p); |
626 | + |
627 | + XWindowChanges wc; |
628 | + wc.border_width = p->bw; |
629 | + XConfigureWindow(dpy, p->win, CWBorderWidth, &wc); |
630 | + XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); |
631 | + XSetWindowBorder(dpy, p->win, scheme[SchemeNorm][ColBorder].pixel); |
632 | + |
633 | + arrange(p->mon); |
634 | + configure(p); |
635 | + updateclientlist(); |
636 | +} |
637 | + |
638 | +void |
639 | +unswallow(Client *c) |
640 | +{ |
641 | + c->win = c->swallowing->win; |
642 | + |
643 | + free(c->swallowing); |
644 | + c->swallowing = NULL; |
645 | + |
646 | + /* unfullscreen the client */ |
647 | + setfullscreen(c, 0); |
648 | + updatetitle(c); |
649 | + arrange(c->mon); |
650 | + XMapWindow(dpy, c->win); |
651 | + |
652 | + XWindowChanges wc; |
653 | + wc.border_width = c->bw; |
654 | + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); |
655 | + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
656 | + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); |
657 | + |
658 | + setclientstate(c, NormalState); |
659 | + focus(NULL); |
660 | + arrange(c->mon); |
661 | +} |
662 | + |
663 | +void |
664 | +buttonpress(XEvent *e) |
665 | +{ |
666 | + int click, i, r, mi; |
667 | + Arg arg = {0}; |
668 | + Client *c; |
669 | + Monitor *m; |
670 | + Bar *bar; |
671 | + XButtonPressedEvent *ev = &e->xbutton; |
672 | + const BarRule *br; |
673 | + BarClickArg carg = { 0, 0, 0, 0 }; |
674 | + |
675 | + click = ClkRootWin; |
676 | + /* focus monitor if necessary */ |
677 | + if ((m = wintomon(ev->window)) && m != selmon |
678 | + ) { |
679 | + unfocus(selmon->sel, 1); |
680 | + selmon = m; |
681 | + focus(NULL); |
682 | + } |
683 | + |
684 | + for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index |
685 | + for (bar = selmon->bar; bar; bar = bar->next) { |
686 | + if (ev->window == bar->win) { |
687 | + for (r = 0; r < LENGTH(barrules); r++) { |
688 | + br = &barrules[r]; |
689 | + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) |
690 | + continue; |
691 | + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) |
692 | + continue; |
693 | + if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { |
694 | + carg.rel_x = ev->x - bar->x[r]; |
695 | + carg.rel_y = ev->y; |
696 | + carg.rel_w = bar->w[r]; |
697 | + carg.rel_h = bar->bh; |
698 | + click = br->clickfunc(bar, &arg, &carg); |
699 | + if (click < 0) |
700 | + return; |
701 | + break; |
702 | + } |
703 | + } |
704 | + break; |
705 | + } |
706 | + } |
707 | + |
708 | + if (click == ClkRootWin && (c = wintoclient(ev->window))) { |
709 | + focus(c); |
710 | + restack(selmon); |
711 | + XAllowEvents(dpy, ReplayPointer, CurrentTime); |
712 | + click = ClkClientWin; |
713 | + } |
714 | + |
715 | + for (i = 0; i < LENGTH(buttons); i++) { |
716 | + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button |
717 | + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { |
718 | + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); |
719 | + } |
720 | + } |
721 | +} |
722 | + |
723 | +void |
724 | +checkotherwm(void) |
725 | +{ |
726 | + xerrorxlib = XSetErrorHandler(xerrorstart); |
727 | + /* this causes an error if some other window manager is running */ |
728 | + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); |
729 | + XSync(dpy, False); |
730 | + XSetErrorHandler(xerror); |
731 | + XSync(dpy, False); |
732 | +} |
733 | + |
734 | +void |
735 | +cleanup(void) |
736 | +{ |
737 | + Arg a = {.ui = ~0}; |
738 | + Layout foo = { "", NULL }; |
739 | + Monitor *m; |
740 | + size_t i; |
741 | + |
742 | + view(&a); |
743 | + selmon->lt[selmon->sellt] = &foo; |
744 | + for (m = mons; m; m = m->next) |
745 | + while (m->stack) |
746 | + unmanage(m->stack, 0); |
747 | + XUngrabKey(dpy, AnyKey, AnyModifier, root); |
748 | + while (mons) |
749 | + cleanupmon(mons); |
750 | + for (i = 0; i < CurLast; i++) |
751 | + drw_cur_free(drw, cursor[i]); |
752 | + for (i = 0; i < LENGTH(colors); i++) |
753 | + free(scheme[i]); |
754 | + XDestroyWindow(dpy, wmcheckwin); |
755 | + drw_free(drw); |
756 | + XSync(dpy, False); |
757 | + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
758 | + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
759 | +} |
760 | + |
761 | +void |
762 | +cleanupmon(Monitor *mon) |
763 | +{ |
764 | + Monitor *m; |
765 | + Bar *bar; |
766 | + |
767 | + if (mon == mons) |
768 | + mons = mons->next; |
769 | + else { |
770 | + for (m = mons; m && m->next != mon; m = m->next); |
771 | + m->next = mon->next; |
772 | + } |
773 | + for (bar = mon->bar; bar; bar = mon->bar) { |
774 | + XUnmapWindow(dpy, bar->win); |
775 | + XDestroyWindow(dpy, bar->win); |
776 | + mon->bar = bar->next; |
777 | + free(bar); |
778 | + } |
779 | + free(mon); |
780 | +} |
781 | + |
782 | +void |
783 | +clientmessage(XEvent *e) |
784 | +{ |
785 | + XClientMessageEvent *cme = &e->xclient; |
786 | + Client *c = wintoclient(cme->window); |
787 | + |
788 | + if (!c) |
789 | + return; |
790 | + if (cme->message_type == netatom[NetWMState]) { |
791 | + if (cme->data.l[1] == netatom[NetWMFullscreen] |
792 | + || cme->data.l[2] == netatom[NetWMFullscreen]) |
793 | + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ |
794 | + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); |
795 | + } else if (cme->message_type == netatom[NetActiveWindow]) { |
796 | + if (c != selmon->sel && !c->isurgent) |
797 | + seturgent(c, 1); |
798 | + } |
799 | +} |
800 | + |
801 | +void |
802 | +configure(Client *c) |
803 | +{ |
804 | + XConfigureEvent ce; |
805 | + |
806 | + ce.type = ConfigureNotify; |
807 | + ce.display = dpy; |
808 | + ce.event = c->win; |
809 | + ce.window = c->win; |
810 | + ce.x = c->x; |
811 | + ce.y = c->y; |
812 | + ce.width = c->w; |
813 | + ce.height = c->h; |
814 | + ce.border_width = c->bw; |
815 | + ce.above = None; |
816 | + ce.override_redirect = False; |
817 | + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
818 | +} |
819 | + |
820 | +void |
821 | +configurenotify(XEvent *e) |
822 | +{ |
823 | + Monitor *m; |
824 | + Bar *bar; |
825 | + Client *c; |
826 | + XConfigureEvent *ev = &e->xconfigure; |
827 | + int dirty; |
828 | + |
829 | + /* TODO: updategeom handling sucks, needs to be simplified */ |
830 | + if (ev->window == root) { |
831 | + dirty = (sw != ev->width || sh != ev->height); |
832 | + sw = ev->width; |
833 | + sh = ev->height; |
834 | + if (updategeom() || dirty) { |
835 | + drw_resize(drw, sw, bh); |
836 | + updatebars(); |
837 | + for (m = mons; m; m = m->next) { |
838 | + for (c = m->clients; c; c = c->next) |
839 | + if (c->isfullscreen) |
840 | + resizeclient(c, m->mx, m->my, m->mw, m->mh); |
841 | + for (bar = m->bar; bar; bar = bar->next) |
842 | + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); |
843 | + } |
844 | + focus(NULL); |
845 | + arrange(NULL); |
846 | + } |
847 | + } |
848 | +} |
849 | + |
850 | +void |
851 | +configurerequest(XEvent *e) |
852 | +{ |
853 | + Client *c; |
854 | + Monitor *m; |
855 | + XConfigureRequestEvent *ev = &e->xconfigurerequest; |
856 | + XWindowChanges wc; |
857 | + |
858 | + if ((c = wintoclient(ev->window))) { |
859 | + if (ev->value_mask & CWBorderWidth) |
860 | + c->bw = ev->border_width; |
861 | + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { |
862 | + m = c->mon; |
863 | + if (ev->value_mask & CWX) { |
864 | + c->oldx = c->x; |
865 | + c->x = m->mx + ev->x; |
866 | + } |
867 | + if (ev->value_mask & CWY) { |
868 | + c->oldy = c->y; |
869 | + c->y = m->my + ev->y; |
870 | + } |
871 | + if (ev->value_mask & CWWidth) { |
872 | + c->oldw = c->w; |
873 | + c->w = ev->width; |
874 | + } |
875 | + if (ev->value_mask & CWHeight) { |
876 | + c->oldh = c->h; |
877 | + c->h = ev->height; |
878 | + } |
879 | + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) |
880 | + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ |
881 | + if ((c->y + c->h) > m->my + m->mh && c->isfloating) |
882 | + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ |
883 | + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) |
884 | + configure(c); |
885 | + if (ISVISIBLE(c)) |
886 | + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
887 | + } else |
888 | + configure(c); |
889 | + } else { |
890 | + wc.x = ev->x; |
891 | + wc.y = ev->y; |
892 | + wc.width = ev->width; |
893 | + wc.height = ev->height; |
894 | + wc.border_width = ev->border_width; |
895 | + wc.sibling = ev->above; |
896 | + wc.stack_mode = ev->detail; |
897 | + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
898 | + } |
899 | + XSync(dpy, False); |
900 | +} |
901 | + |
902 | +void |
903 | +copyvalidchars(char *text, char *rawtext) |
904 | +{ |
905 | + int i = -1, j = 0; |
906 | + |
907 | + while(rawtext[++i]) { |
908 | + if ((unsigned char)rawtext[i] >= ' ') { |
909 | + text[j++] = rawtext[i]; |
910 | + } |
911 | + } |
912 | + text[j] = '\0'; |
913 | +} |
914 | + |
915 | +Monitor * |
916 | +createmon(void) |
917 | +{ |
918 | + Monitor *m, *mon; |
919 | + int i, n, mi, max_bars = 2, istopbar = topbar; |
920 | + |
921 | + const BarRule *br; |
922 | + Bar *bar; |
923 | + |
924 | + m = ecalloc(1, sizeof(Monitor)); |
925 | + m->tagset[0] = m->tagset[1] = 1; |
926 | + m->mfact = mfact; |
927 | + m->nmaster = nmaster; |
928 | + m->showbar = showbar; |
929 | + m->gappih = gappih; |
930 | + m->gappiv = gappiv; |
931 | + m->gappoh = gappoh; |
932 | + m->gappov = gappov; |
933 | + |
934 | + for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index |
935 | + m->lt[0] = &layouts[0]; |
936 | + m->lt[1] = &layouts[1 % LENGTH(layouts)]; |
937 | + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); |
938 | + |
939 | + /* Derive the number of bars for this monitor based on bar rules */ |
940 | + for (n = -1, i = 0; i < LENGTH(barrules); i++) { |
941 | + br = &barrules[i]; |
942 | + if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) |
943 | + n = MAX(br->bar, n); |
944 | + } |
945 | + |
946 | + for (i = 0; i <= n && i < max_bars; i++) { |
947 | + bar = ecalloc(1, sizeof(Bar)); |
948 | + bar->mon = m; |
949 | + bar->idx = i; |
950 | + bar->next = m->bar; |
951 | + bar->topbar = istopbar; |
952 | + m->bar = bar; |
953 | + istopbar = !istopbar; |
954 | + } |
955 | + |
956 | + return m; |
957 | +} |
958 | + |
959 | +void |
960 | +destroynotify(XEvent *e) |
961 | +{ |
962 | + Client *c; |
963 | + XDestroyWindowEvent *ev = &e->xdestroywindow; |
964 | + |
965 | + if ((c = wintoclient(ev->window))) |
966 | + unmanage(c, 1); |
967 | + |
968 | + else if ((c = swallowingclient(ev->window))) |
969 | + unmanage(c->swallowing, 1); |
970 | +} |
971 | + |
972 | +void |
973 | +detach(Client *c) |
974 | +{ |
975 | + Client **tc; |
976 | + |
977 | + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); |
978 | + *tc = c->next; |
979 | +} |
980 | + |
981 | +void |
982 | +detachstack(Client *c) |
983 | +{ |
984 | + Client **tc, *t; |
985 | + |
986 | + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); |
987 | + *tc = c->snext; |
988 | + |
989 | + if (c == c->mon->sel) { |
990 | + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); |
991 | + c->mon->sel = t; |
992 | + } |
993 | +} |
994 | + |
995 | +Monitor * |
996 | +dirtomon(int dir) |
997 | +{ |
998 | + Monitor *m = NULL; |
999 | + |