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 <christophe.curis@free.fr>
This commit is contained in:
Christophe CURIS 2014-06-07 21:21:54 +02:00 committed by Carlos R. Mafra
parent 3e2d8a730a
commit 4379368290
6 changed files with 251 additions and 2 deletions

View file

@ -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

View file

@ -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] )))

34
wmix/include/mmkeys.h Normal file
View file

@ -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 */

168
wmix/mmkeys.c Normal file
View file

@ -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 <stdio.h>
#include <string.h>
#include <X11/Xlib.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;
/* 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;
}
}
}

View file

@ -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 <timecop@japan.co.jp>,
with some code by Daniel Richard G. <skunk@mit.edu>
and some addition by Christophe CURIS.

View file

@ -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;