Package: hotkeys
Version: 0.5.7.4-0.1
Severity: normal
Tags: patch

I've implemnted support in hotkeys for using the alsa mixer. A patch is
attached. It's fairly complete, though I hardcoded -lasound in the
Makefiile, as I don't enjoy messing around with autoconf.

I chose to only enable alsa if the ALSAcontrol config file option, which
probably needs to be tuned for each system anyway, is filled in. Without
that it will continue doing the old-style OSS mixing, which doesn't seem
to affect the alsa volume at all.

Since the patch allows ALSAcontrol to be set to a list of controls,
which are all controlled at once, it fixes bug #305716. A useful tip for
laptops with separate speaker and headphone volumes: Have hotkeys
control them both, and use alsamixer to mute the headphones. Then when
you toggle the muting, the headphones will unmute, and the speaker will
mute, so you can switch between them like that.

-- System Information:
Debian Release: lenny/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing'), (1, 'experimental')
Architecture: i386 (i686)

Kernel: Linux 2.6.21-2-686 (SMP w/1 CPU core)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages hotkeys depends on:
ii  libasound2                 1.0.14a-2     ALSA library
ii  libatk1.0-0                1.18.0-2      The ATK accessibility toolkit
ii  libc6                      2.6-2         GNU C Library: Shared libraries
ii  libcairo2                  1.4.10-1      The Cairo 2D vector graphics libra
ii  libdb4.5                   4.5.20-5      Berkeley v4.5 Database Libraries [
ii  libfontconfig1             2.4.2-1.2     generic font configuration library
ii  libglib2.0-0               2.12.13-1     The GLib library of C routines
ii  libgtk2.0-0                2.10.13-1     The GTK+ graphical user interface 
ii  libpango1.0-0              1.16.4-3      Layout and rendering of internatio
ii  libx11-6                   2:1.0.3-7     X11 client-side library
ii  libxcursor1                1:1.1.8-2     X cursor management library
ii  libxext6                   1:1.0.3-2     X11 miscellaneous extension librar
ii  libxfixes3                 1:4.0.3-2     X11 miscellaneous 'fixes' extensio
ii  libxi6                     2:1.1.1-1     X11 Input extension library
ii  libxinerama1               1:1.0.2-1     X11 Xinerama extension library
ii  libxml2                    2.6.29.dfsg-1 GNOME XML library
ii  libxmu6                    1:1.0.3-1     X11 miscellaneous utility library
ii  libxosd2                   2.2.14-1.3    X On-Screen Display library - runt
ii  libxrandr2                 2:1.2.1-1     X11 RandR extension library
ii  libxrender1                1:0.9.2-1     X Rendering Extension client libra

hotkeys recommends no packages.

-- no debconf information

-- 
see shy jo
diff --new-file -ur old/hotkeys-0.5.7.4/TODO hotkeys-0.5.7.4/TODO
--- old/hotkeys-0.5.7.4/TODO	2002-11-28 09:30:14.000000000 -0500
+++ hotkeys-0.5.7.4/TODO	2007-07-27 23:55:36.000000000 -0400
@@ -12,8 +12,6 @@
 
 - Support of devfs
 
-- Support of ALSA (hardware mute)
-
 - Multiple functionalities per key
 
 - Support true sleeping, suspend and power down (or logging out from WM).
diff --new-file -ur old/hotkeys-0.5.7.4/src/Makefile.am hotkeys-0.5.7.4/src/Makefile.am
--- old/hotkeys-0.5.7.4/src/Makefile.am	2002-12-08 09:16:20.000000000 -0500
+++ hotkeys-0.5.7.4/src/Makefile.am	2007-07-27 12:51:23.000000000 -0400
@@ -1,13 +1,13 @@
 bin_PROGRAMS    = hotkeys
 hotkeys_SOURCES = hotkeys.c kbddef.c conf.c fixVMware.c apmlib.c xmalloc.c \
-                  splash.c \
+                  splash.c amixer.c \
                   XF86keysym.h apm.h common.h conf.h hotkeys.h kbddef.h \
-                  xmalloc.h splash.h
+                  xmalloc.h splash.h amixer.h
 sysconf_DATA    = hotkeys.conf
 man_MANS        = hotkeys.1
 
 CFLAGS      = @CFLAGS@ @X_CFLAGS@ @XML_CFLAGS@ @LIBXOSD_CFLAGS@ @GTK_CFLAGS@ -I.. # -DDEBUG 
-LIBS        = @LIBS@ @X_LIBS@ @XML_LIBS@ @LIBXOSD_LIBS@ @GTK_LIBS@
+LIBS        = @LIBS@ @X_LIBS@ @XML_LIBS@ @LIBXOSD_LIBS@ @GTK_LIBS@ -lasound
 DEFS        = @DEFS@ -DSHAREDIR=\"@datadir@/@[EMAIL PROTECTED]" \
               -DCONFDIR=\"@[EMAIL PROTECTED]" -DCONFIG_NAME=\"@[EMAIL PROTECTED]"
 
diff --new-file -ur old/hotkeys-0.5.7.4/src/Makefile.in hotkeys-0.5.7.4/src/Makefile.in
--- old/hotkeys-0.5.7.4/src/Makefile.in	2002-12-08 09:34:44.000000000 -0500
+++ hotkeys-0.5.7.4/src/Makefile.in	2007-07-27 12:51:25.000000000 -0400
@@ -96,13 +96,13 @@
 l = @l@
 
 bin_PROGRAMS = hotkeys
-hotkeys_SOURCES = hotkeys.c kbddef.c conf.c fixVMware.c apmlib.c xmalloc.c                   splash.c                   XF86keysym.h apm.h common.h conf.h hotkeys.h kbddef.h                   xmalloc.h splash.h
+hotkeys_SOURCES = hotkeys.c kbddef.c conf.c fixVMware.c apmlib.c xmalloc.c                   splash.c amixer.c                   XF86keysym.h apm.h common.h conf.h hotkeys.h kbddef.h                   xmalloc.h splash.h amixer.h
 
 sysconf_DATA = hotkeys.conf
 man_MANS = hotkeys.1
 
 CFLAGS = @CFLAGS@ @X_CFLAGS@ @XML_CFLAGS@ @LIBXOSD_CFLAGS@ @GTK_CFLAGS@ -I.. # -DDEBUG 
-LIBS = @LIBS@ @X_LIBS@ @XML_LIBS@ @LIBXOSD_LIBS@ @GTK_LIBS@
+LIBS = @LIBS@ @X_LIBS@ @XML_LIBS@ @LIBXOSD_LIBS@ @GTK_LIBS@ -lasound
 DEFS = @DEFS@ -DSHAREDIR=\"@datadir@/@[EMAIL PROTECTED]"               -DCONFDIR=\"@[EMAIL PROTECTED]" -DCONFIG_NAME=\"@[EMAIL PROTECTED]"
 
 
@@ -121,7 +121,7 @@
 X_EXTRA_LIBS = @X_EXTRA_LIBS@
 X_PRE_LIBS = @X_PRE_LIBS@
 hotkeys_OBJECTS =  hotkeys.o kbddef.o conf.o fixVMware.o apmlib.o \
-xmalloc.o splash.o
+xmalloc.o splash.o amixer.o
 hotkeys_LDADD = $(LDADD)
 hotkeys_DEPENDENCIES = 
 hotkeys_LDFLAGS = 
diff --new-file -ur old/hotkeys-0.5.7.4/src/amixer.c hotkeys-0.5.7.4/src/amixer.c
--- old/hotkeys-0.5.7.4/src/amixer.c	1969-12-31 19:00:00.000000000 -0500
+++ hotkeys-0.5.7.4/src/amixer.c	2007-07-28 15:04:25.000000000 -0400
@@ -0,0 +1,293 @@
+/*
+ *   ALSA command line mixer utility
+ *   Copyright (c) 1999-2000 by Jaroslav Kysela <[EMAIL PROTECTED]>
+ *   Modified for hotkeys by Joey Hess <[EMAIL PROTECTED]>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <math.h>
+#include <errno.h>
+#include <assert.h>
+#include <alsa/asoundlib.h>
+#include <sys/poll.h>
+
+#include "hotkeys.h"
+#include "xmalloc.h"
+
+char *card;
+char **controls;
+int numcontrols=0;
+snd_mixer_t *handle = NULL;
+
+static int parse_simple_id(const char *str, snd_mixer_selem_id_t *sid) {
+	int c, size;
+	char buf[128];
+	char *ptr = buf;
+
+	while (*str == ' ' || *str == '\t')
+		str++;
+	if (!(*str))
+		return -EINVAL;
+	size = 1;	/* for '\0' */
+	if (*str != '"' && *str != '\'') {
+		while (*str && *str != ',') {
+			if (size < (int)sizeof(buf)) {
+				*ptr++ = *str;
+				size++;
+			}
+			str++;
+		}
+	} else {
+		c = *str++;
+		while (*str && *str != c) {
+			if (size < (int)sizeof(buf)) {
+				*ptr++ = *str;
+				size++;
+			}
+			str++;
+		}
+		if (*str == c)
+			str++;
+	}
+	if (*str == '\0') {
+		snd_mixer_selem_id_set_index(sid, 0);
+		*ptr = 0;
+		goto _set;
+	}
+	if (*str != ',')
+		return -EINVAL;
+	*ptr = 0;	/* terminate the string */
+	str++;
+	if (!isdigit(*str))
+		return -EINVAL;
+	snd_mixer_selem_id_set_index(sid, atoi(str));
+       _set:
+	snd_mixer_selem_id_set_name(sid, buf);
+	return 0;
+}
+
+snd_mixer_elem_t *getelem(char *control) {
+	snd_mixer_elem_t *elem;
+	snd_mixer_selem_id_t *sid;
+	snd_mixer_selem_id_alloca(&sid);
+
+	if (control == NULL)
+		return NULL;
+
+	if (parse_simple_id(control, sid)) {
+		uError("Bad ALSA control '%s'", control);
+		return NULL;
+	}
+	elem = snd_mixer_find_selem(handle, sid);
+	if (!elem) {
+		uError("Unable to find ALSA control '%s',%i", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
+		return NULL;
+	}
+
+	return elem;
+}
+
+int gethandle(void) {
+	int err=0;
+
+	if (handle) {
+		snd_mixer_close(handle);
+		handle=NULL;
+	}
+	
+	if ((err = snd_mixer_open(&handle, 0)) < 0) {
+		uError("Mixer %s open error: %s", card, snd_strerror(err));
+		return err;
+	}
+	if ((err = snd_mixer_attach(handle, card)) < 0) {
+		uError("Mixer attach %s error: %s", card, snd_strerror(err));
+		snd_mixer_close(handle);
+		handle = NULL;
+		return err;
+	}
+	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
+		uError("Mixer register error: %s", snd_strerror(err));
+		snd_mixer_close(handle);
+		handle = NULL;
+		return err;
+	}
+	err = snd_mixer_load(handle);
+	if (err < 0) {
+		uError("Mixer %s load error: %s", card, snd_strerror(err));
+		snd_mixer_close(handle);
+		handle = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+int configALSA(char *alsacard, char *alsacontrols) {
+        int i = 0;
+	char *c = alsacontrols;
+
+	if (alsacard == NULL || alsacontrols == NULL) {
+		return 1;
+	}
+
+	card=alsacard;
+
+	/* Split controls into array and parse. */
+        do {
+		c = strchr( c+1, '|' );
+		numcontrols++;
+        } while ( c != NULL );
+        controls = XMALLOC( char *, numcontrols );
+        /* dup needed since strtok modifies the string */
+        c = (char*) xstrdup( alsacontrols );
+        controls[0] = strtok( c, "|" );
+        while ( i < numcontrols ) {
+            i++;
+            controls[i] = strtok( NULL, "|" );
+        }
+
+	return 0; /* success */
+}
+
+#define check_range(val, min, max) \
+	((val < min) ? (min) : (val > max) ? (max) : (val)) 
+
+/* Fuction to convert from volume to percentage. val = volume */
+
+static int convert_prange(int val, int min, int max) {
+	int range = max - min;
+	int tmp;
+
+	if (range == 0)
+		return 0;
+	val -= min;
+	tmp = rint((double)val/(double)range * 100);
+	return tmp;
+}
+
+#define convert_prange1(val, min, max) \
+	ceil((val) * ((max) - (min)) * 0.01 + (min))
+
+static int set_volume_simple(snd_mixer_elem_t *elem,
+			     snd_mixer_selem_channel_id_t chn,
+			     int percent) {
+	long val, pmin, pmax;
+	int invalid = 0, err = 0;
+
+	if (! snd_mixer_selem_has_playback_volume(elem))
+		invalid = 1;
+
+	if (! invalid &&
+	    snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax) < 0)
+		invalid = 1;
+
+	if (! invalid)
+		val = (long)convert_prange1(percent, pmin, pmax);
+	
+	if (! invalid) {
+		val = check_range(val, pmin, pmax);
+		err = snd_mixer_selem_set_playback_volume(elem, chn, val);
+	}
+	return err ? err : (invalid ? -ENOENT : 0);
+}
+
+/* Pass percent to change mixer, or zero to toggle mute. Returns new
+ * mixer volume if changed, or mute status if mute changed, or negative 
+ * on error. (Returned values are for the first control only.) */
+int amixer(int percent) {
+	snd_mixer_selem_channel_id_t chn;
+	unsigned int channels = ~0U;
+	long pvol;
+	long pmin = 0, pmax = 0;
+	int i;
+	int err=0, ret = -1;
+ 
+	/* Need to reopen the handle every time to detect changes to the
+	 * mixer state. This is probably not the best way.. */
+	if (gethandle() != 0) {
+		err=-1;
+	}
+
+	if (! numcontrols) {
+		uError("internal error: no controls");
+		err=-1;
+	}
+
+	int firstcontrol = 1;
+	for (i = 0; i < numcontrols; i++ ) {
+		snd_mixer_elem_t *elem = getelem(controls[i]);
+
+		if (elem == NULL)
+			continue;
+		
+		int firstchn = 1;
+		for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
+			int ival;
+			if (!(channels & (1 << chn)))
+				continue;
+			if (snd_mixer_selem_has_playback_channel(elem, chn)) {
+				if (percent == 0) {
+					if (snd_mixer_selem_has_playback_switch(elem) &&
+					   (firstchn || !snd_mixer_selem_has_playback_switch_joined(elem))) {
+						snd_mixer_selem_get_playback_switch(elem, chn, &ival);
+						snd_mixer_selem_set_playback_switch(elem, chn, (ival ? 1 : 0) ^ 1);
+						if (firstcontrol) {
+							ret = ival;
+							firstcontrol=0;
+						}
+					}
+				}
+				else {
+					int current_percent;
+					
+					snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
+					snd_mixer_selem_get_playback_volume(elem, chn, &pvol);
+					current_percent = convert_prange(pvol, pmin, pmax);
+
+					current_percent += percent;
+					if (current_percent > 100)
+						current_percent = 100;
+					/* 1 not 0 to avoid muting */
+					else if (current_percent < 1)
+						current_percent = 1;
+
+					if (set_volume_simple(elem, chn, current_percent) > 0) {
+						uError("volume set failed");
+						err=-1;
+					}
+
+					if (firstcontrol) {
+						ret = current_percent;
+						firstcontrol=0;
+					}
+				}
+			}
+			firstchn = 0;
+		}
+	}
+
+	if (err != 0)
+		return err;
+	else
+		return ret;
+}
diff --new-file -ur old/hotkeys-0.5.7.4/src/amixer.h hotkeys-0.5.7.4/src/amixer.h
--- old/hotkeys-0.5.7.4/src/amixer.h	1969-12-31 19:00:00.000000000 -0500
+++ hotkeys-0.5.7.4/src/amixer.h	2007-07-27 12:53:56.000000000 -0400
@@ -0,0 +1,2 @@
+int amixer(int percent);
+int configALSA(char *alsacard, char *alsacontrols);
diff --new-file -ur old/hotkeys-0.5.7.4/src/conf.c hotkeys-0.5.7.4/src/conf.c
--- old/hotkeys-0.5.7.4/src/conf.c	2007-07-20 09:53:06.000000000 -0400
+++ hotkeys-0.5.7.4/src/conf.c	2007-07-27 12:02:13.000000000 -0400
@@ -41,6 +41,11 @@
     /* CDROM device */
     "CDROM",           "/dev/cdrom",
 
+    /* ALSA card */
+    "ALSAcard",		"default",
+    /* List of ALSA control names, separated by "|" */
+    //"ALSAcontrols"	"Master",
+
     /* general actions */
     "PrevTrack",       "xmms --rew",
     "Play",            "xmms --play-pause",
diff --new-file -ur old/hotkeys-0.5.7.4/src/hotkeys.1 hotkeys-0.5.7.4/src/hotkeys.1
--- old/hotkeys-0.5.7.4/src/hotkeys.1	2004-12-24 10:45:02.000000000 -0500
+++ hotkeys-0.5.7.4/src/hotkeys.1	2007-07-28 00:00:58.000000000 -0400
@@ -141,6 +141,11 @@
   # CDROM device
   CDROM           /dev/cdrom
 
+  # ALSA card
+  ALSAcard	  default
+  # List of ALSA control names, separated by "|"
+  ALSAcontrols    Master
+
   # audio actions
   PrevTrack       xmms --rew
   Play            xmms --play
@@ -180,6 +185,11 @@
 .SH "NOTES"
 Do not disable the XKEYBOARD extension. For XFree86 3.x, make sure
 \fBXkbDisable\fR is not set.
+.P
+The ALSA control names have to be set to enable using ALSA. Otherwise, it
+defaults to using OSS volume control. The ALSA control names are the same
+as those used by the amixer(1) program, use "amixer scontrols" to list.
+All listed controls will be adjusted at the same time by hotkeys.
 .SH "TODO"
 Please consult the TODO file.
 .SH "BUGS"
diff --new-file -ur old/hotkeys-0.5.7.4/src/hotkeys.c hotkeys-0.5.7.4/src/hotkeys.c
--- old/hotkeys-0.5.7.4/src/hotkeys.c	2005-02-19 13:29:23.000000000 -0500
+++ hotkeys-0.5.7.4/src/hotkeys.c	2007-07-27 23:49:30.000000000 -0400
@@ -75,6 +75,7 @@
 
 #include "hotkeys.h"
 #include "conf.h"
+#include "amixer.h"
 
 #include <X11/Xmu/Error.h>
 
@@ -97,6 +98,7 @@
 int	            loglevel      = 0;
 Bool            background    = True;
 Bool            noSplash      = False;
+Bool		haveALSA      = False;
 
 FILE *          errorFile     = NULL;
 
@@ -340,7 +342,6 @@
     close (fd);
 }
 
-
 static void
 setLoglevel(int level)
 {
@@ -624,6 +625,19 @@
         last_time.tv_usec = this_time.tv_usec;
     }
 
+    if (haveALSA) {
+	master_vol = amixer(adj);
+        if (master_vol < 0)
+		return master_vol;
+#ifdef HAVE_LIBXOSD
+	if (osd) {
+        	xosd_display(osd, 0, XOSD_string, "Volume");
+        	xosd_display(osd, 1, XOSD_percentage, master_vol);
+        }
+#endif
+	return ret;
+    }
+
     /* open the mixer device */
     if ( (mixer_fd = open( MIXER_DEV, O_RDWR|O_NONBLOCK )) == -1 )
     {
@@ -745,6 +759,18 @@
 
     short ret = 0;      /* return value */
 
+    if (haveALSA)
+	ret = amixer(0);
+        if (ret >= 0 && osd) {
+		if (ret) {
+                    xosd_display(osd, 0, XOSD_string, "Mute");
+		}
+		else {
+                    xosd_display(osd, 0, XOSD_string, "Unmute");
+		}
+	}
+	return ret;
+
     /* open the mixer device */
     if ( (mixer_fd = open( MIXER_DEV, O_RDWR|O_NONBLOCK )) == -1 )
     {
@@ -1506,6 +1532,9 @@
     if ( !parseArgs(argc,argv) )
         bailout();
 
+    if ( configALSA(strdup(getConfig("ALSAcard")), strdup(getConfig("ALSAcontrols"))) == 0)
+	    haveALSA = True;
+
 #if HAVE_GTK
     if ( !noSplash )
     {
diff --new-file -ur old/hotkeys-0.5.7.4/src/hotkeys.conf hotkeys-0.5.7.4/src/hotkeys.conf
--- old/hotkeys-0.5.7.4/src/hotkeys.conf	2002-11-21 13:03:35.000000000 -0500
+++ hotkeys-0.5.7.4/src/hotkeys.conf	2007-07-27 12:01:08.000000000 -0400
@@ -10,6 +10,11 @@
 # Kbd=acerwl
 # CDROM=/dev/cdrom
 
+### ALSA card
+# ALSAcard=default
+### List of ALSA control names, separated by "|"
+# ALSAcontrols=Master
+
 # PrevTrack=xmms --rew
 # Play=xmms --play-pause
 # Stop=xmms --stop

Attachment: signature.asc
Description: Digital signature

Reply via email to