wmfs.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. /*
  2. * wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
  3. * For license, see COPYING.
  4. */
  5. #include <getopt.h>
  6. #include <X11/keysym.h>
  7. #include <X11/cursorfont.h>
  8. #include "wmfs.h"
  9. #include "event.h"
  10. #include "ewmh.h"
  11. #include "screen.h"
  12. #include "infobar.h"
  13. #include "util.h"
  14. #include "config.h"
  15. #include "client.h"
  16. #include "fifo.h"
  17. int
  18. wmfs_error_handler(Display *d, XErrorEvent *event)
  19. {
  20. char mess[256];
  21. /* Check if there is another WM running */
  22. if(event->error_code == BadAccess
  23. && W->root == event->resourceid)
  24. errl(EXIT_FAILURE, "Another Window Manager is already running.");
  25. /* Ignore focus change error for unmapped client
  26. * 42 = X_SetInputFocus
  27. * 28 = X_GrabButton
  28. */
  29. if(client_gb_win(event->resourceid))
  30. if(event->error_code == BadWindow
  31. || event->request_code == 42
  32. || event->request_code == 28)
  33. return 0;
  34. if(XGetErrorText(d, event->error_code, mess, 128))
  35. warnxl("%s(%d) opcodes %d/%d\n resource #%lx\n",
  36. mess,
  37. event->error_code,
  38. event->request_code,
  39. event->minor_code,
  40. event->resourceid);
  41. return 1;
  42. }
  43. int
  44. wmfs_error_handler_dummy(Display *d, XErrorEvent *event)
  45. {
  46. (void)d;
  47. (void)event;
  48. return 0;
  49. }
  50. void
  51. wmfs_numlockmask(void)
  52. {
  53. int i, j;
  54. XModifierKeymap *mm = XGetModifierMapping(W->dpy);
  55. for(i = 0; i < 8; i++)
  56. for(j = 0; j < mm->max_keypermod; ++j)
  57. if(mm->modifiermap[i * mm->max_keypermod + j]
  58. == XKeysymToKeycode(W->dpy, XK_Num_Lock))
  59. W->numlockmask = (1 << i);
  60. XFreeModifiermap(mm);
  61. }
  62. void
  63. wmfs_init_font(char *font, struct theme *t)
  64. {
  65. XFontStruct **xfs = NULL;
  66. char **misschar, **names, *defstring;
  67. int d;
  68. if(!(t->font.fontset = XCreateFontSet(W->dpy, font, &misschar, &d, &defstring)))
  69. {
  70. warnxl("Can't load font '%s'", font);
  71. t->font.fontset = XCreateFontSet(W->dpy, "fixed", &misschar, &d, &defstring);
  72. }
  73. XExtentsOfFontSet(t->font.fontset);
  74. XFontsOfFontSet(t->font.fontset, &xfs, &names);
  75. t->font.as = xfs[0]->max_bounds.ascent;
  76. t->font.de = xfs[0]->max_bounds.descent;
  77. t->font.width = xfs[0]->max_bounds.width;
  78. t->font.height = t->font.as + t->font.de;
  79. if(misschar)
  80. XFreeStringList(misschar);
  81. }
  82. static void
  83. wmfs_xinit(void)
  84. {
  85. XGCValues xgc =
  86. {
  87. .function = GXinvert,
  88. .subwindow_mode = IncludeInferiors,
  89. .line_width = 1
  90. };
  91. XSetWindowAttributes at =
  92. {
  93. .event_mask = (KeyMask | ButtonMask | MouseMask
  94. | PropertyChangeMask | SubstructureRedirectMask
  95. | SubstructureNotifyMask | StructureNotifyMask),
  96. .cursor = XCreateFontCursor(W->dpy, XC_left_ptr)
  97. };
  98. /*
  99. * X Error handler
  100. */
  101. XSetErrorHandler(wmfs_error_handler);
  102. /*
  103. * X var
  104. */
  105. W->xscreen = DefaultScreen(W->dpy);
  106. W->xdepth = DefaultDepth(W->dpy, W->xscreen);
  107. W->gc = DefaultGC(W->dpy, W->xscreen);
  108. W->xmaxw = DisplayWidth(W->dpy, W->xscreen);
  109. W->xmaxh = DisplayHeight(W->dpy, W->xscreen);
  110. /*
  111. * Keys
  112. */
  113. wmfs_numlockmask();
  114. /*
  115. * Root window/cursor
  116. */
  117. W->root = RootWindow(W->dpy, W->xscreen);
  118. XChangeWindowAttributes(W->dpy, W->root, CWEventMask | CWCursor, &at);
  119. W->rgc = XCreateGC(W->dpy, W->root, GCFunction | GCSubwindowMode | GCLineWidth, &xgc);
  120. /*
  121. * Locale (font encode)
  122. */
  123. setlocale(LC_CTYPE, "");
  124. /*
  125. * Barwin linked list
  126. */
  127. SLIST_INIT(&W->h.barwin);
  128. W->flags |= WMFS_RUNNING;
  129. }
  130. void
  131. wmfs_grab_keys(void)
  132. {
  133. KeyCode c;
  134. struct keybind *k;
  135. wmfs_numlockmask();
  136. XUngrabKey(W->dpy, AnyKey, AnyModifier, W->root);
  137. SLIST_FOREACH(k, &W->h.keybind, next)
  138. if((c = XKeysymToKeycode(W->dpy, k->keysym)))
  139. {
  140. XGrabKey(W->dpy, c, k->mod, W->root, True, GrabModeAsync, GrabModeAsync);
  141. XGrabKey(W->dpy, c, k->mod | LockMask, W->root, True, GrabModeAsync, GrabModeAsync);
  142. XGrabKey(W->dpy, c, k->mod | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync);
  143. XGrabKey(W->dpy, c, k->mod | LockMask | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync);
  144. }
  145. }
  146. /** Scan xprops of previous session to set it back
  147. * Check if there are windows on X (previous sessions windows)
  148. */
  149. static void
  150. wmfs_scan(void)
  151. {
  152. struct geo g;
  153. struct client *c, *cc, *fc;
  154. struct screen *s;
  155. int i, n, rf, nscreen = 0;
  156. int tag = -1, screen = -1, flags = -1;
  157. unsigned long ir, il;
  158. long *ret, *tret;
  159. bool getg = false;
  160. XWindowAttributes wa;
  161. Window usl, usl2, *w = NULL, tm, focus;
  162. Atom rt;
  163. SLIST_INIT(&W->h.client);
  164. W->flags |= WMFS_SCAN;
  165. /* Get previous selected tag to apply it at the end */
  166. if(XGetWindowProperty(W->dpy, W->root, W->net_atom[wmfs_current_tag], 0, 32,
  167. False, XA_CARDINAL, &rt, &rf, &ir, &il,
  168. (unsigned char**)&tret)
  169. == Success && ret)
  170. {
  171. nscreen = (int)ir;
  172. }
  173. /* Previous focused client before reload */
  174. if(XGetWindowProperty(W->dpy, W->root, W->net_atom[wmfs_focus], 0, 32,
  175. False, XA_WINDOW, &rt, &rf, &ir, &il,
  176. (unsigned char**)&ret)
  177. == Success && ret)
  178. {
  179. focus = *ret;
  180. XFree(ret);
  181. }
  182. if(XQueryTree(W->dpy, W->root, &usl, &usl2, &w, (unsigned int*)&n))
  183. for(i = n - 1; i != -1; --i)
  184. {
  185. XGetWindowAttributes(W->dpy, w[i], &wa);
  186. if(!wa.override_redirect && wa.map_state == IsViewable)
  187. {
  188. if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_TAG"), 0, 32,
  189. False, XA_CARDINAL, &rt, &rf, &ir, &il,
  190. (unsigned char**)&ret)
  191. == Success && ret)
  192. {
  193. tag = *ret;
  194. XFree(ret);
  195. }
  196. if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_SCREEN"), 0, 32,
  197. False, XA_CARDINAL, &rt, &rf, &ir, &il,
  198. (unsigned char**)&ret)
  199. == Success && ret)
  200. {
  201. screen = *ret;
  202. XFree(ret);
  203. }
  204. if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_FLAGS"), 0, 32,
  205. False, XA_CARDINAL, &rt, &rf, &ir, &il,
  206. (unsigned char**)&ret)
  207. == Success && ret)
  208. {
  209. flags = *ret;
  210. flags &= ~(CLIENT_TABBED | CLIENT_REMOVEALL);
  211. XFree(ret);
  212. }
  213. if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_GEO"), 0, 32,
  214. False, XA_CARDINAL, &rt, &rf, &ir, &il,
  215. (unsigned char**)&ret)
  216. == Success && ret)
  217. {
  218. g.x = ret[0];
  219. g.y = ret[1];
  220. g.w = ret[2];
  221. g.h = ret[3];
  222. getg = true;
  223. XFree(ret);
  224. }
  225. if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_TABMASTER"), 0, 32,
  226. False, XA_WINDOW, &rt, &rf, &ir, &il,
  227. (unsigned char**)&ret)
  228. == Success && ret)
  229. {
  230. tm = *ret;
  231. XFree(ret);
  232. }
  233. c = client_new(w[i], &wa, true);
  234. if(tm != c->win)
  235. c->tmp = tm;
  236. tm = 0;
  237. if(flags != -1)
  238. c->flags |= flags;
  239. if(tag != -1 && screen != -1)
  240. {
  241. c->screen = screen_gb_id(screen);
  242. if(getg)
  243. c->flags |= CLIENT_IGNORE_LAYOUT;
  244. client_map(c);
  245. tag_client(tag_gb_id(c->screen, tag), c);
  246. if(getg)
  247. client_moveresize(c, &g);
  248. client_get_name(c);
  249. }
  250. }
  251. }
  252. if(!nscreen)
  253. {
  254. SLIST_FOREACH(s, &W->h.screen, next)
  255. if(!TAILQ_EMPTY(&s->tags))
  256. tag_screen(s, TAILQ_FIRST(&s->tags));
  257. }
  258. else
  259. {
  260. /* Set back selected tag */
  261. for(i = 0; i < nscreen; ++i)
  262. {
  263. s = screen_gb_id(i);
  264. tag_screen(s, tag_gb_id(s, tret[i]));
  265. }
  266. }
  267. /* Re-adjust tabbed clients */
  268. SLIST_FOREACH(c, &W->h.client, next)
  269. if((cc = client_gb_win(c->tmp)) && cc != c)
  270. _client_tab(c, cc);
  271. if((fc = client_gb_win(focus)) && fc != W->client)
  272. client_focus(fc);
  273. W->flags &= ~WMFS_SCAN;
  274. if(tret)
  275. XFree(tret);
  276. XFree(w);
  277. XSync(W->dpy, false);
  278. }
  279. static void
  280. wmfs_loop(void)
  281. {
  282. XEvent ev;
  283. int maxfd, fd = ConnectionNumber(W->dpy);
  284. fd_set iset;
  285. while(W->flags & WMFS_RUNNING)
  286. {
  287. maxfd = fd + 1;
  288. FD_ZERO(&iset);
  289. FD_SET(fd, &iset);
  290. if(W->fifo.fd > 0)
  291. {
  292. maxfd += W->fifo.fd;
  293. FD_SET(W->fifo.fd, &iset);
  294. }
  295. if(select(maxfd, &iset, NULL, NULL, NULL) > 0)
  296. {
  297. if(FD_ISSET(fd, &iset))
  298. {
  299. while((W->flags & WMFS_RUNNING) && XPending(W->dpy))
  300. {
  301. XNextEvent(W->dpy, &ev);
  302. EVENT_HANDLE(&ev);
  303. }
  304. }
  305. else if(W->fifo.fd > 0 && FD_ISSET(W->fifo.fd, &iset))
  306. fifo_read();
  307. }
  308. }
  309. }
  310. static inline void
  311. wmfs_init(void)
  312. {
  313. log_init();
  314. wmfs_xinit();
  315. ewmh_init();
  316. screen_init();
  317. event_init();
  318. config_init();
  319. fifo_init();
  320. }
  321. void
  322. wmfs_quit(void)
  323. {
  324. struct keybind *k;
  325. struct rule *r;
  326. struct theme *t;
  327. struct client *c;
  328. struct mousebind *m;
  329. /* Will free:
  330. *
  331. * Screens -> tags
  332. * -> Infobars -> Elements
  333. */
  334. ewmh_update_wmfs_props();
  335. screen_free();
  336. XFreeGC(W->dpy, W->rgc);
  337. while(!SLIST_EMPTY(&W->h.client))
  338. {
  339. c = SLIST_FIRST(&W->h.client);
  340. client_update_props(c, CPROP_LOC | CPROP_FLAG | CPROP_GEO);
  341. c->flags |= (CLIENT_IGNORE_LAYOUT | CLIENT_REMOVEALL);
  342. client_remove(c);
  343. }
  344. /* Conf stuffs */
  345. while(!SLIST_EMPTY(&W->h.theme))
  346. {
  347. t = SLIST_FIRST(&W->h.theme);
  348. SLIST_REMOVE_HEAD(&W->h.theme, next);
  349. XFreeFontSet(W->dpy, t->font.fontset);
  350. free(t);
  351. }
  352. while(!SLIST_EMPTY(&W->h.rule))
  353. {
  354. r = SLIST_FIRST(&W->h.rule);
  355. SLIST_REMOVE_HEAD(&W->h.rule, next);
  356. free(r->class);
  357. free(r->instance);
  358. free(r->role);
  359. free(r->name);
  360. free(r);
  361. }
  362. while(!SLIST_EMPTY(&W->h.keybind))
  363. {
  364. k = SLIST_FIRST(&W->h.keybind);
  365. SLIST_REMOVE_HEAD(&W->h.keybind, next);
  366. free((void*)k->cmd);
  367. free(k);
  368. }
  369. while(!SLIST_EMPTY(&W->h.mousebind))
  370. {
  371. m = SLIST_FIRST(&W->h.mousebind);
  372. SLIST_REMOVE_HEAD(&W->h.mousebind, globnext);
  373. free((void*)m->cmd);
  374. free(m);
  375. }
  376. /* FIFO stuffs */
  377. if(W->fifo.fd > 0)
  378. {
  379. close(W->fifo.fd);
  380. unlink(W->fifo.path);
  381. }
  382. /* close log */
  383. if(W->log)
  384. fclose(W->log), W->log = NULL;
  385. W->flags &= ~WMFS_RUNNING;
  386. XCloseDisplay(W->dpy);
  387. }
  388. /** Reload WMFS binary
  389. */
  390. void
  391. uicb_reload(Uicb cmd)
  392. {
  393. (void)cmd;
  394. W->flags &= ~WMFS_RUNNING;
  395. W->flags |= WMFS_RELOAD;
  396. }
  397. void
  398. uicb_quit(Uicb cmd)
  399. {
  400. (void)cmd;
  401. W->flags &= ~WMFS_RUNNING;
  402. }
  403. int
  404. main(int argc, char **argv)
  405. {
  406. int i;
  407. bool r;
  408. (void)argc;
  409. W = (struct wmfs*)xcalloc(1, sizeof(struct wmfs));
  410. /* Default path ~/.config/wmfs/wmfsrc */
  411. sprintf(W->confpath, "%s/"CONFIG_DEFAULT_PATH, getenv("HOME"));
  412. /* Opt */
  413. while((i = getopt(argc, argv, "hvC:")) != -1)
  414. {
  415. switch(i)
  416. {
  417. case 'h':
  418. printf("usage: %s [-hv] [-C <file>]\n"
  419. " -h Show this page\n"
  420. " -v Show WMFS version\n"
  421. " -C <file> Launch WMFS with a specified configuration file\n", argv[0]);
  422. free(W);
  423. exit(EXIT_SUCCESS);
  424. break;
  425. case 'v':
  426. printf("wmfs("WMFS_VERSION") 2 beta\n");
  427. free(W);
  428. exit(EXIT_SUCCESS);
  429. break;
  430. case 'C':
  431. strncpy(W->confpath, optarg, sizeof(W->confpath));
  432. break;
  433. }
  434. }
  435. /* Get X display */
  436. if(!(W->dpy = XOpenDisplay(NULL)))
  437. {
  438. fprintf(stderr, "%s: Can't open X server\n", argv[0]);
  439. exit(EXIT_FAILURE);
  440. }
  441. /* Core */
  442. wmfs_init();
  443. wmfs_scan();
  444. wmfs_loop();
  445. wmfs_quit();
  446. r = (W->flags & WMFS_RELOAD);
  447. free(W);
  448. if(r)
  449. execvp(argv[0], argv);
  450. return 1;
  451. }