diff options
Diffstat (limited to 'src/core/dwm.c')
| -rw-r--r-- | src/core/dwm.c | 2735 |
1 files changed, 2735 insertions, 0 deletions
diff --git a/src/core/dwm.c b/src/core/dwm.c new file mode 100644 index 0000000..6082987 --- /dev/null +++ b/src/core/dwm.c | |||
| @@ -0,0 +1,2735 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. | ||
| 2 | * | ||
| 3 | * dynamic window manager is designed like any other X client as well. It is | ||
| 4 | * driven through handling X events. In contrast to other X clients, a window | ||
| 5 | * manager selects for SubstructureRedirectMask on the root window, to receive | ||
| 6 | * events about window (dis-)appearance. Only one X connection at a time is | ||
| 7 | * allowed to select for this event mask. | ||
| 8 | * | ||
| 9 | * The event handlers of dwm are organized in an array which is accessed | ||
| 10 | * whenever a new event has been fetched. This allows event dispatching | ||
| 11 | * in O(1) time. | ||
| 12 | * | ||
| 13 | * Each child of the root window is called a client, except windows which have | ||
| 14 | * set the override_redirect flag. Clients are organized in a linked client | ||
| 15 | * list on each monitor, the focus history is remembered through a stack list | ||
| 16 | * on each monitor. Each client contains a bit array to indicate the tags of a | ||
| 17 | * client. | ||
| 18 | * | ||
| 19 | * Keys and tagging rules are organized as arrays and defined in config.h. | ||
| 20 | * | ||
| 21 | * To understand everything else, start reading main(). | ||
| 22 | */ | ||
| 23 | #include <X11/Xatom.h> | ||
| 24 | #include <X11/Xlib.h> | ||
| 25 | #include <X11/Xproto.h> | ||
| 26 | #include <X11/Xutil.h> | ||
| 27 | #include <X11/cursorfont.h> | ||
| 28 | #include <X11/keysym.h> | ||
| 29 | #include <errno.h> | ||
| 30 | #include <locale.h> | ||
| 31 | #include <signal.h> | ||
| 32 | #include <stdarg.h> | ||
| 33 | #include <stdio.h> | ||
| 34 | #include <stdlib.h> | ||
| 35 | #include <string.h> | ||
| 36 | #include <sys/types.h> | ||
| 37 | #include <sys/wait.h> | ||
| 38 | #include <unistd.h> | ||
| 39 | #ifdef XINERAMA | ||
| 40 | #include <X11/extensions/Xinerama.h> | ||
| 41 | #endif /* XINERAMA */ | ||
| 42 | #include <X11/Xft/Xft.h> | ||
| 43 | #include <X11/Xlib-xcb.h> | ||
| 44 | #include <xcb/res.h> | ||
| 45 | #ifdef __OpenBSD__ | ||
| 46 | #include <kvm.h> | ||
| 47 | #include <sys/sysctl.h> | ||
| 48 | #endif /* __OpenBSD */ | ||
| 49 | |||
| 50 | #include "../drw/drw.h" | ||
| 51 | #include "../util/util.h" | ||
| 52 | #include <X11/XF86keysym.h> | ||
| 53 | |||
| 54 | /* macros */ | ||
| 55 | #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) | ||
| 56 | #define CLEANMASK(mask) \ | ||
| 57 | (mask & ~(numlockmask | LockMask) & \ | ||
| 58 | (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | \ | ||
| 59 | Mod5Mask)) | ||
| 60 | #define INTERSECT(x, y, w, h, m) \ | ||
| 61 | (MAX(0, MIN((x) + (w), (m)->wx + (m)->ww) - MAX((x), (m)->wx)) * \ | ||
| 62 | MAX(0, MIN((y) + (h), (m)->wy + (m)->wh) - MAX((y), (m)->wy))) | ||
| 63 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) | ||
| 64 | #define LENGTH(X) (sizeof X / sizeof X[0]) | ||
| 65 | #define MOUSEMASK (BUTTONMASK | PointerMotionMask) | ||
| 66 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) | ||
| 67 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) | ||
| 68 | #define TAGMASK ((1 << LENGTH(tags)) - 1) | ||
| 69 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) | ||
| 70 | |||
| 71 | #define SYSTEM_TRAY_REQUEST_DOCK 0 | ||
| 72 | /* XEMBED messages */ | ||
| 73 | #define XEMBED_EMBEDDED_NOTIFY 0 | ||
| 74 | #define XEMBED_WINDOW_ACTIVATE 1 | ||
| 75 | #define XEMBED_FOCUS_IN 4 | ||
| 76 | #define XEMBED_MODALITY_ON 10 | ||
| 77 | #define XEMBED_MAPPED (1 << 0) | ||
| 78 | #define XEMBED_WINDOW_ACTIVATE 1 | ||
| 79 | #define XEMBED_WINDOW_DEACTIVATE 2 | ||
| 80 | #define VERSION_MAJOR 0 | ||
| 81 | #define VERSION_MINOR 0 | ||
| 82 | #define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR | ||
| 83 | |||
| 84 | /* enums */ | ||
| 85 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ | ||
| 86 | enum { SchemeNorm, SchemeSel }; /* color schemes */ | ||
| 87 | enum { | ||
| 88 | NetSupported, | ||
| 89 | NetWMName, | ||
| 90 | NetWMState, | ||
| 91 | NetWMCheck, | ||
| 92 | NetSystemTray, | ||
| 93 | NetSystemTrayOP, | ||
| 94 | NetSystemTrayOrientation, | ||
| 95 | NetSystemTrayOrientationHorz, | ||
| 96 | NetWMFullscreen, | ||
| 97 | NetActiveWindow, | ||
| 98 | NetWMWindowType, | ||
| 99 | NetWMWindowTypeDialog, | ||
| 100 | NetClientList, | ||
| 101 | NetLast | ||
| 102 | }; /* EWMH atoms */ | ||
| 103 | enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ | ||
| 104 | enum { | ||
| 105 | WMProtocols, | ||
| 106 | WMDelete, | ||
| 107 | WMState, | ||
| 108 | WMTakeFocus, | ||
| 109 | WMLast | ||
| 110 | }; /* default atoms */ | ||
| 111 | enum { | ||
| 112 | ClkTagBar, | ||
| 113 | ClkLtSymbol, | ||
| 114 | ClkStatusText, | ||
| 115 | ClkWinTitle, | ||
| 116 | ClkClientWin, | ||
| 117 | ClkRootWin, | ||
| 118 | ClkLast | ||
| 119 | }; /* clicks */ | ||
| 120 | |||
| 121 | typedef union { | ||
| 122 | int i; | ||
| 123 | unsigned int ui; | ||
| 124 | float f; | ||
| 125 | const void *v; | ||
| 126 | } Arg; | ||
| 127 | |||
| 128 | typedef struct { | ||
| 129 | unsigned int click; | ||
| 130 | unsigned int mask; | ||
| 131 | unsigned int button; | ||
| 132 | void (*func)(const Arg *arg); | ||
| 133 | const Arg arg; | ||
| 134 | } Button; | ||
| 135 | |||
| 136 | typedef struct Monitor Monitor; | ||
| 137 | typedef struct Client Client; | ||
| 138 | struct Client { | ||
| 139 | char name[256]; | ||
| 140 | float mina, maxa; | ||
| 141 | int x, y, w, h; | ||
| 142 | int oldx, oldy, oldw, oldh; | ||
| 143 | int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; | ||
| 144 | int bw, oldbw; | ||
| 145 | unsigned int tags; | ||
| 146 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, | ||
| 147 | isterminal, noswallow; | ||
| 148 | pid_t pid; | ||
| 149 | Client *next; | ||
| 150 | Client *snext; | ||
| 151 | Client *swallowing; | ||
| 152 | Monitor *mon; | ||
| 153 | Window win; | ||
| 154 | }; | ||
| 155 | |||
| 156 | typedef struct { | ||
| 157 | unsigned int mod; | ||
| 158 | KeySym keysym; | ||
| 159 | void (*func)(const Arg *); | ||
| 160 | const Arg arg; | ||
| 161 | } Key; | ||
| 162 | |||
| 163 | typedef struct { | ||
| 164 | const char *symbol; | ||
| 165 | void (*arrange)(Monitor *); | ||
| 166 | } Layout; | ||
| 167 | |||
| 168 | struct Monitor { | ||
| 169 | char ltsymbol[16]; | ||
| 170 | float mfact; | ||
| 171 | int nmaster; | ||
| 172 | int num; | ||
| 173 | int by; /* bar geometry */ | ||
| 174 | int mx, my, mw, mh; /* screen size */ | ||
| 175 | int wx, wy, ww, wh; /* window area */ | ||
| 176 | int gappih; /* horizontal gap between windows */ | ||
| 177 | int gappiv; /* vertical gap between windows */ | ||
| 178 | int gappoh; /* horizontal outer gaps */ | ||
| 179 | int gappov; /* vertical outer gaps */ | ||
| 180 | unsigned int seltags; | ||
| 181 | unsigned int sellt; | ||
| 182 | unsigned int tagset[2]; | ||
| 183 | int showbar; | ||
| 184 | int topbar; | ||
| 185 | Client *clients; | ||
| 186 | Client *sel; | ||
| 187 | Client *stack; | ||
| 188 | Monitor *next; | ||
| 189 | Window barwin; | ||
| 190 | const Layout *lt[2]; | ||
| 191 | }; | ||
| 192 | |||
| 193 | typedef struct { | ||
| 194 | const char *class; | ||
| 195 | const char *instance; | ||
| 196 | const char *title; | ||
| 197 | unsigned int tags; | ||
| 198 | int isfloating; | ||
| 199 | int isterminal; | ||
| 200 | int noswallow; | ||
| 201 | int monitor; | ||
| 202 | } Rule; | ||
| 203 | |||
| 204 | typedef struct Systray Systray; | ||
| 205 | struct Systray { | ||
| 206 | Window win; | ||
| 207 | Client *icons; | ||
| 208 | }; | ||
| 209 | |||
| 210 | /* function declarations */ | ||
| 211 | static void applyrules(Client *c); | ||
| 212 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, | ||
| 213 | int interact); | ||
| 214 | static void arrange(Monitor *m); | ||
| 215 | static void arrangemon(Monitor *m); | ||
| 216 | static void attach(Client *c); | ||
| 217 | static void attachstack(Client *c); | ||
| 218 | static void buttonpress(XEvent *e); | ||
| 219 | static void checkotherwm(void); | ||
| 220 | static void cleanup(void); | ||
| 221 | static void cleanupmon(Monitor *mon); | ||
| 222 | static void clientmessage(XEvent *e); | ||
| 223 | static void configure(Client *c); | ||
| 224 | static void configurenotify(XEvent *e); | ||
| 225 | static void configurerequest(XEvent *e); | ||
| 226 | static Monitor *createmon(void); | ||
| 227 | static void destroynotify(XEvent *e); | ||
| 228 | static void detach(Client *c); | ||
| 229 | static void detachstack(Client *c); | ||
| 230 | static Monitor *dirtomon(int dir); | ||
| 231 | static void drawbar(Monitor *m); | ||
| 232 | static void drawbars(void); | ||
| 233 | static void enternotify(XEvent *e); | ||
| 234 | static void expose(XEvent *e); | ||
| 235 | static void focus(Client *c); | ||
| 236 | static void focusin(XEvent *e); | ||
| 237 | static void focusmon(const Arg *arg); | ||
| 238 | static void focusstack(const Arg *arg); | ||
| 239 | static Atom getatomprop(Client *c, Atom prop); | ||
| 240 | static int getrootptr(int *x, int *y); | ||
| 241 | static long getstate(Window w); | ||
| 242 | static unsigned int getsystraywidth(); | ||
| 243 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); | ||
| 244 | static void grabbuttons(Client *c, int focused); | ||
| 245 | static void grabkeys(void); | ||
| 246 | static void incnmaster(const Arg *arg); | ||
| 247 | static void keypress(XEvent *e); | ||
| 248 | static void killclient(const Arg *arg); | ||
| 249 | static void manage(Window w, XWindowAttributes *wa); | ||
| 250 | static void mappingnotify(XEvent *e); | ||
| 251 | static void maprequest(XEvent *e); | ||
| 252 | static void monocle(Monitor *m); | ||
| 253 | static void motionnotify(XEvent *e); | ||
| 254 | static void movemouse(const Arg *arg); | ||
| 255 | static Client *nexttiled(Client *c); | ||
| 256 | static void pop(Client *c); | ||
| 257 | static void propertynotify(XEvent *e); | ||
| 258 | static void quit(const Arg *arg); | ||
| 259 | static Monitor *recttomon(int x, int y, int w, int h); | ||
| 260 | static void removesystrayicon(Client *i); | ||
| 261 | static void resize(Client *c, int x, int y, int w, int h, int interact); | ||
| 262 | static void resizebarwin(Monitor *m); | ||
| 263 | static void resizeclient(Client *c, int x, int y, int w, int h); | ||
| 264 | static void resizemouse(const Arg *arg); | ||
| 265 | static void resizerequest(XEvent *e); | ||
| 266 | static void restack(Monitor *m); | ||
| 267 | static void run(void); | ||
| 268 | static void scan(void); | ||
| 269 | static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, | ||
| 270 | long d3, long d4); | ||
| 271 | static void sendmon(Client *c, Monitor *m); | ||
| 272 | static void setclientstate(Client *c, long state); | ||
| 273 | static void setfocus(Client *c); | ||
| 274 | static void setfullscreen(Client *c, int fullscreen); | ||
| 275 | static void setgaps(int oh, int ov, int ih, int iv); | ||
| 276 | static void incrgaps(const Arg *arg); | ||
| 277 | static void incrigaps(const Arg *arg); | ||
| 278 | static void incrogaps(const Arg *arg); | ||
| 279 | static void incrohgaps(const Arg *arg); | ||
| 280 | static void incrovgaps(const Arg *arg); | ||
| 281 | static void incrihgaps(const Arg *arg); | ||
| 282 | static void incrivgaps(const Arg *arg); | ||
| 283 | static void togglegaps(const Arg *arg); | ||
| 284 | static void defaultgaps(const Arg *arg); | ||
| 285 | static void setlayout(const Arg *arg); | ||
| 286 | static void setmfact(const Arg *arg); | ||
| 287 | static void setup(void); | ||
| 288 | static void seturgent(Client *c, int urg); | ||
| 289 | static void showhide(Client *c); | ||
| 290 | static void spawn(const Arg *arg); | ||
| 291 | static Monitor *systraytomon(Monitor *m); | ||
| 292 | static void tag(const Arg *arg); | ||
| 293 | static void tagmon(const Arg *arg); | ||
| 294 | static void tile(Monitor *m); | ||
| 295 | static void togglebar(const Arg *arg); | ||
| 296 | static void togglefloating(const Arg *arg); | ||
| 297 | static void togglefullscr(const Arg *arg); | ||
| 298 | static void toggletag(const Arg *arg); | ||
| 299 | static void toggleview(const Arg *arg); | ||
| 300 | static void unfocus(Client *c, int setfocus); | ||
| 301 | static void unmanage(Client *c, int destroyed); | ||
| 302 | static void unmapnotify(XEvent *e); | ||
| 303 | static void updatebarpos(Monitor *m); | ||
| 304 | static void updatebars(void); | ||
| 305 | static void updateclientlist(void); | ||
| 306 | static int updategeom(void); | ||
| 307 | static void updatenumlockmask(void); | ||
| 308 | static void updatesystray(void); | ||
| 309 | static void updatesystrayicongeom(Client *i, int w, int h); | ||
| 310 | static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); | ||
| 311 | static void updatesizehints(Client *c); | ||
| 312 | static void updatestatus(void); | ||
| 313 | static void updatetitle(Client *c); | ||
| 314 | static void updatewindowtype(Client *c); | ||
| 315 | static void updatewmhints(Client *c); | ||
| 316 | static void view(const Arg *arg); | ||
| 317 | static Client *wintoclient(Window w); | ||
| 318 | static Monitor *wintomon(Window w); | ||
| 319 | static Client *wintosystrayicon(Window w); | ||
| 320 | static int xerror(Display *dpy, XErrorEvent *ee); | ||
| 321 | static int xerrordummy(Display *dpy, XErrorEvent *ee); | ||
| 322 | static int xerrorstart(Display *dpy, XErrorEvent *ee); | ||
| 323 | static void zoom(const Arg *arg); | ||
| 324 | |||
| 325 | static pid_t getparentprocess(pid_t p); | ||
| 326 | static int isdescprocess(pid_t p, pid_t c); | ||
| 327 | static Client *swallowingclient(Window w); | ||
| 328 | static Client *termforwin(const Client *c); | ||
| 329 | static pid_t winpid(Window w); | ||
| 330 | |||
| 331 | /* variables */ | ||
| 332 | static Systray *systray = NULL; | ||
| 333 | static const char broken[] = "broken"; | ||
| 334 | static char stext[256]; | ||
| 335 | static int screen; | ||
| 336 | static int sw, sh; /* X display screen geometry width, height */ | ||
| 337 | static int bh; /* bar height */ | ||
| 338 | static int enablegaps = 1; /* enables gaps, used by togglegaps */ | ||
| 339 | static int lrpad; /* sum of left and right padding for text */ | ||
| 340 | static int (*xerrorxlib)(Display *, XErrorEvent *); | ||
| 341 | static unsigned int numlockmask = 0; | ||
| 342 | static void (*handler[LASTEvent])(XEvent *) = { | ||
| 343 | [ButtonPress] = buttonpress, | ||
| 344 | [ClientMessage] = clientmessage, | ||
| 345 | [ConfigureRequest] = configurerequest, | ||
| 346 | [ConfigureNotify] = configurenotify, | ||
| 347 | [DestroyNotify] = destroynotify, | ||
| 348 | [EnterNotify] = enternotify, | ||
| 349 | [Expose] = expose, | ||
| 350 | [FocusIn] = focusin, | ||
| 351 | [KeyPress] = keypress, | ||
| 352 | [MappingNotify] = mappingnotify, | ||
| 353 | [MapRequest] = maprequest, | ||
| 354 | [MotionNotify] = motionnotify, | ||
| 355 | [PropertyNotify] = propertynotify, | ||
| 356 | [ResizeRequest] = resizerequest, | ||
| 357 | [UnmapNotify] = unmapnotify}; | ||
| 358 | |||
| 359 | static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; | ||
| 360 | static int running = 1; | ||
| 361 | static Cur *cursor[CurLast]; | ||
| 362 | static Clr **scheme; | ||
| 363 | static Display *dpy; | ||
| 364 | static Drw *drw; | ||
| 365 | static Monitor *mons, *selmon; | ||
| 366 | static Window root, wmcheckwin; | ||
| 367 | |||
| 368 | static xcb_connection_t *xcon; | ||
| 369 | |||
| 370 | /* configuration, allows nested code to access above variables */ | ||
| 371 | #include "../config/config.h" | ||
| 372 | |||
| 373 | /* compile-time check if all tags fit into an unsigned int bit array. */ | ||
| 374 | struct NumTags { | ||
| 375 | char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; | ||
| 376 | }; | ||
| 377 | |||
| 378 | /* function implementations */ | ||
| 379 | void applyrules(Client *c) { | ||
| 380 | const char *class, *instance; | ||
| 381 | unsigned int i; | ||
| 382 | const Rule *r; | ||
| 383 | Monitor *m; | ||
| 384 | XClassHint ch = {NULL, NULL}; | ||
| 385 | |||
| 386 | /* rule matching */ | ||
| 387 | c->isfloating = 0; | ||
| 388 | c->tags = 0; | ||
| 389 | XGetClassHint(dpy, c->win, &ch); | ||
| 390 | class = ch.res_class ? ch.res_class : broken; | ||
| 391 | instance = ch.res_name ? ch.res_name : broken; | ||
| 392 | |||
| 393 | for (i = 0; i < LENGTH(rules); i++) { | ||
| 394 | r = &rules[i]; | ||
| 395 | if ((!r->title || strstr(c->name, r->title)) && | ||
| 396 | (!r->class || strstr(class, r->class)) && | ||
| 397 | (!r->instance || strstr(instance, r->instance))) { | ||
| 398 | c->isterminal = r->isterminal; | ||
| 399 | c->noswallow = r->noswallow; | ||
| 400 | c->isfloating = r->isfloating; | ||
| 401 | c->tags |= r->tags; | ||
| 402 | for (m = mons; m && m->num != r->monitor; m = m->next) | ||
| 403 | ; | ||
| 404 | if (m) | ||
| 405 | c->mon = m; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | if (ch.res_class) | ||
| 409 | XFree(ch.res_class); | ||
| 410 | if (ch.res_name) | ||
| 411 | XFree(ch.res_name); | ||
| 412 | c->tags = | ||
| 413 | c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; | ||
| 414 | } | ||
| 415 | |||
| 416 | int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) { | ||
| 417 | int baseismin; | ||
| 418 | Monitor *m = c->mon; | ||
| 419 | |||
| 420 | /* set minimum possible */ | ||
| 421 | *w = MAX(1, *w); | ||
| 422 | *h = MAX(1, *h); | ||
| 423 | if (interact) { | ||
| 424 | if (*x > sw) | ||
| 425 | *x = sw - WIDTH(c); | ||
| 426 | if (*y > sh) | ||
| 427 | *y = sh - HEIGHT(c); | ||
| 428 | if (*x + *w + 2 * c->bw < 0) | ||
| 429 | *x = 0; | ||
| 430 | if (*y + *h + 2 * c->bw < 0) | ||
| 431 | *y = 0; | ||
| 432 | } else { | ||
| 433 | if (*x >= m->wx + m->ww) | ||
| 434 | *x = m->wx + m->ww - WIDTH(c); | ||
| 435 | if (*y >= m->wy + m->wh) | ||
| 436 | *y = m->wy + m->wh - HEIGHT(c); | ||
| 437 | if (*x + *w + 2 * c->bw <= m->wx) | ||
| 438 | *x = m->wx; | ||
| 439 | if (*y + *h + 2 * c->bw <= m->wy) | ||
| 440 | *y = m->wy; | ||
| 441 | } | ||
| 442 | if (*h < bh) | ||
| 443 | *h = bh; | ||
| 444 | if (*w < bh) | ||
| 445 | *w = bh; | ||
| 446 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { | ||
| 447 | if (!c->hintsvalid) | ||
| 448 | updatesizehints(c); | ||
| 449 | /* see last two sentences in ICCCM 4.1.2.3 */ | ||
| 450 | baseismin = c->basew == c->minw && c->baseh == c->minh; | ||
| 451 | if (!baseismin) { /* temporarily remove base dimensions */ | ||
| 452 | *w -= c->basew; | ||
| 453 | *h -= c->baseh; | ||
| 454 | } | ||
| 455 | /* adjust for aspect limits */ | ||
| 456 | if (c->mina > 0 && c->maxa > 0) { | ||
| 457 | if (c->maxa < (float)*w / *h) | ||
| 458 | *w = *h * c->maxa + 0.5; | ||
| 459 | else if (c->mina < (float)*h / *w) | ||
| 460 | *h = *w * c->mina + 0.5; | ||
| 461 | } | ||
| 462 | if (baseismin) { /* increment calculation requires this */ | ||
| 463 | *w -= c->basew; | ||
| 464 | *h -= c->baseh; | ||
| 465 | } | ||
| 466 | /* adjust for increment value */ | ||
| 467 | if (c->incw) | ||
| 468 | *w -= *w % c->incw; | ||
| 469 | if (c->inch) | ||
| 470 | *h -= *h % c->inch; | ||
| 471 | /* restore base dimensions */ | ||
| 472 | *w = MAX(*w + c->basew, c->minw); | ||
| 473 | *h = MAX(*h + c->baseh, c->minh); | ||
| 474 | if (c->maxw) | ||
| 475 | *w = MIN(*w, c->maxw); | ||
| 476 | if (c->maxh) | ||
| 477 | *h = MIN(*h, c->maxh); | ||
| 478 | } | ||
| 479 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; | ||
| 480 | } | ||
| 481 | |||
| 482 | void arrange(Monitor *m) { | ||
| 483 | if (m) | ||
| 484 | showhide(m->stack); | ||
| 485 | else | ||
| 486 | for (m = mons; m; m = m->next) | ||
| 487 | showhide(m->stack); | ||
| 488 | if (m) { | ||
| 489 | arrangemon(m); | ||
| 490 | restack(m); | ||
| 491 | } else | ||
| 492 | for (m = mons; m; m = m->next) | ||
| 493 | arrangemon(m); | ||
| 494 | } | ||
| 495 | |||
| 496 | void arrangemon(Monitor *m) { | ||
| 497 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); | ||
| 498 | if (m->lt[m->sellt]->arrange) | ||
| 499 | m->lt[m->sellt]->arrange(m); | ||
| 500 | } | ||
| 501 | |||
| 502 | void attach(Client *c) { | ||
| 503 | c->next = c->mon->clients; | ||
| 504 | c->mon->clients = c; | ||
| 505 | } | ||
| 506 | |||
| 507 | void attachstack(Client *c) { | ||
| 508 | c->snext = c->mon->stack; | ||
| 509 | c->mon->stack = c; | ||
| 510 | } | ||
| 511 | |||
| 512 | void swallow(Client *p, Client *c) { | ||
| 513 | |||
| 514 | if (c->noswallow || c->isterminal) | ||
| 515 | return; | ||
| 516 | if (c->noswallow && !swallowfloating && c->isfloating) | ||
| 517 | return; | ||
| 518 | |||
| 519 | detach(c); | ||
| 520 | detachstack(c); | ||
| 521 | |||
| 522 | setclientstate(c, WithdrawnState); | ||
| 523 | XUnmapWindow(dpy, p->win); | ||
| 524 | |||
| 525 | p->swallowing = c; | ||
| 526 | c->mon = p->mon; | ||
| 527 | |||
| 528 | Window w = p->win; | ||
| 529 | p->win = c->win; | ||
| 530 | c->win = w; | ||
| 531 | updatetitle(p); | ||
| 532 | XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); | ||
| 533 | arrange(p->mon); | ||
| 534 | configure(p); | ||
| 535 | updateclientlist(); | ||
| 536 | } | ||
| 537 | |||
| 538 | void unswallow(Client *c) { | ||
| 539 | c->win = c->swallowing->win; | ||
| 540 | |||
| 541 | free(c->swallowing); | ||
| 542 | c->swallowing = NULL; | ||
| 543 | |||
| 544 | /* unfullscreen the client */ | ||
| 545 | setfullscreen(c, 0); | ||
| 546 | updatetitle(c); | ||
| 547 | arrange(c->mon); | ||
| 548 | XMapWindow(dpy, c->win); | ||
| 549 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); | ||
| 550 | setclientstate(c, NormalState); | ||
| 551 | focus(NULL); | ||
| 552 | arrange(c->mon); | ||
| 553 | } | ||
| 554 | |||
| 555 | void buttonpress(XEvent *e) { | ||
| 556 | unsigned int i, x, click; | ||
| 557 | Arg arg = {0}; | ||
| 558 | Client *c; | ||
| 559 | Monitor *m; | ||
| 560 | XButtonPressedEvent *ev = &e->xbutton; | ||
| 561 | |||
| 562 | click = ClkRootWin; | ||
| 563 | /* focus monitor if necessary */ | ||
| 564 | if ((m = wintomon(ev->window)) && m != selmon) { | ||
| 565 | unfocus(selmon->sel, 1); | ||
| 566 | selmon = m; | ||
| 567 | focus(NULL); | ||
| 568 | } | ||
| 569 | if (ev->window == selmon->barwin) { | ||
| 570 | i = x = 0; | ||
| 571 | unsigned int occ = 0; | ||
| 572 | for (c = m->clients; c; c = c->next) | ||
| 573 | occ |= c->tags; | ||
| 574 | do { | ||
| 575 | /* Do not reserve space for vacant tags */ | ||
| 576 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) | ||
| 577 | continue; | ||
| 578 | x += TEXTW(tags[i]); | ||
| 579 | } while (ev->x >= x && ++i < LENGTH(tags)); | ||
| 580 | if (i < LENGTH(tags)) { | ||
| 581 | click = ClkTagBar; | ||
| 582 | arg.ui = 1 << i; | ||
| 583 | } else if (ev->x < x + TEXTW(selmon->ltsymbol)) | ||
| 584 | click = ClkLtSymbol; | ||
| 585 | else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) | ||
| 586 | click = ClkStatusText; | ||
| 587 | else | ||
| 588 | click = ClkWinTitle; | ||
| 589 | } else if ((c = wintoclient(ev->window))) { | ||
| 590 | focus(c); | ||
| 591 | restack(selmon); | ||
| 592 | XAllowEvents(dpy, ReplayPointer, CurrentTime); | ||
| 593 | click = ClkClientWin; | ||
| 594 | } | ||
| 595 | for (i = 0; i < LENGTH(buttons); i++) | ||
| 596 | if (click == buttons[i].click && buttons[i].func && | ||
| 597 | buttons[i].button == ev->button && | ||
| 598 | CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) | ||
| 599 | buttons[i].func( | ||
| 600 | click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); | ||
| 601 | } | ||
| 602 | |||
| 603 | void checkotherwm(void) { | ||
| 604 | xerrorxlib = XSetErrorHandler(xerrorstart); | ||
| 605 | /* this causes an error if some other window manager is running */ | ||
| 606 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); | ||
| 607 | XSync(dpy, False); | ||
| 608 | XSetErrorHandler(xerror); | ||
| 609 | XSync(dpy, False); | ||
| 610 | } | ||
| 611 | |||
| 612 | void cleanup(void) { | ||
| 613 | Arg a = {.ui = ~0}; | ||
| 614 | Layout foo = {"", NULL}; | ||
| 615 | Monitor *m; | ||
| 616 | size_t i; | ||
| 617 | |||
| 618 | view(&a); | ||
| 619 | selmon->lt[selmon->sellt] = &foo; | ||
| 620 | for (m = mons; m; m = m->next) | ||
| 621 | while (m->stack) | ||
| 622 | unmanage(m->stack, 0); | ||
| 623 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
| 624 | while (mons) | ||
| 625 | cleanupmon(mons); | ||
| 626 | |||
| 627 | if (showsystray) { | ||
| 628 | XUnmapWindow(dpy, systray->win); | ||
| 629 | XDestroyWindow(dpy, systray->win); | ||
| 630 | free(systray); | ||
| 631 | } | ||
| 632 | |||
| 633 | for (i = 0; i < CurLast; i++) | ||
| 634 | drw_cur_free(drw, cursor[i]); | ||
| 635 | for (i = 0; i < LENGTH(colors); i++) | ||
| 636 | free(scheme[i]); | ||
| 637 | free(scheme); | ||
| 638 | XDestroyWindow(dpy, wmcheckwin); | ||
| 639 | drw_free(drw); | ||
| 640 | XSync(dpy, False); | ||
| 641 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); | ||
| 642 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 643 | } | ||
| 644 | |||
| 645 | void cleanupmon(Monitor *mon) { | ||
| 646 | Monitor *m; | ||
| 647 | |||
| 648 | if (mon == mons) | ||
| 649 | mons = mons->next; | ||
| 650 | else { | ||
| 651 | for (m = mons; m && m->next != mon; m = m->next) | ||
| 652 | ; | ||
| 653 | m->next = mon->next; | ||
| 654 | } | ||
| 655 | XUnmapWindow(dpy, mon->barwin); | ||
| 656 | XDestroyWindow(dpy, mon->barwin); | ||
| 657 | free(mon); | ||
| 658 | } | ||
| 659 | |||
| 660 | void clientmessage(XEvent *e) { | ||
| 661 | XWindowAttributes wa; | ||
| 662 | XSetWindowAttributes swa; | ||
| 663 | XClientMessageEvent *cme = &e->xclient; | ||
| 664 | Client *c = wintoclient(cme->window); | ||
| 665 | |||
| 666 | if (showsystray && cme->window == systray->win && | ||
| 667 | cme->message_type == netatom[NetSystemTrayOP]) { | ||
| 668 | /* add systray icons */ | ||
| 669 | if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { | ||
| 670 | if (!(c = (Client *)calloc(1, sizeof(Client)))) | ||
| 671 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); | ||
| 672 | if (!(c->win = cme->data.l[2])) { | ||
| 673 | free(c); | ||
| 674 | return; | ||
| 675 | } | ||
| 676 | c->mon = selmon; | ||
| 677 | c->next = systray->icons; | ||
| 678 | systray->icons = c; | ||
| 679 | if (!XGetWindowAttributes(dpy, c->win, &wa)) { | ||
| 680 | /* use sane defaults */ | ||
| 681 | wa.width = bh; | ||
| 682 | wa.height = bh; | ||
| 683 | wa.border_width = 0; | ||
| 684 | } | ||
| 685 | c->x = c->oldx = c->y = c->oldy = 0; | ||
| 686 | c->w = c->oldw = wa.width; | ||
| 687 | c->h = c->oldh = wa.height; | ||
| 688 | c->oldbw = wa.border_width; | ||
| 689 | c->bw = 0; | ||
| 690 | c->isfloating = True; | ||
| 691 | /* reuse tags field as mapped status */ | ||
| 692 | c->tags = 1; | ||
| 693 | updatesizehints(c); | ||
| 694 | updatesystrayicongeom(c, wa.width, wa.height); | ||
| 695 | XAddToSaveSet(dpy, c->win); | ||
| 696 | XSelectInput(dpy, c->win, | ||
| 697 | StructureNotifyMask | PropertyChangeMask | | ||
| 698 | ResizeRedirectMask); | ||
| 699 | XReparentWindow(dpy, c->win, systray->win, 0, 0); | ||
| 700 | /* use parents background color */ | ||
| 701 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; | ||
| 702 | XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); | ||
| 703 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, | ||
| 704 | XEMBED_EMBEDDED_NOTIFY, 0, systray->win, | ||
| 705 | XEMBED_EMBEDDED_VERSION); | ||
| 706 | /* FIXME not sure if I have to send these events, too */ | ||
| 707 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, | ||
| 708 | XEMBED_FOCUS_IN, 0, systray->win, XEMBED_EMBEDDED_VERSION); | ||
| 709 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, | ||
| 710 | XEMBED_WINDOW_ACTIVATE, 0, systray->win, | ||
| 711 | XEMBED_EMBEDDED_VERSION); | ||
| 712 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, | ||
| 713 | XEMBED_MODALITY_ON, 0, systray->win, XEMBED_EMBEDDED_VERSION); | ||
| 714 | XSync(dpy, False); | ||
| 715 | resizebarwin(selmon); | ||
| 716 | updatesystray(); | ||
| 717 | setclientstate(c, NormalState); | ||
| 718 | } | ||
| 719 | return; | ||
| 720 | } | ||
| 721 | |||
| 722 | if (!c) | ||
| 723 | return; | ||
| 724 | if (cme->message_type == netatom[NetWMState]) { | ||
| 725 | if (cme->data.l[1] == netatom[NetWMFullscreen] || | ||
| 726 | cme->data.l[2] == netatom[NetWMFullscreen]) | ||
| 727 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ | ||
| 728 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ | ||
| 729 | && !c->isfullscreen))); | ||
| 730 | } else if (cme->message_type == netatom[NetActiveWindow]) { | ||
| 731 | if (c != selmon->sel && !c->isurgent) | ||
| 732 | seturgent(c, 1); | ||
| 733 | } | ||
| 734 | } | ||
| 735 | |||
| 736 | void configure(Client *c) { | ||
| 737 | XConfigureEvent ce; | ||
| 738 | |||
| 739 | ce.type = ConfigureNotify; | ||
| 740 | ce.display = dpy; | ||
| 741 | ce.event = c->win; | ||
| 742 | ce.window = c->win; | ||
| 743 | ce.x = c->x; | ||
| 744 | ce.y = c->y; | ||
| 745 | ce.width = c->w; | ||
| 746 | ce.height = c->h; | ||
| 747 | ce.border_width = c->bw; | ||
| 748 | ce.above = None; | ||
| 749 | ce.override_redirect = False; | ||
| 750 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); | ||
| 751 | } | ||
| 752 | |||
| 753 | void configurenotify(XEvent *e) { | ||
| 754 | Monitor *m; | ||
| 755 | Client *c; | ||
| 756 | XConfigureEvent *ev = &e->xconfigure; | ||
| 757 | int dirty; | ||
| 758 | |||
| 759 | /* TODO: updategeom handling sucks, needs to be simplified */ | ||
| 760 | if (ev->window == root) { | ||
| 761 | dirty = (sw != ev->width || sh != ev->height); | ||
| 762 | sw = ev->width; | ||
| 763 | sh = ev->height; | ||
| 764 | if (updategeom() || dirty) { | ||
| 765 | drw_resize(drw, sw, bh); | ||
| 766 | updatebars(); | ||
| 767 | for (m = mons; m; m = m->next) { | ||
| 768 | for (c = m->clients; c; c = c->next) | ||
| 769 | if (c->isfullscreen) | ||
| 770 | resizeclient(c, m->mx, m->my, m->mw, m->mh); | ||
| 771 | resizebarwin(m); | ||
| 772 | } | ||
| 773 | focus(NULL); | ||
| 774 | arrange(NULL); | ||
| 775 | } | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | void configurerequest(XEvent *e) { | ||
| 780 | Client *c; | ||
| 781 | Monitor *m; | ||
| 782 | XConfigureRequestEvent *ev = &e->xconfigurerequest; | ||
| 783 | XWindowChanges wc; | ||
| 784 | |||
| 785 | if ((c = wintoclient(ev->window))) { | ||
| 786 | if (ev->value_mask & CWBorderWidth) | ||
| 787 | c->bw = ev->border_width; | ||
| 788 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { | ||
| 789 | m = c->mon; | ||
| 790 | if (ev->value_mask & CWX) { | ||
| 791 | c->oldx = c->x; | ||
| 792 | c->x = m->mx + ev->x; | ||
| 793 | } | ||
| 794 | if (ev->value_mask & CWY) { | ||
| 795 | c->oldy = c->y; | ||
| 796 | c->y = m->my + ev->y; | ||
| 797 | } | ||
| 798 | if (ev->value_mask & CWWidth) { | ||
| 799 | c->oldw = c->w; | ||
| 800 | c->w = ev->width; | ||
| 801 | } | ||
| 802 | if (ev->value_mask & CWHeight) { | ||
| 803 | c->oldh = c->h; | ||
| 804 | c->h = ev->height; | ||
| 805 | } | ||
| 806 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) | ||
| 807 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ | ||
| 808 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) | ||
| 809 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ | ||
| 810 | if ((ev->value_mask & (CWX | CWY)) && | ||
| 811 | !(ev->value_mask & (CWWidth | CWHeight))) | ||
| 812 | configure(c); | ||
| 813 | if (ISVISIBLE(c)) | ||
| 814 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); | ||
| 815 | } else | ||
| 816 | configure(c); | ||
| 817 | } else { | ||
| 818 | wc.x = ev->x; | ||
| 819 | wc.y = ev->y; | ||
| 820 | wc.width = ev->width; | ||
| 821 | wc.height = ev->height; | ||
| 822 | wc.border_width = ev->border_width; | ||
| 823 | wc.sibling = ev->above; | ||
| 824 | wc.stack_mode = ev->detail; | ||
| 825 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); | ||
| 826 | } | ||
| 827 | XSync(dpy, False); | ||
| 828 | } | ||
| 829 | |||
| 830 | Monitor *createmon(void) { | ||
| 831 | Monitor *m; | ||
| 832 | |||
| 833 | m = ecalloc(1, sizeof(Monitor)); | ||
| 834 | m->tagset[0] = m->tagset[1] = 1; | ||
| 835 | m->mfact = mfact; | ||
| 836 | m->nmaster = nmaster; | ||
| 837 | m->showbar = showbar; | ||
| 838 | m->topbar = topbar; | ||
| 839 | m->gappih = gappih; | ||
| 840 | m->gappiv = gappiv; | ||
| 841 | m->gappoh = gappoh; | ||
| 842 | m->gappov = gappov; | ||
| 843 | m->lt[0] = &layouts[0]; | ||
| 844 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; | ||
| 845 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); | ||
| 846 | return m; | ||
| 847 | } | ||
| 848 | |||
| 849 | void destroynotify(XEvent *e) { | ||
| 850 | Client *c; | ||
| 851 | XDestroyWindowEvent *ev = &e->xdestroywindow; | ||
| 852 | |||
| 853 | if ((c = wintoclient(ev->window))) | ||
| 854 | unmanage(c, 1); | ||
| 855 | else if ((c = wintosystrayicon(ev->window))) { | ||
| 856 | removesystrayicon(c); | ||
| 857 | resizebarwin(selmon); | ||
| 858 | updatesystray(); | ||
| 859 | } else if ((c = swallowingclient(ev->window))) | ||
| 860 | unmanage(c->swallowing, 1); | ||
| 861 | } | ||
| 862 | |||
| 863 | void detach(Client *c) { | ||
| 864 | Client **tc; | ||
| 865 | |||
| 866 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next) | ||
| 867 | ; | ||
| 868 | *tc = c->next; | ||
| 869 | } | ||
| 870 | |||
| 871 | void detachstack(Client *c) { | ||
| 872 | Client **tc, *t; | ||
| 873 | |||
| 874 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext) | ||
| 875 | ; | ||
| 876 | *tc = c->snext; | ||
| 877 | |||
| 878 | if (c == c->mon->sel) { | ||
| 879 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext) | ||
| 880 | ; | ||
| 881 | c->mon->sel = t; | ||
| 882 | } | ||
| 883 | } | ||
| 884 | |||
| 885 | Monitor *dirtomon(int dir) { | ||
| 886 | Monitor *m = NULL; | ||
| 887 | |||
| 888 | if (dir > 0) { | ||
| 889 | if (!(m = selmon->next)) | ||
| 890 | m = mons; | ||
| 891 | } else if (selmon == mons) | ||
| 892 | for (m = mons; m->next; m = m->next) | ||
| 893 | ; | ||
| 894 | else | ||
| 895 | for (m = mons; m->next != selmon; m = m->next) | ||
| 896 | ; | ||
| 897 | return m; | ||
| 898 | } | ||
| 899 | |||
| 900 | void drawbar(Monitor *m) { | ||
| 901 | int x, w, tw = 0, stw = 0; | ||
| 902 | int boxs = drw->fonts->h / 9; | ||
| 903 | int boxw = drw->fonts->h / 6 + 2; | ||
| 904 | unsigned int i, occ = 0, urg = 0; | ||
| 905 | Client *c; | ||
| 906 | |||
| 907 | if (!m->showbar) | ||
| 908 | return; | ||
| 909 | |||
| 910 | if (showsystray && m == systraytomon(m) && !systrayonleft) | ||
| 911 | stw = getsystraywidth(); | ||
| 912 | |||
| 913 | /* draw status first so it can be overdrawn by tags later */ | ||
| 914 | if (m == selmon) { /* status is only drawn on selected monitor */ | ||
| 915 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 916 | tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */ | ||
| 917 | drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); | ||
| 918 | } | ||
| 919 | |||
| 920 | resizebarwin(m); | ||
| 921 | for (c = m->clients; c; c = c->next) { | ||
| 922 | occ |= c->tags; | ||
| 923 | if (c->isurgent) | ||
| 924 | urg |= c->tags; | ||
| 925 | } | ||
| 926 | x = 0; | ||
| 927 | for (i = 0; i < LENGTH(tags); i++) { | ||
| 928 | /* Do not draw vacant tags */ | ||
| 929 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) | ||
| 930 | continue; | ||
| 931 | w = TEXTW(tags[i]); | ||
| 932 | drw_setscheme( | ||
| 933 | drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); | ||
| 934 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); | ||
| 935 | if (occ & 1 << i) | ||
| 936 | drw_rect(drw, x + boxs, boxs, boxw, boxw, | ||
| 937 | m == selmon && selmon->sel && selmon->sel->tags & 1 << i, | ||
| 938 | urg & 1 << i); | ||
| 939 | x += w; | ||
| 940 | } | ||
| 941 | w = TEXTW(m->ltsymbol); | ||
| 942 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 943 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); | ||
| 944 | |||
| 945 | if ((w = m->ww - tw - stw - x) > bh) { | ||
| 946 | if (m->sel) { | ||
| 947 | drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); | ||
| 948 | drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); | ||
| 949 | if (m->sel->isfloating) | ||
| 950 | drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); | ||
| 951 | } else { | ||
| 952 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 953 | drw_rect(drw, x, 0, w, bh, 1, 1); | ||
| 954 | } | ||
| 955 | } | ||
| 956 | drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); | ||
| 957 | } | ||
| 958 | |||
| 959 | void drawbars(void) { | ||
| 960 | Monitor *m; | ||
| 961 | |||
| 962 | for (m = mons; m; m = m->next) | ||
| 963 | drawbar(m); | ||
| 964 | } | ||
| 965 | |||
| 966 | void enternotify(XEvent *e) { | ||
| 967 | Client *c; | ||
| 968 | Monitor *m; | ||
| 969 | XCrossingEvent *ev = &e->xcrossing; | ||
| 970 | |||
| 971 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && | ||
| 972 | ev->window != root) | ||
| 973 | return; | ||
| 974 | c = wintoclient(ev->window); | ||
| 975 | m = c ? c->mon : wintomon(ev->window); | ||
| 976 | if (m != selmon) { | ||
| 977 | unfocus(selmon->sel, 1); | ||
| 978 | selmon = m; | ||
| 979 | } else if (!c || c == selmon->sel) | ||
| 980 | return; | ||
| 981 | focus(c); | ||
| 982 | } | ||
| 983 | |||
| 984 | void expose(XEvent *e) { | ||
| 985 | Monitor *m; | ||
| 986 | XExposeEvent *ev = &e->xexpose; | ||
| 987 | |||
| 988 | if (ev->count == 0 && (m = wintomon(ev->window))) { | ||
| 989 | drawbar(m); | ||
| 990 | if (m == selmon) | ||
| 991 | updatesystray(); | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | void focus(Client *c) { | ||
| 996 | if (!c || !ISVISIBLE(c)) | ||
| 997 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext) | ||
| 998 | ; | ||
| 999 | if (selmon->sel && selmon->sel != c) | ||
| 1000 | unfocus(selmon->sel, 0); | ||
| 1001 | if (c) { | ||
| 1002 | if (c->mon != selmon) | ||
| 1003 | selmon = c->mon; | ||
| 1004 | if (c->isurgent) | ||
| 1005 | seturgent(c, 0); | ||
| 1006 | detachstack(c); | ||
| 1007 | attachstack(c); | ||
| 1008 | grabbuttons(c, 1); | ||
| 1009 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); | ||
| 1010 | setfocus(c); | ||
| 1011 | } else { | ||
| 1012 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | ||
| 1013 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 1014 | } | ||
| 1015 | selmon->sel = c; | ||
| 1016 | drawbars(); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | /* there are some broken focus acquiring clients needing extra handling */ | ||
| 1020 | void focusin(XEvent *e) { | ||
| 1021 | XFocusChangeEvent *ev = &e->xfocus; | ||
| 1022 | |||
| 1023 | if (selmon->sel && ev->window != selmon->sel->win) | ||
| 1024 | setfocus(selmon->sel); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | void focusmon(const Arg *arg) { | ||
| 1028 | Monitor *m; | ||
| 1029 | |||
| 1030 | if (!mons->next) | ||
| 1031 | return; | ||
| 1032 | if ((m = dirtomon(arg->i)) == selmon) | ||
| 1033 | return; | ||
| 1034 | unfocus(selmon->sel, 0); | ||
| 1035 | selmon = m; | ||
| 1036 | focus(NULL); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | void focusstack(const Arg *arg) { | ||
| 1040 | Client *c = NULL, *i; | ||
| 1041 | |||
| 1042 | if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) | ||
| 1043 | return; | ||
| 1044 | if (arg->i > 0) { | ||
| 1045 | for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next) | ||
| 1046 | ; | ||
| 1047 | if (!c) | ||
| 1048 | for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next) | ||
| 1049 | ; | ||
| 1050 | } else { | ||
| 1051 | for (i = selmon->clients; i != selmon->sel; i = i->next) | ||
| 1052 | if (ISVISIBLE(i)) | ||
| 1053 | c = i; | ||
| 1054 | if (!c) | ||
| 1055 | for (; i; i = i->next) | ||
| 1056 | if (ISVISIBLE(i)) | ||
| 1057 | c = i; | ||
| 1058 | } | ||
| 1059 | if (c) { | ||
| 1060 | focus(c); | ||
| 1061 | restack(selmon); | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | Atom getatomprop(Client *c, Atom prop) { | ||
| 1066 | int di; | ||
| 1067 | unsigned long dl; | ||
| 1068 | unsigned char *p = NULL; | ||
| 1069 | Atom da, atom = None; | ||
| 1070 | |||
| 1071 | /* FIXME getatomprop should return the number of items and a pointer to | ||
| 1072 | * the stored data instead of this workaround */ | ||
| 1073 | Atom req = XA_ATOM; | ||
| 1074 | if (prop == xatom[XembedInfo]) | ||
| 1075 | req = xatom[XembedInfo]; | ||
| 1076 | |||
| 1077 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, &da, | ||
| 1078 | &di, &dl, &dl, &p) == Success && | ||
| 1079 | p) { | ||
| 1080 | atom = *(Atom *)p; | ||
| 1081 | if (da == xatom[XembedInfo] && dl == 2) | ||
| 1082 | atom = ((Atom *)p)[1]; | ||
| 1083 | XFree(p); | ||
| 1084 | } | ||
| 1085 | return atom; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | unsigned int getsystraywidth() { | ||
| 1089 | unsigned int w = 0; | ||
| 1090 | Client *i; | ||
| 1091 | if (showsystray) | ||
| 1092 | for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next) | ||
| 1093 | ; | ||
| 1094 | return w ? w + systrayspacing : 1; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | int getrootptr(int *x, int *y) { | ||
| 1098 | int di; | ||
| 1099 | unsigned int dui; | ||
| 1100 | Window dummy; | ||
| 1101 | |||
| 1102 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | long getstate(Window w) { | ||
| 1106 | int format; | ||
| 1107 | long result = -1; | ||
| 1108 | unsigned char *p = NULL; | ||
| 1109 | unsigned long n, extra; | ||
| 1110 | Atom real; | ||
| 1111 | |||
| 1112 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, | ||
| 1113 | wmatom[WMState], &real, &format, &n, &extra, | ||
| 1114 | (unsigned char **)&p) != Success) | ||
| 1115 | return -1; | ||
| 1116 | if (n != 0) | ||
| 1117 | result = *p; | ||
| 1118 | XFree(p); | ||
| 1119 | return result; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | int gettextprop(Window w, Atom atom, char *text, unsigned int size) { | ||
| 1123 | char **list = NULL; | ||
| 1124 | int n; | ||
| 1125 | XTextProperty name; | ||
| 1126 | |||
| 1127 | if (!text || size == 0) | ||
| 1128 | return 0; | ||
| 1129 | text[0] = '\0'; | ||
| 1130 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) | ||
| 1131 | return 0; | ||
| 1132 | if (name.encoding == XA_STRING) { | ||
| 1133 | strncpy(text, (char *)name.value, size - 1); | ||
| 1134 | } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && | ||
| 1135 | n > 0 && *list) { | ||
| 1136 | strncpy(text, *list, size - 1); | ||
| 1137 | XFreeStringList(list); | ||
| 1138 | } | ||
| 1139 | text[size - 1] = '\0'; | ||
| 1140 | XFree(name.value); | ||
| 1141 | return 1; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | void grabbuttons(Client *c, int focused) { | ||
| 1145 | updatenumlockmask(); | ||
| 1146 | { | ||
| 1147 | unsigned int i, j; | ||
| 1148 | unsigned int modifiers[] = {0, LockMask, numlockmask, | ||
| 1149 | numlockmask | LockMask}; | ||
| 1150 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | ||
| 1151 | if (!focused) | ||
| 1152 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, | ||
| 1153 | GrabModeSync, GrabModeSync, None, None); | ||
| 1154 | for (i = 0; i < LENGTH(buttons); i++) | ||
| 1155 | if (buttons[i].click == ClkClientWin) | ||
| 1156 | for (j = 0; j < LENGTH(modifiers); j++) | ||
| 1157 | XGrabButton(dpy, buttons[i].button, buttons[i].mask | modifiers[j], | ||
| 1158 | c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, | ||
| 1159 | None, None); | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | void grabkeys(void) { | ||
| 1164 | updatenumlockmask(); | ||
| 1165 | { | ||
| 1166 | unsigned int i, j, k; | ||
| 1167 | unsigned int modifiers[] = {0, LockMask, numlockmask, | ||
| 1168 | numlockmask | LockMask}; | ||
| 1169 | int start, end, skip; | ||
| 1170 | KeySym *syms; | ||
| 1171 | |||
| 1172 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
| 1173 | XDisplayKeycodes(dpy, &start, &end); | ||
| 1174 | syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); | ||
| 1175 | if (!syms) | ||
| 1176 | return; | ||
| 1177 | for (k = start; k <= end; k++) | ||
| 1178 | for (i = 0; i < LENGTH(keys); i++) | ||
| 1179 | /* skip modifier codes, we do that ourselves */ | ||
| 1180 | if (keys[i].keysym == syms[(k - start) * skip]) | ||
| 1181 | for (j = 0; j < LENGTH(modifiers); j++) | ||
| 1182 | XGrabKey(dpy, k, keys[i].mod | modifiers[j], root, True, | ||
| 1183 | GrabModeAsync, GrabModeAsync); | ||
| 1184 | XFree(syms); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | void incnmaster(const Arg *arg) { | ||
| 1189 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); | ||
| 1190 | arrange(selmon); | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | #ifdef XINERAMA | ||
| 1194 | static int isuniquegeom(XineramaScreenInfo *unique, size_t n, | ||
| 1195 | XineramaScreenInfo *info) { | ||
| 1196 | while (n--) | ||
| 1197 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org && | ||
| 1198 | unique[n].width == info->width && unique[n].height == info->height) | ||
| 1199 | return 0; | ||
| 1200 | return 1; | ||
| 1201 | } | ||
| 1202 | #endif /* XINERAMA */ | ||
| 1203 | |||
| 1204 | void keypress(XEvent *e) { | ||
| 1205 | unsigned int i; | ||
| 1206 | KeySym keysym; | ||
| 1207 | XKeyEvent *ev; | ||
| 1208 | |||
| 1209 | ev = &e->xkey; | ||
| 1210 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); | ||
| 1211 | for (i = 0; i < LENGTH(keys); i++) | ||
| 1212 | if (keysym == keys[i].keysym && | ||
| 1213 | CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].func) | ||
| 1214 | keys[i].func(&(keys[i].arg)); | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | void killclient(const Arg *arg) { | ||
| 1218 | if (!selmon->sel) | ||
| 1219 | return; | ||
| 1220 | |||
| 1221 | if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, | ||
| 1222 | wmatom[WMDelete], CurrentTime, 0, 0, 0)) { | ||
| 1223 | XGrabServer(dpy); | ||
| 1224 | XSetErrorHandler(xerrordummy); | ||
| 1225 | XSetCloseDownMode(dpy, DestroyAll); | ||
| 1226 | XKillClient(dpy, selmon->sel->win); | ||
| 1227 | XSync(dpy, False); | ||
| 1228 | XSetErrorHandler(xerror); | ||
| 1229 | XUngrabServer(dpy); | ||
| 1230 | } | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | void manage(Window w, XWindowAttributes *wa) { | ||
| 1234 | Client *c, *t = NULL, *term = NULL; | ||
| 1235 | Window trans = None; | ||
| 1236 | XWindowChanges wc; | ||
| 1237 | |||
| 1238 | c = ecalloc(1, sizeof(Client)); | ||
| 1239 | c->win = w; | ||
| 1240 | c->pid = winpid(w); | ||
| 1241 | /* geometry */ | ||
| 1242 | c->x = c->oldx = wa->x; | ||
| 1243 | c->y = c->oldy = wa->y; | ||
| 1244 | c->w = c->oldw = wa->width; | ||
| 1245 | c->h = c->oldh = wa->height; | ||
| 1246 | c->oldbw = wa->border_width; | ||
| 1247 | |||
| 1248 | updatetitle(c); | ||
| 1249 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { | ||
| 1250 | c->mon = t->mon; | ||
| 1251 | c->tags = t->tags; | ||
| 1252 | } else { | ||
| 1253 | c->mon = selmon; | ||
| 1254 | applyrules(c); | ||
| 1255 | term = termforwin(c); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) | ||
| 1259 | c->x = c->mon->wx + c->mon->ww - WIDTH(c); | ||
| 1260 | if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) | ||
| 1261 | c->y = c->mon->wy + c->mon->wh - HEIGHT(c); | ||
| 1262 | c->x = MAX(c->x, c->mon->wx); | ||
| 1263 | c->y = MAX(c->y, c->mon->wy); | ||
| 1264 | c->bw = borderpx; | ||
| 1265 | |||
| 1266 | wc.border_width = c->bw; | ||
| 1267 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); | ||
| 1268 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); | ||
| 1269 | configure(c); /* propagates border_width, if size doesn't change */ | ||
| 1270 | updatewindowtype(c); | ||
| 1271 | updatesizehints(c); | ||
| 1272 | updatewmhints(c); | ||
| 1273 | XSelectInput(dpy, w, | ||
| 1274 | EnterWindowMask | FocusChangeMask | PropertyChangeMask | | ||
| 1275 | StructureNotifyMask); | ||
| 1276 | grabbuttons(c, 0); | ||
| 1277 | if (!c->isfloating) | ||
| 1278 | c->isfloating = c->oldstate = trans != None || c->isfixed; | ||
| 1279 | if (c->isfloating) | ||
| 1280 | XRaiseWindow(dpy, c->win); | ||
| 1281 | attach(c); | ||
| 1282 | attachstack(c); | ||
| 1283 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, | ||
| 1284 | PropModeAppend, (unsigned char *)&(c->win), 1); | ||
| 1285 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, | ||
| 1286 | c->h); /* some windows require this */ | ||
| 1287 | setclientstate(c, NormalState); | ||
| 1288 | if (c->mon == selmon) | ||
| 1289 | unfocus(selmon->sel, 0); | ||
| 1290 | c->mon->sel = c; | ||
| 1291 | arrange(c->mon); | ||
| 1292 | XMapWindow(dpy, c->win); | ||
| 1293 | if (term) | ||
| 1294 | swallow(term, c); | ||
| 1295 | focus(NULL); | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | void mappingnotify(XEvent *e) { | ||
| 1299 | XMappingEvent *ev = &e->xmapping; | ||
| 1300 | |||
| 1301 | XRefreshKeyboardMapping(ev); | ||
| 1302 | if (ev->request == MappingKeyboard) | ||
| 1303 | grabkeys(); | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | void maprequest(XEvent *e) { | ||
| 1307 | static XWindowAttributes wa; | ||
| 1308 | XMapRequestEvent *ev = &e->xmaprequest; | ||
| 1309 | |||
| 1310 | Client *i; | ||
| 1311 | if ((i = wintosystrayicon(ev->window))) { | ||
| 1312 | sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, | ||
| 1313 | XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); | ||
| 1314 | resizebarwin(selmon); | ||
| 1315 | updatesystray(); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) | ||
| 1319 | return; | ||
| 1320 | if (!wintoclient(ev->window)) | ||
| 1321 | manage(ev->window, &wa); | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | void monocle(Monitor *m) { | ||
| 1325 | unsigned int n = 0; | ||
| 1326 | Client *c; | ||
| 1327 | |||
| 1328 | for (c = m->clients; c; c = c->next) | ||
| 1329 | if (ISVISIBLE(c)) | ||
| 1330 | n++; | ||
| 1331 | if (n > 0) /* override layout symbol */ | ||
| 1332 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); | ||
| 1333 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) | ||
| 1334 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | void motionnotify(XEvent *e) { | ||
| 1338 | static Monitor *mon = NULL; | ||
| 1339 | Monitor *m; | ||
| 1340 | XMotionEvent *ev = &e->xmotion; | ||
| 1341 | |||
| 1342 | if (ev->window != root) | ||
| 1343 | return; | ||
| 1344 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { | ||
| 1345 | unfocus(selmon->sel, 1); | ||
| 1346 | selmon = m; | ||
| 1347 | focus(NULL); | ||
| 1348 | } | ||
| 1349 | mon = m; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | void movemouse(const Arg *arg) { | ||
| 1353 | int x, y, ocx, ocy, nx, ny; | ||
| 1354 | Client *c; | ||
| 1355 | Monitor *m; | ||
| 1356 | XEvent ev; | ||
| 1357 | Time lasttime = 0; | ||
| 1358 | |||
| 1359 | if (!(c = selmon->sel)) | ||
| 1360 | return; | ||
| 1361 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ | ||
| 1362 | return; | ||
| 1363 | restack(selmon); | ||
| 1364 | ocx = c->x; | ||
| 1365 | ocy = c->y; | ||
| 1366 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | ||
| 1367 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | ||
| 1368 | return; | ||
| 1369 | if (!getrootptr(&x, &y)) | ||
| 1370 | return; | ||
| 1371 | do { | ||
| 1372 | XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); | ||
| 1373 | switch (ev.type) { | ||
| 1374 | case ConfigureRequest: | ||
| 1375 | case Expose: | ||
| 1376 | case MapRequest: | ||
| 1377 | handler[ev.type](&ev); | ||
| 1378 | break; | ||
| 1379 | case MotionNotify: | ||
| 1380 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | ||
| 1381 | continue; | ||
| 1382 | lasttime = ev.xmotion.time; | ||
| 1383 | |||
| 1384 | nx = ocx + (ev.xmotion.x - x); | ||
| 1385 | ny = ocy + (ev.xmotion.y - y); | ||
| 1386 | if (abs(selmon->wx - nx) < snap) | ||
| 1387 | nx = selmon->wx; | ||
| 1388 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) | ||
| 1389 | nx = selmon->wx + selmon->ww - WIDTH(c); | ||
| 1390 | if (abs(selmon->wy - ny) < snap) | ||
| 1391 | ny = selmon->wy; | ||
| 1392 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) | ||
| 1393 | ny = selmon->wy + selmon->wh - HEIGHT(c); | ||
| 1394 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange && | ||
| 1395 | (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) | ||
| 1396 | togglefloating(NULL); | ||
| 1397 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) | ||
| 1398 | resize(c, nx, ny, c->w, c->h, 1); | ||
| 1399 | break; | ||
| 1400 | } | ||
| 1401 | } while (ev.type != ButtonRelease); | ||
| 1402 | XUngrabPointer(dpy, CurrentTime); | ||
| 1403 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { | ||
| 1404 | sendmon(c, m); | ||
| 1405 | selmon = m; | ||
| 1406 | focus(NULL); | ||
| 1407 | } | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | Client *nexttiled(Client *c) { | ||
| 1411 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next) | ||
| 1412 | ; | ||
| 1413 | return c; | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | void pop(Client *c) { | ||
| 1417 | detach(c); | ||
| 1418 | attach(c); | ||
| 1419 | focus(c); | ||
| 1420 | arrange(c->mon); | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | void propertynotify(XEvent *e) { | ||
| 1424 | Client *c; | ||
| 1425 | Window trans; | ||
| 1426 | XPropertyEvent *ev = &e->xproperty; | ||
| 1427 | |||
| 1428 | if ((c = wintosystrayicon(ev->window))) { | ||
| 1429 | if (ev->atom == XA_WM_NORMAL_HINTS) { | ||
| 1430 | updatesizehints(c); | ||
| 1431 | updatesystrayicongeom(c, c->w, c->h); | ||
| 1432 | } else | ||
| 1433 | updatesystrayiconstate(c, ev); | ||
| 1434 | resizebarwin(selmon); | ||
| 1435 | updatesystray(); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) | ||
| 1439 | updatestatus(); | ||
| 1440 | else if (ev->state == PropertyDelete) | ||
| 1441 | return; /* ignore */ | ||
| 1442 | else if ((c = wintoclient(ev->window))) { | ||
| 1443 | switch (ev->atom) { | ||
| 1444 | default: | ||
| 1445 | break; | ||
| 1446 | case XA_WM_TRANSIENT_FOR: | ||
| 1447 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && | ||
| 1448 | (c->isfloating = (wintoclient(trans)) != NULL)) | ||
| 1449 | arrange(c->mon); | ||
| 1450 | break; | ||
| 1451 | case XA_WM_NORMAL_HINTS: | ||
| 1452 | c->hintsvalid = 0; | ||
| 1453 | break; | ||
| 1454 | case XA_WM_HINTS: | ||
| 1455 | updatewmhints(c); | ||
| 1456 | drawbars(); | ||
| 1457 | break; | ||
| 1458 | } | ||
| 1459 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { | ||
| 1460 | updatetitle(c); | ||
| 1461 | if (c == c->mon->sel) | ||
| 1462 | drawbar(c->mon); | ||
| 1463 | } | ||
| 1464 | if (ev->atom == netatom[NetWMWindowType]) | ||
| 1465 | updatewindowtype(c); | ||
| 1466 | } | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | void quit(const Arg *arg) { running = 0; } | ||
| 1470 | |||
| 1471 | Monitor *recttomon(int x, int y, int w, int h) { | ||
| 1472 | Monitor *m, *r = selmon; | ||
| 1473 | int a, area = 0; | ||
| 1474 | |||
| 1475 | for (m = mons; m; m = m->next) | ||
| 1476 | if ((a = INTERSECT(x, y, w, h, m)) > area) { | ||
| 1477 | area = a; | ||
| 1478 | r = m; | ||
| 1479 | } | ||
| 1480 | return r; | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | void removesystrayicon(Client *i) { | ||
| 1484 | Client **ii; | ||
| 1485 | |||
| 1486 | if (!showsystray || !i) | ||
| 1487 | return; | ||
| 1488 | for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next) | ||
| 1489 | ; | ||
| 1490 | if (ii) | ||
| 1491 | *ii = i->next; | ||
| 1492 | free(i); | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | void resize(Client *c, int x, int y, int w, int h, int interact) { | ||
| 1496 | if (applysizehints(c, &x, &y, &w, &h, interact)) | ||
| 1497 | resizeclient(c, x, y, w, h); | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | void resizebarwin(Monitor *m) { | ||
| 1501 | unsigned int w = m->ww; | ||
| 1502 | if (showsystray && m == systraytomon(m) && !systrayonleft) | ||
| 1503 | w -= getsystraywidth(); | ||
| 1504 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | void resizeclient(Client *c, int x, int y, int w, int h) { | ||
| 1508 | XWindowChanges wc; | ||
| 1509 | |||
| 1510 | c->oldx = c->x; | ||
| 1511 | c->x = wc.x = x; | ||
| 1512 | c->oldy = c->y; | ||
| 1513 | c->y = wc.y = y; | ||
| 1514 | c->oldw = c->w; | ||
| 1515 | c->w = wc.width = w; | ||
| 1516 | c->oldh = c->h; | ||
| 1517 | c->h = wc.height = h; | ||
| 1518 | wc.border_width = c->bw; | ||
| 1519 | XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, | ||
| 1520 | &wc); | ||
| 1521 | configure(c); | ||
| 1522 | XSync(dpy, False); | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | void resizerequest(XEvent *e) { | ||
| 1526 | XResizeRequestEvent *ev = &e->xresizerequest; | ||
| 1527 | Client *i; | ||
| 1528 | |||
| 1529 | if ((i = wintosystrayicon(ev->window))) { | ||
| 1530 | updatesystrayicongeom(i, ev->width, ev->height); | ||
| 1531 | resizebarwin(selmon); | ||
| 1532 | updatesystray(); | ||
| 1533 | } | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | void resizemouse(const Arg *arg) { | ||
| 1537 | int ocx, ocy, nw, nh; | ||
| 1538 | Client *c; | ||
| 1539 | Monitor *m; | ||
| 1540 | XEvent ev; | ||
| 1541 | Time lasttime = 0; | ||
| 1542 | |||
| 1543 | if (!(c = selmon->sel)) | ||
| 1544 | return; | ||
| 1545 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | ||
| 1546 | return; | ||
| 1547 | restack(selmon); | ||
| 1548 | ocx = c->x; | ||
| 1549 | ocy = c->y; | ||
| 1550 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | ||
| 1551 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | ||
| 1552 | return; | ||
| 1553 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, | ||
| 1554 | c->h + c->bw - 1); | ||
| 1555 | do { | ||
| 1556 | XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); | ||
| 1557 | switch (ev.type) { | ||
| 1558 | case ConfigureRequest: | ||
| 1559 | case Expose: | ||
| 1560 | case MapRequest: | ||
| 1561 | handler[ev.type](&ev); | ||
| 1562 | break; | ||
| 1563 | case MotionNotify: | ||
| 1564 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | ||
| 1565 | continue; | ||
| 1566 | lasttime = ev.xmotion.time; | ||
| 1567 | |||
| 1568 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); | ||
| 1569 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); | ||
| 1570 | if (c->mon->wx + nw >= selmon->wx && | ||
| 1571 | c->mon->wx + nw <= selmon->wx + selmon->ww && | ||
| 1572 | c->mon->wy + nh >= selmon->wy && | ||
| 1573 | c->mon->wy + nh <= selmon->wy + selmon->wh) { | ||
| 1574 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange && | ||
| 1575 | (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) | ||
| 1576 | togglefloating(NULL); | ||
| 1577 | } | ||
| 1578 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) | ||
| 1579 | resize(c, c->x, c->y, nw, nh, 1); | ||
| 1580 | break; | ||
| 1581 | } | ||
| 1582 | } while (ev.type != ButtonRelease); | ||
| 1583 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, | ||
| 1584 | c->h + c->bw - 1); | ||
| 1585 | XUngrabPointer(dpy, CurrentTime); | ||
| 1586 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) | ||
| 1587 | ; | ||
| 1588 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { | ||
| 1589 | sendmon(c, m); | ||
| 1590 | selmon = m; | ||
| 1591 | focus(NULL); | ||
| 1592 | } | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | void restack(Monitor *m) { | ||
| 1596 | Client *c; | ||
| 1597 | XEvent ev; | ||
| 1598 | XWindowChanges wc; | ||
| 1599 | |||
| 1600 | drawbar(m); | ||
| 1601 | if (!m->sel) | ||
| 1602 | return; | ||
| 1603 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) | ||
| 1604 | XRaiseWindow(dpy, m->sel->win); | ||
| 1605 | if (m->lt[m->sellt]->arrange) { | ||
| 1606 | wc.stack_mode = Below; | ||
| 1607 | wc.sibling = m->barwin; | ||
| 1608 | for (c = m->stack; c; c = c->snext) | ||
| 1609 | if (!c->isfloating && ISVISIBLE(c)) { | ||
| 1610 | XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); | ||
| 1611 | wc.sibling = c->win; | ||
| 1612 | } | ||
| 1613 | } | ||
| 1614 | XSync(dpy, False); | ||
| 1615 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) | ||
| 1616 | ; | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | void run(void) { | ||
| 1620 | XEvent ev; | ||
| 1621 | /* main event loop */ | ||
| 1622 | XSync(dpy, False); | ||
| 1623 | while (running && !XNextEvent(dpy, &ev)) | ||
| 1624 | if (handler[ev.type]) | ||
| 1625 | handler[ev.type](&ev); /* call handler */ | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | void scan(void) { | ||
| 1629 | unsigned int i, num; | ||
| 1630 | Window d1, d2, *wins = NULL; | ||
| 1631 | XWindowAttributes wa; | ||
| 1632 | |||
| 1633 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { | ||
| 1634 | for (i = 0; i < num; i++) { | ||
| 1635 | if (!XGetWindowAttributes(dpy, wins[i], &wa) || wa.override_redirect || | ||
| 1636 | XGetTransientForHint(dpy, wins[i], &d1)) | ||
| 1637 | continue; | ||
| 1638 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) | ||
| 1639 | manage(wins[i], &wa); | ||
| 1640 | } | ||
| 1641 | for (i = 0; i < num; i++) { /* now the transients */ | ||
| 1642 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) | ||
| 1643 | continue; | ||
| 1644 | if (XGetTransientForHint(dpy, wins[i], &d1) && | ||
| 1645 | (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) | ||
| 1646 | manage(wins[i], &wa); | ||
| 1647 | } | ||
| 1648 | if (wins) | ||
| 1649 | XFree(wins); | ||
| 1650 | } | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | void sendmon(Client *c, Monitor *m) { | ||
| 1654 | if (c->mon == m) | ||
| 1655 | return; | ||
| 1656 | unfocus(c, 1); | ||
| 1657 | detach(c); | ||
| 1658 | detachstack(c); | ||
| 1659 | c->mon = m; | ||
| 1660 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ | ||
| 1661 | attach(c); | ||
| 1662 | attachstack(c); | ||
| 1663 | focus(NULL); | ||
| 1664 | arrange(NULL); | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | void setclientstate(Client *c, long state) { | ||
| 1668 | long data[] = {state, None}; | ||
| 1669 | |||
| 1670 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, | ||
| 1671 | PropModeReplace, (unsigned char *)data, 2); | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | int sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, | ||
| 1675 | long d3, long d4) { | ||
| 1676 | int n; | ||
| 1677 | Atom *protocols, mt; | ||
| 1678 | int exists = 0; | ||
| 1679 | XEvent ev; | ||
| 1680 | |||
| 1681 | if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { | ||
| 1682 | mt = wmatom[WMProtocols]; | ||
| 1683 | if (XGetWMProtocols(dpy, w, &protocols, &n)) { | ||
| 1684 | while (!exists && n--) | ||
| 1685 | exists = protocols[n] == proto; | ||
| 1686 | XFree(protocols); | ||
| 1687 | } | ||
| 1688 | } else { | ||
| 1689 | exists = True; | ||
| 1690 | mt = proto; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | if (exists) { | ||
| 1694 | ev.type = ClientMessage; | ||
| 1695 | ev.xclient.window = w; | ||
| 1696 | ev.xclient.message_type = mt; | ||
| 1697 | ev.xclient.format = 32; | ||
| 1698 | ev.xclient.data.l[0] = d0; | ||
| 1699 | ev.xclient.data.l[1] = d1; | ||
| 1700 | ev.xclient.data.l[2] = d2; | ||
| 1701 | ev.xclient.data.l[3] = d3; | ||
| 1702 | ev.xclient.data.l[4] = d4; | ||
| 1703 | XSendEvent(dpy, w, False, mask, &ev); | ||
| 1704 | } | ||
| 1705 | return exists; | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | void setfocus(Client *c) { | ||
| 1709 | if (!c->neverfocus) { | ||
| 1710 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | ||
| 1711 | XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, | ||
| 1712 | PropModeReplace, (unsigned char *)&(c->win), 1); | ||
| 1713 | } | ||
| 1714 | sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], | ||
| 1715 | CurrentTime, 0, 0, 0); | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | void setfullscreen(Client *c, int fullscreen) { | ||
| 1719 | if (fullscreen && !c->isfullscreen) { | ||
| 1720 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, | ||
| 1721 | PropModeReplace, (unsigned char *)&netatom[NetWMFullscreen], | ||
| 1722 | 1); | ||
| 1723 | c->isfullscreen = 1; | ||
| 1724 | c->oldstate = c->isfloating; | ||
| 1725 | c->oldbw = c->bw; | ||
| 1726 | c->bw = 0; | ||
| 1727 | c->isfloating = 1; | ||
| 1728 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); | ||
| 1729 | XRaiseWindow(dpy, c->win); | ||
| 1730 | } else if (!fullscreen && c->isfullscreen) { | ||
| 1731 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, | ||
| 1732 | PropModeReplace, (unsigned char *)0, 0); | ||
| 1733 | c->isfullscreen = 0; | ||
| 1734 | c->isfloating = c->oldstate; | ||
| 1735 | c->bw = c->oldbw; | ||
| 1736 | c->x = c->oldx; | ||
| 1737 | c->y = c->oldy; | ||
| 1738 | c->w = c->oldw; | ||
| 1739 | c->h = c->oldh; | ||
| 1740 | resizeclient(c, c->x, c->y, c->w, c->h); | ||
| 1741 | arrange(c->mon); | ||
| 1742 | } | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | void setgaps(int oh, int ov, int ih, int iv) { | ||
| 1746 | if (oh < 0) | ||
| 1747 | oh = 0; | ||
| 1748 | if (ov < 0) | ||
| 1749 | ov = 0; | ||
| 1750 | if (ih < 0) | ||
| 1751 | ih = 0; | ||
| 1752 | if (iv < 0) | ||
| 1753 | iv = 0; | ||
| 1754 | |||
| 1755 | selmon->gappoh = oh; | ||
| 1756 | selmon->gappov = ov; | ||
| 1757 | selmon->gappih = ih; | ||
| 1758 | selmon->gappiv = iv; | ||
| 1759 | arrange(selmon); | ||
| 1760 | } | ||
| 1761 | |||
| 1762 | void togglegaps(const Arg *arg) { | ||
| 1763 | enablegaps = !enablegaps; | ||
| 1764 | arrange(selmon); | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | void defaultgaps(const Arg *arg) { setgaps(gappoh, gappov, gappih, gappiv); } | ||
| 1768 | |||
| 1769 | void incrgaps(const Arg *arg) { | ||
| 1770 | setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, | ||
| 1771 | selmon->gappih + arg->i, selmon->gappiv + arg->i); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | void incrigaps(const Arg *arg) { | ||
| 1775 | setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, | ||
| 1776 | selmon->gappiv + arg->i); | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | void incrogaps(const Arg *arg) { | ||
| 1780 | setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih, | ||
| 1781 | selmon->gappiv); | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | void incrohgaps(const Arg *arg) { | ||
| 1785 | setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih, | ||
| 1786 | selmon->gappiv); | ||
| 1787 | } | ||
| 1788 | |||
| 1789 | void incrovgaps(const Arg *arg) { | ||
| 1790 | setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih, | ||
| 1791 | selmon->gappiv); | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | void incrihgaps(const Arg *arg) { | ||
| 1795 | setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, | ||
| 1796 | selmon->gappiv); | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | void incrivgaps(const Arg *arg) { | ||
| 1800 | setgaps(selmon->gappoh, selmon->gappov, selmon->gappih, | ||
| 1801 | selmon->gappiv + arg->i); | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | void setlayout(const Arg *arg) { | ||
| 1805 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) | ||
| 1806 | selmon->sellt ^= 1; | ||
| 1807 | if (arg && arg->v) | ||
| 1808 | selmon->lt[selmon->sellt] = (Layout *)arg->v; | ||
| 1809 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, | ||
| 1810 | sizeof selmon->ltsymbol); | ||
| 1811 | if (selmon->sel) | ||
| 1812 | arrange(selmon); | ||
| 1813 | else | ||
| 1814 | drawbar(selmon); | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | /* arg > 1.0 will set mfact absolutely */ | ||
| 1818 | void setmfact(const Arg *arg) { | ||
| 1819 | float f; | ||
| 1820 | |||
| 1821 | if (!arg || !selmon->lt[selmon->sellt]->arrange) | ||
| 1822 | return; | ||
| 1823 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; | ||
| 1824 | if (f < 0.05 || f > 0.95) | ||
| 1825 | return; | ||
| 1826 | selmon->mfact = f; | ||
| 1827 | arrange(selmon); | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | void setup(void) { | ||
| 1831 | int i; | ||
| 1832 | XSetWindowAttributes wa; | ||
| 1833 | Atom utf8string; | ||
| 1834 | struct sigaction sa; | ||
| 1835 | |||
| 1836 | /* do not transform children into zombies when they terminate */ | ||
| 1837 | sigemptyset(&sa.sa_mask); | ||
| 1838 | sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; | ||
| 1839 | sa.sa_handler = SIG_IGN; | ||
| 1840 | sigaction(SIGCHLD, &sa, NULL); | ||
| 1841 | |||
| 1842 | /* clean up any zombies (inherited from .xinitrc etc) immediately */ | ||
| 1843 | while (waitpid(-1, NULL, WNOHANG) > 0) | ||
| 1844 | ; | ||
| 1845 | |||
| 1846 | /* init screen */ | ||
| 1847 | screen = DefaultScreen(dpy); | ||
| 1848 | sw = DisplayWidth(dpy, screen); | ||
| 1849 | sh = DisplayHeight(dpy, screen); | ||
| 1850 | root = RootWindow(dpy, screen); | ||
| 1851 | drw = drw_create(dpy, screen, root, sw, sh); | ||
| 1852 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) | ||
| 1853 | die("no fonts could be loaded."); | ||
| 1854 | lrpad = drw->fonts->h; | ||
| 1855 | bh = drw->fonts->h + 2; | ||
| 1856 | updategeom(); | ||
| 1857 | /* init atoms */ | ||
| 1858 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); | ||
| 1859 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); | ||
| 1860 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); | ||
| 1861 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); | ||
| 1862 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); | ||
| 1863 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); | ||
| 1864 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); | ||
| 1865 | netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); | ||
| 1866 | netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); | ||
| 1867 | netatom[NetSystemTrayOrientation] = | ||
| 1868 | XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); | ||
| 1869 | netatom[NetSystemTrayOrientationHorz] = | ||
| 1870 | XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); | ||
| 1871 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); | ||
| 1872 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); | ||
| 1873 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); | ||
| 1874 | netatom[NetWMFullscreen] = | ||
| 1875 | XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); | ||
| 1876 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); | ||
| 1877 | netatom[NetWMWindowTypeDialog] = | ||
| 1878 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); | ||
| 1879 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); | ||
| 1880 | xatom[Manager] = XInternAtom(dpy, "MANAGER", False); | ||
| 1881 | xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); | ||
| 1882 | xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); | ||
| 1883 | /* init cursors */ | ||
| 1884 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); | ||
| 1885 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); | ||
| 1886 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); | ||
| 1887 | /* init appearance */ | ||
| 1888 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); | ||
| 1889 | for (i = 0; i < LENGTH(colors); i++) | ||
| 1890 | scheme[i] = drw_scm_create(drw, colors[i], 3); | ||
| 1891 | /* init system tray */ | ||
| 1892 | updatesystray(); | ||
| 1893 | /* init bars */ | ||
| 1894 | updatebars(); | ||
| 1895 | updatestatus(); | ||
| 1896 | /* supporting window for NetWMCheck */ | ||
| 1897 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); | ||
| 1898 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, | ||
| 1899 | PropModeReplace, (unsigned char *)&wmcheckwin, 1); | ||
| 1900 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, | ||
| 1901 | PropModeReplace, (unsigned char *)"dwm", 3); | ||
| 1902 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, | ||
| 1903 | PropModeReplace, (unsigned char *)&wmcheckwin, 1); | ||
| 1904 | /* EWMH support per view */ | ||
| 1905 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, | ||
| 1906 | PropModeReplace, (unsigned char *)netatom, NetLast); | ||
| 1907 | XDeleteProperty(dpy, root, netatom[NetClientList]); | ||
| 1908 | /* select events */ | ||
| 1909 | wa.cursor = cursor[CurNormal]->cursor; | ||
| 1910 | wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | | ||
| 1911 | ButtonPressMask | PointerMotionMask | EnterWindowMask | | ||
| 1912 | LeaveWindowMask | StructureNotifyMask | PropertyChangeMask; | ||
| 1913 | XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); | ||
| 1914 | XSelectInput(dpy, root, wa.event_mask); | ||
| 1915 | grabkeys(); | ||
| 1916 | focus(NULL); | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | void seturgent(Client *c, int urg) { | ||
| 1920 | XWMHints *wmh; | ||
| 1921 | |||
| 1922 | c->isurgent = urg; | ||
| 1923 | if (!(wmh = XGetWMHints(dpy, c->win))) | ||
| 1924 | return; | ||
| 1925 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); | ||
| 1926 | XSetWMHints(dpy, c->win, wmh); | ||
| 1927 | XFree(wmh); | ||
| 1928 | } | ||
| 1929 | |||
| 1930 | void showhide(Client *c) { | ||
| 1931 | if (!c) | ||
| 1932 | return; | ||
| 1933 | if (ISVISIBLE(c)) { | ||
| 1934 | /* show clients top down */ | ||
| 1935 | XMoveWindow(dpy, c->win, c->x, c->y); | ||
| 1936 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && | ||
| 1937 | !c->isfullscreen) | ||
| 1938 | resize(c, c->x, c->y, c->w, c->h, 0); | ||
| 1939 | showhide(c->snext); | ||
| 1940 | } else { | ||
| 1941 | /* hide clients bottom up */ | ||
| 1942 | showhide(c->snext); | ||
| 1943 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | void spawn(const Arg *arg) { | ||
| 1948 | struct sigaction sa; | ||
| 1949 | |||
| 1950 | if (arg->v == dmenucmd) | ||
| 1951 | dmenumon[0] = '0' + selmon->num; | ||
| 1952 | if (fork() == 0) { | ||
| 1953 | if (dpy) | ||
| 1954 | close(ConnectionNumber(dpy)); | ||
| 1955 | setsid(); | ||
| 1956 | |||
| 1957 | sigemptyset(&sa.sa_mask); | ||
| 1958 | sa.sa_flags = 0; | ||
| 1959 | sa.sa_handler = SIG_DFL; | ||
| 1960 | sigaction(SIGCHLD, &sa, NULL); | ||
| 1961 | |||
| 1962 | execvp(((char **)arg->v)[0], (char **)arg->v); | ||
| 1963 | die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); | ||
| 1964 | } | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | void tag(const Arg *arg) { | ||
| 1968 | if (selmon->sel && arg->ui & TAGMASK) { | ||
| 1969 | selmon->sel->tags = arg->ui & TAGMASK; | ||
| 1970 | focus(NULL); | ||
| 1971 | arrange(selmon); | ||
| 1972 | } | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | void tagmon(const Arg *arg) { | ||
| 1976 | if (!selmon->sel || !mons->next) | ||
| 1977 | return; | ||
| 1978 | sendmon(selmon->sel, dirtomon(arg->i)); | ||
| 1979 | } | ||
| 1980 | |||
| 1981 | void tile(Monitor *m) { | ||
| 1982 | unsigned int i, n, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty; | ||
| 1983 | Client *c; | ||
| 1984 | |||
| 1985 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) | ||
| 1986 | ; | ||
| 1987 | if (n == 0) | ||
| 1988 | return; | ||
| 1989 | if (smartgaps == n) { | ||
| 1990 | oe = 0; // outer gaps disabled | ||
| 1991 | } | ||
| 1992 | |||
| 1993 | if (n > m->nmaster) | ||
| 1994 | mw = m->nmaster ? (m->ww + m->gappiv * ie) * m->mfact : 0; | ||
| 1995 | else | ||
| 1996 | mw = m->ww - 2 * m->gappov * oe + m->gappiv * ie; | ||
| 1997 | for (i = 0, my = ty = m->gappoh * oe, c = nexttiled(m->clients); c; | ||
| 1998 | c = nexttiled(c->next), i++) | ||
| 1999 | if (i < m->nmaster) { | ||
| 2000 | r = MIN(n, m->nmaster) - i; | ||
| 2001 | h = (m->wh - my - m->gappoh * oe - m->gappih * ie * (r - 1)) / r; | ||
| 2002 | resize(c, m->wx + m->gappov * oe, m->wy + my, | ||
| 2003 | mw - (2 * c->bw) - m->gappiv * ie, h - (2 * c->bw), 0); | ||
| 2004 | my += HEIGHT(c) + m->gappih * ie; | ||
| 2005 | |||
| 2006 | if (my + HEIGHT(c) < m->wh) | ||
| 2007 | my += HEIGHT(c); | ||
| 2008 | } else { | ||
| 2009 | r = n - i; | ||
| 2010 | h = (m->wh - ty - m->gappoh * oe - m->gappih * ie * (r - 1)) / r; | ||
| 2011 | resize(c, m->wx + mw + m->gappov * oe, m->wy + ty, | ||
| 2012 | m->ww - mw - (2 * c->bw) - 2 * m->gappov * oe, h - (2 * c->bw), 0); | ||
| 2013 | ty += HEIGHT(c) + m->gappih * ie; | ||
| 2014 | } | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | void togglebar(const Arg *arg) { | ||
| 2018 | selmon->showbar = !selmon->showbar; | ||
| 2019 | updatebarpos(selmon); | ||
| 2020 | resizebarwin(selmon); | ||
| 2021 | if (showsystray) { | ||
| 2022 | XWindowChanges wc; | ||
| 2023 | if (!selmon->showbar) | ||
| 2024 | wc.y = -bh; | ||
| 2025 | else if (selmon->showbar) { | ||
| 2026 | wc.y = 0; | ||
| 2027 | if (!selmon->topbar) | ||
| 2028 | wc.y = selmon->mh - bh; | ||
| 2029 | } | ||
| 2030 | XConfigureWindow(dpy, systray->win, CWY, &wc); | ||
| 2031 | } | ||
| 2032 | arrange(selmon); | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | void togglefloating(const Arg *arg) { | ||
| 2036 | if (!selmon->sel) | ||
| 2037 | return; | ||
| 2038 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ | ||
| 2039 | return; | ||
| 2040 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; | ||
| 2041 | if (selmon->sel->isfloating) | ||
| 2042 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->sel->w, | ||
| 2043 | selmon->sel->h, 0); | ||
| 2044 | arrange(selmon); | ||
| 2045 | } | ||
| 2046 | |||
| 2047 | void togglefullscr(const Arg *arg) { | ||
| 2048 | if (selmon->sel) | ||
| 2049 | setfullscreen(selmon->sel, !selmon->sel->isfullscreen); | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | void toggletag(const Arg *arg) { | ||
| 2053 | unsigned int newtags; | ||
| 2054 | |||
| 2055 | if (!selmon->sel) | ||
| 2056 | return; | ||
| 2057 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); | ||
| 2058 | if (newtags) { | ||
| 2059 | selmon->sel->tags = newtags; | ||
| 2060 | focus(NULL); | ||
| 2061 | arrange(selmon); | ||
| 2062 | } | ||
| 2063 | } | ||
| 2064 | |||
| 2065 | void toggleview(const Arg *arg) { | ||
| 2066 | unsigned int newtagset = | ||
| 2067 | selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); | ||
| 2068 | |||
| 2069 | if (newtagset) { | ||
| 2070 | selmon->tagset[selmon->seltags] = newtagset; | ||
| 2071 | focus(NULL); | ||
| 2072 | arrange(selmon); | ||
| 2073 | } | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | void unfocus(Client *c, int setfocus) { | ||
| 2077 | if (!c) | ||
| 2078 | return; | ||
| 2079 | grabbuttons(c, 0); | ||
| 2080 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); | ||
| 2081 | if (setfocus) { | ||
| 2082 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | ||
| 2083 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 2084 | } | ||
| 2085 | } | ||
| 2086 | |||
| 2087 | void unmanage(Client *c, int destroyed) { | ||
| 2088 | Monitor *m = c->mon; | ||
| 2089 | XWindowChanges wc; | ||
| 2090 | |||
| 2091 | if (c->swallowing) { | ||
| 2092 | unswallow(c); | ||
| 2093 | return; | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | Client *s = swallowingclient(c->win); | ||
| 2097 | if (s) { | ||
| 2098 | free(s->swallowing); | ||
| 2099 | s->swallowing = NULL; | ||
| 2100 | arrange(m); | ||
| 2101 | focus(NULL); | ||
| 2102 | return; | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | detach(c); | ||
| 2106 | detachstack(c); | ||
| 2107 | if (!destroyed) { | ||
| 2108 | wc.border_width = c->oldbw; | ||
| 2109 | XGrabServer(dpy); /* avoid race conditions */ | ||
| 2110 | XSetErrorHandler(xerrordummy); | ||
| 2111 | XSelectInput(dpy, c->win, NoEventMask); | ||
| 2112 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ | ||
| 2113 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | ||
| 2114 | setclientstate(c, WithdrawnState); | ||
| 2115 | XSync(dpy, False); | ||
| 2116 | XSetErrorHandler(xerror); | ||
| 2117 | XUngrabServer(dpy); | ||
| 2118 | } | ||
| 2119 | free(c); | ||
| 2120 | |||
| 2121 | if (!s) { | ||
| 2122 | arrange(m); | ||
| 2123 | focus(NULL); | ||
| 2124 | updateclientlist(); | ||
| 2125 | } | ||
| 2126 | } | ||
| 2127 | |||
| 2128 | void unmapnotify(XEvent *e) { | ||
| 2129 | Client *c; | ||
| 2130 | XUnmapEvent *ev = &e->xunmap; | ||
| 2131 | |||
| 2132 | if ((c = wintoclient(ev->window))) { | ||
| 2133 | if (ev->send_event) | ||
| 2134 | setclientstate(c, WithdrawnState); | ||
| 2135 | else | ||
| 2136 | unmanage(c, 0); | ||
| 2137 | } else if ((c = wintosystrayicon(ev->window))) { | ||
| 2138 | /* KLUDGE! sometimes icons occasionally unmap their windows, but do | ||
| 2139 | * _not_ destroy them. We map those windows back */ | ||
| 2140 | XMapRaised(dpy, c->win); | ||
| 2141 | updatesystray(); | ||
| 2142 | } | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | void updatebars(void) { | ||
| 2146 | unsigned int w; | ||
| 2147 | Monitor *m; | ||
| 2148 | XSetWindowAttributes wa = {.override_redirect = True, | ||
| 2149 | .background_pixmap = ParentRelative, | ||
| 2150 | .event_mask = ButtonPressMask | ExposureMask}; | ||
| 2151 | XClassHint ch = {"dwm", "dwm"}; | ||
| 2152 | for (m = mons; m; m = m->next) { | ||
| 2153 | if (m->barwin) | ||
| 2154 | continue; | ||
| 2155 | w = m->ww; | ||
| 2156 | if (showsystray && m == systraytomon(m)) | ||
| 2157 | w -= getsystraywidth(); | ||
| 2158 | m->barwin = XCreateWindow( | ||
| 2159 | dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), | ||
| 2160 | CopyFromParent, DefaultVisual(dpy, screen), | ||
| 2161 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||
| 2162 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); | ||
| 2163 | if (showsystray && m == systraytomon(m)) | ||
| 2164 | XMapRaised(dpy, systray->win); | ||
| 2165 | XMapRaised(dpy, m->barwin); | ||
| 2166 | XSetClassHint(dpy, m->barwin, &ch); | ||
| 2167 | } | ||
| 2168 | } | ||
| 2169 | |||
| 2170 | void updatebarpos(Monitor *m) { | ||
| 2171 | m->wy = m->my; | ||
| 2172 | m->wh = m->mh; | ||
| 2173 | if (m->showbar) { | ||
| 2174 | m->wh -= bh; | ||
| 2175 | m->by = m->topbar ? m->wy : m->wy + m->wh; | ||
| 2176 | m->wy = m->topbar ? m->wy + bh : m->wy; | ||
| 2177 | } else | ||
| 2178 | m->by = -bh; | ||
| 2179 | } | ||
| 2180 | |||
| 2181 | void updateclientlist() { | ||
| 2182 | Client *c; | ||
| 2183 | Monitor *m; | ||
| 2184 | |||
| 2185 | XDeleteProperty(dpy, root, netatom[NetClientList]); | ||
| 2186 | for (m = mons; m; m = m->next) | ||
| 2187 | for (c = m->clients; c; c = c->next) | ||
| 2188 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, | ||
| 2189 | PropModeAppend, (unsigned char *)&(c->win), 1); | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | int updategeom(void) { | ||
| 2193 | int dirty = 0; | ||
| 2194 | |||
| 2195 | #ifdef XINERAMA | ||
| 2196 | if (XineramaIsActive(dpy)) { | ||
| 2197 | int i, j, n, nn; | ||
| 2198 | Client *c; | ||
| 2199 | Monitor *m; | ||
| 2200 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); | ||
| 2201 | XineramaScreenInfo *unique = NULL; | ||
| 2202 | |||
| 2203 | for (n = 0, m = mons; m; m = m->next, n++) | ||
| 2204 | ; | ||
| 2205 | /* only consider unique geometries as separate screens */ | ||
| 2206 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); | ||
| 2207 | for (i = 0, j = 0; i < nn; i++) | ||
| 2208 | if (isuniquegeom(unique, j, &info[i])) | ||
| 2209 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); | ||
| 2210 | XFree(info); | ||
| 2211 | nn = j; | ||
| 2212 | |||
| 2213 | /* new monitors if nn > n */ | ||
| 2214 | for (i = n; i < nn; i++) { | ||
| 2215 | for (m = mons; m && m->next; m = m->next) | ||
| 2216 | ; | ||
| 2217 | if (m) | ||
| 2218 | m->next = createmon(); | ||
| 2219 | else | ||
| 2220 | mons = createmon(); | ||
| 2221 | } | ||
| 2222 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) | ||
| 2223 | if (i >= n || unique[i].x_org != m->mx || unique[i].y_org != m->my || | ||
| 2224 | unique[i].width != m->mw || unique[i].height != m->mh) { | ||
| 2225 | dirty = 1; | ||
| 2226 | m->num = i; | ||
| 2227 | m->mx = m->wx = unique[i].x_org; | ||
| 2228 | m->my = m->wy = unique[i].y_org; | ||
| 2229 | m->mw = m->ww = unique[i].width; | ||
| 2230 | m->mh = m->wh = unique[i].height; | ||
| 2231 | updatebarpos(m); | ||
| 2232 | } | ||
| 2233 | /* removed monitors if n > nn */ | ||
| 2234 | for (i = nn; i < n; i++) { | ||
| 2235 | for (m = mons; m && m->next; m = m->next) | ||
| 2236 | ; | ||
| 2237 | while ((c = m->clients)) { | ||
| 2238 | dirty = 1; | ||
| 2239 | m->clients = c->next; | ||
| 2240 | detachstack(c); | ||
| 2241 | c->mon = mons; | ||
| 2242 | attach(c); | ||
| 2243 | attachstack(c); | ||
| 2244 | } | ||
| 2245 | if (m == selmon) | ||
| 2246 | selmon = mons; | ||
| 2247 | cleanupmon(m); | ||
| 2248 | } | ||
| 2249 | free(unique); | ||
| 2250 | } else | ||
| 2251 | #endif /* XINERAMA */ | ||
| 2252 | { /* default monitor setup */ | ||
| 2253 | if (!mons) | ||
| 2254 | mons = createmon(); | ||
| 2255 | if (mons->mw != sw || mons->mh != sh) { | ||
| 2256 | dirty = 1; | ||
| 2257 | mons->mw = mons->ww = sw; | ||
| 2258 | mons->mh = mons->wh = sh; | ||
| 2259 | updatebarpos(mons); | ||
| 2260 | } | ||
| 2261 | } | ||
| 2262 | if (dirty) { | ||
| 2263 | selmon = mons; | ||
| 2264 | selmon = wintomon(root); | ||
| 2265 | } | ||
| 2266 | return dirty; | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | void updatenumlockmask(void) { | ||
| 2270 | unsigned int i, j; | ||
| 2271 | XModifierKeymap *modmap; | ||
| 2272 | |||
| 2273 | numlockmask = 0; | ||
| 2274 | modmap = XGetModifierMapping(dpy); | ||
| 2275 | for (i = 0; i < 8; i++) | ||
| 2276 | for (j = 0; j < modmap->max_keypermod; j++) | ||
| 2277 | if (modmap->modifiermap[i * modmap->max_keypermod + j] == | ||
| 2278 | XKeysymToKeycode(dpy, XK_Num_Lock)) | ||
| 2279 | numlockmask = (1 << i); | ||
| 2280 | XFreeModifiermap(modmap); | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | void updatesizehints(Client *c) { | ||
| 2284 | long msize; | ||
| 2285 | XSizeHints size; | ||
| 2286 | |||
| 2287 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) | ||
| 2288 | /* size is uninitialized, ensure that size.flags aren't used */ | ||
| 2289 | size.flags = PSize; | ||
| 2290 | if (size.flags & PBaseSize) { | ||
| 2291 | c->basew = size.base_width; | ||
| 2292 | c->baseh = size.base_height; | ||
| 2293 | } else if (size.flags & PMinSize) { | ||
| 2294 | c->basew = size.min_width; | ||
| 2295 | c->baseh = size.min_height; | ||
| 2296 | } else | ||
| 2297 | c->basew = c->baseh = 0; | ||
| 2298 | if (size.flags & PResizeInc) { | ||
| 2299 | c->incw = size.width_inc; | ||
| 2300 | c->inch = size.height_inc; | ||
| 2301 | } else | ||
| 2302 | c->incw = c->inch = 0; | ||
| 2303 | if (size.flags & PMaxSize) { | ||
| 2304 | c->maxw = size.max_width; | ||
| 2305 | c->maxh = size.max_height; | ||
| 2306 | } else | ||
| 2307 | c->maxw = c->maxh = 0; | ||
| 2308 | if (size.flags & PMinSize) { | ||
| 2309 | c->minw = size.min_width; | ||
| 2310 | c->minh = size.min_height; | ||
| 2311 | } else if (size.flags & PBaseSize) { | ||
| 2312 | c->minw = size.base_width; | ||
| 2313 | c->minh = size.base_height; | ||
| 2314 | } else | ||
| 2315 | c->minw = c->minh = 0; | ||
| 2316 | if (size.flags & PAspect) { | ||
| 2317 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; | ||
| 2318 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; | ||
| 2319 | } else | ||
| 2320 | c->maxa = c->mina = 0.0; | ||
| 2321 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); | ||
| 2322 | c->hintsvalid = 1; | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | void updatestatus(void) { | ||
| 2326 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) | ||
| 2327 | strcpy(stext, "dwm-" VERSION); | ||
| 2328 | drawbar(selmon); | ||
| 2329 | updatesystray(); | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | void updatesystrayicongeom(Client *i, int w, int h) { | ||
| 2333 | if (i) { | ||
| 2334 | i->h = bh; | ||
| 2335 | if (w == h) | ||
| 2336 | i->w = bh; | ||
| 2337 | else if (h == bh) | ||
| 2338 | i->w = w; | ||
| 2339 | else | ||
| 2340 | i->w = (int)((float)bh * ((float)w / (float)h)); | ||
| 2341 | applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); | ||
| 2342 | /* force icons into the systray dimensions if they don't want to */ | ||
| 2343 | if (i->h > bh) { | ||
| 2344 | if (i->w == i->h) | ||
| 2345 | i->w = bh; | ||
| 2346 | else | ||
| 2347 | i->w = (int)((float)bh * ((float)i->w / (float)i->h)); | ||
| 2348 | i->h = bh; | ||
| 2349 | } | ||
| 2350 | } | ||
| 2351 | } | ||
| 2352 | |||
| 2353 | void updatesystrayiconstate(Client *i, XPropertyEvent *ev) { | ||
| 2354 | long flags; | ||
| 2355 | int code = 0; | ||
| 2356 | |||
| 2357 | if (!showsystray || !i || ev->atom != xatom[XembedInfo] || | ||
| 2358 | !(flags = getatomprop(i, xatom[XembedInfo]))) | ||
| 2359 | return; | ||
| 2360 | |||
| 2361 | if (flags & XEMBED_MAPPED && !i->tags) { | ||
| 2362 | i->tags = 1; | ||
| 2363 | code = XEMBED_WINDOW_ACTIVATE; | ||
| 2364 | XMapRaised(dpy, i->win); | ||
| 2365 | setclientstate(i, NormalState); | ||
| 2366 | } else if (!(flags & XEMBED_MAPPED) && i->tags) { | ||
| 2367 | i->tags = 0; | ||
| 2368 | code = XEMBED_WINDOW_DEACTIVATE; | ||
| 2369 | XUnmapWindow(dpy, i->win); | ||
| 2370 | setclientstate(i, WithdrawnState); | ||
| 2371 | } else | ||
| 2372 | return; | ||
| 2373 | sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, | ||
| 2374 | systray->win, XEMBED_EMBEDDED_VERSION); | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | void updatesystray(void) { | ||
| 2378 | XSetWindowAttributes wa; | ||
| 2379 | XWindowChanges wc; | ||
| 2380 | Client *i; | ||
| 2381 | Monitor *m = systraytomon(NULL); | ||
| 2382 | unsigned int x = m->mx + m->mw; | ||
| 2383 | unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; | ||
| 2384 | unsigned int w = 1; | ||
| 2385 | |||
| 2386 | if (!showsystray) | ||
| 2387 | return; | ||
| 2388 | if (systrayonleft) | ||
| 2389 | x -= sw + lrpad / 2; | ||
| 2390 | if (!systray) { | ||
| 2391 | /* init systray */ | ||
| 2392 | if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) | ||
| 2393 | die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); | ||
| 2394 | systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, | ||
| 2395 | scheme[SchemeSel][ColBg].pixel); | ||
| 2396 | wa.event_mask = ButtonPressMask | ExposureMask; | ||
| 2397 | wa.override_redirect = True; | ||
| 2398 | wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; | ||
| 2399 | XSelectInput(dpy, systray->win, SubstructureNotifyMask); | ||
| 2400 | XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], | ||
| 2401 | XA_CARDINAL, 32, PropModeReplace, | ||
| 2402 | (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); | ||
| 2403 | XChangeWindowAttributes( | ||
| 2404 | dpy, systray->win, CWEventMask | CWOverrideRedirect | CWBackPixel, &wa); | ||
| 2405 | XMapRaised(dpy, systray->win); | ||
| 2406 | XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); | ||
| 2407 | if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { | ||
| 2408 | sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, | ||
| 2409 | netatom[NetSystemTray], systray->win, 0, 0); | ||
| 2410 | XSync(dpy, False); | ||
| 2411 | } else { | ||
| 2412 | fprintf(stderr, "dwm: unable to obtain system tray.\n"); | ||
| 2413 | free(systray); | ||
| 2414 | systray = NULL; | ||
| 2415 | return; | ||
| 2416 | } | ||
| 2417 | } | ||
| 2418 | for (w = 0, i = systray->icons; i; i = i->next) { | ||
| 2419 | /* make sure the background color stays the same */ | ||
| 2420 | wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; | ||
| 2421 | XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); | ||
| 2422 | XMapRaised(dpy, i->win); | ||
| 2423 | w += systrayspacing; | ||
| 2424 | i->x = w; | ||
| 2425 | XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); | ||
| 2426 | w += i->w; | ||
| 2427 | if (i->mon != m) | ||
| 2428 | i->mon = m; | ||
| 2429 | } | ||
| 2430 | w = w ? w + systrayspacing : 1; | ||
| 2431 | x -= w; | ||
| 2432 | XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); | ||
| 2433 | wc.x = x; | ||
| 2434 | wc.y = m->by; | ||
| 2435 | wc.width = w; | ||
| 2436 | wc.height = bh; | ||
| 2437 | wc.stack_mode = Above; | ||
| 2438 | wc.sibling = m->barwin; | ||
| 2439 | XConfigureWindow(dpy, systray->win, | ||
| 2440 | CWX | CWY | CWWidth | CWHeight | CWSibling | CWStackMode, | ||
| 2441 | &wc); | ||
| 2442 | XMapWindow(dpy, systray->win); | ||
| 2443 | XMapSubwindows(dpy, systray->win); | ||
| 2444 | /* redraw background */ | ||
| 2445 | XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); | ||
| 2446 | XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); | ||
| 2447 | XSync(dpy, False); | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | void updatetitle(Client *c) { | ||
| 2451 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) | ||
| 2452 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); | ||
| 2453 | if (c->name[0] == '\0') /* hack to mark broken clients */ | ||
| 2454 | strcpy(c->name, broken); | ||
| 2455 | } | ||
| 2456 | |||
| 2457 | void updatewindowtype(Client *c) { | ||
| 2458 | Atom state = getatomprop(c, netatom[NetWMState]); | ||
| 2459 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); | ||
| 2460 | |||
| 2461 | if (state == netatom[NetWMFullscreen]) | ||
| 2462 | setfullscreen(c, 1); | ||
| 2463 | if (wtype == netatom[NetWMWindowTypeDialog]) | ||
| 2464 | c->isfloating = 1; | ||
| 2465 | } | ||
| 2466 | |||
| 2467 | void updatewmhints(Client *c) { | ||
| 2468 | XWMHints *wmh; | ||
| 2469 | |||
| 2470 | if ((wmh = XGetWMHints(dpy, c->win))) { | ||
| 2471 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { | ||
| 2472 | wmh->flags &= ~XUrgencyHint; | ||
| 2473 | XSetWMHints(dpy, c->win, wmh); | ||
| 2474 | } else | ||
| 2475 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; | ||
| 2476 | if (wmh->flags & InputHint) | ||
| 2477 | c->neverfocus = !wmh->input; | ||
| 2478 | else | ||
| 2479 | c->neverfocus = 0; | ||
| 2480 | XFree(wmh); | ||
| 2481 | } | ||
| 2482 | } | ||
| 2483 | |||
| 2484 | void view(const Arg *arg) { | ||
| 2485 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | ||
| 2486 | return; | ||
| 2487 | selmon->seltags ^= 1; /* toggle sel tagset */ | ||
| 2488 | if (arg->ui & TAGMASK) | ||
| 2489 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; | ||
| 2490 | focus(NULL); | ||
| 2491 | arrange(selmon); | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | pid_t winpid(Window w) { | ||
| 2495 | |||
| 2496 | pid_t result = 0; | ||
| 2497 | |||
| 2498 | #ifdef __linux__ | ||
| 2499 | xcb_res_client_id_spec_t spec = {0}; | ||
| 2500 | spec.client = w; | ||
| 2501 | spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; | ||
| 2502 | |||
| 2503 | xcb_generic_error_t *e = NULL; | ||
| 2504 | xcb_res_query_client_ids_cookie_t c = | ||
| 2505 | xcb_res_query_client_ids(xcon, 1, &spec); | ||
| 2506 | xcb_res_query_client_ids_reply_t *r = | ||
| 2507 | xcb_res_query_client_ids_reply(xcon, c, &e); | ||
| 2508 | |||
| 2509 | if (!r) | ||
| 2510 | return (pid_t)0; | ||
| 2511 | |||
| 2512 | xcb_res_client_id_value_iterator_t i = | ||
| 2513 | xcb_res_query_client_ids_ids_iterator(r); | ||
| 2514 | for (; i.rem; xcb_res_client_id_value_next(&i)) { | ||
| 2515 | spec = i.data->spec; | ||
| 2516 | if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { | ||
| 2517 | uint32_t *t = xcb_res_client_id_value_value(i.data); | ||
| 2518 | result = *t; | ||
| 2519 | break; | ||
| 2520 | } | ||
| 2521 | } | ||
| 2522 | |||
| 2523 | free(r); | ||
| 2524 | |||
| 2525 | if (result == (pid_t)-1) | ||
| 2526 | result = 0; | ||
| 2527 | |||
| 2528 | #endif /* __linux__ */ | ||
| 2529 | |||
| 2530 | #ifdef __OpenBSD__ | ||
| 2531 | Atom type; | ||
| 2532 | int format; | ||
| 2533 | unsigned long len, bytes; | ||
| 2534 | unsigned char *prop; | ||
| 2535 | pid_t ret; | ||
| 2536 | |||
| 2537 | if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, | ||
| 2538 | False, AnyPropertyType, &type, &format, &len, &bytes, | ||
| 2539 | &prop) != Success || | ||
| 2540 | !prop) | ||
| 2541 | return 0; | ||
| 2542 | |||
| 2543 | ret = *(pid_t *)prop; | ||
| 2544 | XFree(prop); | ||
| 2545 | result = ret; | ||
| 2546 | |||
| 2547 | #endif /* __OpenBSD__ */ | ||
| 2548 | return result; | ||
| 2549 | } | ||
| 2550 | |||
| 2551 | pid_t getparentprocess(pid_t p) { | ||
| 2552 | unsigned int v = 0; | ||
| 2553 | |||
| 2554 | #ifdef __linux__ | ||
| 2555 | FILE *f; | ||
| 2556 | char buf[256]; | ||
| 2557 | snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); | ||
| 2558 | |||
| 2559 | if (!(f = fopen(buf, "r"))) | ||
| 2560 | return 0; | ||
| 2561 | |||
| 2562 | fscanf(f, "%*u %*s %*c %u", &v); | ||
| 2563 | fclose(f); | ||
| 2564 | #endif /* __linux__ */ | ||
| 2565 | |||
| 2566 | #ifdef __OpenBSD__ | ||
| 2567 | int n; | ||
| 2568 | kvm_t *kd; | ||
| 2569 | struct kinfo_proc *kp; | ||
| 2570 | |||
| 2571 | kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); | ||
| 2572 | if (!kd) | ||
| 2573 | return 0; | ||
| 2574 | |||
| 2575 | kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); | ||
| 2576 | v = kp->p_ppid; | ||
| 2577 | #endif /* __OpenBSD__ */ | ||
| 2578 | |||
| 2579 | return (pid_t)v; | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | int isdescprocess(pid_t p, pid_t c) { | ||
| 2583 | while (p != c && c != 0) | ||
| 2584 | c = getparentprocess(c); | ||
| 2585 | |||
| 2586 | return (int)c; | ||
| 2587 | } | ||
| 2588 | |||
| 2589 | Client *termforwin(const Client *w) { | ||
| 2590 | Client *c; | ||
| 2591 | Monitor *m; | ||
| 2592 | |||
| 2593 | if (!w->pid || w->isterminal) | ||
| 2594 | return NULL; | ||
| 2595 | |||
| 2596 | for (m = mons; m; m = m->next) { | ||
| 2597 | for (c = m->clients; c; c = c->next) { | ||
| 2598 | if (c->isterminal && !c->swallowing && c->pid && | ||
| 2599 | isdescprocess(c->pid, w->pid)) | ||
| 2600 | return c; | ||
| 2601 | } | ||
| 2602 | } | ||
| 2603 | |||
| 2604 | return NULL; | ||
| 2605 | } | ||
| 2606 | |||
| 2607 | Client *swallowingclient(Window w) { | ||
| 2608 | Client *c; | ||
| 2609 | Monitor *m; | ||
| 2610 | |||
| 2611 | for (m = mons; m; m = m->next) { | ||
| 2612 | for (c = m->clients; c; c = c->next) { | ||
| 2613 | if (c->swallowing && c->swallowing->win == w) | ||
| 2614 | return c; | ||
| 2615 | } | ||
| 2616 | } | ||
| 2617 | |||
| 2618 | return NULL; | ||
| 2619 | } | ||
| 2620 | |||
| 2621 | Client *wintoclient(Window w) { | ||
| 2622 | Client *c; | ||
| 2623 | Monitor *m; | ||
| 2624 | |||
| 2625 | for (m = mons; m; m = m->next) | ||
| 2626 | for (c = m->clients; c; c = c->next) | ||
| 2627 | if (c->win == w) | ||
| 2628 | return c; | ||
| 2629 | return NULL; | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | Client *wintosystrayicon(Window w) { | ||
| 2633 | Client *i = NULL; | ||
| 2634 | |||
| 2635 | if (!showsystray || !w) | ||
| 2636 | return i; | ||
| 2637 | for (i = systray->icons; i && i->win != w; i = i->next) | ||
| 2638 | ; | ||
| 2639 | return i; | ||
| 2640 | } | ||
| 2641 | |||
| 2642 | Monitor *wintomon(Window w) { | ||
| 2643 | int x, y; | ||
| 2644 | Client *c; | ||
| 2645 | Monitor *m; | ||
| 2646 | |||
| 2647 | if (w == root && getrootptr(&x, &y)) | ||
| 2648 | return recttomon(x, y, 1, 1); | ||
| 2649 | for (m = mons; m; m = m->next) | ||
| 2650 | if (w == m->barwin) | ||
| 2651 | return m; | ||
| 2652 | if ((c = wintoclient(w))) | ||
| 2653 | return c->mon; | ||
| 2654 | return selmon; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | /* There's no way to check accesses to destroyed windows, thus those cases are | ||
| 2658 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs | ||
| 2659 | * default error handler, which may call exit. */ | ||
| 2660 | int xerror(Display *dpy, XErrorEvent *ee) { | ||
| 2661 | if (ee->error_code == BadWindow || | ||
| 2662 | (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) || | ||
| 2663 | (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) || | ||
| 2664 | (ee->request_code == X_PolyFillRectangle && | ||
| 2665 | ee->error_code == BadDrawable) || | ||
| 2666 | (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) || | ||
| 2667 | (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) || | ||
| 2668 | (ee->request_code == X_GrabButton && ee->error_code == BadAccess) || | ||
| 2669 | (ee->request_code == X_GrabKey && ee->error_code == BadAccess) || | ||
| 2670 | (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) | ||
| 2671 | return 0; | ||
| 2672 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", | ||
| 2673 | ee->request_code, ee->error_code); | ||
| 2674 | return xerrorxlib(dpy, ee); /* may call exit */ | ||
| 2675 | } | ||
| 2676 | |||
| 2677 | int xerrordummy(Display *dpy, XErrorEvent *ee) { return 0; } | ||
| 2678 | |||
| 2679 | /* Startup Error handler to check if another window manager | ||
| 2680 | * is already running. */ | ||
| 2681 | int xerrorstart(Display *dpy, XErrorEvent *ee) { | ||
| 2682 | die("dwm: another window manager is already running"); | ||
| 2683 | return -1; | ||
| 2684 | } | ||
| 2685 | |||
| 2686 | Monitor *systraytomon(Monitor *m) { | ||
| 2687 | Monitor *t; | ||
| 2688 | int i, n; | ||
| 2689 | if (!systraypinning) { | ||
| 2690 | if (!m) | ||
| 2691 | return selmon; | ||
| 2692 | return m == selmon ? m : NULL; | ||
| 2693 | } | ||
| 2694 | for (n = 1, t = mons; t && t->next; n++, t = t->next) | ||
| 2695 | ; | ||
| 2696 | for (i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) | ||
| 2697 | ; | ||
| 2698 | if (systraypinningfailfirst && n < systraypinning) | ||
| 2699 | return mons; | ||
| 2700 | return t; | ||
| 2701 | } | ||
| 2702 | |||
| 2703 | void zoom(const Arg *arg) { | ||
| 2704 | Client *c = selmon->sel; | ||
| 2705 | |||
| 2706 | if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) | ||
| 2707 | return; | ||
| 2708 | if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) | ||
| 2709 | return; | ||
| 2710 | pop(c); | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | int main(int argc, char *argv[]) { | ||
| 2714 | if (argc == 2 && !strcmp("-v", argv[1])) | ||
| 2715 | die("dwm-" VERSION); | ||
| 2716 | else if (argc != 1) | ||
| 2717 | die("usage: dwm [-v]"); | ||
| 2718 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 2719 | fputs("warning: no locale support\n", stderr); | ||
| 2720 | if (!(dpy = XOpenDisplay(NULL))) | ||
| 2721 | die("dwm: cannot open display"); | ||
| 2722 | if (!(xcon = XGetXCBConnection(dpy))) | ||
| 2723 | die("dwm: cannot get xcb connection\n"); | ||
| 2724 | checkotherwm(); | ||
| 2725 | setup(); | ||
| 2726 | #ifdef __OpenBSD__ | ||
| 2727 | if (pledge("stdio rpath proc exec ps", NULL) == -1) | ||
| 2728 | die("pledge"); | ||
| 2729 | #endif /* __OpenBSD__ */ | ||
| 2730 | scan(); | ||
| 2731 | run(); | ||
| 2732 | cleanup(); | ||
| 2733 | XCloseDisplay(dpy); | ||
| 2734 | return EXIT_SUCCESS; | ||
| 2735 | } | ||
