X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fmain.c;h=610a2c19648009b0cc02701372238134c74d6c42;hb=5f52c78aa04326c25c1f178c3612c4b5613b5f85;hp=aee95f757b7763eaa4f87f1f250448281d828315;hpb=aa65b507ad107053a0d11989080dbc94b0f63d17;p=i3%2Fi3 diff --git a/src/main.c b/src/main.c index aee95f75..610a2c19 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,9 @@ */ #include #include +#include +#include +#include #include "all.h" #include "sd-daemon.h" @@ -16,6 +19,16 @@ extern Con *focused; char **start_argv; xcb_connection_t *conn; +/* The screen (0 when you are using DISPLAY=:0) of the connection 'conn' */ +int conn_screen; + +/* Display handle for libstartup-notification */ +SnDisplay *sndisplay; + +/* The last timestamp we got from X11 (timestamps are included in some events + * and are used for some things, like determining a unique ID in startup + * notification). */ +xcb_timestamp_t last_timestamp = XCB_CURRENT_TIME; xcb_screen_t *root_screen; xcb_window_t root; @@ -163,9 +176,15 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) { DLOG("Done\n"); } +/* + * Exit handler which destroys the main_loop. Will trigger cleanup handlers. + * + */ +static void i3_exit() { + ev_loop_destroy(main_loop); +} + int main(int argc, char *argv[]) { - //parse_cmd("[ foo ] attach, attach ; focus"); - int screens; char *override_configpath = NULL; bool autostart = true; char *layout_path = NULL; @@ -182,6 +201,7 @@ int main(int argc, char *argv[]) { {"restart", required_argument, 0, 0}, {"force-xinerama", no_argument, 0, 0}, {"disable-signalhandler", no_argument, 0, 0}, + {"get-socketpath", no_argument, 0, 0}, {0, 0, 0, 0} }; int option_index = 0, opt; @@ -240,6 +260,14 @@ int main(int argc, char *argv[]) { } else if (strcmp(long_options[option_index].name, "disable-signalhandler") == 0) { disable_signalhandler = true; break; + } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0) { + char *socket_path = socket_path_from_x11(); + if (socket_path) { + printf("%s\n", socket_path); + return 0; + } + + return 1; } else if (strcmp(long_options[option_index].name, "restart") == 0) { FREE(layout_path); layout_path = sstrdup(optarg); @@ -250,26 +278,100 @@ int main(int argc, char *argv[]) { default: fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]); fprintf(stderr, "\n"); - fprintf(stderr, "-a: disable autostart\n"); - fprintf(stderr, "-L : load the layout from \n"); - fprintf(stderr, "-v: display version and exit\n"); - fprintf(stderr, "-V: enable verbose mode\n"); - fprintf(stderr, "-d : enable debug loglevel \n"); - fprintf(stderr, "-c : use the provided configfile instead\n"); - fprintf(stderr, "-C: check configuration file and exit\n"); - fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This " - "option should only be used if you are stuck with the " - "nvidia closed source driver which does not support RandR.\n"); + fprintf(stderr, "\t-a disable autostart ('exec' lines in config)\n"); + fprintf(stderr, "\t-c use the provided configfile instead\n"); + fprintf(stderr, "\t-C validate configuration file and exit\n"); + fprintf(stderr, "\t-d enable debug output with the specified loglevel\n"); + fprintf(stderr, "\t-L path to the serialized layout during restarts\n"); + fprintf(stderr, "\t-v display version and exit\n"); + fprintf(stderr, "\t-V enable verbose mode\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "\t--force-xinerama\n" + "\tUse Xinerama instead of RandR.\n" + "\tThis option should only be used if you are stuck with the\n" + "\tnvidia closed source driver which does not support RandR.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "\t--get-socketpath\n" + "\tRetrieve the i3 IPC socket path from X11, print it, then exit.\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" + "\n" + "\ti3 border none\n" + "\ti3 floating toggle\n" + "\ti3 kill window\n" + "\n"); exit(EXIT_FAILURE); } } + /* If the user passes more arguments, we act like i3-msg would: Just send + * the arguments as an IPC message to i3. This allows for nice semantic + * commands such as 'i3 border none'. */ + if (optind < argc) { + /* We enable verbose mode so that the user knows what’s going on. + * This should make it easier to find mistakes when the user passes + * arguments by mistake. */ + set_verbosity(true); + + LOG("Additional arguments passed. Sending them as a command to i3.\n"); + char *payload = NULL; + while (optind < argc) { + if (!payload) { + payload = sstrdup(argv[optind]); + } else { + char *both; + if (asprintf(&both, "%s %s", payload, argv[optind]) == -1) + err(EXIT_FAILURE, "asprintf"); + free(payload); + payload = both; + } + optind++; + } + LOG("Command is: %s (%d bytes)\n", payload, strlen(payload)); + char *socket_path = socket_path_from_x11(); + if (!socket_path) { + ELOG("Could not get i3 IPC socket path\n"); + return 1; + } + + int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (sockfd == -1) + err(EXIT_FAILURE, "Could not create socket"); + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_LOCAL; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + 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, + (uint8_t*)payload) == -1) + err(EXIT_FAILURE, "IPC: write()"); + + uint32_t reply_length; + uint8_t *reply; + int ret; + if ((ret = ipc_recv_message(sockfd, I3_IPC_MESSAGE_TYPE_COMMAND, + &reply_length, &reply)) != 0) { + if (ret == -1) + err(EXIT_FAILURE, "IPC: read()"); + return 1; + } + printf("%.*s\n", reply_length, reply); + return 0; + } + LOG("i3 (tree) version " I3_VERSION " starting\n"); - conn = xcb_connect(NULL, &screens); + conn = xcb_connect(NULL, &conn_screen); if (xcb_connection_has_error(conn)) errx(EXIT_FAILURE, "Cannot open display\n"); + sndisplay = sn_xcb_display_new(conn, NULL, NULL); + /* Initialize the libev event loop. This needs to be done before loading * the config file because the parser will install an ev_child watcher * for the nagbar when config errors are found. */ @@ -277,7 +379,7 @@ int main(int argc, char *argv[]) { if (main_loop == NULL) die("Could not initialize libev. Bad LIBEV_FLAGS?\n"); - root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, conn_screen); root = root_screen->root; root_depth = root_screen->root_depth; xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root); @@ -340,17 +442,9 @@ int main(int argc, char *argv[]) { /* Set a cursor for the root window (otherwise the root window will show no cursor until the first client is launched). */ - if (xcursor_supported) { - xcursor_set_root_cursor(); - } else { - xcb_cursor_t cursor_id = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - int xcb_cursor = xcursor_get_xcb_cursor(XCURSOR_CURSOR_POINTER); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.id, cursor_font.id, - xcb_cursor, xcb_cursor + 1, 0, 0, 0, 65535, 65535, 65535); - xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id); - xcb_free_cursor(conn, cursor_id); - } + if (xcursor_supported) + xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); + else xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); if (xkb_supported) { int errBase, @@ -424,7 +518,10 @@ int main(int argc, char *argv[]) { free(greply); - 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 */ + if (force_xinerama || config.force_xinerama) { xinerama_init(); } else { DLOG("Checking for XRandR...\n"); @@ -529,5 +626,9 @@ int main(int argc, char *argv[]) { start_application(exec_always->command); } + /* Make sure to destroy the event loop to invoke the cleeanup callbacks + * when calling exit() */ + atexit(i3_exit); + ev_loop(main_loop, 0); }