wmfs.c 14 KB

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