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