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 31KB

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