wmix: added error catch for XGrabKey on multimedia keys

If another application has already set a grab on these keys then the call
would fail (BadAccess) and wmix stop. With the X error handler, we can
display a warning to user and continue anyway.

Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
This commit is contained in:
Christophe CURIS 2014-06-07 21:21:55 +02:00 committed by Carlos R. Mafra
parent 4379368290
commit d83b160217
2 changed files with 59 additions and 0 deletions

View file

@ -23,6 +23,7 @@
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
@ -59,8 +60,19 @@ typedef struct {
unsigned int list[1 << lengthof(modifier_symbol)];
} modifier_masks;
/* The structure to track grab installation for errors */
static struct mmkey_track {
XErrorHandler previous_handler;
struct {
const char *key_name;
unsigned long serial[1 << lengthof(modifier_symbol)];
Bool displayed;
} request[lengthof(key_list)];
} *track_install = NULL;
/* Local functions */
static void mmkey_build_modifier_list(Display *display, modifier_masks *result);
static int mmkey_catch_grab_error(Display *display, XErrorEvent *event);
/*
@ -73,6 +85,7 @@ static void mmkey_build_modifier_list(Display *display, modifier_masks *result);
void mmkey_install(Display *display)
{
modifier_masks mod_masks;
struct mmkey_track install_info;
Window root_window;
int i, j;
@ -80,6 +93,9 @@ void mmkey_install(Display *display)
root_window = DefaultRootWindow(display);
memset(&install_info, 0, sizeof(install_info));
install_info.previous_handler = XSetErrorHandler(mmkey_catch_grab_error);
track_install = &install_info;
for (i = 0; i < lengthof(key_list); i++) {
KeyCode key;
@ -89,13 +105,21 @@ void mmkey_install(Display *display)
if (key == None)
continue;
install_info.request[i].key_name = key_list[i].name;
install_info.request[i].displayed = False;
for (j = 0; j < mod_masks.count; j++) {
install_info.request[i].serial[j] = NextRequest(display);
XGrabKey(display, key, mod_masks.list[j], root_window,
False, GrabModeAsync, GrabModeAsync);
}
if (config.verbose)
printf("Found multimedia key: %s\n", key_list[i].name);
}
/* The grab may fail, so make sure it is reported now */
XSync(display, False);
XSetErrorHandler(install_info.previous_handler);
track_install = NULL;
}
/*
@ -166,3 +190,34 @@ static void mmkey_build_modifier_list(Display *display, modifier_masks *result)
}
}
}
/*
* Callback when X11 reports an error
*
* We only track errors from XGrabKey and display them to user, instead of
* letting the default error handler exit
*/
static int mmkey_catch_grab_error(Display *display, XErrorEvent *event)
{
int i, j;
if ((event->error_code == BadAccess) && (event->request_code == X_GrabKey)) {
for (i = 0; i < lengthof(track_install->request); i++) {
for (j = 0; j < lengthof(track_install->request[i].serial); j++) {
if (track_install->request[i].serial[j] == 0L)
break;
if (event->serial == track_install->request[i].serial[j]) {
if (!track_install->request[i].displayed) {
fprintf(stderr, "wmix:warning: could not grab key %s, is another application using it?\n",
track_install->request[i].key_name);
track_install->request[i].displayed = True;
}
return 0;
}
}
}
}
/* That's not an XGrabKey known issue, let the default handler manage this */
return track_install->previous_handler(display, event);
}

View file

@ -134,6 +134,10 @@ if you have other application controlling the volume, and possibly
implementing mute in similar ways, because there is no way to know
that a channel has been muted.
.LP
The X server allows only one application at a time to place a grab on a key,
so if another application already claimed the volume control keys then \fBwmix\fP
will warn you about it and continue without the functionality.
.LP
If you modify the configuration file, do not expect \fBwmix\fP to reload
it automatically, this is considered a too costly feature for such a
small application.