]> git.sur5r.net Git - i3/i3/blobdiff - src/main.c
Merge branch 'master' into next
[i3/i3] / src / main.c
index 7a7c20ac437d3415392d93d61ad2b30076307846..76ea838d71ce9143f4cbfe552fefdff0901d8714 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * main.c: Initialization, main loop
  *
@@ -14,6 +14,8 @@
 #include <sys/un.h>
 #include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
 #include "all.h"
 
 #include "sd-daemon.h"
  * RLIM_INFINITY for i3 debugging versions. */
 struct rlimit original_rlimit_core;
 
+/* Whether this version of i3 is a debug build or a release build. */
+bool debug_build = false;
+
+/** The number of file descriptors passed via socket activation. */
+int listen_fds;
+
 static int xkb_event_base;
 
 int xkb_current_group;
@@ -45,7 +53,13 @@ xcb_timestamp_t last_timestamp = XCB_CURRENT_TIME;
 
 xcb_screen_t *root_screen;
 xcb_window_t root;
+
+/* Color depth, visual id and colormap to use when creating windows and
+ * pixmaps. Will use 32 bit depth and an appropriate visual, if available,
+ * otherwise the root window’s default (usually 24 bit TrueColor). */
 uint8_t root_depth;
+xcb_visualid_t visual_id;
+xcb_colormap_t colormap;
 
 struct ev_loop *main_loop;
 
@@ -205,6 +219,28 @@ static void i3_exit() {
 #if EV_VERSION_MAJOR >= 4
     ev_loop_destroy(main_loop);
 #endif
+
+    if (*shmlogname != '\0') {
+        fprintf(stderr, "Closing SHM log \"%s\"\n", shmlogname);
+        fflush(stderr);
+        shm_unlink(shmlogname);
+    }
+}
+
+/*
+ * (One-shot) Handler for all signals with default action "Term", see signal(7)
+ *
+ * Unlinks the SHM log and re-raises the signal.
+ *
+ */
+static void handle_signal(int sig, siginfo_t *info, void *data) {
+    fprintf(stderr, "Received signal %d, terminating\n", sig);
+    if (*shmlogname != '\0') {
+        fprintf(stderr, "Closing SHM log \"%s\"\n", shmlogname);
+        shm_unlink(shmlogname);
+    }
+    fflush(stderr);
+    raise(sig);
 }
 
 int main(int argc, char *argv[]) {
@@ -214,6 +250,7 @@ int main(int argc, char *argv[]) {
     bool delete_layout_path = false;
     bool force_xinerama = false;
     bool disable_signalhandler = false;
+    bool enable_32bit_visual = false;
     static struct option long_options[] = {
         {"no-autostart", no_argument, 0, 'a'},
         {"config", required_argument, 0, 'c'},
@@ -224,11 +261,16 @@ int main(int argc, char *argv[]) {
         {"force-xinerama", no_argument, 0, 0},
         {"force_xinerama", no_argument, 0, 0},
         {"disable-signalhandler", no_argument, 0, 0},
+        {"shmlog-size", required_argument, 0, 0},
+        {"shmlog_size", required_argument, 0, 0},
         {"get-socketpath", no_argument, 0, 0},
         {"get_socketpath", no_argument, 0, 0},
+        {"enable-32bit-visual", no_argument, 0, 0},
+        {"enable_32bit_visual", no_argument, 0, 0},
         {0, 0, 0, 0}
     };
     int option_index = 0, opt;
+    xcb_void_cookie_t colormap_cookie;
 
     setlocale(LC_ALL, "");
 
@@ -242,8 +284,21 @@ int main(int argc, char *argv[]) {
 
     srand(time(NULL));
 
+    /* Init logging *before* initializing debug_build to guarantee early
+     * (file) logging. */
     init_logging();
 
+    /* I3_VERSION contains either something like this:
+     *     "4.0.2 (2011-11-11, branch "release")".
+     * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
+     *
+     * So we check for the offset of the first opening round bracket to
+     * determine whether this is a git version or a release version. */
+    debug_build = ((strchr(I3_VERSION, '(') - I3_VERSION) > 10);
+
+    /* On non-release builds, disable SHM logging by default. */
+    shmlog_size = (debug_build ? 25 * 1024 * 1024 : 0);
+
     start_argv = argv;
 
     while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) {
@@ -293,18 +348,31 @@ int main(int argc, char *argv[]) {
                     break;
                 } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0 ||
                            strcmp(long_options[option_index].name, "get_socketpath") == 0) {
-                    char *socket_path = socket_path_from_x11();
+                    char *socket_path = root_atom_contents("I3_SOCKET_PATH");
                     if (socket_path) {
                         printf("%s\n", socket_path);
                         return 0;
                     }
 
                     return 1;
+                } else if (strcmp(long_options[option_index].name, "shmlog-size") == 0 ||
+                           strcmp(long_options[option_index].name, "shmlog_size") == 0) {
+                    shmlog_size = atoi(optarg);
+                    /* Re-initialize logging immediately to get as many
+                     * logmessages as possible into the SHM log. */
+                    init_logging();
+                    LOG("Limiting SHM log size to %d bytes\n", shmlog_size);
+                    break;
                 } else if (strcmp(long_options[option_index].name, "restart") == 0) {
                     FREE(layout_path);
                     layout_path = sstrdup(optarg);
                     delete_layout_path = true;
                     break;
+                } else if (strcmp(long_options[option_index].name, "enable_32bit_visual") == 0 ||
+                           strcmp(long_options[option_index].name, "enable-32bit-visual") == 0) {
+                    LOG("Enabling 32 bit visual (if available)\n");
+                    enable_32bit_visual = true;
+                    break;
                 }
                 /* fall-through */
             default:
@@ -326,6 +394,15 @@ int main(int argc, char *argv[]) {
                 fprintf(stderr, "\t--get-socketpath\n"
                                 "\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n");
                 fprintf(stderr, "\n");
+                fprintf(stderr, "\t--shmlog-size <limit>\n"
+                                "\tLimits the size of the i3 SHM log to <limit> bytes. Setting this\n"
+                                "\tto 0 disables SHM logging entirely.\n"
+                                "\tThe default is %d bytes.\n", shmlog_size);
+                fprintf(stderr, "\n");
+                fprintf(stderr, "\t--enable-32bit-visual\n"
+                                "\tMakes i3 use a 32 bit visual, if available. Necessary for\n"
+                                "\tpseudo-transparency with xcompmgr.\n");
+                fprintf(stderr, "\n");
                 fprintf(stderr, "If you pass plain text arguments, i3 will interpret them as a command\n"
                                 "to send to a currently running i3 (like i3-msg). This allows you to\n"
                                 "use nice and logical commands, such as:\n"
@@ -361,7 +438,7 @@ int main(int argc, char *argv[]) {
             optind++;
         }
         LOG("Command is: %s (%d bytes)\n", payload, strlen(payload));
-        char *socket_path = socket_path_from_x11();
+        char *socket_path = root_atom_contents("I3_SOCKET_PATH");
         if (!socket_path) {
             ELOG("Could not get i3 IPC socket path\n");
             return 1;
@@ -395,13 +472,11 @@ int main(int argc, char *argv[]) {
         return 0;
     }
 
-    /* I3_VERSION contains either something like this:
-     *     "4.0.2 (2011-11-11, branch "release")".
-     * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
-     *
-     * So we check for the offset of the first opening round bracket to
-     * determine whether this is a git version or a release version. */
-    if ((strchr(I3_VERSION, '(') - I3_VERSION) > 10) {
+    /* Enable logging to handle the case when the user did not specify --shmlog-size */
+    init_logging();
+
+    /* Try to enable core dumps by default when running a debug build */
+    if (debug_build) {
         struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
         setrlimit(RLIMIT_CORE, &limit);
 
@@ -438,7 +513,38 @@ int main(int argc, char *argv[]) {
 
     root_screen = xcb_aux_get_screen(conn, conn_screen);
     root = root_screen->root;
+
+    /* By default, we use the same depth and visual as the root window, which
+     * usually is TrueColor (24 bit depth) and the corresponding visual.
+     * However, we also check if a 32 bit depth and visual are available (for
+     * transparency) and use it if so. */
     root_depth = root_screen->root_depth;
+    visual_id = root_screen->root_visual;
+    colormap = root_screen->default_colormap;
+
+    if (enable_32bit_visual) {
+        xcb_depth_iterator_t depth_iter;
+        xcb_visualtype_iterator_t visual_iter;
+        for (depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
+             depth_iter.rem;
+             xcb_depth_next(&depth_iter)) {
+            if (depth_iter.data->depth != 32)
+                continue;
+            visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
+            if (!visual_iter.rem)
+                continue;
+
+            visual_id = visual_iter.data->visual_id;
+            root_depth = depth_iter.data->depth;
+            colormap = xcb_generate_id(conn);
+            colormap_cookie = xcb_create_colormap_checked(conn, XCB_COLORMAP_ALLOC_NONE, colormap, root, visual_id);
+            DLOG("Found a visual with 32 bit depth.\n");
+            break;
+        }
+    }
+
+    DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id);
+
     xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root);
     xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
 
@@ -468,6 +574,20 @@ int main(int argc, char *argv[]) {
     cookie = xcb_change_window_attributes_checked(conn, root, mask, values);
     check_error(conn, cookie, "Another window manager seems to be running");
 
+    /* By now we already checked for replies once, so let’s see if colormap
+     * creation worked (if requested). */
+    if (colormap != root_screen->default_colormap) {
+        xcb_generic_error_t *error = xcb_request_check(conn, colormap_cookie);
+        if (error != NULL) {
+            ELOG("Could not create ColorMap for 32 bit visual, falling back to X11 default.\n");
+            root_depth = root_screen->root_depth;
+            visual_id = root_screen->root_visual;
+            colormap = root_screen->default_colormap;
+            DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id);
+            free(error);
+        }
+    }
+
     xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL);
     if (greply == NULL) {
         ELOG("Could not get geometry of the root window, exiting\n");
@@ -544,16 +664,7 @@ int main(int argc, char *argv[]) {
 
     property_handlers_init();
 
-    /* Set up the atoms we support */
-    xcb_atom_t supported_atoms[] = {
-#define xmacro(atom) A_ ## atom,
-#include "atoms.xmacro"
-#undef xmacro
-    };
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 16, supported_atoms);
-    /* Set up the window manager’s name */
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &root);
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
+    ewmh_setup_hints();
 
     keysyms = xcb_key_symbols_alloc(conn);
 
@@ -618,14 +729,26 @@ int main(int argc, char *argv[]) {
     /* Also handle the UNIX domain sockets passed via socket activation. The
      * parameter 1 means "remove the environment variables", we don’t want to
      * pass these to child processes. */
-    int fds = sd_listen_fds(1);
-    if (fds < 0)
+    listen_fds = sd_listen_fds(0);
+    if (listen_fds < 0)
         ELOG("socket activation: Error in sd_listen_fds\n");
-    else if (fds == 0)
+    else if (listen_fds == 0)
         DLOG("socket activation: no sockets passed\n");
     else {
-        for (int fd = SD_LISTEN_FDS_START; fd < (SD_LISTEN_FDS_START + fds); fd++) {
+        int flags;
+        for (int fd = SD_LISTEN_FDS_START;
+             fd < (SD_LISTEN_FDS_START + listen_fds);
+             fd++) {
             DLOG("socket activation: also listening on fd %d\n", fd);
+
+            /* sd_listen_fds() enables FD_CLOEXEC by default.
+             * However, we need to keep the file descriptors open for in-place
+             * restarting, therefore we explicitly disable FD_CLOEXEC. */
+            if ((flags = fcntl(fd, F_GETFD)) < 0 ||
+                fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
+                ELOG("Could not disable FD_CLOEXEC on fd %d\n", fd);
+            }
+
             struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
             ev_io_init(ipc_io, ipc_new_client, fd, EV_READ);
             ev_io_start(main_loop, ipc_io);
@@ -662,8 +785,31 @@ int main(int argc, char *argv[]) {
 
     manage_existing_windows(root);
 
+    struct sigaction action;
+
+    action.sa_sigaction = handle_signal;
+    action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
+    sigemptyset(&action.sa_mask);
+
     if (!disable_signalhandler)
         setup_signal_handler();
+    else {
+        /* Catch all signals with default action "Core", see signal(7) */
+        if (sigaction(SIGQUIT, &action, NULL) == -1 ||
+            sigaction(SIGILL, &action, NULL) == -1 ||
+            sigaction(SIGABRT, &action, NULL) == -1 ||
+            sigaction(SIGFPE, &action, NULL) == -1 ||
+            sigaction(SIGSEGV, &action, NULL) == -1)
+            ELOG("Could not setup signal handler");
+    }
+
+    /* Catch all signals with default action "Term", see signal(7) */
+    if (sigaction(SIGHUP, &action, NULL) == -1 ||
+        sigaction(SIGINT, &action, NULL) == -1 ||
+        sigaction(SIGALRM, &action, NULL) == -1 ||
+        sigaction(SIGUSR1, &action, NULL) == -1 ||
+        sigaction(SIGUSR2, &action, NULL) == -1)
+        ELOG("Could not setup signal handler");
 
     /* Ignore SIGPIPE to survive errors when an IPC client disconnects
      * while we are sending him a message */
@@ -689,8 +835,9 @@ int main(int argc, char *argv[]) {
     Barconfig *barconfig;
     TAILQ_FOREACH(barconfig, &barconfigs, configs) {
         char *command = NULL;
-        sasprintf(&command, "i3bar --bar_id=%s --socket=\"%s\"",
-                  barconfig->id, current_socketpath);
+        sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
+                barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",
+                barconfig->id, current_socketpath);
         LOG("Starting bar process: %s\n", command);
         start_application(command, true);
         free(command);