From 4379368290a72e30b2dab3a0c36389e96e875381 Mon Sep 17 00:00:00 2001 From: Christophe CURIS Date: Sat, 7 Jun 2014 21:21:54 +0200 Subject: [PATCH] wmix: added function to handle Multimedia keys related to volume We can handle the keys AudioRaiseVolume, AudioLowerVolume and AudioMute, so we ask the X server to send the key press event for them to us and update the volume appropriately. Signed-off-by: Christophe CURIS --- wmix/Makefile | 2 +- wmix/include/common.h | 8 ++ wmix/include/mmkeys.h | 34 +++++++++ wmix/mmkeys.c | 168 ++++++++++++++++++++++++++++++++++++++++++ wmix/wmix.1x | 4 +- wmix/wmix.c | 37 ++++++++++ 6 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 wmix/include/mmkeys.h create mode 100644 wmix/mmkeys.c diff --git a/wmix/Makefile b/wmix/Makefile index f7a44b7..8f9a2eb 100644 --- a/wmix/Makefile +++ b/wmix/Makefile @@ -2,7 +2,7 @@ CC = gcc CFLAGS = -O3 -W -Wall LDFLAGS = -L/usr/X11R6/lib LIBS = -lXpm -lXext -lX11 -lm -OBJECTS = misc.o config.o mixer-oss.o ui_x.o wmix.o +OBJECTS = misc.o config.o mixer-oss.o ui_x.o mmkeys.o wmix.o # where to install this program (also for packaging stuff) PREFIX = /usr/local diff --git a/wmix/include/common.h b/wmix/include/common.h index 8d9b303..6d6e895 100644 --- a/wmix/include/common.h +++ b/wmix/include/common.h @@ -33,3 +33,11 @@ typedef unsigned int bool; #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #define MAX_DOUBLE_CLICK_TIME 0.5 + +/* + * Get the number of element in a static array + * + * Do not use on an allocated array, it will not work + */ +#define lengthof(arr) \ + ((ssize_t)(sizeof( arr ) / sizeof( arr[0] ))) diff --git a/wmix/include/mmkeys.h b/wmix/include/mmkeys.h new file mode 100644 index 0000000..2d0fc99 --- /dev/null +++ b/wmix/include/mmkeys.h @@ -0,0 +1,34 @@ +/* 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. + */ +/* include/mmkeys.h: functions related to handling Multimedia keys */ + +#ifndef WMIX_MMKEYS_H +#define WMIX_MMKEYS_H + + +/* Global Configuration */ +extern struct multimedia_keys { + KeyCode raise_volume; + KeyCode lower_volume; + KeyCode mute; +} mmkeys; + +/* Grab the multimedia keys */ +void mmkey_install(Display *display); + +#endif /* WMIX_MMKEYS_H */ diff --git a/wmix/mmkeys.c b/wmix/mmkeys.c new file mode 100644 index 0000000..bc22625 --- /dev/null +++ b/wmix/mmkeys.c @@ -0,0 +1,168 @@ +/* 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 grabing the Multimedia Keys on keyboard + */ + +#include +#include + +#include +#include +#include + +#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; + +/* Local functions */ +static void mmkey_build_modifier_list(Display *display, modifier_masks *result); + + +/* + * 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; + Window root_window; + int i, j; + + mmkey_build_modifier_list(display, &mod_masks); + + root_window = DefaultRootWindow(display); + + 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; + + for (j = 0; j < mod_masks.count; j++) { + 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); + } +} + +/* + * 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; + } + } +} diff --git a/wmix/wmix.1x b/wmix/wmix.1x index e077f21..117f42e 100644 --- a/wmix/wmix.1x +++ b/wmix/wmix.1x @@ -15,7 +15,8 @@ Allows toggling record source, muting individual channels, adjusting volume and balance, all in a compact dockapp size, with TV\-like on\-screen\-display for volume levels. .LP -Supports mouse wheel to adjust current channel's volume +Supports mouse wheel to adjust current channel's volume, +supports also the volume control keys on \(lqmultimedia\(rq keyboards and can be controlled remotely with unix signals .IR SIGUSR1 / SIGUSR2 to raise/lower the volume. @@ -144,3 +145,4 @@ It was expanded by Christophe CURIS for the Window Maker Dev Team. .PP wmix was written by Tim, timecop , with some code by Daniel Richard G. +and some addition by Christophe CURIS. diff --git a/wmix/wmix.c b/wmix/wmix.c index 1f7ddab..ae8bce5 100644 --- a/wmix/wmix.c +++ b/wmix/wmix.c @@ -37,6 +37,7 @@ #include "include/mixer.h" #include "include/misc.h" #include "include/ui_x.h" +#include "include/mmkeys.h" #include "include/config.h" @@ -55,6 +56,7 @@ static int idle_loop; static void signal_catch(int sig); static void button_press_event(XButtonEvent *event); static void button_release_event(XButtonEvent *event); +static int key_press_event(XKeyEvent *event); static void motion_event(XMotionEvent *event); @@ -91,6 +93,7 @@ int main(int argc, char **argv) dockapp_init(display); new_window("wmix", 64, 64); new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60); + mmkey_install(display); config_release(); @@ -116,6 +119,10 @@ int main(int argc, char **argv) if (button_pressed || slider_pressed || (XPending(display) > 0)) { XNextEvent(display, &event); switch (event.type) { + case KeyPress: + if (key_press_event(&event.xkey)) + idle_loop = 0; + break; case Expose: redraw_window(); break; @@ -276,6 +283,36 @@ static void button_press_event(XButtonEvent *event) } } +static int key_press_event(XKeyEvent *event) +{ + if (event->keycode == mmkeys.raise_volume) { + mixer_set_volume_rel(config.scrollstep); + if (!osd_mapped()) + map_osd(); + if (osd_mapped()) + update_osd(mixer_get_volume(), false); + ui_update(); + return 1; + } + if (event->keycode == mmkeys.lower_volume) { + mixer_set_volume_rel(-config.scrollstep); + if (!osd_mapped()) + map_osd(); + if (osd_mapped()) + update_osd(mixer_get_volume(), false); + ui_update(); + return 1; + } + if (event->keycode == mmkeys.mute) { + mixer_toggle_mute(); + ui_update(); + return 1; + } + + /* Ignore other keys */ + return 0; +} + static void button_release_event(XButtonEvent *event) { int x = event->x;