Mirror of Awesome WM window manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

awesome.c 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. /*
  2. * awesome.c - awesome main functions
  3. *
  4. * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. */
  21. #include "awesome.h"
  22. #include "banning.h"
  23. #include "common/atoms.h"
  24. #include "common/backtrace.h"
  25. #include "common/version.h"
  26. #include "common/xutil.h"
  27. #include "xkb.h"
  28. #include "dbus.h"
  29. #include "event.h"
  30. #include "ewmh.h"
  31. #include "globalconf.h"
  32. #include "objects/client.h"
  33. #include "objects/screen.h"
  34. #include "spawn.h"
  35. #include "systray.h"
  36. #include "xwindow.h"
  37. #include <getopt.h>
  38. #include <locale.h>
  39. #include <stdio.h>
  40. #include <unistd.h>
  41. #include <signal.h>
  42. #include <sys/time.h>
  43. #include <xcb/bigreq.h>
  44. #include <xcb/randr.h>
  45. #include <xcb/xcb_atom.h>
  46. #include <xcb/xcb_aux.h>
  47. #include <xcb/xcb_event.h>
  48. #include <xcb/xinerama.h>
  49. #include <xcb/xtest.h>
  50. #include <xcb/shape.h>
  51. #include <xcb/xfixes.h>
  52. #include <glib-unix.h>
  53. awesome_t globalconf;
  54. /** argv used to run awesome */
  55. static char **awesome_argv;
  56. /** time of last main loop wakeup */
  57. static struct timeval last_wakeup;
  58. /** current limit for the main loop's runtime */
  59. static float main_loop_iteration_limit = 0.1;
  60. /** A pipe that is used to asynchronously handle SIGCHLD */
  61. static int sigchld_pipe[2];
  62. /* Initialise various random number generators */
  63. static void
  64. init_rng(void)
  65. {
  66. /* LuaJIT uses its own, internal RNG, so initialise that */
  67. lua_State *L = globalconf_get_lua_State();
  68. /* Get math.randomseed */
  69. lua_getglobal(L, "math");
  70. lua_getfield(L, -1, "randomseed");
  71. /* Push a seed */
  72. lua_pushnumber(L, g_random_int() + g_random_double());
  73. /* Call math.randomseed */
  74. lua_call(L, 1, 0);
  75. /* Remove "math" global */
  76. lua_pop(L, 1);
  77. /* Lua 5.1, Lua 5.2, and (sometimes) Lua 5.3 use rand()/srand() */
  78. srand(g_random_int());
  79. /* When Lua 5.3 is built with LUA_USE_POSIX, it uses random()/srandom() */
  80. srandom(g_random_int());
  81. }
  82. /** Call before exiting.
  83. */
  84. void
  85. awesome_atexit(bool restart)
  86. {
  87. lua_State *L = globalconf_get_lua_State();
  88. lua_pushboolean(L, restart);
  89. signal_object_emit(L, &global_signals, "exit", 1);
  90. /* Move clients where we want them to be and keep the stacking order intact */
  91. foreach(c, globalconf.stack)
  92. {
  93. xcb_reparent_window(globalconf.connection, (*c)->window, globalconf.screen->root,
  94. (*c)->geometry.x, (*c)->geometry.y);
  95. }
  96. /* Save the client order. This is useful also for "hard" restarts. */
  97. xcb_window_t *wins = p_alloca(xcb_window_t, globalconf.clients.len);
  98. int n = 0;
  99. foreach(client, globalconf.clients)
  100. wins[n++] = (*client)->window;
  101. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  102. globalconf.screen->root,
  103. AWESOME_CLIENT_ORDER, XCB_ATOM_WINDOW, 32, n, wins);
  104. a_dbus_cleanup();
  105. systray_cleanup();
  106. /* Close Lua */
  107. lua_close(L);
  108. /* X11 is a great protocol. There is a save-set so that reparenting WMs
  109. * don't kill clients when they shut down. However, when a focused windows
  110. * is saved, the focus will move to its parent with revert-to none.
  111. * Immediately afterwards, this parent is destroyed and the focus is gone.
  112. * Work around this by placing the focus where we like it to be.
  113. */
  114. xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT,
  115. XCB_NONE, globalconf.timestamp);
  116. xcb_aux_sync(globalconf.connection);
  117. xkb_free();
  118. /* Disconnect *after* closing lua */
  119. xcb_cursor_context_free(globalconf.cursor_ctx);
  120. #ifdef WITH_XCB_ERRORS
  121. xcb_errors_context_free(globalconf.errors_ctx);
  122. #endif
  123. xcb_disconnect(globalconf.connection);
  124. close(sigchld_pipe[0]);
  125. close(sigchld_pipe[1]);
  126. }
  127. /** Restore the client order after a restart */
  128. static void
  129. restore_client_order(xcb_get_property_cookie_t prop_cookie)
  130. {
  131. int client_idx = 0;
  132. xcb_window_t *windows;
  133. xcb_get_property_reply_t *reply;
  134. reply = xcb_get_property_reply(globalconf.connection, prop_cookie, NULL);
  135. if (!reply || reply->format != 32 || reply->value_len == 0) {
  136. p_delete(&reply);
  137. return;
  138. }
  139. windows = xcb_get_property_value(reply);
  140. for (uint32_t i = 0; i < reply->value_len; i++)
  141. /* Find windows[i] and swap it to where it belongs */
  142. foreach(c, globalconf.clients)
  143. if ((*c)->window == windows[i])
  144. {
  145. client_t *tmp = *c;
  146. *c = globalconf.clients.tab[client_idx];
  147. globalconf.clients.tab[client_idx] = tmp;
  148. client_idx++;
  149. }
  150. luaA_class_emit_signal(globalconf_get_lua_State(), &client_class, "list", 0);
  151. p_delete(&reply);
  152. }
  153. /** Scan X to find windows to manage.
  154. */
  155. static void
  156. scan(xcb_query_tree_cookie_t tree_c)
  157. {
  158. int i, tree_c_len;
  159. xcb_query_tree_reply_t *tree_r;
  160. xcb_window_t *wins = NULL;
  161. xcb_get_window_attributes_reply_t *attr_r;
  162. xcb_get_geometry_reply_t *geom_r;
  163. xcb_get_property_cookie_t prop_cookie;
  164. tree_r = xcb_query_tree_reply(globalconf.connection,
  165. tree_c,
  166. NULL);
  167. if(!tree_r)
  168. return;
  169. /* This gets the property and deletes it */
  170. prop_cookie = xcb_get_property_unchecked(globalconf.connection, true,
  171. globalconf.screen->root, AWESOME_CLIENT_ORDER,
  172. XCB_ATOM_WINDOW, 0, UINT_MAX);
  173. /* Get the tree of the children windows of the current root window */
  174. if(!(wins = xcb_query_tree_children(tree_r)))
  175. fatal("cannot get tree children");
  176. tree_c_len = xcb_query_tree_children_length(tree_r);
  177. xcb_get_window_attributes_cookie_t attr_wins[tree_c_len];
  178. xcb_get_property_cookie_t state_wins[tree_c_len];
  179. xcb_get_geometry_cookie_t geom_wins[tree_c_len];
  180. for(i = 0; i < tree_c_len; i++)
  181. {
  182. attr_wins[i] = xcb_get_window_attributes_unchecked(globalconf.connection,
  183. wins[i]);
  184. state_wins[i] = xwindow_get_state_unchecked(wins[i]);
  185. geom_wins[i] = xcb_get_geometry_unchecked(globalconf.connection, wins[i]);
  186. }
  187. for(i = 0; i < tree_c_len; i++)
  188. {
  189. attr_r = xcb_get_window_attributes_reply(globalconf.connection,
  190. attr_wins[i],
  191. NULL);
  192. geom_r = xcb_get_geometry_reply(globalconf.connection, geom_wins[i], NULL);
  193. long state = xwindow_get_state_reply(state_wins[i]);
  194. if(!geom_r || !attr_r || attr_r->override_redirect
  195. || attr_r->map_state == XCB_MAP_STATE_UNMAPPED
  196. || state == XCB_ICCCM_WM_STATE_WITHDRAWN)
  197. {
  198. p_delete(&attr_r);
  199. p_delete(&geom_r);
  200. continue;
  201. }
  202. client_manage(wins[i], geom_r, attr_r);
  203. p_delete(&attr_r);
  204. p_delete(&geom_r);
  205. }
  206. p_delete(&tree_r);
  207. restore_client_order(prop_cookie);
  208. }
  209. static void
  210. acquire_WM_Sn(bool replace)
  211. {
  212. xcb_intern_atom_cookie_t atom_q;
  213. xcb_intern_atom_reply_t *atom_r;
  214. char *atom_name;
  215. xcb_get_selection_owner_reply_t *get_sel_reply;
  216. xcb_client_message_event_t ev;
  217. /* Get the WM_Sn atom */
  218. globalconf.selection_owner_window = xcb_generate_id(globalconf.connection);
  219. xcb_create_window(globalconf.connection, globalconf.screen->root_depth,
  220. globalconf.selection_owner_window, globalconf.screen->root,
  221. -1, -1, 1, 1, 0,
  222. XCB_COPY_FROM_PARENT, globalconf.screen->root_visual,
  223. 0, NULL);
  224. xwindow_set_class_instance(globalconf.selection_owner_window);
  225. xwindow_set_name_static(globalconf.selection_owner_window,
  226. "Awesome WM_Sn selection owner window");
  227. atom_name = xcb_atom_name_by_screen("WM_S", globalconf.default_screen);
  228. if(!atom_name)
  229. fatal("error getting WM_Sn atom name");
  230. atom_q = xcb_intern_atom_unchecked(globalconf.connection, false,
  231. a_strlen(atom_name), atom_name);
  232. p_delete(&atom_name);
  233. atom_r = xcb_intern_atom_reply(globalconf.connection, atom_q, NULL);
  234. if(!atom_r)
  235. fatal("error getting WM_Sn atom");
  236. globalconf.selection_atom = atom_r->atom;
  237. p_delete(&atom_r);
  238. /* Is the selection already owned? */
  239. get_sel_reply = xcb_get_selection_owner_reply(globalconf.connection,
  240. xcb_get_selection_owner(globalconf.connection, globalconf.selection_atom),
  241. NULL);
  242. if (!get_sel_reply)
  243. fatal("GetSelectionOwner for WM_Sn failed");
  244. if (!replace && get_sel_reply->owner != XCB_NONE)
  245. fatal("another window manager is already running (selection owned; use --replace)");
  246. /* Acquire the selection */
  247. xcb_set_selection_owner(globalconf.connection, globalconf.selection_owner_window,
  248. globalconf.selection_atom, globalconf.timestamp);
  249. if (get_sel_reply->owner != XCB_NONE)
  250. {
  251. /* Wait for the old owner to go away */
  252. xcb_get_geometry_reply_t *geom_reply = NULL;
  253. do {
  254. p_delete(&geom_reply);
  255. geom_reply = xcb_get_geometry_reply(globalconf.connection,
  256. xcb_get_geometry(globalconf.connection, get_sel_reply->owner),
  257. NULL);
  258. } while (geom_reply != NULL);
  259. }
  260. p_delete(&get_sel_reply);
  261. /* Announce that we are the new owner */
  262. p_clear(&ev, 1);
  263. ev.response_type = XCB_CLIENT_MESSAGE;
  264. ev.window = globalconf.screen->root;
  265. ev.format = 32;
  266. ev.type = MANAGER;
  267. ev.data.data32[0] = globalconf.timestamp;
  268. ev.data.data32[1] = globalconf.selection_atom;
  269. ev.data.data32[2] = globalconf.selection_owner_window;
  270. ev.data.data32[3] = ev.data.data32[4] = 0;
  271. xcb_send_event(globalconf.connection, false, globalconf.screen->root, 0xFFFFFF, (char *) &ev);
  272. }
  273. static void
  274. acquire_timestamp(void)
  275. {
  276. /* Getting a current timestamp is hard. ICCCM recommends a zero-length
  277. * append to a property, so let's do that.
  278. */
  279. xcb_generic_event_t *event;
  280. xcb_window_t win = globalconf.screen->root;
  281. xcb_atom_t atom = XCB_ATOM_RESOURCE_MANAGER; /* Just something random */
  282. xcb_atom_t type = XCB_ATOM_STRING; /* Equally random */
  283. xcb_grab_server(globalconf.connection);
  284. xcb_change_window_attributes(globalconf.connection, win,
  285. XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_PROPERTY_CHANGE });
  286. xcb_change_property(globalconf.connection, XCB_PROP_MODE_APPEND, win,
  287. atom, type, 8, 0, "");
  288. xcb_change_window_attributes(globalconf.connection, win,
  289. XCB_CW_EVENT_MASK, (uint32_t[]) { 0 });
  290. xutil_ungrab_server(globalconf.connection);
  291. /* Now wait for the event */
  292. while((event = xcb_wait_for_event(globalconf.connection)))
  293. {
  294. /* Is it the event we are waiting for? */
  295. if(XCB_EVENT_RESPONSE_TYPE(event) == XCB_PROPERTY_NOTIFY)
  296. {
  297. xcb_property_notify_event_t *ev = (void *) event;
  298. globalconf.timestamp = ev->time;
  299. p_delete(&event);
  300. break;
  301. }
  302. /* Hm, not the right event. */
  303. if (globalconf.pending_event != NULL)
  304. {
  305. event_handle(globalconf.pending_event);
  306. p_delete(&globalconf.pending_event);
  307. }
  308. globalconf.pending_event = event;
  309. }
  310. }
  311. static xcb_generic_event_t *poll_for_event(void)
  312. {
  313. if (globalconf.pending_event) {
  314. xcb_generic_event_t *event = globalconf.pending_event;
  315. globalconf.pending_event = NULL;
  316. return event;
  317. }
  318. return xcb_poll_for_event(globalconf.connection);
  319. }
  320. static void
  321. a_xcb_check(void)
  322. {
  323. xcb_generic_event_t *mouse = NULL, *event;
  324. while((event = poll_for_event()))
  325. {
  326. /* We will treat mouse events later.
  327. * We cannot afford to treat all mouse motion events,
  328. * because that would be too much CPU intensive, so we just
  329. * take the last we get after a bunch of events. */
  330. if(XCB_EVENT_RESPONSE_TYPE(event) == XCB_MOTION_NOTIFY)
  331. {
  332. p_delete(&mouse);
  333. mouse = event;
  334. }
  335. else
  336. {
  337. uint8_t type = XCB_EVENT_RESPONSE_TYPE(event);
  338. if(mouse && (type == XCB_ENTER_NOTIFY || type == XCB_LEAVE_NOTIFY
  339. || type == XCB_BUTTON_PRESS || type == XCB_BUTTON_RELEASE))
  340. {
  341. /* Make sure enter/motion/leave/press/release events are handled
  342. * in the correct order */
  343. event_handle(mouse);
  344. p_delete(&mouse);
  345. }
  346. event_handle(event);
  347. p_delete(&event);
  348. }
  349. }
  350. if(mouse)
  351. {
  352. event_handle(mouse);
  353. p_delete(&mouse);
  354. }
  355. }
  356. static gboolean
  357. a_xcb_io_cb(GIOChannel *source, GIOCondition cond, gpointer data)
  358. {
  359. /* a_xcb_check() already handled all events */
  360. if(xcb_connection_has_error(globalconf.connection))
  361. fatal("X server connection broke (error %d)",
  362. xcb_connection_has_error(globalconf.connection));
  363. return TRUE;
  364. }
  365. static gint
  366. a_glib_poll(GPollFD *ufds, guint nfsd, gint timeout)
  367. {
  368. guint res;
  369. struct timeval now, length_time;
  370. float length;
  371. int saved_errno;
  372. lua_State *L = globalconf_get_lua_State();
  373. /* Do all deferred work now */
  374. awesome_refresh();
  375. /* Check if the Lua stack is the way it should be */
  376. if (lua_gettop(L) != 0) {
  377. warn("Something was left on the Lua stack, this is a bug!");
  378. luaA_dumpstack(L);
  379. lua_settop(L, 0);
  380. }
  381. /* Don't sleep if there is a pending event */
  382. assert(globalconf.pending_event == NULL);
  383. globalconf.pending_event = xcb_poll_for_event(globalconf.connection);
  384. if (globalconf.pending_event != NULL)
  385. timeout = 0;
  386. /* Check how long this main loop iteration took */
  387. gettimeofday(&now, NULL);
  388. timersub(&now, &last_wakeup, &length_time);
  389. length = length_time.tv_sec + length_time.tv_usec * 1.0f / 1e6;
  390. if (length > main_loop_iteration_limit) {
  391. warn("Last main loop iteration took %.6f seconds! Increasing limit for "
  392. "this warning to that value.", length);
  393. main_loop_iteration_limit = length;
  394. }
  395. /* Actually do the polling, record time of wakeup and check for new xcb events */
  396. res = g_poll(ufds, nfsd, timeout);
  397. saved_errno = errno;
  398. gettimeofday(&last_wakeup, NULL);
  399. a_xcb_check();
  400. errno = saved_errno;
  401. return res;
  402. }
  403. static void
  404. signal_fatal(int signum)
  405. {
  406. buffer_t buf;
  407. backtrace_get(&buf);
  408. fatal("signal %d, dumping backtrace\n%s", signum, buf.s);
  409. }
  410. /* Signal handler for SIGCHLD. Causes reap_children() to be called. */
  411. static void
  412. signal_child(int signum)
  413. {
  414. assert(signum == SIGCHLD);
  415. int res = write(sigchld_pipe[1], " ", 1);
  416. (void) res;
  417. assert(res == 1);
  418. }
  419. /* There was a SIGCHLD signal. Read from sigchld_pipe and reap children. */
  420. static gboolean
  421. reap_children(GIOChannel *channel, GIOCondition condition, gpointer user_data)
  422. {
  423. pid_t child;
  424. int status;
  425. char buffer[1024];
  426. ssize_t result = read(sigchld_pipe[0], &buffer[0], sizeof(buffer));
  427. if (result < 0)
  428. fatal("Error reading from signal pipe: %s", strerror(errno));
  429. while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
  430. spawn_child_exited(child, status);
  431. }
  432. if (child < 0 && errno != ECHILD)
  433. warn("waitpid(-1) failed: %s", strerror(errno));
  434. return TRUE;
  435. }
  436. /** Function to exit on some signals.
  437. * \param data currently unused
  438. */
  439. static gboolean
  440. exit_on_signal(gpointer data)
  441. {
  442. g_main_loop_quit(globalconf.loop);
  443. return TRUE;
  444. }
  445. void
  446. awesome_restart(void)
  447. {
  448. awesome_atexit(true);
  449. execvp(awesome_argv[0], awesome_argv);
  450. fatal("execv() failed: %s", strerror(errno));
  451. }
  452. /** Function to restart awesome on some signals.
  453. * \param data currently unused
  454. */
  455. static gboolean
  456. restart_on_signal(gpointer data)
  457. {
  458. awesome_restart();
  459. return TRUE;
  460. }
  461. static bool
  462. true_config_callback(const char *unused)
  463. {
  464. return true;
  465. }
  466. /** Print help and exit(2) with given exit_code.
  467. * \param exit_code The exit code.
  468. */
  469. static void __attribute__ ((noreturn))
  470. exit_help(int exit_code)
  471. {
  472. FILE *outfile = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
  473. fprintf(outfile,
  474. "Usage: awesome [OPTION]\n\
  475. -h, --help show help\n\
  476. -v, --version show version\n\
  477. -c, --config FILE configuration file to use\n\
  478. --search DIR add a directory to the library search path\n\
  479. -k, --check check configuration file syntax\n\
  480. -a, --no-argb disable client transparency support\n\
  481. -r, --replace replace an existing window manager\n");
  482. exit(exit_code);
  483. }
  484. /** Hello, this is main.
  485. * \param argc Who knows.
  486. * \param argv Who knows.
  487. * \return EXIT_SUCCESS I hope.
  488. */
  489. int
  490. main(int argc, char **argv)
  491. {
  492. char *confpath = NULL;
  493. string_array_t searchpath;
  494. int xfd, opt;
  495. xdgHandle xdg;
  496. bool no_argb = false;
  497. bool run_test = false;
  498. bool replace_wm = false;
  499. xcb_query_tree_cookie_t tree_c;
  500. static struct option long_options[] =
  501. {
  502. { "help", 0, NULL, 'h' },
  503. { "version", 0, NULL, 'v' },
  504. { "config", 1, NULL, 'c' },
  505. { "check", 0, NULL, 'k' },
  506. { "search", 1, NULL, 's' },
  507. { "no-argb", 0, NULL, 'a' },
  508. { "replace", 0, NULL, 'r' },
  509. { "reap", 1, NULL, '\1' },
  510. { NULL, 0, NULL, 0 }
  511. };
  512. /* Make stdout/stderr line buffered. */
  513. setvbuf(stdout, NULL, _IOLBF, 0);
  514. setvbuf(stderr, NULL, _IOLBF, 0);
  515. /* clear the globalconf structure */
  516. p_clear(&globalconf, 1);
  517. globalconf.keygrabber = LUA_REFNIL;
  518. globalconf.mousegrabber = LUA_REFNIL;
  519. globalconf.exit_code = EXIT_SUCCESS;
  520. buffer_init(&globalconf.startup_errors);
  521. string_array_init(&searchpath);
  522. /* save argv */
  523. awesome_argv = argv;
  524. /* Text won't be printed correctly otherwise */
  525. setlocale(LC_CTYPE, "");
  526. /* check args */
  527. while((opt = getopt_long(argc, argv, "vhkc:ar",
  528. long_options, NULL)) != -1)
  529. switch(opt)
  530. {
  531. case 'v':
  532. eprint_version();
  533. break;
  534. case 'h':
  535. exit_help(EXIT_SUCCESS);
  536. break;
  537. case 'k':
  538. run_test = true;
  539. break;
  540. case 'c':
  541. if (confpath != NULL)
  542. fatal("--config may only be specified once");
  543. confpath = a_strdup(optarg);
  544. break;
  545. case 's':
  546. string_array_append(&searchpath, a_strdup(optarg));
  547. break;
  548. case 'a':
  549. no_argb = true;
  550. break;
  551. case 'r':
  552. replace_wm = true;
  553. break;
  554. case '\1':
  555. /* Silently ignore --reap and its argument */
  556. break;
  557. default:
  558. exit_help(EXIT_FAILURE);
  559. break;
  560. }
  561. /* Get XDG basedir data */
  562. if(!xdgInitHandle(&xdg))
  563. fatal("Function xdgInitHandle() failed, is $HOME unset?");
  564. /* add XDG_CONFIG_DIR as include path */
  565. const char * const *xdgconfigdirs = xdgSearchableConfigDirectories(&xdg);
  566. for(; *xdgconfigdirs; xdgconfigdirs++)
  567. {
  568. /* Append /awesome to *xdgconfigdirs */
  569. const char *suffix = "/awesome";
  570. size_t len = a_strlen(*xdgconfigdirs) + a_strlen(suffix) + 1;
  571. char *entry = p_new(char, len);
  572. a_strcat(entry, len, *xdgconfigdirs);
  573. a_strcat(entry, len, suffix);
  574. string_array_append(&searchpath, entry);
  575. }
  576. if (run_test)
  577. {
  578. bool success = true;
  579. /* Get the first config that will be tried */
  580. const char *config = luaA_find_config(&xdg, confpath, true_config_callback);
  581. /* Try to parse it */
  582. lua_State *L = luaL_newstate();
  583. if(luaL_loadfile(L, config))
  584. {
  585. const char *err = lua_tostring(L, -1);
  586. fprintf(stderr, "%s\n", err);
  587. success = false;
  588. }
  589. p_delete(&config);
  590. lua_close(L);
  591. if(!success)
  592. {
  593. fprintf(stderr, "✘ Configuration file syntax error.\n");
  594. return EXIT_FAILURE;
  595. }
  596. else
  597. {
  598. fprintf(stderr, "✔ Configuration file syntax OK.\n");
  599. return EXIT_SUCCESS;
  600. }
  601. }
  602. /* Setup pipe for SIGCHLD processing */
  603. {
  604. if (!g_unix_open_pipe(sigchld_pipe, FD_CLOEXEC, NULL))
  605. fatal("Failed to create pipe");
  606. GIOChannel *channel = g_io_channel_unix_new(sigchld_pipe[0]);
  607. g_io_add_watch(channel, G_IO_IN, reap_children, NULL);
  608. g_io_channel_unref(channel);
  609. }
  610. /* register function for signals */
  611. g_unix_signal_add(SIGINT, exit_on_signal, NULL);
  612. g_unix_signal_add(SIGTERM, exit_on_signal, NULL);
  613. g_unix_signal_add(SIGHUP, restart_on_signal, NULL);
  614. struct sigaction sa = { .sa_handler = signal_fatal, .sa_flags = SA_RESETHAND };
  615. sigemptyset(&sa.sa_mask);
  616. sigaction(SIGABRT, &sa, 0);
  617. sigaction(SIGBUS, &sa, 0);
  618. sigaction(SIGFPE, &sa, 0);
  619. sigaction(SIGILL, &sa, 0);
  620. sigaction(SIGSEGV, &sa, 0);
  621. signal(SIGPIPE, SIG_IGN);
  622. sa.sa_handler = signal_child;
  623. sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
  624. sigaction(SIGCHLD, &sa, 0);
  625. /* We have no clue where the input focus is right now */
  626. globalconf.focus.need_update = true;
  627. /* set the default preferred icon size */
  628. globalconf.preferred_icon_size = 0;
  629. /* X stuff */
  630. globalconf.connection = xcb_connect(NULL, &globalconf.default_screen);
  631. if(xcb_connection_has_error(globalconf.connection))
  632. fatal("cannot open display (error %d)", xcb_connection_has_error(globalconf.connection));
  633. globalconf.screen = xcb_aux_get_screen(globalconf.connection, globalconf.default_screen);
  634. globalconf.default_visual = draw_default_visual(globalconf.screen);
  635. if(!no_argb)
  636. globalconf.visual = draw_argb_visual(globalconf.screen);
  637. if(!globalconf.visual)
  638. globalconf.visual = globalconf.default_visual;
  639. globalconf.default_depth = draw_visual_depth(globalconf.screen, globalconf.visual->visual_id);
  640. globalconf.default_cmap = globalconf.screen->default_colormap;
  641. if(globalconf.default_depth != globalconf.screen->root_depth)
  642. {
  643. // We need our own color map if we aren't using the default depth
  644. globalconf.default_cmap = xcb_generate_id(globalconf.connection);
  645. xcb_create_colormap(globalconf.connection, XCB_COLORMAP_ALLOC_NONE,
  646. globalconf.default_cmap, globalconf.screen->root,
  647. globalconf.visual->visual_id);
  648. }
  649. #ifdef WITH_XCB_ERRORS
  650. if (xcb_errors_context_new(globalconf.connection, &globalconf.errors_ctx) < 0)
  651. fatal("Failed to initialize xcb-errors");
  652. #endif
  653. /* Get a recent timestamp */
  654. acquire_timestamp();
  655. /* Prefetch all the extensions we might need */
  656. xcb_prefetch_extension_data(globalconf.connection, &xcb_big_requests_id);
  657. xcb_prefetch_extension_data(globalconf.connection, &xcb_test_id);
  658. xcb_prefetch_extension_data(globalconf.connection, &xcb_randr_id);
  659. xcb_prefetch_extension_data(globalconf.connection, &xcb_xinerama_id);
  660. xcb_prefetch_extension_data(globalconf.connection, &xcb_shape_id);
  661. xcb_prefetch_extension_data(globalconf.connection, &xcb_xfixes_id);
  662. if (xcb_cursor_context_new(globalconf.connection, globalconf.screen, &globalconf.cursor_ctx) < 0)
  663. fatal("Failed to initialize xcb-cursor");
  664. globalconf.xrmdb = xcb_xrm_database_from_default(globalconf.connection);
  665. if (globalconf.xrmdb == NULL)
  666. globalconf.xrmdb = xcb_xrm_database_from_string("");
  667. if (globalconf.xrmdb == NULL)
  668. fatal("Failed to initialize xcb-xrm");
  669. /* Did we get some usable data from the above X11 setup? */
  670. draw_test_cairo_xcb();
  671. /* Acquire the WM_Sn selection */
  672. acquire_WM_Sn(replace_wm);
  673. /* initialize dbus */
  674. a_dbus_init();
  675. /* Get the file descriptor corresponding to the X connection */
  676. xfd = xcb_get_file_descriptor(globalconf.connection);
  677. GIOChannel *channel = g_io_channel_unix_new(xfd);
  678. g_io_add_watch(channel, G_IO_IN, a_xcb_io_cb, NULL);
  679. g_io_channel_unref(channel);
  680. /* Grab server */
  681. xcb_grab_server(globalconf.connection);
  682. {
  683. const uint32_t select_input_val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
  684. xcb_void_cookie_t cookie;
  685. /* This causes an error if some other window manager is running */
  686. cookie = xcb_change_window_attributes_checked(globalconf.connection,
  687. globalconf.screen->root,
  688. XCB_CW_EVENT_MASK, &select_input_val);
  689. if (xcb_request_check(globalconf.connection, cookie))
  690. fatal("another window manager is already running (can't select SubstructureRedirect)");
  691. }
  692. /* Prefetch the maximum request length */
  693. xcb_prefetch_maximum_request_length(globalconf.connection);
  694. /* check for xtest extension */
  695. const xcb_query_extension_reply_t *query;
  696. query = xcb_get_extension_data(globalconf.connection, &xcb_test_id);
  697. globalconf.have_xtest = query && query->present;
  698. /* check for shape extension */
  699. query = xcb_get_extension_data(globalconf.connection, &xcb_shape_id);
  700. globalconf.have_shape = query && query->present;
  701. if (globalconf.have_shape)
  702. {
  703. xcb_shape_query_version_reply_t *reply =
  704. xcb_shape_query_version_reply(globalconf.connection,
  705. xcb_shape_query_version_unchecked(globalconf.connection),
  706. NULL);
  707. globalconf.have_input_shape = reply && (reply->major_version > 1 ||
  708. (reply->major_version == 1 && reply->minor_version >= 1));
  709. p_delete(&reply);
  710. }
  711. /* check for xfixes extension */
  712. query = xcb_get_extension_data(globalconf.connection, &xcb_xfixes_id);
  713. globalconf.have_xfixes = query && query->present;
  714. if (globalconf.have_xfixes)
  715. xcb_discard_reply(globalconf.connection,
  716. xcb_xfixes_query_version(globalconf.connection, 1, 0).sequence);
  717. event_init();
  718. /* Allocate the key symbols */
  719. globalconf.keysyms = xcb_key_symbols_alloc(globalconf.connection);
  720. /* init atom cache */
  721. atoms_init(globalconf.connection);
  722. ewmh_init();
  723. systray_init();
  724. /* init spawn (sn) */
  725. spawn_init();
  726. /* init xkb */
  727. xkb_init();
  728. /* The default GC is just a newly created associated with a window with
  729. * depth globalconf.default_depth.
  730. * The window_no_focus is used for "nothing has the input focus". */
  731. globalconf.focus.window_no_focus = xcb_generate_id(globalconf.connection);
  732. globalconf.gc = xcb_generate_id(globalconf.connection);
  733. xcb_create_window(globalconf.connection, globalconf.default_depth,
  734. globalconf.focus.window_no_focus, globalconf.screen->root,
  735. -1, -1, 1, 1, 0,
  736. XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
  737. XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
  738. XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP,
  739. (const uint32_t [])
  740. {
  741. globalconf.screen->black_pixel,
  742. globalconf.screen->black_pixel,
  743. 1,
  744. globalconf.default_cmap
  745. });
  746. xwindow_set_class_instance(globalconf.focus.window_no_focus);
  747. xwindow_set_name_static(globalconf.focus.window_no_focus, "Awesome no input window");
  748. xcb_map_window(globalconf.connection, globalconf.focus.window_no_focus);
  749. xcb_create_gc(globalconf.connection, globalconf.gc, globalconf.focus.window_no_focus,
  750. XCB_GC_FOREGROUND | XCB_GC_BACKGROUND,
  751. (const uint32_t[]) { globalconf.screen->black_pixel, globalconf.screen->white_pixel });
  752. /* Get the window tree associated to this screen */
  753. tree_c = xcb_query_tree_unchecked(globalconf.connection,
  754. globalconf.screen->root);
  755. xcb_change_window_attributes(globalconf.connection,
  756. globalconf.screen->root,
  757. XCB_CW_EVENT_MASK,
  758. ROOT_WINDOW_EVENT_MASK);
  759. /* we will receive events, stop grabbing server */
  760. xutil_ungrab_server(globalconf.connection);
  761. /* get the current wallpaper, from now on we are informed when it changes */
  762. root_update_wallpaper();
  763. /* init lua */
  764. luaA_init(&xdg, &searchpath);
  765. string_array_wipe(&searchpath);
  766. init_rng();
  767. ewmh_init_lua();
  768. /* init screens information */
  769. screen_scan();
  770. /* Parse and run configuration file */
  771. if (!luaA_parserc(&xdg, confpath))
  772. fatal("couldn't find any rc file");
  773. p_delete(&confpath);
  774. xdgWipeHandle(&xdg);
  775. /* scan existing windows */
  776. scan(tree_c);
  777. luaA_emit_startup();
  778. /* Setup the main context */
  779. g_main_context_set_poll_func(g_main_context_default(), &a_glib_poll);
  780. gettimeofday(&last_wakeup, NULL);
  781. /* main event loop (if not NULL, awesome.quit() was already called) */
  782. if (globalconf.loop == NULL)
  783. {
  784. globalconf.loop = g_main_loop_new(NULL, FALSE);
  785. g_main_loop_run(globalconf.loop);
  786. }
  787. g_main_loop_unref(globalconf.loop);
  788. globalconf.loop = NULL;
  789. awesome_atexit(false);
  790. return globalconf.exit_code;
  791. }
  792. // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80