[Banshee-List] Multimediakeys Plugin



Hello,

im new to banshee and this list. I like banshee and it fits my needs.
The only thing i missed was the support for x11 multimedia keys. I found
this support in the code, but it is commented out. To readd it again
i've create a small plugin

I have "copied" the commented out code in latest banshee release 0.10.4
for multimedia key handling to a separate plugin. My changes are some
error handling and code for proper ungrabbing the keys.

I dont know why there was a decision to exclude this code from banshee,
but i think to add a plugin would be a good solution for the moment (or
longer) to get back this x11 integration capability. A least i hope
it :) And I hope this plugin will be added to the main plugins.

I doesnt tried to compile using the makefiles, maybe is miss something
to add in it.

Bye Danilo

MCS_FLAGS = -debug -unsafe
ASSEMBLY_NAME = MMKeys
ASSEMBLY = $(ASSEMBLY_NAME).dll

fsmondir = $(pkglibdir)/Banshee.Plugins
fsmon_SCRIPTS = $(ASSEMBLY) $(ASSEMBLY).mdb $(ASSEMBLY).config

ASSEMBLY_SOURCES = \
	$(srcdir)/MMKeysPlugin.cs \
	$(srcdir)/MMKeysSpecialKeys.cs

PKG_REFERENCES = \
	$(GTKSHARP_LIBS) \
	-r:System.Data \
	-r:Mono.Posix

schema_in_files = MMKeys.schemas.in
schemadir = $(GCONF_SCHEMA_FILE_DIR)
schema_DATA = $(schema_in_files:.schemas.in=.schemas)

@INTLTOOL_SCHEMAS_RULE@

$(ASSEMBLY): $(ASSEMBLY_SOURCES) 
	$(MCS) $(MCS_FLAGS) -target:library  -out:$@ $(ASSEMBLY_SOURCES) -r:$(top_builddir)/src/Banshee.Base/Banshee.Base.dll -r:$(top_builddir)/src/Banshee.Widgets/Banshee.Widgets.dll $(PKG_REFERENCES)

EXTRA_DIST = $(ASSEMBLY_SOURCES) $(ASSEMBLY).config.in $(schema_in_files)

CLEANFILES = $(ASSEMBLY) *.dll *.exe
DISTCLEANFILES = *.mdb Makefile.in $(schema_DATA)

if GCONF_SCHEMAS_INSTALL
install-data-local:
	if [ -z "$(DESTDIR)" ]; then \
		GCONF_CONFIG_SOURCE="" $(GCONFTOOL) --makefile-install-rule $(builddir)$(schema_DATA); \
	fi
endif

<configuration>
	<dllmap dll="libglib-2.0.so" target="libglib-2.0.so.0" />
	<dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0.so.0" />
	<dllmap dll="libgtk-win32-2.0-0.dll" target="libgtk-x11-2.0.so.0" />
	<dllmap dll="libbonobo-2.so" target="libbonobo-2.so.0" />
	<dllmap dll="gdk-x11-2.0" target="libgdk-x11-2.0.so.0" />
	<dllmap dll="libbanshee" target="@expanded_libdir@/@PACKAGE@/libbanshee.so" />
</configuration>
<?xml version="1.0"?>
<gconfschemafile>
  <schemalist>
    <schema>
      <key>/schemas/apps/Banshee/MMKeysPlugin/Enabled</key>
      <applyto>/apps/Banshee/MMKeysPlugin/Enabled</applyto>
      <owner>banshee</owner>
      <type>bool</type>
      <default>false</default>
      <locale name="C">
        <short>Enable the MultimediaKeys plugin</short>
        <long></long>
      </locale>
    </schema>
  </schemalist>
</gconfschemafile>

/* -*- Mode: csharp; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
/***************************************************************************
 *  MMKeysPlugin.cs
 *
 *  Written by Danilo Reinhardt (danilo reinhardt gmx net)
 ****************************************************************************/

/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),  
 *  to deal in the Software without restriction, including without limitation  
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
 *  and/or sell copies of the Software, and to permit persons to whom the  
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE. 
 */

using System;
using System.IO;
using Gtk;
using Gdk;
using Mono.Unix;

using Banshee.Base;


namespace Banshee.Plugins.MMKeysPlugin 
{
	public class MMKeysPlugin : Banshee.Plugins.Plugin
	{
		private Action playAction;
		private Action nextTrackAction;
		private Action prevTrackAction;
		
		private MMKeysSpecialKeys special_keys;
		
		protected override string ConfigurationName { get { return "MMKeysPlugin"; } }
        public override string DisplayName { get { return "Multimediakeys"; } }
	
		public override string Description {
            get {
                return Catalog.GetString(
                    "Adding support for configured gnome multimedia keys."
                );
            }
        }

        public override string [] Authors {
            get {
                return new string [] {
                    "Danilo Reinhardt"
                };
            }
        }
        
        protected override void PluginInitialize()
        {
        	playAction = Globals.ActionManager["PlayPauseAction"];
            nextTrackAction = Globals.ActionManager["NextAction"];
            prevTrackAction = Globals.ActionManager["PreviousAction"];
            
            try {
                if((bool)Globals.Configuration.Get(GConfKeys.EnableSpecialKeys)) {
                    special_keys = new MMKeysSpecialKeys();
                    special_keys.Delay = new TimeSpan(350 * TimeSpan.TicksPerMillisecond);
                    
                    special_keys.RegisterHandler(OnSpecialKeysPressed, 
                        MMKeysSpecialKey.AudioPlay,
                        MMKeysSpecialKey.AudioPrev,
                        MMKeysSpecialKey.AudioNext
                    );
                }
            } catch(GConf.NoSuchKeyException) {
            } catch(Exception e) {
                special_keys = null;
                Console.WriteLine (e.Message);
                LogCore.Instance.PushWarning(Catalog.GetString("Could not setup special keys"), e.Message, false);
            }
            Console.WriteLine ("MMKeys: loaded");
        }
        
        private void OnSpecialKeysPressed(object o, MMKeysSpecialKey key)
        {
            switch(key) {
                case MMKeysSpecialKey.AudioPlay:
                    playAction.Activate();
                    break;
                case MMKeysSpecialKey.AudioNext:
                    nextTrackAction.Activate();
                    break;
                case MMKeysSpecialKey.AudioPrev:
                    prevTrackAction.Activate();
                    break;
            }
        }
        
        protected override void PluginDispose()
        {
            special_keys.UnregisterHandler(OnSpecialKeysPressed, 
                        MMKeysSpecialKey.AudioPlay,
                        MMKeysSpecialKey.AudioPrev,
                        MMKeysSpecialKey.AudioNext
                    );
            special_keys.UngrabKeys();        
            special_keys = null;
        	playAction = null;
        	nextTrackAction = null;
        	prevTrackAction = null;
        	Console.WriteLine ("MMKeys: unloaded");
        }
        
        
	}
}
/* -*- Mode: csharp; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
/***************************************************************************
 *  MMKeysSpecialKeys.cs
 *
 *  Written by Danilo Reinhardt (danilo reinhardt gmx net)
 ****************************************************************************/

/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),  
 *  to deal in the Software without restriction, including without limitation  
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
 *  and/or sell copies of the Software, and to permit persons to whom the  
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE. 
 */
 
using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace Banshee.Plugins.MMKeysPlugin 
{
    public delegate void MMKeysSpecialKeyPressedHandler(object o, MMKeysSpecialKey key);

    public enum MMKeysSpecialKey {
        None = 0,
        AudioLowerVolume = 0x1008FF11,
        AudioMute = 0x1008FF12,
        AudioRaiseVolume = 0x1008FF13,
        AudioPlay = 0x1008FF14,
        AudioStop = 0x1008FF15,
        AudioPrev = 0x1008FF16,
        AudioNext = 0x1008FF17
    };

    public class MMKeysSpecialKeys
    {
        private Hashtable key_map = new Hashtable();
        private Hashtable key_registrations = new Hashtable();
        private TimeSpan raise_delay = new TimeSpan(0);
        private DateTime last_raise = DateTime.MinValue;
        
        private bool keysInited;
        
        public MMKeysSpecialKeys()
        {
            InitializeKeys();
        }
        
        ~MMKeysSpecialKeys() {
        	UnitializeKeys();
        }
        
        public void RegisterHandler(MMKeysSpecialKeyPressedHandler handler, params MMKeysSpecialKey [] specialKeys)
        {
        	foreach(MMKeysSpecialKey specialKey in specialKeys) {
        		if (key_map.Contains(specialKey)) {
	        		int key = (int)key_map[specialKey];
		            key_registrations[key] = Delegate.Combine(key_registrations[key] as Delegate, handler);
		        }
            }
        }
        
        public void UnregisterHandler(MMKeysSpecialKeyPressedHandler handler, params MMKeysSpecialKey [] specialKeys)
        {
            foreach(MMKeysSpecialKey specialKey in specialKeys) {
            	if (key_map.Contains(specialKey)) {
	                int key = (int)key_map[specialKey];
	                key_registrations[key] = Delegate.Remove(key_registrations[key] as Delegate, handler); 
	            }
            }
        }
        
        public void UngrabKeys() {
        	UnitializeKeys();	
        }
        
        private void InitializeKeys()
        {
            if (!keysInited) {
            	ArrayList kc_list = new ArrayList();
            
	            foreach(MMKeysSpecialKey key in Enum.GetValues(typeof(MMKeysSpecialKey))) {
	               	IntPtr xdisplay = gdk_x11_get_default_xdisplay();
	            	if (!xdisplay.Equals(IntPtr.Zero))
	            	{
		                int keycode = XKeysymToKeycode(xdisplay, key);
		                if (keycode != 0) 
		                {
		                	//Console.WriteLine ("MMKeys: Adding key " + key + " with keycode " + keycode);
			                key_map[keycode] = key;
			                key_map[key] = keycode;
			                kc_list.Add(keycode);
			            }
			            //else Console.WriteLine ("MMKeys: keycode for key "+ key + " is not defined");
		            }
		            //else Console.WriteLine ("MMKeys: xdisplay == null");
	            }
	            
	            for(int i = 0; i < Gdk.Display.Default.NScreens; i++) {
	                Gdk.Screen screen = Gdk.Display.Default.GetScreen(i);
	                foreach(int keycode in kc_list) 
	                {
	                	//Console.WriteLine ("MMKeys: Grabbing key with keycode " + keycode);
	                    GrabKey(screen.RootWindow, keycode);
	                }
	                screen.RootWindow.AddFilter(FilterKey);
	            }
	            keysInited = true;
	        }
            
            
        }
        
   
        private void UnitializeKeys() {
        	//Console.WriteLine ("MMKeys: UnitializeKeys()");
        	
            if (keysInited) {
    	    	ArrayList kc_list = new ArrayList();
    	    	Console.WriteLine ("1");
    	    	foreach(MMKeysSpecialKey key in Enum.GetValues(typeof(MMKeysSpecialKey))) {
	               	IntPtr xdisplay = gdk_x11_get_default_xdisplay();
	            	if (!xdisplay.Equals(IntPtr.Zero))
	            	{
		                int keycode = XKeysymToKeycode(xdisplay, key);
		                if (keycode != 0) 
		                {
		                	//Console.WriteLine ("MMKeys: Adding key " + key + " with keycode " + keycode);
			                key_map[keycode] = key;
			                key_map[key] = keycode;
			                kc_list.Add(keycode);
			    Console.WriteLine ("2");
			    }
			            //else Console.WriteLine ("MMKeys: keycode for key "+ key + " is not defined");
		            }
		            //else Console.WriteLine ("MMKeys: xdisplay == null");
	            }
	        Console.WriteLine ("3");
	        	for(int i = 0; i < Gdk.Display.Default.NScreens; i++) {
	                Gdk.Screen screen = Gdk.Display.Default.GetScreen(i);
	                foreach(int keycode in kc_list) 
	                {
	                	//Console.WriteLine ("MMKeys: Grabbing key with keycode " + keycode);
	                    UngrabKey(screen.RootWindow, keycode);
	                }
	                screen.RootWindow.RemoveFilter(FilterKey);
	            }
	            Console.WriteLine ("4");
	            keysInited = false;
	        }
            
        }
        
        private void GrabKey(Gdk.Window root, int keycode)
        {
            IntPtr xid = gdk_x11_drawable_get_xid(root.Handle);
            IntPtr xdisplay = gdk_x11_get_default_xdisplay();
            
            gdk_error_trap_push();
            
            XGrabKey(xdisplay, keycode, XModMask.None, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod2, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod5, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Lock, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Mod5, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Lock, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod5 | XModMask.Lock, xid, true, XGrabMode.Async, XGrabMode.Async);
            XGrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Mod5 | XModMask.Lock, xid, true, 
                XGrabMode.Async, XGrabMode.Async);
        
        	gdk_flush ();
            if(gdk_error_trap_pop() != 0) {
                Console.Error.WriteLine("MMKeys: Could not grab key {0} (maybe another application has grabbed this key)", keycode);
            }
        }
        
        private void UngrabKey(Gdk.Window root, int keycode)
        {
            IntPtr xid = gdk_x11_drawable_get_xid(root.Handle);
            IntPtr xdisplay = gdk_x11_get_default_xdisplay();
            
            gdk_error_trap_push();
            
            XUngrabKey(xdisplay, keycode, XModMask.None, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod2, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod5, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Lock, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Mod5, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Lock, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod5 | XModMask.Lock, xid);
            XUngrabKey(xdisplay, keycode, XModMask.Mod2 | XModMask.Mod5 | XModMask.Lock,xid);
        
        	gdk_flush ();
            if(gdk_error_trap_pop() != 0) {
                Console.Error.WriteLine("MMKeys: Could not ungrab key {0} (maybe another application has grabbed this key)", keycode);
            }
        }
        
        private Gdk.FilterReturn FilterKey(IntPtr xeventPtr, Gdk.Event gdkEvent)
        {
        	XKeyEvent xevent = (XKeyEvent)Marshal.PtrToStructure(xeventPtr, typeof(XKeyEvent));
            
            if(xevent.type != XEventName.KeyPress) {
                return Gdk.FilterReturn.Continue;
            }

            if(DateTime.Now - last_raise < raise_delay) {
                return Gdk.FilterReturn.Continue;
            }
            
            last_raise = DateTime.Now;

            int keycode = (int)xevent.keycode;
			object x = key_map [keycode];
			if (x == null)
				return Gdk.FilterReturn.Continue;

            MMKeysSpecialKey key = (MMKeysSpecialKey)key_map[keycode];
            
            if(key_registrations[keycode] != null) {
		    	x = key_registrations [keycode];
		    	if (x is MMKeysSpecialKeyPressedHandler){
                        ((MMKeysSpecialKeyPressedHandler)x)(this, key);	
		    	}
                return Gdk.FilterReturn.Remove;
            }
            
            return Gdk.FilterReturn.Continue;
        }
        
        public TimeSpan Delay {
            get {
                return raise_delay;
            }
            
            set {
                raise_delay = value;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct XKeyEvent
        {
            public XEventName type;
            public IntPtr serial;
            public bool send_event;
            public IntPtr display;
            public IntPtr window;
            public IntPtr root;
            public IntPtr subwindow;
            public IntPtr time;
            public int x;
            public int y;
            public int x_root;
            public int x_y;
            public uint state;
            public uint keycode;
            public bool same_screen;
        }

        [DllImport("libX11")]
        extern static int XKeysymToKeycode(IntPtr display, MMKeysSpecialKey keysym);

        [DllImport("libX11")]
        extern static void XGrabKey(IntPtr display, int keycode, XModMask modifiers, 
            IntPtr window, bool owner_events, XGrabMode pointer_mode, XGrabMode keyboard_mode);
            
        [DllImport("libX11")]
        extern static void XUngrabKey(IntPtr display, int keycode, XModMask modifiers, 
            IntPtr window);

        [DllImport("libgdk-x11-2.0")]
        extern static IntPtr gdk_x11_drawable_get_xid(IntPtr window);
        
        [DllImport("libgdk-x11-2.0")]
        extern static IntPtr gdk_x11_get_default_xdisplay();
        
        [DllImport("libgdk-x11-2.0")]
        extern static void gdk_error_trap_push();
        
        [DllImport("libgdk-x11-2.0")]
        extern static int gdk_error_trap_pop();
        
        [DllImport("libgdk-x11-2.0")]
        extern static void gdk_flush();
        
        [Flags]
        private enum XModMask {
            None    = 0,
            Shift   = 1 << 0,
            Lock    = 1 << 1,
            Control = 1 << 2,
            Mod1    = 1 << 3,
            Mod2    = 1 << 4,
            Mod3    = 1 << 5,
            Mod4    = 1 << 6,
            Mod5    = 1 << 7
        }
        
        private enum XGrabMode {
            Sync  = 0,
            Async = 1
        }
        
        private enum XEventName {
            KeyPress   = 2,
            KeyRelease = 3,
        }
    }
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]