wmfs.c 17 KB

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