X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=blobdiff_plain;f=i3-input%2Fmain.c;h=64a089dd8f9cb5d947f4f572a8c0361531556310;hp=fbc551e684a247cece513f570505ec318bd9f436;hb=fbfbdb8e124480bc90bbd6a8b59c1692c4ebd531;hpb=af15087b995f912f68eab21ce7602ea71e79dfd1 diff --git a/i3-input/main.c b/i3-input/main.c index fbc551e6..64a089dd 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -2,13 +2,12 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * * i3-input/main.c: Utility which lets the user input commands and sends them * to i3. * */ -#include #include #include #include @@ -55,9 +54,10 @@ static int limit; xcb_window_t root; xcb_connection_t *conn; xcb_screen_t *root_screen; +static xcb_get_input_focus_cookie_t focus_cookie; /* - * Having verboselog() and errorlog() is necessary when using libi3. + * Having verboselog(), errorlog() and debuglog() is necessary when using libi3. * */ void verboselog(char *fmt, ...) { @@ -76,13 +76,34 @@ void errorlog(char *fmt, ...) { va_end(args); } +void debuglog(char *fmt, ...) { +} + +/* + * Restores the X11 input focus to whereever it was before. + * This is necessary because i3-input’s window has override_redirect=1 + * (→ unmanaged by the window manager) and thus i3-input changes focus itself. + * This function is called on exit(). + * + */ +static void restore_input_focus(void) { + xcb_generic_error_t *error; + xcb_get_input_focus_reply_t *reply = xcb_get_input_focus_reply(conn, focus_cookie, &error); + if (error != NULL) { + fprintf(stderr, "[i3-input] ERROR: Could not restore input focus (X error %d)\n", error->error_code); + return; + } + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, reply->focus, XCB_CURRENT_TIME); + xcb_flush(conn); +} + /* * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for * rendering it (UCS-2) or sending it to i3 (UTF-8). * */ static uint8_t *concat_strings(char **glyphs, int max) { - uint8_t *output = calloc(max+1, 4); + uint8_t *output = scalloc(max + 1, 4); uint8_t *walk = output; for (int c = 0; c < max; c++) { printf("at %c\n", glyphs[c][0]); @@ -91,7 +112,7 @@ static uint8_t *concat_strings(char **glyphs, int max) { memcpy(walk, glyphs[c], 2); walk += 2; } else { - strcpy((char*)walk, glyphs[c]); + strcpy((char *)walk, glyphs[c]); walk += strlen(glyphs[c]); } } @@ -108,29 +129,29 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t printf("expose!\n"); /* re-draw the background */ - xcb_rectangle_t border = {0, 0, 500, font.height + 8}, inner = {2, 2, 496, font.height + 8 - 4}; - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FF0000") }); + xcb_rectangle_t border = {0, 0, logical_px(500), font.height + logical_px(8)}, + inner = {logical_px(2), logical_px(2), logical_px(496), font.height + logical_px(8) - logical_px(4)}; + xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")}); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border); - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#000000") }); + xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")}); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner); /* restore font color */ - set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); + set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000")); /* draw the prompt … */ if (prompt != NULL) { - draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492); + draw_text(prompt, pixmap, pixmap_gc, NULL, logical_px(4), logical_px(4), logical_px(492)); } /* … and the text */ - if (input_position > 0) - { + if (input_position > 0) { i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); - draw_text(input, pixmap, pixmap_gc, 4, 4, 492); + draw_text(input, pixmap, pixmap_gc, NULL, prompt_offset + logical_px(4), logical_px(4), logical_px(492)); i3string_free(input); } /* Copy the contents of the pixmap to the real window */ - xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8); + xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), font.height + logical_px(8)); xcb_flush(conn); return 1; @@ -153,28 +174,28 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel } static void finish_input() { - char *command = (char*)concat_strings(glyphs_utf8, input_position); + char *command = (char *)concat_strings(glyphs_utf8, input_position); - /* count the occurences of %s in the string */ + /* count the occurrences of %s in the string */ int c; int len = strlen(format); int cnt = 0; - for (c = 0; c < (len-1); c++) - if (format[c] == '%' && format[c+1] == 's') + for (c = 0; c < (len - 1); c++) + if (format[c] == '%' && format[c + 1] == 's') cnt++; - printf("occurences = %d\n", cnt); + printf("occurrences = %d\n", cnt); /* allocate space for the output */ int inputlen = strlen(command); - char *full = calloc(1, - strlen(format) - (2 * cnt) /* format without all %s */ - + (inputlen * cnt) /* replaced %s */ - + 1); /* trailing NUL */ + char *full = scalloc(strlen(format) - (2 * cnt) /* format without all %s */ + + (inputlen * cnt) /* replaced %s */ + + 1, /* trailing NUL */ + 1); char *dest = full; for (c = 0; c < len; c++) { /* if this is not % or it is % but without a following 's', * just copy the character */ - if (format[c] != '%' || (c == (len-1)) || format[c+1] != 's') + if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's') *(dest++) = format[c]; else { strncat(dest, command, inputlen); @@ -187,7 +208,13 @@ static void finish_input() { /* prefix the command if a prefix was specified on commandline */ printf("command = %s\n", full); - ipc_send_message(sockfd, strlen(full), 0, (uint8_t*)full); + restore_input_focus(); + + xcb_aux_sync(conn); + + ipc_send_message(sockfd, strlen(full), 0, (uint8_t *)full); + + free(full); #if 0 free(command); @@ -208,6 +235,9 @@ static void finish_input() { static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { printf("Keypress %d, state raw = %d\n", event->detail, event->state); + // TODO: port the input handling code from i3lock once libxkbcommon ≥ 0.5.0 + // is available in distros. + /* See the documentation of xcb_key_symbols_get_keysym for this one. * Basically: We get either col 0 or col 1, depending on whether shift is * pressed. */ @@ -239,6 +269,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press return 1; } if (sym == XK_Escape) { + restore_input_focus(); exit(0); } @@ -264,8 +295,8 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press } xcb_char2b_t inp; - inp.byte1 = ( ucs & 0xff00 ) >> 2; - inp.byte2 = ( ucs & 0x00ff ) >> 0; + inp.byte1 = (ucs & 0xff00) >> 2; + inp.byte2 = (ucs & 0x00ff) >> 0; printf("inp.byte1 = %02x, inp.byte2 = %02x\n", inp.byte1, inp.byte2); /* convert it to UTF-8 */ @@ -283,10 +314,54 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press return 1; } +static xcb_rectangle_t get_window_position(void) { + xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8)}; + + xcb_get_input_focus_reply_t *input_focus = NULL; + xcb_get_geometry_reply_t *geometry = NULL; + xcb_translate_coordinates_reply_t *coordinates = NULL; + + /* In rare cases, the window holding the input focus might disappear while we are figuring out its + * position. To avoid this, we grab the server in the meantime. */ + xcb_grab_server(conn); + + input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); + if (input_focus == NULL || input_focus->focus == XCB_NONE) { + DLOG("Failed to receive the current input focus or no window has the input focus right now.\n"); + goto free_resources; + } + + geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL); + if (geometry == NULL) { + DLOG("Failed to received window geometry.\n"); + goto free_resources; + } + + coordinates = xcb_translate_coordinates_reply( + conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL); + if (coordinates == NULL) { + DLOG("Failed to translate coordinates.\n"); + goto free_resources; + } + + DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y); + result.x += coordinates->dst_x; + result.y += coordinates->dst_y; + +free_resources: + xcb_ungrab_server(conn); + xcb_flush(conn); + + FREE(input_focus); + FREE(geometry); + FREE(coordinates); + return result; +} + int main(int argc, char *argv[]) { - format = strdup("%s"); + format = sstrdup("%s"); socket_path = getenv("I3SOCK"); - char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"); + char *pattern = sstrdup("pango:monospace 8"); int o, option_index = 0; static struct option long_options[] = { @@ -298,8 +373,7 @@ int main(int argc, char *argv[]) { {"format", required_argument, 0, 'F'}, {"font", required_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0} - }; + {0, 0, 0, 0}}; char *options_string = "s:p:P:f:l:F:vh"; @@ -307,7 +381,7 @@ int main(int argc, char *argv[]) { switch (o) { case 's': FREE(socket_path); - socket_path = strdup(optarg); + socket_path = sstrdup(optarg); break; case 'v': printf("i3-input " I3_VERSION); @@ -327,11 +401,11 @@ int main(int argc, char *argv[]) { break; case 'f': FREE(pattern); - pattern = strdup(optarg); + pattern = sstrdup(optarg); break; case 'F': FREE(format); - format = strdup(optarg); + format = sstrdup(optarg); break; case 'h': printf("i3-input " I3_VERSION "\n"); @@ -345,20 +419,23 @@ int main(int argc, char *argv[]) { printf("using format \"%s\"\n", format); + int screen; + conn = xcb_connect(NULL, &screen); + if (!conn || xcb_connection_has_error(conn)) + die("Cannot open display\n"); + if (socket_path == NULL) - socket_path = root_atom_contents("I3_SOCKET_PATH"); + socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen); if (socket_path == NULL) socket_path = "/tmp/i3-ipc.sock"; sockfd = ipc_connect(socket_path); - int screens; - conn = xcb_connect(NULL, &screens); - if (!conn || xcb_connection_has_error(conn)) - die("Cannot open display\n"); + /* Request the current InputFocus to restore when i3-input exits. */ + focus_cookie = xcb_get_input_focus(conn); - root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screen); root = root_screen->root; symbols = xcb_key_symbols_alloc(conn); @@ -369,23 +446,24 @@ int main(int argc, char *argv[]) { if (prompt != NULL) prompt_offset = predict_text_width(prompt); + const xcb_rectangle_t win_pos = get_window_position(); + /* Open an input window */ win = xcb_generate_id(conn); xcb_create_window( conn, XCB_COPY_FROM_PARENT, - win, /* the window id */ - root, /* parent == root */ - 50, 50, 500, font.height + 8, /* dimensions */ - 0, /* X11 border = 0, we draw our own */ + win, /* the window id */ + root, /* parent == root */ + win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */ + 0, /* X11 border = 0, we draw our own */ XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */ XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, (uint32_t[]){ 0, /* back pixel: black */ 1, /* override redirect: don’t manage this window */ - XCB_EVENT_MASK_EXPOSURE - }); + XCB_EVENT_MASK_EXPOSURE}); /* Map the window (make it visible) */ xcb_map_window(conn, win); @@ -393,7 +471,7 @@ int main(int argc, char *argv[]) { /* Create pixmap */ pixmap = xcb_generate_id(conn); pixmap_gc = xcb_generate_id(conn); - xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8); + xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), font.height + logical_px(8)); xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0); /* Set input focus (we have override_redirect=1, so the wm will not do @@ -418,6 +496,7 @@ int main(int argc, char *argv[]) { if (reply->status != XCB_GRAB_STATUS_SUCCESS) { fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status); + restore_input_focus(); exit(-1); } @@ -435,15 +514,15 @@ int main(int argc, char *argv[]) { switch (type) { case XCB_KEY_PRESS: - handle_key_press(NULL, conn, (xcb_key_press_event_t*)event); + handle_key_press(NULL, conn, (xcb_key_press_event_t *)event); break; case XCB_KEY_RELEASE: - handle_key_release(NULL, conn, (xcb_key_release_event_t*)event); + handle_key_release(NULL, conn, (xcb_key_release_event_t *)event); break; case XCB_EXPOSE: - handle_expose(NULL, conn, (xcb_expose_event_t*)event); + handle_expose(NULL, conn, (xcb_expose_event_t *)event); break; }