From: Christophe CURIS <[email protected]>

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 <[email protected]>
---
 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 <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;
+               }
+       }
+}
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 <[email protected]>,
 with some code by Daniel Richard G. <[email protected]>
+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;
-- 
1.9.2


-- 
To unsubscribe, send mail to [email protected].

Reply via email to