rpms/rhythmbox/devel rhythmbox-0.9.6-fix-keybinding.patch, NONE, 1.1 rhythmbox.spec, 1.83, 1.84

fedora-cvs-commits at redhat.com fedora-cvs-commits at redhat.com
Mon Oct 23 02:06:19 UTC 2006


Author: mclasen

Update of /cvs/dist/rpms/rhythmbox/devel
In directory cvs.devel.redhat.com:/tmp/cvs-serv30469

Modified Files:
	rhythmbox.spec 
Added Files:
	rhythmbox-0.9.6-fix-keybinding.patch 
Log Message:
0.9.6


rhythmbox-0.9.6-fix-keybinding.patch:
 lib/rb-preferences.h    |    7 
 shell/rb-shell-player.c |  767 ++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 743 insertions(+), 31 deletions(-)

--- NEW FILE rhythmbox-0.9.6-fix-keybinding.patch ---
--- rhythmbox-0.9.6/lib/rb-preferences.h.fix-keybinding	2006-08-02 08:16:03.000000000 -0400
+++ rhythmbox-0.9.6/lib/rb-preferences.h	2006-10-22 21:58:29.000000000 -0400
@@ -69,6 +69,13 @@
 #define CONF_PLUGIN_ACTIVE_KEY		CONF_PLUGINS_PREFIX "/%s/active"
 #define CONF_PLUGIN_HIDDEN_KEY		CONF_PLUGINS_PREFIX "/%s/hidden"
 
+#define CONF_KEYBINDINGS_PREFIX "/apps/gnome_settings_daemon/keybindings"
+#define CONF_KEYBINDING_PLAY  CONF_KEYBINDINGS_PREFIX "/play"
+#define CONF_KEYBINDING_PAUSE CONF_KEYBINDINGS_PREFIX "/pause"
+#define CONF_KEYBINDING_STOP CONF_KEYBINDINGS_PREFIX "/stop"
+#define CONF_KEYBINDING_PREVIOUS CONF_KEYBINDINGS_PREFIX "/previous"
+#define CONF_KEYBINDING_NEXT CONF_KEYBINDINGS_PREFIX "/next"
+
 G_END_DECLS
 
 #endif /* __RB_PREFERENCES_H */
--- rhythmbox-0.9.6/shell/rb-shell-player.c.fix-keybinding	2006-09-24 07:03:49.000000000 -0400
+++ rhythmbox-0.9.6/shell/rb-shell-player.c	2006-10-22 22:07:49.000000000 -0400
@@ -23,6 +23,7 @@
 
 #include "config.h"
 
+#include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <time.h>
@@ -41,6 +42,11 @@
 #include <gdk/gdkx.h>
 #endif /* HAVE_MMKEYS */
 
+#define HAVE_XKB
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#include <gdk/gdkx.h>
+#endif 
 #include "rb-property-view.h"
 #include "rb-shell-player.h"
 #include "rb-stock-icons.h"
@@ -164,12 +170,31 @@
 
 #define CONF_STATE		CONF_PREFIX "/state"
 
+typedef enum 
+{
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE = 0,
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PLAY,
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PAUSE,
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_STOP,
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PREVIOUS,
+    RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NEXT,
+    RB_SHELL_PLAYER_NUMBER_OF_MEDIA_ACTIONS
+} RBShellPlayerMediaActionType;
+
+typedef struct 
+{ 
+  RBShellPlayerMediaActionType type;
+  guint gconf_global_keybinding_id;
+  GList *key_grab_list;
+} RBShellPlayerMediaAction; 
+
 struct RBShellPlayerPrivate
 {
 	RhythmDB *db;
 
 	gboolean syncing_state;
 	gboolean queue_only;
+	gboolean uses_xkb;
 
 	RBSource *selected_source;
 	RBSource *source;
@@ -201,6 +226,8 @@
 	guint gconf_play_order_id;
 	guint gconf_song_position_slider_visibility_id;
 
+	RBShellPlayerMediaAction media_actions[RB_SHELL_PLAYER_NUMBER_OF_MEDIA_ACTIONS - 1];
+
 	gboolean mute;
 	float volume;
 
@@ -686,6 +713,623 @@
 	GDK_THREADS_LEAVE ();
 }
 
+typedef struct {
+	KeyCode key_code;
+	guint xmodifier_mask; 
+	gint  group;
+} RBShellPlayerKeyGrab;
+
+static gint
+get_current_group (void)
+{
+	gint current_group = 0;
+
+#ifdef HAVE_XKB
+	XkbStateRec state = { 0 };
+
+
+	if (XkbGetState (GDK_DISPLAY (), XkbUseCoreKbd, &state) == Success)
+		current_group = (gint) XkbStateGroup (&state);
+#endif
+	return current_group;
+}
+
+static void
+set_current_group (gint group)
+{
+    XkbLockGroup (GDK_DISPLAY (), XkbUseCoreKbd, group);
+}
+
+static RBShellPlayerKeyGrab *
+rb_shell_player_key_grab_new (RBShellPlayer *player,
+			      KeyCode        key_code, 
+			      guint          xmodifier_mask,
+			      gint           group)
+{
+    RBShellPlayerKeyGrab *key_grab;
+    Screen *screen;
+    Window root_window;
+    gint old_group, status;
+
+    key_grab = NULL;
+
+    screen = GDK_SCREEN_XSCREEN (gtk_widget_get_screen (GTK_WIDGET (player)));
+    root_window = RootWindowOfScreen (screen);
+
+    if (group >= 0) {
+	    gdk_x11_grab_server ();
+	    old_group = get_current_group ();
+
+	    if (old_group != group)
+		    set_current_group (group);
+
+	    XkbChangeEnabledControls (GDK_DISPLAY (), XkbUseCoreKbd,
+				      XkbIgnoreGroupLockMask, 0);
+    } else {
+	    XkbChangeEnabledControls (GDK_DISPLAY (), XkbUseCoreKbd,
+				      XkbIgnoreGroupLockMask,
+				      XkbIgnoreGroupLockMask);
+    }
+
+    if (status = XGrabKey (GDK_DISPLAY (), key_code, xmodifier_mask, 
+		  root_window, True, GrabModeAsync, 
+		  GrabModeAsync) != True) {
+	    goto out;
+    }
+
+    key_grab = g_slice_new0 (RBShellPlayerKeyGrab);
+
+    key_grab->key_code = key_code;
+    key_grab->xmodifier_mask = xmodifier_mask;
+    key_grab->group = group;
+
+out:
+    if (group >= 0) {
+	    if (old_group != group)
+		    set_current_group (old_group);
+	    gdk_x11_ungrab_server ();
+    }
+
+    return key_grab;
+}
+
+static void
+rb_shell_player_key_grab_free (RBShellPlayer        *player,
+			       RBShellPlayerKeyGrab *key_grab)
+{
+    Screen *screen;
+    Window root_window;
+
+    screen = GDK_SCREEN_XSCREEN (gtk_widget_get_screen (GTK_WIDGET (player)));
+    root_window = RootWindowOfScreen (screen);
+
+    XUngrabKey (GDK_DISPLAY (), key_grab->key_code,
+		key_grab->xmodifier_mask,
+		root_window);
+
+    g_slice_free (RBShellPlayerKeyGrab, key_grab);
+}
+
+
+
+/* Translate real and virtual modifiers specified in GdkModifierType
+ * to something that can be passed to XGrabKey.
+ * Returns FALSE if a virtual modifier can not be mapped because it 
+ * is not bound.
+ */
+static gboolean
+modifiers_to_xmodifiers (GdkModifierType modifiers, guint *xmodifiers)
+{
+#ifdef HAVE_XKB
+    XkbDescRec *xkb;
+#endif
+    gboolean mask_is_translated;
+    guint new_xmodifiers;
+    gint i, j;
+
+    static struct {
+	    GdkModifierType type;
+	    guint xmodifier_mask;
+	    const gchar *virtual_name;
+    } modifier_mapping[] = {
+	    { GDK_SHIFT_MASK,   ShiftMask   },
+	    { GDK_LOCK_MASK,    LockMask    },
+	    { GDK_CONTROL_MASK, ControlMask },
+	    { GDK_MOD1_MASK,    Mod1Mask    },
+	    { GDK_MOD2_MASK,    Mod2Mask    },
+	    { GDK_MOD3_MASK,    Mod3Mask    },
+	    { GDK_MOD4_MASK,    Mod4Mask    },
+	    { GDK_MOD5_MASK,    Mod5Mask    },
+	    { GDK_BUTTON1_MASK, Button1Mask },
+	    { GDK_BUTTON2_MASK, Button2Mask },
+	    { GDK_BUTTON3_MASK, Button3Mask },
+	    { GDK_BUTTON4_MASK, Button4Mask },
+	    { GDK_BUTTON5_MASK, Button5Mask },
+	    { GDK_META_MASK,    0, "Meta"   },
+	    { GDK_SUPER_MASK,   0, "Super"  },
+	    { GDK_HYPER_MASK,   0, "Hyper"  },
+	    { (GdkModifierType) 0 }
+    };
+
+    mask_is_translated = TRUE;
+
+#if HAVE_XKB
+    xkb = XkbGetMap (GDK_DISPLAY (), XkbModifierMapMask | XkbVirtualModsMask, XkbUseCoreKbd);
+    XkbGetNames (GDK_DISPLAY (), XkbGroupNamesMask | XkbVirtualModNamesMask, xkb);
+#endif
+
+    new_xmodifiers = 0;
+    for (i = 0; modifier_mapping[i].type != (GdkModifierType) 0; i++) {
+	    if (modifiers & modifier_mapping[i].type) {
+		    Atom atom;
+
+		    if (modifier_mapping[j].xmodifier_mask != 0) {
+			    new_xmodifiers |= modifier_mapping[i].xmodifier_mask;
+			    continue;
+		    }
+
+#if HAVE_XKB
+		    g_assert (modifier_mapping[j].virtual_name != NULL);
+
+		    atom = gdk_x11_get_xatom_by_name (modifier_mapping[j].virtual_name);
+		    for (j = 0; j < XkbNumVirtualMods; j++) {
+
+			    if (atom == xkb->names->vmods[j]) {
+				    if (!xkb->server->vmods[j]) {
+					    XkbFreeKeyboard (xkb, 0, True);
+					    return FALSE;
+				    }
+
+				    new_xmodifiers |= xkb->server->vmods[j]; 
+				    break;
+			    }
+		    }
+#endif
+	    }
+    }
+
+#if HAVE_XKB
+    XkbFreeKeyboard (xkb, 0, True);
+#endif
+
+    if (xmodifiers)
+	    *xmodifiers = new_xmodifiers;
+
+    return TRUE;
+}
+
+static gboolean
+global_keybindings_are_independent_of_lock_modifiers (void)
+{
+    gboolean lock_modifiers_are_ignored;
+
+#ifdef HAVE_XKB
+    XkbDescRec *xkb;
+
+    lock_modifiers_are_ignored = FALSE;
+
+    xkb = XkbGetMap (GDK_DISPLAY (), 0, XkbUseCoreKbd);
+
+    if (XkbGetControls (GDK_DISPLAY (), 
+			XkbIgnoreLockModsMask, xkb) != Success)
+	    goto out;
+
+    if (xkb->ctrls == NULL)
+	    goto out;
+
+    if (!(xkb->ctrls->enabled_ctrls & XkbIgnoreGroupLockMask))
+	    goto out;
+
+    if (xkb->ctrls->ignore_lock.mask == 0)
+	    goto out;
+
+    /* We assume that a non-zero ignore_lock mask means
+     * that settings daemon has setup xkb to make XGrabKey
+     * have sane semantics (where changing capslock doesn't
+     * make key bindings stop working)
+     */
+    lock_modifiers_are_ignored = TRUE;
+out:
+    if (xkb->ctrls != NULL)
+	    XkbFreeControls (xkb, 0, True);
+
+    if (xkb != NULL)
+	    XkbFreeKeyboard (xkb, 0, True);
+#endif
+
+    return lock_modifiers_are_ignored;
+}
+
+static gboolean
+get_key_code_from_keyval (guint keyval, GdkModifierType modifiers,
+			  KeyCode *key_code, guint *xmodifiers)
+{
+    GdkKeymapKey *keys;
+    gint number_of_keys, i, current_group;
+
+    keys = NULL;
+    number_of_keys = 0;
+    if (!gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (),
+					    keyval, &keys, &number_of_keys))
+	    return FALSE;
+
+    current_group = get_current_group ();
+    for (i = 0; i < number_of_keys; i++) {
+	    if (keys[i].group == current_group)
+		    break;
+    }
+
+    if (i != number_of_keys) {
+	    if (key_code)
+		    *key_code = keys[i].keycode;
+    }
+
+    g_free (keys);
+
+    if (xmodifiers) {
+	    if (!modifiers_to_xmodifiers (modifiers, xmodifiers))
+		    return FALSE;
+    }
+
+    return i != number_of_keys;
+}
+static gboolean
+rb_shell_player_bind_keyval_globally_fallback (RBShellPlayer *player, 
+					       guint          keyval, 
+					       GdkModifierType modifiers)
+{
+    return FALSE;
+}
+
+static gboolean
+rb_shell_player_bind_keyval_globally (RBShellPlayer *player, 
+				      guint          keyval, 
+				      GdkModifierType modifiers,
+				      RBShellPlayerKeyGrab ***key_grabs)
+{
+    KeyCode key_code;
+    guint key_mask;
+    gboolean keyval_is_bound;
+    Window root_window;
+    GdkKeymapKey *keys;
+    gint number_of_keys, i, group;
+    GPtrArray *key_grabs_array;
+    RBShellPlayerKeyGrab *key_grab;
+
+
+#if 0
+    if (!player->priv->uses_xkb ||
+	!global_keybindings_are_independent_of_lock_modifiers ()) 
+	    return rb_shell_player_bind_keyval_globally_fallback (player,
+								  keyval,
+								  modifiers);
+#endif
+
+    /* So there is conceivably more than one "keycode" (think physical key)
+     * per "keyval" (think the symbol that actually gets produced,
+     * ~KeySym in X speak).   This can happen because some keyboards are
+     * configured to have multiple layouts (say qwerty and dvorak).
+     * 
+     * In those cases we really only want to grab one keycode because it 
+     * would be rather strange if "ctrl-o" got grabbed just because ctrl-s
+     * got grabbed (in the qwerty/dvorak case).  We can't grab for both and 
+     * then filter out (ignore) based on group, because then other apps
+     * won't be able to grab for the ignored keybindings.
+     *
+     * There is another multiple layout case that's also interesting here,
+     * a keyboard with multiple alphabets (say latin and cyrillic). 
+     * In these cases there is no overlap in between layouts so there won't
+     * be any conflict. We do want (say) ctrl-Ñ‹ to act like ctrl-s
+     * is pressed though. 
+     *
+     * Loosely, we could say the way we want to handle those two types of
+     * cases are "bind to keyval" and "bind to keycode", respectively.
+     *
+     * Now that's the semantics we want, how do we implement it? The core
+     * X protocol only gives us XGrabKey which takes a key code and a
+     * modifier mask and produces a passive grab that, by default,
+     * will only activate when the exact combination of modifiers 
+     * specified in the mask are present.  This is just a broken design,
+     * because it means global keybindings stop working if the user presses
+     * caps lock or scroll lock (which are modifiers, too!).  With a little
+     * XKB tweaking, (namely a call to XkbSetIgnoreLockMods after some prep
+     * work) we can change the semantics of this function to activate only
+     * when the interesting modifiers are present.  Now how do groups fit
+     * in? XKB doesn't give us a lot of help here.  We can only say whether
+     * the grab is per the current group or for ALL groups (by setting the
+     * IgnoreGroupLock boolean).  The latter case is fine for our
+     * "bind to keycode" case but we still need to figure out the "bind to
+     * keyval" case.  I guess the answer is GrabServer, set group, GrabKey,
+     * unset group, UnGrabServer. ick. So the final plan is, given a keyval
+     * and modifier mask:
+     *
+     * - loop through all the available groups of the system.  
+     *    - If only one group has the keyval in their layout then we look up
+     *      the key code associated with keyval and grab it for all groups.
+     *    - If more than one group has the keyval in their layout then all
+     *      those layouts need "bind to keyval" semantics.  We grab the
+     *      server, switch to each group in turn, grab the key with the passed
+     *      in mask for each group, revert to the original group and ungrab
+     *      the server.  The groups that don't have the keysym in their
+     *      layout need the "bind to keycode" semantics.  The keycode to
+     *      bind to is given by the keysym in the original group.
+     *
+     * One interesting note is that we are jumping through A LOT of hoops to
+     * support users with qwerty and dvorak layouts.  I'm wondering if it's 
+     * really, actually worth it.  If we didn't support that case, then we
+     * could just use IgnoreGroupLock and be done.
+     */
+
+    keyval_is_bound = FALSE;
+
+    keys = NULL;
+    number_of_keys = 0;
+    if (!gdk_keymap_get_entries_for_keyval (NULL, keyval, &keys, &number_of_keys))
+	    return FALSE;
+
+    g_assert (number_of_keys > 0);
+
+    root_window = 
+	    RootWindowOfScreen (GDK_SCREEN_XSCREEN (gtk_widget_get_screen (GTK_WIDGET (player))));
+
+    group = keys[0].group;
+    for (i = 0; i < number_of_keys; i++) {
+	    if (keys[i].group != group)
+		    break;
+    }
+
+    key_grabs_array = g_ptr_array_new ();
+
+    /* every entry for keyval is in the same group
+     * we need to use "bind to keycode" semantics
+     */
+    if (i == number_of_keys) {
+	    g_free (keys);
+	    keys = NULL;
+
+	    get_key_code_from_keyval (keyval, modifiers, &key_code, &key_mask);
+	    key_grab = rb_shell_player_key_grab_new (player, key_code, key_mask,
+						     -1);
+
+	    if (!key_grab)
+		    goto out;
+
+	    g_ptr_array_add (key_grabs_array, key_grab);
+	    g_ptr_array_add (key_grabs_array, NULL);
+
+	    keyval_is_bound = TRUE;
+	    goto out;
+
+    } else {
+	    /* Otherwise, we need to pick between
+	     * "bind to keyval" and "bind to keycode" based 
+	     * on whether the group matches the current group
+	     * (well actually the group that was active when the
+	     * user set the keybinding, but we don't currently
+	     * export that information)
+	     */
+	    for (i = 0; i < number_of_keys; i++) {
+
+		    /* "bind to keycode" 
+		     */
+		    if (keys[i].group == group) {
+			    key_grab = rb_shell_player_key_grab_new (player, 
+								     keys[i].keycode, 
+								     key_mask,
+								     -1);
+
+			    if (!key_grab)
+				    goto out;
+
+			    g_ptr_array_add (key_grabs_array, key_grab);
+			    continue;
+		    }
+
+		    /* "bind to keyval" 
+		     */
+		    set_current_group (keys[i].group);
+		    get_key_code_from_keyval (keyval, modifiers, &key_code, &key_mask);
+		    key_grab = rb_shell_player_key_grab_new (player, key_code, key_mask,
+							     keys[i].group);
+
+		    if (!key_grab)
+			    goto out;
+
+		    g_ptr_array_add (key_grabs_array, key_grab);
+	    }
+	    g_ptr_array_add (key_grabs_array, NULL);
+
+	    g_free (keys);
+	    keys = NULL;
+    }
+
+out:
+    if (keyval_is_bound && key_grabs != NULL)
+	    *key_grabs = (RBShellPlayerKeyGrab **) g_ptr_array_free (key_grabs_array, FALSE);
+    else
+	    g_ptr_array_free (key_grabs_array, TRUE);
+
+    return keyval_is_bound;
+}
+
+static void
+rb_shell_player_unbind_keyval_globally (RBShellPlayer         *player, 
+					RBShellPlayerKeyGrab **key_grabs)
+{
+    Window root_window;
+    int i;
+
+    root_window = 
+	    RootWindowOfScreen (GDK_SCREEN_XSCREEN (gtk_widget_get_screen (GTK_WIDGET (player))));
+
+    for (i = 0; key_grabs[i] != NULL; i++) {
+	    rb_shell_player_key_grab_free (player, key_grabs[i]);
+    }
+    g_free (key_grabs);
+}
+
+static gboolean
+rb_shell_player_bind_media_action (RBShellPlayer *player,
+				   RBShellPlayerMediaActionType action_type,
+				   const char *accelerator)
+{
+    guint keyval;
+    int i, action_index;
+    GdkModifierType modifiers;
+    RBShellPlayerKeyGrab *key_grab, **key_grabs;
+
+    keyval = 0;
+    modifiers = 0;
+
+    if (action_type == RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE)
+	    return FALSE;
+
+    if (accelerator == NULL)
+	    return FALSE;
+
+    gtk_accelerator_parse (accelerator, &keyval, &modifiers);
+
+    /* Not a valid accelerator name.  Maybe it's just a raw
+     * hex key code?
+     */
+    if (keyval == 0) {
+	    gulong key_code;
+	    char *end;
+
+	    end = NULL;
+	    errno = 0;
+	    key_code = strtoul (accelerator, &end, 0);
+	    g_print ("trying key code 0x%x for accelerator '%s'\n", key_code,
+		     accelerator);
+
+	    if ((accelerator[0] != '\0') && (*end == '\0') &&
+		(((KeyCode) key_code) == key_code) &&
+		((key_code != G_MAXLONG) || (errno == 0))) {
+		    key_grab = rb_shell_player_key_grab_new (player, key_code, 0,
+							     -1);
+
+		    if (!key_grab)
+			    return FALSE;
+
+		    for (i = 0; i < G_N_ELEMENTS (player->priv->media_actions); i++) 
+			    if (player->priv->media_actions[i].type == action_type) {
+				    player->priv->media_actions[i].key_grab_list = 
+					    g_list_append (player->priv->media_actions[i].key_grab_list, 
+							   key_grab);
+				    break;
+			    }
+	    }
+	    return FALSE;
+    }
+
+    key_grabs = NULL;
+    if (!rb_shell_player_bind_keyval_globally (player, keyval, modifiers, &key_grabs))
+	    return FALSE;
+
+    action_index = -1;
+    for (i = 0; i < G_N_ELEMENTS (player->priv->media_actions); i++) {
+	    if (player->priv->media_actions[i].type == action_type) {
+		    action_index = i;
+		    break;
+	    }
+    }
+
+    if (action_index >= 0)
+    for (i = 0; key_grabs[i] != NULL; i++) {
+	    player->priv->media_actions[action_index].key_grab_list = 
+		    g_list_append (player->priv->media_actions[action_index].key_grab_list, 
+				   key_grabs[i]);
+    }
+    g_free (key_grabs);
+
+    return TRUE;
+}
+
+static void
+rb_shell_player_unbind_media_action (RBShellPlayer *player,
+				     RBShellPlayerMediaActionType action_type)
+{
+    int i;
+
+    for (i = 0; i < G_N_ELEMENTS (player->priv->media_actions); i++) {
+	    if (player->priv->media_actions[i].type == action_type) {
+		    GList *tmp;
+		    GPtrArray *key_grabs_array;
+		    RBShellPlayerKeyGrab **key_grabs;
+
+		    key_grabs_array = g_ptr_array_new ();
+		    tmp = player->priv->media_actions[i].key_grab_list;
+		    while (tmp != NULL) {
+			    g_ptr_array_add (key_grabs_array, tmp->data);
+
+			    tmp = tmp->next;
+		    }
+		    g_ptr_array_add (key_grabs_array, NULL);
+		    key_grabs = (RBShellPlayerKeyGrab **) 
+			    g_ptr_array_free (key_grabs_array, FALSE);
+		    rb_shell_player_unbind_keyval_globally (player, key_grabs);
+		    break;
+	    }
+    }
+}
+
+static RBShellPlayerMediaActionType 
+rb_shell_player_get_action_type_from_conf_key (const char *conf_key)
+{
+    static const struct { 
+	    const char *conf_key;
+	    RBShellPlayerMediaActionType action_type;
+    } action_key_map[] = {
+	    { CONF_KEYBINDING_PLAY, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PLAY },
+	    { CONF_KEYBINDING_PAUSE, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PAUSE },
+	    { CONF_KEYBINDING_STOP, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_STOP },
+	    { CONF_KEYBINDING_PREVIOUS, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PREVIOUS },
+	    { CONF_KEYBINDING_NEXT, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NEXT },
+	    { NULL, RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE },
+    };
+    int i;
+
+    for (i = 0; action_key_map[i].conf_key != NULL; i++) {
+	    if (strcmp (action_key_map[i].conf_key, conf_key) == 0)
+		    return action_key_map[i].action_type;
+    }
+
+    return RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE;
+}
+
+static void 
+gconf_global_keybinding_changed (GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data)
+{
+    RBShellPlayer *player;
+    RBShellPlayerMediaActionType action_type;
+    const gchar *keybinding;
+
+    player = RB_SHELL_PLAYER (user_data);
+    keybinding = gconf_value_get_string (gconf_entry_get_value (entry));
+
+    action_type = rb_shell_player_get_action_type_from_conf_key (gconf_entry_get_key (entry));
+
+    rb_shell_player_unbind_media_action (player, action_type);
+    rb_shell_player_bind_media_action (player, action_type, keybinding);
+}
+
+static void
+rb_shell_player_init_xkb (RBShellPlayer *player)
+{
+#ifdef HAVE_XKB
+    int op_code, xkb_event_code, error_code, major, minor;
+
+    error_code = 0;
+    major = XkbMajorVersion; 
+    minor = XkbMinorVersion;
+
+    player->priv->uses_xkb = 
+	    XkbQueryExtension (GDK_DISPLAY (),
+			       &op_code, &xkb_event_code,
+			       &error_code, &major, &minor) == Success;
+#endif
+}
+
 static void
 rb_shell_player_init (RBShellPlayer *player)
 {
@@ -705,6 +1349,8 @@
 		exit (1);
 	}
 
+	rb_shell_player_init_xkb (player);
+
 	gtk_box_set_spacing (GTK_BOX (player), 12);
 	gtk_container_set_border_width (GTK_CONTAINER (player), 3);
 
@@ -758,6 +1404,40 @@
 					    player);
 
 #ifdef HAVE_MMKEYS
+	struct {
+		RBShellPlayerMediaActionType type;
+		const gchar                  *conf_key;
+	} type_key_map[] = { 
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PLAY, CONF_KEYBINDING_PLAY },
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PAUSE, CONF_KEYBINDING_PAUSE },
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_STOP, CONF_KEYBINDING_STOP },
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PREVIOUS, CONF_KEYBINDING_PREVIOUS },
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NEXT, CONF_KEYBINDING_NEXT },
+		{ RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE, NULL }
+	};
+	int i;
+
+	gconf_client_add_dir (eel_gconf_client_get_global (),
+			      CONF_KEYBINDINGS_PREFIX,
+			      GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+	for (i = 0; type_key_map[i].type != RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE; i++) {
+		const char *keybinding;
+
+		player->priv->media_actions[i].type = type_key_map[i].type;
+		player->priv->media_actions[i].gconf_global_keybinding_id = 
+			eel_gconf_notification_add (type_key_map[i].conf_key,
+						    (GConfClientNotifyFunc) gconf_global_keybinding_changed,
+						    player);
+		player->priv->media_actions[i].key_grab_list = NULL;
+		keybinding = gconf_client_get_string (eel_gconf_client_get_global (),
+						      type_key_map[i].conf_key,
+						      NULL);
+
+		rb_shell_player_bind_media_action (player, 
+						   player->priv->media_actions[i].type, 
+						   keybinding);
+	}
+
 	/* Enable Multimedia Keys */
 	rb_shell_player_init_mmkeys (player);
 #endif /* HAVE_MMKEYS */
@@ -2970,6 +3650,38 @@
 	}
 }
 
+static RBShellPlayerMediaActionType 
+rb_shell_player_find_action_from_key_press (RBShellPlayer *player,
+                                            XKeyEvent     *event)
+{
+      XkbDescRec *xkb;
+      int i;
+      int group;
+      KeySym keysym;
+
+      group = XkbGroupForCoreState (event->state);
+      for (i = 0; i < G_N_ELEMENTS (player->priv->media_actions); i++) {
+              GList *tmp;
+
+              tmp = player->priv->media_actions[i].key_grab_list;
+              while (tmp != NULL) {
+                      RBShellPlayerKeyGrab *key_grab;
+
+                      key_grab = (RBShellPlayerKeyGrab *) tmp->data;
+
+                      if ((key_grab->key_code == event->keycode) &&
+                         (((key_grab->group < 0) || (key_grab->group == group))) &&
+                          (key_grab->xmodifier_mask == event->state)) {
+                              return player->priv->media_actions[i].type;
+                      }
+
+                      tmp = tmp->next;
+              }
+      }
+
+      return RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NONE;
+}
+
 static GdkFilterReturn
 filter_mmkeys (GdkXEvent *xevent,
 	       GdkEvent *event,
@@ -2978,6 +3690,7 @@
 	XEvent *xev;
 	XKeyEvent *key;
 	RBShellPlayer *player;
+        RBShellPlayerMediaActionType action_type;
 	xev = (XEvent *) xevent;
 	if (xev->type != KeyPress) {
 		return GDK_FILTER_CONTINUE;
@@ -2986,41 +3699,38 @@
 	key = (XKeyEvent *) xevent;
 
 	player = (RBShellPlayer *)data;
+        action_type = rb_shell_player_find_action_from_key_press (player, key);
 
-	if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay) == key->keycode) {
-		rb_shell_player_playpause (player, FALSE, NULL);
-		return GDK_FILTER_REMOVE;
-	} else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause) == key->keycode) {
-		rb_shell_player_pause (player, NULL);
-		return GDK_FILTER_REMOVE;
-	} else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop) == key->keycode) {
-		rb_shell_player_stop (player);
-		return GDK_FILTER_REMOVE;
-	} else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev) == key->keycode) {
-		rb_shell_player_cmd_previous (NULL, player);
-		return GDK_FILTER_REMOVE;
-	} else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext) == key->keycode) {
-		rb_shell_player_cmd_next (NULL, player);
-		return GDK_FILTER_REMOVE;
-	} else {
-		return GDK_FILTER_CONTINUE;
-	}
+        switch (action_type) {
+               case RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PLAY:
+                       rb_shell_player_playpause (player, FALSE, NULL);
+                       return GDK_FILTER_REMOVE;
+               case RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PAUSE:
+                       rb_shell_player_pause (player, NULL);
+                       return GDK_FILTER_REMOVE;
+               case RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_STOP:
+                       rb_shell_player_stop (player);
+                       return GDK_FILTER_REMOVE;
+               case RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_PREVIOUS:
+                       rb_shell_player_cmd_previous (NULL, player);
+                       return GDK_FILTER_REMOVE;
+               case RB_SHELL_PLAYER_MEDIA_ACTION_TYPE_NEXT:
+                       rb_shell_player_cmd_next (NULL, player);
+                       return GDK_FILTER_REMOVE;
+               default:
+                       break;
+       }
+ 
+       return GDK_FILTER_CONTINUE;
 }
 
 static void
 rb_shell_player_init_mmkeys (RBShellPlayer *shell_player)
 {
-	gint keycodes[] = {0, 0, 0, 0, 0};
 	GdkDisplay *display;
 	GdkScreen *screen;
 	GdkWindow *root;
-	guint i, j;
-
-	keycodes[0] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay);
-	keycodes[1] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop);
-	keycodes[2] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev);
-	keycodes[3] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext);
-	keycodes[4] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause);
+	guint i;
 
 	display = gdk_display_get_default ();
 
@@ -3030,11 +3740,6 @@
 		if (screen != NULL) {
 			root = gdk_screen_get_root_window (screen);
 
-			for (j = 0; j < G_N_ELEMENTS (keycodes) ; j++) {
-				if (keycodes[j] != 0)
-					grab_mmkey (keycodes[j], root);
-			}
-
 			gdk_window_add_filter (root, filter_mmkeys,
 					       (gpointer) shell_player);
 		}


Index: rhythmbox.spec
===================================================================
RCS file: /cvs/dist/rpms/rhythmbox/devel/rhythmbox.spec,v
retrieving revision 1.83
retrieving revision 1.84
diff -u -r1.83 -r1.84
--- rhythmbox.spec	23 Oct 2006 01:46:27 -0000	1.83
+++ rhythmbox.spec	23 Oct 2006 02:06:17 -0000	1.84
@@ -40,11 +40,7 @@
 BuildRequires: perl-XML-Parser
 
 Patch2: rhythmbox-0.9.6-use-icon-name.patch
-Patch3: rhythmbox-0.9.5-dbus-deprecated.patch
-Patch4: rhythmbox-0.9.5-transparent.patch
-# http://bugzilla.gnome.org/show_bug.cgi?id=355713
-Patch5: rhythmbox-0.9.5-missing-radio-crash.patch
-Patch6: rhythmbox-0.9.5-fix-keybinding.patch
+Patch6: rhythmbox-0.9.6-fix-keybinding.patch
 
 %description
 Rhythmbox is an integrated music management application based on the powerful
@@ -55,9 +51,6 @@
 %prep
 %setup -q
 %patch2 -p1 -b .use-icon-name
-%patch3 -p1 -b .dbus-deprecated
-%patch4 -p1 -b .transparent
-%patch5 -p1 -b .missing-radio-crash
 %patch6 -p1 -b .fix-keybinding
 
 %build




More information about the fedora-cvs-commits mailing list