Implement keybindings, adjust CMDMODE grammar, update DEPENDS
This commit is contained in:
parent
069a740a94
commit
3e7b12ba43
8
CMDMODE
8
CMDMODE
|
@ -13,7 +13,11 @@ cmd := [ <times> ] [ <move> | <snap> ] <where>
|
|||
|
||||
oder
|
||||
|
||||
with := { [ <times> ] <where> }+ <enter> <cmd>
|
||||
with := <w> { [ <times> ] <where> }+ <space> <cmd>
|
||||
|
||||
oder
|
||||
|
||||
exec := exec <path>
|
||||
|
||||
an jeder Stelle kann mit escape abgebrochen werden
|
||||
|
||||
|
@ -29,4 +33,4 @@ Fenster nach rechts verschieben:
|
|||
ml
|
||||
|
||||
Fenster und Fenster untendrunter nach rechts verschieben:
|
||||
wk<enter>ml
|
||||
wk ml
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
You need the following libraries. The version given is to be understand as the minimum
|
||||
version. However, if any of these libraries changes the API, i3 may not compile anymore.
|
||||
In that case, please try using the versions mentioned below until a fix is provided.
|
||||
|
||||
* xcb-proto-1.3 (2008-12-10)
|
||||
* libxcb-1.1.93 (2008-12-11)
|
||||
* xcb-util-0.3.3 (2009-01-31)
|
||||
* Xlib, the one that comes with your X-Server
|
||||
|
||||
Get the libraries from:
|
||||
http://xcb.freedesktop.org/dist/xcb-proto-1.3.tar.bz2
|
||||
http://xcb.freedesktop.org/dist/libxcb-1.1.93.tar.bz2
|
||||
http://xcb.freedesktop.org/dist/xcb-util-0.3.3.tar.bz2
|
|
@ -1,3 +0,0 @@
|
|||
We’ve developed with:
|
||||
* libxcb-1.1.93 (2008-12-11)
|
||||
* xcb-util-0.3.3 (2009-01-31)
|
25
data.h
25
data.h
|
@ -13,9 +13,23 @@ typedef struct Cell Cell;
|
|||
typedef struct Font i3Font;
|
||||
typedef struct Container Container;
|
||||
typedef struct Client Client;
|
||||
typedef struct Binding Binding;
|
||||
|
||||
/* Helper types */
|
||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
||||
|
||||
enum {
|
||||
BIND_NONE = 0,
|
||||
BIND_MOD_1 = XCB_MOD_MASK_1,
|
||||
BIND_MOD_2 = XCB_MOD_MASK_2,
|
||||
BIND_MOD_3 = XCB_MOD_MASK_3,
|
||||
BIND_MOD_4 = XCB_MOD_MASK_4,
|
||||
BIND_MOD_5 = XCB_MOD_MASK_5,
|
||||
BIND_SHIFT = XCB_MOD_MASK_SHIFT,
|
||||
BIND_CONTROL = XCB_MOD_MASK_CONTROL,
|
||||
BIND_MODE_SWITCH = (1 << 8)
|
||||
};
|
||||
|
||||
struct table_dimensions_t {
|
||||
int x;
|
||||
int y;
|
||||
|
@ -30,6 +44,17 @@ struct Cell {
|
|||
int column;
|
||||
};
|
||||
|
||||
struct Binding {
|
||||
/* Keycode to bind */
|
||||
uint32_t keycode;
|
||||
/* Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */
|
||||
uint32_t mods;
|
||||
/* Command, like in command mode */
|
||||
char *command;
|
||||
|
||||
TAILQ_ENTRY(Binding) bindings;
|
||||
};
|
||||
|
||||
/*
|
||||
* We need to save the height of a font because it is required for each drawing of
|
||||
* text but relatively hard to get. As soon as a new font needs to be loaded, a
|
||||
|
|
227
mainx.c
227
mainx.c
|
@ -26,9 +26,10 @@
|
|||
|
||||
#define TERMINAL "/usr/pkg/bin/urxvt"
|
||||
|
||||
i3Font *myfont;
|
||||
Display *xkbdpy;
|
||||
|
||||
TAILQ_HEAD(bindings_head, Binding) bindings;
|
||||
|
||||
static const int TOP = 20;
|
||||
static const int LEFT = 5;
|
||||
static const int BOTTOM = 5;
|
||||
|
@ -797,84 +798,142 @@ static void start_application(char *path, char *args) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to bindings like Mode_switch + <a>, we need to bind some keys in XCB_GRAB_MODE_SYNC.
|
||||
* Therefore, we just replay all key presses.
|
||||
*
|
||||
*/
|
||||
static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) {
|
||||
printf("got key release, just passing\n");
|
||||
xcb_allow_events(conn, ReplayKeyboard, event->time);
|
||||
xcb_flush(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a command, see file CMDMODE for more information
|
||||
*
|
||||
*/
|
||||
static void parse_command(xcb_connection_t *conn, const char *command) {
|
||||
printf("--- parsing command \"%s\" ---\n", command);
|
||||
/* Hmm, just to be sure */
|
||||
if (command[0] == '\0')
|
||||
return;
|
||||
|
||||
/* Is it an <exec>? */
|
||||
if (strncmp(command, "exec ", strlen("exec ")) == 0) {
|
||||
printf("starting \"%s\"\n", command + strlen("exec "));
|
||||
start_application(command+strlen("exec "), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is it a <with>? */
|
||||
if (command[0] == 'w') {
|
||||
/* TODO: implement */
|
||||
printf("not yet implemented.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* It's a normal <cmd> */
|
||||
int times;
|
||||
char *rest = NULL;
|
||||
enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
|
||||
direction_t direction;
|
||||
times = strtol(command, &rest, 10);
|
||||
if (rest == NULL) {
|
||||
printf("Invalid command: Consists only of a movement\n");
|
||||
return;
|
||||
}
|
||||
if (*rest == 'm' || *rest == 's') {
|
||||
action = (*rest == 'm' ? ACTION_MOVE : ACTION_SNAP);
|
||||
rest++;
|
||||
}
|
||||
|
||||
/* Now perform action to <where> */
|
||||
while (*rest != '\0') {
|
||||
/* TODO: tags */
|
||||
if (*rest == 'h')
|
||||
direction = D_LEFT;
|
||||
else if (*rest == 'j')
|
||||
direction = D_DOWN;
|
||||
else if (*rest == 'k')
|
||||
direction = D_UP;
|
||||
else if (*rest == 'l')
|
||||
direction = D_RIGHT;
|
||||
else {
|
||||
printf("unknown direction: %c\n", *rest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == ACTION_FOCUS)
|
||||
focus_window(conn, direction);
|
||||
else if (action == ACTION_MOVE)
|
||||
move_current_window(conn, direction);
|
||||
else if (action == ACTION_SNAP)
|
||||
/* TODO: implement */
|
||||
printf("snap not yet implemented\n");
|
||||
|
||||
rest++;
|
||||
|
||||
}
|
||||
|
||||
printf("--- done ---\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* There was a key press. We lookup the key symbol and see if there are any bindings
|
||||
* on that. This allows to do things like binding special characters (think of ä) to
|
||||
* functions to get one more modifier while not losing AltGr :-)
|
||||
* TODO: this description needs to be more understandable
|
||||
*
|
||||
*/
|
||||
static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
|
||||
/* FIXME: We need to translate the keypress + state into a string (like, ä)
|
||||
because they do not generate keysyms (use xev and see for yourself) */
|
||||
|
||||
printf("oh yay!\n");
|
||||
printf("gots press %d\n", event->detail);
|
||||
printf("Keypress %d\n", event->detail);
|
||||
|
||||
/* We need to get the keysym group (There are group 1 to group 4, each holding
|
||||
two keysyms (without shift and with shift) using Xkb because X fails to
|
||||
provide them reliably (it works in Xephyr, it does not in real X) */
|
||||
XkbStateRec state;
|
||||
if (XkbGetState(xkbdpy, XkbUseCoreKbd, &state) == Success) {
|
||||
if (state.group+1 == 2)
|
||||
event->state |= 0x2;
|
||||
if (XkbGetState(xkbdpy, XkbUseCoreKbd, &state) == Success && (state.group+1) == 2)
|
||||
event->state |= 0x2;
|
||||
|
||||
printf("state %d\n", event->state);
|
||||
|
||||
/* Find the binding */
|
||||
Binding *bind, *best_match = TAILQ_END(&bindings);
|
||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
||||
if (bind->keycode == event->detail &&
|
||||
(bind->mods & event->state) == bind->mods) {
|
||||
if (best_match == TAILQ_END(&bindings) ||
|
||||
bind->mods > best_match->mods)
|
||||
best_match = bind;
|
||||
}
|
||||
}
|
||||
printf("i'm in state %d\n", event->state);
|
||||
|
||||
if (best_match == TAILQ_END(&bindings)) {
|
||||
printf("This key was not bound by us?! (most likely a bug)\n");
|
||||
return 1; /* TODO: return 0? what do the codes mean? */
|
||||
}
|
||||
|
||||
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(conn);
|
||||
|
||||
xcb_keysym_t k0 = xcb_key_symbols_get_keysym(keysyms, event->detail, event->state);
|
||||
if (k0 == XCB_NONE)
|
||||
printf("couldn't get k0\n");
|
||||
|
||||
printf("gots keysym %d and \n", k0);
|
||||
|
||||
|
||||
/* 30 = u
|
||||
* 57 = n
|
||||
* 27 = r
|
||||
* 28 = t
|
||||
* 40 = d
|
||||
*
|
||||
* …uhm, I should probably say that I’ve remapped my keys in hardware :)
|
||||
*/
|
||||
direction_t direction;
|
||||
if (event->detail == 30) {
|
||||
/* 'u' */
|
||||
start_application(TERMINAL, NULL);
|
||||
|
||||
return 1;
|
||||
} else if (event->detail == 57) {
|
||||
direction = D_LEFT;
|
||||
} else if (event->detail == 27) {
|
||||
direction = D_DOWN;
|
||||
} else if (event->detail == 28) {
|
||||
direction = D_UP;
|
||||
} else if (event->detail == 40) {
|
||||
direction = D_RIGHT;
|
||||
} else if (event->detail == 25) {
|
||||
Container *con = CUR_CELL;
|
||||
if (con->colspan == 1)
|
||||
con->colspan++;
|
||||
else con->colspan--;
|
||||
render_layout(conn);
|
||||
if (event->state & 0x2) {
|
||||
printf("that's mode_switch\n");
|
||||
parse_command(conn, best_match->command);
|
||||
printf("ok, hiding this event.\n");
|
||||
xcb_allow_events(conn, SyncKeyboard, event->time);
|
||||
xcb_flush(conn);
|
||||
return 1;
|
||||
} else {
|
||||
printf("don't want this.\n");
|
||||
}
|
||||
|
||||
/* If this was an actively grabbed key, and we did not handle it, we need to pass it */
|
||||
if (best_match->mods & BIND_MODE_SWITCH) {
|
||||
printf("passing...\n");
|
||||
xcb_allow_events(conn, ReplayKeyboard, event->time);
|
||||
xcb_flush(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TODO: ctrl -> focus_container(conn, direction) */
|
||||
/* FIXME: actually wrong but i'm too lazy to grab my keys all the time */
|
||||
if (event->state & XCB_MOD_MASK_CONTROL) {
|
||||
move_current_window(conn, direction);
|
||||
} else if (event->state & XCB_MOD_MASK_1)
|
||||
focus_window(conn, direction);
|
||||
/* TODO: shift -> move_current_window(conn, direction) */
|
||||
/* TODO: shift + ctrl -> move_current_container(conn, direction) */
|
||||
|
||||
return 1;
|
||||
parse_command(conn, best_match->command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1036,10 +1095,13 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
byChild = alloc_table();
|
||||
byParent = alloc_table();
|
||||
|
||||
TAILQ_INIT(&bindings);
|
||||
|
||||
c = xcb_connect(NULL, &screens);
|
||||
|
||||
printf("x screen is %d\n", screens);
|
||||
|
||||
/* TODO: this has to be more beautiful somewhen */
|
||||
int major, minor, error;
|
||||
|
||||
major = XkbMajorVersion;
|
||||
|
@ -1057,9 +1119,7 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
fprintf(stderr, "XKB not supported by X-server\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Font loading */
|
||||
myfont = load_font(c, pattern);
|
||||
/* end of ugliness */
|
||||
|
||||
xcb_event_handlers_init(c, &evenths);
|
||||
for(i = 2; i < 128; ++i)
|
||||
|
@ -1072,8 +1132,9 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
* contents (= top/bottom bar, titlebars for each window) */
|
||||
xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0);
|
||||
|
||||
/* Key presses are pretty obvious, I think */
|
||||
/* Key presses/releases are pretty obvious, I think */
|
||||
xcb_event_set_key_press_handler(&evenths, handle_key_press, 0);
|
||||
xcb_event_set_key_release_handler(&evenths, handle_key_release, 0);
|
||||
|
||||
/* Enter window = user moved his mouse over the window */
|
||||
xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0);
|
||||
|
@ -1090,27 +1151,37 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
|
||||
xcb_change_window_attributes(c, root, mask, values);
|
||||
|
||||
/* Grab 'a' */
|
||||
//xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
#define BIND(key, modifier, cmd) { \
|
||||
Binding *new = malloc(sizeof(Binding)); \
|
||||
new->keycode = key; \
|
||||
new->mods = modifier; \
|
||||
new->command = cmd; \
|
||||
TAILQ_INSERT_TAIL(&bindings, new, bindings); \
|
||||
}
|
||||
|
||||
xcb_grab_key(c, 0, root, 0, 30, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
/* 38 = 'a' */
|
||||
BIND(38, BIND_MODE_SWITCH, "foo");
|
||||
|
||||
BIND(30, 0, "exec /usr/pkg/bin/urxvt");
|
||||
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
BIND(44, BIND_MOD_1, "h");
|
||||
BIND(45, BIND_MOD_1, "j");
|
||||
BIND(46, BIND_MOD_1, "k");
|
||||
BIND(47, BIND_MOD_1, "l");
|
||||
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 25, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
BIND(44, BIND_MOD_1 | BIND_CONTROL, "mh");
|
||||
BIND(45, BIND_MOD_1 | BIND_CONTROL, "mj");
|
||||
BIND(46, BIND_MOD_1 | BIND_CONTROL, "mk");
|
||||
BIND(47, BIND_MOD_1 | BIND_CONTROL, "ml");
|
||||
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
||||
printf("Grabbing %d\n", bind->keycode);
|
||||
if (bind->mods & BIND_MODE_SWITCH)
|
||||
xcb_grab_key(c, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
|
||||
else xcb_grab_key(c, 0, root, bind->mods, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
}
|
||||
|
||||
|
||||
//xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
start_application(TERMINAL, NULL);
|
||||
|
||||
xcb_flush(c);
|
||||
|
|
Loading…
Reference in New Issue