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:
parent
9e1261c350
commit
4380af776f
12
README.pod
12
README.pod
@ -44,13 +44,15 @@ timeouts for low/normal/critical messages.
|
||||
|
||||
=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
|
||||
modifiers are: ctrl, shift, mod1-mod4. This option can be used
|
||||
multiple times to combine modifiers.
|
||||
remove all notifications by pressing key.
|
||||
|
||||
=item B<-history_key key>
|
||||
|
||||
redisplay last notification by pressing key.
|
||||
|
||||
=item B<-format fmt>
|
||||
|
||||
|
245
dunst.c
245
dunst.c
@ -65,10 +65,6 @@ char *geom = "0x0"; /* geometry */
|
||||
int height_limit;
|
||||
int sort = True; /* sort messages by urgency */
|
||||
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 show_age_threshold = -1;
|
||||
enum alignment align = left;
|
||||
@ -85,9 +81,12 @@ static DC *dc;
|
||||
static Window win;
|
||||
static time_t now;
|
||||
static int visible = False;
|
||||
static KeySym key = NoSymbol;
|
||||
static KeySym history_key = NoSymbol;
|
||||
static KeySym all_key = NoSymbol;
|
||||
static keyboard_shortcut close_ks = {.str = NULL,.code = 0,.sym =
|
||||
NoSymbol,.mask = 0,.is_valid = False };
|
||||
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 dimension_t geometry;
|
||||
static XScreenSaverInfo *screensaver_info;
|
||||
@ -125,6 +124,9 @@ void hide_win(void);
|
||||
void move_all_to_history(void);
|
||||
void print_version(void);
|
||||
|
||||
void init_shortcut(keyboard_shortcut * shortcut);
|
||||
KeySym string_to_mask(char *str);
|
||||
|
||||
int cmp_notification(void *a, void *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
@ -480,8 +482,7 @@ void draw_win(void)
|
||||
XMoveWindow(dc->dpy, win, x, y);
|
||||
mapdc(dc, win, width, height * font_h);
|
||||
|
||||
|
||||
draw_win_cleanup:
|
||||
draw_win_cleanup:
|
||||
/* cleanup */
|
||||
free(n_buf);
|
||||
}
|
||||
@ -594,17 +595,23 @@ void handleXEvents(void)
|
||||
}
|
||||
break;
|
||||
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)) {
|
||||
notification *n = (notification *)
|
||||
displayed_notifications->head->data;
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -794,6 +801,62 @@ int close_notification(notification * n, int 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 *r = malloc(sizeof(rule_t));
|
||||
@ -893,21 +956,14 @@ void run(void)
|
||||
|
||||
void hide_win()
|
||||
{
|
||||
KeyCode code;
|
||||
Window root;
|
||||
root = RootWindow(dc->dpy, DefaultScreen(dc->dpy));
|
||||
|
||||
if (key != NoSymbol) {
|
||||
code = XKeysymToKeycode(dc->dpy, key);
|
||||
if (code != NoSymbol)
|
||||
XUngrabKey(dc->dpy, code, mask, root);
|
||||
}
|
||||
if (close_ks.is_valid)
|
||||
XUngrabKey(dc->dpy, close_ks.code, close_ks.mask, root);
|
||||
|
||||
if (all_key != NoSymbol) {
|
||||
code = XKeysymToKeycode(dc->dpy, all_key);
|
||||
if (code != NoSymbol)
|
||||
XUngrabKey(dc->dpy, code, mask, root);
|
||||
}
|
||||
if (close_all_ks.is_valid)
|
||||
XUngrabKey(dc->dpy, close_all_ks.code, close_all_ks.mask, root);
|
||||
|
||||
XUngrabButton(dc->dpy, AnyButton, AnyModifier, win);
|
||||
XUnmapWindow(dc->dpy, win);
|
||||
@ -946,7 +1002,6 @@ void setup(void)
|
||||
{
|
||||
Window root;
|
||||
XSetWindowAttributes wa;
|
||||
KeyCode code;
|
||||
|
||||
notification_queue = l_init();
|
||||
notification_history = l_init();
|
||||
@ -978,15 +1033,9 @@ void setup(void)
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
|
||||
|
||||
/* grab keys */
|
||||
if (history_key != NoSymbol) {
|
||||
code = XKeysymToKeycode(dc->dpy, history_key);
|
||||
if (code == NoSymbol)
|
||||
fprintf(stderr, "no keycode for keysym '%s'\n", history_key_string);
|
||||
else
|
||||
XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync,
|
||||
GrabModeAsync);
|
||||
}
|
||||
|
||||
if (history_ks.is_valid)
|
||||
XGrabKey(dc->dpy, history_ks.code, history_ks.mask, root,
|
||||
True, GrabModeAsync, GrabModeAsync);
|
||||
}
|
||||
|
||||
void map_win(void)
|
||||
@ -996,26 +1045,16 @@ void map_win(void)
|
||||
return;
|
||||
}
|
||||
|
||||
KeyCode code;
|
||||
Window root;
|
||||
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) {
|
||||
code = XKeysymToKeycode(dc->dpy, all_key);
|
||||
if (code == NoSymbol)
|
||||
fprintf(stderr, "no keycode for keysym '%s'\n", all_key_string);
|
||||
else
|
||||
XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync,
|
||||
GrabModeAsync);
|
||||
}
|
||||
if (close_ks.is_valid)
|
||||
XGrabKey(dc->dpy, close_ks.code, close_ks.mask, root, True,
|
||||
GrabModeAsync, GrabModeAsync);
|
||||
|
||||
if (close_all_ks.is_valid)
|
||||
XGrabKey(dc->dpy, close_all_ks.code, close_all_ks.mask, root,
|
||||
True, GrabModeAsync, GrabModeAsync);
|
||||
|
||||
update_screen_info();
|
||||
|
||||
@ -1044,7 +1083,6 @@ void parse_cmdline(int argc, char *argv[])
|
||||
{"lto", required_argument, NULL, '0'},
|
||||
{"nto", required_argument, NULL, '1'},
|
||||
{"cto", required_argument, NULL, '2'},
|
||||
{"mon", required_argument, NULL, 'm'},
|
||||
{"format", required_argument, NULL, 'f'},
|
||||
{"key", required_argument, NULL, 'k'},
|
||||
{"history_key", required_argument, NULL, 'K'},
|
||||
@ -1066,6 +1104,7 @@ void parse_cmdline(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
|
||||
KeySym mod = 0;
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
@ -1112,36 +1151,23 @@ void parse_cmdline(int argc, char *argv[])
|
||||
case 'f':
|
||||
format = optarg;
|
||||
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':
|
||||
key_string = optarg;
|
||||
close_ks.str = optarg;
|
||||
break;
|
||||
case 'K':
|
||||
history_key_string = optarg;
|
||||
break;
|
||||
history_ks.str = optarg;
|
||||
case 'A':
|
||||
all_key_string = optarg;
|
||||
break;
|
||||
close_all_ks.str = optarg;
|
||||
case 'g':
|
||||
geom = optarg;
|
||||
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':
|
||||
sort = True;
|
||||
break;
|
||||
@ -1234,38 +1260,34 @@ dunst_ini_handle(void *user_data, const char *section,
|
||||
sort = dunst_ini_get_boolean(value);
|
||||
else if (strcmp(name, "indicate_hidden") == 0)
|
||||
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)
|
||||
idle_threshold = atoi(value);
|
||||
else if (strcmp(name, "monitor") == 0)
|
||||
scr.scr = atoi(value);
|
||||
else if (strcmp(name, "geometry") == 0) {
|
||||
else if (strcmp(name, "geometry") == 0)
|
||||
geom = dunst_ini_get_string(value);
|
||||
} else if (strcmp(name, "modifier") == 0) {
|
||||
char *mod = dunst_ini_get_string(value);
|
||||
else if (strcmp(name, "modifier") == 0) {
|
||||
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) {
|
||||
if (strcmp(value, "left") == 0)
|
||||
align = left;
|
||||
@ -1278,6 +1300,7 @@ dunst_ini_handle(void *user_data, const char *section,
|
||||
show_age_threshold = atoi(value);
|
||||
else if (strcmp(name, "sticky_history") == 0)
|
||||
sticky_history = dunst_ini_get_boolean(value);
|
||||
|
||||
} else if (strcmp(section, "urgency_low") == 0) {
|
||||
if (strcmp(name, "background") == 0)
|
||||
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);
|
||||
else if (strcmp(name, "timeout") == 0)
|
||||
timeouts[LOW] = atoi(value);
|
||||
|
||||
} else if (strcmp(section, "urgency_normal") == 0) {
|
||||
if (strcmp(name, "background") == 0)
|
||||
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);
|
||||
else if (strcmp(name, "timeout") == 0)
|
||||
timeouts[NORM] = atoi(value);
|
||||
|
||||
} else if (strcmp(section, "urgency_critical") == 0) {
|
||||
if (strcmp(name, "background") == 0)
|
||||
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);
|
||||
else if (strcmp(name, "timeout") == 0)
|
||||
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 {
|
||||
|
||||
rule_t *current_rule = dunst_rules_find_or_create(section);
|
||||
@ -1392,19 +1425,19 @@ int main(int argc, char *argv[])
|
||||
now = time(&now);
|
||||
|
||||
cmdline_config_path = parse_cmdline_for_config_file(argc, argv);
|
||||
|
||||
parse_dunstrc(cmdline_config_path);
|
||||
parse_cmdline(argc, argv);
|
||||
dc = initdc();
|
||||
|
||||
init_shortcut(&close_ks);
|
||||
init_shortcut(&close_all_ks);
|
||||
init_shortcut(&history_ks);
|
||||
|
||||
geometry.mask = XParseGeometry(geom,
|
||||
&geometry.x, &geometry.y,
|
||||
&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();
|
||||
|
||||
initdbus();
|
||||
@ -1429,7 +1462,7 @@ int main(int argc, char *argv[])
|
||||
void usage(int exit_status)
|
||||
{
|
||||
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);
|
||||
exit(exit_status);
|
||||
}
|
||||
|
8
dunst.h
8
dunst.h
@ -59,6 +59,14 @@ typedef struct _dimension_t {
|
||||
int mask;
|
||||
} dimension_t;
|
||||
|
||||
typedef struct _keyboard_shortcut {
|
||||
const char *str;
|
||||
KeyCode code;
|
||||
KeySym sym;
|
||||
KeySym mask;
|
||||
int is_valid;
|
||||
} keyboard_shortcut;
|
||||
|
||||
/* return id of notification */
|
||||
int init_notification(notification * n, int id);
|
||||
int close_notification(notification * n, int reason);
|
||||
|
16
dunstrc
16
dunstrc
@ -15,18 +15,6 @@
|
||||
# Show how many messages are currently hidden (because of geometry)
|
||||
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.
|
||||
# Possible values are "left", "center" and "right"
|
||||
alignment = left
|
||||
@ -59,6 +47,10 @@
|
||||
# timeout as if it would normally down
|
||||
sticky_history = yes
|
||||
|
||||
[shortcuts]
|
||||
close = ctrl+space
|
||||
close_all = ctrl+shift+space
|
||||
history = ctrl+grave
|
||||
|
||||
[urgency_low]
|
||||
# IMPORTANT: colors have to be defined in quotation marks.
|
||||
|
Loading…
x
Reference in New Issue
Block a user