f6531a0130
Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
224 lines
6.3 KiB
C
224 lines
6.3 KiB
C
/* WMix -- a mixer using the OSS mixer API.
|
|
* Copyright (C) 2014 Christophe CURIS for the WindowMaker Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* mmkeys.c: functions related to grabbing the Multimedia Keys on keyboard
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/XF86keysym.h>
|
|
|
|
#include "include/common.h"
|
|
#include "include/config.h"
|
|
#include "include/mmkeys.h"
|
|
|
|
|
|
/* The global configuration */
|
|
struct multimedia_keys mmkeys;
|
|
|
|
/* The list of keys we're interrested in */
|
|
static const struct {
|
|
KeySym symbol;
|
|
KeyCode *store;
|
|
const char *name;
|
|
} key_list[] = {
|
|
{ XF86XK_AudioRaiseVolume, &mmkeys.raise_volume, "AudioRaiseVolume" },
|
|
{ XF86XK_AudioLowerVolume, &mmkeys.lower_volume, "AudioLowerVolume" },
|
|
{ XF86XK_AudioMute, &mmkeys.mute, "AudioMute" }
|
|
};
|
|
|
|
/* The modifiers that should not have impact on the key grabbed */
|
|
static const struct {
|
|
KeySym symbol;
|
|
const char *name;
|
|
} modifier_symbol[] = {
|
|
{ XK_Caps_Lock, "CapsLock" },
|
|
{ XK_Num_Lock, "NumLock" }
|
|
};
|
|
|
|
typedef struct {
|
|
int count;
|
|
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);
|
|
|
|
|
|
/*
|
|
* Grab the multimedia keys on the X server
|
|
*
|
|
* That basically means that whenever these keys are pressed
|
|
* the events will be sent to us instead of the application
|
|
* that has current focus.
|
|
*/
|
|
void mmkey_install(Display *display)
|
|
{
|
|
modifier_masks mod_masks;
|
|
struct mmkey_track install_info;
|
|
Window root_window;
|
|
int i, j;
|
|
|
|
mmkey_build_modifier_list(display, &mod_masks);
|
|
|
|
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;
|
|
|
|
key = XKeysymToKeycode(display, key_list[i].symbol);
|
|
*(key_list[i].store) = key;
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Build the list of bit-masks for all the modifiers we want to not have impact on our grab
|
|
*/
|
|
static void mmkey_build_modifier_list(Display *display, modifier_masks *result)
|
|
{
|
|
XModifierKeymap *mods;
|
|
KeyCode mod_code[lengthof(modifier_symbol)];
|
|
unsigned int mod_mask[lengthof(modifier_symbol)];
|
|
char buffer[256];
|
|
int nb_modifiers;
|
|
int i, j, k;
|
|
|
|
/* Get the bitmask associated with the modifiers */
|
|
for (i = 0; i < lengthof(modifier_symbol); i++) {
|
|
mod_code[i] = XKeysymToKeycode(display, modifier_symbol[i].symbol);
|
|
mod_mask[i] = 0L;
|
|
}
|
|
|
|
mods = XGetModifierMapping(display);
|
|
for (i = 0; i < 8; i++) {
|
|
for (j = 0; j < mods->max_keypermod; j++) {
|
|
KeyCode key_mod;
|
|
|
|
key_mod = mods->modifiermap[i * mods->max_keypermod + j];
|
|
for (k = 0; k < lengthof(mod_code); k++) {
|
|
if ((mod_code[k] != None) && (key_mod == mod_code[k]))
|
|
mod_mask[k] |= 1 << i;
|
|
}
|
|
}
|
|
}
|
|
XFreeModifiermap(mods);
|
|
|
|
/* Count the number of modifiers found (and display the list to the user) */
|
|
if (config.verbose)
|
|
strcpy(buffer, "Found key modifiers: ");
|
|
|
|
nb_modifiers = 0;
|
|
for (i = 0; i < lengthof(modifier_symbol); i++) {
|
|
if (mod_mask[i] != 0) {
|
|
if (config.verbose) {
|
|
if (nb_modifiers > 0)
|
|
strcat(buffer, ", ");
|
|
strcat(buffer, modifier_symbol[i].name);
|
|
}
|
|
nb_modifiers++;
|
|
}
|
|
}
|
|
if (config.verbose) {
|
|
if (nb_modifiers == 0)
|
|
strcat(buffer, "None");
|
|
puts(buffer);
|
|
}
|
|
|
|
/* Build the list of possible combinations of modifiers */
|
|
result->count = 1 << nb_modifiers;
|
|
for (i = 0; i < lengthof(result->list); i++)
|
|
result->list[i] = 0L;
|
|
k = 1;
|
|
for (i = 0; i < lengthof(mod_mask); i++) {
|
|
if (mod_mask[i] != 0) {
|
|
for (j = 1; j < result->count; j++)
|
|
if (j & k)
|
|
result->list[j] |= mod_mask[i];
|
|
|
|
k <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|