| 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 | |
| 44 | #include "drw.h" |
| 45 | #include "util.h" |
| 46 | |
| 47 | /* macros */ |
| 48 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) |
| 49 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) |
| 50 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ |
| 51 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) |
| 52 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) |
| 53 | #define LENGTH(X) (sizeof X / sizeof X[0]) |
| 54 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) |
| 55 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) |
| 56 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) |
| 57 | #define TAGMASK ((1 << LENGTH(tags)) - 1) |
| 58 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) |
| 59 | |
| 60 | /* enums */ |
| 61 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ |
| 62 | enum { SchemeNorm, SchemeSel }; /* color schemes */ |
| 63 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, |
| 64 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, |
| 65 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ |
| 66 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ |
| 67 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, |
| 68 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ |
| 69 | |
| 70 | typedef union { |
| 71 | int i; |
| 72 | unsigned int ui; |
| 73 | float f; |
| 74 | const void *v; |
| 75 | } Arg; |
| 76 | |
| 77 | typedef struct { |
| 78 | unsigned int click; |
| 79 | unsigned int mask; |
| 80 | unsigned int button; |
| 81 | void (*func)(const Arg *arg); |
| 82 | const Arg arg; |
| 83 | } Button; |
| 84 | |
| 85 | typedef struct Monitor Monitor; |
| 86 | typedef struct Client Client; |
| 87 | struct Client { |
| 88 | char name[256]; |
| 89 | float mina, maxa; |
| 90 | int x, y, w, h; |
| 91 | int oldx, oldy, oldw, oldh; |
| 92 | int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; |
| 93 | int bw, oldbw; |
| 94 | unsigned int tags; |
| 95 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; |
| 96 | Client *next; |
| 97 | Client *snext; |
| 98 | Monitor *mon; |
| 99 | Window win; |
| 100 | }; |
| 101 | |
| 102 | typedef struct { |
| 103 | unsigned int mod; |
| 104 | KeySym keysym; |
| 105 | void (*func)(const Arg *); |
| 106 | const Arg arg; |
| 107 | } Key; |
| 108 | |
| 109 | typedef struct { |
| 110 | const char *symbol; |
| 111 | void (*arrange)(Monitor *); |
| 112 | } Layout; |
| 113 | |
| 114 | typedef struct { |
| 115 | const char *class; |
| 116 | const char *instance; |
| 117 | const char *title; |
| 118 | unsigned int tags; |
| 119 | int isfloating; |
| 120 | int monitor; |
| 121 | } Rule; |
| 122 | |
| 123 | /* function declarations */ |
| 124 | static void applyrules(Client *c); |
| 125 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); |
| 126 | static void arrange(Monitor *m); |
| 127 | static void arrangemon(Monitor *m); |
| 128 | static void attach(Client *c); |
| 129 | static void attachstack(Client *c); |
| 130 | static void buttonpress(XEvent *e); |
| 131 | static void checkotherwm(void); |
| 132 | static void cleanup(void); |
| 133 | static void cleanupmon(Monitor *mon); |
| 134 | static void clientmessage(XEvent *e); |
| 135 | static void configure(Client *c); |
| 136 | static void configurenotify(XEvent *e); |
| 137 | static void configurerequest(XEvent *e); |
| 138 | static Monitor *createmon(void); |
| 139 | static void destroynotify(XEvent *e); |
| 140 | static void detach(Client *c); |
| 141 | static void detachstack(Client *c); |
| 142 | static Monitor *dirtomon(int dir); |
| 143 | static void drawbar(Monitor *m); |
| 144 | static void drawbars(void); |
| 145 | static void enternotify(XEvent *e); |
| 146 | static void expose(XEvent *e); |
| 147 | static void focus(Client *c); |
| 148 | static void focusin(XEvent *e); |
| 149 | static void focusmon(const Arg *arg); |
| 150 | static void focusstack(const Arg *arg); |
| 151 | static Atom getatomprop(Client *c, Atom prop); |
| 152 | static int getrootptr(int *x, int *y); |
| 153 | static long getstate(Window w); |
| 154 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); |
| 155 | static void grabbuttons(Client *c, int focused); |
| 156 | static void grabkeys(void); |
| 157 | static void keypress(XEvent *e); |
| 158 | static void killclient(const Arg *arg); |
| 159 | static void layoutmenu(const Arg *arg); |
| 160 | static void manage(Window w, XWindowAttributes *wa); |
| 161 | static void mappingnotify(XEvent *e); |
| 162 | static void maprequest(XEvent *e); |
| 163 | static void monocle(Monitor *m); |
| 164 | static void motionnotify(XEvent *e); |
| 165 | static void movemouse(const Arg *arg); |
| 166 | static void nametag(const Arg *arg); |
| 167 | static Client *nexttiled(Client *c); |
| 168 | static void pop(Client *c); |
| 169 | static void propertynotify(XEvent *e); |
| 170 | static void quit(const Arg *arg); |
| 171 | static void quitprompt(const Arg *arg); |
| 172 | static Monitor *recttomon(int x, int y, int w, int h); |
| 173 | static void resize(Client *c, int x, int y, int w, int h, int interact); |
| 174 | static void resizeclient(Client *c, int x, int y, int w, int h); |
| 175 | static void resizemouse(const Arg *arg); |
| 176 | static void restack(Monitor *m); |
| 177 | static void run(void); |
| 178 | static void scan(void); |
| 179 | static int sendevent(Client *c, Atom proto); |
| 180 | static void sendmon(Client *c, Monitor *m); |
| 181 | static void setclientstate(Client *c, long state); |
| 182 | static void setfocus(Client *c); |
| 183 | static void setfullscreen(Client *c, int fullscreen); |
| 184 | static void setlayout(const Arg *arg); |
| 185 | static void setmfact(const Arg *arg); |
| 186 | static void setup(void); |
| 187 | static void seturgent(Client *c, int urg); |
| 188 | static void showhide(Client *c); |
| 189 | static void sigchld(int unused); |
| 190 | static void spawn(const Arg *arg); |
| 191 | static void tag(const Arg *arg); |
| 192 | static void tagmon(const Arg *arg); |
| 193 | static void tile(Monitor *m); |
| 194 | static void togglebar(const Arg *arg); |
| 195 | static void togglefloating(const Arg *arg); |
| 196 | static void toggletag(const Arg *arg); |
| 197 | static void toggleview(const Arg *arg); |
| 198 | static void unfocus(Client *c, int setfocus); |
| 199 | static void unmanage(Client *c, int destroyed); |
| 200 | static void unmapnotify(XEvent *e); |
| 201 | static void updatebarpos(Monitor *m); |
| 202 | static void updatebars(void); |
| 203 | static void updateclientlist(void); |
| 204 | static int updategeom(void); |
| 205 | static void updatenumlockmask(void); |
| 206 | static void updatesizehints(Client *c); |
| 207 | static void updatestatus(void); |
| 208 | static void updatetitle(Client *c); |
| 209 | static void updatewindowtype(Client *c); |
| 210 | static void updatewmhints(Client *c); |
| 211 | static void view(const Arg *arg); |
| 212 | static Client *wintoclient(Window w); |
| 213 | static Monitor *wintomon(Window w); |
| 214 | static int xerror(Display *dpy, XErrorEvent *ee); |
| 215 | static int xerrordummy(Display *dpy, XErrorEvent *ee); |
| 216 | static int xerrorstart(Display *dpy, XErrorEvent *ee); |
| 217 | static void zoom(const Arg *arg); |
| 218 | |
| 219 | static void keyrelease(XEvent *e); |
| 220 | static void combotag(const Arg *arg); |
| 221 | static void comboview(const Arg *arg); |
| 222 | |
| 223 | |
| 224 | /* variables */ |
| 225 | static const char broken[] = "broken"; |
| 226 | static char stext[256]; |
| 227 | static int screen; |
| 228 | static int sw, sh; /* X display screen geometry width, height */ |
| 229 | static int bh; /* bar height */ |
| 230 | static int lrpad; /* sum of left and right padding for text */ |
| 231 | static int (*xerrorxlib)(Display *, XErrorEvent *); |
| 232 | static unsigned int numlockmask = 0; |
| 233 | static void (*handler[LASTEvent]) (XEvent *) = { |
| 234 | [ButtonPress] = buttonpress, |
| 235 | [ButtonRelease] = keyrelease, |
| 236 | [ClientMessage] = clientmessage, |
| 237 | [ConfigureRequest] = configurerequest, |
| 238 | [ConfigureNotify] = configurenotify, |
| 239 | [DestroyNotify] = destroynotify, |
| 240 | [EnterNotify] = enternotify, |
| 241 | [Expose] = expose, |
| 242 | [FocusIn] = focusin, |
| 243 | [KeyRelease] = keyrelease, |
| 244 | [KeyPress] = keypress, |
| 245 | [MappingNotify] = mappingnotify, |
| 246 | [MapRequest] = maprequest, |
| 247 | [MotionNotify] = motionnotify, |
| 248 | [PropertyNotify] = propertynotify, |
| 249 | [UnmapNotify] = unmapnotify |
| 250 | }; |
| 251 | static Atom wmatom[WMLast], netatom[NetLast]; |
| 252 | static int running = 1; |
| 253 | static int restart = 1; |
| 254 | static Cur *cursor[CurLast]; |
| 255 | static Clr **scheme; |
| 256 | static Display *dpy; |
| 257 | static Drw *drw; |
| 258 | static Monitor *mons, *selmon; |
| 259 | static Window root, wmcheckwin; |
| 260 | |
| 261 | /* configuration, allows nested code to access above variables */ |
| 262 | #include "config.h" |
| 263 | |
| 264 | /* compile-time check if all tags fit into an unsigned int bit array. */ |
| 265 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; |
| 266 | |
| 267 | /* function implementations */ |
| 268 | static int combo = 0; |
| 269 | |
| 270 | void |
| 271 | keyrelease(XEvent *e) { |
| 272 | combo = 0; |
| 273 | } |
| 274 | |
| 275 | void |
| 276 | combotag(const Arg *arg) { |
| 277 | if(selmon->sel && arg->ui & TAGMASK) { |
| 278 | if (combo) { |
| 279 | selmon->sel->tags |= arg->ui & TAGMASK; |
| 280 | } else { |
| 281 | combo = 1; |
| 282 | selmon->sel->tags = arg->ui & TAGMASK; |
| 283 | } |
| 284 | focus(NULL); |
| 285 | arrange(selmon); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | void |
| 290 | comboview(const Arg *arg) { |
| 291 | unsigned newtags = arg->ui & TAGMASK; |
| 292 | if (combo) { |
| 293 | selmon->tagset[selmon->seltags] |= newtags; |
| 294 | } else { |
| 295 | selmon->seltags ^= 1; /*toggle tagset*/ |
| 296 | combo = 1; |
| 297 | if (newtags) |
| 298 | selmon->tagset[selmon->seltags] = newtags; |
| 299 | } |
| 300 | focus(NULL); |
| 301 | arrange(selmon); |
| 302 | } |
| 303 | |
| 304 | void |
| 305 | applyrules(Client *c) |
| 306 | { |
| 307 | const char *class, *instance; |
| 308 | unsigned int i; |
| 309 | const Rule *r; |
| 310 | Monitor *m; |
| 311 | XClassHint ch = { NULL, NULL }; |
| 312 | |
| 313 | /* rule matching */ |
| 314 | c->isfloating = 0; |
| 315 | c->tags = 0; |
| 316 | XGetClassHint(dpy, c->win, &ch); |
| 317 | class = ch.res_class ? ch.res_class : broken; |
| 318 | instance = ch.res_name ? ch.res_name : broken; |
| 319 | |
| 320 | for (i = 0; i < LENGTH(rules); i++) { |
| 321 | r = &rules[i]; |
| 322 | if ((!r->title || strstr(c->name, r->title)) |
| 323 | && (!r->class || strstr(class, r->class)) |
| 324 | && (!r->instance || strstr(instance, r->instance))) |
| 325 | { |
| 326 | c->isfloating = r->isfloating; |
| 327 | c->tags |= r->tags; |
| 328 | for (m = mons; m && m->num != r->monitor; m = m->next); |
| 329 | if (m) |
| 330 | c->mon = m; |
| 331 | } |
| 332 | } |
| 333 | if (ch.res_class) |
| 334 | XFree(ch.res_class); |
| 335 | if (ch.res_name) |
| 336 | XFree(ch.res_name); |
| 337 | if(c->tags & TAGMASK) c->tags = c->tags & TAGMASK; |
| 338 | else if(c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags]; |
| 339 | else c->tags = 1; |
| 340 | } |
| 341 | |
| 342 | int |
| 343 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) |
| 344 | { |
| 345 | int baseismin; |
| 346 | Monitor *m = c->mon; |
| 347 | |
| 348 | /* set minimum possible */ |
| 349 | *w = MAX(1, *w); |
| 350 | *h = MAX(1, *h); |
| 351 | if (interact) { |
| 352 | if (*x > sw) |
| 353 | *x = sw - WIDTH(c); |
| 354 | if (*y > sh) |
| 355 | *y = sh - HEIGHT(c); |
| 356 | if (*x + *w + 2 * c->bw < 0) |
| 357 | *x = 0; |
| 358 | if (*y + *h + 2 * c->bw < 0) |
| 359 | *y = 0; |
| 360 | } else { |
| 361 | if (*x >= m->wx + m->ww) |
| 362 | *x = m->wx + m->ww - WIDTH(c); |
| 363 | if (*y >= m->wy + m->wh) |
| 364 | *y = m->wy + m->wh - HEIGHT(c); |
| 365 | if (*x + *w + 2 * c->bw <= m->wx) |
| 366 | *x = m->wx; |
| 367 | if (*y + *h + 2 * c->bw <= m->wy) |
| 368 | *y = m->wy; |
| 369 | } |
| 370 | if (*h < bh) |
| 371 | *h = bh; |
| 372 | if (*w < bh) |
| 373 | *w = bh; |
| 374 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { |
| 375 | if (!c->hintsvalid) |
| 376 | updatesizehints(c); |
| 377 | /* see last two sentences in ICCCM 4.1.2.3 */ |
| 378 | baseismin = c->basew == c->minw && c->baseh == c->minh; |
| 379 | if (!baseismin) { /* temporarily remove base dimensions */ |
| 380 | *w -= c->basew; |
| 381 | *h -= c->baseh; |
| 382 | } |
| 383 | /* adjust for aspect limits */ |
| 384 | if (c->mina > 0 && c->maxa > 0) { |
| 385 | if (c->maxa < (float)*w / *h) |
| 386 | *w = *h * c->maxa + 0.5; |
| 387 | else if (c->mina < (float)*h / *w) |
| 388 | *h = *w * c->mina + 0.5; |
| 389 | } |
| 390 | if (baseismin) { /* increment calculation requires this */ |
| 391 | *w -= c->basew; |
| 392 | *h -= c->baseh; |
| 393 | } |
| 394 | /* adjust for increment value */ |
| 395 | if (c->incw) |
| 396 | *w -= *w % c->incw; |
| 397 | if (c->inch) |
| 398 | *h -= *h % c->inch; |
| 399 | /* restore base dimensions */ |
| 400 | *w = MAX(*w + c->basew, c->minw); |
| 401 | *h = MAX(*h + c->baseh, c->minh); |
| 402 | if (c->maxw) |
| 403 | *w = MIN(*w, c->maxw); |
| 404 | if (c->maxh) |
| 405 | *h = MIN(*h, c->maxh); |
| 406 | } |
| 407 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; |
| 408 | } |
| 409 | |
| 410 | void |
| 411 | arrange(Monitor *m) |
| 412 | { |
| 413 | if (m) |
| 414 | showhide(m->stack); |
| 415 | else for (m = mons; m; m = m->next) |
| 416 | showhide(m->stack); |
| 417 | if (m) { |
| 418 | arrangemon(m); |
| 419 | restack(m); |
| 420 | } else for (m = mons; m; m = m->next) |
| 421 | arrangemon(m); |
| 422 | } |
| 423 | |
| 424 | void |
| 425 | arrangemon(Monitor *m) |
| 426 | { |
| 427 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); |
| 428 | if (m->lt[m->sellt]->arrange) |
| 429 | m->lt[m->sellt]->arrange(m); |
| 430 | } |
| 431 | |
| 432 | void |
| 433 | attach(Client *c) |
| 434 | { |
| 435 | c->next = c->mon->clients; |
| 436 | c->mon->clients = c; |
| 437 | } |
| 438 | |
| 439 | void |
| 440 | attachstack(Client *c) |
| 441 | { |
| 442 | c->snext = c->mon->stack; |
| 443 | c->mon->stack = c; |
| 444 | } |
| 445 | |
| 446 | void |
| 447 | buttonpress(XEvent *e) |
| 448 | { |
| 449 | unsigned int i, x, click; |
| 450 | Arg arg = {0}; |
| 451 | Client *c; |
| 452 | Monitor *m; |
| 453 | XButtonPressedEvent *ev = &e->xbutton; |
| 454 | |
| 455 | click = ClkRootWin; |
| 456 | /* focus monitor if necessary */ |
| 457 | if ((m = wintomon(ev->window)) && m != selmon) { |
| 458 | unfocus(selmon->sel, 1); |
| 459 | selmon = m; |
| 460 | focus(NULL); |
| 461 | } |
| 462 | if (ev->window == selmon->barwin) { |
| 463 | i = x = 0; |
| 464 | do |
| 465 | x += TEXTW(tags[i]); |
| 466 | while (ev->x >= x && ++i < LENGTH(tags)); |
| 467 | if (i < LENGTH(tags)) { |
| 468 | click = ClkTagBar; |
| 469 | arg.ui = 1 << i; |
| 470 | } else if (ev->x < x + TEXTW(selmon->ltsymbol)) |
| 471 | click = ClkLtSymbol; |
| 472 | else if (ev->x > selmon->ww - (int)TEXTW(stext)) |
| 473 | click = ClkStatusText; |
| 474 | else |
| 475 | click = ClkWinTitle; |
| 476 | } else if ((c = wintoclient(ev->window))) { |
| 477 | focus(c); |
| 478 | restack(selmon); |
| 479 | XAllowEvents(dpy, ReplayPointer, CurrentTime); |
| 480 | click = ClkClientWin; |
| 481 | } |
| 482 | for (i = 0; i < LENGTH(buttons); i++) |
| 483 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button |
| 484 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) |
| 485 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); |
| 486 | } |
| 487 | |
| 488 | void |
| 489 | checkotherwm(void) |
| 490 | { |
| 491 | xerrorxlib = XSetErrorHandler(xerrorstart); |
| 492 | /* this causes an error if some other window manager is running */ |
| 493 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); |
| 494 | XSync(dpy, False); |
| 495 | XSetErrorHandler(xerror); |
| 496 | XSync(dpy, False); |
| 497 | } |
| 498 | |
| 499 | void |
| 500 | cleanup(void) |
| 501 | { |
| 502 | Arg a = {.ui = ~0}; |
| 503 | Layout foo = { "", NULL }; |
| 504 | Monitor *m; |
| 505 | size_t i; |
| 506 | |
| 507 | view(&a); |
| 508 | selmon->lt[selmon->sellt] = &foo; |
| 509 | for (m = mons; m; m = m->next) |
| 510 | while (m->stack) |
| 511 | unmanage(m->stack, 0); |
| 512 | XUngrabKey(dpy, AnyKey, AnyModifier, root); |
| 513 | while (mons) |
| 514 | cleanupmon(mons); |
| 515 | for (i = 0; i < CurLast; i++) |
| 516 | drw_cur_free(drw, cursor[i]); |
| 517 | for (i = 0; i < LENGTH(colors); i++) |
| 518 | free(scheme[i]); |
| 519 | free(scheme); |
| 520 | XDestroyWindow(dpy, wmcheckwin); |
| 521 | drw_free(drw); |
| 522 | XSync(dpy, False); |
| 523 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
| 524 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
| 525 | } |
| 526 | |
| 527 | void |
| 528 | cleanupmon(Monitor *mon) |
| 529 | { |
| 530 | Monitor *m; |
| 531 | |
| 532 | if (mon == mons) |
| 533 | mons = mons->next; |
| 534 | else { |
| 535 | for (m = mons; m && m->next != mon; m = m->next); |
| 536 | m->next = mon->next; |
| 537 | } |
| 538 | XUnmapWindow(dpy, mon->barwin); |
| 539 | XDestroyWindow(dpy, mon->barwin); |
| 540 | free(mon); |
| 541 | } |
| 542 | |
| 543 | void |
| 544 | clientmessage(XEvent *e) |
| 545 | { |
| 546 | XClientMessageEvent *cme = &e->xclient; |
| 547 | Client *c = wintoclient(cme->window); |
| 548 | |
| 549 | if (!c) |
| 550 | return; |
| 551 | if (cme->message_type == netatom[NetWMState]) { |
| 552 | if (cme->data.l[1] == netatom[NetWMFullscreen] |
| 553 | || cme->data.l[2] == netatom[NetWMFullscreen]) |
| 554 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ |
| 555 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); |
| 556 | } else if (cme->message_type == netatom[NetActiveWindow]) { |
| 557 | if (c != selmon->sel && !c->isurgent) |
| 558 | seturgent(c, 1); |
| 559 | } |
| 560 | } |
| 561 | |
| 562 | void |
| 563 | configure(Client *c) |
| 564 | { |
| 565 | XConfigureEvent ce; |
| 566 | |
| 567 | ce.type = ConfigureNotify; |
| 568 | ce.display = dpy; |
| 569 | ce.event = c->win; |
| 570 | ce.window = c->win; |
| 571 | ce.x = c->x; |
| 572 | ce.y = c->y; |
| 573 | ce.width = c->w; |
| 574 | ce.height = c->h; |
| 575 | ce.border_width = c->bw; |
| 576 | ce.above = None; |
| 577 | ce.override_redirect = False; |
| 578 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
| 579 | } |
| 580 | |
| 581 | void |
| 582 | configurenotify(XEvent *e) |
| 583 | { |
| 584 | Monitor *m; |
| 585 | Client *c; |
| 586 | XConfigureEvent *ev = &e->xconfigure; |
| 587 | int dirty; |
| 588 | |
| 589 | /* TODO: updategeom handling sucks, needs to be simplified */ |
| 590 | if (ev->window == root) { |
| 591 | dirty = (sw != ev->width || sh != ev->height); |
| 592 | sw = ev->width; |
| 593 | sh = ev->height; |
| 594 | if (updategeom() || dirty) { |
| 595 | drw_resize(drw, sw, bh); |
| 596 | updatebars(); |
| 597 | for (m = mons; m; m = m->next) { |
| 598 | for (c = m->clients; c; c = c->next) |
| 599 | if (c->isfullscreen) |
| 600 | resizeclient(c, m->mx, m->my, m->mw, m->mh); |
| 601 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); |
| 602 | } |
| 603 | focus(NULL); |
| 604 | arrange(NULL); |
| 605 | } |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | void |
| 610 | configurerequest(XEvent *e) |
| 611 | { |
| 612 | Client *c; |
| 613 | Monitor *m; |
| 614 | XConfigureRequestEvent *ev = &e->xconfigurerequest; |
| 615 | XWindowChanges wc; |
| 616 | |
| 617 | if ((c = wintoclient(ev->window))) { |
| 618 | if (ev->value_mask & CWBorderWidth) |
| 619 | c->bw = ev->border_width; |
| 620 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { |
| 621 | m = c->mon; |
| 622 | if (ev->value_mask & CWX) { |
| 623 | c->oldx = c->x; |
| 624 | c->x = m->mx + ev->x; |
| 625 | } |
| 626 | if (ev->value_mask & CWY) { |
| 627 | c->oldy = c->y; |
| 628 | c->y = m->my + ev->y; |
| 629 | } |
| 630 | if (ev->value_mask & CWWidth) { |
| 631 | c->oldw = c->w; |
| 632 | c->w = ev->width; |
| 633 | } |
| 634 | if (ev->value_mask & CWHeight) { |
| 635 | c->oldh = c->h; |
| 636 | c->h = ev->height; |
| 637 | } |
| 638 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) |
| 639 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ |
| 640 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) |
| 641 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ |
| 642 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) |
| 643 | configure(c); |
| 644 | if (ISVISIBLE(c)) |
| 645 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
| 646 | } else |
| 647 | configure(c); |
| 648 | } else { |
| 649 | wc.x = ev->x; |
| 650 | wc.y = ev->y; |
| 651 | wc.width = ev->width; |
| 652 | wc.height = ev->height; |
| 653 | wc.border_width = ev->border_width; |
| 654 | wc.sibling = ev->above; |
| 655 | wc.stack_mode = ev->detail; |
| 656 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
| 657 | } |
| 658 | XSync(dpy, False); |
| 659 | } |
| 660 | |
| 661 | Monitor * |
| 662 | createmon(void) |
| 663 | { |
| 664 | unsigned int i; |
| 665 | Monitor *m; |
| 666 | |
| 667 | m = ecalloc(1, sizeof(Monitor)); |
| 668 | m->tagset[0] = m->tagset[1] = startontag ? 1 : 0; |
| 669 | m->mfact = mfact; |
| 670 | m->showbar = showbar; |
| 671 | m->topbar = topbar; |
| 672 | m->lt[0] = &layouts[0]; |
| 673 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; |
| 674 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); |
| 675 | m->ltaxis[0] = layoutaxis[0]; |
| 676 | m->ltaxis[1] = layoutaxis[1]; |
| 677 | m->ltaxis[2] = layoutaxis[2]; |
| 678 | m->msplit = 1; |
| 679 | /* init tags, bars, layouts, axes, msplits and mfacts */ |
| 680 | m->curtag = m->prevtag = 1; |
| 681 | for(i = 0; i < LENGTH(tags) + 1; i++){ |
| 682 | m->showbars[i] = m->showbar; |
| 683 | m->lts[i] = &layouts[0]; |
| 684 | m->mfacts[i] = m->mfact; |
| 685 | m->ltaxes[i][0] = m->ltaxis[0]; |
| 686 | m->ltaxes[i][1] = m->ltaxis[1]; |
| 687 | m->ltaxes[i][2] = m->ltaxis[2]; |
| 688 | m->msplits[i] = m->msplit; |
| 689 | } |
| 690 | return m; |
| 691 | } |
| 692 | |
| 693 | void |
| 694 | destroynotify(XEvent *e) |
| 695 | { |
| 696 | Client *c; |
| 697 | XDestroyWindowEvent *ev = &e->xdestroywindow; |
| 698 | |
| 699 | if ((c = wintoclient(ev->window))) |
| 700 | unmanage(c, 1); |
| 701 | } |
| 702 | |
| 703 | void |
| 704 | detach(Client *c) |
| 705 | { |
| 706 | Client **tc; |
| 707 | |
| 708 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); |
| 709 | *tc = c->next; |
| 710 | } |
| 711 | |
| 712 | void |
| 713 | detachstack(Client *c) |
| 714 | { |
| 715 | Client **tc, *t; |
| 716 | |
| 717 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); |
| 718 | *tc = c->snext; |
| 719 | |
| 720 | if (c == c->mon->sel) { |
| 721 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); |
| 722 | c->mon->sel = t; |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | Monitor * |
| 727 | dirtomon(int dir) |
| 728 | { |
| 729 | Monitor *m = NULL; |
| 730 | |
| 731 | if (dir > 0) { |
| 732 | if (!(m = selmon->next)) |
| 733 | m = mons; |
| 734 | } else if (selmon == mons) |
| 735 | for (m = mons; m->next; m = m->next); |
| 736 | else |
| 737 | for (m = mons; m->next != selmon; m = m->next); |
| 738 | return m; |
| 739 | } |
| 740 | |
| 741 | void |
| 742 | drawbar(Monitor *m) |
| 743 | { |
| 744 | int indn; |
| 745 | int x, w, tw = 0; |
| 746 | int boxs = drw->fonts->h / 9; |
| 747 | int boxw = drw->fonts->h / 6 + 2; |
| 748 | unsigned int i, occ = 0, urg = 0; |
| 749 | Client *c; |
| 750 | |
| 751 | if (!m->showbar) |
| 752 | return; |
| 753 | |
| 754 | /* draw status first so it can be overdrawn by tags later */ |
| 755 | if (m == selmon) { /* status is only drawn on selected monitor */ |
| 756 | drw_setscheme(drw, scheme[SchemeNorm]); |
| 757 | tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ |
| 758 | drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); |
| 759 | } |
| 760 | |
| 761 | for (c = m->clients; c; c = c->next) { |
| 762 | occ |= c->tags; |
| 763 | if (c->isurgent) |
| 764 | urg |= c->tags; |
| 765 | } |
| 766 | x = 0; |
| 767 | for (i = 0; i < LENGTH(tags); i++) { |
| 768 | indn = 0; |
| 769 | w = TEXTW(tags[i]); |
| 770 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); |
| 771 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); |
| 772 | |
| 773 | for (c = m->clients; c; c = c->next) { |
| 774 | if (c->tags & (1 << i)) { |
| 775 | drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); |
| 776 | indn++; |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | x += w; |
| 781 | } |
| 782 | w = TEXTW(m->ltsymbol); |
| 783 | drw_setscheme(drw, scheme[SchemeNorm]); |
| 784 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); |
| 785 | |
| 786 | if ((w = m->ww - tw - x) > bh) { |
| 787 | if (m->sel) { |
| 788 | /* fix overflow when window name is bigger than window width */ |
| 789 | int mid = (m->ww - (int)TEXTW(m->sel->name)) / 2 - x; |
| 790 | /* make sure name will not overlap on tags even when it is very long */ |
| 791 | mid = mid >= lrpad / 2 ? mid : lrpad / 2; |
| 792 | drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); |
| 793 | drw_text(drw, x, 0, w, bh, mid, m->sel->name, 0); |
| 794 | if (m->sel->isfloating) |
| 795 | drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); |
| 796 | } else { |
| 797 | drw_setscheme(drw, scheme[SchemeNorm]); |
| 798 | drw_rect(drw, x, 0, w, bh, 1, 1); |
| 799 | } |
| 800 | } |
| 801 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); |
| 802 | } |
| 803 | |
| 804 | void |
| 805 | drawbars(void) |
| 806 | { |
| 807 | Monitor *m; |
| 808 | |
| 809 | for (m = mons; m; m = m->next) |
| 810 | drawbar(m); |
| 811 | } |
| 812 | |
| 813 | void |
| 814 | enternotify(XEvent *e) |
| 815 | { |
| 816 | Client *c; |
| 817 | Monitor *m; |
| 818 | XCrossingEvent *ev = &e->xcrossing; |
| 819 | |
| 820 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) |
| 821 | return; |
| 822 | c = wintoclient(ev->window); |
| 823 | m = c ? c->mon : wintomon(ev->window); |
| 824 | if (m != selmon) { |
| 825 | unfocus(selmon->sel, 1); |
| 826 | selmon = m; |
| 827 | } else if (!c || c == selmon->sel) |
| 828 | return; |
| 829 | focus(c); |
| 830 | } |
| 831 | |
| 832 | void |
| 833 | expose(XEvent *e) |
| 834 | { |
| 835 | Monitor *m; |
| 836 | XExposeEvent *ev = &e->xexpose; |
| 837 | |
| 838 | if (ev->count == 0 && (m = wintomon(ev->window))) |
| 839 | drawbar(m); |
| 840 | } |
| 841 | |
| 842 | void |
| 843 | focus(Client *c) |
| 844 | { |
| 845 | if (!c || !ISVISIBLE(c)) |
| 846 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); |
| 847 | if (selmon->sel && selmon->sel != c) |
| 848 | unfocus(selmon->sel, 0); |
| 849 | if (c) { |
| 850 | if (c->mon != selmon) |
| 851 | selmon = c->mon; |
| 852 | if (c->isurgent) |
| 853 | seturgent(c, 0); |
| 854 | detachstack(c); |
| 855 | attachstack(c); |
| 856 | grabbuttons(c, 1); |
| 857 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); |
| 858 | setfocus(c); |
| 859 | } else { |
| 860 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
| 861 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
| 862 | } |
| 863 | selmon->sel = c; |
| 864 | drawbars(); |
| 865 | } |
| 866 | |
| 867 | /* there are some broken focus acquiring clients needing extra handling */ |
| 868 | void |
| 869 | focusin(XEvent *e) |
| 870 | { |
| 871 | XFocusChangeEvent *ev = &e->xfocus; |
| 872 | |
| 873 | if (selmon->sel && ev->window != selmon->sel->win) |
| 874 | setfocus(selmon->sel); |
| 875 | } |
| 876 | |
| 877 | void |
| 878 | focusmon(const Arg *arg) |
| 879 | { |
| 880 | Monitor *m; |
| 881 | |
| 882 | if (!mons->next) |
| 883 | return; |
| 884 | if ((m = dirtomon(arg->i)) == selmon) |
| 885 | return; |
| 886 | unfocus(selmon->sel, 0); |
| 887 | selmon = m; |
| 888 | focus(NULL); |
| 889 | } |
| 890 | |
| 891 | void |
| 892 | focusstack(const Arg *arg) |
| 893 | { |
| 894 | Client *c = NULL, *i; |
| 895 | |
| 896 | if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) |
| 897 | return; |
| 898 | if (arg->i > 0) { |
| 899 | for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); |
| 900 | if (!c) |
| 901 | for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); |
| 902 | } else { |
| 903 | for (i = selmon->clients; i != selmon->sel; i = i->next) |
| 904 | if (ISVISIBLE(i)) |
| 905 | c = i; |
| 906 | if (!c) |
| 907 | for (; i; i = i->next) |
| 908 | if (ISVISIBLE(i)) |
| 909 | c = i; |
| 910 | } |
| 911 | if (c) { |
| 912 | focus(c); |
| 913 | restack(selmon); |
| 914 | } |
| 915 | } |
| 916 | |
| 917 | Atom |
| 918 | getatomprop(Client *c, Atom prop) |
| 919 | { |
| 920 | int di; |
| 921 | unsigned long dl; |
| 922 | unsigned char *p = NULL; |
| 923 | Atom da, atom = None; |
| 924 | |
| 925 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, |
| 926 | &da, &di, &dl, &dl, &p) == Success && p) { |
| 927 | atom = *(Atom *)p; |
| 928 | XFree(p); |
| 929 | } |
| 930 | return atom; |
| 931 | } |
| 932 | |
| 933 | int |
| 934 | getrootptr(int *x, int *y) |
| 935 | { |
| 936 | int di; |
| 937 | unsigned int dui; |
| 938 | Window dummy; |
| 939 | |
| 940 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); |
| 941 | } |
| 942 | |
| 943 | long |
| 944 | getstate(Window w) |
| 945 | { |
| 946 | int format; |
| 947 | long result = -1; |
| 948 | unsigned char *p = NULL; |
| 949 | unsigned long n, extra; |
| 950 | Atom real; |
| 951 | |
| 952 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
| 953 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) |
| 954 | return -1; |
| 955 | if (n != 0) |
| 956 | result = *p; |
| 957 | XFree(p); |
| 958 | return result; |
| 959 | } |
| 960 | |
| 961 | int |
| 962 | gettextprop(Window w, Atom atom, char *text, unsigned int size) |
| 963 | { |
| 964 | char **list = NULL; |
| 965 | int n; |
| 966 | XTextProperty name; |
| 967 | |
| 968 | if (!text || size == 0) |
| 969 | return 0; |
| 970 | text[0] = '\0'; |
| 971 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) |
| 972 | return 0; |
| 973 | if (name.encoding == XA_STRING) { |
| 974 | strncpy(text, (char *)name.value, size - 1); |
| 975 | } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { |
| 976 | strncpy(text, *list, size - 1); |
| 977 | XFreeStringList(list); |
| 978 | } |
| 979 | text[size - 1] = '\0'; |
| 980 | XFree(name.value); |
| 981 | return 1; |
| 982 | } |
| 983 | |
| 984 | void |
| 985 | grabbuttons(Client *c, int focused) |
| 986 | { |
| 987 | updatenumlockmask(); |
| 988 | { |
| 989 | unsigned int i, j; |
| 990 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
| 991 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
| 992 | if (!focused) |
| 993 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, |
| 994 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); |
| 995 | for (i = 0; i < LENGTH(buttons); i++) |
| 996 | if (buttons[i].click == ClkClientWin) |
| 997 | for (j = 0; j < LENGTH(modifiers); j++) |
| 998 | XGrabButton(dpy, buttons[i].button, |
| 999 | buttons[i].mask | modifiers[j], |
| 1000 | c->win, False, BUTTONMASK, |
| 1001 | GrabModeAsync, GrabModeSync, None, None); |
| 1002 | } |
| 1003 | } |
| 1004 | |
| 1005 | void |
| 1006 | grabkeys(void) |
| 1007 | { |
| 1008 | updatenumlockmask(); |
| 1009 | { |
| 1010 | unsigned int i, j; |
| 1011 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
| 1012 | KeyCode code; |
| 1013 | |
| 1014 | XUngrabKey(dpy, AnyKey, AnyModifier, root); |
| 1015 | for (i = 0; i < LENGTH(keys); i++) |
| 1016 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) |
| 1017 | for (j = 0; j < LENGTH(modifiers); j++) |
| 1018 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, |
| 1019 | True, GrabModeAsync, GrabModeAsync); |
| 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | #ifdef XINERAMA |
| 1024 | static int |
| 1025 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) |
| 1026 | { |
| 1027 | while (n--) |
| 1028 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org |
| 1029 | && unique[n].width == info->width && unique[n].height == info->height) |
| 1030 | return 0; |
| 1031 | return 1; |
| 1032 | } |
| 1033 | #endif /* XINERAMA */ |
| 1034 | |
| 1035 | void |
| 1036 | keypress(XEvent *e) |
| 1037 | { |
| 1038 | unsigned int i; |
| 1039 | KeySym keysym; |
| 1040 | XKeyEvent *ev; |
| 1041 | |
| 1042 | ev = &e->xkey; |
| 1043 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
| 1044 | for (i = 0; i < LENGTH(keys); i++) |
| 1045 | if (keysym == keys[i].keysym |
| 1046 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) |
| 1047 | && keys[i].func) |
| 1048 | keys[i].func(&(keys[i].arg)); |
| 1049 | } |
| 1050 | |
| 1051 | void |
| 1052 | killclient(const Arg *arg) |
| 1053 | { |
| 1054 | if (!selmon->sel) |
| 1055 | return; |
| 1056 | if (!sendevent(selmon->sel, wmatom[WMDelete])) { |
| 1057 | XGrabServer(dpy); |
| 1058 | XSetErrorHandler(xerrordummy); |
| 1059 | XSetCloseDownMode(dpy, DestroyAll); |
| 1060 | XKillClient(dpy, selmon->sel->win); |
| 1061 | XSync(dpy, False); |
| 1062 | XSetErrorHandler(xerror); |
| 1063 | XUngrabServer(dpy); |
| 1064 | } |
| 1065 | } |
| 1066 | |
| 1067 | void |
| 1068 | layoutmenu(const Arg *arg) { |
| 1069 | FILE *p; |
| 1070 | char c[3], *s; |
| 1071 | int i; |
| 1072 | |
| 1073 | if (!(p = popen(layoutmenu_cmd, "r"))) |
| 1074 | return; |
| 1075 | s = fgets(c, sizeof(c), p); |
| 1076 | pclose(p); |
| 1077 | |
| 1078 | if (!s || *s == '\0' || c[0] == '\0') |
| 1079 | return; |
| 1080 | |
| 1081 | i = atoi(c); |
| 1082 | setlayout(&((Arg) { .v = &layouts[i] })); |
| 1083 | } |
| 1084 | |
| 1085 | void |
| 1086 | manage(Window w, XWindowAttributes *wa) |
| 1087 | { |
| 1088 | Client *c, *t = NULL; |
| 1089 | Window trans = None; |
| 1090 | XWindowChanges wc; |
| 1091 | |
| 1092 | c = ecalloc(1, sizeof(Client)); |
| 1093 | c->win = w; |
| 1094 | /* geometry */ |
| 1095 | c->x = c->oldx = wa->x; |
| 1096 | c->y = c->oldy = wa->y; |
| 1097 | c->w = c->oldw = wa->width; |
| 1098 | c->h = c->oldh = wa->height; |
| 1099 | c->oldbw = wa->border_width; |
| 1100 | |
| 1101 | updatetitle(c); |
| 1102 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { |
| 1103 | c->mon = t->mon; |
| 1104 | c->tags = t->tags; |
| 1105 | } else { |
| 1106 | c->mon = selmon; |
| 1107 | applyrules(c); |
| 1108 | } |
| 1109 | |
| 1110 | if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) |
| 1111 | c->x = c->mon->wx + c->mon->ww - WIDTH(c); |
| 1112 | if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) |
| 1113 | c->y = c->mon->wy + c->mon->wh - HEIGHT(c); |
| 1114 | c->x = MAX(c->x, c->mon->wx); |
| 1115 | c->y = MAX(c->y, c->mon->wy); |
| 1116 | c->bw = borderpx; |
| 1117 | |
| 1118 | wc.border_width = c->bw; |
| 1119 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); |
| 1120 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); |
| 1121 | configure(c); /* propagates border_width, if size doesn't change */ |
| 1122 | updatewindowtype(c); |
| 1123 | updatesizehints(c); |
| 1124 | updatewmhints(c); |
| 1125 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); |
| 1126 | grabbuttons(c, 0); |
| 1127 | if (!c->isfloating) |
| 1128 | c->isfloating = c->oldstate = trans != None || c->isfixed; |
| 1129 | if (c->isfloating) |
| 1130 | XRaiseWindow(dpy, c->win); |
| 1131 | attach(c); |
| 1132 | attachstack(c); |
| 1133 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, |
| 1134 | (unsigned char *) &(c->win), 1); |
| 1135 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ |
| 1136 | setclientstate(c, NormalState); |
| 1137 | if (c->mon == selmon) |
| 1138 | unfocus(selmon->sel, 0); |
| 1139 | c->mon->sel = c; |
| 1140 | arrange(c->mon); |
| 1141 | XMapWindow(dpy, c->win); |
| 1142 | focus(NULL); |
| 1143 | } |
| 1144 | |
| 1145 | void |
| 1146 | mappingnotify(XEvent *e) |
| 1147 | { |
| 1148 | XMappingEvent *ev = &e->xmapping; |
| 1149 | |
| 1150 | XRefreshKeyboardMapping(ev); |
| 1151 | if (ev->request == MappingKeyboard) |
| 1152 | grabkeys(); |
| 1153 | } |
| 1154 | |
| 1155 | void |
| 1156 | maprequest(XEvent *e) |
| 1157 | { |
| 1158 | static XWindowAttributes wa; |
| 1159 | XMapRequestEvent *ev = &e->xmaprequest; |
| 1160 | |
| 1161 | if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) |
| 1162 | return; |
| 1163 | if (!wintoclient(ev->window)) |
| 1164 | manage(ev->window, &wa); |
| 1165 | } |
| 1166 | |
| 1167 | void |
| 1168 | monocle(Monitor *m) |
| 1169 | { |
| 1170 | unsigned int n = 0; |
| 1171 | Client *c; |
| 1172 | |
| 1173 | for (c = m->clients; c; c = c->next) |
| 1174 | if (ISVISIBLE(c)) |
| 1175 | n++; |
| 1176 | if (n > 0) /* override layout symbol */ |
| 1177 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); |
| 1178 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) |
| 1179 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); |
| 1180 | } |
| 1181 | |
| 1182 | void |
| 1183 | motionnotify(XEvent *e) |
| 1184 | { |
| 1185 | static Monitor *mon = NULL; |
| 1186 | Monitor *m; |
| 1187 | XMotionEvent *ev = &e->xmotion; |
| 1188 | |
| 1189 | if (ev->window != root) |
| 1190 | return; |
| 1191 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { |
| 1192 | unfocus(selmon->sel, 1); |
| 1193 | selmon = m; |
| 1194 | focus(NULL); |
| 1195 | } |
| 1196 | mon = m; |
| 1197 | } |
| 1198 | |
| 1199 | void |
| 1200 | movemouse(const Arg *arg) |
| 1201 | { |
| 1202 | int x, y, ocx, ocy, nx, ny; |
| 1203 | Client *c; |
| 1204 | Monitor *m; |
| 1205 | XEvent ev; |
| 1206 | Time lasttime = 0; |
| 1207 | |
| 1208 | if (!(c = selmon->sel)) |
| 1209 | return; |
| 1210 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ |
| 1211 | return; |
| 1212 | restack(selmon); |
| 1213 | ocx = c->x; |
| 1214 | ocy = c->y; |
| 1215 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
| 1216 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) |
| 1217 | return; |
| 1218 | if (!getrootptr(&x, &y)) |
| 1219 | return; |
| 1220 | do { |
| 1221 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
| 1222 | switch(ev.type) { |
| 1223 | case ConfigureRequest: |
| 1224 | case Expose: |
| 1225 | case MapRequest: |
| 1226 | handler[ev.type](&ev); |
| 1227 | break; |
| 1228 | case MotionNotify: |
| 1229 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) |
| 1230 | continue; |
| 1231 | lasttime = ev.xmotion.time; |
| 1232 | |
| 1233 | nx = ocx + (ev.xmotion.x - x); |
| 1234 | ny = ocy + (ev.xmotion.y - y); |
| 1235 | if (abs(selmon->wx - nx) < snap) |
| 1236 | nx = selmon->wx; |
| 1237 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) |
| 1238 | nx = selmon->wx + selmon->ww - WIDTH(c); |
| 1239 | if (abs(selmon->wy - ny) < snap) |
| 1240 | ny = selmon->wy; |
| 1241 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) |
| 1242 | ny = selmon->wy + selmon->wh - HEIGHT(c); |
| 1243 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
| 1244 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) |
| 1245 | togglefloating(NULL); |
| 1246 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
| 1247 | resize(c, nx, ny, c->w, c->h, 1); |
| 1248 | break; |
| 1249 | } |
| 1250 | } while (ev.type != ButtonRelease); |
| 1251 | XUngrabPointer(dpy, CurrentTime); |
| 1252 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
| 1253 | sendmon(c, m); |
| 1254 | selmon = m; |
| 1255 | focus(NULL); |
| 1256 | } |
| 1257 | } |
| 1258 | |
| 1259 | void |
| 1260 | nametag(const Arg *arg) { |
| 1261 | char *p, name[MAX_TAGLEN]; |
| 1262 | FILE *f; |
| 1263 | int i; |
| 1264 | |
| 1265 | errno = 0; // popen(3p) says on failure it "may" set errno |
| 1266 | // TODO: Create a central location for the dmenu font specification. |
| 1267 | // It should not be hardcoded in multiple locations. |
| 1268 | if(!(f = popen("dmenu -fn monospace:size=12 < /dev/null", "r"))) { |
| 1269 | fprintf(stderr, "dwm: popen 'dmenu < /dev/null' failed%s%s\n", errno ? ": " : "", errno ? strerror(errno) : ""); |
| 1270 | return; |
| 1271 | } |
| 1272 | if (!(p = fgets(name, MAX_TAGLEN, f)) && (i = errno) && ferror(f)) |
| 1273 | fprintf(stderr, "dwm: fgets failed: %s\n", strerror(i)); |
| 1274 | if (pclose(f) < 0) |
| 1275 | fprintf(stderr, "dwm: pclose failed: %s\n", strerror(errno)); |
| 1276 | if(!p) |
| 1277 | return; |
| 1278 | if((p = strchr(name, '\n'))) |
| 1279 | *p = '\0'; |
| 1280 | |
| 1281 | for(i = 0; i < LENGTH(tags); i++) |
| 1282 | if(selmon->tagset[selmon->seltags] & (1 << i)) |
| 1283 | strcpy(tags[i], name); |
| 1284 | drawbars(); |
| 1285 | } |
| 1286 | |
| 1287 | Client * |
| 1288 | nexttiled(Client *c) |
| 1289 | { |
| 1290 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); |
| 1291 | return c; |
| 1292 | } |
| 1293 | |
| 1294 | void |
| 1295 | pop(Client *c) |
| 1296 | { |
| 1297 | detach(c); |
| 1298 | attach(c); |
| 1299 | focus(c); |
| 1300 | arrange(c->mon); |
| 1301 | } |
| 1302 | |
| 1303 | void |
| 1304 | propertynotify(XEvent *e) |
| 1305 | { |
| 1306 | Client *c; |
| 1307 | Window trans; |
| 1308 | XPropertyEvent *ev = &e->xproperty; |
| 1309 | |
| 1310 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) |
| 1311 | updatestatus(); |
| 1312 | else if (ev->state == PropertyDelete) |
| 1313 | return; /* ignore */ |
| 1314 | else if ((c = wintoclient(ev->window))) { |
| 1315 | switch(ev->atom) { |
| 1316 | default: break; |
| 1317 | case XA_WM_TRANSIENT_FOR: |
| 1318 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && |
| 1319 | (c->isfloating = (wintoclient(trans)) != NULL)) |
| 1320 | arrange(c->mon); |
| 1321 | break; |
| 1322 | case XA_WM_NORMAL_HINTS: |
| 1323 | c->hintsvalid = 0; |
| 1324 | break; |
| 1325 | case XA_WM_HINTS: |
| 1326 | updatewmhints(c); |
| 1327 | drawbars(); |
| 1328 | break; |
| 1329 | } |
| 1330 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
| 1331 | updatetitle(c); |
| 1332 | if (c == c->mon->sel) |
| 1333 | drawbar(c->mon); |
| 1334 | } |
| 1335 | if (ev->atom == netatom[NetWMWindowType]) |
| 1336 | updatewindowtype(c); |
| 1337 | } |
| 1338 | } |
| 1339 | |
| 1340 | void |
| 1341 | quit(const Arg *arg) |
| 1342 | { |
| 1343 | running = 0; |
| 1344 | } |
| 1345 | |
| 1346 | void |
| 1347 | quitprompt(const Arg *arg) |
| 1348 | { |
| 1349 | // TODO: Create a central location for the dmenu font specification. |
| 1350 | // It should not be hardcoded in multiple locations. |
| 1351 | FILE *pp = popen("echo -e \"no\nrestart\nyes\" | dmenu -fn monospace:size=12 -i -sb red -p \"Quit DWM?\"", "r"); |
| 1352 | if(pp != NULL) { |
| 1353 | char buf[1024]; |
| 1354 | if (fgets(buf, sizeof(buf), pp) == NULL) { |
| 1355 | fprintf(stderr, "Quitprompt: Error reading pipe!\n"); |
| 1356 | return; |
| 1357 | } |
| 1358 | if (strcmp(buf, "yes\n") == 0) { |
| 1359 | pclose(pp); |
| 1360 | restart = 0; |
| 1361 | quit(NULL); |
| 1362 | } else if (strcmp(buf, "restart\n") == 0) { |
| 1363 | pclose(pp); |
| 1364 | restart = 1; |
| 1365 | quit(NULL); |
| 1366 | } else if (strcmp(buf, "no\n") == 0) { |
| 1367 | pclose(pp); |
| 1368 | return; |
| 1369 | } |
| 1370 | } |
| 1371 | } |
| 1372 | |
| 1373 | Monitor * |
| 1374 | recttomon(int x, int y, int w, int h) |
| 1375 | { |
| 1376 | Monitor *m, *r = selmon; |
| 1377 | int a, area = 0; |
| 1378 | |
| 1379 | for (m = mons; m; m = m->next) |
| 1380 | if ((a = INTERSECT(x, y, w, h, m)) > area) { |
| 1381 | area = a; |
| 1382 | r = m; |
| 1383 | } |
| 1384 | return r; |
| 1385 | } |
| 1386 | |
| 1387 | void |
| 1388 | resize(Client *c, int x, int y, int w, int h, int interact) |
| 1389 | { |
| 1390 | if (applysizehints(c, &x, &y, &w, &h, interact)) |
| 1391 | resizeclient(c, x, y, w, h); |
| 1392 | } |
| 1393 | |
| 1394 | void |
| 1395 | resizeclient(Client *c, int x, int y, int w, int h) |
| 1396 | { |
| 1397 | XWindowChanges wc; |
| 1398 | |
| 1399 | c->oldx = c->x; c->x = wc.x = x; |
| 1400 | c->oldy = c->y; c->y = wc.y = y; |
| 1401 | c->oldw = c->w; c->w = wc.width = w; |
| 1402 | c->oldh = c->h; c->h = wc.height = h; |
| 1403 | wc.border_width = c->bw; |
| 1404 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); |
| 1405 | configure(c); |
| 1406 | XSync(dpy, False); |
| 1407 | } |
| 1408 | |
| 1409 | void |
| 1410 | resizemouse(const Arg *arg) |
| 1411 | { |
| 1412 | int ocx, ocy, nw, nh; |
| 1413 | Client *c; |
| 1414 | Monitor *m; |
| 1415 | XEvent ev; |
| 1416 | Time lasttime = 0; |
| 1417 | |
| 1418 | if (!(c = selmon->sel)) |
| 1419 | return; |
| 1420 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ |
| 1421 | return; |
| 1422 | restack(selmon); |
| 1423 | ocx = c->x; |
| 1424 | ocy = c->y; |
| 1425 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
| 1426 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) |
| 1427 | return; |
| 1428 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
| 1429 | do { |
| 1430 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
| 1431 | switch(ev.type) { |
| 1432 | case ConfigureRequest: |
| 1433 | case Expose: |
| 1434 | case MapRequest: |
| 1435 | handler[ev.type](&ev); |
| 1436 | break; |
| 1437 | case MotionNotify: |
| 1438 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) |
| 1439 | continue; |
| 1440 | lasttime = ev.xmotion.time; |
| 1441 | |
| 1442 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); |
| 1443 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); |
| 1444 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww |
| 1445 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) |
| 1446 | { |
| 1447 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
| 1448 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) |
| 1449 | togglefloating(NULL); |
| 1450 | } |
| 1451 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
| 1452 | resize(c, c->x, c->y, nw, nh, 1); |
| 1453 | break; |
| 1454 | } |
| 1455 | } while (ev.type != ButtonRelease); |
| 1456 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
| 1457 | XUngrabPointer(dpy, CurrentTime); |
| 1458 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
| 1459 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
| 1460 | sendmon(c, m); |
| 1461 | selmon = m; |
| 1462 | focus(NULL); |
| 1463 | } |
| 1464 | } |
| 1465 | |
| 1466 | void |
| 1467 | restack(Monitor *m) |
| 1468 | { |
| 1469 | Client *c; |
| 1470 | XEvent ev; |
| 1471 | XWindowChanges wc; |
| 1472 | |
| 1473 | drawbar(m); |
| 1474 | if (!m->sel) |
| 1475 | return; |
| 1476 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) |
| 1477 | XRaiseWindow(dpy, m->sel->win); |
| 1478 | if (m->lt[m->sellt]->arrange) { |
| 1479 | wc.stack_mode = Below; |
| 1480 | wc.sibling = m->barwin; |
| 1481 | for (c = m->stack; c; c = c->snext) |
| 1482 | if (!c->isfloating && ISVISIBLE(c)) { |
| 1483 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); |
| 1484 | wc.sibling = c->win; |
| 1485 | } |
| 1486 | } |
| 1487 | XSync(dpy, False); |
| 1488 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
| 1489 | } |
| 1490 | |
| 1491 | void |
| 1492 | run(void) |
| 1493 | { |
| 1494 | XEvent ev; |
| 1495 | /* main event loop */ |
| 1496 | XSync(dpy, False); |
| 1497 | while (running && !XNextEvent(dpy, &ev)) |
| 1498 | if (handler[ev.type]) |
| 1499 | handler[ev.type](&ev); /* call handler */ |
| 1500 | } |
| 1501 | |
| 1502 | void |
| 1503 | scan(void) |
| 1504 | { |
| 1505 | unsigned int i, num; |
| 1506 | Window d1, d2, *wins = NULL; |
| 1507 | XWindowAttributes wa; |
| 1508 | |
| 1509 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
| 1510 | for (i = 0; i < num; i++) { |
| 1511 | if (!XGetWindowAttributes(dpy, wins[i], &wa) |
| 1512 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
| 1513 | continue; |
| 1514 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
| 1515 | manage(wins[i], &wa); |
| 1516 | } |
| 1517 | for (i = 0; i < num; i++) { /* now the transients */ |
| 1518 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) |
| 1519 | continue; |
| 1520 | if (XGetTransientForHint(dpy, wins[i], &d1) |
| 1521 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
| 1522 | manage(wins[i], &wa); |
| 1523 | } |
| 1524 | if (wins) |
| 1525 | XFree(wins); |
| 1526 | } |
| 1527 | } |
| 1528 | |
| 1529 | void |
| 1530 | sendmon(Client *c, Monitor *m) |
| 1531 | { |
| 1532 | if (c->mon == m) |
| 1533 | return; |
| 1534 | unfocus(c, 1); |
| 1535 | detach(c); |
| 1536 | detachstack(c); |
| 1537 | c->mon = m; |
| 1538 | c->tags = (m->tagset[m->seltags] ? m->tagset[m->seltags] : 1); |
| 1539 | attach(c); |
| 1540 | attachstack(c); |
| 1541 | focus(NULL); |
| 1542 | arrange(NULL); |
| 1543 | } |
| 1544 | |
| 1545 | void |
| 1546 | setclientstate(Client *c, long state) |
| 1547 | { |
| 1548 | long data[] = { state, None }; |
| 1549 | |
| 1550 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
| 1551 | PropModeReplace, (unsigned char *)data, 2); |
| 1552 | } |
| 1553 | |
| 1554 | int |
| 1555 | sendevent(Client *c, Atom proto) |
| 1556 | { |
| 1557 | int n; |
| 1558 | Atom *protocols; |
| 1559 | int exists = 0; |
| 1560 | XEvent ev; |
| 1561 | |
| 1562 | if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { |
| 1563 | while (!exists && n--) |
| 1564 | exists = protocols[n] == proto; |
| 1565 | XFree(protocols); |
| 1566 | } |
| 1567 | if (exists) { |
| 1568 | ev.type = ClientMessage; |
| 1569 | ev.xclient.window = c->win; |
| 1570 | ev.xclient.message_type = wmatom[WMProtocols]; |
| 1571 | ev.xclient.format = 32; |
| 1572 | ev.xclient.data.l[0] = proto; |
| 1573 | ev.xclient.data.l[1] = CurrentTime; |
| 1574 | XSendEvent(dpy, c->win, False, NoEventMask, &ev); |
| 1575 | } |
| 1576 | return exists; |
| 1577 | } |
| 1578 | |
| 1579 | void |
| 1580 | setfocus(Client *c) |
| 1581 | { |
| 1582 | if (!c->neverfocus) { |
| 1583 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
| 1584 | XChangeProperty(dpy, root, netatom[NetActiveWindow], |
| 1585 | XA_WINDOW, 32, PropModeReplace, |
| 1586 | (unsigned char *) &(c->win), 1); |
| 1587 | } |
| 1588 | sendevent(c, wmatom[WMTakeFocus]); |
| 1589 | } |
| 1590 | |
| 1591 | void |
| 1592 | setfullscreen(Client *c, int fullscreen) |
| 1593 | { |
| 1594 | if (fullscreen && !c->isfullscreen) { |
| 1595 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
| 1596 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); |
| 1597 | c->isfullscreen = 1; |
| 1598 | c->oldstate = c->isfloating; |
| 1599 | c->oldbw = c->bw; |
| 1600 | c->bw = 0; |
| 1601 | c->isfloating = 1; |
| 1602 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); |
| 1603 | XRaiseWindow(dpy, c->win); |
| 1604 | } else if (!fullscreen && c->isfullscreen){ |
| 1605 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
| 1606 | PropModeReplace, (unsigned char*)0, 0); |
| 1607 | c->isfullscreen = 0; |
| 1608 | c->isfloating = c->oldstate; |
| 1609 | c->bw = c->oldbw; |
| 1610 | c->x = c->oldx; |
| 1611 | c->y = c->oldy; |
| 1612 | c->w = c->oldw; |
| 1613 | c->h = c->oldh; |
| 1614 | resizeclient(c, c->x, c->y, c->w, c->h); |
| 1615 | arrange(c->mon); |
| 1616 | } |
| 1617 | } |
| 1618 | |
| 1619 | void |
| 1620 | setlayout(const Arg *arg) |
| 1621 | { |
| 1622 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) |
| 1623 | selmon->sellt ^= 1; |
| 1624 | if (arg && arg->v) |
| 1625 | selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag] = (Layout *)arg->v; |
| 1626 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); |
| 1627 | if (selmon->sel) |
| 1628 | arrange(selmon); |
| 1629 | else |
| 1630 | drawbar(selmon); |
| 1631 | } |
| 1632 | |
| 1633 | /* arg > 1.0 will set mfact absolutely */ |
| 1634 | void |
| 1635 | setmfact(const Arg *arg) |
| 1636 | { |
| 1637 | float f; |
| 1638 | |
| 1639 | if (!arg || !selmon->lt[selmon->sellt]->arrange) |
| 1640 | return; |
| 1641 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; |
| 1642 | if (f < 0.05 || f > 0.95) |
| 1643 | return; |
| 1644 | selmon->mfact = selmon->mfacts[selmon->curtag] = f; |
| 1645 | arrange(selmon); |
| 1646 | } |
| 1647 | |
| 1648 | void |
| 1649 | setup(void) |
| 1650 | { |
| 1651 | int i; |
| 1652 | XSetWindowAttributes wa; |
| 1653 | Atom utf8string; |
| 1654 | |
| 1655 | /* clean up any zombies immediately */ |
| 1656 | sigchld(0); |
| 1657 | |
| 1658 | /* init screen */ |
| 1659 | screen = DefaultScreen(dpy); |
| 1660 | sw = DisplayWidth(dpy, screen); |
| 1661 | sh = DisplayHeight(dpy, screen); |
| 1662 | root = RootWindow(dpy, screen); |
| 1663 | drw = drw_create(dpy, screen, root, sw, sh); |
| 1664 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) |
| 1665 | die("no fonts could be loaded."); |
| 1666 | lrpad = drw->fonts->h; |
| 1667 | bh = drw->fonts->h + 2; |
| 1668 | updategeom(); |
| 1669 | /* init atoms */ |
| 1670 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); |
| 1671 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
| 1672 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
| 1673 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
| 1674 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); |
| 1675 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); |
| 1676 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
| 1677 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
| 1678 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); |
| 1679 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); |
| 1680 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); |
| 1681 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); |
| 1682 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); |
| 1683 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); |
| 1684 | /* init cursors */ |
| 1685 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); |
| 1686 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); |
| 1687 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); |
| 1688 | /* init appearance */ |
| 1689 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); |
| 1690 | for (i = 0; i < LENGTH(colors); i++) |
| 1691 | scheme[i] = drw_scm_create(drw, colors[i], 3); |
| 1692 | /* init bars */ |
| 1693 | updatebars(); |
| 1694 | updatestatus(); |
| 1695 | /* supporting window for NetWMCheck */ |
| 1696 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); |
| 1697 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, |
| 1698 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
| 1699 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, |
| 1700 | PropModeReplace, (unsigned char *) "dwm", 3); |
| 1701 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, |
| 1702 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
| 1703 | /* EWMH support per view */ |
| 1704 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
| 1705 | PropModeReplace, (unsigned char *) netatom, NetLast); |
| 1706 | XDeleteProperty(dpy, root, netatom[NetClientList]); |
| 1707 | /* select events */ |
| 1708 | wa.cursor = cursor[CurNormal]->cursor; |
| 1709 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask |
| 1710 | |ButtonPressMask|PointerMotionMask|EnterWindowMask |
| 1711 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; |
| 1712 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); |
| 1713 | XSelectInput(dpy, root, wa.event_mask); |
| 1714 | grabkeys(); |
| 1715 | focus(NULL); |
| 1716 | } |
| 1717 | |
| 1718 | void |
| 1719 | seturgent(Client *c, int urg) |
| 1720 | { |
| 1721 | XWMHints *wmh; |
| 1722 | |
| 1723 | c->isurgent = urg; |
| 1724 | if (!(wmh = XGetWMHints(dpy, c->win))) |
| 1725 | return; |
| 1726 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); |
| 1727 | XSetWMHints(dpy, c->win, wmh); |
| 1728 | XFree(wmh); |
| 1729 | } |
| 1730 | |
| 1731 | void |
| 1732 | showhide(Client *c) |
| 1733 | { |
| 1734 | if (!c) |
| 1735 | return; |
| 1736 | if (ISVISIBLE(c)) { |
| 1737 | /* show clients top down */ |
| 1738 | XMoveWindow(dpy, c->win, c->x, c->y); |
| 1739 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) |
| 1740 | resize(c, c->x, c->y, c->w, c->h, 0); |
| 1741 | showhide(c->snext); |
| 1742 | } else { |
| 1743 | /* hide clients bottom up */ |
| 1744 | showhide(c->snext); |
| 1745 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); |
| 1746 | } |
| 1747 | } |
| 1748 | |
| 1749 | void |
| 1750 | sigchld(int unused) |
| 1751 | { |
| 1752 | if (signal(SIGCHLD, sigchld) == SIG_ERR) |
| 1753 | die("can't install SIGCHLD handler:"); |
| 1754 | while (0 < waitpid(-1, NULL, WNOHANG)); |
| 1755 | } |
| 1756 | |
| 1757 | void |
| 1758 | spawn(const Arg *arg) |
| 1759 | { |
| 1760 | if (fork() == 0) { |
| 1761 | if (dpy) |
| 1762 | close(ConnectionNumber(dpy)); |
| 1763 | setsid(); |
| 1764 | execvp(((char **)arg->v)[0], (char **)arg->v); |
| 1765 | die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); |
| 1766 | } |
| 1767 | } |
| 1768 | |
| 1769 | void |
| 1770 | tag(const Arg *arg) |
| 1771 | { |
| 1772 | if (selmon->sel && arg->ui & TAGMASK) { |
| 1773 | selmon->sel->tags = arg->ui & TAGMASK; |
| 1774 | focus(NULL); |
| 1775 | arrange(selmon); |
| 1776 | } |
| 1777 | } |
| 1778 | |
| 1779 | void |
| 1780 | tagmon(const Arg *arg) |
| 1781 | { |
| 1782 | if (!selmon->sel || !mons->next) |
| 1783 | return; |
| 1784 | sendmon(selmon->sel, dirtomon(arg->i)); |
| 1785 | } |
| 1786 | |
| 1787 | void |
| 1788 | togglebar(const Arg *arg) |
| 1789 | { |
| 1790 | selmon->showbar = selmon->showbars[selmon->curtag] = !selmon->showbar; |
| 1791 | updatebarpos(selmon); |
| 1792 | XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); |
| 1793 | arrange(selmon); |
| 1794 | } |
| 1795 | |
| 1796 | void |
| 1797 | togglefloating(const Arg *arg) |
| 1798 | { |
| 1799 | if (!selmon->sel) |
| 1800 | return; |
| 1801 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ |
| 1802 | return; |
| 1803 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; |
| 1804 | if (selmon->sel->isfloating) |
| 1805 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, |
| 1806 | selmon->sel->w, selmon->sel->h, 0); |
| 1807 | arrange(selmon); |
| 1808 | } |
| 1809 | |
| 1810 | void |
| 1811 | toggletag(const Arg *arg) |
| 1812 | { |
| 1813 | unsigned int newtags; |
| 1814 | unsigned int i; |
| 1815 | |
| 1816 | if (!selmon->sel) |
| 1817 | return; |
| 1818 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); |
| 1819 | if (newtags) { |
| 1820 | selmon->sel->tags = newtags; |
| 1821 | if(newtags == ~0) { |
| 1822 | selmon->prevtag = selmon->curtag; |
| 1823 | selmon->curtag = 0; |
| 1824 | } |
| 1825 | if(!(newtags & 1 << (selmon->curtag - 1))) { |
| 1826 | selmon->prevtag = selmon->curtag; |
| 1827 | for (i=0; !(newtags & 1 << i); i++); |
| 1828 | selmon->curtag = i + 1; |
| 1829 | } |
| 1830 | selmon->sel->tags = newtags; |
| 1831 | selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag]; |
| 1832 | selmon->mfact = selmon->mfacts[selmon->curtag]; |
| 1833 | if (selmon->showbar != selmon->showbars[selmon->curtag]) |
| 1834 | togglebar(NULL); |
| 1835 | selmon->ltaxis[0] = selmon->ltaxes[selmon->curtag][0]; |
| 1836 | selmon->ltaxis[1] = selmon->ltaxes[selmon->curtag][1]; |
| 1837 | selmon->ltaxis[2] = selmon->ltaxes[selmon->curtag][2]; |
| 1838 | selmon->msplit = selmon->msplits[selmon->curtag]; |
| 1839 | focus(NULL); |
| 1840 | arrange(selmon); |
| 1841 | } |
| 1842 | } |
| 1843 | |
| 1844 | void |
| 1845 | toggleview(const Arg *arg) |
| 1846 | { |
| 1847 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); |
| 1848 | |
| 1849 | selmon->tagset[selmon->seltags] = newtagset; |
| 1850 | focus(NULL); |
| 1851 | arrange(selmon); |
| 1852 | } |
| 1853 | |
| 1854 | void |
| 1855 | unfocus(Client *c, int setfocus) |
| 1856 | { |
| 1857 | if (!c) |
| 1858 | return; |
| 1859 | grabbuttons(c, 0); |
| 1860 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); |
| 1861 | if (setfocus) { |
| 1862 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
| 1863 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
| 1864 | } |
| 1865 | } |
| 1866 | |
| 1867 | void |
| 1868 | unmanage(Client *c, int destroyed) |
| 1869 | { |
| 1870 | Monitor *m = c->mon; |
| 1871 | XWindowChanges wc; |
| 1872 | |
| 1873 | detach(c); |
| 1874 | detachstack(c); |
| 1875 | if (!destroyed) { |
| 1876 | wc.border_width = c->oldbw; |
| 1877 | XGrabServer(dpy); /* avoid race conditions */ |
| 1878 | XSetErrorHandler(xerrordummy); |
| 1879 | XSelectInput(dpy, c->win, NoEventMask); |
| 1880 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
| 1881 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
| 1882 | setclientstate(c, WithdrawnState); |
| 1883 | XSync(dpy, False); |
| 1884 | XSetErrorHandler(xerror); |
| 1885 | XUngrabServer(dpy); |
| 1886 | } |
| 1887 | free(c); |
| 1888 | focus(NULL); |
| 1889 | updateclientlist(); |
| 1890 | arrange(m); |
| 1891 | } |
| 1892 | |
| 1893 | void |
| 1894 | unmapnotify(XEvent *e) |
| 1895 | { |
| 1896 | Client *c; |
| 1897 | XUnmapEvent *ev = &e->xunmap; |
| 1898 | |
| 1899 | if ((c = wintoclient(ev->window))) { |
| 1900 | if (ev->send_event) |
| 1901 | setclientstate(c, WithdrawnState); |
| 1902 | else |
| 1903 | unmanage(c, 0); |
| 1904 | } |
| 1905 | } |
| 1906 | |
| 1907 | void |
| 1908 | updatebars(void) |
| 1909 | { |
| 1910 | Monitor *m; |
| 1911 | XSetWindowAttributes wa = { |
| 1912 | .override_redirect = True, |
| 1913 | .background_pixmap = ParentRelative, |
| 1914 | .event_mask = ButtonPressMask|ExposureMask |
| 1915 | }; |
| 1916 | XClassHint ch = {"dwm", "dwm"}; |
| 1917 | for (m = mons; m; m = m->next) { |
| 1918 | if (m->barwin) |
| 1919 | continue; |
| 1920 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), |
| 1921 | CopyFromParent, DefaultVisual(dpy, screen), |
| 1922 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); |
| 1923 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); |
| 1924 | XMapRaised(dpy, m->barwin); |
| 1925 | XSetClassHint(dpy, m->barwin, &ch); |
| 1926 | } |
| 1927 | } |
| 1928 | |
| 1929 | void |
| 1930 | updatebarpos(Monitor *m) |
| 1931 | { |
| 1932 | m->wy = m->my; |
| 1933 | m->wh = m->mh; |
| 1934 | if (m->showbar) { |
| 1935 | m->wh -= bh; |
| 1936 | m->by = m->topbar ? m->wy : m->wy + m->wh; |
| 1937 | m->wy = m->topbar ? m->wy + bh : m->wy; |
| 1938 | } else |
| 1939 | m->by = -bh; |
| 1940 | } |
| 1941 | |
| 1942 | void |
| 1943 | updateclientlist() |
| 1944 | { |
| 1945 | Client *c; |
| 1946 | Monitor *m; |
| 1947 | |
| 1948 | XDeleteProperty(dpy, root, netatom[NetClientList]); |
| 1949 | for (m = mons; m; m = m->next) |
| 1950 | for (c = m->clients; c; c = c->next) |
| 1951 | XChangeProperty(dpy, root, netatom[NetClientList], |
| 1952 | XA_WINDOW, 32, PropModeAppend, |
| 1953 | (unsigned char *) &(c->win), 1); |
| 1954 | } |
| 1955 | |
| 1956 | int |
| 1957 | updategeom(void) |
| 1958 | { |
| 1959 | int dirty = 0; |
| 1960 | |
| 1961 | #ifdef XINERAMA |
| 1962 | if (XineramaIsActive(dpy)) { |
| 1963 | int i, j, n, nn; |
| 1964 | Client *c; |
| 1965 | Monitor *m; |
| 1966 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); |
| 1967 | XineramaScreenInfo *unique = NULL; |
| 1968 | |
| 1969 | for (n = 0, m = mons; m; m = m->next, n++); |
| 1970 | /* only consider unique geometries as separate screens */ |
| 1971 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); |
| 1972 | for (i = 0, j = 0; i < nn; i++) |
| 1973 | if (isuniquegeom(unique, j, &info[i])) |
| 1974 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); |
| 1975 | XFree(info); |
| 1976 | nn = j; |
| 1977 | |
| 1978 | /* new monitors if nn > n */ |
| 1979 | for (i = n; i < nn; i++) { |
| 1980 | for (m = mons; m && m->next; m = m->next); |
| 1981 | if (m) |
| 1982 | m->next = createmon(); |
| 1983 | else |
| 1984 | mons = createmon(); |
| 1985 | } |
| 1986 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) |
| 1987 | if (i >= n |
| 1988 | || unique[i].x_org != m->mx || unique[i].y_org != m->my |
| 1989 | || unique[i].width != m->mw || unique[i].height != m->mh) |
| 1990 | { |
| 1991 | dirty = 1; |
| 1992 | m->num = i; |
| 1993 | m->mx = m->wx = unique[i].x_org; |
| 1994 | m->my = m->wy = unique[i].y_org; |
| 1995 | m->mw = m->ww = unique[i].width; |
| 1996 | m->mh = m->wh = unique[i].height; |
| 1997 | updatebarpos(m); |
| 1998 | } |
| 1999 | /* removed monitors if n > nn */ |
| 2000 | for (i = nn; i < n; i++) { |
| 2001 | for (m = mons; m && m->next; m = m->next); |
| 2002 | while ((c = m->clients)) { |
| 2003 | dirty = 1; |
| 2004 | m->clients = c->next; |
| 2005 | detachstack(c); |
| 2006 | c->mon = mons; |
| 2007 | attach(c); |
| 2008 | attachstack(c); |
| 2009 | } |
| 2010 | if (m == selmon) |
| 2011 | selmon = mons; |
| 2012 | cleanupmon(m); |
| 2013 | } |
| 2014 | free(unique); |
| 2015 | } else |
| 2016 | #endif /* XINERAMA */ |
| 2017 | { /* default monitor setup */ |
| 2018 | if (!mons) |
| 2019 | mons = createmon(); |
| 2020 | if (mons->mw != sw || mons->mh != sh) { |
| 2021 | dirty = 1; |
| 2022 | mons->mw = mons->ww = sw; |
| 2023 | mons->mh = mons->wh = sh; |
| 2024 | updatebarpos(mons); |
| 2025 | } |
| 2026 | } |
| 2027 | if (dirty) { |
| 2028 | selmon = mons; |
| 2029 | selmon = wintomon(root); |
| 2030 | } |
| 2031 | return dirty; |
| 2032 | } |
| 2033 | |
| 2034 | void |
| 2035 | updatenumlockmask(void) |
| 2036 | { |
| 2037 | unsigned int i, j; |
| 2038 | XModifierKeymap *modmap; |
| 2039 | |
| 2040 | numlockmask = 0; |
| 2041 | modmap = XGetModifierMapping(dpy); |
| 2042 | for (i = 0; i < 8; i++) |
| 2043 | for (j = 0; j < modmap->max_keypermod; j++) |
| 2044 | if (modmap->modifiermap[i * modmap->max_keypermod + j] |
| 2045 | == XKeysymToKeycode(dpy, XK_Num_Lock)) |
| 2046 | numlockmask = (1 << i); |
| 2047 | XFreeModifiermap(modmap); |
| 2048 | } |
| 2049 | |
| 2050 | void |
| 2051 | updatesizehints(Client *c) |
| 2052 | { |
| 2053 | long msize; |
| 2054 | XSizeHints size; |
| 2055 | |
| 2056 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) |
| 2057 | /* size is uninitialized, ensure that size.flags aren't used */ |
| 2058 | size.flags = PSize; |
| 2059 | if (size.flags & PBaseSize) { |
| 2060 | c->basew = size.base_width; |
| 2061 | c->baseh = size.base_height; |
| 2062 | } else if (size.flags & PMinSize) { |
| 2063 | c->basew = size.min_width; |
| 2064 | c->baseh = size.min_height; |
| 2065 | } else |
| 2066 | c->basew = c->baseh = 0; |
| 2067 | if (size.flags & PResizeInc) { |
| 2068 | c->incw = size.width_inc; |
| 2069 | c->inch = size.height_inc; |
| 2070 | } else |
| 2071 | c->incw = c->inch = 0; |
| 2072 | if (size.flags & PMaxSize) { |
| 2073 | c->maxw = size.max_width; |
| 2074 | c->maxh = size.max_height; |
| 2075 | } else |
| 2076 | c->maxw = c->maxh = 0; |
| 2077 | if (size.flags & PMinSize) { |
| 2078 | c->minw = size.min_width; |
| 2079 | c->minh = size.min_height; |
| 2080 | } else if (size.flags & PBaseSize) { |
| 2081 | c->minw = size.base_width; |
| 2082 | c->minh = size.base_height; |
| 2083 | } else |
| 2084 | c->minw = c->minh = 0; |
| 2085 | if (size.flags & PAspect) { |
| 2086 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; |
| 2087 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; |
| 2088 | } else |
| 2089 | c->maxa = c->mina = 0.0; |
| 2090 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); |
| 2091 | c->hintsvalid = 1; |
| 2092 | } |
| 2093 | |
| 2094 | void |
| 2095 | updatestatus(void) |
| 2096 | { |
| 2097 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) |
| 2098 | strcpy(stext, "dwm-"VERSION); |
| 2099 | drawbar(selmon); |
| 2100 | } |
| 2101 | |
| 2102 | void |
| 2103 | updatetitle(Client *c) |
| 2104 | { |
| 2105 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
| 2106 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); |
| 2107 | if (c->name[0] == '\0') /* hack to mark broken clients */ |
| 2108 | strcpy(c->name, broken); |
| 2109 | } |
| 2110 | |
| 2111 | void |
| 2112 | updatewindowtype(Client *c) |
| 2113 | { |
| 2114 | Atom state = getatomprop(c, netatom[NetWMState]); |
| 2115 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); |
| 2116 | |
| 2117 | if (state == netatom[NetWMFullscreen]) |
| 2118 | setfullscreen(c, 1); |
| 2119 | if (wtype == netatom[NetWMWindowTypeDialog]) |
| 2120 | c->isfloating = 1; |
| 2121 | } |
| 2122 | |
| 2123 | void |
| 2124 | updatewmhints(Client *c) |
| 2125 | { |
| 2126 | XWMHints *wmh; |
| 2127 | |
| 2128 | if ((wmh = XGetWMHints(dpy, c->win))) { |
| 2129 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { |
| 2130 | wmh->flags &= ~XUrgencyHint; |
| 2131 | XSetWMHints(dpy, c->win, wmh); |
| 2132 | } else |
| 2133 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; |
| 2134 | if (wmh->flags & InputHint) |
| 2135 | c->neverfocus = !wmh->input; |
| 2136 | else |
| 2137 | c->neverfocus = 0; |
| 2138 | XFree(wmh); |
| 2139 | } |
| 2140 | } |
| 2141 | |
| 2142 | void |
| 2143 | view(const Arg *arg) |
| 2144 | { |
| 2145 | unsigned int i; |
| 2146 | |
| 2147 | if(arg->ui && (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) |
| 2148 | return; |
| 2149 | selmon->seltags ^= 1; /* toggle sel tagset */ |
| 2150 | if (arg->ui & TAGMASK) { |
| 2151 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; |
| 2152 | selmon->prevtag = selmon->curtag; |
| 2153 | if(arg->ui == ~0) |
| 2154 | selmon->curtag = 0; |
| 2155 | else { |
| 2156 | for (i=0; !(arg->ui & 1 << i); i++); |
| 2157 | selmon->curtag = i + 1; |
| 2158 | } |
| 2159 | } else { |
| 2160 | selmon->prevtag = selmon->curtag ^ selmon->prevtag; |
| 2161 | selmon->curtag ^= selmon->prevtag; |
| 2162 | selmon->prevtag = selmon->curtag ^ selmon->prevtag; |
| 2163 | } |
| 2164 | selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag]; |
| 2165 | selmon->mfact = selmon->mfacts[selmon->curtag]; |
| 2166 | if(selmon->showbar != selmon->showbars[selmon->curtag]) |
| 2167 | togglebar(NULL); |
| 2168 | selmon->ltaxis[0] = selmon->ltaxes[selmon->curtag][0]; |
| 2169 | selmon->ltaxis[1] = selmon->ltaxes[selmon->curtag][1]; |
| 2170 | selmon->ltaxis[2] = selmon->ltaxes[selmon->curtag][2]; |
| 2171 | selmon->msplit = selmon->msplits[selmon->curtag]; |
| 2172 | focus(NULL); |
| 2173 | arrange(selmon); |
| 2174 | } |
| 2175 | |
| 2176 | Client * |
| 2177 | wintoclient(Window w) |
| 2178 | { |
| 2179 | Client *c; |
| 2180 | Monitor *m; |
| 2181 | |
| 2182 | for (m = mons; m; m = m->next) |
| 2183 | for (c = m->clients; c; c = c->next) |
| 2184 | if (c->win == w) |
| 2185 | return c; |
| 2186 | return NULL; |
| 2187 | } |
| 2188 | |
| 2189 | Monitor * |
| 2190 | wintomon(Window w) |
| 2191 | { |
| 2192 | int x, y; |
| 2193 | Client *c; |
| 2194 | Monitor *m; |
| 2195 | |
| 2196 | if (w == root && getrootptr(&x, &y)) |
| 2197 | return recttomon(x, y, 1, 1); |
| 2198 | for (m = mons; m; m = m->next) |
| 2199 | if (w == m->barwin) |
| 2200 | return m; |
| 2201 | if ((c = wintoclient(w))) |
| 2202 | return c->mon; |
| 2203 | return selmon; |
| 2204 | } |
| 2205 | |
| 2206 | /* There's no way to check accesses to destroyed windows, thus those cases are |
| 2207 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
| 2208 | * default error handler, which may call exit. */ |
| 2209 | int |
| 2210 | xerror(Display *dpy, XErrorEvent *ee) |
| 2211 | { |
| 2212 | if (ee->error_code == BadWindow |
| 2213 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
| 2214 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
| 2215 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
| 2216 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
| 2217 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
| 2218 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) |
| 2219 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
| 2220 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
| 2221 | return 0; |
| 2222 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
| 2223 | ee->request_code, ee->error_code); |
| 2224 | return xerrorxlib(dpy, ee); /* may call exit */ |
| 2225 | } |
| 2226 | |
| 2227 | int |
| 2228 | xerrordummy(Display *dpy, XErrorEvent *ee) |
| 2229 | { |
| 2230 | return 0; |
| 2231 | } |
| 2232 | |
| 2233 | /* Startup Error handler to check if another window manager |
| 2234 | * is already running. */ |
| 2235 | int |
| 2236 | xerrorstart(Display *dpy, XErrorEvent *ee) |
| 2237 | { |
| 2238 | die("dwm: another window manager is already running"); |
| 2239 | return -1; |
| 2240 | } |
| 2241 | |
| 2242 | void |
| 2243 | zoom(const Arg *arg) |
| 2244 | { |
| 2245 | Client *c = selmon->sel; |
| 2246 | |
| 2247 | if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) |
| 2248 | return; |
| 2249 | if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) |
| 2250 | return; |
| 2251 | pop(c); |
| 2252 | } |
| 2253 | |
| 2254 | int |
| 2255 | main(int argc, char *argv[]) |
| 2256 | { |
| 2257 | if (argc == 2 && !strcmp("-v", argv[1])) |
| 2258 | die("dwm-"VERSION); |
| 2259 | else if (argc != 1) |
| 2260 | die("usage: dwm [-v]"); |
| 2261 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
| 2262 | fputs("warning: no locale support\n", stderr); |
| 2263 | if (!(dpy = XOpenDisplay(NULL))) |
| 2264 | die("dwm: cannot open display"); |
| 2265 | checkotherwm(); |
| 2266 | setup(); |
| 2267 | #ifdef __OpenBSD__ |
| 2268 | if (pledge("stdio rpath proc exec", NULL) == -1) |
| 2269 | die("pledge"); |
| 2270 | #endif /* __OpenBSD__ */ |
| 2271 | scan(); |
| 2272 | run(); |
| 2273 | cleanup(); |
| 2274 | XCloseDisplay(dpy); |
| 2275 | if (restart == 1) { |
| 2276 | execlp("dwm-sgk", "dwm-sgk", NULL); |
| 2277 | } |
| 2278 | return EXIT_SUCCESS; |
| 2279 | } |