Index: configure.in
===================================================================
RCS file: /cvs/gnome/gnome-control-center/configure.in,v
retrieving revision 1.407
diff -u -r1.407 configure.in
--- configure.in	12 Nov 2003 21:11:02 -0000	1.407
+++ configure.in	4 Jan 2004 04:40:12 -0000
@@ -68,7 +68,7 @@
 COMMON_MODULES="gtk+-2.0 >= 2.2.0 gconf-2.0 libgnomeui-2.0 >= 2.2.0 libglade-2.0 >= 1.99.12 libbonobo-2.0 libbonoboui-2.0 libglade-2.0 gconf-2.0 gnome-desktop-2.0 >= 2.2.0"
 PKG_CHECK_MODULES(CAPPLET, $COMMON_MODULES)
 PKG_CHECK_MODULES(GNOMECC, $COMMON_MODULES libxml-2.0 gnome-desktop-2.0 gnome-vfs-2.0)
-PKG_CHECK_MODULES(GNOME_SETTINGS_DAEMON, gtk+-2.0 gconf-2.0 libgnomeui-2.0 esound gnome-desktop-2.0)
+PKG_CHECK_MODULES(GNOME_SETTINGS_DAEMON, gtk+-2.0 gconf-2.0 libgnomeui-2.0 esound gnome-desktop-2.0 libglade-2.0)
 PKG_CHECK_MODULES(OLD_CAPPLET, libgnomeui-2.0 libglade-2.0)
 PKG_CHECK_MODULES(GNOME, libgnomeui-2.0)
 PKG_CHECK_MODULES(VFS_CAPPLET, $COMMON_MODULES gnome-vfs-module-2.0 gnome-vfs-2.0)
@@ -269,6 +269,46 @@
 AM_GCONF_SOURCE_2
 
 dnl ==============================================
+dnl OSS and FB Level section
+dnl ==============================================
+have_oss=no
+AC_TRY_COMPILE([
+	#ifdef __NetBSD__
+	#include <soundcard.h>
+	#else
+	#include <sys/soundcard.h>
+	#endif
+	],[
+	int arg = SNDCTL_DSP_SETFRAGMENT;
+	],[
+	have_oss=yes
+])
+AC_MSG_CHECKING(for OSS audio support)
+AC_MSG_RESULT($have_oss)
+AM_CONDITIONAL(HAVE_OSS, test x"$have_oss" = "xyes")
+
+have_alsa=no
+PKG_CHECK_MODULES(ALSA, alsa >= 0.9.0,
+	have_alsa=yes,
+	AC_MSG_RESULT(*** All of ALSA dependent parts will be disabled ***))
+AM_CONDITIONAL(HAVE_ALSA, test x"$have_alsa" = "xyes")
+AC_SUBST(ALSA_LIBS)
+AC_SUBST(ALSA_CFLAGS)
+
+have_fb=no
+AC_TRY_COMPILE([
+	#include <linux/fb.h>
+	#include <linux/pmu.h>
+	],[
+	int arg = 0;
+	],[
+	have_fb=yes
+])
+AC_MSG_CHECKING(for Brightness control support)
+AC_MSG_RESULT($have_fb)
+AM_CONDITIONAL(HAVE_FB, test x"$have_fb" = "xyes")
+
+dnl ==============================================
 dnl Define the main variables
 dnl ==============================================
 COMMON_CFLAGS="-I\$(top_srcdir)/capplets/common"
@@ -387,6 +427,7 @@
 idl/Makefile
 gnome-settings-daemon/Makefile
 gnome-settings-daemon/xrdb/Makefile
+gnome-settings-daemon/multimedia-keys/Makefile
 control-center/Makefile
 libbackground/Makefile
 libwindow-settings/Makefile
Index: capplets/keybindings/gnome-keybinding-properties.c
===================================================================
RCS file: /cvs/gnome/gnome-control-center/capplets/keybindings/gnome-keybinding-properties.c,v
retrieving revision 1.36
diff -u -r1.36 gnome-keybinding-properties.c
--- capplets/keybindings/gnome-keybinding-properties.c	18 Nov 2003 17:23:06 -0000	1.36
+++ capplets/keybindings/gnome-keybinding-properties.c	4 Jan 2004 04:40:15 -0000
@@ -101,6 +101,39 @@
   { NULL }
 };
 
+#if defined(__powerpc__) && defined (__linux__)
+#define USE_FBLEVEL
+#else
+#undef USE_FBLEVEL
+#endif
+
+const KeyListEntry multimedia_key_list[] =
+{
+  { "/apps/gnome_settings_daemon/keybindings/volume_mute", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/volume_down", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/volume_up", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/power", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/eject", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/home", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/search", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/email", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/sleep", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/screensaver", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/help", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/www", ALWAYS_VISIBLE, 0 },
+#ifdef USE_FBLEVEL
+  { "/apps/gnome_settings_daemon/keybindings/brightness_down", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/brightness_up", ALWAYS_VISIBLE, 0 },
+#endif
+  /* Other ones that need keysyms bindings */
+  { "/apps/gnome_settings_daemon/keybindings/play", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/pause", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/stop", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/previous", ALWAYS_VISIBLE, 0 },
+  { "/apps/gnome_settings_daemon/keybindings/next", ALWAYS_VISIBLE, 0 },
+  { NULL }
+};
+
 enum
 {
   DESCRIPTION_COLUMN,
@@ -571,6 +604,8 @@
     {
       append_keys_to_tree (dialog, _("Window Management"), metacity_key_list);
     }
+
+  append_keys_to_tree (dialog, _("Multimedia keys"), multimedia_key_list);
 }
 
 static void
Index: gnome-settings-daemon/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-control-center/gnome-settings-daemon/Makefile.am,v
retrieving revision 1.34
diff -u -r1.34 Makefile.am
--- gnome-settings-daemon/Makefile.am	2 Dec 2003 00:15:27 -0000	1.34
+++ gnome-settings-daemon/Makefile.am	4 Jan 2004 04:40:16 -0000
@@ -1,12 +1,17 @@
-SUBDIRS = xrdb
+SUBDIRS = xrdb multimedia-keys
 
 INCLUDES=$(LIBXKLAVIER_CFLAGS) $(GNOME_SETTINGS_DAEMON_CFLAGS) -I$(top_srcdir)/libbackground -I$(top_srcdir) \
   -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
   -DESD_SERVER="\"$(ESD_SERVER)\""	\
-  -DDATADIR="\"$(datadir)\""
+  -DDATADIR="\"$(datadir)\""		\
+  -DACME_PIXMAPS="\"$(GNOMECC_PIXMAPS_DIR)/\"" \
+  -DACME_GLADE="\"$(datadir)/control-center-2.0/interfaces/\""
+  
 bin_PROGRAMS=gnome-settings-daemon
 
 gnome_settings_daemon_SOURCES =	\
+	eggaccelerators.h		\
+	eggaccelerators.c		\
 	factory.c			\
 	gnome-settings-daemon.h		\
 	gnome-settings-daemon.c		\
@@ -14,6 +19,8 @@
 	gnome-settings-font.c		\
 	gnome-settings-mouse.h		\
 	gnome-settings-mouse.c		\
+	gnome-settings-multimedia-keys.h \
+	gnome-settings-multimedia-keys.c \
 	gnome-settings-keyboard-xkb.h	\
 	gnome-settings-keyboard-xkb.c	\
 	gnome-settings-keyboard.h	\
@@ -57,6 +64,7 @@
 	$(top_builddir)/libsounds/libsounds.a				\
 	$(top_builddir)/libgswitchit/libgswitchit.a			\
 	$(top_builddir)/libwindow-settings/libgnome-window-settings.la 	\
+	$(top_builddir)/gnome-settings-daemon/multimedia-keys/libacme.la\
 	$(XF86MISC_LIBS)
 
 #AccessX_files = \
Index: gnome-settings-daemon/eggaccelerators.c
===================================================================
RCS file: gnome-settings-daemon/eggaccelerators.c
diff -N gnome-settings-daemon/eggaccelerators.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/eggaccelerators.c	4 Jan 2004 04:40:18 -0000
@@ -0,0 +1,701 @@
+/* eggaccelerators.c
+ * Copyright (C) 2002  Red Hat, Inc.; Copyright 1998, 2001 Tim Janik
+ * Developed by Havoc Pennington, Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "eggaccelerators.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+
+enum
+{
+  EGG_MODMAP_ENTRY_SHIFT   = 0,
+  EGG_MODMAP_ENTRY_LOCK    = 1,
+  EGG_MODMAP_ENTRY_CONTROL = 2,
+  EGG_MODMAP_ENTRY_MOD1    = 3,
+  EGG_MODMAP_ENTRY_MOD2    = 4,
+  EGG_MODMAP_ENTRY_MOD3    = 5,
+  EGG_MODMAP_ENTRY_MOD4    = 6,
+  EGG_MODMAP_ENTRY_MOD5    = 7,
+  EGG_MODMAP_ENTRY_LAST    = 8
+};
+
+#define MODMAP_ENTRY_TO_MODIFIER(x) (1 << (x))
+
+typedef struct
+{
+  EggVirtualModifierType mapping[EGG_MODMAP_ENTRY_LAST];
+
+} EggModmap;
+
+const EggModmap* egg_keymap_get_modmap (GdkKeymap *keymap);
+
+static inline gboolean
+is_alt (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'a' || string[1] == 'A') &&
+	  (string[2] == 'l' || string[2] == 'L') &&
+	  (string[3] == 't' || string[3] == 'T') &&
+	  (string[4] == '>'));
+}
+
+static inline gboolean
+is_ctl (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'c' || string[1] == 'C') &&
+	  (string[2] == 't' || string[2] == 'T') &&
+	  (string[3] == 'l' || string[3] == 'L') &&
+	  (string[4] == '>'));
+}
+
+static inline gboolean
+is_modx (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'm' || string[1] == 'M') &&
+	  (string[2] == 'o' || string[2] == 'O') &&
+	  (string[3] == 'd' || string[3] == 'D') &&
+	  (string[4] >= '1' && string[4] <= '5') &&
+	  (string[5] == '>'));
+}
+
+static inline gboolean
+is_ctrl (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'c' || string[1] == 'C') &&
+	  (string[2] == 't' || string[2] == 'T') &&
+	  (string[3] == 'r' || string[3] == 'R') &&
+	  (string[4] == 'l' || string[4] == 'L') &&
+	  (string[5] == '>'));
+}
+
+static inline gboolean
+is_shft (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 's' || string[1] == 'S') &&
+	  (string[2] == 'h' || string[2] == 'H') &&
+	  (string[3] == 'f' || string[3] == 'F') &&
+	  (string[4] == 't' || string[4] == 'T') &&
+	  (string[5] == '>'));
+}
+
+static inline gboolean
+is_shift (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 's' || string[1] == 'S') &&
+	  (string[2] == 'h' || string[2] == 'H') &&
+	  (string[3] == 'i' || string[3] == 'I') &&
+	  (string[4] == 'f' || string[4] == 'F') &&
+	  (string[5] == 't' || string[5] == 'T') &&
+	  (string[6] == '>'));
+}
+
+static inline gboolean
+is_control (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'c' || string[1] == 'C') &&
+	  (string[2] == 'o' || string[2] == 'O') &&
+	  (string[3] == 'n' || string[3] == 'N') &&
+	  (string[4] == 't' || string[4] == 'T') &&
+	  (string[5] == 'r' || string[5] == 'R') &&
+	  (string[6] == 'o' || string[6] == 'O') &&
+	  (string[7] == 'l' || string[7] == 'L') &&
+	  (string[8] == '>'));
+}
+
+static inline gboolean
+is_release (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'r' || string[1] == 'R') &&
+	  (string[2] == 'e' || string[2] == 'E') &&
+	  (string[3] == 'l' || string[3] == 'L') &&
+	  (string[4] == 'e' || string[4] == 'E') &&
+	  (string[5] == 'a' || string[5] == 'A') &&
+	  (string[6] == 's' || string[6] == 'S') &&
+	  (string[7] == 'e' || string[7] == 'E') &&
+	  (string[8] == '>'));
+}
+
+static inline gboolean
+is_meta (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'm' || string[1] == 'M') &&
+	  (string[2] == 'e' || string[2] == 'E') &&
+	  (string[3] == 't' || string[3] == 'T') &&
+	  (string[4] == 'a' || string[4] == 'A') &&
+	  (string[5] == '>'));
+}
+
+static inline gboolean
+is_super (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 's' || string[1] == 'S') &&
+	  (string[2] == 'u' || string[2] == 'U') &&
+	  (string[3] == 'p' || string[3] == 'P') &&
+	  (string[4] == 'e' || string[4] == 'E') &&
+	  (string[5] == 'r' || string[5] == 'R') &&
+	  (string[6] == '>'));
+}
+
+static inline gboolean
+is_hyper (const gchar *string)
+{
+  return ((string[0] == '<') &&
+	  (string[1] == 'h' || string[1] == 'H') &&
+	  (string[2] == 'y' || string[2] == 'Y') &&
+	  (string[3] == 'p' || string[3] == 'P') &&
+	  (string[4] == 'e' || string[4] == 'E') &&
+	  (string[5] == 'r' || string[5] == 'R') &&
+	  (string[6] == '>'));
+}
+
+static inline gboolean
+is_keycode (const gchar *string)
+{
+  return ((string[0] == '0') &&
+	  (string[1] == 'x'));
+}
+
+/**
+ * egg_accelerator_parse_virtual:
+ * @accelerator:      string representing an accelerator
+ * @accelerator_key:  return location for accelerator keyval
+ * @accelerator_mods: return location for accelerator modifier mask
+ *
+ * Parses a string representing a virtual accelerator. The format
+ * looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
+ * "&lt;Release&gt;z" (the last one is for key release).  The parser
+ * is fairly liberal and allows lower or upper case, and also
+ * abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
+ *
+ * If the parse fails, @accelerator_key and @accelerator_mods will
+ * be set to 0 (zero) and %FALSE will be returned. If the string contains
+ * only modifiers, @accelerator_key will be set to 0 but %TRUE will be
+ * returned.
+ *
+ * The virtual vs. concrete accelerator distinction is a relic of
+ * how the X Window System works; there are modifiers Mod2-Mod5 that
+ * can represent various keyboard keys (numlock, meta, hyper, etc.),
+ * the virtual modifier represents the keyboard key, the concrete
+ * modifier the actual Mod2-Mod5 bits in the key press event.
+ * 
+ * Returns: %TRUE on success.
+ */
+gboolean
+egg_accelerator_parse_virtual (const gchar            *accelerator,
+                               guint                  *accelerator_key,
+			       guint                  *keycode,
+                               EggVirtualModifierType *accelerator_mods)
+{
+  guint keyval;
+  GdkModifierType mods;
+  gint len;
+  gboolean bad_keyval;
+  
+  if (accelerator_key)
+    *accelerator_key = 0;
+  if (accelerator_mods)
+    *accelerator_mods = 0;
+  if (keycode)
+    *keycode = 0;
+
+  g_return_val_if_fail (accelerator != NULL, FALSE);
+
+  bad_keyval = FALSE;
+  
+  keyval = 0;
+  mods = 0;
+  len = strlen (accelerator);
+  while (len)
+    {
+      if (*accelerator == '<')
+	{
+	  if (len >= 9 && is_release (accelerator))
+	    {
+	      accelerator += 9;
+	      len -= 9;
+	      mods |= EGG_VIRTUAL_RELEASE_MASK;
+	    }
+	  else if (len >= 9 && is_control (accelerator))
+	    {
+	      accelerator += 9;
+	      len -= 9;
+	      mods |= EGG_VIRTUAL_CONTROL_MASK;
+	    }
+	  else if (len >= 7 && is_shift (accelerator))
+	    {
+	      accelerator += 7;
+	      len -= 7;
+	      mods |= EGG_VIRTUAL_SHIFT_MASK;
+	    }
+	  else if (len >= 6 && is_shft (accelerator))
+	    {
+	      accelerator += 6;
+	      len -= 6;
+	      mods |= EGG_VIRTUAL_SHIFT_MASK;
+	    }
+	  else if (len >= 6 && is_ctrl (accelerator))
+	    {
+	      accelerator += 6;
+	      len -= 6;
+	      mods |= EGG_VIRTUAL_CONTROL_MASK;
+	    }
+	  else if (len >= 6 && is_modx (accelerator))
+	    {
+	      static const guint mod_vals[] = {
+		EGG_VIRTUAL_ALT_MASK, EGG_VIRTUAL_MOD2_MASK, EGG_VIRTUAL_MOD3_MASK,
+		EGG_VIRTUAL_MOD4_MASK, EGG_VIRTUAL_MOD5_MASK
+	      };
+
+	      len -= 6;
+	      accelerator += 4;
+	      mods |= mod_vals[*accelerator - '1'];
+	      accelerator += 2;
+	    }
+	  else if (len >= 5 && is_ctl (accelerator))
+	    {
+	      accelerator += 5;
+	      len -= 5;
+	      mods |= EGG_VIRTUAL_CONTROL_MASK;
+	    }
+	  else if (len >= 5 && is_alt (accelerator))
+	    {
+	      accelerator += 5;
+	      len -= 5;
+	      mods |= EGG_VIRTUAL_ALT_MASK;
+	    }
+          else if (len >= 6 && is_meta (accelerator))
+	    {
+	      accelerator += 6;
+	      len -= 6;
+	      mods |= EGG_VIRTUAL_META_MASK;
+	    }
+          else if (len >= 7 && is_hyper (accelerator))
+	    {
+	      accelerator += 7;
+	      len -= 7;
+	      mods |= EGG_VIRTUAL_HYPER_MASK;
+	    }
+          else if (len >= 7 && is_super (accelerator))
+	    {
+	      accelerator += 7;
+	      len -= 7;
+	      mods |= EGG_VIRTUAL_SUPER_MASK;
+	    }
+	  else
+	    {
+	      gchar last_ch;
+	      
+	      last_ch = *accelerator;
+	      while (last_ch && last_ch != '>')
+		{
+		  last_ch = *accelerator;
+		  accelerator += 1;
+		  len -= 1;
+		}
+	    }
+	}
+      else
+	{
+          keyval = gdk_keyval_from_name (accelerator);
+
+          if (keyval == 0)
+	    {
+	      /* If keyval is 0, than maybe it's a keycode.  Check for 0x## */
+	      if (len >= 4 && is_keycode (accelerator))
+		{
+		  char keystring[5];
+		  gchar *endptr;
+		  gint tmp_keycode;
+
+		  memcpy (keystring, accelerator, 4);
+		  keystring [4] = '\000';
+
+		  tmp_keycode = strtol (keystring, &endptr, 16);
+
+		  if (endptr == NULL || *endptr != '\000')
+		    {
+		      bad_keyval = TRUE;
+		    }
+		  else
+		    {
+		      *keycode = tmp_keycode;
+		      /* 0x00 is an invalid keycode too. */
+		      if (*keycode == 0)
+			bad_keyval = TRUE;
+		    }
+		}
+	    }
+
+          accelerator += len;
+          len -= len;
+	}
+    }
+  
+  if (accelerator_key)
+    *accelerator_key = gdk_keyval_to_lower (keyval);
+  if (accelerator_mods)
+    *accelerator_mods = mods;
+
+  return !bad_keyval;
+}
+
+
+/**
+ * egg_virtual_accelerator_name:
+ * @accelerator_key:  accelerator keyval
+ * @accelerator_mods: accelerator modifier mask
+ * @returns:          a newly-allocated accelerator name
+ * 
+ * Converts an accelerator keyval and modifier mask
+ * into a string parseable by egg_accelerator_parse_virtual().
+ * For example, if you pass in #GDK_q and #EGG_VIRTUAL_CONTROL_MASK,
+ * this function returns "&lt;Control&gt;q".
+ *
+ * The caller of this function must free the returned string.
+ */
+gchar*
+egg_virtual_accelerator_name (guint                  accelerator_key,
+			      guint		     keycode,
+                              EggVirtualModifierType accelerator_mods)
+{
+  static const gchar text_release[] = "<Release>";
+  static const gchar text_shift[] = "<Shift>";
+  static const gchar text_control[] = "<Control>";
+  static const gchar text_mod1[] = "<Alt>";
+  static const gchar text_mod2[] = "<Mod2>";
+  static const gchar text_mod3[] = "<Mod3>";
+  static const gchar text_mod4[] = "<Mod4>";
+  static const gchar text_mod5[] = "<Mod5>";
+  static const gchar text_meta[] = "<Meta>";
+  static const gchar text_super[] = "<Super>";
+  static const gchar text_hyper[] = "<Hyper>";
+  guint l;
+  gchar *keyval_name;
+  gchar *accelerator;
+
+  accelerator_mods &= EGG_VIRTUAL_MODIFIER_MASK;
+
+  if (!accelerator_key)
+    {
+      keyval_name = g_strdup_printf ("0x%02x", keycode);
+    }
+  else
+    {
+      keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
+      if (!keyval_name)
+        keyval_name = "";
+    }
+
+  l = 0;
+  if (accelerator_mods & EGG_VIRTUAL_RELEASE_MASK)
+    l += sizeof (text_release) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_SHIFT_MASK)
+    l += sizeof (text_shift) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_CONTROL_MASK)
+    l += sizeof (text_control) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_ALT_MASK)
+    l += sizeof (text_mod1) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_MOD2_MASK)
+    l += sizeof (text_mod2) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_MOD3_MASK)
+    l += sizeof (text_mod3) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_MOD4_MASK)
+    l += sizeof (text_mod4) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_MOD5_MASK)
+    l += sizeof (text_mod5) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_META_MASK)
+    l += sizeof (text_meta) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_HYPER_MASK)
+    l += sizeof (text_hyper) - 1;
+  if (accelerator_mods & EGG_VIRTUAL_SUPER_MASK)
+    l += sizeof (text_super) - 1;
+  l += strlen (keyval_name);
+
+  accelerator = g_new (gchar, l + 1);
+
+  l = 0;
+  accelerator[l] = 0;
+  if (accelerator_mods & EGG_VIRTUAL_RELEASE_MASK)
+    {
+      strcpy (accelerator + l, text_release);
+      l += sizeof (text_release) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_SHIFT_MASK)
+    {
+      strcpy (accelerator + l, text_shift);
+      l += sizeof (text_shift) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_CONTROL_MASK)
+    {
+      strcpy (accelerator + l, text_control);
+      l += sizeof (text_control) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_ALT_MASK)
+    {
+      strcpy (accelerator + l, text_mod1);
+      l += sizeof (text_mod1) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_MOD2_MASK)
+    {
+      strcpy (accelerator + l, text_mod2);
+      l += sizeof (text_mod2) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_MOD3_MASK)
+    {
+      strcpy (accelerator + l, text_mod3);
+      l += sizeof (text_mod3) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_MOD4_MASK)
+    {
+      strcpy (accelerator + l, text_mod4);
+      l += sizeof (text_mod4) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_MOD5_MASK)
+    {
+      strcpy (accelerator + l, text_mod5);
+      l += sizeof (text_mod5) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_META_MASK)
+    {
+      strcpy (accelerator + l, text_meta);
+      l += sizeof (text_meta) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_HYPER_MASK)
+    {
+      strcpy (accelerator + l, text_hyper);
+      l += sizeof (text_hyper) - 1;
+    }
+  if (accelerator_mods & EGG_VIRTUAL_SUPER_MASK)
+    {
+      strcpy (accelerator + l, text_super);
+      l += sizeof (text_super) - 1;
+    }
+  
+  strcpy (accelerator + l, keyval_name);
+
+  return accelerator;
+}
+
+void
+egg_keymap_resolve_virtual_modifiers (GdkKeymap              *keymap,
+                                      EggVirtualModifierType  virtual_mods,
+                                      GdkModifierType        *concrete_mods)
+{
+  GdkModifierType concrete;
+  int i;
+  const EggModmap *modmap;
+
+  g_return_if_fail (GDK_IS_KEYMAP (keymap));
+  g_return_if_fail (concrete_mods != NULL);
+  
+  modmap = egg_keymap_get_modmap (keymap);
+  
+  /* Not so sure about this algorithm. */
+  
+  concrete = 0;
+  i = 0;
+  while (i < EGG_MODMAP_ENTRY_LAST)
+    {
+      if (modmap->mapping[i] & virtual_mods)
+        concrete |= (1 << i);
+
+      ++i;
+    }
+
+  *concrete_mods = concrete;
+}
+
+void
+egg_keymap_virtualize_modifiers (GdkKeymap              *keymap,
+                                 GdkModifierType         concrete_mods,
+                                 EggVirtualModifierType *virtual_mods)
+{
+  GdkModifierType virtual;
+  int i;
+  const EggModmap *modmap;
+  
+  g_return_if_fail (GDK_IS_KEYMAP (keymap));
+  g_return_if_fail (virtual_mods != NULL);
+
+  modmap = egg_keymap_get_modmap (keymap);
+  
+  /* Not so sure about this algorithm. */
+  
+  virtual = 0;
+  i = 0;
+  while (i < EGG_MODMAP_ENTRY_LAST)
+    {
+      if ((1 << i) & concrete_mods)
+        {
+          EggVirtualModifierType cleaned;
+          
+          cleaned = modmap->mapping[i] & ~(EGG_VIRTUAL_MOD2_MASK |
+                                           EGG_VIRTUAL_MOD3_MASK |
+                                           EGG_VIRTUAL_MOD4_MASK |
+                                           EGG_VIRTUAL_MOD5_MASK);
+          
+          if (cleaned != 0)
+            {
+              virtual |= cleaned;
+            }
+          else
+            {
+              /* Rather than dropping mod2->mod5 if not bound,
+               * go ahead and use the concrete names
+               */
+              virtual |= modmap->mapping[i];
+            }
+        }
+      
+      ++i;
+    }
+  
+  *virtual_mods = virtual;
+}
+
+static void
+reload_modmap (GdkKeymap *keymap,
+               EggModmap *modmap)
+{
+  XModifierKeymap *xmodmap;
+  int map_size;
+  int i;
+
+  /* FIXME multihead */
+  xmodmap = XGetModifierMapping (gdk_x11_get_default_xdisplay ());
+
+  memset (modmap->mapping, 0, sizeof (modmap->mapping));
+  
+  /* there are 8 modifiers, and the first 3 are shift, shift lock,
+   * and control
+   */
+  map_size = 8 * xmodmap->max_keypermod;
+  i = 3 * xmodmap->max_keypermod;
+  while (i < map_size)
+    {
+      /* get the key code at this point in the map,
+       * see if its keysym is one we're interested in
+       */
+      int keycode = xmodmap->modifiermap[i];
+      GdkKeymapKey *keys;
+      guint *keyvals;
+      int n_entries;
+      int j;
+      EggVirtualModifierType mask;
+      
+      keys = NULL;
+      keyvals = NULL;
+      n_entries = 0;
+
+      gdk_keymap_get_entries_for_keycode (keymap,
+                                          keycode,
+                                          &keys, &keyvals, &n_entries);
+      
+      mask = 0;
+      j = 0;
+      while (j < n_entries)
+        {          
+          if (keyvals[j] == GDK_Num_Lock)
+            mask |= EGG_VIRTUAL_NUM_LOCK_MASK;
+          else if (keyvals[j] == GDK_Scroll_Lock)
+            mask |= EGG_VIRTUAL_SCROLL_LOCK_MASK;
+          else if (keyvals[j] == GDK_Meta_L ||
+                   keyvals[j] == GDK_Meta_R)
+            mask |= EGG_VIRTUAL_META_MASK;
+          else if (keyvals[j] == GDK_Hyper_L ||
+                   keyvals[j] == GDK_Hyper_R)
+            mask |= EGG_VIRTUAL_HYPER_MASK;
+          else if (keyvals[j] == GDK_Super_L ||
+                   keyvals[j] == GDK_Super_R)
+            mask |= EGG_VIRTUAL_SUPER_MASK;
+          else if (keyvals[j] == GDK_Mode_switch)
+            mask |= EGG_VIRTUAL_MODE_SWITCH_MASK;
+          
+          ++j;
+        }
+
+      /* Mod1Mask is 1 << 3 for example, i.e. the
+       * fourth modifier, i / keyspermod is the modifier
+       * index
+       */      
+      modmap->mapping[i/xmodmap->max_keypermod] |= mask;
+      
+      g_free (keyvals);
+      g_free (keys);      
+      
+      ++i;
+    }
+
+  /* Add in the not-really-virtual fixed entries */
+  modmap->mapping[EGG_MODMAP_ENTRY_SHIFT] |= EGG_VIRTUAL_SHIFT_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_CONTROL] |= EGG_VIRTUAL_CONTROL_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_LOCK] |= EGG_VIRTUAL_LOCK_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_MOD1] |= EGG_VIRTUAL_ALT_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_MOD2] |= EGG_VIRTUAL_MOD2_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_MOD3] |= EGG_VIRTUAL_MOD3_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_MOD4] |= EGG_VIRTUAL_MOD4_MASK;
+  modmap->mapping[EGG_MODMAP_ENTRY_MOD5] |= EGG_VIRTUAL_MOD5_MASK;
+  
+  XFreeModifiermap (xmodmap);
+}
+
+const EggModmap*
+egg_keymap_get_modmap (GdkKeymap *keymap)
+{
+  EggModmap *modmap;
+
+  /* This is all a hack, much simpler when we can just
+   * modify GDK directly.
+   */
+  
+  modmap = g_object_get_data (G_OBJECT (keymap),
+                              "egg-modmap");
+
+  if (modmap == NULL)
+    {
+      modmap = g_new0 (EggModmap, 1);
+
+      /* FIXME modify keymap change events with an event filter
+       * and force a reload if we get one
+       */
+      
+      reload_modmap (keymap, modmap);
+      
+      g_object_set_data_full (G_OBJECT (keymap),
+                              "egg-modmap",
+                              modmap,
+                              g_free);
+    }
+
+  g_assert (modmap != NULL);
+  
+  return modmap;
+}
Index: gnome-settings-daemon/eggaccelerators.h
===================================================================
RCS file: gnome-settings-daemon/eggaccelerators.h
diff -N gnome-settings-daemon/eggaccelerators.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/eggaccelerators.h	4 Jan 2004 04:40:18 -0000
@@ -0,0 +1,89 @@
+/* eggaccelerators.h
+ * Copyright (C) 2002  Red Hat, Inc.
+ * Developed by Havoc Pennington
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_ACCELERATORS_H__
+#define __EGG_ACCELERATORS_H__
+
+#include <gtk/gtkaccelgroup.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+/* Where a value is also in GdkModifierType we coincide,
+ * otherwise we don't overlap.
+ */
+typedef enum
+{
+  EGG_VIRTUAL_SHIFT_MASK    = 1 << 0,
+  EGG_VIRTUAL_LOCK_MASK	    = 1 << 1,
+  EGG_VIRTUAL_CONTROL_MASK  = 1 << 2,
+
+  EGG_VIRTUAL_ALT_MASK      = 1 << 3, /* fixed as Mod1 */
+  
+  EGG_VIRTUAL_MOD2_MASK	    = 1 << 4,
+  EGG_VIRTUAL_MOD3_MASK	    = 1 << 5,
+  EGG_VIRTUAL_MOD4_MASK	    = 1 << 6,
+  EGG_VIRTUAL_MOD5_MASK	    = 1 << 7,
+
+#if 0
+  GDK_BUTTON1_MASK  = 1 << 8,
+  GDK_BUTTON2_MASK  = 1 << 9,
+  GDK_BUTTON3_MASK  = 1 << 10,
+  GDK_BUTTON4_MASK  = 1 << 11,
+  GDK_BUTTON5_MASK  = 1 << 12,
+  /* 13, 14 are used by Xkb for the keyboard group */
+#endif
+  
+  EGG_VIRTUAL_META_MASK = 1 << 24,
+  EGG_VIRTUAL_SUPER_MASK = 1 << 25,
+  EGG_VIRTUAL_HYPER_MASK = 1 << 26,
+  EGG_VIRTUAL_MODE_SWITCH_MASK = 1 << 27, 
+  EGG_VIRTUAL_NUM_LOCK_MASK = 1 << 28,
+  EGG_VIRTUAL_SCROLL_LOCK_MASK = 1 << 29,
+
+  /* Also in GdkModifierType */
+  EGG_VIRTUAL_RELEASE_MASK  = 1 << 30,
+
+  /*     28-31 24-27 20-23 16-19 12-15 8-11 4-7 0-3
+   *       7     f     0     0     0    0    f   f
+   */  
+  EGG_VIRTUAL_MODIFIER_MASK = 0x7f0000ff
+
+} EggVirtualModifierType;
+
+gboolean egg_accelerator_parse_virtual        (const gchar            *accelerator,
+                                               guint                  *accelerator_key,
+					       guint                  *keycode,
+                                               EggVirtualModifierType *accelerator_mods);
+void     egg_keymap_resolve_virtual_modifiers (GdkKeymap              *keymap,
+                                               EggVirtualModifierType  virtual_mods,
+                                               GdkModifierType        *concrete_mods);
+void     egg_keymap_virtualize_modifiers      (GdkKeymap              *keymap,
+                                               GdkModifierType         concrete_mods,
+                                               EggVirtualModifierType *virtual_mods);
+
+gchar* egg_virtual_accelerator_name (guint                  accelerator_key,
+				     guint		    keycode,
+                                     EggVirtualModifierType accelerator_mods);
+
+G_END_DECLS
+
+
+#endif /* __EGG_ACCELERATORS_H__ */
Index: gnome-settings-daemon/gnome-settings-daemon.c
===================================================================
RCS file: /cvs/gnome/gnome-control-center/gnome-settings-daemon/gnome-settings-daemon.c,v
retrieving revision 1.32
diff -u -r1.32 gnome-settings-daemon.c
--- gnome-settings-daemon/gnome-settings-daemon.c	12 Nov 2003 21:58:51 -0000	1.32
+++ gnome-settings-daemon/gnome-settings-daemon.c	4 Jan 2004 04:40:19 -0000
@@ -52,6 +52,7 @@
 #include "gnome-settings-screensaver.h"
 #include "gnome-settings-default-editor.h"
 #include "gnome-settings-keybindings.h"
+#include "gnome-settings-multimedia-keys.h"
 #include "gnome-settings-gtk1theme.h"
 #include "gnome-settings-xrdb.h"
 #include "gnome-settings-typing-break.h"
@@ -276,6 +277,7 @@
 /* Essential - xkb initialization should happen before */
   gnome_settings_keyboard_xkb_init (client);
   gnome_settings_keyboard_init (client);
+  gnome_settings_multimedia_keys_init (client);
 /* */
   gnome_settings_sound_init (client);
   gnome_settings_accessibility_keyboard_init (client);
@@ -329,6 +331,7 @@
 /* Essential - xkb initialization should happen before */
   gnome_settings_keyboard_xkb_load (client);
   gnome_settings_keyboard_load (client);
+  gnome_settings_multimedia_keys_load (client);
 /* */
   gnome_settings_sound_load (client);
   gnome_settings_accessibility_keyboard_load (client);
Index: gnome-settings-daemon/gnome-settings-keybindings.c
===================================================================
RCS file: /cvs/gnome/gnome-control-center/gnome-settings-daemon/gnome-settings-keybindings.c,v
retrieving revision 1.5
diff -u -r1.5 gnome-settings-keybindings.c
--- gnome-settings-daemon/gnome-settings-keybindings.c	3 Jul 2003 21:39:23 -0000	1.5
+++ gnome-settings-daemon/gnome-settings-keybindings.c	4 Jan 2004 04:40:19 -0000
@@ -8,6 +8,7 @@
 #include <libgnome/gnome-i18n.h>
 #include "gnome-settings-daemon.h"
 #include "gnome-settings-keybindings.h"
+#include "eggaccelerators.h"
 
 /* we exclude shift, GDK_CONTROL_MASK and GDK_MOD1_MASK since we know what 
    these modifiers mean 
@@ -117,13 +118,9 @@
       strcmp (binding->binding_str, "Disabled") == 0)
           return FALSE;
 
-  gtk_accelerator_parse (binding->binding_str, &binding->key.keysym, &binding->key.state);
-
-  if (binding->key.keysym == 0)
+  if (egg_accelerator_parse_virtual (binding->binding_str, &binding->key.keysym, &binding->key.keycode, &binding->key.state) == FALSE)
           return FALSE;
 
-  binding->key.keycode = XKeysymToKeycode (GDK_DISPLAY (), binding->key.keysym);
-
   return TRUE;
 }
 
Index: gnome-settings-daemon/gnome-settings-keybindings.h
===================================================================
RCS file: /cvs/gnome/gnome-control-center/gnome-settings-daemon/gnome-settings-keybindings.h,v
retrieving revision 1.1
diff -u -r1.1 gnome-settings-keybindings.h
--- gnome-settings-daemon/gnome-settings-keybindings.h	30 May 2002 00:25:29 -0000	1.1
+++ gnome-settings-daemon/gnome-settings-keybindings.h	4 Jan 2004 04:40:19 -0000
@@ -1,4 +1,4 @@
-/* gnome-settings-sound.h
+/* gnome-settings-keybindings.h
  *
  * Copyright (C) 2002 Sun Microsystems Inc.
  *
Index: gnome-settings-daemon/gnome-settings-multimedia-keys.c
===================================================================
RCS file: gnome-settings-daemon/gnome-settings-multimedia-keys.c
diff -N gnome-settings-daemon/gnome-settings-multimedia-keys.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/gnome-settings-multimedia-keys.c	4 Jan 2004 04:40:21 -0000
@@ -0,0 +1,962 @@
+/* ACME
+ * Copyright (C) 2001,2002,2003 Bastien Nocera <hadess@hadess.net>
+ *
+ * acme.c
+ *
+ * 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 <config.h>
+
+#include <sys/file.h>
+#include <X11/X.h>
+
+/* Gnome headers */
+#include <gdk/gdkx.h>
+#include <gnome.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
+#include "eggaccelerators.h"
+
+#include "multimedia-keys/acme.h"
+#include "multimedia-keys/acme-volume.h"
+
+#define DIALOG_TIMEOUT 1000     /* dialog timeout in ms */
+#define VOLUME_STEP 6           /* percents for one volume button press */
+                                                                                
+#if defined(__powerpc__) && defined (__linux__)
+#define USE_FBLEVEL
+#include "multimedia-keys/acme-fb-level.h"
+#else
+#undef USE_FBLEVEL
+#endif
+
+/* we exclude shift, GDK_CONTROL_MASK and GDK_MOD1_MASK since we know what
+   these modifiers mean
+   these are the mods whose combinations are bound by the keygrabbing code */
+#define IGNORED_MODS (0x2000 /*Xkb modifier*/ | GDK_LOCK_MASK  | \
+	GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK)
+/* these are the ones we actually use for global keys, we always only check
+ * for these set */
+#define USED_MODS (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)
+
+typedef struct {
+	AcmeVolume *volobj;
+#ifdef USE_FBLEVEL
+	AcmeFblevel *levobj;
+#endif
+	GladeXML *xml;
+	GtkWidget *dialog;
+	GConfClient *conf_client;
+	guint dialog_timeout;
+
+	/* Multihead stuff */
+	GdkScreen *current_screen;
+	GSList *screens;
+} Acme;
+
+enum {
+	ICON_MUTED,
+	ICON_LOUD,
+	ICON_BRIGHT,
+	ICON_EJECT,
+};
+
+static void
+acme_error (char * msg)
+{
+	GtkWidget *error_dialog;
+
+	error_dialog =
+	    gtk_message_dialog_new (NULL,
+			    GTK_DIALOG_MODAL,
+			    GTK_MESSAGE_ERROR,
+			    GTK_BUTTONS_OK,
+			    "%s", msg);
+	gtk_dialog_set_default_response (GTK_DIALOG (error_dialog),
+			GTK_RESPONSE_OK);
+	gtk_widget_show (error_dialog);
+	gtk_dialog_run (GTK_DIALOG (error_dialog));
+	gtk_widget_destroy (error_dialog);
+}
+
+static void
+execute (char *cmd, gboolean sync)
+{
+	gboolean retval;
+
+	if (sync != FALSE)
+		retval = g_spawn_command_line_sync
+			(cmd, NULL, NULL, NULL, NULL);
+	else
+		retval = g_spawn_command_line_async (cmd, NULL);
+
+	if (retval == FALSE)
+	{
+		char *msg;
+
+		msg = g_strdup_printf
+			(_("Couldn't execute command: %s\n"
+			   "Verify that this command exists."),
+			 cmd);
+
+		acme_error (msg);
+		g_free (msg);
+	}
+}
+
+static void
+do_sleep_action (char *cmd1, char *cmd2)
+{
+	if (g_spawn_command_line_async (cmd1, NULL) == FALSE)
+	{
+		if (g_spawn_command_line_async (cmd2, NULL) == FALSE)
+		{
+			acme_error (_("Couldn't put the machine to sleep.\n"
+					"Verify that the machine is correctly configured."));
+		}
+	}
+}
+
+#ifdef USE_FBLEVEL
+static char*
+permission_problem_string (const char *file)
+{
+	return g_strdup_printf (_("Permissions on the file %s are broken\n"
+				"Please check ACME's documentation, correct "
+				"the problem and restart ACME."), file);
+}
+
+static void
+fblevel_problem_cb (void)
+{
+	char *msg;
+
+	msg = permission_problem_string ("/dev/pmu");
+	acme_error (msg);
+	g_free (msg);
+
+	return;
+}
+#endif
+
+static char *images[] = {
+	ACME_PIXMAPS "gnome-speakernotes-muted.png",
+	ACME_PIXMAPS "gnome-speakernotes.png",
+	ACME_PIXMAPS "acme-brightness.png",
+	ACME_PIXMAPS "acme-eject.png",
+};
+
+static void
+acme_image_set (Acme *acme, int icon)
+{
+	GtkWidget *image;
+
+	image = glade_xml_get_widget (acme->xml, "image1");
+	g_return_if_fail (image != NULL);
+
+	if (icon > ICON_EJECT)
+		g_assert_not_reached ();
+
+	gtk_image_set_from_file (GTK_IMAGE(image), images[icon]);
+}
+
+static gboolean
+grab_key_real (Key *key, GdkWindow *root, gboolean grab, int result)
+{
+	gdk_error_trap_push ();
+	if (grab)
+		XGrabKey (GDK_DISPLAY(), key->keycode, (result | key->state),
+				GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
+	else
+		XUngrabKey(GDK_DISPLAY(), key->keycode, (result | key->state),
+				GDK_WINDOW_XID (root));
+	gdk_flush ();
+
+	gdk_error_trap_pop ();
+
+	return TRUE;
+}
+
+/* inspired from all_combinations from gnome-panel/gnome-panel/global-keys.c */
+#define N_BITS 32
+static void
+grab_key (Acme *acme, Key *key, gboolean grab)
+{
+	int indexes[N_BITS];/*indexes of bits we need to flip*/
+	int i, bit, bits_set_cnt;
+	int uppervalue;
+	guint mask_to_traverse = IGNORED_MODS & ~ key->state;
+
+	bit = 0;
+	for (i = 0; i < N_BITS; i++) {
+		if (mask_to_traverse & (1<<i))
+			indexes[bit++]=i;
+	}
+
+	bits_set_cnt = bit;
+
+	uppervalue = 1<<bits_set_cnt;
+	for (i = 0; i < uppervalue; i++) {
+		GSList *l;
+		int j, result = 0;
+
+		for (j = 0; j < bits_set_cnt; j++) {
+			if (i & (1<<j))
+				result |= (1<<indexes[j]);
+		}
+
+		for (l = acme->screens; l ; l = l->next) {
+			GdkScreen *screen = l->data;
+			if (grab_key_real (key, gdk_screen_get_root_window (screen), grab, result) == FALSE)
+				return;
+		}
+	}
+}
+
+static void
+unhookup_keysym (int keycode)
+{
+	char *command;
+
+	if (keycode <= 0)
+		return;
+
+	command = g_strdup_printf ("xmodmap -e \"keycode %d = \"", keycode);
+
+	g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL);
+	g_free (command);
+}
+
+static gboolean
+hookup_keysym (int keycode, const char *keysym)
+{
+	char *command;
+
+	if (keycode <= 0)
+		return TRUE;
+
+	command = g_strdup_printf ("xmodmap -e \"keycode %d = %s\"",
+			keycode, keysym);
+
+	g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL);
+	g_free (command);
+
+	return FALSE;
+}
+
+static gboolean
+is_valid_shortcut (const char *string)
+{
+	if (string == NULL || string[0] == '\0')
+		return FALSE;
+	if (strcmp (string, "disabled") == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+update_kbd_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data)
+{
+	Acme *acme = (Acme *) data;
+	int i;
+	gboolean found = FALSE;
+
+	g_return_if_fail (entry->key != NULL);
+
+	/* Find the key that was modified */
+	for (i = 0; i < PLAY_KEY; i++)
+	{
+		if (strcmp (entry->key, keys[i].gconf_key) == 0)
+		{
+			const char *tmp;
+			Key *key;
+
+			found = TRUE;
+
+			if (keys[i].key != NULL)
+				grab_key (acme, keys[i].key, FALSE);
+
+			g_free (keys[i].key);
+			keys[i].key = NULL;
+
+			tmp = gconf_client_get_string (acme->conf_client,
+					keys[i].gconf_key, NULL);
+
+			if (is_valid_shortcut (tmp) == FALSE)
+				break;
+
+			key = g_new0 (Key, 1);
+			if (egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycode, &key->state) == FALSE)
+			{
+				g_free (key);
+				break;
+			}
+
+			grab_key (acme, key, TRUE);
+			keys[i].key = key;
+
+			break;
+		}
+	}
+
+	if (found != FALSE)
+		return;
+
+	for (i = PLAY_KEY; i < HANDLED_KEYS; i++)
+	{
+		if (strcmp (entry->key, keys[i].gconf_key) == 0)
+		{
+			const char *tmp;
+			Key *key;
+
+			if (keys[i].key != NULL)
+				unhookup_keysym (keys[i].key->keycode);
+
+			g_free (keys[i].key);
+			keys[i].key = NULL;
+
+			tmp = gconf_client_get_string (acme->conf_client,
+					keys[i].gconf_key, NULL);
+
+			if (is_valid_shortcut (tmp) == FALSE)
+				break;
+
+			key = g_new0 (Key, 1);
+			if (egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycode, &key->state) == FALSE)
+			{
+				g_free (key);
+				break;
+			}
+
+			switch (keys[i].key_type) {
+			case PLAY_KEY:
+				hookup_keysym (key->keycode, "XF86AudioPlay");
+				break;
+			case PAUSE_KEY:
+				hookup_keysym (key->keycode, "XF86AudioPause");
+				break;
+			case STOP_KEY:
+				hookup_keysym (key->keycode, "XF86AudioStop");
+				break;
+			case PREVIOUS_KEY:
+				hookup_keysym (key->keycode, "XF86AudioPrev");
+				break;
+			case NEXT_KEY:
+				hookup_keysym (key->keycode, "XF86AudioNext");
+				break;
+			}
+
+			keys[i].key = key;
+		}
+	}
+}
+
+static void
+init_screens (Acme *acme)
+{
+	GdkDisplay *display = gdk_display_get_default ();
+	int i;
+
+	if (gdk_display_get_n_screens (display) == 1) {
+		GdkScreen *screen = gdk_screen_get_default ();
+		acme->screens = g_slist_append (acme->screens, screen);
+		acme->current_screen = screen;
+		return;
+	}
+
+	for (i = 0; i < gdk_display_get_n_screens (display); i++)
+	{
+		GdkScreen *screen;
+
+		screen = gdk_display_get_screen (display, i);
+		if (screen == NULL)
+			continue;
+		acme->screens = g_slist_append (acme->screens, screen);
+	}
+
+	acme->current_screen = (GdkScreen *)acme->screens->data;
+}
+
+static void
+init_kbd (Acme *acme)
+{
+	int i;
+
+	for (i = 0; i < PLAY_KEY; i++)
+	{
+		const char *tmp;
+		Key *key;
+
+		gconf_client_notify_add (acme->conf_client,
+				keys[i].gconf_key, update_kbd_cb,
+				acme, NULL, NULL);
+
+		tmp = gconf_client_get_string (acme->conf_client,
+				keys[i].gconf_key, NULL);
+		if (is_valid_shortcut (tmp) == FALSE)
+			continue;
+
+		key = g_new0 (Key, 1);
+		if (egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycode, &key->state) == FALSE)
+		{
+			g_free (key);
+			continue;
+		}
+
+		keys[i].key = key;
+
+		grab_key (acme, key, TRUE);
+	}
+
+	for (i = PLAY_KEY; i < HANDLED_KEYS; i++)
+	{
+		const char *tmp;
+		Key *key;
+
+		gconf_client_notify_add (acme->conf_client,
+				keys[i].gconf_key, update_kbd_cb,
+				acme, NULL, NULL);
+
+		tmp = gconf_client_get_string (acme->conf_client,
+				keys[i].gconf_key, NULL);
+		if (is_valid_shortcut (tmp) == FALSE)
+			continue;
+
+		key = g_new0 (Key, 1);
+		if (egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycode, &key->state) == FALSE)
+		{
+			g_free (key);
+			continue;
+		}
+
+		keys[i].key = key;
+
+		switch (keys[i].key_type) {
+		case PLAY_KEY:
+			hookup_keysym (keys[i].key->keycode,
+					"XF86AudioPlay");
+			break;
+		case PAUSE_KEY:
+			hookup_keysym (keys[i].key->keycode,
+					"XF86AudioPause");
+			break;
+		case STOP_KEY:
+			hookup_keysym (keys[i].key->keycode,
+					"XF86AudioStop");
+			break;
+		case PREVIOUS_KEY:
+			hookup_keysym (keys[i].key->keycode,
+					"XF86AudioPrev");
+			break;
+		case NEXT_KEY:
+			hookup_keysym (keys[i].key->keycode,
+					"XF86AudioNext");
+			break;
+		}
+	}
+
+	return;
+}
+
+static gboolean
+dialog_hide (Acme *acme)
+{
+	gtk_widget_hide (acme->dialog);
+	acme->dialog_timeout = 0;
+	return FALSE;
+}
+
+static void
+dialog_show (Acme *acme)
+{
+	int orig_x, orig_y, orig_w, orig_h, orig_d;
+	int screen_w, screen_h;
+	int x, y;
+	int pointer_x, pointer_y;
+	GdkScreen *pointer_screen;
+	GdkRectangle geometry;
+	int monitor;
+
+	gtk_window_set_screen (GTK_WINDOW (acme->dialog), acme->current_screen);
+	gtk_widget_realize (GTK_WIDGET (acme->dialog));
+
+	gdk_window_get_geometry (GTK_WIDGET (acme->dialog)->window,
+				 &orig_x, &orig_y,
+				 &orig_w, &orig_h, &orig_d);
+
+	pointer_screen = NULL;
+	gdk_display_get_pointer (gdk_screen_get_display (acme->current_screen),
+				 &pointer_screen, &pointer_x,
+				 &pointer_y, NULL);
+	if (pointer_screen != acme->current_screen) {
+		/* The pointer isn't on the current screen, so just
+		 * assume the default monitor
+		 */
+		monitor = 0;
+	} else {
+		monitor =
+			gdk_screen_get_monitor_at_point (acme->current_screen,
+							 pointer_x, pointer_y);
+	}
+		
+	gdk_screen_get_monitor_geometry (acme->current_screen, monitor,
+					 &geometry);
+
+	screen_w = geometry.width;
+	screen_h = geometry.height;
+
+	x = ((screen_w - orig_w) / 2) + geometry.x;
+	y = geometry.y + (screen_h / 2) + (screen_h / 2 - orig_h) / 2;
+
+	gdk_window_move (GTK_WIDGET (acme->dialog)->window, x, y);
+
+	gtk_widget_show (acme->dialog);
+
+	/* this makes sure the dialog is actually shown */
+	while (gtk_events_pending())
+		gtk_main_iteration();
+
+	acme->dialog_timeout = gtk_timeout_add (DIALOG_TIMEOUT,
+			(GtkFunction) dialog_hide, acme);
+}
+
+static void
+do_unknown_action (Acme *acme, const char *url)
+{
+	char *string, *command;
+
+	g_return_if_fail (url != NULL);
+
+	string = gconf_client_get_string (acme->conf_client,
+			"/desktop/gnome/url-handlers/unknown/command",
+			NULL);
+
+	if (string == NULL || strcmp (string, "") == 0)
+		return;
+
+	command = g_strdup_printf (string, url);
+
+	execute (command, FALSE);
+
+	g_free (command);
+	g_free (string);
+}
+
+static void
+do_help_action (Acme *acme)
+{
+	char *string, *command;
+
+	string = gconf_client_get_string (acme->conf_client,
+			"/desktop/gnome/url-handlers/ghelp/command",
+			NULL);
+
+	if (string == NULL && strcmp (string, "") == 0)
+	{
+		do_unknown_action (acme, "ghelp:");
+		return;
+	}
+
+	command = g_strdup_printf (string, "");
+
+	execute (command, FALSE);
+
+	g_free (command);
+	g_free (string);
+}
+
+static void
+do_mail_action (Acme *acme)
+{
+	char *string, *command;
+
+	string = gconf_client_get_string (acme->conf_client,
+			"/desktop/gnome/url-handlers/mailto/command",
+			NULL);
+
+	if (string == NULL || strcmp (string, "") == 0)
+		return;
+
+	command = g_strdup_printf (string, "");
+
+	execute (command, FALSE);
+
+	g_free (command);
+	g_free (string);
+}
+
+static void
+do_www_action (Acme *acme, const char *url)
+{
+	char *string, *command;
+
+	string = gconf_client_get_string (acme->conf_client,
+		"/desktop/gnome/url-handlers/http/command",
+		 NULL);
+
+	if (string == NULL || strcmp (string, "") == 0)
+	{
+		do_unknown_action (acme, url ? url : "");
+		return;
+	}
+
+	if (url == NULL)
+		command = g_strdup_printf (string, "about:blank");
+	else
+		command = g_strdup_printf (string, url);
+
+	execute (command, FALSE);
+
+	g_free (command);
+	g_free (string);
+}
+
+static void
+do_exit_action (Acme *acme)
+{
+	GnomeClient *master;
+
+	master = gnome_master_client();
+	g_return_if_fail(master != NULL);
+
+	gnome_client_request_save(master,
+			GNOME_SAVE_BOTH,
+			TRUE,
+			GNOME_INTERACT_ANY,
+			FALSE,
+			TRUE);
+}
+
+static void
+do_eject_action (Acme *acme)
+{
+	GtkWidget *progress;
+	char *command;
+
+	if (acme->dialog_timeout != 0)
+	{
+		gtk_timeout_remove (acme->dialog_timeout);
+		acme->dialog_timeout = 0;
+	}
+
+	progress = glade_xml_get_widget (acme->xml, "progressbar");
+	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
+			(double) 0);
+	gtk_widget_set_sensitive (progress, FALSE);
+
+	acme_image_set (acme, ICON_EJECT);
+	dialog_show (acme);
+
+	command = gconf_client_get_string (acme->conf_client,
+			GCONF_MISC_DIR "/eject_command", NULL);
+	if ((command != NULL) && (strcmp (command, "") != 0))
+		execute (command, TRUE);
+	else
+		execute ("eject", TRUE);
+
+	gtk_widget_set_sensitive (progress, TRUE);
+}
+
+#ifdef USE_FBLEVEL
+static void
+do_brightness_action (Acme *acme, int type)
+{
+	GtkWidget *progress;
+	int level;
+
+	if (acme->levobj == NULL)
+		return;
+
+	if (acme->dialog_timeout != 0)
+	{
+		gtk_timeout_remove (acme->dialog_timeout);
+		acme->dialog_timeout = 0;
+	}
+
+	level = acme_fblevel_get_level (acme->levobj);
+	acme_image_set (acme, ICON_BRIGHT);
+
+	switch (type) {
+	case BRIGHT_DOWN_KEY:
+		acme_fblevel_set_level (acme->levobj, level - 1);
+		break;
+	case BRIGHT_UP_KEY:
+		acme_fblevel_set_level (acme->levobj, level + 1);
+		break;
+	}
+
+	level = acme_fblevel_get_level (acme->levobj);
+	progress = glade_xml_get_widget (acme->xml, "progressbar");
+	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
+			(double) level / 15);
+
+	dialog_show (acme);
+}
+#endif
+
+static void
+do_sound_action (Acme *acme, int type)
+{
+	GtkWidget *progress;
+	gboolean muted;
+	int vol;
+	int vol_step;
+
+	if (acme->volobj == NULL)
+		return;
+
+	vol_step = gconf_client_get_int (acme->conf_client,
+			GCONF_MISC_DIR "/volume_step", NULL);
+
+	if (vol_step == 0)
+		vol_step = VOLUME_STEP;
+
+	if (acme->dialog_timeout != 0)
+	{
+		gtk_timeout_remove (acme->dialog_timeout);
+		acme->dialog_timeout = 0;
+	}
+
+	vol = acme_volume_get_volume (acme->volobj);
+	muted = acme_volume_get_mute (acme->volobj);
+
+	switch (type) {
+	case MUTE_KEY:
+		acme_volume_mute_toggle(acme->volobj);
+		break;
+	case VOLUME_DOWN_KEY:
+		if (muted)
+		{
+			acme_volume_mute_toggle(acme->volobj);
+		} else {
+			acme_volume_set_volume (acme->volobj, vol - vol_step);
+		}
+		break;
+	case VOLUME_UP_KEY:
+		if (muted)
+		{
+			acme_volume_mute_toggle(acme->volobj);
+		} else {
+			acme_volume_set_volume (acme->volobj, vol + vol_step);
+		}
+		break;
+	}
+
+	muted = acme_volume_get_mute(acme->volobj);
+	acme_image_set (acme, muted ? ICON_MUTED : ICON_LOUD);
+
+	vol = acme_volume_get_volume (acme->volobj);
+	progress = glade_xml_get_widget (acme->xml, "progressbar");
+	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
+			(double) vol / 100);
+
+	dialog_show (acme);
+}
+
+static void
+do_action (int type, Acme *acme)
+{
+	switch (type) {
+	case MUTE_KEY:
+	case VOLUME_DOWN_KEY:
+	case VOLUME_UP_KEY:
+		do_sound_action (acme, type);
+		break;
+	case POWER_KEY:
+		do_exit_action (acme);
+		break;
+	case EJECT_KEY:
+		do_eject_action (acme);
+		break;
+	case HOME_KEY:
+		execute ("nautilus", FALSE);
+		break;
+	case SEARCH_KEY:
+		execute ("gnome-search-tool", FALSE);
+		break;
+	case EMAIL_KEY:
+		do_mail_action (acme);
+		break;
+	case SLEEP_KEY:
+		do_sleep_action ("apm", "xset dpms force off");
+		break;
+	case SCREENSAVER_KEY:
+		execute ("xscreensaver-command -lock", FALSE);
+		break;
+	case HELP_KEY:
+		do_help_action (acme);
+		break;
+	case WWW_KEY:
+		do_www_action (acme, NULL);
+		break;
+#ifdef USE_FBLEVEL
+	case BRIGHT_DOWN_KEY:
+	case BRIGHT_UP_KEY:
+		do_brightness_action (acme, type);
+		break;
+#endif
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static GdkFilterReturn
+acme_filter_events (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+	Acme *acme = (Acme *) data;
+	XEvent *xev = (XEvent *) xevent;
+	XAnyEvent *xanyev = (XAnyEvent *) xevent;
+	GdkScreen *event_screen = NULL;
+	guint keycode, state;
+	GSList *l;
+	int i;
+
+	/* Look for which screen we're receiving events */
+	for (l = acme->screens; (l != NULL) && (event_screen == NULL);
+			l = l->next)
+	{
+		GdkWindow *window;
+		GdkScreen *screen;
+
+		screen = (GdkScreen *) l->data;
+		window = gdk_screen_get_root_window (screen);
+
+		if (GDK_WINDOW_XID (window) == xanyev->window)
+		{
+			event_screen = screen;
+			break;
+		}
+	}
+
+	keycode = xev->xkey.keycode;
+	state = xev->xkey.state;
+
+	for (i = 0; i < HANDLED_KEYS; i++)
+	{
+		if (keys[i].key == NULL)
+			continue;
+
+		if (keys[i].key->keycode == keycode &&
+				(state & USED_MODS) == keys[i].key->state)
+		{
+			switch (keys[i].key_type) {
+			case PLAY_KEY:
+			case PAUSE_KEY:
+			case STOP_KEY:
+			case PREVIOUS_KEY:
+			case NEXT_KEY:
+				return GDK_FILTER_CONTINUE;
+			case VOLUME_DOWN_KEY:
+			case VOLUME_UP_KEY:
+#ifdef USE_FBLEVEL
+			case BRIGHT_DOWN_KEY:
+			case BRIGHT_UP_KEY:
+#endif
+				/* auto-repeatable keys */
+				if (xev->type != KeyPress)
+					return GDK_FILTER_CONTINUE;
+				break;
+			default:
+				if (xev->type != KeyRelease)
+					return GDK_FILTER_CONTINUE;
+			}
+
+			acme->current_screen = event_screen;
+
+			do_action (keys[i].key_type, acme);
+			return GDK_FILTER_REMOVE;
+		}
+	}
+
+	return GDK_FILTER_CONTINUE;
+}
+
+static Acme *global_acme = NULL;
+
+void
+gnome_settings_multimedia_keys_init (GConfClient *client)
+{
+	Acme *acme;
+
+	acme = g_new0 (Acme, 1);
+	acme->conf_client = client;
+
+	glade_gnome_init ();
+	acme->xml = glade_xml_new (ACME_GLADE "acme.glade", NULL, NULL);
+
+	if (acme->xml == NULL) {
+		acme_error (_("Couldn't load the Glade file.\n"
+					"Make sure that this daemon is properly"
+					" installed."));
+		g_free (acme);
+		return;
+	}
+
+	acme->dialog = glade_xml_get_widget (acme->xml, "dialog");
+	acme_image_set (acme, ICON_LOUD);
+
+	global_acme = acme;
+}
+
+void
+gnome_settings_multimedia_keys_load (GConfClient *client)
+{
+	Acme *acme = global_acme;
+	GSList *l;
+
+	if (global_acme == NULL)
+		return;
+
+	acme->conf_client = gconf_client_get_default ();
+	gconf_client_add_dir (acme->conf_client,
+			GCONF_BINDING_DIR,
+			GCONF_CLIENT_PRELOAD_ONELEVEL,
+			NULL);
+
+	init_screens (acme);
+	init_kbd (acme);
+	gtk_widget_realize (acme->dialog);
+	acme->dialog_timeout = 0;
+
+	/* initialise Volume handler */
+	acme->volobj = acme_volume_new();
+
+#ifdef USE_FBLEVEL
+	/* initialise Frame Buffer level handler */
+	if (acme_fblevel_is_powerbook () != FALSE)
+	{
+		acme->levobj = acme_fblevel_new();
+		if (acme->levobj == NULL)
+			fblevel_problem_cb ();
+	}
+#endif
+
+	/* Start filtering the events */
+	for (l = acme->screens; l != NULL; l = l->next)
+	{
+		GdkScreen *screen;
+		GdkWindow *window;
+
+		screen = (GdkScreen *) l->data;
+		window = gdk_screen_get_root_window (screen);
+		gdk_window_add_filter (window, acme_filter_events,
+				(gpointer) acme);
+	}
+}
+
Index: gnome-settings-daemon/gnome-settings-multimedia-keys.h
===================================================================
RCS file: gnome-settings-daemon/gnome-settings-multimedia-keys.h
diff -N gnome-settings-daemon/gnome-settings-multimedia-keys.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/gnome-settings-multimedia-keys.h	4 Jan 2004 04:40:21 -0000
@@ -0,0 +1,31 @@
+/* gnome-settings-multimedia_keys.h
+ *
+ * Copyright (C) 2002 Sun Microsystems Inc.
+ *
+ * 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, 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.
+ *
+ * Author: Erwann Chénedé <erwann.chenede@sun.com>
+ */
+
+
+#ifndef __GNOME_SETTINGS_MULTIMEDIA_KEYS_H__
+#define __GNOME_SETTINGS_MULTIMEDIA_KEYS_H__
+
+#include <gconf/gconf.h>
+void gnome_settings_multimedia_keys_init (GConfClient *client);
+void gnome_settings_multimedia_keys_load (GConfClient *client);
+
+#endif /*__GNOME_SETTINGS_MULTIMEDIA_KEYS_H__*/
Index: gnome-settings-daemon/multimedia-keys/.cvsignore
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/.cvsignore
diff -N gnome-settings-daemon/multimedia-keys/.cvsignore
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/.cvsignore	4 Jan 2004 04:40:21 -0000
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
Index: gnome-settings-daemon/multimedia-keys/Makefile.am
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/Makefile.am
diff -N gnome-settings-daemon/multimedia-keys/Makefile.am
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/Makefile.am	4 Jan 2004 04:40:21 -0000
@@ -0,0 +1,36 @@
+
+noinst_LTLIBRARIES = libacme.la
+
+if HAVE_ALSA
+ALSA_CFLAGS = -DHAVE_ALSA
+ALSA_files = acme-volume-alsa.c acme-volume-alsa.h
+endif
+                                                                                
+if HAVE_OSS
+OSS_CFLAGS = -DHAVE_OSS
+OSS_files = acme-volume-oss.c acme-volume-oss.h
+endif
+                                                                                
+if HAVE_FB
+FB_CFLAGS = -DHAVE_FB
+FB_files = acme-fb-level.c acme-fb-level.h
+endif
+
+INCLUDES = $(GNOME_CFLAGS) -I$(top_srcdir) $(ALSA_CFLAGS) $(OSS_CFLAGS) $(FB_CFLAGS)
+
+libacme_la_SOURCES = \
+	$(FB_files) $(OSS_files) $(ALSA_files)	\
+	acme-volume.c acme-volume.h		\
+	acme-volume-dummy.c acme-volume-dummy.h
+
+Datadir = $(datadir)/control-center-2.0/interfaces/
+Data_DATA = acme.glade
+
+pixmapsdir = $(GNOMECC_PIXMAPS_DIR)
+pixmaps_DATA = acme-brightness.png acme-eject.png gnome-speakernotes-muted.png gnome-speakernotes.png
+
+EXTRA_DIST =					\
+	acme-volume-alsa.c acme-volume-alsa.h	\
+	acme-volume-oss.c acme-volume-oss.h	\
+	acme-fb-level.c acme-fb-level.h
+
Index: gnome-settings-daemon/multimedia-keys/acme-brightness.png
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-brightness.png
diff -N gnome-settings-daemon/multimedia-keys/acme-brightness.png
Binary files /dev/null and acme-brightness.png differ
Index: gnome-settings-daemon/multimedia-keys/acme-eject.png
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-eject.png
diff -N gnome-settings-daemon/multimedia-keys/acme-eject.png
Binary files /dev/null and acme-eject.png differ
Index: gnome-settings-daemon/multimedia-keys/acme-fb-level.c
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-fb-level.c
diff -N gnome-settings-daemon/multimedia-keys/acme-fb-level.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-fb-level.c	4 Jan 2004 04:40:22 -0000
@@ -0,0 +1,197 @@
+/* acme-fb-level.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "acme-fb-level.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <linux/pmu.h>
+#include <errno.h>
+
+#ifndef FBIOBLANK
+#define FBIOBLANK      0x4611          /* 0 or vesa-level+1 */
+#endif
+                                                                                
+#ifndef PMU_IOC_GRAB_BACKLIGHT
+#define PMU_IOC_GRAB_BACKLIGHT  _IOR('B', 6, 0)
+#endif
+
+struct AcmeFblevelPrivate {
+	int pmu_fd;
+	int saved_level;
+};
+
+static GObjectClass *parent_class = NULL;
+
+static void
+acme_fblevel_finalize (GObject *obj_self)
+{
+	AcmeFblevel *self = ACME_FBLEVEL (obj_self);
+	gpointer priv = self->_priv;
+
+	if (G_OBJECT_CLASS(parent_class)->finalize)
+		(* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
+	g_free (priv);
+
+	return;
+}
+
+static void
+acme_fblevel_class_init (AcmeFblevelClass *klass)
+{
+	GObjectClass *g_object_class = (GObjectClass*) klass;
+	parent_class = g_type_class_ref (G_TYPE_OBJECT);
+	g_object_class->finalize = acme_fblevel_finalize;
+
+	return;
+}
+
+static void
+acme_fblevel_init (AcmeFblevel *fblevel)
+{
+	fblevel->_priv = g_new0 (AcmeFblevelPrivate, 1);
+	fblevel->level = 0;
+	fblevel->dim = FALSE;
+	fblevel->_priv->pmu_fd = -1;
+	fblevel->_priv->saved_level = 0;
+
+	return;
+}
+
+GType acme_fblevel_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (AcmeFblevelClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) acme_fblevel_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL /* class_data */,
+			sizeof (AcmeFblevel),
+			0 /* n_preallocs */,
+			(GInstanceInitFunc) acme_fblevel_init,
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "AcmeFblevel",
+				&info, (GTypeFlags)0);
+	}
+	return type;
+}
+
+int
+acme_fblevel_get_level (AcmeFblevel *self)
+{
+	int level;
+	ioctl (self->_priv->pmu_fd,
+			PMU_IOC_GET_BACKLIGHT, &level);
+	return level;
+}
+
+void
+acme_fblevel_set_level (AcmeFblevel *self, int val)
+{
+	int level;
+
+	level = CLAMP (val, 0, 15);
+
+	ioctl (self->_priv->pmu_fd,
+			PMU_IOC_SET_BACKLIGHT, &level);
+	self->level = level;
+}
+
+gboolean
+acme_fblevel_get_dim (AcmeFblevel *self)
+{
+	return self->dim;
+}
+
+void
+acme_fblevel_set_dim (AcmeFblevel *self, gboolean val)
+{
+	if (self->dim == FALSE && val == TRUE)
+	{
+		self->_priv->saved_level = acme_fblevel_get_level(self);
+		acme_fblevel_set_level (self, 1);
+		self->dim = TRUE;
+	} else if (self->dim == TRUE && val == FALSE) {
+		acme_fblevel_set_level (self, self->_priv->saved_level);
+		self->dim = FALSE;
+	}
+}
+
+AcmeFblevel *
+acme_fblevel_new (void)
+{
+	AcmeFblevel *self;
+	int fd, foo;
+
+	if (g_file_test ("/dev/pmu", G_FILE_TEST_EXISTS) == FALSE)
+		return NULL;
+
+	if (acme_fblevel_is_powerbook () == FALSE)
+		return NULL;
+
+	self = ACME_FBLEVEL (g_object_new (ACME_TYPE_FBLEVEL, NULL));
+	/* This function switches the kernel backlight control off.
+	 * This is part of the PPC kernel branch since version
+	 * 2.4.18-rc2-benh. It does nothing with older kernels.
+	 * For those kernels a separate kernel patch is nessecary to
+	 * get backlight control in user space.
+	 *
+	 * Notice nicked from pbbuttons*/
+	fd  = open ("/dev/pmu", O_RDWR);
+	/* We can't emit the signal yet, the signal isn't connected! */
+	if (fd < 0)
+		return NULL;
+
+	foo = ioctl(fd, PMU_IOC_GRAB_BACKLIGHT, 0);
+	self->_priv->pmu_fd = fd;
+	return self;
+}
+
+gboolean
+acme_fblevel_is_powerbook (void)
+{
+	FILE *fd;
+	char str[2048];
+	gboolean found = FALSE;
+
+	fd = fopen ("/proc/cpuinfo", "r");
+	while (!feof (fd) && found == FALSE)
+	{
+		fread (str, 1, 2048, fd);
+		if (strstr (str, "PowerBook") != NULL)
+			found = TRUE;
+	}
+
+	fclose (fd);
+
+	return found;
+}
+
Index: gnome-settings-daemon/multimedia-keys/acme-fb-level.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-fb-level.h
diff -N gnome-settings-daemon/multimedia-keys/acme-fb-level.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-fb-level.h	4 Jan 2004 04:40:22 -0000
@@ -0,0 +1,55 @@
+/* acme-fb-level.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define ACME_TYPE_FBLEVEL		(acme_fblevel_get_type ())
+#define ACME_FBLEVEL(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_FBLEVEL, AcmeFblevel))
+#define ACME_FBLEVEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_FBLEVEL, AcmeFblevelClass))
+#define ACME_IS_FBLEVEL(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_FBLEVEL))
+#define ACME_FBLEVEL_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_FBLEVEL, AcmeFblevelClass))
+
+typedef struct AcmeFblevelPrivate AcmeFblevelPrivate;
+typedef struct AcmeFblevel AcmeFblevel;
+typedef struct AcmeFblevelClass AcmeFblevelClass;
+
+struct AcmeFblevel {
+	GObject parent;
+	int level;
+	gboolean dim;
+	AcmeFblevelPrivate *_priv;
+};
+
+struct AcmeFblevelClass {
+	GObjectClass parent;
+};
+
+GType acme_fblevel_get_type			(void);
+int acme_fblevel_get_level			(AcmeFblevel *self);
+void acme_fblevel_set_level			(AcmeFblevel *self, int val);
+gboolean acme_fblevel_get_dim			(AcmeFblevel *self);
+void acme_fblevel_set_dim			(AcmeFblevel *self,
+						 gboolean val);
+AcmeFblevel *acme_fblevel_new			(void);
+gboolean acme_fblevel_is_powerbook		(void);
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-alsa.c
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-alsa.c
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-alsa.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-alsa.c	4 Jan 2004 04:40:23 -0000
@@ -0,0 +1,261 @@
+/* acme-volume-alsa.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "acme-volume-alsa.h"
+
+#include <alsa/asoundlib.h>
+
+#ifndef DEFAULT_CARD
+#define DEFAULT_CARD "default"
+#endif
+
+#undef LOG
+#ifdef LOG
+#define D(x...) g_message (x)
+#else
+#define D(x...)
+#endif
+
+struct AcmeVolumeAlsaPrivate
+{
+	gboolean use_pcm;
+	long pmin, pmax;
+	snd_mixer_t *handle;
+	snd_mixer_elem_t *elem;
+	gboolean mixerpb;
+	int saved_volume;
+};
+
+static GObjectClass *parent_class = NULL;
+
+static int acme_volume_alsa_get_volume (AcmeVolume *self);
+static void acme_volume_alsa_set_volume (AcmeVolume *self, int val);
+static gboolean acme_volume_alsa_mixer_check (AcmeVolumeAlsa *self, int fd);
+
+static void
+acme_volume_alsa_finalize (GObject *object)
+{
+	AcmeVolumeAlsa *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (ACME_IS_VOLUME_ALSA (object));
+
+	self = ACME_VOLUME_ALSA (object);
+
+	if (self->_priv)
+		g_free (self->_priv);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static int
+acme_volume_alsa_vol_check (AcmeVolumeAlsa *self, int volume)
+{
+	return CLAMP (volume, self->_priv->pmin, self->_priv->pmax);
+}
+
+static void
+acme_volume_alsa_set_use_pcm (AcmeVolume *vol, gboolean val)
+{
+   snd_mixer_selem_id_t *sid = NULL;
+   snd_mixer_elem_t *elem;
+                                                                    
+   AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+   self->_priv->use_pcm = val;
+                                                                    
+   snd_mixer_selem_id_alloca(&sid);
+   snd_mixer_selem_id_set_name (sid, (val?"PCM":"Master"));
+                                                                    
+   elem = snd_mixer_find_selem(self->_priv->handle, sid);
+                                                                    
+   if (!elem)
+   {
+      D("snd_mixer_find_selem");
+   }
+   else
+   {
+      self->_priv->elem = elem;
+   }
+}
+
+static void
+acme_volume_alsa_set_mute (AcmeVolume *vol, gboolean val)
+{
+	AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+
+	snd_mixer_selem_set_playback_switch(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_LEFT, !val);
+	if (val == TRUE)
+	{
+		self->_priv->saved_volume = acme_volume_alsa_get_volume (vol);
+		acme_volume_alsa_set_volume (vol, 0);
+	} else {
+		if (self->_priv->saved_volume != -1)
+			acme_volume_alsa_set_volume (vol,
+					self->_priv->saved_volume);
+	}
+}
+
+static gboolean
+acme_volume_alsa_get_mute (AcmeVolume *vol)
+{
+	AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+	int ival;
+
+	snd_mixer_selem_get_playback_switch(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_LEFT, &ival);
+
+	return !ival;
+}
+
+static int
+acme_volume_alsa_get_volume (AcmeVolume *vol)
+{
+	AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+	long lval, rval;
+	int tmp;
+
+	snd_mixer_selem_get_playback_volume(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_LEFT, &lval);
+	snd_mixer_selem_get_playback_volume(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_RIGHT, &rval);
+
+	tmp = acme_volume_alsa_vol_check (self, (int) (lval + rval) / 2);
+	return (tmp * 100 / self->_priv->pmax);
+}
+
+static void
+acme_volume_alsa_set_volume (AcmeVolume *vol, int val)
+{
+	AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+
+	val = (long) acme_volume_alsa_vol_check (self,
+			val * self->_priv->pmax / 100);
+	snd_mixer_selem_set_playback_volume(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_LEFT, val);
+	snd_mixer_selem_set_playback_volume(self->_priv->elem,
+			SND_MIXER_SCHN_FRONT_RIGHT, val);
+}
+
+static void
+acme_volume_alsa_init (AcmeVolume *vol)
+{
+	AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol;
+	snd_mixer_selem_id_t *sid;
+	snd_mixer_t *handle;
+	snd_mixer_elem_t *elem;
+
+	self->_priv = g_new0 (AcmeVolumeAlsaPrivate, 1);
+
+	 /* open the mixer */
+	if (snd_mixer_open (&handle, 0) < 0)
+	{
+		D("snd_mixer_open");
+		return;
+	}
+	/* attach the handle to the default card */
+	if (snd_mixer_attach(handle, DEFAULT_CARD) <0)
+	{
+		D("snd_mixer_attach");
+		goto bail;
+	}
+	/* ? */
+	if (snd_mixer_selem_register(handle, NULL, NULL) < 0)
+	{
+		D("snd_mixer_selem_register");
+		goto bail;
+	}
+	if (snd_mixer_load(handle) < 0)
+	{
+		D("snd_mixer_load");
+		goto bail;
+	}
+
+	snd_mixer_selem_id_alloca(&sid);
+	snd_mixer_selem_id_set_name (sid, "Master");
+	elem = snd_mixer_find_selem(handle, sid);
+	if (!elem)
+	{
+		D("snd_mixer_find_selem");
+		goto bail;
+	}
+
+	if (!snd_mixer_selem_has_playback_volume(elem))
+	{
+		D("snd_mixer_selem_has_capture_volume");
+		goto bail;
+	}
+
+	snd_mixer_selem_get_playback_volume_range (elem,
+			&(self->_priv->pmin),
+			&(self->_priv->pmax));
+
+	self->_priv->handle = handle;
+	self->_priv->elem = elem;
+
+	return;
+bail:
+	g_free (self->_priv);
+	self->_priv = NULL;
+}
+
+static void
+acme_volume_alsa_class_init (AcmeVolumeAlsaClass *klass)
+{
+	AcmeVolumeClass *volume_class = ACME_VOLUME_CLASS (klass);
+	G_OBJECT_CLASS (klass)->finalize = acme_volume_alsa_finalize;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	volume_class->set_volume = acme_volume_alsa_set_volume;
+	volume_class->get_volume = acme_volume_alsa_get_volume;
+	volume_class->set_mute = acme_volume_alsa_set_mute;
+	volume_class->get_mute = acme_volume_alsa_get_mute;
+}
+
+GType acme_volume_alsa_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info =
+		{
+			sizeof (AcmeVolumeAlsaClass),
+			NULL,         /* base_init */
+			NULL,         /* base_finalize */
+			(GClassInitFunc) acme_volume_alsa_class_init,
+			NULL,         /* class_finalize */
+			NULL,         /* class_data */
+			sizeof (AcmeVolumeAlsa),
+			0,            /* n_preallocs */
+			(GInstanceInitFunc) acme_volume_alsa_init
+		};
+
+		object_type = g_type_register_static (ACME_TYPE_VOLUME,
+				"AcmeVolumeAlsa", &object_info, 0);
+	}
+
+	return object_type;
+}
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-alsa.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-alsa.h
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-alsa.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-alsa.h	4 Jan 2004 04:40:23 -0000
@@ -0,0 +1,47 @@
+/* acme-volume-alsa.h
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include "acme-volume.h"
+
+#define ACME_TYPE_VOLUME_ALSA		(acme_volume_get_type ())
+#define ACME_VOLUME_ALSA(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsa))
+#define ACME_VOLUME_ALSA_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsaClass))
+#define ACME_IS_VOLUME_ALSA(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME_ALSA))
+#define ACME_VOLUME_ALSA_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsaClass))
+
+typedef struct AcmeVolumeAlsa AcmeVolumeAlsa;
+typedef struct AcmeVolumeAlsaClass AcmeVolumeAlsaClass;
+typedef struct AcmeVolumeAlsaPrivate AcmeVolumeAlsaPrivate;
+
+struct AcmeVolumeAlsa {
+	AcmeVolume parent;
+	AcmeVolumeAlsaPrivate *_priv;
+};
+
+struct AcmeVolumeAlsaClass {
+	AcmeVolumeClass parent;
+};
+
+GType acme_volume_alsa_get_type		(void);
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-dummy.c
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-dummy.c
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-dummy.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-dummy.c	4 Jan 2004 04:40:23 -0000
@@ -0,0 +1,110 @@
+/* acme-volume-dummy.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "acme-volume-dummy.h"
+
+static GObjectClass *parent_class = NULL;
+
+static int acme_volume_dummy_get_volume (AcmeVolume *self);
+static void acme_volume_dummy_set_volume (AcmeVolume *self, int val);
+
+static void
+acme_volume_dummy_finalize (GObject *object)
+{
+	AcmeVolumeDummy *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (ACME_IS_VOLUME_DUMMY (object));
+
+	self = ACME_VOLUME_DUMMY (object);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+acme_volume_dummy_set_mute (AcmeVolume *vol, gboolean val)
+{
+}
+
+static gboolean
+acme_volume_dummy_get_mute (AcmeVolume *vol)
+{
+	return FALSE;
+}
+
+static int
+acme_volume_dummy_get_volume (AcmeVolume *vol)
+{
+	return 0;
+}
+
+static void
+acme_volume_dummy_set_volume (AcmeVolume *vol, int val)
+{
+}
+
+static void
+acme_volume_dummy_init (AcmeVolume *vol)
+{
+}
+
+static void
+acme_volume_dummy_class_init (AcmeVolumeDummyClass *klass)
+{
+	AcmeVolumeClass *volume_class = ACME_VOLUME_CLASS (klass);
+	G_OBJECT_CLASS (klass)->finalize = acme_volume_dummy_finalize;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	volume_class->set_volume = acme_volume_dummy_set_volume;
+	volume_class->get_volume = acme_volume_dummy_get_volume;
+	volume_class->set_mute = acme_volume_dummy_set_mute;
+	volume_class->get_mute = acme_volume_dummy_get_mute;
+}
+
+GType acme_volume_dummy_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info =
+		{
+			sizeof (AcmeVolumeDummyClass),
+			NULL,         /* base_init */
+			NULL,         /* base_finalize */
+			(GClassInitFunc) acme_volume_dummy_class_init,
+			NULL,         /* class_finalize */
+			NULL,         /* class_data */
+			sizeof (AcmeVolumeDummy),
+			0,            /* n_preallocs */
+			(GInstanceInitFunc) acme_volume_dummy_init
+		};
+
+		object_type = g_type_register_static (ACME_TYPE_VOLUME,
+				"AcmeVolumeDummy", &object_info, 0);
+	}
+
+	return object_type;
+}
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-dummy.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-dummy.h
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-dummy.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-dummy.h	4 Jan 2004 04:40:23 -0000
@@ -0,0 +1,46 @@
+/* acme-volume-dummy.h
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include "acme-volume.h"
+
+#define ACME_TYPE_VOLUME_DUMMY		(acme_volume_get_type ())
+#define ACME_VOLUME_DUMMY(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME_DUMMY, AcmeVolumeDummy))
+#define ACME_VOLUME_DUMMY_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME_DUMMY, AcmeVolumeDummyClass))
+#define ACME_IS_VOLUME_DUMMY(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME_DUMMY))
+#define ACME_VOLUME_DUMMY_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME_DUMMY, AcmeVolumeDummyClass))
+
+typedef struct AcmeVolumeDummy AcmeVolumeDummy;
+typedef struct AcmeVolumeDummyClass AcmeVolumeDummyClass;
+
+struct AcmeVolumeDummy {
+	AcmeVolume parent;
+};
+
+struct AcmeVolumeDummyClass {
+	AcmeVolumeClass parent;
+};
+
+GType acme_volume_dummy_get_type		(void);
+AcmeVolume* acme_volume_dummy_new		(void);
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-oss.c
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-oss.c
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-oss.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-oss.c	4 Jan 2004 04:40:23 -0000
@@ -0,0 +1,236 @@
+/* acme-volume-oss.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "acme-volume-oss.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef __NetBSD__
+#include <soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif /* __NetBSD__ */
+
+struct AcmeVolumeOssPrivate
+{
+	gboolean use_pcm;
+	gboolean mixerpb;
+	int volume;
+	int saved_volume;
+	gboolean pcm_avail;
+	gboolean mute;
+};
+
+static GObjectClass *parent_class = NULL;
+
+static int acme_volume_oss_get_volume (AcmeVolume *self);
+static void acme_volume_oss_set_volume (AcmeVolume *self, int val);
+static gboolean acme_volume_oss_mixer_check (AcmeVolumeOss *self, int fd);
+
+static void
+acme_volume_oss_finalize (GObject *object)
+{
+	AcmeVolumeOss *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (ACME_IS_VOLUME_OSS (object));
+
+	self = ACME_VOLUME_OSS (object);
+
+	g_return_if_fail (self->_priv != NULL);
+	g_free (self->_priv);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static int
+acme_volume_oss_vol_check (int volume)
+{
+	return CLAMP (volume, 0, 100);
+}
+
+static void
+acme_volume_oss_set_mute (AcmeVolume *vol, gboolean val)
+{
+	AcmeVolumeOss *self = (AcmeVolumeOss *) vol;
+
+	if (self->_priv->mute == FALSE)
+	{
+		self->_priv->saved_volume =
+			acme_volume_oss_get_volume (vol);
+		acme_volume_oss_set_volume (vol, 0);
+		self->_priv->mute = TRUE;
+	} else {
+		acme_volume_oss_set_volume (vol, self->_priv->saved_volume);
+		self->_priv->mute = FALSE;
+	}
+}
+
+static gboolean
+acme_volume_oss_get_mute (AcmeVolume *vol)
+{
+	AcmeVolumeOss *self = (AcmeVolumeOss *) vol;
+
+	/* somebody else might have changed the volume */
+	if ((self->_priv->mute == TRUE) && (self->_priv->volume != 0))
+	{
+		self->_priv->mute = FALSE;
+	}
+
+	return self->_priv->mute;
+}
+
+static int
+acme_volume_oss_get_volume (AcmeVolume *vol)
+{
+	gint volume, r, l, fd;
+	AcmeVolumeOss *self = (AcmeVolumeOss *) vol;
+
+	fd  = open ("/dev/mixer", O_RDONLY);
+	if (acme_volume_oss_mixer_check(self, fd) == FALSE)
+	{
+		volume = 0;
+	} else {
+		if (self->_priv->use_pcm && self->_priv->pcm_avail)
+			ioctl (fd, MIXER_READ (SOUND_MIXER_PCM), &volume);
+		else
+			ioctl (fd, MIXER_READ (SOUND_MIXER_VOLUME), &volume);
+		close (fd);
+
+		r = (volume & 0xff);
+		l = (volume & 0xff00) >> 8;
+		volume = (r + l) / 2;
+		volume = acme_volume_oss_vol_check (volume);
+	}
+
+	return volume;
+}
+
+static void
+acme_volume_oss_set_volume (AcmeVolume *vol, int val)
+{
+	int fd, tvol, volume;
+	AcmeVolumeOss *self = (AcmeVolumeOss *) vol;
+
+	volume = acme_volume_oss_vol_check (val);
+
+	fd = open ("/dev/mixer", O_RDONLY);
+	if (acme_volume_oss_mixer_check (self, fd) == FALSE)
+	{
+		return;
+	} else {
+		tvol = (volume << 8) + volume;
+		if (self->_priv->use_pcm && self->_priv->pcm_avail)
+			ioctl (fd, MIXER_WRITE (SOUND_MIXER_PCM), &tvol);
+		else
+			ioctl (fd, MIXER_WRITE (SOUND_MIXER_VOLUME), &tvol);
+		close (fd);
+		self->_priv->volume = volume;
+	}
+}
+
+static void
+acme_volume_oss_init (AcmeVolume *vol)
+{
+	AcmeVolumeOss *self = (AcmeVolumeOss *) vol;
+	int fd;
+
+	self->_priv = g_new0 (AcmeVolumeOssPrivate, 1);
+
+	fd  = open ("/dev/mixer", O_RDONLY);
+	if (acme_volume_oss_mixer_check(self, fd) == FALSE)
+	{
+		self->_priv->pcm_avail = FALSE;
+	} else {
+		int mask = 0;
+
+		ioctl (fd, SOUND_MIXER_READ_DEVMASK, &mask);
+		if (mask & ( 1 << SOUND_MIXER_PCM))
+			self->_priv->pcm_avail = TRUE;
+		else
+			self->_priv->pcm_avail = FALSE;
+		if (!(mask & ( 1 << SOUND_MIXER_VOLUME)))
+			self->_priv->use_pcm = TRUE;
+
+		close (fd);
+	}
+}
+
+static void
+acme_volume_oss_class_init (AcmeVolumeOssClass *klass)
+{
+	AcmeVolumeClass *volume_class = ACME_VOLUME_CLASS (klass);
+	G_OBJECT_CLASS (klass)->finalize = acme_volume_oss_finalize;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	volume_class->set_volume = acme_volume_oss_set_volume;
+	volume_class->get_volume = acme_volume_oss_get_volume;
+	volume_class->set_mute = acme_volume_oss_set_mute;
+	volume_class->get_mute = acme_volume_oss_get_mute;
+}
+
+GType acme_volume_oss_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info =
+		{
+			sizeof (AcmeVolumeOssClass),
+			NULL,         /* base_init */
+			NULL,         /* base_finalize */
+			(GClassInitFunc) acme_volume_oss_class_init,
+			NULL,         /* class_finalize */
+			NULL,         /* class_data */
+			sizeof (AcmeVolumeOss),
+			0,            /* n_preallocs */
+			(GInstanceInitFunc) acme_volume_oss_init
+		};
+
+		object_type = g_type_register_static (ACME_TYPE_VOLUME,
+				"AcmeVolumeOss", &object_info, 0);
+	}
+
+	return object_type;
+}
+
+static gboolean
+acme_volume_oss_mixer_check (AcmeVolumeOss *self, int fd)
+{
+	gboolean retval;
+
+	if (fd <0) {
+		if (self->_priv->mixerpb == FALSE) {
+			self->_priv->mixerpb = TRUE;
+			//FIXME
+			//volume_oss_fd_problem(self);
+		}
+	}
+	retval = (!self->_priv->mixerpb);
+	return retval;
+}
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume-oss.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume-oss.h
diff -N gnome-settings-daemon/multimedia-keys/acme-volume-oss.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume-oss.h	4 Jan 2004 04:40:24 -0000
@@ -0,0 +1,47 @@
+/* acme-volume-oss.h
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include "acme-volume.h"
+
+#define ACME_TYPE_VOLUME_OSS		(acme_volume_get_type ())
+#define ACME_VOLUME_OSS(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME_OSS, AcmeVolumeOss))
+#define ACME_VOLUME_OSS_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME_OSS, AcmeVolumeOssClass))
+#define ACME_IS_VOLUME_OSS(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME_OSS))
+#define ACME_VOLUME_OSS_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME_OSS, AcmeVolumeOssClass))
+
+typedef struct AcmeVolumeOss AcmeVolumeOss;
+typedef struct AcmeVolumeOssClass AcmeVolumeOssClass;
+typedef struct AcmeVolumeOssPrivate AcmeVolumeOssPrivate;
+
+struct AcmeVolumeOss {
+	AcmeVolume parent;
+	AcmeVolumeOssPrivate *_priv;
+};
+
+struct AcmeVolumeOssClass {
+	AcmeVolumeClass parent;
+};
+
+GType acme_volume_oss_get_type		(void);
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume.c
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume.c
diff -N gnome-settings-daemon/multimedia-keys/acme-volume.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume.c	4 Jan 2004 04:40:24 -0000
@@ -0,0 +1,133 @@
+/* acme-volume.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "acme-volume.h"
+#ifdef HAVE_OSS
+#include "acme-volume-oss.h"
+#endif
+#ifdef HAVE_ALSA
+#include "acme-volume-alsa.h"
+#endif
+#include "acme-volume-dummy.h"
+
+static GObjectClass *parent_class = NULL;
+
+static void
+acme_volume_class_init (AcmeVolumeClass *klass)
+{
+	parent_class = g_type_class_peek_parent (klass);
+};
+
+static void
+acme_volume_init (AcmeVolume *vol)
+{
+}
+
+GType acme_volume_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (AcmeVolumeClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) acme_volume_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL /* class_data */,
+			sizeof (AcmeVolume),
+			0 /* n_preallocs */,
+			(GInstanceInitFunc) acme_volume_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "AcmeVolume",
+				&info, (GTypeFlags)0);
+	}
+	return type;
+}
+
+int
+acme_volume_get_volume (AcmeVolume *self)
+{
+	g_return_val_if_fail (self != NULL, 0);
+	g_return_val_if_fail (ACME_IS_VOLUME (self), 0);
+
+	return ACME_VOLUME_GET_CLASS (self)->get_volume (self);
+}
+
+void
+acme_volume_set_volume (AcmeVolume *self, int val)
+{
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (ACME_IS_VOLUME (self));
+
+	ACME_VOLUME_GET_CLASS (self)->set_volume (self, val);
+}
+
+gboolean
+acme_volume_get_mute (AcmeVolume *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (ACME_IS_VOLUME (self), FALSE);
+
+	return ACME_VOLUME_GET_CLASS (self)->get_mute (self);
+}
+
+void
+acme_volume_set_mute (AcmeVolume *self, gboolean val)
+{
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (ACME_IS_VOLUME (self));
+
+	ACME_VOLUME_GET_CLASS (self)->set_mute (self, val);
+}
+
+void
+acme_volume_mute_toggle (AcmeVolume * self)
+{
+	gboolean muted;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (ACME_IS_VOLUME (self));
+
+	muted = ACME_VOLUME_GET_CLASS (self)->get_mute (self);
+	ACME_VOLUME_GET_CLASS (self)->set_mute (self, !muted);
+}
+
+AcmeVolume *acme_volume_new (void)
+{
+	AcmeVolume *vol;
+
+#ifdef HAVE_ALSA
+	vol = ACME_VOLUME  (g_object_new (acme_volume_alsa_get_type (), NULL));
+	if (vol != NULL && ACME_VOLUME_ALSA (vol)->_priv != NULL)
+		return vol;
+	if (ACME_VOLUME_ALSA (vol)->_priv == NULL)
+		g_object_unref (vol);
+#endif
+#ifdef HAVE_OSS
+	vol = ACME_VOLUME  (g_object_new (acme_volume_oss_get_type (), NULL));
+	return vol;
+#endif
+	return ACME_VOLUME  (g_object_new (acme_volume_dummy_get_type (), NULL));
+}
+
Index: gnome-settings-daemon/multimedia-keys/acme-volume.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme-volume.h
diff -N gnome-settings-daemon/multimedia-keys/acme-volume.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme-volume.h	4 Jan 2004 04:40:24 -0000
@@ -0,0 +1,61 @@
+/* acme-volume.h
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#ifndef _ACME_VOLUME_H
+#define _ACME_VOLUME_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define ACME_TYPE_VOLUME		(acme_volume_get_type ())
+#define ACME_VOLUME(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME, AcmeVolume))
+#define ACME_VOLUME_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME, AcmeVolumeClass))
+#define ACME_IS_VOLUME(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME))
+#define ACME_VOLUME_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME, AcmeVolumeClass))
+
+typedef struct {
+	GObject parent;
+} AcmeVolume;
+
+typedef struct {
+	GObjectClass parent;
+
+	void (* set_volume) (AcmeVolume *self, int val);
+	int (* get_volume) (AcmeVolume *self);
+	void (* set_mute) (AcmeVolume *self, gboolean val);
+	int (* get_mute) (AcmeVolume *self);
+} AcmeVolumeClass;
+
+GType acme_volume_get_type			(void);
+int acme_volume_get_volume			(AcmeVolume *self);
+void acme_volume_set_volume			(AcmeVolume *self, int val);
+gboolean acme_volume_get_mute			(AcmeVolume *self);
+void acme_volume_set_mute			(AcmeVolume *self,
+						 gboolean val);
+void acme_volume_mute_toggle			(AcmeVolume * self);
+AcmeVolume *acme_volume_new			(void);
+
+G_END_DECLS
+
+#endif /* _ACME_VOLUME_H */
Index: gnome-settings-daemon/multimedia-keys/acme.glade
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme.glade
diff -N gnome-settings-daemon/multimedia-keys/acme.glade
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme.glade	4 Jan 2004 04:40:24 -0000
@@ -0,0 +1,66 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="dialog">
+  <property name="title" translatable="yes">Volume</property>
+  <property name="type">GTK_WINDOW_POPUP</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+
+  <child>
+    <widget class="GtkFrame" id="frame1">
+      <property name="visible">True</property>
+      <property name="label_xalign">0</property>
+      <property name="label_yalign">0.5</property>
+      <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+      <child>
+	<widget class="GtkVBox" id="vbox2">
+	  <property name="border_width">5</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkImage" id="image1">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkProgressBar" id="progressbar">
+	      <property name="visible">True</property>
+	      <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+	      <property name="fraction">0</property>
+	      <property name="pulse_step">0.1</property>
+	      <property name="activity_mode">False</property>
+	      <property name="show_text">False</property>
+	      <property name="text_xalign">0.5</property>
+	      <property name="text_yalign">0.5</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
Index: gnome-settings-daemon/multimedia-keys/acme.h
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/acme.h
diff -N gnome-settings-daemon/multimedia-keys/acme.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-settings-daemon/multimedia-keys/acme.h	4 Jan 2004 04:40:24 -0000
@@ -0,0 +1,88 @@
+/* ACME
+ * Copyright (C) 2001 Bastien Nocera <hadess@hadess.net>
+ *
+ * acme.h
+ *
+ * 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.
+ */
+
+#ifndef __ACME_H__
+#define __ACME_H__
+
+#define GCONF_BINDING_DIR "/apps/gnome_settings_daemon/keybindings"
+#define GCONF_MISC_DIR "/apps/gnome_settings_daemon"
+
+enum {
+	MUTE_KEY,
+	VOLUME_DOWN_KEY,
+	VOLUME_UP_KEY,
+	POWER_KEY,
+	EJECT_KEY,
+	HOME_KEY,
+	SEARCH_KEY,
+	EMAIL_KEY,
+	SLEEP_KEY,
+	SCREENSAVER_KEY,
+	HELP_KEY,
+	WWW_KEY,
+#ifdef USE_FBLEVEL
+	BRIGHT_DOWN_KEY,
+	BRIGHT_UP_KEY,
+#endif
+	PLAY_KEY,
+	PAUSE_KEY,
+	STOP_KEY,
+	PREVIOUS_KEY,
+	NEXT_KEY,
+	HANDLED_KEYS,
+};
+
+typedef struct {
+  guint keysym;
+  guint state;
+  guint keycode;
+} Key;
+
+static struct {
+	int key_type;
+	const char *gconf_key;
+	Key *key;
+} keys[HANDLED_KEYS] = {
+	{ MUTE_KEY, GCONF_BINDING_DIR "/volume_mute",NULL },
+	{ VOLUME_DOWN_KEY, GCONF_BINDING_DIR "/volume_down", NULL },
+	{ VOLUME_UP_KEY, GCONF_BINDING_DIR "/volume_up", NULL },
+	{ POWER_KEY, GCONF_BINDING_DIR "/power", NULL },
+	{ EJECT_KEY, GCONF_BINDING_DIR "/eject", NULL },
+	{ HOME_KEY, GCONF_BINDING_DIR "/home", NULL },
+	{ SEARCH_KEY, GCONF_BINDING_DIR "/search", NULL },
+	{ EMAIL_KEY, GCONF_BINDING_DIR "/email", NULL },
+	{ SLEEP_KEY, GCONF_BINDING_DIR "/sleep", NULL },
+	{ SCREENSAVER_KEY, GCONF_BINDING_DIR "/screensaver", NULL },
+	{ HELP_KEY, GCONF_BINDING_DIR "/help", NULL },
+	{ WWW_KEY, GCONF_BINDING_DIR "/www_key_str", NULL },
+#ifdef USE_FBLEVEL
+	{ BRIGHT_DOWN_KEY, GCONF_BINDING_DIR "/brightness_down", NULL },
+	{ BRIGHT_UP_KEY, GCONF_BINDING_DIR "/brightness_up", NULL },
+#endif
+	{ PLAY_KEY, GCONF_BINDING_DIR "/play", NULL },
+	{ PAUSE_KEY, GCONF_BINDING_DIR "/pause", NULL },
+	{ STOP_KEY, GCONF_BINDING_DIR "/stop", NULL },
+	{ PREVIOUS_KEY, GCONF_BINDING_DIR "/previous", NULL },
+	{ NEXT_KEY, GCONF_BINDING_DIR "/next", NULL },
+};
+
+#endif /* __ACME_H__ */
+
Index: gnome-settings-daemon/multimedia-keys/gnome-speakernotes-muted.png
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/gnome-speakernotes-muted.png
diff -N gnome-settings-daemon/multimedia-keys/gnome-speakernotes-muted.png
Binary files /dev/null and gnome-speakernotes-muted.png differ
Index: gnome-settings-daemon/multimedia-keys/gnome-speakernotes.png
===================================================================
RCS file: gnome-settings-daemon/multimedia-keys/gnome-speakernotes.png
diff -N gnome-settings-daemon/multimedia-keys/gnome-speakernotes.png
Binary files /dev/null and gnome-speakernotes.png differ
Index: schemas/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-control-center/schemas/Makefile.am,v
retrieving revision 1.13
diff -u -r1.13 Makefile.am
--- schemas/Makefile.am	30 Nov 2003 01:48:04 -0000	1.13
+++ schemas/Makefile.am	4 Jan 2004 04:40:26 -0000
@@ -3,7 +3,8 @@
 	apps_gnome_settings_daemon_screensaver.schemas.in	\
 	apps_gnome_settings_daemon_default_editor.schemas.in	\
 	desktop_gnome_font_rendering.schemas \
-	desktop_gnome_peripherals_keyboard_xkb.schemas.in 
+	desktop_gnome_peripherals_keyboard_xkb.schemas.in	\
+	apps_gnome_settings_daemon_multimedia_keys.schemas.in
 
 schema_DATA = $(schema_in_files:.schemas.in=.schemas)
 
Index: schemas/apps_gnome_settings_daemon_multimedia_keys.schemas.in
===================================================================
RCS file: schemas/apps_gnome_settings_daemon_multimedia_keys.schemas.in
diff -N schemas/apps_gnome_settings_daemon_multimedia_keys.schemas.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ schemas/apps_gnome_settings_daemon_multimedia_keys.schemas.in	4 Jan 2004 04:40:26 -0000
@@ -0,0 +1,244 @@
+<?xml version="1.0"?>
+<gconfschemafile>
+    <schemalist>
+
+        <schema>
+	    <key>/schemas/apps/gnome_settings_daemon/volume_step</key>
+	    <applyto>/apps/gnome_settings_daemon/volume_step</applyto>
+	    <type>int</type>
+	    <default>6</default>
+	    <locale name="C">
+	        <short>Volume step</short>
+		<long>Volume step as percentage of volume.</long>
+	    </locale>
+	</schema>
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/volume_mute</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/volume_mute</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Volume mute</short>
+                <long>Volume mute key's shortcut</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/volume_down</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/volume_down</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Volume down</short>
+                <long>Volume down key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/volume_up</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/volume_up</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Volume up</short>
+                <long>Volume up's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/power</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/power</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Power key</short>
+                <long>Power key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/eject</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/eject</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Eject key</short>
+                <long>Eject key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/home</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/home</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>My Home key</short>
+                <long>My Home key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/search</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/search</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Search key</short>
+                <long>Search key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/email</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/email</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>E-mail key</short>
+                <long>E-mail key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/sleep</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/sleep</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Sleep key</short>
+                <long>Sleep key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/screensaver</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/screensaver</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Screensaver key</short>
+                <long>Screensaver key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/help</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/help</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Help key</short>
+                <long>Help key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/www</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/www</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>World Wide Web key</short>
+                <long>World Wide Web key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/brightness_down</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/brightness_down</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Brightness down key</short>
+                <long>Brightness down key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/brightness_up</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/brightness_up</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Brightness up key</short>
+                <long>Brightness up key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/play</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/play</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Play (or Play/Pause) key</short>
+                <long>Play (or Play/Pause) key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/pause</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/pause</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Pause key</short>
+                <long>Pause key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/stop</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/stop</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Stop key</short>
+                <long>Stop key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/previous</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/previous</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Previous (audio) key</short>
+                <long>Previous (audio) key's shortcut.</long>
+            </locale>
+        </schema>
+
+
+        <schema>
+            <key>/schemas/apps/gnome_settings_daemon/keybindings/next</key>
+            <applyto>/apps/gnome_settings_daemon/keybindings/next</applyto>
+            <type>string</type>
+            <default></default>
+            <locale name="C">
+                <short>Next (audio) key</short>
+                <long>Next (audio) key's shortcut.</long>
+            </locale>
+        </schema>
+
+    </schemalist>
+</gconfschemafile>
