]> git.sur5r.net Git - i3/i3/commitdiff
Implement vim-like marks
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 20 Sep 2009 14:54:29 +0000 (16:54 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 20 Sep 2009 14:54:29 +0000 (16:54 +0200)
Commands are 'mark' and 'goto'. Both can be used either directly,
like 'mark a' and 'goto a', or interactively (just 'mark'). For
interactive mode, i3-input must be installed and in your PATH.

i3-input/i3-input.h
i3-input/main.c
i3-input/ucs2_to_utf8.c
include/client.h
include/data.h
src/client.c
src/commands.c

index c884ba02964204d045abba2e34af58fd246d8c77..6c982bc529508e76e54eec8222c7986ed80ed31b 100644 (file)
@@ -6,6 +6,7 @@
 #define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
 
 char *convert_ucs_to_utf8(char *input);
+char *convert_utf8_to_ucs2(char *input, int *real_strlen);
 uint32_t get_colorpixel(xcb_connection_t *conn, char *hex);
 uint32_t get_mode_switch_mask(xcb_connection_t *conn);
 int connect_ipc(char *socket_path);
index 588c36b795443273e3a05dee2206d5fe9356d8b8..9b856d8a60f1f7219f3fe4bb3353505b60c74092 100644 (file)
@@ -46,6 +46,9 @@ static char *glyphs_utf8[512];
 static int input_position;
 static int font_height;
 static char *command_prefix;
+static char *prompt;
+static int prompt_len;
+static int limit;
 
 /*
  * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
@@ -88,13 +91,23 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
         /* restore font color */
         xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#FFFFFF"));
         uint8_t *con = concat_strings(glyphs_ucs, input_position);
-        xcb_image_text_16(conn, input_position, pixmap, pixmap_gc, 4 /* X */,
-                          font_height + 2 /* Y = baseline of font */, (xcb_char2b_t*)con);
+        char *full_text = (char*)con;
+        if (prompt != NULL) {
+                full_text = malloc((prompt_len + input_position) * 2 + 1);
+                if (full_text == NULL)
+                        err(EXIT_FAILURE, "malloc() failed\n");
+                memcpy(full_text, prompt, prompt_len * 2);
+                memcpy(full_text + (prompt_len * 2), con, input_position * 2);
+        }
+        xcb_image_text_16(conn, input_position + prompt_len, pixmap, pixmap_gc, 4 /* X */,
+                          font_height + 2 /* Y = baseline of font */, (xcb_char2b_t*)full_text);
 
         /* 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_flush(conn);
         free(con);
+        if (prompt != NULL)
+                free(full_text);
 
         return 1;
 }
@@ -115,6 +128,25 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
         return 1;
 }
 
+static void finish_input() {
+        uint8_t *command = concat_strings(glyphs_utf8, input_position);
+        char *full_command = (char*)command;
+        /* prefix the command if a prefix was specified on commandline */
+        if (command_prefix != NULL) {
+                if (asprintf(&full_command, "%s%s", command_prefix, command) == -1)
+                        err(EXIT_FAILURE, "asprintf() failed\n");
+        }
+        printf("command = %s\n", full_command);
+
+        ipc_send_message(sockfd, strlen(full_command), 0, (uint8_t*)full_command);
+
+#if 0
+        free(command);
+        return 1;
+#endif
+        exit(0);
+}
+
 /*
  * Handles keypresses by converting the keycodes to keysymbols, then the
  * keysymbols to UCS-2. If the conversion succeeded, the glyph is saved in the
@@ -138,24 +170,8 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
                 return 1;
         }
 
-        if (sym == XK_Return) {
-                uint8_t *command = concat_strings(glyphs_utf8, input_position);
-                char *full_command = (char*)command;
-                /* prefix the command if a prefix was specified on commandline */
-                if (command_prefix != NULL) {
-                        if (asprintf(&full_command, "%s%s", command_prefix, command) == -1)
-                                err(EXIT_FAILURE, "asprintf() failed\n");
-                }
-                printf("command = %s\n", full_command);
-
-                ipc_send_message(sockfd, strlen(full_command), 0, (uint8_t*)full_command);
-
-#if 0
-                free(command);
-                return 1;
-#endif
-                exit(0);
-        }
+        if (sym == XK_Return)
+                finish_input();
 
         if (sym == XK_BackSpace) {
                 if (input_position == 0)
@@ -208,6 +224,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
         glyphs_utf8[input_position] = strdup(out);
         input_position++;
 
+        if (input_position == limit)
+                finish_input();
+
         handle_expose(NULL, conn, NULL);
         return 1;
 }
@@ -220,30 +239,43 @@ int main(int argc, char *argv[]) {
         static struct option long_options[] = {
                 {"socket", required_argument, 0, 's'},
                 {"version", no_argument, 0, 'v'},
+                {"limit", required_argument, 0, 'l'},
+                {"prompt", required_argument, 0, 'P'},
                 {"prefix", required_argument, 0, 'p'},
                 {"help", no_argument, 0, 'h'},
                 {0, 0, 0, 0}
         };
 
-        char *options_string = "s:p:vh";
+        char *options_string = "s:p:P:l:vh";
 
         while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
-                if (o == 's') {
-                        socket_path = strdup(optarg);
-                } else if (o == 'v') {
-                        printf("i3-input " I3_VERSION);
-                        return 0;
-                } else if (o == 'p') {
-                        command_prefix = strdup(optarg);
-                } else if (o == 'h') {
-                        printf("i3-input " I3_VERSION);
-                        printf("i3-input [-s <socket>] [-p <prefix>]\n");
-                        return 0;
+                switch (o) {
+                        case 's':
+                                socket_path = strdup(optarg);
+                                break;
+                        case 'v':
+                                printf("i3-input " I3_VERSION);
+                                return 0;
+                        case 'p':
+                                command_prefix = strdup(optarg);
+                                break;
+                        case 'l':
+                                limit = atoi(optarg);
+                                break;
+                        case 'P':
+                                prompt = strdup(optarg);
+                                break;
+                        case 'h':
+                                printf("i3-input " I3_VERSION);
+                                printf("i3-input [-s <socket>] [-p <prefix>] [-l <limit>] [-P <prompt>] [-v]\n");
+                                return 0;
                 }
         }
 
         sockfd = connect_ipc(socket_path);
 
+        prompt = convert_utf8_to_ucs2(prompt, &prompt_len);
+
         int screens;
         xcb_connection_t *conn = xcb_connect(NULL, &screens);
         if (xcb_connection_has_error(conn))
index dcd0619702607c5cdbe16069f4fec6758a4a75e1..4557c9dab73199b270642a57ba724859ab08bf8b 100644 (file)
  */
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <err.h>
 #include <iconv.h>
 
 static iconv_t conversion_descriptor = 0;
+static iconv_t conversion_descriptor2 = 0;
 
 /*
  * Returns the input string, but converted from UCS-2 to UTF-8. Memory will be
@@ -53,3 +55,50 @@ char *convert_ucs_to_utf8(char *input) {
 
        return buffer;
 }
+
+/*
+ * Converts the given string to UCS-2 big endian for use with
+ * xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
+ * a buffer containing the UCS-2 encoded string (16 bit per glyph) is
+ * returned. It has to be freed when done.
+ *
+ */
+char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
+       size_t input_size = strlen(input) + 1;
+       /* UCS-2 consumes exactly two bytes for each glyph */
+       int buffer_size = input_size * 2;
+
+       char *buffer = malloc(buffer_size);
+        if (buffer == NULL)
+                err(EXIT_FAILURE, "malloc() failed\n");
+       size_t output_size = buffer_size;
+       /* We need to use an additional pointer, because iconv() modifies it */
+       char *output = buffer;
+
+       /* We convert the input into UCS-2 big endian */
+        if (conversion_descriptor2 == 0) {
+                conversion_descriptor2 = iconv_open("UCS-2BE", "UTF-8");
+                if (conversion_descriptor2 == 0) {
+                        fprintf(stderr, "error opening the conversion context\n");
+                        exit(1);
+                }
+        }
+
+       /* Get the conversion descriptor back to original state */
+       iconv(conversion_descriptor2, NULL, NULL, NULL, NULL);
+
+       /* Convert our text */
+       int rc = iconv(conversion_descriptor2, (void*)&input, &input_size, &output, &output_size);
+        if (rc == (size_t)-1) {
+                perror("Converting to UCS-2 failed");
+                if (real_strlen != NULL)
+                       *real_strlen = 0;
+                return NULL;
+       }
+
+        if (real_strlen != NULL)
+               *real_strlen = ((buffer_size - output_size) / 2) - 1;
+
+       return buffer;
+}
+
index e43f81b957ab20023724782f48e21cd58644264c..88335f6269624ac21ee9b8af40755253d2212f1f 100644 (file)
@@ -97,6 +97,13 @@ void client_unmap(xcb_connection_t *conn, Client *client);
  */
 void client_map(xcb_connection_t *conn, Client *client);
 
+/**
+ * Set the given mark for this client. Used for jumping to the client
+ * afterwards (like m<mark> and '<mark> in vim).
+ *
+ */
+void client_mark(xcb_connection_t *conn, Client *client, const char *mark);
+
 /**
  * Pretty-prints the client’s information into the logfile.
  *
index 6d1869c0bfe8f79400a927c5d47276bf497bf6e0..0f0a243daac7690194a5934cbb422555f9919745 100644 (file)
@@ -376,6 +376,9 @@ struct Client {
         /** Holds the WM_CLASS, useful for matching the client in commands */
         char *window_class;
 
+        /** Holds the client’s mark, for vim-like jumping */
+        char *mark;
+
         /** Holds the xcb_window_t (just an ID) for the leader window (logical
          * parent for toolwindows and similar floating windows) */
         xcb_window_t leader;
index ce11561414f232cdd89e4c4359b1de901d40342b..93f9315df2f24c5e2c0d9b91be62a49a8afea3e7 100644 (file)
@@ -24,6 +24,7 @@
 #include "queue.h"
 #include "layout.h"
 #include "client.h"
+#include "table.h"
 
 /*
  * Removes the given client from the container, either because it will be inserted into another
@@ -316,3 +317,30 @@ void client_map(xcb_connection_t *conn, Client *client) {
 
         xcb_map_window(conn, client->frame);
 }
+
+/*
+ * Set the given mark for this client. Used for jumping to the client
+ * afterwards (like m<mark> and '<mark> in vim).
+ *
+ */
+void client_mark(xcb_connection_t *conn, Client *client, const char *mark) {
+        if (client->mark != NULL)
+                free(client->mark);
+        client->mark = sstrdup(mark);
+
+        /* Make sure no other client has this mark set */
+        Client *current;
+        for (int c = 0; c < 10; c++)
+                SLIST_FOREACH(current, &(workspaces[c].focus_stack), focus_clients) {
+                        if (current == client ||
+                            current->mark == NULL ||
+                            strcmp(current->mark, mark) != 0)
+                                continue;
+
+                        free(current->mark);
+                        current->mark = NULL;
+                        /* We can break here since there can only be one other
+                         * client with this mark. */
+                        break;
+                }
+}
index 473af8dc80424daee055eb71199b735d86234bbb..5ca92b3185ee02f135296920804d26f25ce9c30b 100644 (file)
@@ -58,6 +58,22 @@ bool focus_window_in_container(xcb_connection_t *conn, Container *container, dir
 
 typedef enum { THING_WINDOW, THING_CONTAINER, THING_SCREEN } thing_t;
 
+static void jump_to_mark(xcb_connection_t *conn, const char *mark) {
+        Client *current;
+        LOG("Jumping to \"%s\"\n", mark);
+
+        for (int c = 0; c < 10; c++)
+                SLIST_FOREACH(current, &(workspaces[c].focus_stack), focus_clients) {
+                        if (current->mark == NULL || strcmp(current->mark, mark) != 0)
+                                continue;
+
+                        set_focus(conn, current, true);
+                        return;
+                }
+
+        LOG("No window with this mark found\n");
+}
+
 static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t thing) {
         LOG("focusing direction %d\n", direction);
 
@@ -817,6 +833,38 @@ void parse_command(xcb_connection_t *conn, const char *command) {
                 return;
         }
 
+        if (STARTS_WITH(command, "mark")) {
+                if (last_focused == NULL) {
+                        LOG("There is no window to mark\n");
+                        return;
+                }
+                const char *rest = command + strlen("mark");
+                while (*rest == ' ')
+                        rest++;
+                if (*rest == '\0') {
+                        LOG("interactive mark starting\n");
+                        start_application("i3-input -p 'mark ' -l 1 -P 'Mark: '");
+                } else {
+                        LOG("mark with \"%s\"\n", rest);
+                        client_mark(conn, last_focused, rest);
+                }
+                return;
+        }
+
+        if (STARTS_WITH(command, "goto")) {
+                const char *rest = command + strlen("goto");
+                while (*rest == ' ')
+                        rest++;
+                if (*rest == '\0') {
+                        LOG("interactive go to mark starting\n");
+                        start_application("i3-input -p 'goto ' -l 1 -P 'Goto: '");
+                } else {
+                        LOG("go to \"%s\"\n", rest);
+                        jump_to_mark(conn, rest);
+                }
+                return;
+        }
+
         /* Is it an <exit>? */
         if (STARTS_WITH(command, "exit")) {
                 LOG("User issued exit-command, exiting without error.\n");