*
* i3 - an improved dynamic tiling window manager
*
- * © 2009 Michael Stapelberg and contributors
+ * © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
#include <err.h>
#include <stdint.h>
#include <getopt.h>
+#include <glob.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
static int sockfd;
static xcb_key_symbols_t *symbols;
static int modeswitchmask;
+static int numlockmask;
static bool modeswitch_active = false;
static xcb_window_t win;
static xcb_pixmap_t pixmap;
static int input_position;
static int font_height;
static char *command_prefix;
+static char *prompt;
+static int prompt_len;
+static int limit;
+
+/*
+ * This function resolves ~ in pathnames (and more, see glob(3)).
+ *
+ */
+static char *glob_path(const char *path) {
+ static glob_t globbuf;
+ if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0)
+ errx(EXIT_FAILURE, "glob() failed");
+ char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
+ if (result == NULL)
+ err(EXIT_FAILURE, "malloc() failed");
+ globfree(&globbuf);
+ return result;
+}
/*
* Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
*
*/
static uint8_t *concat_strings(char **glyphs, int max) {
- uint8_t *output = calloc(max, 4);
+ uint8_t *output = calloc(max+1, 4);
uint8_t *walk = output;
for (int c = 0; c < max; c++) {
printf("at %c\n", glyphs[c][0]);
/* 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;
}
static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) {
printf("releasing %d, state raw = %d\n", event->detail, event->state);
+ /* fix state */
+ event->state &= ~numlockmask;
+
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
if (sym == XK_Mode_switch) {
printf("Mode switch disabled\n");
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
if (modeswitch_active)
event->state |= modeswitchmask;
+ /* Apparantly, after activating numlock once, the numlock modifier
+ * stays turned on (use xev(1) to verify). So, to resolve useful
+ * keysyms, we remove the numlock flag from the event state */
+ event->state &= ~numlockmask;
+
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
if (sym == XK_Mode_switch) {
printf("Mode switch enabled\n");
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)
glyphs_utf8[input_position] = strdup(out);
input_position++;
+ if (input_position == limit)
+ finish_input();
+
handle_expose(NULL, conn, NULL);
return 1;
}
int main(int argc, char *argv[]) {
- char *socket_path = "/tmp/i3-ipc.sock";
+ char *socket_path = glob_path("~/.i3/ipc.sock");
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
int o, option_index = 0;
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 = glob_path(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);
+ if (prompt != NULL)
+ prompt = convert_utf8_to_ucs2(prompt, &prompt_len);
+
int screens;
xcb_connection_t *conn = xcb_connect(NULL, &screens);
if (xcb_connection_has_error(conn))
die("Cannot open display\n");
/* Set up event handlers for key press and key release */
- xcb_event_handlers_t evenths;
- memset(&evenths, 0, sizeof(xcb_event_handlers_t));
- xcb_event_handlers_init(conn, &evenths);
- xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
- xcb_event_set_key_release_handler(&evenths, handle_key_release, NULL);
- xcb_event_set_expose_handler(&evenths, handle_expose, NULL);
-
- modeswitchmask = get_mode_switch_mask(conn);
+ modeswitchmask = get_mod_mask(conn, XK_Mode_switch);
+ numlockmask = get_mod_mask(conn, XK_Num_Lock);
symbols = xcb_key_symbols_alloc(conn);
uint32_t font_id = get_font_id(conn, pattern, &font_height);
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font_height + 8);
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+ /* Set input focus (we have override_redirect=1, so the wm will not do
+ * this for us) */
+ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
+
/* Create graphics context */
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FONT, font_id);
/* Grab the keyboard to get all input */
- xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ xcb_flush(conn);
+
+ /* Try (repeatedly, if necessary) to grab the keyboard. We might not
+ * get the keyboard at the first attempt because of the keybinding
+ * still being active when started via a wm’s keybinding. */
+ xcb_grab_keyboard_cookie_t cookie;
+ xcb_grab_keyboard_reply_t *reply = NULL;
+
+ int count = 0;
+ while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) {
+ cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ reply = xcb_grab_keyboard_reply(conn, cookie, NULL);
+ usleep(1000);
+ }
+
+ if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
+ fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
+ exit(-1);
+ }
xcb_flush(conn);
- xcb_event_wait_for_event_loop(&evenths);
+ xcb_generic_event_t *event;
+ while ((event = xcb_wait_for_event(conn)) != NULL) {
+ if (event->response_type == 0) {
+ fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
+ continue;
+ }
+
+ /* Strip off the highest bit (set if the event is generated) */
+ int type = (event->response_type & 0x7F);
+
+ switch (type) {
+ case XCB_KEY_PRESS:
+ 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);
+ break;
+
+ case XCB_EXPOSE:
+ handle_expose(NULL, conn, (xcb_expose_event_t*)event);
+ break;
+ }
+
+ free(event);
+ }
+
return 0;
}