rewrite keyboard shortcut handling

this adds more flexibility to the shourtcuts since now you can have
separate modifiers for different keys.
This commit is contained in:
Sascha Kruse 2012-07-23 00:08:01 +02:00
parent 9e1261c350
commit 4380af776f
4 changed files with 158 additions and 123 deletions

View File

@ -44,13 +44,15 @@ timeouts for low/normal/critical messages.
=item B<-key key> =item B<-key key>
close window by pressing key. See also -mod. remove notification by pressing key.
=item B<-mod modifier> =item B<-all_key key>
defines the modifier used in conjunction with -key. Available remove all notifications by pressing key.
modifiers are: ctrl, shift, mod1-mod4. This option can be used
multiple times to combine modifiers. =item B<-history_key key>
redisplay last notification by pressing key.
=item B<-format fmt> =item B<-format fmt>

245
dunst.c
View File

@ -65,10 +65,6 @@ char *geom = "0x0"; /* geometry */
int height_limit; int height_limit;
int sort = True; /* sort messages by urgency */ int sort = True; /* sort messages by urgency */
int indicate_hidden = True; /* show count of hidden messages */ int indicate_hidden = True; /* show count of hidden messages */
char *key_string = NULL;
char *history_key_string = NULL;
char *all_key_string = NULL;
KeySym mask = 0;
int idle_threshold = 0; int idle_threshold = 0;
int show_age_threshold = -1; int show_age_threshold = -1;
enum alignment align = left; enum alignment align = left;
@ -85,9 +81,12 @@ static DC *dc;
static Window win; static Window win;
static time_t now; static time_t now;
static int visible = False; static int visible = False;
static KeySym key = NoSymbol; static keyboard_shortcut close_ks = {.str = NULL,.code = 0,.sym =
static KeySym history_key = NoSymbol; NoSymbol,.mask = 0,.is_valid = False };
static KeySym all_key = NoSymbol; static keyboard_shortcut close_all_ks = {.str = NULL,.code = 0,.sym =
NoSymbol,.mask = 0,.is_valid = False };
static keyboard_shortcut history_ks = {.str = NULL,.code = 0,.sym =
NoSymbol,.mask = 0,.is_valid = False };
static screen_info scr; static screen_info scr;
static dimension_t geometry; static dimension_t geometry;
static XScreenSaverInfo *screensaver_info; static XScreenSaverInfo *screensaver_info;
@ -125,6 +124,9 @@ void hide_win(void);
void move_all_to_history(void); void move_all_to_history(void);
void print_version(void); void print_version(void);
void init_shortcut(keyboard_shortcut * shortcut);
KeySym string_to_mask(char *str);
int cmp_notification(void *a, void *b) int cmp_notification(void *a, void *b)
{ {
if (a == NULL && b == NULL) if (a == NULL && b == NULL)
@ -480,8 +482,7 @@ void draw_win(void)
XMoveWindow(dc->dpy, win, x, y); XMoveWindow(dc->dpy, win, x, y);
mapdc(dc, win, width, height * font_h); mapdc(dc, win, width, height * font_h);
draw_win_cleanup:
draw_win_cleanup:
/* cleanup */ /* cleanup */
free(n_buf); free(n_buf);
} }
@ -594,17 +595,23 @@ void handleXEvents(void)
} }
break; break;
case KeyPress: case KeyPress:
if (XLookupKeysym(&ev.xkey, 0) == key) { if (close_ks.str
&& XLookupKeysym(&ev.xkey, 0) == close_ks.sym
&& close_ks.mask == ev.xkey.state) {
if (!l_is_empty(displayed_notifications)) { if (!l_is_empty(displayed_notifications)) {
notification *n = (notification *) notification *n = (notification *)
displayed_notifications->head->data; displayed_notifications->head->data;
close_notification(n, 2); close_notification(n, 2);
} }
} }
if (XLookupKeysym(&ev.xkey, 0) == history_key) { if (history_ks.str
&& XLookupKeysym(&ev.xkey, 0) == history_ks.sym
&& history_ks.mask == ev.xkey.state) {
history_pop(); history_pop();
} }
if (XLookupKeysym(&ev.xkey, 0) == all_key) { if (close_all_ks.str
&& XLookupKeysym(&ev.xkey, 0) == close_all_ks.sym
&& close_all_ks.mask == ev.xkey.state) {
move_all_to_history(); move_all_to_history();
} }
} }
@ -794,6 +801,62 @@ int close_notification(notification * n, int reason)
return close_notification_by_id(n->id, reason); return close_notification_by_id(n->id, reason);
} }
KeySym string_to_mask(char *str)
{
if (!strcmp(str, "ctrl")) {
return ControlMask;
} else if (!strcmp(str, "mod4")) {
return Mod4Mask;
} else if (!strcmp(str, "mod3")) {
return Mod3Mask;
} else if (!strcmp(str, "mod2")) {
return Mod2Mask;
} else if (!strcmp(str, "mod1")) {
return Mod1Mask;
} else if (!strcmp(str, "shift")) {
return ShiftMask;
} else {
fprintf(stderr, "Warning: Unknown Modifier: %s\n", str);
return 0;
}
}
void init_shortcut(keyboard_shortcut * ks)
{
if (ks == NULL || ks->str == NULL)
return;
char *str = strdup(ks->str);
char *str_begin = str;
if (str == NULL) {
fprintf(stderr, "Unable to allocate memory");
exit(EXIT_FAILURE);
}
while (strstr(str, "+")) {
char delim = '+';
char *mod = strsep(&str, &delim);
strtrim(mod);
ks->mask = ks->mask | string_to_mask(mod);
}
strtrim(str);
ks->sym = XStringToKeysym(str);
ks->code = XKeysymToKeycode(dc->dpy, ks->sym);
if (ks->sym == NoSymbol || ks->code == NoSymbol) {
fprintf(stderr, "Warning: Unknown keyboard shortcut: %s\n",
ks->str);
ks->is_valid = False;
} else {
ks->is_valid = True;
}
free(str_begin);
}
rule_t *initrule(void) rule_t *initrule(void)
{ {
rule_t *r = malloc(sizeof(rule_t)); rule_t *r = malloc(sizeof(rule_t));
@ -893,21 +956,14 @@ void run(void)
void hide_win() void hide_win()
{ {
KeyCode code;
Window root; Window root;
root = RootWindow(dc->dpy, DefaultScreen(dc->dpy)); root = RootWindow(dc->dpy, DefaultScreen(dc->dpy));
if (key != NoSymbol) { if (close_ks.is_valid)
code = XKeysymToKeycode(dc->dpy, key); XUngrabKey(dc->dpy, close_ks.code, close_ks.mask, root);
if (code != NoSymbol)
XUngrabKey(dc->dpy, code, mask, root);
}
if (all_key != NoSymbol) { if (close_all_ks.is_valid)
code = XKeysymToKeycode(dc->dpy, all_key); XUngrabKey(dc->dpy, close_all_ks.code, close_all_ks.mask, root);
if (code != NoSymbol)
XUngrabKey(dc->dpy, code, mask, root);
}
XUngrabButton(dc->dpy, AnyButton, AnyModifier, win); XUngrabButton(dc->dpy, AnyButton, AnyModifier, win);
XUnmapWindow(dc->dpy, win); XUnmapWindow(dc->dpy, win);
@ -946,7 +1002,6 @@ void setup(void)
{ {
Window root; Window root;
XSetWindowAttributes wa; XSetWindowAttributes wa;
KeyCode code;
notification_queue = l_init(); notification_queue = l_init();
notification_history = l_init(); notification_history = l_init();
@ -978,15 +1033,9 @@ void setup(void)
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
/* grab keys */ /* grab keys */
if (history_key != NoSymbol) { if (history_ks.is_valid)
code = XKeysymToKeycode(dc->dpy, history_key); XGrabKey(dc->dpy, history_ks.code, history_ks.mask, root,
if (code == NoSymbol) True, GrabModeAsync, GrabModeAsync);
fprintf(stderr, "no keycode for keysym '%s'\n", history_key_string);
else
XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync,
GrabModeAsync);
}
} }
void map_win(void) void map_win(void)
@ -996,26 +1045,16 @@ void map_win(void)
return; return;
} }
KeyCode code;
Window root; Window root;
root = RootWindow(dc->dpy, DefaultScreen(dc->dpy)); root = RootWindow(dc->dpy, DefaultScreen(dc->dpy));
if (key != NoSymbol) {
code = XKeysymToKeycode(dc->dpy, key);
if (code == NoSymbol)
fprintf(stderr, "no keycode for keysym '%s'\n", key_string);
else
XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync,
GrabModeAsync);
}
if (all_key != NoSymbol) { if (close_ks.is_valid)
code = XKeysymToKeycode(dc->dpy, all_key); XGrabKey(dc->dpy, close_ks.code, close_ks.mask, root, True,
if (code == NoSymbol) GrabModeAsync, GrabModeAsync);
fprintf(stderr, "no keycode for keysym '%s'\n", all_key_string);
else if (close_all_ks.is_valid)
XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync, XGrabKey(dc->dpy, close_all_ks.code, close_all_ks.mask, root,
GrabModeAsync); True, GrabModeAsync, GrabModeAsync);
}
update_screen_info(); update_screen_info();
@ -1044,7 +1083,6 @@ void parse_cmdline(int argc, char *argv[])
{"lto", required_argument, NULL, '0'}, {"lto", required_argument, NULL, '0'},
{"nto", required_argument, NULL, '1'}, {"nto", required_argument, NULL, '1'},
{"cto", required_argument, NULL, '2'}, {"cto", required_argument, NULL, '2'},
{"mon", required_argument, NULL, 'm'},
{"format", required_argument, NULL, 'f'}, {"format", required_argument, NULL, 'f'},
{"key", required_argument, NULL, 'k'}, {"key", required_argument, NULL, 'k'},
{"history_key", required_argument, NULL, 'K'}, {"history_key", required_argument, NULL, 'K'},
@ -1066,6 +1104,7 @@ void parse_cmdline(int argc, char *argv[])
break; break;
} }
KeySym mod = 0;
switch (c) { switch (c) {
case 0: case 0:
break; break;
@ -1112,36 +1151,23 @@ void parse_cmdline(int argc, char *argv[])
case 'f': case 'f':
format = optarg; format = optarg;
break; break;
case 'M':
fprintf(stderr, "-mod is depricated. Use \"-key mod+key\" instead\n");
mod = string_to_mask(optarg);
close_ks.mask = mod;
close_all_ks.mask = mod;
history_ks.mask = mod;
break;
case 'k': case 'k':
key_string = optarg; close_ks.str = optarg;
break; break;
case 'K': case 'K':
history_key_string = optarg; history_ks.str = optarg;
break;
case 'A': case 'A':
all_key_string = optarg; close_all_ks.str = optarg;
break;
case 'g': case 'g':
geom = optarg; geom = optarg;
break; break;
case 'M':
if (!strcmp(optarg, "ctrl")) {
mask |= ControlMask;
} else if (!strcmp(optarg, "mod1")) {
mask |= Mod1Mask;
} else if (!strcmp(optarg, "mod2")) {
mask |= Mod2Mask;
} else if (!strcmp(optarg, "mod3")) {
mask |= Mod3Mask;
} else if (!strcmp(optarg, "mod4")) {
mask |= Mod4Mask;
} else {
fprintf(stderr, "Unable to find mask: %s\n",
optarg);
fprintf(stderr,
"See manpage for list of available masks\n");
}
break;
case 's': case 's':
sort = True; sort = True;
break; break;
@ -1234,38 +1260,34 @@ dunst_ini_handle(void *user_data, const char *section,
sort = dunst_ini_get_boolean(value); sort = dunst_ini_get_boolean(value);
else if (strcmp(name, "indicate_hidden") == 0) else if (strcmp(name, "indicate_hidden") == 0)
indicate_hidden = dunst_ini_get_boolean(value); indicate_hidden = dunst_ini_get_boolean(value);
else if (strcmp(name, "key") == 0)
key_string = dunst_ini_get_string(value);
else if (strcmp(name, "history_key") == 0)
history_key_string = dunst_ini_get_string(value);
else if (strcmp(name, "all_key") == 0)
all_key_string = dunst_ini_get_string(value);
else if (strcmp(name, "idle_threshold") == 0) else if (strcmp(name, "idle_threshold") == 0)
idle_threshold = atoi(value); idle_threshold = atoi(value);
else if (strcmp(name, "monitor") == 0) else if (strcmp(name, "monitor") == 0)
scr.scr = atoi(value); scr.scr = atoi(value);
else if (strcmp(name, "geometry") == 0) { else if (strcmp(name, "geometry") == 0)
geom = dunst_ini_get_string(value); geom = dunst_ini_get_string(value);
} else if (strcmp(name, "modifier") == 0) { else if (strcmp(name, "modifier") == 0) {
char *mod = dunst_ini_get_string(value); fprintf(stderr,
"WARNING: Keyboard Shortcuts in [global] are depricated. See example dunstrc for new [shortcuts] section\n");
KeySym mod =
string_to_mask(dunst_ini_get_string(value));
close_ks.mask = mod;
close_all_ks.mask = mod;
history_ks.mask = mod;
} else if (strcmp(name, "key") == 0) {
fprintf(stderr,
"WARNING: Keyboard Shortcuts in [global] are depricated. See example dunstrc for new [shortcuts] section\n");
close_ks.str = dunst_ini_get_string(value);
} else if (strcmp(name, "all_key") == 0) {
fprintf(stderr,
"WARNING: Keyboard Shortcuts in [global] are depricated. See example dunstrc for new [shortcuts] section\n");
close_all_ks.str = dunst_ini_get_string(value);
} else if (strcmp(name, "history_key") == 0) {
fprintf(stderr,
"WARNING: Keyboard Shortcuts in [global] are depricated. See example dunstrc for new [shortcuts] section\n");
history_ks.str = dunst_ini_get_string(value);
if (mod == NULL) {
mask = 0;
} else if (!strcmp(mod, "ctrl")) {
mask = ControlMask;
} else if (!strcmp(mod, "mod4")) {
mask = Mod4Mask;
} else if (!strcmp(mod, "mod3")) {
mask = Mod3Mask;
} else if (!strcmp(mod, "mod2")) {
mask = Mod2Mask;
} else if (!strcmp(mod, "mod1")) {
mask = Mod1Mask;
} else {
/* FIXME warning on unknown modifier */
mask = 0;
}
free(mod);
} else if (strcmp(name, "alignment") == 0) { } else if (strcmp(name, "alignment") == 0) {
if (strcmp(value, "left") == 0) if (strcmp(value, "left") == 0)
align = left; align = left;
@ -1278,6 +1300,7 @@ dunst_ini_handle(void *user_data, const char *section,
show_age_threshold = atoi(value); show_age_threshold = atoi(value);
else if (strcmp(name, "sticky_history") == 0) else if (strcmp(name, "sticky_history") == 0)
sticky_history = dunst_ini_get_boolean(value); sticky_history = dunst_ini_get_boolean(value);
} else if (strcmp(section, "urgency_low") == 0) { } else if (strcmp(section, "urgency_low") == 0) {
if (strcmp(name, "background") == 0) if (strcmp(name, "background") == 0)
lowbgcolor = dunst_ini_get_string(value); lowbgcolor = dunst_ini_get_string(value);
@ -1285,6 +1308,7 @@ dunst_ini_handle(void *user_data, const char *section,
lowfgcolor = dunst_ini_get_string(value); lowfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0) else if (strcmp(name, "timeout") == 0)
timeouts[LOW] = atoi(value); timeouts[LOW] = atoi(value);
} else if (strcmp(section, "urgency_normal") == 0) { } else if (strcmp(section, "urgency_normal") == 0) {
if (strcmp(name, "background") == 0) if (strcmp(name, "background") == 0)
normbgcolor = dunst_ini_get_string(value); normbgcolor = dunst_ini_get_string(value);
@ -1292,6 +1316,7 @@ dunst_ini_handle(void *user_data, const char *section,
normfgcolor = dunst_ini_get_string(value); normfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0) else if (strcmp(name, "timeout") == 0)
timeouts[NORM] = atoi(value); timeouts[NORM] = atoi(value);
} else if (strcmp(section, "urgency_critical") == 0) { } else if (strcmp(section, "urgency_critical") == 0) {
if (strcmp(name, "background") == 0) if (strcmp(name, "background") == 0)
critbgcolor = dunst_ini_get_string(value); critbgcolor = dunst_ini_get_string(value);
@ -1299,6 +1324,14 @@ dunst_ini_handle(void *user_data, const char *section,
critfgcolor = dunst_ini_get_string(value); critfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0) else if (strcmp(name, "timeout") == 0)
timeouts[CRIT] = atoi(value); timeouts[CRIT] = atoi(value);
} else if (strcmp(section, "shortcuts") == 0) {
if (strcmp(name, "close") == 0)
close_ks.str = dunst_ini_get_string(value);
else if (strcmp(name, "close_all") == 0)
close_all_ks.str = dunst_ini_get_string(value);
else if (strcmp(name, "history") == 0)
history_ks.str = dunst_ini_get_string(value);
} else { } else {
rule_t *current_rule = dunst_rules_find_or_create(section); rule_t *current_rule = dunst_rules_find_or_create(section);
@ -1392,19 +1425,19 @@ int main(int argc, char *argv[])
now = time(&now); now = time(&now);
cmdline_config_path = parse_cmdline_for_config_file(argc, argv); cmdline_config_path = parse_cmdline_for_config_file(argc, argv);
parse_dunstrc(cmdline_config_path); parse_dunstrc(cmdline_config_path);
parse_cmdline(argc, argv); parse_cmdline(argc, argv);
dc = initdc(); dc = initdc();
init_shortcut(&close_ks);
init_shortcut(&close_all_ks);
init_shortcut(&history_ks);
geometry.mask = XParseGeometry(geom, geometry.mask = XParseGeometry(geom,
&geometry.x, &geometry.y, &geometry.x, &geometry.y,
&geometry.w, &geometry.h); &geometry.w, &geometry.h);
/* initialize keys */
key = key_string ? XStringToKeysym(key_string) : NoSymbol;
history_key =
history_key_string ? XStringToKeysym(history_key_string) : NoSymbol;
all_key = all_key_string ? XStringToKeysym(all_key_string) : NoSymbol;
screensaver_info = XScreenSaverAllocInfo(); screensaver_info = XScreenSaverAllocInfo();
initdbus(); initdbus();
@ -1429,7 +1462,7 @@ int main(int argc, char *argv[])
void usage(int exit_status) void usage(int exit_status)
{ {
fputs fputs
("usage: dunst [-h/--help] [-v] [-geometry geom] [-fn font] [-format fmt]\n[-nb color] [-nf color] [-lb color] [-lf color] [-cb color] [ -cf color]\n[-to secs] [-lto secs] [-cto secs] [-nto secs] [-key key] [-history_key key] [-all_key key] [-mod modifier] [-mon n] [-config dunstrc]\n", ("usage: dunst [-h/--help] [-v] [-geometry geom] [-fn font] [-format fmt]\n[-nb color] [-nf color] [-lb color] [-lf color] [-cb color] [ -cf color]\n[-to secs] [-lto secs] [-cto secs] [-nto secs] [-key key] [-history_key key] [-all_key key] [-mon n] [-config dunstrc]\n",
stderr); stderr);
exit(exit_status); exit(exit_status);
} }

View File

@ -59,6 +59,14 @@ typedef struct _dimension_t {
int mask; int mask;
} dimension_t; } dimension_t;
typedef struct _keyboard_shortcut {
const char *str;
KeyCode code;
KeySym sym;
KeySym mask;
int is_valid;
} keyboard_shortcut;
/* return id of notification */ /* return id of notification */
int init_notification(notification * n, int id); int init_notification(notification * n, int id);
int close_notification(notification * n, int reason); int close_notification(notification * n, int reason);

16
dunstrc
View File

@ -15,18 +15,6 @@
# Show how many messages are currently hidden (because of geometry) # Show how many messages are currently hidden (because of geometry)
indicate_hidden = yes indicate_hidden = yes
# keybindings to remove notification
# available modifiers are 'ctrl', 'mod1' (the alt-key), 'mod2', 'mod3'
# and 'mod4' (windows-key)
modifier = ctrl
# use xev to find the names for keys
key = space
# key to clear all messages
all_key = Menu
# key to redisplay last message(s)
history_key = grave
# alignment of message text. # alignment of message text.
# Possible values are "left", "center" and "right" # Possible values are "left", "center" and "right"
alignment = left alignment = left
@ -59,6 +47,10 @@
# timeout as if it would normally down # timeout as if it would normally down
sticky_history = yes sticky_history = yes
[shortcuts]
close = ctrl+space
close_all = ctrl+shift+space
history = ctrl+grave
[urgency_low] [urgency_low]
# IMPORTANT: colors have to be defined in quotation marks. # IMPORTANT: colors have to be defined in quotation marks.