X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fmain.c;h=d87b9a29b0bcb75febe7e60b91b52bb220b50e97;hb=0d8b6714e39af81cbd6f4fbad500872a715dea24;hp=e8e0daa8c84a4bbfa783ef5afb8f691cda19de5e;hpb=43386c51d413dc569cfd4939d534f8396f01586d;p=i3%2Fi3 diff --git a/src/main.c b/src/main.c index e8e0daa8..d87b9a29 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "main.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,6 +7,8 @@ * main.c: Initialization, main loop * */ +#include "all.h" + #include #include #include @@ -19,9 +19,12 @@ #include #include #include -#include "all.h" #include "shmlog.h" +#ifdef I3_ASAN_ENABLED +#include +#endif + #include "sd-daemon.h" /* The original value of RLIMIT_CORE when i3 was started. We need to restore @@ -32,9 +35,9 @@ struct rlimit original_rlimit_core; /** The number of file descriptors passed via socket activation. */ int listen_fds; -/* We keep the xcb_check watcher around to be able to enable and disable it +/* We keep the xcb_prepare watcher around to be able to enable and disable it * temporarily for drag_pointer(). */ -static struct ev_check *xcb_check; +static struct ev_prepare *xcb_prepare; extern Con *focused; @@ -59,7 +62,7 @@ xcb_window_t root; * 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_visualtype_t *visual_type; xcb_colormap_t colormap; struct ev_loop *main_loop; @@ -87,30 +90,28 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment /* We hope that those are supported and set them to true */ bool xcursor_supported = true; +bool xkb_supported = true; + +bool force_xinerama = false; /* - * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. + * This callback is only a dummy, see xcb_prepare_cb. * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop * */ static void xcb_got_event(EV_P_ struct ev_io *w, int revents) { - /* empty, because xcb_prepare_cb and xcb_check_cb are used */ + /* empty, because xcb_prepare_cb are used */ } /* - * Flush before blocking (and waiting for new events) + * Called just before the event loop sleeps. Ensures xcb’s incoming and outgoing + * queues are empty so that any activity will trigger another event loop + * iteration, and hence another xcb_prepare_cb invocation. * */ static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { - xcb_flush(conn); -} - -/* - * Instead of polling the X connection socket we leave this to - * xcb_poll_for_event() which knows better than we can ever know. - * - */ -static void xcb_check_cb(EV_P_ ev_check *w, int revents) { + /* Process all queued (and possibly new) events before the event loop + sleeps. */ xcb_generic_event_t *event; while ((event = xcb_poll_for_event(conn)) != NULL) { @@ -133,6 +134,9 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { free(event); } + + /* Flush all queued events to X11. */ + xcb_flush(conn); } /* @@ -144,12 +148,12 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { void main_set_x11_cb(bool enable) { DLOG("Setting main X11 callback to enabled=%d\n", enable); if (enable) { - ev_check_start(main_loop, xcb_check); + ev_prepare_start(main_loop, xcb_prepare); /* Trigger the watcher explicitly to handle all remaining X11 events. * drag_pointer()’s event handler exits in the middle of the loop. */ - ev_feed_event(main_loop, xcb_check, 0); + ev_feed_event(main_loop, xcb_prepare, 0); } else { - ev_check_stop(main_loop, xcb_check); + ev_prepare_stop(main_loop, xcb_prepare); } } @@ -188,12 +192,12 @@ static void handle_signal(int sig, siginfo_t *info, void *data) { int main(int argc, char *argv[]) { /* Keep a symbol pointing to the I3_VERSION string constant so that we have * it in gdb backtraces. */ - const char *_i3_version __attribute__((unused)) = i3_version; + static const char *_i3_version __attribute__((used)) = I3_VERSION; char *override_configpath = NULL; bool autostart = true; char *layout_path = NULL; bool delete_layout_path = false; - bool force_xinerama = false; + bool disable_randr15 = false; char *fake_outputs = NULL; bool disable_signalhandler = false; bool only_check_config = false; @@ -209,6 +213,8 @@ int main(int argc, char *argv[]) { {"restart", required_argument, 0, 0}, {"force-xinerama", no_argument, 0, 0}, {"force_xinerama", no_argument, 0, 0}, + {"disable-randr15", no_argument, 0, 0}, + {"disable_randr15", no_argument, 0, 0}, {"disable-signalhandler", no_argument, 0, 0}, {"shmlog-size", required_argument, 0, 0}, {"shmlog_size", required_argument, 0, 0}, @@ -289,6 +295,10 @@ int main(int argc, char *argv[]) { "Please check if your driver really does not support RandR " "and disable this option as soon as you can.\n"); break; + } else if (strcmp(long_options[option_index].name, "disable-randr15") == 0 || + strcmp(long_options[option_index].name, "disable_randr15") == 0) { + disable_randr15 = true; + break; } else if (strcmp(long_options[option_index].name, "disable-signalhandler") == 0) { disable_signalhandler = true; break; @@ -403,12 +413,14 @@ int main(int argc, char *argv[]) { memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + FREE(socket_path); if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) err(EXIT_FAILURE, "Could not connect to i3"); - if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_COMMAND, + if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_RUN_COMMAND, (uint8_t *)payload) == -1) err(EXIT_FAILURE, "IPC: write()"); + FREE(payload); uint32_t reply_length; uint32_t reply_type; @@ -419,9 +431,10 @@ int main(int argc, char *argv[]) { err(EXIT_FAILURE, "IPC: read()"); return 1; } - if (reply_type != I3_IPC_MESSAGE_TYPE_COMMAND) - errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_MESSAGE_TYPE_COMMAND); + if (reply_type != I3_IPC_REPLY_TYPE_COMMAND) + errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_REPLY_TYPE_COMMAND); printf("%.*s\n", reply_length, reply); + FREE(reply); return 0; } @@ -480,18 +493,33 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - /* 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; + visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32); + if (visual_type != NULL) { + root_depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id); + colormap = xcb_generate_id(conn); + + xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(conn, + XCB_COLORMAP_ALLOC_NONE, + colormap, + root, + visual_type->visual_id); + + xcb_generic_error_t *error = xcb_request_check(conn, cm_cookie); + if (error != NULL) { + ELOG("Could not create colormap. Error code: %d\n", error->error_code); + exit(EXIT_FAILURE); + } + } else { + visual_type = get_visualtype(root_screen); + } + + init_dpi(); - DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id); - DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d, dpi = %d\n", - root_screen->height_in_pixels, root_screen->height_in_millimeters, - (int)((double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters)); + DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_type->visual_id); + DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d\n", + root_screen->height_in_pixels, root_screen->height_in_millimeters); DLOG("One logical pixel corresponds to %d physical pixels on this display.\n", logical_px(1)); xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root); @@ -521,9 +549,20 @@ int main(int argc, char *argv[]) { config.ipc_socket_path = sstrdup(config.ipc_socket_path); } + if (config.force_xinerama) { + force_xinerama = true; + } + xcb_void_cookie_t cookie; cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK}); - check_error(conn, cookie, "Another window manager seems to be running"); + xcb_generic_error_t *error = xcb_request_check(conn, cookie); + if (error != NULL) { + ELOG("Another window manager seems to be running (X error %d)\n", error->error_code); +#ifdef I3_ASAN_ENABLED + __lsan_do_leak_check(); +#endif + return 1; + } xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL); if (greply == NULL) { @@ -543,6 +582,7 @@ int main(int argc, char *argv[]) { const xcb_query_extension_reply_t *extreply; extreply = xcb_get_extension_data(conn, &xcb_xkb_id); + xkb_supported = extreply->present; if (!extreply->present) { DLOG("xkb is not present on this server\n"); } else { @@ -556,6 +596,37 @@ int main(int argc, char *argv[]) { 0xff, 0xff, NULL); + + /* Setting both, XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE and + * XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED, will lead to the + * X server sending us the full XKB state in KeyPress and KeyRelease: + * https://sources.debian.net/src/xorg-server/2:1.17.2-1.1/xkb/xkbEvents.c/?hl=927#L927 + */ + xcb_xkb_per_client_flags_reply_t *pcf_reply; + /* The last three parameters are unset because they are only relevant + * when using a feature called “automatic reset of boolean controls”: + * https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Automatic_Reset_of_Boolean_Controls + * */ + pcf_reply = xcb_xkb_per_client_flags_reply( + conn, + xcb_xkb_per_client_flags( + conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE | XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED, + XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE | XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED, + 0 /* uint32_t ctrlsToChange */, + 0 /* uint32_t autoCtrls */, + 0 /* uint32_t autoCtrlsValues */), + NULL); + if (pcf_reply == NULL || + !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE)) { + ELOG("Could not set XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE\n"); + } + if (pcf_reply == NULL || + !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED)) { + ELOG("Could not set XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED\n"); + } + free(pcf_reply); xkb_base = extreply->first_event; } @@ -569,12 +640,15 @@ int main(int argc, char *argv[]) { xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms); + if (!load_keymap()) + die("Could not load keymap\n"); + translate_keysyms(); - grab_all_keys(conn, false); + grab_all_keys(conn); bool needs_tree_init = true; - if (layout_path) { - LOG("Trying to restore the layout from %s...", layout_path); + if (layout_path != NULL) { + LOG("Trying to restore the layout from \"%s\".\n", layout_path); needs_tree_init = !tree_restore(layout_path, greply); if (delete_layout_path) { unlink(layout_path); @@ -583,7 +657,6 @@ int main(int argc, char *argv[]) { * sockets) left. */ rmdir(dir); } - free(layout_path); } if (needs_tree_init) tree_init(greply); @@ -598,15 +671,42 @@ int main(int argc, char *argv[]) { fake_outputs_init(fake_outputs); FREE(fake_outputs); config.fake_outputs = NULL; - } else if (force_xinerama || config.force_xinerama) { + } else if (force_xinerama) { /* Force Xinerama (for drivers which don't support RandR yet, esp. the * nVidia binary graphics driver), when specified either in the config * file or on command-line */ xinerama_init(); } else { DLOG("Checking for XRandR...\n"); - randr_init(&randr_base); + randr_init(&randr_base, disable_randr15 || config.disable_randr15); + } + + /* We need to force disabling outputs which have been loaded from the + * layout file but are no longer active. This can happen if the output has + * been disabled in the short time between writing the restart layout file + * and restarting i3. See #2326. */ + if (layout_path != NULL && randr_base > -1) { + Con *con; + TAILQ_FOREACH(con, &(croot->nodes_head), nodes) { + Output *output; + TAILQ_FOREACH(output, &outputs, outputs) { + if (output->active || strcmp(con->name, output_primary_name(output)) != 0) + continue; + + /* This will correctly correlate the output with its content + * container. We need to make the connection to properly + * disable the output. */ + if (output->con == NULL) { + output_init_con(output); + output->changed = false; + } + + output->to_be_disabled = true; + randr_disable_output(output); + } + } } + FREE(layout_path); scratchpad_fix_resolution(); @@ -621,11 +721,10 @@ int main(int argc, char *argv[]) { ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n", pointerreply->root_x, pointerreply->root_y); output = get_first_output(); - if (!output) - die("No usable outputs available.\n"); } con_focus(con_descend_focused(output_get_content(output->con))); + free(pointerreply); } tree_render(); @@ -635,7 +734,7 @@ int main(int argc, char *argv[]) { if (ipc_socket == -1) { ELOG("Could not create the IPC socket, IPC disabled\n"); } else { - struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); + struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io)); ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); ev_io_start(main_loop, ipc_io); } @@ -663,7 +762,7 @@ int main(int argc, char *argv[]) { ELOG("Could not disable FD_CLOEXEC on fd %d\n", fd); } - struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); + struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io)); ev_io_init(ipc_io, ipc_new_client, fd, EV_READ); ev_io_start(main_loop, ipc_io); } @@ -679,16 +778,12 @@ int main(int argc, char *argv[]) { ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); - struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); - xcb_check = scalloc(sizeof(struct ev_check)); - struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); + struct ev_io *xcb_watcher = scalloc(1, sizeof(struct ev_io)); + xcb_prepare = scalloc(1, sizeof(struct ev_prepare)); ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); ev_io_start(main_loop, xcb_watcher); - ev_check_init(xcb_check, xcb_check_cb); - ev_check_start(main_loop, xcb_check); - ev_prepare_init(xcb_prepare, xcb_prepare_cb); ev_prepare_start(main_loop, xcb_prepare); @@ -747,12 +842,17 @@ int main(int argc, char *argv[]) { (uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS}); xcb_copy_area(conn, root->root, pixmap, gc, 0, 0, 0, 0, width, height); - xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap}); + xcb_change_window_attributes(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap}); xcb_flush(conn); xcb_free_gc(conn, gc); xcb_free_pixmap(conn, pixmap); } +#if defined(__OpenBSD__) + if (pledge("stdio rpath wpath cpath proc exec unix", NULL) == -1) + err(EXIT_FAILURE, "pledge"); +#endif + struct sigaction action; action.sa_sigaction = handle_signal; @@ -768,7 +868,7 @@ int main(int argc, char *argv[]) { sigaction(SIGABRT, &action, NULL) == -1 || sigaction(SIGFPE, &action, NULL) == -1 || sigaction(SIGSEGV, &action, NULL) == -1) - ELOG("Could not setup signal handler"); + ELOG("Could not setup signal handler.\n"); } /* Catch all signals with default action "Term", see signal(7) */ @@ -777,7 +877,7 @@ int main(int argc, char *argv[]) { sigaction(SIGALRM, &action, NULL) == -1 || sigaction(SIGUSR1, &action, NULL) == -1 || sigaction(SIGUSR2, &action, NULL) == -1) - ELOG("Could not setup signal handler"); + ELOG("Could not setup signal handler.\n"); /* Ignore SIGPIPE to survive errors when an IPC client disconnects * while we are sending them a message */ @@ -785,18 +885,28 @@ int main(int argc, char *argv[]) { /* Autostarting exec-lines */ if (autostart) { - struct Autostart *exec; - TAILQ_FOREACH(exec, &autostarts, autostarts) { + while (!TAILQ_EMPTY(&autostarts)) { + struct Autostart *exec = TAILQ_FIRST(&autostarts); + LOG("auto-starting %s\n", exec->command); start_application(exec->command, exec->no_startup_id); + + FREE(exec->command); + TAILQ_REMOVE(&autostarts, exec, autostarts); + FREE(exec); } } /* Autostarting exec_always-lines */ - struct Autostart *exec_always; - TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) { + while (!TAILQ_EMPTY(&autostarts_always)) { + struct Autostart *exec_always = TAILQ_FIRST(&autostarts_always); + LOG("auto-starting (always!) %s\n", exec_always->command); start_application(exec_always->command, exec_always->no_startup_id); + + FREE(exec_always->command); + TAILQ_REMOVE(&autostarts_always, exec_always, autostarts_always); + FREE(exec_always); } /* Start i3bar processes for all configured bars */