i3 - improved tiling WM


new command line argument -g for i3-input options: -g <default,focused,WxH+X+Y> default: legacy behavior focused: i3-input widget is placed at top of the window with input focus WxH+X+Y: specify fixed position in X11 geometry style

Patch status: needinfo

Patch by Bastian

To apply this patch, use:
curl http://cr.i3wm.org/patch/231/raw.patch | git am

b/i3-input/i3-input.h

17
@@ -12,6 +12,8 @@
18
 } \
19
 while (0)
20
 
21
+xcb_rectangle_t geometry;
22
+
23
 extern xcb_window_t root;
24
 
25
 #endif

b/i3-input/main.c

30
@@ -55,6 +55,7 @@ xcb_window_t root;
31
 xcb_connection_t *conn;
32
 xcb_screen_t *root_screen;
33
 static xcb_get_input_focus_cookie_t focus_cookie;
34
+static xcb_get_input_focus_reply_t *focus_reply;
35
 
36
 /*
37
  * Having verboselog() and errorlog() is necessary when using libi3.
38
@@ -84,13 +85,7 @@ void errorlog(char *fmt, ...) {
39
  *
40
  */
41
 static void restore_input_focus(void) {
42
-    xcb_generic_error_t *error;
43
-    xcb_get_input_focus_reply_t *reply = xcb_get_input_focus_reply(conn, focus_cookie, &error);
44
-    if (error != NULL) {
45
-        fprintf(stderr, "[i3-input] ERROR: Could not restore input focus (X error %d)\n", error->error_code);
46
-        return;
47
-    }
48
-    xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, reply->focus, XCB_CURRENT_TIME);
49
+    xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, focus_reply->focus, XCB_CURRENT_TIME);
50
     xcb_flush(conn);
51
 }
52
 
53
@@ -122,11 +117,12 @@ static uint8_t *concat_strings(char **glyphs, int max) {
54
  * be called from the code with event == NULL or from X with event != NULL.
55
  *
56
  */
57
-static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
58
+static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t *event, xcb_rectangle_t* geometry) {
59
     printf("expose!\n");
60
 
61
     /* re-draw the background */
62
-    xcb_rectangle_t border = {0, 0, 500, font.height + 8}, inner = {2, 2, 496, font.height + 8 - 4};
63
+    xcb_rectangle_t border = {0, 0, geometry->width, geometry->height},
64
+                    inner = {2, 2, geometry->width-4, geometry->height - 4};
65
     xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FF0000") });
66
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
67
     xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#000000") });
68
@@ -137,18 +133,18 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
69
 
70
     /* draw the prompt … */
71
     if (prompt != NULL) {
72
-        draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
73
+        draw_text(prompt, pixmap, pixmap_gc, 4, 4, geometry->width-8);
74
     }
75
     /* … and the text */
76
     if (input_position > 0)
77
     {
78
         i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
79
-        draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492);
80
+        draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, geometry->width-8);
81
         i3string_free(input);
82
     }
83
 
84
     /* Copy the contents of the pixmap to the real window */
85
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
86
+    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ geometry->width, geometry->height);
87
     xcb_flush(conn);
88
 
89
     return 1;
90
@@ -227,7 +223,7 @@ static void finish_input() {
91
  * command to i3).
92
  *
93
  */
94
-static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
95
+static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event, xcb_rectangle_t* geometry) {
96
     printf("Keypress %d, state raw = %d\n", event->detail, event->state);
97
 
98
     /* See the documentation of xcb_key_symbols_get_keysym for this one.
99
@@ -257,7 +253,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
100
         input_position--;
101
         free(glyphs_utf8[input_position]);
102
 
103
-        handle_expose(NULL, conn, NULL);
104
+        handle_expose(NULL, conn, NULL, geometry);
105
         return 1;
106
     }
107
     if (sym == XK_Escape) {
108
@@ -302,15 +298,88 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
109
     if (input_position == limit)
110
         finish_input();
111
 
112
-    handle_expose(NULL, conn, NULL);
113
+    handle_expose(NULL, conn, NULL, geometry);
114
     return 1;
115
 }
116
 
117
+
118
+xcb_rectangle_t* compute_geometry(char* geometry_arg) {
119
+    xcb_get_geometry_cookie_t focused_geometry_cookie;
120
+    xcb_get_geometry_reply_t *focused_geometry_reply;
121
+    xcb_translate_coordinates_reply_t *trans_reply;
122
+    xcb_generic_error_t *error;
123
+    xcb_rectangle_t* geometry = malloc( sizeof (xcb_rectangle_t) );
124
+    unsigned int scan_w, scan_h;
125
+    int scan_x, scan_y;
126
+
127
+    /* set default geometry */
128
+    geometry->x = 50; geometry->y = 50;
129
+    geometry->width = 500; geometry->height = 0;  /* if h==0: h=font.size + 8 */
130
+
131
+    if( strcasecmp(geometry_arg, "default") == 0 ) {
132
+        fprintf(stdout, "applying default geometry\n");
133
+    } else if( sscanf(geometry_arg, "%ux%u%d%d",
134
+                &scan_w, &scan_h, &scan_x, &scan_y) == 4 ) {
135
+        geometry->width = (uint16_t) scan_w;
136
+        geometry->height = (uint16_t) scan_h;
137
+        geometry->x = (int16_t) scan_x;
138
+        geometry->y = (int16_t) scan_y;
139
+        fprintf(stdout, "fixed geometry parsed from argument is %dx%d+%d+%d\n",
140
+                geometry->width, geometry->height,
141
+                geometry->x, geometry->y );
142
+    } else if( strcasecmp(geometry_arg, "focused") == 0 ) {
143
+        fprintf(stdout, "geometry will be applyed to currently focused window, if any\n");
144
+        focused_geometry_cookie = xcb_get_geometry(conn, focus_reply->focus);
145
+        focused_geometry_reply = xcb_get_geometry_reply(conn, focused_geometry_cookie, &error);
146
+        if (error != NULL) {
147
+            fprintf(stderr, "[i3-input] ERROR: Could not get geometry of focused window (X error %d)\n", error->error_code);
148
+            return geometry;
149
+        }
150
+        printf("Geometry of focused window is: %ux%u%+d%+d\n",
151
+                focused_geometry_reply->width, focused_geometry_reply->height,
152
+                focused_geometry_reply->x, focused_geometry_reply->y);
153
+        /* When firefox/vimperator has the input focus, function
154
+         * xcb_get_input_focus returns the id of a child window of firefox, which
155
+         * has the dimension 1x1. I am too unexperienced with xcb to debug this.
156
+         * So here is just a silly workaround to set the default size
157
+         * This is a workaround, please sb improve !
158
+         * */
159
+        if( focused_geometry_reply->width <= 1 ) {
160
+            focused_geometry_reply->width = 500;
161
+        }
162
+
163
+        trans_reply = xcb_translate_coordinates_reply(
164
+                conn,
165
+                xcb_translate_coordinates(conn, focus_reply->focus, root,
166
+                    focused_geometry_reply->x, focused_geometry_reply->y),
167
+                &error);
168
+        if (error != NULL) {
169
+            fprintf(stderr, "[i3-input] ERROR: Could not translate coordiantes for focused window (X error %d)\n",
170
+                    error->error_code);
171
+            return geometry;
172
+        }
173
+        fprintf(stdout, "Translated coordinates are: %+d%+d\n", trans_reply->dst_x, trans_reply->dst_y);
174
+        geometry->width=focused_geometry_reply->width - 10;
175
+        geometry->height=0;
176
+        geometry->x=trans_reply->dst_x + 5;
177
+        geometry->y=trans_reply->dst_y + 5;
178
+    } else {
179
+        fprintf(stderr, "[i3-input] ERROR: could not understand geometry argument %s.\n", geometry_arg);
180
+        return geometry;
181
+    }
182
+
183
+
184
+    return geometry;
185
+}
186
+
187
 int main(int argc, char *argv[]) {
188
-    format = strdup("%s");
189
+    format = sstrdup("%s");
190
     socket_path = getenv("I3SOCK");
191
     char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
192
     int o, option_index = 0;
193
+    xcb_generic_error_t *error;
194
+    char* geometry_arg = sstrdup("default");
195
+    xcb_rectangle_t* geometry = NULL;
196
 
197
     static struct option long_options[] = {
198
         {"socket", required_argument, 0, 's'},
199
@@ -321,16 +390,17 @@ int main(int argc, char *argv[]) {
200
         {"format", required_argument, 0, 'F'},
201
         {"font", required_argument, 0, 'f'},
202
         {"help", no_argument, 0, 'h'},
203
+        {"geometry", required_argument, 0, 'g'},
204
         {0, 0, 0, 0}
205
     };
206
 
207
-    char *options_string = "s:p:P:f:l:F:vh";
208
+    char *options_string = "s:p:P:f:l:F:vhg:";
209
 
210
     while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
211
         switch (o) {
212
             case 's':
213
                 FREE(socket_path);
214
-                socket_path = strdup(optarg);
215
+                socket_path = sstrdup(optarg);
216
                 break;
217
             case 'v':
218
                 printf("i3-input " I3_VERSION);
219
@@ -350,15 +420,19 @@ int main(int argc, char *argv[]) {
220
                 break;
221
             case 'f':
222
                 FREE(pattern);
223
-                pattern = strdup(optarg);
224
+                pattern = sstrdup(optarg);
225
                 break;
226
             case 'F':
227
                 FREE(format);
228
-                format = strdup(optarg);
229
+                format = sstrdup(optarg);
230
+                break;
231
+            case 'g':
232
+                FREE(geometry_arg);
233
+                geometry_arg = sstrdup(optarg);
234
                 break;
235
             case 'h':
236
                 printf("i3-input " I3_VERSION "\n");
237
-                printf("i3-input [-s <socket>] [-F <format>] [-l <limit>] [-P <prompt>] [-f <font>] [-v]\n");
238
+                printf("i3-input [-s <socket>] [-F <format>] [-l <limit>] [-P <prompt>] [-f <font>] [-v] [-g <default|focused|WxH+X+Y>]\n");
239
                 printf("\n");
240
                 printf("Example:\n");
241
                 printf("    i3-input -F 'workspace \"%%s\"' -P 'Switch to workspace: '\n");
242
@@ -381,17 +455,30 @@ int main(int argc, char *argv[]) {
243
     if (!conn || xcb_connection_has_error(conn))
244
         die("Cannot open display\n");
245
 
246
-    /* Request the current InputFocus to restore when i3-input exits. */
247
-    focus_cookie = xcb_get_input_focus(conn);
248
-
249
     root_screen = xcb_aux_get_screen(conn, screens);
250
     root = root_screen->root;
251
 
252
+    /* fetch the currently focused window */
253
+    focus_cookie = xcb_get_input_focus(conn);
254
+    focus_reply = xcb_get_input_focus_reply(conn, focus_cookie, &error);
255
+    if (error != NULL) {
256
+        fprintf(stderr, "[i3-input] ERROR: Could get currently focused window (X error %d)\n", error->error_code);
257
+    }
258
+    printf("Focused window is %x\n", focus_reply->focus);
259
+
260
     symbols = xcb_key_symbols_alloc(conn);
261
 
262
     font = load_font(pattern, true);
263
     set_font(&font);
264
 
265
+    geometry =  compute_geometry(geometry_arg);
266
+    if ( geometry->height == 0 ) {
267
+        geometry->height = font.height + 8;
268
+    }
269
+    fprintf(stdout, "[i3-input] INFO: using geometry %dx%d+%d+%d\n",
270
+            geometry->width, geometry->height,
271
+            geometry->x, geometry->y);
272
+
273
     if (prompt != NULL)
274
         prompt_offset = predict_text_width(prompt);
275
 
276
@@ -402,7 +489,7 @@ int main(int argc, char *argv[]) {
277
         XCB_COPY_FROM_PARENT,
278
         win, /* the window id */
279
         root, /* parent == root */
280
-        50, 50, 500, font.height + 8, /* dimensions */
281
+        geometry->x, geometry->y, geometry->width, geometry->height, /* dimensions */
282
         0, /* X11 border = 0, we draw our own */
283
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
284
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
285
@@ -419,7 +506,8 @@ int main(int argc, char *argv[]) {
286
     /* Create pixmap */
287
     pixmap = xcb_generate_id(conn);
288
     pixmap_gc = xcb_generate_id(conn);
289
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
290
+    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win,
291
+            geometry->width, geometry->height);
292
     xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
293
 
294
     /* Set input focus (we have override_redirect=1, so the wm will not do
295
@@ -462,7 +550,7 @@ int main(int argc, char *argv[]) {
296
 
297
         switch (type) {
298
             case XCB_KEY_PRESS:
299
-                handle_key_press(NULL, conn, (xcb_key_press_event_t*)event);
300
+                handle_key_press(NULL, conn, (xcb_key_press_event_t*)event, geometry);
301
                 break;
302
 
303
             case XCB_KEY_RELEASE:
304
@@ -470,7 +558,7 @@ int main(int argc, char *argv[]) {
305
                 break;
306
 
307
             case XCB_EXPOSE:
308
-                handle_expose(NULL, conn, (xcb_expose_event_t*)event);
309
+                handle_expose(NULL, conn, (xcb_expose_event_t*)event, geometry);
310
                 break;
311
         }
312