* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- *
- * © 2009-2011 Michael Stapelberg and contributors
- *
- * See file LICENSE for license information.
+ * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-input/main.c: Utility which lets the user input commands and sends them
- * to i3.
+ * to i3.
*
*/
#include <ev.h>
#include "i3-input.h"
+#include "libi3.h"
+
/* IPC format string. %s will be replaced with what the user entered, then
* the command will be sent to i3 */
static char *format;
static char *socket_path;
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 char *glyphs_ucs[512];
static char *glyphs_utf8[512];
static int input_position;
-static int font_height;
+static i3Font font;
static char *prompt;
-static int prompt_len;
+static size_t prompt_len;
static int limit;
xcb_window_t root;
-
-/*
- * Try to get the socket path from X11 and return NULL if it doesn’t work.
- * As i3-msg is a short-running tool, we don’t bother with cleaning up the
- * connection and leave it up to the operating system on exit.
- *
- */
-static char *socket_path_from_x11() {
- xcb_connection_t *conn;
- int screen;
- if ((conn = xcb_connect(NULL, &screen)) == NULL ||
- xcb_connection_has_error(conn))
- return NULL;
- xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen);
- xcb_window_t root = root_screen->root;
-
- xcb_intern_atom_cookie_t atom_cookie;
- xcb_intern_atom_reply_t *atom_reply;
-
- atom_cookie = xcb_intern_atom(conn, 0, strlen("I3_SOCKET_PATH"), "I3_SOCKET_PATH");
- atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
- if (atom_reply == NULL)
- return NULL;
-
- xcb_get_property_cookie_t prop_cookie;
- xcb_get_property_reply_t *prop_reply;
- prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
- XCB_GET_PROPERTY_TYPE_ANY, 0, PATH_MAX);
- prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
- if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0)
- return NULL;
- if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply),
- (char*)xcb_get_property_value(prop_reply)) == -1)
- return NULL;
- return socket_path;
-}
+xcb_connection_t *conn;
/*
* Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
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_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#FF0000"));
+ 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_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
- xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#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 */
- xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#FFFFFF"));
+ set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
+
+ /* draw the text */
uint8_t *con = concat_strings(glyphs_ucs, input_position);
char *full_text = (char*)con;
if (prompt != NULL) {
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);
+ if (input_position + prompt_len != 0)
+ draw_text(full_text, input_position + prompt_len, true, pixmap, pixmap_gc, 4, 4, 492);
/* 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, /* */ 500, font.height + 8);
xcb_flush(conn);
free(con);
if (prompt != NULL)
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;
+ /* 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. */
+ int col = (event->state & XCB_MOD_MASK_SHIFT);
+
+ /* If modeswitch is currently active, we need to look in group 2 or 3,
+ * respectively. */
+ if (modeswitch_active)
+ col += 2;
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
if (sym == XK_Mode_switch) {
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);
- /* fix state */
- if (modeswitch_active)
- event->state |= modeswitchmask;
+ /* 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. */
+ int col = (event->state & XCB_MOD_MASK_SHIFT);
- /* 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;
+ /* If modeswitch is currently active, we need to look in group 2 or 3,
+ * respectively. */
+ if (modeswitch_active)
+ col += 2;
- xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
+ xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, col);
if (sym == XK_Mode_switch) {
printf("Mode switch enabled\n");
modeswitch_active = true;
printf("inp[0] = %02x, inp[1] = %02x, inp[2] = %02x\n", inp[0], inp[1], inp[2]);
/* convert it to UTF-8 */
- char *out = convert_ucs_to_utf8((char*)inp);
+ char *out = convert_ucs2_to_utf8((xcb_char2b_t*)inp, 1);
printf("converted to %s\n", out);
glyphs_ucs[input_position] = malloc(3 * sizeof(uint8_t));
if (glyphs_ucs[input_position] == NULL)
err(EXIT_FAILURE, "malloc() failed\n");
memcpy(glyphs_ucs[input_position], inp, 3);
- glyphs_utf8[input_position] = strdup(out);
+ glyphs_utf8[input_position] = out;
input_position++;
if (input_position == limit)
int main(int argc, char *argv[]) {
format = strdup("%s");
socket_path = getenv("I3SOCK");
- char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
+ char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
int o, option_index = 0;
static struct option long_options[] = {
/* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
fprintf(stderr, "i3-input: WARNING: the -p option is DEPRECATED in favor of the -F (format) option\n");
FREE(format);
- asprintf(&format, "%s%%s", optarg);
+ sasprintf(&format, "%s%%s", optarg);
break;
case 'l':
limit = atoi(optarg);
printf("using format \"%s\"\n", format);
if (socket_path == NULL)
- socket_path = socket_path_from_x11();
+ socket_path = root_atom_contents("I3_SOCKET_PATH");
if (socket_path == NULL)
socket_path = "/tmp/i3-ipc.sock";
- sockfd = connect_ipc(socket_path);
+ sockfd = ipc_connect(socket_path);
if (prompt != NULL)
- prompt = convert_utf8_to_ucs2(prompt, &prompt_len);
+ prompt = (char*)convert_utf8_to_ucs2(prompt, &prompt_len);
int screens;
- xcb_connection_t *conn = xcb_connect(NULL, &screens);
- if (xcb_connection_has_error(conn))
+ conn = xcb_connect(NULL, &screens);
+ if (!conn || xcb_connection_has_error(conn))
die("Cannot open display\n");
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
root = root_screen->root;
- 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);
+ font = load_font(pattern, true);
+ set_font(&font);
/* Open an input window */
- win = open_input_window(conn, 500, font_height + 8);
+ 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 */
+ 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
+ });
+
+ /* Map the window (make it visible) */
+ xcb_map_window(conn, win);
/* 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, 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_flush(conn);