0 /* See LICENSE file for copyright and license details.

1 *

2 * dynamic window manager is designed like any other X client as well. It is

3 * driven through handling X events. In contrast to other X clients, a window

4 * manager selects for SubstructureRedirectMask on the root window, to receive

5 * events about window (dis-)appearance. Only one X connection at a time is

6 * allowed to select for this event mask.

7 *

8 * The event handlers of dwm are organized in an array which is accessed

9 * whenever a new event has been fetched. This allows event dispatching

10 * in O(1) time.

11 *

12 * Each child of the root window is called a client, except windows which have

13 * set the override_redirect flag. Clients are organized in a linked client

14 * list on each monitor, the focus history is remembered through a stack list

15 * on each monitor. Each client contains a bit array to indicate the tags of a

16 * client.

17 *

18 * Keys and tagging rules are organized as arrays and defined in config.h.

19 *

20 * To understand everything else, start reading main().

21 */

22 #include <errno.h>

23 #include <locale.h>

24 #include <signal.h>

25 #include <stdarg.h>

26 #include <stdio.h>

27 #include <stdlib.h>

28 #include <string.h>

29 #include <unistd.h>

30 #include <time.h>

31 #include <pthread.h>

32 #include <sys/types.h>

33 #include <sys/wait.h>

34 #include <X11/cursorfont.h>

35 #include <X11/keysym.h>

36 #include <X11/Xatom.h>

37 #include <X11/Xlib.h>

38 #include <X11/Xproto.h>

39 #include <X11/Xutil.h>

40 #ifdef XINERAMA

41 #include <X11/extensions/Xinerama.h>

42 #endif /* XINERAMA */

43 #include <X11/Xft/Xft.h>

44

45 #include "drw.h"

46 #include "util.h"

47

48 /* macros */

49 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)

50 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & \

51 (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|\

52 Mod3Mask|Mod4Mask|Mod5Mask))

53 #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) \

54 - MAX((x),(m)->wx)) \

55 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) \

56 - MAX((y),(m)->wy)))

57 #define ISVISIBLE(C) (workspace == C->workspace)

58 #define LENGTH(X) (sizeof X / sizeof X[0])

59 #define MOUSEMASK (BUTTONMASK|PointerMotionMask)

60 #define WIDTH(X) ((X)->w + 2 * (X)->bw)

61 #define HEIGHT(X) ((X)->h + 2 * (X)->bw)

62 #define TAGMASK ((1 << LENGTH(tags)) - 1)

63 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)

64 #define ATOM(X) XInternAtom(dpy, X, False)

65

66 /* enums */

67 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */

68 enum { SchemeNorm, SchemeSel }; /* color schemes */

69 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,

70 NetWMFullscreen, NetActiveWindow, NetWMWindowType,

71 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */

72 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast };

73 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,

74 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */

75

76 typedef union {

77 int i;

78 unsigned int ui;

79 float f;

80 const void *v;

81 } Arg;

82

83 typedef struct {

84 unsigned int click;

85 unsigned int mask;

86 unsigned int button;

87 void (*func)(const Arg *arg);

88 const Arg arg;

89 } Button;

90

91 typedef struct Monitor Monitor;

92 typedef struct Client Client;

93 typedef struct Hidden Hidden;

94 struct Client {

95 char name[256];

96 float mina, maxa;

97 int x, y, w, h;

98 int oldx, oldy, oldw, oldh;

99 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;

100 int bw, oldbw;

101 int isfixed, isurgent, neverfocus, oldstate, isfullscreen;

102 int nx, ny, nw, nh;

103 int ismaximized;

104 int workspace;

105 Client *next;

106 Client *snext;

107 Monitor *mon;

108 Window win;

109 int backward;

110 };

111

112 typedef struct {

113 unsigned int mod;

114 KeySym keysym;

115 void (*func)(const Arg *);

116 const Arg arg;

117 } Key;

118

119 struct Monitor {

120 int num;

121 int by; /* bar geometry */

122 int mx, my, mw, mh; /* screen size */

123 int wx, wy, ww, wh; /* window area */

124 int showbar;

125 int topbar;

126 Monitor *next;

127 Window barwin;

128 };

129

130 struct Hidden {

131 struct Hidden *next;

132 struct Hidden *prev;

133 Window window;

134 };

135

136 /* function declarations */

137 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int act);

138 static void arrange(Monitor *m);

139 static void attach(Client *c);

140 static void attachstack(Client *c);

141 static void buttonpress(XEvent *e);

142 static void checkotherwm(void);

143 static void cleanup(void);

144 static void cleanupmon(Monitor *mon);

145 static void clientmessage(XEvent *e);

146 static void configure(Client *c);

147 static void configurenotify(XEvent *e);

148 static void configurerequest(XEvent *e);

149 static Monitor *createmon(void);

150 static void destroynotify(XEvent *e);

151 static void detach(Client *c);

152 static void detachstack(Client *c);

153 static void drawbar(Monitor *m);

154 static void drawbars(void);

155 static void expose(XEvent *e);

156 static void focus(Client *c);

157 static void focusin(XEvent *e);

158 static Atom getatomprop(Client *c, Atom prop);

159 static int getrootptr(int *x, int *y);

160 static long getstate(Window w);

161 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);

162 static void grabbuttons(Client *c, int focused);

163 static void grabkeys(void);

164 static void keypress(XEvent *e);

165 static void killclient(const Arg *arg);

166 static void manage(Window w, XWindowAttributes *wa);

167 static void mappingnotify(XEvent *e);

168 static void maprequest(XEvent *e);

169 static void motionnotify(XEvent *e);

170 static void movemouse(const Arg *arg);

171 static void propertynotify(XEvent *e);

172 static void quit(const Arg *arg);

173 static Monitor *recttomon(int x, int y, int w, int h);

174 static void resize(Client *c, int x, int y, int w, int h, int interact);

175 static void resizeclient(Client *c, int x, int y, int w, int h);

176 static void resizemouse(const Arg *arg);

177 static void restack(Monitor *m);

178 static void run(void);

179 static void scan(void);

180 static int sendevent(Client *c, Atom proto);

181 static void sendmon(Client *c, Monitor *m);

182 static void setclientstate(Client *c, long state);

183 static void setfocus(Client *c);

184 static void setfullscreen(Client *c, int fullscreen);

185 static void setup(void);

186 static void seturgent(Client *c, int urg);

187 static void showhide(Client *c);

188 static void sigchld(int unused);

189 static void spawn(const Arg *arg);

190 static void togglebar(const Arg *arg);

191 static void unfocus(Client *c, int setfocus);

192 static void unmanage(Client *c, int destroyed);

193 static void unmapnotify(XEvent *e);

194 static void updatebarpos(Monitor *m);

195 static void updatebars(void);

196 static void updateclientlist(void);

197 static int updategeom(void);

198 static void updatenumlockmask(void);

199 static void updatesizehints(Client *c);

200 static void* update(void* args);

201 static int updatestatus(void);

202 static void updatetitle(Client *c);

203 static void updatewindowtype(Client *c);

204 static void updatewmhints(Client *c);

205 static Client *wintoclient(Window w);

206 static Monitor *wintomon(Window w);

207 static int xerror(Display *dpy, XErrorEvent *ee);

208 static int xerrordummy(Display *dpy, XErrorEvent *ee);

209 static int xerrorstart(Display *dpy, XErrorEvent *ee);

210

211 static void focusnext(const Arg *arg);

212 static void focusprev(const Arg *arg);

213 static void maximize(const Arg *arg);

214 static void minimize(const Arg *arg);

215 static void reveal(const Arg *arg);

216 static void movex(const Arg *arg);

217 static void movey(const Arg *arg);

218 static void resizex(const Arg *arg);

219 static void resizey(const Arg *arg);

220 static void nextscreen(const Arg *arg);

221 static void prevscreen(const Arg *arg);

222 static void nextworkspace(const Arg *arg);

223 static void moveworkspace(const Arg *arg);

224

225 /* variables */

226 static const char broken[] = "broken";

227 static char stext[256];

228 static int screen;

229 static int sw, sh; /* X display screen geometry width, height */

230 static int bh; /* bar height */

231 static int lrpad; /* sum of left and right padding for text */

232 static int (*xerrorxlib)(Display *, XErrorEvent *);

233 static unsigned int numlockmask = 0;

234 static void (*handler[LASTEvent]) (XEvent *) = {

235 [ButtonPress] = buttonpress,

236 [ClientMessage] = clientmessage,

237 [ConfigureRequest] = configurerequest,

238 [ConfigureNotify] = configurenotify,

239 [DestroyNotify] = destroynotify,

240 [Expose] = expose,

241 [FocusIn] = focusin,

242 [KeyPress] = keypress,

243 [MappingNotify] = mappingnotify,

244 [MapRequest] = maprequest,

245 [MotionNotify] = motionnotify,

246 [PropertyNotify] = propertynotify,

247 [UnmapNotify] = unmapnotify

248 };

249 static Atom wmatom[WMLast], netatom[NetLast];

250 static int running = 1;

251 static Cur *cursor[CurLast];

252 static Clr **scheme;

253 static Display *dpy;

254 static Drw *drw;

255 static Monitor *mons, *selmon;

256 static Window root, wmcheckwin;

257 static Hidden *hidden = NULL;

258 static int workspace = 0;

259

260 static Client *clients;

261 static Client *sel;

262 static Client *stack;

263

264 /* configuration, allows nested code to access above variables */

265 #include "config.h"

266 #define WM "fdwm"

267 #define WORKSPACES 3

268

269

270 int

271 applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)

272 {

273 int baseismin;

274 Monitor *m = c->mon;

275

276 /* set minimum possible */

277 *w = MAX(1, *w);

278 *h = MAX(1, *h);

279 if (interact) {

280 if (*x > sw)

281 *x = sw - WIDTH(c);

282 if (*y > sh)

283 *y = sh - HEIGHT(c);

284 if (*x + *w + 2 * c->bw < 0)

285 *x = 0;

286 if (*y + *h + 2 * c->bw < 0)

287 *y = 0;

288 } else {

289 if (*x >= m->wx + m->ww)

290 *x = m->wx + m->ww - WIDTH(c);

291 if (*y >= m->wy + m->wh)

292 *y = m->wy + m->wh - HEIGHT(c);

293 if (*x + *w + 2 * c->bw <= m->wx)

294 *x = m->wx;

295 if (*y + *h + 2 * c->bw <= m->wy)

296 *y = m->wy;

297 }

298 if (*h < bh)

299 *h = bh;

300 if (*w < bh)

301 *w = bh;

302 if (!c->hintsvalid)

303 updatesizehints(c);

304 /* see last two sentences in ICCCM 4.1.2.3 */

305 baseismin = c->basew == c->minw && c->baseh == c->minh;

306 if (!baseismin) { /* temporarily remove base dimensions */

307 *w -= c->basew;

308 *h -= c->baseh;

309 }

310 /* adjust for aspect limits */

311 #if ADJUST_ASPECT_RATIO

312 if (c->mina > 0 && c->maxa > 0) {

313 if (c->maxa < (float)*w / *h)

314 *w = *h * c->maxa + 0.5;

315 else if (c->mina < (float)*h / *w)

316 *h = *w * c->mina + 0.5;

317 }

318 #endif

319 if (baseismin) { /* increment calculation requires this */

320 *w -= c->basew;

321 *h -= c->baseh;

322 }

323 /* adjust for increment value */

324 if (c->incw)

325 *w -= *w % c->incw;

326 if (c->inch)

327 *h -= *h % c->inch;

328 /* restore base dimensions */

329 *w = MAX(*w + c->basew, c->minw);

330 *h = MAX(*h + c->baseh, c->minh);

331 if (c->maxw)

332 *w = MIN(*w, c->maxw);

333 if (c->maxh)

334 *h = MIN(*h, c->maxh);

335 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;

336 }

337

338 void

339 arrange(Monitor *m)

340 {

341 if (m) {

342 showhide(stack);

343 restack(m);

344 } else for (m = mons; m; m = m->next)

345 showhide(stack);

346 }

347

348 void

349 attach(Client *c)

350 {

351 c->next = clients;

352 clients = c;

353 }

354

355 void

356 attachstack(Client *c)

357 {

358 c->snext = stack;

359 stack = c;

360 }

361

362 void

363 buttonpress(XEvent *e)

364 {

365 unsigned int i, x, click;

366 Arg arg = {0};

367 Client *c;

368 Monitor *m;

369 XButtonPressedEvent *ev = &e->xbutton;

370

371 click = ClkRootWin;

372 /* focus monitor if necessary */

373 if ((m = wintomon(ev->window)) && m != selmon) {

374 unfocus(sel, 1);

375 selmon = m;

376 focus(NULL);

377 }

378 if (ev->window == selmon->barwin) {

379 i = x = 0;

380 if (ev->x > selmon->ww - (int)TEXTW(stext))

381 click = ClkStatusText;

382 else

383 click = ClkWinTitle;

384 } else if ((c = wintoclient(ev->window))) {

385 focus(c);

386 restack(selmon);

387 XAllowEvents(dpy, ReplayPointer, CurrentTime);

388 click = ClkClientWin;

389 }

390 for (i = 0; i < LENGTH(buttons); i++)

391 if (click == buttons[i].click

392 && buttons[i].func && buttons[i].button == ev->button

393 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))

394 buttons[i].func(click == ClkTagBar &&

395 buttons[i].arg.i == 0 ?

396 &arg : &buttons[i].arg);

397 }

398

399 void

400 checkotherwm(void)

401 {

402 xerrorxlib = XSetErrorHandler(xerrorstart);

403 /* this causes an error if some other window manager is running */

404 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);

405 XSync(dpy, False);

406 XSetErrorHandler(xerror);

407 XSync(dpy, False);

408 }

409

410 void

411 cleanup(void)

412 {

413 size_t i;

414

415 while (stack)

416 unmanage(stack, 0);

417 XUngrabKey(dpy, AnyKey, AnyModifier, root);

418 while (mons)

419 cleanupmon(mons);

420 for (i = 0; i < CurLast; i++)

421 drw_cur_free(drw, cursor[i]);

422 for (i = 0; i < LENGTH(colors); i++)

423 free(scheme[i]);

424 free(scheme);

425 XDestroyWindow(dpy, wmcheckwin);

426 drw_free(drw);

427 XSync(dpy, False);

428 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);

429 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);

430 }

431

432 void

433 cleanupmon(Monitor *mon)

434 {

435 Monitor *m;

436

437 if (mon == mons)

438 mons = mons->next;

439 else {

440 for (m = mons; m && m->next != mon; m = m->next);

441 m->next = mon->next;

442 }

443 XUnmapWindow(dpy, mon->barwin);

444 XDestroyWindow(dpy, mon->barwin);

445 free(mon);

446 }

447

448 void

449 clientmessage(XEvent *e)

450 {

451 XClientMessageEvent *cme = &e->xclient;

452 Client *c = wintoclient(cme->window);

453

454 if (!c)

455 return;

456 if (cme->message_type == netatom[NetWMState]) {

457 if (cme->data.l[1] == netatom[NetWMFullscreen]

458 || cme->data.l[2] == netatom[NetWMFullscreen])

459 setfullscreen(c,

460 (cme->data.l[0] == 1 || /* _NET_WM_STATE_ADD */

461 (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */

462 && !c->isfullscreen)));

463 } else if (cme->message_type == netatom[NetActiveWindow]) {

464 if (c != sel && !c->isurgent)

465 seturgent(c, 1);

466 }

467 }

468

469 void

470 configure(Client *c)

471 {

472 XConfigureEvent ce;

473

474 ce.type = ConfigureNotify;

475 ce.display = dpy;

476 ce.event = c->win;

477 ce.window = c->win;

478 ce.x = c->x;

479 ce.y = c->y;

480 ce.width = c->w;

481 ce.height = c->h;

482 ce.border_width = c->bw;

483 ce.above = None;

484 ce.override_redirect = False;

485 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);

486 }

487

488 void

489 configurenotify(XEvent *e)

490 {

491 Monitor *m;

492 Client *c;

493 XConfigureEvent *ev = &e->xconfigure;

494 int dirty;

495

496 /* TODO: updategeom handling sucks, needs to be simplified */

497 if (ev->window == root) {

498 dirty = (sw != ev->width || sh != ev->height);

499 sw = ev->width;

500 sh = ev->height;

501 if (updategeom() || dirty) {

502 drw_resize(drw, sw, bh);

503 updatebars();

504 for (m = mons; m; m = m->next) {

505 for (c = clients; c; c = c->next) {

506 if (!c->isfullscreen) continue;

507 resizeclient(c, c->mon->mx, c->mon->my,

508 c->mon->mw, c->mon->mh);

509 }

510 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by,

511 m->ww, bh);

512 }

513 focus(NULL);

514 arrange(NULL);

515 }

516 }

517 }

518

519 void

520 configurerequest(XEvent *e)

521 {

522 Client *c;

523 Monitor *m;

524 XConfigureRequestEvent *ev = &e->xconfigurerequest;

525 XWindowChanges wc;

526

527 if ((c = wintoclient(ev->window))) {

528 if (ev->value_mask & CWBorderWidth)

529 c->bw = ev->border_width;

530 else {

531 m = c->mon;

532 if (ev->value_mask & CWX) {

533 c->oldx = c->x;

534 c->x = m->mx + ev->x;

535 }

536 if (ev->value_mask & CWY) {

537 c->oldy = c->y;

538 c->y = m->my + ev->y;

539 }

540 if (ev->value_mask & CWWidth) {

541 c->oldw = c->w;

542 c->w = ev->width;

543 }

544 if (ev->value_mask & CWHeight) {

545 c->oldh = c->h;

546 c->h = ev->height;

547 }

548 if ((c->x + c->w) > m->mx + m->mw)

549 /* center in x direction */

550 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2);

551 if ((c->y + c->h) > m->my + m->mh)

552 /* center in y direction */

553 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2);

554 if ((ev->value_mask & (CWX|CWY))

555 && !(ev->value_mask & (CWWidth|CWHeight)))

556 configure(c);

557 if (ISVISIBLE(c))

558 XMoveResizeWindow(dpy, c->win, c->x, c->y,

559 c->w, c->h);

560 }

561 } else {

562 wc.x = ev->x;

563 wc.y = ev->y;

564 wc.width = ev->width;

565 wc.height = ev->height;

566 wc.border_width = ev->border_width;

567 wc.sibling = ev->above;

568 wc.stack_mode = ev->detail;

569 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);

570 }

571 XSync(dpy, False);

572 }

573

574 Monitor *

575 createmon(void)

576 {

577 Monitor *m;

578

579 m = ecalloc(1, sizeof(Monitor));

580 m->showbar = showbar;

581 m->topbar = topbar;

582 return m;

583 }

584

585 void

586 destroynotify(XEvent *e)

587 {

588 Client *c;

589 XDestroyWindowEvent *ev = &e->xdestroywindow;

590

591 if ((c = wintoclient(ev->window)))

592 unmanage(c, 1);

593 }

594

595 void

596 detach(Client *c)

597 {

598 Client **tc;

599

600 for (tc = &clients; *tc && *tc != c; tc = &(*tc)->next);

601 *tc = c->next;

602 }

603

604 void

605 detachstack(Client *c)

606 {

607 Client **tc, *t;

608

609 for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);

610 *tc = c->snext;

611

612 if (c == sel) {

613 for (t = stack; t && !ISVISIBLE(t); t = t->snext);

614 sel = t;

615 }

616 }

617

618 void

619 drawbar(Monitor *m)

620 {

621 int x, w, tw = 0;

622 int boxs = drw->fonts->h / 9;

623 int boxw = drw->fonts->h / 6 + 2;

624 char ws[16];

625

626 if (!m->showbar)

627 return;

628

629 /* draw status first so it can be overdrawn by tags later */

630 drw_setscheme(drw, scheme[SchemeNorm]);

631 tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */

632 drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);

633

634 /* draw current workspace */

635 x = 0;

636 snprintf(ws, sizeof(ws), "%d", workspace);

637 w = TEXTW(ws);

638 drw_setscheme(drw, scheme[SchemeNorm]);

639 drw_text(drw, x, 0, w, bh, lrpad / 2, ws, 0);

640 x += w;

641

642 drw_setscheme(drw, scheme[SchemeNorm]);

643

644 if ((w = m->ww - tw - x) > bh) {

645 if (sel) {

646 drw_setscheme(drw, scheme[m == selmon ?

647 SchemeSel : SchemeNorm]);

648 drw_text(drw, x, 0, w, bh, lrpad / 2, sel->name, 0);

649 drw_rect(drw, x + boxs, boxs, boxw, boxw,

650 sel->isfixed, 0);

651 } else {

652 drw_setscheme(drw, scheme[SchemeNorm]);

653 drw_rect(drw, x, 0, w, bh, 1, 1);

654 }

655 }

656 drw_map(drw, m->barwin, 0, 0, m->ww, bh);

657 }

658

659 void

660 drawbars(void)

661 {

662 Monitor *m;

663

664 for (m = mons; m; m = m->next)

665 drawbar(m);

666 }

667

668 void

669 expose(XEvent *e)

670 {

671 Monitor *m;

672 XExposeEvent *ev = &e->xexpose;

673

674 if (ev->count == 0 && (m = wintomon(ev->window)))

675 drawbar(m);

676 }

677

678 void

679 focus(Client *c)

680 {

681 if (!c || !ISVISIBLE(c))

682 for (c = stack; c && !ISVISIBLE(c); c = c->snext);

683 if (sel && sel != c)

684 unfocus(sel, 0);

685 if (c) {

686 c->backward = 0;

687 if (c->mon != selmon)

688 selmon = c->mon;

689 if (c->isurgent)

690 seturgent(c, 0);

691 detachstack(c);

692 attachstack(c);

693 grabbuttons(c, 1);

694 XSetWindowBorder(dpy, c->win,

695 scheme[SchemeSel][ColBorder].pixel);

696 setfocus(c);

697 } else {

698 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);

699 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);

700 }

701 sel = c;

702 drawbars();

703 }

704

705 /* there are some broken focus acquiring clients needing extra handling */

706 void

707 focusin(XEvent *e)

708 {

709 XFocusChangeEvent *ev = &e->xfocus;

710

711 if (sel && ev->window != sel->win)

712 setfocus(sel);

713 }

714

715 Atom

716 getatomprop(Client *c, Atom prop)

717 {

718 int di;

719 unsigned long dl;

720 unsigned char *p = NULL;

721 Atom da, atom = None;

722

723 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False,

724 XA_ATOM, &da, &di, &dl, &dl, &p) == Success

725 && p) {

726 atom = *(Atom *)p;

727 XFree(p);

728 }

729 return atom;

730 }

731

732 int

733 getrootptr(int *x, int *y)

734 {

735 int di;

736 unsigned int dui;

737 Window dummy;

738

739 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);

740 }

741

742 long

743 getstate(Window w)

744 {

745 int format;

746 long result = -1;

747 unsigned char *p = NULL;

748 unsigned long n, extra;

749 Atom real;

750

751 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False,

752 wmatom[WMState], &real, &format, &n,

753 &extra, (unsigned char **)&p) != Success)

754 return -1;

755 if (n != 0)

756 result = *p;

757 XFree(p);

758 return result;

759 }

760

761 int

762 gettextprop(Window w, Atom atom, char *text, unsigned int size)

763 {

764 char **list = NULL;

765 int n;

766 XTextProperty name;

767

768 if (!text || size == 0)

769 return 0;

770 text[0] = '\0';

771 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)

772 return 0;

773 if (name.encoding == XA_STRING) {

774 strncpy(text, (char *)name.value, size - 1);

775 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success

776 && n > 0 && *list) {

777 strncpy(text, *list, size - 1);

778 XFreeStringList(list);

779 }

780 text[size - 1] = '\0';

781 XFree(name.value);

782 return 1;

783 }

784

785 void

786 grabbuttons(Client *c, int focused)

787 {

788 updatenumlockmask();

789 {

790 unsigned int i, j;

791 unsigned int modifiers[] = {

792 0, LockMask, numlockmask,

793 numlockmask|LockMask

794 };

795 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);

796 if (!focused)

797 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,

798 BUTTONMASK, GrabModeSync, GrabModeSync,

799 None, None);

800 for (i = 0; i < LENGTH(buttons); i++)

801 if (buttons[i].click == ClkClientWin)

802 for (j = 0; j < LENGTH(modifiers); j++)

803 XGrabButton(dpy, buttons[i].button,

804 buttons[i].mask | modifiers[j],

805 c->win, False, BUTTONMASK,

806 GrabModeAsync, GrabModeSync,

807 None, None);

808 }

809 }

810

811 void

812 grabkeys(void)

813 {

814 updatenumlockmask();

815 {

816 unsigned int i, j, k;

817 unsigned int modifiers[] = {

818 0, LockMask, numlockmask, numlockmask|LockMask

819 };

820 int start, end, skip;

821 KeySym *syms;

822

823 XUngrabKey(dpy, AnyKey, AnyModifier, root);

824 XDisplayKeycodes(dpy, &start, &end);

825 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);

826 if (!syms)

827 return;

828 for (k = start; k <= end; k++)

829 for (i = 0; i < LENGTH(keys); i++) {

830 /* skip modifier codes, we do that ourselves */

831 if (keys[i].keysym != syms[(k - start) * skip])

832 continue;

833 for (j = 0; j < LENGTH(modifiers); j++)

834 XGrabKey(dpy, k,

835 keys[i].mod | modifiers[j],

836 root, True,

837 GrabModeAsync, GrabModeAsync);

838 }

839 XFree(syms);

840 }

841 }

842

843 #ifdef XINERAMA

844 static int

845 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)

846 {

847 while (n--)

848 if (unique[n].x_org == info->x_org

849 && unique[n].y_org == info->y_org

850 && unique[n].width == info->width

851 && unique[n].height == info->height)

852 return 0;

853 return 1;

854 }

855 #endif /* XINERAMA */

856

857 void

858 keypress(XEvent *e)

859 {

860 unsigned int i;

861 KeySym keysym;

862 XKeyEvent *ev;

863

864 ev = &e->xkey;

865 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);

866 for (i = 0; i < LENGTH(keys); i++)

867 if (keysym == keys[i].keysym

868 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)

869 && keys[i].func)

870 keys[i].func(&(keys[i].arg));

871 }

872

873 void

874 killclient(const Arg *arg)

875 {

876 if (!sel)

877 return;

878 if (!sendevent(sel, wmatom[WMDelete])) {

879 XGrabServer(dpy);

880 XSetErrorHandler(xerrordummy);

881 XSetCloseDownMode(dpy, DestroyAll);

882 XKillClient(dpy, sel->win);

883 XSync(dpy, False);

884 XSetErrorHandler(xerror);

885 XUngrabServer(dpy);

886 }

887 }

888

889 void

890 manage(Window w, XWindowAttributes *wa)

891 {

892 Client *c, *t = NULL;

893 Window trans = None;

894 XWindowChanges wc;

895

896 c = ecalloc(1, sizeof(Client));

897 c->win = w;

898 /* geometry */

899 c->x = c->oldx = wa->x;

900 c->y = c->oldy = wa->y;

901 c->w = c->oldw = wa->width;

902 c->h = c->oldh = wa->height;

903 c->oldbw = wa->border_width;

904

905 updatetitle(c);

906 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {

907 c->mon = t->mon;

908 } else {

909 c->mon = selmon;

910 }

911 c->w = c->oldw = MIN(c->mon->ww, c->w);

912 c->h = c->oldh = MIN(c->mon->wh, c->h);

913

914 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)

915 c->x = c->mon->wx + c->mon->ww - WIDTH(c);

916 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh)

917 c->y = c->mon->wy + c->mon->wh - HEIGHT(c);

918 c->x = MAX(c->x, c->mon->wx) +

919 (wa->x > 0 ? 0 : (c->w >= c->mon->ww ? 0 :

920 (rand() % (c->mon->ww - c->w))));

921 c->y = MAX(c->y, c->mon->wy) +

922 (wa->y > 0 ? 0 : (c->h >= c->mon->wh ? 0 :

923 (rand() % (c->mon->wh - c->h))));

924

925 c->bw = borderpx;

926 c->workspace = workspace;

927

928 wc.border_width = c->bw;

929 XConfigureWindow(dpy, w, CWBorderWidth, &wc);

930 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);

931 configure(c); /* propagates border_width, if size doesn't change */

932

933 updatewindowtype(c);

934 updatesizehints(c);

935 updatewmhints(c);

936 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|

937 PropertyChangeMask|StructureNotifyMask);

938 grabbuttons(c, 0);

939 XRaiseWindow(dpy, c->win);

940 attach(c);

941 attachstack(c);

942 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32,

943 PropModeAppend, (unsigned char *) &(c->win), 1);

944 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h);

945 setclientstate(c, NormalState);

946 if (c->mon == selmon)

947 unfocus(sel, 0);

948 sel = c;

949 arrange(c->mon);

950 XMapWindow(dpy, c->win);

951 focus(NULL);

952 }

953

954 void

955 mappingnotify(XEvent *e)

956 {

957 XMappingEvent *ev = &e->xmapping;

958

959 XRefreshKeyboardMapping(ev);

960 if (ev->request == MappingKeyboard)

961 grabkeys();

962 }

963

964 void

965 maprequest(XEvent *e)

966 {

967 static XWindowAttributes wa;

968 XMapRequestEvent *ev = &e->xmaprequest;

969

970 if (!XGetWindowAttributes(dpy, ev->window, &wa)

971 || wa.override_redirect)

972 return;

973 if (!wintoclient(ev->window))

974 manage(ev->window, &wa);

975 }

976

977 void

978 motionnotify(XEvent *e)

979 {

980 static Monitor *mon = NULL;

981 Monitor *m;

982 XMotionEvent *ev = &e->xmotion;

983

984 if (ev->window != root)

985 return;

986 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {

987 unfocus(sel, 1);

988 selmon = m;

989 focus(NULL);

990 }

991 mon = m;

992 }

993

994 void

995 movemouse(const Arg *arg)

996 {

997 int x, y, ocx, ocy, nx, ny;

998 Client *c;

999 Monitor *m;

1000 XEvent ev;

1001 Time lasttime = 0;

1002

1003 if (!(c = sel))

1004 return;

1005 if (c->isfullscreen) /* can't move fullscreen windows by mouse */

1006 return;

1007 restack(selmon);

1008 ocx = c->x;

1009 ocy = c->y;

1010 if (XGrabPointer(dpy, root, False, MOUSEMASK,

1011 GrabModeAsync, GrabModeAsync, None,

1012 cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)

1013 return;

1014 if (!getrootptr(&x, &y))

1015 return;

1016 do {

1017 XMaskEvent(dpy,

1018 MOUSEMASK|ExposureMask|SubstructureRedirectMask,

1019 &ev);

1020 switch(ev.type) {

1021 case ConfigureRequest:

1022 case Expose:

1023 case MapRequest:

1024 handler[ev.type](&ev);

1025 break;

1026 case MotionNotify:

1027 if ((ev.xmotion.time - lasttime) <= (1000 / FPS))

1028 continue;

1029 lasttime = ev.xmotion.time;

1030 if (c->ismaximized) {

1031 maximize(arg);

1032 ocx = x - c->w/2;

1033 ocy = y - c->h/2;

1034 }

1035

1036 nx = ocx + (ev.xmotion.x - x);

1037 ny = ocy + (ev.xmotion.y - y);

1038 if (abs(selmon->wx - nx) < snap)

1039 nx = selmon->wx;

1040 else if (abs((selmon->wx + selmon->ww) -

1041 (nx + WIDTH(c))) < snap)

1042 nx = selmon->wx + selmon->ww - WIDTH(c);

1043 if (abs(selmon->wy - ny) < snap)

1044 ny = selmon->wy;

1045 else if (abs((selmon->wy + selmon->wh) -

1046 (ny + HEIGHT(c))) < snap)

1047 ny = selmon->wy + selmon->wh - HEIGHT(c);

1048 resize(c, nx, ny, c->w, c->h, 1);

1049 break;

1050 }

1051 } while (ev.type != ButtonRelease);

1052 XUngrabPointer(dpy, CurrentTime);

1053 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {

1054 sendmon(c, m);

1055 selmon = m;

1056 focus(NULL);

1057 }

1058 }

1059

1060 void

1061 propertynotify(XEvent *e)

1062 {

1063 Client *c;

1064 XPropertyEvent *ev = &e->xproperty;

1065

1066 if ((ev->window == root) && (ev->atom == XA_WM_NAME))

1067 updatestatus();

1068 else if (ev->state == PropertyDelete)

1069 return; /* ignore */

1070 else if ((c = wintoclient(ev->window))) {

1071 switch(ev->atom) {

1072 default: break;

1073 case XA_WM_NORMAL_HINTS:

1074 c->hintsvalid = 0;

1075 break;

1076 case XA_WM_HINTS:

1077 updatewmhints(c);

1078 drawbars();

1079 break;

1080 }

1081 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {

1082 updatetitle(c);

1083 if (c == sel)

1084 drawbar(c->mon);

1085 }

1086 if (ev->atom == netatom[NetWMWindowType])

1087 updatewindowtype(c);

1088 }

1089 }

1090

1091 void

1092 quit(const Arg *arg)

1093 {

1094 running = 0;

1095 }

1096

1097 Monitor *

1098 recttomon(int x, int y, int w, int h)

1099 {

1100 Monitor *m, *r = selmon;

1101 int a, area = 0;

1102

1103 for (m = mons; m; m = m->next)

1104 if ((a = INTERSECT(x, y, w, h, m)) > area) {

1105 area = a;

1106 r = m;

1107 }

1108 return r;

1109 }

1110

1111 void

1112 resize(Client *c, int x, int y, int w, int h, int interact)

1113 {

1114 if (applysizehints(c, &x, &y, &w, &h, interact))

1115 resizeclient(c, x, y, w, h);

1116 }

1117

1118 void

1119 resizeclient(Client *c, int x, int y, int w, int h)

1120 {

1121 XWindowChanges wc;

1122

1123 c->oldx = c->x; c->x = wc.x = x;

1124 c->oldy = c->y; c->y = wc.y = y;

1125 c->oldw = c->w; c->w = wc.width = w;

1126 c->oldh = c->h; c->h = wc.height = h;

1127 wc.border_width = c->bw;

1128 XConfigureWindow(dpy, c->win,

1129 CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);

1130 configure(c);

1131 XSync(dpy, False);

1132 }

1133

1134 void

1135 resizemouse(const Arg *arg)

1136 {

1137 int ocx, ocy, nw, nh;

1138 Client *c;

1139 Monitor *m;

1140 XEvent ev;

1141 Time lasttime = 0;

1142

1143 if (!(c = sel))

1144 return;

1145 if (c->isfullscreen) /* can't resize fullscreen windows by mouse */

1146 return;

1147 restack(selmon);

1148 ocx = c->x;

1149 ocy = c->y;

1150 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync,

1151 GrabModeAsync, None,

1152 cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)

1153 return;

1154 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,

1155 c->w + c->bw - 1, c->h + c->bw - 1);

1156 do {

1157 XMaskEvent(dpy,

1158 MOUSEMASK|ExposureMask|SubstructureRedirectMask,

1159 &ev);

1160 switch(ev.type) {

1161 case ConfigureRequest:

1162 case Expose:

1163 case MapRequest:

1164 handler[ev.type](&ev);

1165 break;

1166 case MotionNotify:

1167 if ((ev.xmotion.time - lasttime) <= (1000 / 60))

1168 continue;

1169 lasttime = ev.xmotion.time;

1170

1171 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);

1172 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);

1173 resize(c, c->x, c->y, nw, nh, 1);

1174 break;

1175 }

1176 } while (ev.type != ButtonRelease);

1177 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,

1178 c->w + c->bw - 1, c->h + c->bw - 1);

1179 XUngrabPointer(dpy, CurrentTime);

1180 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));

1181 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {

1182 sendmon(c, m);

1183 selmon = m;

1184 focus(NULL);

1185 }

1186 }

1187

1188 void

1189 restack(Monitor *m)

1190 {

1191 XEvent ev;

1192

1193 drawbar(m);

1194 if (!sel)

1195 return;

1196 XRaiseWindow(dpy, sel->win);

1197 XSync(dpy, False);

1198 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));

1199 }

1200

1201 void

1202 run(void)

1203 {

1204 XEvent ev;

1205 pthread_t t;

1206 /* main event loop */

1207 XSync(dpy, False);

1208 pthread_create(&t, NULL, update, NULL);

1209 while (running && !XNextEvent(dpy, &ev))

1210 if (handler[ev.type])

1211 handler[ev.type](&ev); /* call handler */

1212 pthread_join(t, NULL);

1213 }

1214

1215 void

1216 scan(void)

1217 {

1218 unsigned int i, num;

1219 Window d1, d2, *wins = NULL;

1220 XWindowAttributes wa;

1221

1222 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {

1223 for (i = 0; i < num; i++) {

1224 if (!XGetWindowAttributes(dpy, wins[i], &wa)

1225 || wa.override_redirect

1226 || XGetTransientForHint(dpy, wins[i], &d1))

1227 continue;

1228 if (wa.map_state == IsViewable

1229 || getstate(wins[i]) == IconicState)

1230 manage(wins[i], &wa);

1231 }

1232 for (i = 0; i < num; i++) { /* now the transients */

1233 if (!XGetWindowAttributes(dpy, wins[i], &wa))

1234 continue;

1235 if (XGetTransientForHint(dpy, wins[i], &d1)

1236 && (wa.map_state == IsViewable

1237 || getstate(wins[i]) == IconicState))

1238 manage(wins[i], &wa);

1239 }

1240 if (wins)

1241 XFree(wins);

1242 }

1243 }

1244

1245 void

1246 sendmon(Client *c, Monitor *m)

1247 {

1248 if (c->mon == m)

1249 return;

1250 unfocus(c, 1);

1251 detach(c);

1252 detachstack(c);

1253 c->mon = m;

1254 attach(c);

1255 attachstack(c);

1256 focus(NULL);

1257 arrange(NULL);

1258 }

1259

1260 void

1261 setclientstate(Client *c, long state)

1262 {

1263 long data[] = { state, None };

1264

1265 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,

1266 PropModeReplace, (unsigned char *)data, 2);

1267 }

1268

1269 int

1270 sendevent(Client *c, Atom proto)

1271 {

1272 int n;

1273 Atom *protocols;

1274 int exists = 0;

1275 XEvent ev;

1276

1277 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {

1278 while (!exists && n--)

1279 exists = protocols[n] == proto;

1280 XFree(protocols);

1281 }

1282 if (exists) {

1283 ev.type = ClientMessage;

1284 ev.xclient.window = c->win;

1285 ev.xclient.message_type = wmatom[WMProtocols];

1286 ev.xclient.format = 32;

1287 ev.xclient.data.l[0] = proto;

1288 ev.xclient.data.l[1] = CurrentTime;

1289 XSendEvent(dpy, c->win, False, NoEventMask, &ev);

1290 }

1291 return exists;

1292 }

1293

1294 void

1295 setfocus(Client *c)

1296 {

1297 if (!c->neverfocus) {

1298 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);

1299 XChangeProperty(dpy, root, netatom[NetActiveWindow],

1300 XA_WINDOW, 32, PropModeReplace,

1301 (unsigned char *) &(c->win), 1);

1302 }

1303 sendevent(c, wmatom[WMTakeFocus]);

1304 }

1305

1306 void

1307 setfullscreen(Client *c, int fullscreen)

1308 {

1309 if (fullscreen && !c->isfullscreen) {

1310 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,

1311 PropModeReplace,

1312 (unsigned char*)&netatom[NetWMFullscreen], 1);

1313 c->isfullscreen = 1;

1314 c->oldbw = c->bw;

1315 c->bw = 0;

1316 resizeclient(c, c->mon->mx, c->mon->my,

1317 c->mon->mw, c->mon->mh);

1318 XRaiseWindow(dpy, c->win);

1319 } else if (!fullscreen && c->isfullscreen){

1320 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,

1321 PropModeReplace, (unsigned char*)0, 0);

1322 c->isfullscreen = 0;

1323 c->bw = c->oldbw;

1324 c->x = c->oldx;

1325 c->y = c->oldy;

1326 c->w = c->oldw;

1327 c->h = c->oldh;

1328 resizeclient(c, c->x, c->y, c->w, c->h);

1329 arrange(c->mon);

1330 }

1331 }

1332

1333 void

1334 setup(void)

1335 {

1336 int i;

1337 XSetWindowAttributes wa;

1338 Atom utf8string;

1339

1340 /* clean up any zombies immediately */

1341 sigchld(0);

1342

1343 /* init screen */

1344 screen = DefaultScreen(dpy);

1345 sw = DisplayWidth(dpy, screen);

1346 sh = DisplayHeight(dpy, screen);

1347 root = RootWindow(dpy, screen);

1348 drw = drw_create(dpy, screen, root, sw, sh);

1349 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))

1350 die("no fonts could be loaded.");

1351 lrpad = drw->fonts->h;

1352 bh = drw->fonts->h + 2;

1353 updategeom();

1354 /* init atoms */

1355 utf8string = ATOM("UTF8_STRING");

1356 wmatom[WMProtocols] = ATOM("WM_PROTOCOLS");

1357 wmatom[WMDelete] = ATOM("WM_DELETE_WINDOW");

1358 wmatom[WMState] = ATOM("WM_STATE");

1359 wmatom[WMTakeFocus] = ATOM("WM_TAKE_FOCUS");

1360 netatom[NetActiveWindow] = ATOM("_NET_ACTIVE_WINDOW");

1361 netatom[NetSupported] = ATOM("_NET_SUPPORTED");

1362 netatom[NetWMName] = ATOM("_NET_WM_NAME");

1363 netatom[NetWMState] = ATOM("_NET_WM_STATE");

1364 netatom[NetWMCheck] = ATOM("_NET_SUPPORTING_WM_CHECK");

1365 netatom[NetWMFullscreen] = ATOM("_NET_WM_STATE_FULLSCREEN");

1366 netatom[NetWMWindowType] = ATOM("_NET_WM_WINDOW_TYPE");

1367 netatom[NetWMWindowTypeDialog] = ATOM("_NET_WM_WINDOW_TYPE_DIALOG");

1368 netatom[NetClientList] = ATOM("_NET_CLIENT_LIST");

1369 /* init cursors */

1370 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);

1371 cursor[CurResize] = drw_cur_create(drw, XC_sizing);

1372 cursor[CurMove] = drw_cur_create(drw, XC_fleur);

1373 /* init appearance */

1374 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));

1375 for (i = 0; i < LENGTH(colors); i++)

1376 scheme[i] = drw_scm_create(drw, colors[i], 3);

1377 /* init bars */

1378 updatebars();

1379 updatestatus();

1380 /* supporting window for NetWMCheck */

1381 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);

1382 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,

1383 PropModeReplace, (unsigned char *) &wmcheckwin, 1);

1384 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,

1385 PropModeReplace, (unsigned char *) WM, sizeof(WM) - 1);

1386 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,

1387 PropModeReplace, (unsigned char *) &wmcheckwin, 1);

1388 /* EWMH support per view */

1389 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,

1390 PropModeReplace, (unsigned char *) netatom, NetLast);

1391 XDeleteProperty(dpy, root, netatom[NetClientList]);

1392 /* select events */

1393 wa.cursor = cursor[CurNormal]->cursor;

1394 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask

1395 |ButtonPressMask|PointerMotionMask|EnterWindowMask

1396 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;

1397 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);

1398 XSelectInput(dpy, root, wa.event_mask);

1399 grabkeys();

1400 focus(NULL);

1401 }

1402

1403 void

1404 seturgent(Client *c, int urg)

1405 {

1406 XWMHints *wmh;

1407

1408 c->isurgent = urg;

1409 if (!(wmh = XGetWMHints(dpy, c->win)))

1410 return;

1411 wmh->flags = urg ? (wmh->flags | XUrgencyHint)

1412 : (wmh->flags & ~XUrgencyHint);

1413 XSetWMHints(dpy, c->win, wmh);

1414 XFree(wmh);

1415 }

1416

1417 void

1418 showhide(Client *c)

1419 {

1420 if (!c)

1421 return;

1422 if (ISVISIBLE(c)) {

1423 /* show clients top down */

1424 XMoveWindow(dpy, c->win, c->x, c->y);

1425 if (!c->isfullscreen)

1426 resize(c, c->x, c->y, c->w, c->h, 0);

1427 showhide(c->snext);

1428 } else {

1429 /* hide clients bottom up */

1430 showhide(c->snext);

1431 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);

1432 }

1433 }

1434

1435 void

1436 sigchld(int unused)

1437 {

1438 if (signal(SIGCHLD, sigchld) == SIG_ERR)

1439 die("can't install SIGCHLD handler:");

1440 while (0 < waitpid(-1, NULL, WNOHANG));

1441 }

1442

1443 void

1444 spawn(const Arg *arg)

1445 {

1446 if (arg->v == dmenucmd)

1447 dmenumon[0] = '0' + selmon->num;

1448 if (fork() == 0) {

1449 if (dpy)

1450 close(ConnectionNumber(dpy));

1451 setsid();

1452 execvp(((char **)arg->v)[0], (char **)arg->v);

1453 die(WM": execvp '%s' failed:", ((char **)arg->v)[0]);

1454 }

1455 }

1456

1457 void

1458 togglebar(const Arg *arg)

1459 {

1460 selmon->showbar = !selmon->showbar;

1461 updatebarpos(selmon);

1462 XMoveResizeWindow(dpy, selmon->barwin,

1463 selmon->wx, selmon->by, selmon->ww, bh);

1464 arrange(selmon);

1465 }

1466

1467 void

1468 unfocus(Client *c, int setfocus)

1469 {

1470 if (!c)

1471 return;

1472 grabbuttons(c, 0);

1473 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);

1474 if (setfocus) {

1475 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);

1476 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);

1477 }

1478 }

1479

1480 void

1481 unmanage(Client *c, int destroyed)

1482 {

1483 Monitor *m = c->mon;

1484 XWindowChanges wc;

1485

1486 detach(c);

1487 detachstack(c);

1488 if (!destroyed) {

1489 wc.border_width = c->oldbw;

1490 XGrabServer(dpy); /* avoid race conditions */

1491 XSetErrorHandler(xerrordummy);

1492 XSelectInput(dpy, c->win, NoEventMask);

1493 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* border */

1494 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);

1495 setclientstate(c, WithdrawnState);

1496 XSync(dpy, False);

1497 XSetErrorHandler(xerror);

1498 XUngrabServer(dpy);

1499 }

1500 free(c);

1501 focus(NULL);

1502 updateclientlist();

1503 arrange(m);

1504 }

1505

1506 void

1507 unmapnotify(XEvent *e)

1508 {

1509 Client *c;

1510 XUnmapEvent *ev = &e->xunmap;

1511

1512 if ((c = wintoclient(ev->window))) {

1513 if (ev->send_event)

1514 setclientstate(c, WithdrawnState);

1515 else

1516 unmanage(c, 0);

1517 }

1518 }

1519

1520 void

1521 updatebars(void)

1522 {

1523 Monitor *m;

1524 XSetWindowAttributes wa = {

1525 .override_redirect = True,

1526 .background_pixmap = ParentRelative,

1527 .event_mask = ButtonPressMask|ExposureMask

1528 };

1529 XClassHint ch = {WM, WM};

1530 for (m = mons; m; m = m->next) {

1531 if (m->barwin)

1532 continue;

1533 m->barwin = XCreateWindow(dpy, root,

1534 m->wx, m->by, m->ww, bh, 0,

1535 DefaultDepth(dpy, screen),

1536 CopyFromParent, DefaultVisual(dpy, screen),

1537 CWOverrideRedirect|CWBackPixmap|CWEventMask,

1538 &wa);

1539 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);

1540 XMapRaised(dpy, m->barwin);

1541 XSetClassHint(dpy, m->barwin, &ch);

1542 }

1543 }

1544

1545 void

1546 updatebarpos(Monitor *m)

1547 {

1548 m->wy = m->my;

1549 m->wh = m->mh;

1550 if (m->showbar) {

1551 m->wh -= bh;

1552 m->by = m->topbar ? m->wy : m->wy + m->wh;

1553 m->wy = m->topbar ? m->wy + bh : m->wy;

1554 } else

1555 m->by = -bh;

1556 }

1557

1558 void

1559 updateclientlist()

1560 {

1561 Client *c;

1562 Monitor *m;

1563

1564 XDeleteProperty(dpy, root, netatom[NetClientList]);

1565 for (m = mons; m; m = m->next)

1566 for (c = clients; c; c = c->next)

1567 XChangeProperty(dpy, root, netatom[NetClientList],

1568 XA_WINDOW, 32, PropModeAppend,

1569 (unsigned char *) &(c->win), 1);

1570 }

1571

1572 int

1573 updategeom(void)

1574 {

1575 int dirty = 0;

1576

1577 #ifdef XINERAMA

1578 if (XineramaIsActive(dpy)) {

1579 int i, j, n, nn;

1580 Client *c;

1581 Monitor *m;

1582 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);

1583 XineramaScreenInfo *unique = NULL;

1584

1585 for (n = 0, m = mons; m; m = m->next, n++);

1586 /* only consider unique geometries as separate screens */

1587 unique = ecalloc(nn, sizeof(XineramaScreenInfo));

1588 for (i = 0, j = 0; i < nn; i++)

1589 if (isuniquegeom(unique, j, &info[i]))

1590 memcpy(&unique[j++], &info[i],

1591 sizeof(XineramaScreenInfo));

1592 XFree(info);

1593 nn = j;

1594

1595 /* new monitors if nn > n */

1596 for (i = n; i < nn; i++) {

1597 for (m = mons; m && m->next; m = m->next);

1598 if (m)

1599 m->next = createmon();

1600 else

1601 mons = createmon();

1602 }

1603 for (i = 0, m = mons; i < nn && m; m = m->next, i++)

1604 if (i >= n

1605 || unique[i].x_org != m->mx

1606 || unique[i].y_org != m->my

1607 || unique[i].width != m->mw

1608 || unique[i].height != m->mh)

1609 {

1610 dirty = 1;

1611 m->num = i;

1612 m->showbar = i == PRIMARY_SCREEN;

1613 m->mx = m->wx = unique[i].x_org;

1614 m->my = m->wy = unique[i].y_org;

1615 m->mw = m->ww = unique[i].width;

1616 m->mh = m->wh = unique[i].height;

1617 updatebarpos(m);

1618 }

1619 /* removed monitors if n > nn */

1620 for (i = nn; i < n; i++) {

1621 for (m = mons; m && m->next; m = m->next);

1622 while ((c = clients)) {

1623 dirty = 1;

1624 clients = c->next;

1625 detachstack(c);

1626 c->mon = mons;

1627 attach(c);

1628 attachstack(c);

1629 }

1630 if (m == selmon)

1631 selmon = mons;

1632 cleanupmon(m);

1633 }

1634 free(unique);

1635 } else

1636 #endif /* XINERAMA */

1637 { /* default monitor setup */

1638 if (!mons)

1639 mons = createmon();

1640 if (mons->mw != sw || mons->mh != sh) {

1641 dirty = 1;

1642 mons->mw = mons->ww = sw;

1643 mons->mh = mons->wh = sh;

1644 updatebarpos(mons);

1645 }

1646 }

1647 if (dirty) {

1648 selmon = mons;

1649 selmon = wintomon(root);

1650 }

1651 return dirty;

1652 }

1653

1654 void

1655 updatenumlockmask(void)

1656 {

1657 unsigned int i, j;

1658 XModifierKeymap *modmap;

1659

1660 numlockmask = 0;

1661 modmap = XGetModifierMapping(dpy);

1662 for (i = 0; i < 8; i++)

1663 for (j = 0; j < modmap->max_keypermod; j++)

1664 if (modmap->modifiermap[i * modmap->max_keypermod + j]

1665 == XKeysymToKeycode(dpy, XK_Num_Lock))

1666 numlockmask = (1 << i);

1667 XFreeModifiermap(modmap);

1668 }

1669

1670 void

1671 updatesizehints(Client *c)

1672 {

1673 long msize;

1674 XSizeHints size;

1675

1676 if (!XGetWMNormalHints(dpy, c->win, &size, &msize))

1677 /* size is uninitialized, ensure that size.flags aren't used */

1678 size.flags = PSize;

1679 if (size.flags & PBaseSize) {

1680 c->basew = size.base_width;

1681 c->baseh = size.base_height;

1682 } else if (size.flags & PMinSize) {

1683 c->basew = size.min_width;

1684 c->baseh = size.min_height;

1685 } else

1686 c->basew = c->baseh = 0;

1687 if (size.flags & PResizeInc) {

1688 c->incw = size.width_inc;

1689 c->inch = size.height_inc;

1690 } else

1691 c->incw = c->inch = 0;

1692 if (size.flags & PMaxSize) {

1693 c->maxw = size.max_width;

1694 c->maxh = size.max_height;

1695 } else

1696 c->maxw = c->maxh = 0;

1697 if (size.flags & PMinSize) {

1698 c->minw = size.min_width;

1699 c->minh = size.min_height;

1700 } else if (size.flags & PBaseSize) {

1701 c->minw = size.base_width;

1702 c->minh = size.base_height;

1703 } else

1704 c->minw = c->minh = 0;

1705 if (size.flags & PAspect) {

1706 c->mina = (float)size.min_aspect.y / size.min_aspect.x;

1707 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;

1708 } else

1709 c->maxa = c->mina = 0.0;

1710 c->isfixed = (c->maxw && c->maxh &&

1711 c->maxw == c->minw && c->maxh == c->minh);

1712 c->hintsvalid = 1;

1713 }

1714

1715 void*

1716 update(void* args) {

1717 Display *_dpy = XOpenDisplay(NULL);

1718 Window _root = DefaultRootWindow(dpy);

1719 while (running) {

1720 char buf[5];

1721 buf[0] = rand()%10 + '0';

1722 buf[1] = rand()%10 + '0';

1723 buf[2] = rand()%10 + '0';

1724 buf[3] = rand()%10 + '0';

1725 buf[4] = '\0';

1726 XStoreName(_dpy, _root, buf);

1727 XFlush(_dpy);

1728 sleep(1);

1729 }

1730 XCloseDisplay(_dpy);

1731 return args;

1732 }

1733

1734 #ifdef __FreeBSD__

1735 #define u_int unsigned int

1736 #include <machine/apm_bios.h>

1737 #include <fcntl.h>

1738

1739 int apm_fd = -1;

1740

1741 #define APMDEV "/dev/apm"

1742

1743 int

1744 apm_init(void)

1745 {

1746 apm_fd = open(APMDEV, O_RDONLY);

1747 return apm_fd == -1;

1748 }

1749

1750 static void

1751 apm_getinfo(int fd, apm_info_t aip)

1752 {

1753 if (ioctl(fd, APMIO_GETINFO, aip) == -1)

1754 die("ioctl(APMIO_GETINFO)");

1755 }

1756

1757 int

1758 apm_string(char *out, size_t len)

1759 {

1760 struct apm_info info;

1761

1762 if (apm_fd == -1) {

1763 apm_init();

1764 if (apm_fd == -1) return 0;

1765 }

1766

1767 apm_getinfo(apm_fd, &info);

1768

1769 return snprintf(out, len, "Battery %d%%", info.ai_batt_life);

1770 }

1771 #endif

1772

1773 int

1774 updatestatus(void)

1775 {

1776 time_t t = time(0);

1777 struct tm *now = localtime(&t);

1778 #ifdef __FreeBSD__

1779 char buf[128];

1780 apm_string(buf, sizeof(buf));

1781 snprintf(stext, sizeof(stext), "%d/%02d/%02d %02d:%02d | %s",

1782 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,

1783 now->tm_hour, now->tm_min, buf);

1784 #else

1785 snprintf(stext, sizeof(stext), "%d/%02d/%02d %02d:%02d",

1786 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,

1787 now->tm_hour, now->tm_min);

1788 #endif

1789 drawbar(selmon);

1790 return 59 - now->tm_sec;

1791 }

1792

1793 void

1794 updatetitle(Client *c)

1795 {

1796 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))

1797 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);

1798 if (c->name[0] == '\0') /* hack to mark broken clients */

1799 strcpy(c->name, broken);

1800 }

1801

1802 void

1803 updatewindowtype(Client *c)

1804 {

1805 Atom state = getatomprop(c, netatom[NetWMState]);

1806

1807 if (state == netatom[NetWMFullscreen])

1808 setfullscreen(c, 1);

1809 }

1810

1811 void

1812 updatewmhints(Client *c)

1813 {

1814 XWMHints *wmh;

1815

1816 if ((wmh = XGetWMHints(dpy, c->win))) {

1817 if (c == sel && wmh->flags & XUrgencyHint) {

1818 wmh->flags &= ~XUrgencyHint;

1819 XSetWMHints(dpy, c->win, wmh);

1820 } else

1821 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;

1822 if (wmh->flags & InputHint)

1823 c->neverfocus = !wmh->input;

1824 else

1825 c->neverfocus = 0;

1826 XFree(wmh);

1827 }

1828 }

1829

1830 Client *

1831 wintoclient(Window w)

1832 {

1833 Client *c;

1834 Monitor *m;

1835

1836 for (m = mons; m; m = m->next)

1837 for (c = clients; c; c = c->next)

1838 if (c->win == w)

1839 return c;

1840 return NULL;

1841 }

1842

1843 Monitor *

1844 wintomon(Window w)

1845 {

1846 int x, y;

1847 Client *c;

1848 Monitor *m;

1849

1850 if (w == root && getrootptr(&x, &y))

1851 return recttomon(x, y, 1, 1);

1852 for (m = mons; m; m = m->next)

1853 if (w == m->barwin)

1854 return m;

1855 if ((c = wintoclient(w)))

1856 return c->mon;

1857 return selmon;

1858 }

1859

1860 /* There's no way to check accesses to destroyed windows, thus those cases are

1861 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs

1862 * default error handler, which may call exit. */

1863 int

1864 xerror(Display *dpy, XErrorEvent *ee)

1865 {

1866 if (ee->error_code == BadWindow

1867 || (ee->request_code == X_SetInputFocus

1868 && ee->error_code == BadMatch)

1869 || (ee->request_code == X_PolyText8

1870 && ee->error_code == BadDrawable)

1871 || (ee->request_code == X_PolyFillRectangle

1872 && ee->error_code == BadDrawable)

1873 || (ee->request_code == X_PolySegment

1874 && ee->error_code == BadDrawable)

1875 || (ee->request_code == X_ConfigureWindow

1876 && ee->error_code == BadMatch)

1877 || (ee->request_code == X_GrabButton

1878 && ee->error_code == BadAccess)

1879 || (ee->request_code == X_GrabKey

1880 && ee->error_code == BadAccess)

1881 || (ee->request_code == X_CopyArea

1882 && ee->error_code == BadDrawable))

1883 return 0;

1884 fprintf(stderr, WM": fatal error: request code=%d, error code=%d\n",

1885 ee->request_code, ee->error_code);

1886 return xerrorxlib(dpy, ee); /* may call exit */

1887 }

1888

1889 int

1890 xerrordummy(Display *dpy, XErrorEvent *ee)

1891 {

1892 return 0;

1893 }

1894

1895 /* Startup Error handler to check if another window manager

1896 * is already running. */

1897 int

1898 xerrorstart(Display *dpy, XErrorEvent *ee)

1899 {

1900 die(WM": another window manager is already running");

1901 return -1;

1902 }

1903

1904 /* floating */

1905

1906 void

1907 focusnext(const Arg *arg)

1908 {

1909 Client *c;

1910 if (!sel) return;

1911 c = sel->next;

1912 while (c && c->workspace != workspace)

1913 c = c->next;

1914 if (!c) c = clients;

1915 while (c && c->workspace != workspace)

1916 c = c->next;

1917 if (!c) return;

1918

1919 focus(c);

1920 arrange(selmon);

1921 }

1922

1923 void

1924 focusprev(const Arg *arg)

1925 {

1926 Client *c, *found = NULL, *second = NULL;

1927 int reset;

1928

1929 if (!clients || !clients->next) return;

1930

1931 reset = 1;

1932 c = stack->snext;

1933 while (c && c != sel) {

1934 if (!c->backward && c->workspace == workspace)

1935 reset = 0;

1936 c = c->snext;

1937 }

1938 if (reset) {

1939 c = stack->snext;

1940 while (c) {

1941 if (c->workspace == workspace)

1942 c->backward = 0;

1943 c = c->snext;

1944 }

1945 }

1946 c = stack->snext;

1947 while (c) {

1948 if (c->workspace == workspace) {

1949 second = c;

1950 if (!c->backward)

1951 found = c;

1952 }

1953 if (found)

1954 break;

1955 c = c->snext;

1956 }

1957 if (!found && second) found = second;

1958 if (!found) found = stack;

1959 sel->backward = 1;

1960 focus(found);

1961 arrange(selmon);

1962 }

1963

1964 void

1965 maximize(const Arg *arg)

1966 {

1967 Client *c = sel;

1968 if (!c) return;

1969 c->ismaximized = !c->ismaximized;

1970 if (c->ismaximized) {

1971 c->nx = c->x;

1972 c->ny = c->y;

1973 c->nw = c->w;

1974 c->nh = c->h;

1975 c->x = selmon->wx;

1976 c->y = selmon->wy;

1977 c->w = selmon->ww - borderpx * 2;

1978 c->h = selmon->wh - borderpx * 2;

1979 } else {

1980 c->x = c->nx;

1981 c->y = c->ny;

1982 c->w = c->nw;

1983 c->h = c->nh;

1984 }

1985 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);

1986 }

1987

1988 void

1989 minimize(const Arg *arg)

1990 {

1991 Client *c = sel;

1992 if (!c) return;

1993 if (c->ismaximized) maximize(arg);

1994 if (hidden) {

1995 hidden->next = malloc(sizeof(struct Hidden));

1996 hidden->next->prev = hidden;

1997 hidden = hidden->next;

1998 } else {

1999 hidden = malloc(sizeof(struct Hidden));

2000 hidden->prev = NULL;

2001 }

2002 hidden->window = c->win;

2003 hidden->next = NULL;

2004 XUnmapWindow(dpy, c->win);

2005 }

2006

2007 void

2008 reveal(const Arg *arg)

2009 {

2010 XWindowAttributes wa;

2011 Hidden *h = hidden;

2012 if (!hidden) return;

2013 hidden = h->prev;

2014 if (!XGetWindowAttributes(dpy, h->window, &wa)

2015 || wa.override_redirect)

2016 return;

2017 manage(h->window, &wa);

2018 free(h);

2019 }

2020

2021 void

2022 move(void)

2023 {

2024 struct Monitor *m;

2025 XMoveWindow(dpy, sel->win, sel->x, sel->y);

2026 if ((m = recttomon(sel->x, sel->y, sel->w, sel->h)) != selmon) {

2027 sendmon(sel, m);

2028 selmon = m;

2029 focus(NULL);

2030 }

2031 }

2032

2033 void

2034 movex(const Arg *arg)

2035 {

2036 if (!sel) return;

2037 sel->x += arg->i;

2038 move();

2039 }

2040

2041 void

2042 movey(const Arg *arg)

2043 {

2044 if (!sel) return;

2045 sel->y += arg->i;

2046 move();

2047 }

2048

2049 void

2050 resizex(const Arg *arg)

2051 {

2052 if (!sel) return;

2053 sel->w += arg->i;

2054 if (sel->w < sel->minw) sel->w = sel->minw;

2055 else if (sel->maxh > 0 && sel->w > sel->maxw) sel->w = sel->maxw;

2056 if (sel->w < 1) sel->w = 1;

2057 XResizeWindow(dpy, sel->win, sel->w, sel->h);

2058 }

2059

2060 void

2061 resizey(const Arg *arg)

2062 {

2063 if (!sel) return;

2064 sel->h += arg->i;

2065 if (sel->h < sel->minh) sel->h = sel->minh;

2066 else if (sel->maxh > 0 && sel->h > sel->maxh) sel->h = sel->maxh;

2067 if (sel->h < 1) sel->h = 1;

2068 XResizeWindow(dpy, sel->win, sel->w, sel->h);

2069 }

2070

2071 void

2072 screenmove(struct Client *c, struct Monitor* m)

2073 {

2074 if (!c->ismaximized) {

2075 c->x = MIN(MAX(m->wx + (c->x + c->w/2 - c->mon->wx) *

2076 m->ww / c->mon->ww - c->w/2,

2077 m->wx),

2078 m->wx + m->ww - c->w - borderpx * 2);

2079 c->y = MIN(MAX(m->wy + (c->y + c->h/2 - c->mon->wy) *

2080 m->wh / c->mon->wh - c->h/2,

2081 m->wy),

2082 m->wy + m->wh - c->h - borderpx * 2);

2083 move();

2084 return;

2085 }

2086 c->nx = m->wx + (c->nx - c->mon->wx) * m->ww / c->mon->ww;

2087 c->ny = m->wy + (c->ny - c->mon->wy) * m->wh / c->mon->wh;

2088 c->x = m->wx;

2089 c->y = m->wy;

2090 c->w = m->ww - borderpx * 2;

2091 c->h = m->wh - borderpx * 2;

2092 selmon = c->mon = m;

2093 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);

2094 }

2095

2096 void

2097 nextscreen(const Arg *arg)

2098 {

2099 struct Monitor *m;

2100 if (!sel) return;

2101 m = sel->mon->next;

2102 if (!m && sel->mon == mons) return;

2103 if (!m) m = mons;

2104 screenmove(sel, m);

2105 }

2106

2107 void

2108 prevscreen(const Arg *arg)

2109 {

2110 struct Monitor *m;

2111 if (!sel || !mons->next) return;

2112 m = mons;

2113 while (m->next && m->next != sel->mon) m = m->next;

2114 if (!m) return;

2115 screenmove(sel, m);

2116 }

2117

2118 void

2119 nextworkspace(const Arg *arg)

2120 {

2121 int w = workspace;

2122 workspace += arg->i;

2123 if (workspace < 0) workspace = 0;

2124 else if (workspace >= WORKSPACES) workspace = WORKSPACES - 1;

2125 if (w == workspace) return;

2126 focus(NULL);

2127 arrange(selmon);

2128 }

2129

2130 void

2131 moveworkspace(const Arg *arg)

2132 {

2133 int w;

2134 if (!sel) return;

2135 w = workspace;

2136 workspace += arg->i;

2137 if (workspace < 0) workspace = 0;

2138 else if (workspace >= WORKSPACES) workspace = WORKSPACES - 1;

2139 if (w == workspace) return;

2140 sel->workspace = workspace;

2141 focus(NULL);

2142 arrange(selmon);

2143 unfocus(sel, 1);

2144 }

2145 /* ----- */

2146

2147 int

2148 main(int argc, char *argv[])

2149 {

2150 if (argc == 2 && !strcmp("-v", argv[1]))

2151 die(WM"-"VERSION);

2152 else if (argc != 1)

2153 die("usage: "WM" [-v]");

2154 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())

2155 fputs("warning: no locale support\n", stderr);

2156 if (!(dpy = XOpenDisplay(NULL)))

2157 die(WM": cannot open display");

2158 checkotherwm();

2159 setup();

2160 #ifdef __OpenBSD__

2161 if (pledge("stdio rpath proc exec", NULL) == -1)

2162 die("pledge");

2163 #endif /* __OpenBSD__ */

2164 scan();

2165 system("~/.autostart");

2166 run();

2167 cleanup();

2168 XCloseDisplay(dpy);

2169 return EXIT_SUCCESS;

2170 }

2171