Implement modes. Modes allow you to use different keybindings and switch between them.
For example, you can create a mode which will let you resize windows with some easy to use keys. So, instead of binding a combination of your homerow and modifiers to resize, like this: bind Mod4+44 resize right +10 bind Mod4+45 resize right -10 ... You can instead define a new mode: mode "resize" { bind 44 resize right +10 bind 45 resize right -10 ... bind 36 mode default } bindsym Mod4+r mode resize So, if you press Mod4+r now, your keybindings will be set to the ones defined in your resize mode above. You can then use your homerow (without any other modifier) to resize the current column/row and press enter to go back to the default mode when you are done. Note that using this option requires you to enable the new lexer/parser by passing the -l flag to i3 when starting.
This commit is contained in:
parent
97d949d16c
commit
8f67eba044
|
@ -21,6 +21,7 @@
|
||||||
typedef struct Config Config;
|
typedef struct Config Config;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
extern bool config_use_lexer;
|
extern bool config_use_lexer;
|
||||||
|
extern SLIST_HEAD(modes_head, Mode) modes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the struct Config. It makes sense to group colors for background,
|
* Part of the struct Config. It makes sense to group colors for background,
|
||||||
|
@ -46,6 +47,19 @@ struct Variable {
|
||||||
SLIST_ENTRY(Variable) variables;
|
SLIST_ENTRY(Variable) variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration file can contain multiple sets of bindings. Apart from the
|
||||||
|
* default set (name == "default"), you can specify other sets and change the
|
||||||
|
* currently active set of bindings by using the "mode <name>" command.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct Mode {
|
||||||
|
char *name;
|
||||||
|
struct bindings_head *bindings;
|
||||||
|
|
||||||
|
SLIST_ENTRY(Mode) modes;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds part of the configuration (the part which is not already in dedicated
|
* Holds part of the configuration (the part which is not already in dedicated
|
||||||
* structures in include/data.h).
|
* structures in include/data.h).
|
||||||
|
@ -97,4 +111,10 @@ void ungrab_all_keys(xcb_connection_t *conn);
|
||||||
*/
|
*/
|
||||||
void grab_all_keys(xcb_connection_t *conn);
|
void grab_all_keys(xcb_connection_t *conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches the key bindings to the given mode, if the mode exists
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void switch_mode(xcb_connection_t *conn, const char *new_mode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,7 +27,7 @@ extern xcb_connection_t *global_conn;
|
||||||
extern xcb_key_symbols_t *keysyms;
|
extern xcb_key_symbols_t *keysyms;
|
||||||
extern char **start_argv;
|
extern char **start_argv;
|
||||||
extern Display *xkbdpy;
|
extern Display *xkbdpy;
|
||||||
extern TAILQ_HEAD(bindings_head, Binding) bindings;
|
extern TAILQ_HEAD(bindings_head, Binding) *bindings;
|
||||||
extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
|
extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
|
||||||
extern TAILQ_HEAD(assignments_head, Assignment) assignments;
|
extern TAILQ_HEAD(assignments_head, Assignment) assignments;
|
||||||
extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
|
extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
|
||||||
|
|
|
@ -222,7 +222,7 @@ command:
|
||||||
bindline:
|
bindline:
|
||||||
binding
|
binding
|
||||||
{
|
{
|
||||||
TAILQ_INSERT_TAIL(&bindings, $<binding>1, bindings);
|
TAILQ_INSERT_TAIL(bindings, $<binding>1, bindings);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -262,8 +262,23 @@ bindsym:
|
||||||
mode:
|
mode:
|
||||||
TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
|
TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
|
||||||
{
|
{
|
||||||
|
if (strcasecmp($<string>3, "default") == 0) {
|
||||||
|
printf("You cannot use the name \"default\" for your mode\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
printf("\t now in mode %s\n", $<string>3);
|
printf("\t now in mode %s\n", $<string>3);
|
||||||
printf("\t current bindings = %p\n", current_bindings);
|
printf("\t current bindings = %p\n", current_bindings);
|
||||||
|
Binding *binding;
|
||||||
|
TAILQ_FOREACH(binding, current_bindings, bindings) {
|
||||||
|
printf("got binding on mods %d, keycode %d, symbol %s, command %s\n",
|
||||||
|
binding->mods, binding->keycode, binding->symbol, binding->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mode *mode = scalloc(sizeof(struct Mode));
|
||||||
|
mode->name = strdup($<string>3);
|
||||||
|
mode->bindings = current_bindings;
|
||||||
|
current_bindings = NULL;
|
||||||
|
SLIST_INSERT_HEAD(&modes, mode, modes);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -948,6 +948,12 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (STARTS_WITH(command, "mode ")) {
|
||||||
|
const char *rest = command + strlen("mode ");
|
||||||
|
switch_mode(conn, rest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Is it an <exit>? */
|
/* Is it an <exit>? */
|
||||||
if (STARTS_WITH(command, "exit")) {
|
if (STARTS_WITH(command, "exit")) {
|
||||||
LOG("User issued exit-command, exiting without error.\n");
|
LOG("User issued exit-command, exiting without error.\n");
|
||||||
|
|
58
src/config.c
58
src/config.c
|
@ -31,6 +31,7 @@
|
||||||
void parse_file(const char *f);
|
void parse_file(const char *f);
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
struct modes_head modes;
|
||||||
|
|
||||||
bool config_use_lexer = false;
|
bool config_use_lexer = false;
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
|
||||||
*/
|
*/
|
||||||
void grab_all_keys(xcb_connection_t *conn) {
|
void grab_all_keys(xcb_connection_t *conn) {
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
/* The easy case: the user specified a keycode directly. */
|
/* The easy case: the user specified a keycode directly. */
|
||||||
if (bind->keycode > 0) {
|
if (bind->keycode > 0) {
|
||||||
grab_keycode_for_binding(conn, bind, bind->keycode);
|
grab_keycode_for_binding(conn, bind, bind->keycode);
|
||||||
|
@ -137,6 +138,28 @@ void grab_all_keys(xcb_connection_t *conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switches the key bindings to the given mode, if the mode exists
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void switch_mode(xcb_connection_t *conn, const char *new_mode) {
|
||||||
|
struct Mode *mode;
|
||||||
|
|
||||||
|
LOG("Switching to mode %s\n", new_mode);
|
||||||
|
|
||||||
|
SLIST_FOREACH(mode, &modes, modes) {
|
||||||
|
if (strcasecmp(mode->name, new_mode) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ungrab_all_keys(conn);
|
||||||
|
bindings = mode->bindings;
|
||||||
|
grab_all_keys(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("ERROR: Mode not found\n");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
|
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
|
||||||
*
|
*
|
||||||
|
@ -149,13 +172,22 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
/* First ungrab the keys */
|
/* First ungrab the keys */
|
||||||
ungrab_all_keys(conn);
|
ungrab_all_keys(conn);
|
||||||
|
|
||||||
/* Clear the old binding and assignment lists */
|
struct Mode *mode;
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
while (!TAILQ_EMPTY(&bindings)) {
|
while (!SLIST_EMPTY(&modes)) {
|
||||||
bind = TAILQ_FIRST(&bindings);
|
mode = SLIST_FIRST(&modes);
|
||||||
TAILQ_REMOVE(&bindings, bind, bindings);
|
FREE(mode->name);
|
||||||
FREE(bind->command);
|
|
||||||
FREE(bind);
|
/* Clear the old binding list */
|
||||||
|
bindings = mode->bindings;
|
||||||
|
while (!TAILQ_EMPTY(bindings)) {
|
||||||
|
bind = TAILQ_FIRST(bindings);
|
||||||
|
TAILQ_REMOVE(bindings, bind, bindings);
|
||||||
|
FREE(bind->command);
|
||||||
|
FREE(bind);
|
||||||
|
}
|
||||||
|
FREE(bindings);
|
||||||
|
SLIST_REMOVE(&modes, mode, Mode, modes);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Assignment *assign;
|
struct Assignment *assign;
|
||||||
|
@ -167,6 +199,16 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SLIST_INIT(&modes);
|
||||||
|
|
||||||
|
struct Mode *default_mode = scalloc(sizeof(struct Mode));
|
||||||
|
default_mode->name = sstrdup("default");
|
||||||
|
default_mode->bindings = scalloc(sizeof(struct bindings_head));
|
||||||
|
TAILQ_INIT(default_mode->bindings);
|
||||||
|
SLIST_INSERT_HEAD(&modes, default_mode, modes);
|
||||||
|
|
||||||
|
bindings = default_mode->bindings;
|
||||||
|
|
||||||
SLIST_HEAD(variables_head, Variable) variables;
|
SLIST_HEAD(variables_head, Variable) variables;
|
||||||
|
|
||||||
#define OPTION_STRING(name) \
|
#define OPTION_STRING(name) \
|
||||||
|
@ -364,7 +406,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
LOG("keycode = %d, symbol = %s, modifiers = %d, command = *%s*\n", new->keycode, new->symbol, modifiers, rest);
|
LOG("keycode = %d, symbol = %s, modifiers = %d, command = *%s*\n", new->keycode, new->symbol, modifiers, rest);
|
||||||
new->mods = modifiers;
|
new->mods = modifiers;
|
||||||
new->command = sstrdup(rest);
|
new->command = sstrdup(rest);
|
||||||
TAILQ_INSERT_TAIL(&bindings, new, bindings);
|
TAILQ_INSERT_TAIL(bindings, new, bindings);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
|
||||||
|
|
||||||
/* Find the binding */
|
/* Find the binding */
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
/* First compare the modifiers */
|
/* First compare the modifiers */
|
||||||
if (bind->mods != state_filtered)
|
if (bind->mods != state_filtered)
|
||||||
continue;
|
continue;
|
||||||
|
@ -137,7 +137,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
|
||||||
|
|
||||||
/* No match? Then it was an actively grabbed key, that is with Mode_switch, and
|
/* No match? Then it was an actively grabbed key, that is with Mode_switch, and
|
||||||
the user did not press Mode_switch, so just pass it… */
|
the user did not press Mode_switch, so just pass it… */
|
||||||
if (bind == TAILQ_END(&bindings)) {
|
if (bind == TAILQ_END(bindings)) {
|
||||||
xcb_allow_events(conn, ReplayKeyboard, event->time);
|
xcb_allow_events(conn, ReplayKeyboard, event->time);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -61,7 +61,7 @@ Display *xkbdpy;
|
||||||
xcb_key_symbols_t *keysyms;
|
xcb_key_symbols_t *keysyms;
|
||||||
|
|
||||||
/* The list of key bindings */
|
/* The list of key bindings */
|
||||||
struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
|
struct bindings_head *bindings;
|
||||||
|
|
||||||
/* The list of exec-lines */
|
/* The list of exec-lines */
|
||||||
struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
|
struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
|
||||||
|
|
Loading…
Reference in New Issue