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