banshee r4908 - in trunk/banshee: . src/Core/Banshee.Services src/Core/Banshee.Services/Banshee.Hardware src/Core/Banshee.Services/Banshee.MediaEngine src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage src/Dap/Banshee.Dap/Banshee.Dap src/Extensions/Banshee.AudioCd/Banshee.AudioCd



Author: abock
Date: Sun Jan 11 08:16:37 2009
New Revision: 4908
URL: http://svn.gnome.org/viewvc/banshee?rev=4908&view=rev

Log:
2009-01-11  Aaron Bockover  <abock gnome org>

    This commit addresses BNC #461677, allowing device commands to be sent
    to a new or existing process. Device commands allow Banshee to perform
    actions against a device, such as activating the source in the UI or
    starting playback of an Audio CD. Two .desktop files exist for Nautilus/GIO
    to use to activate device commands on Banshee.

    * src/Core/Banshee.Services/Banshee.Hardware/DeviceCommand.cs: Simple
    structure that parses device action arguments such as --device-activate=x
    or --device-activate-play=y

    * src/Core/Banshee.Services/Banshee.Hardware/HardwareManager.cs: Provide
    a DeviceCommand event which is raised whenever a DeviceCommand argument
    described above is passed; this provides a simple facility for services
    to listen to actions to be applied against devices (e.g. the play CD
    action from Nautilus)

    * src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs:
    Listen to the HW manager's DeviceCommand event and handle cdda actions

    * src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs: Listen to the HW
    manager's DeviceCommand and pass them off to children DapSources at the
    right time for actual handling

    * src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs:
    * src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs:
    * src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod/IpodSource.cs:
    Added/implemented CanHandleDeviceCommand so the DapService can know if
    a DeviceCommand action applies to the DAP

    * src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs:
    Fix a race where services may try to play a stream before the player
    engine has entered the READY state; this happens for example when Banshee
    is started with a --device-play or --device-activate-play command that
    may be handled before the player engine READYs



Added:
   trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/DeviceCommand.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/HardwareManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
   trunk/banshee/src/Core/Banshee.Services/Makefile.am
   trunk/banshee/src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod/IpodSource.cs
   trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
   trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs
   trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/DeviceCommand.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/DeviceCommand.cs	Sun Jan 11 08:16:37 2009
@@ -0,0 +1,104 @@
+//
+// DeviceCommand.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Banshee.Base;
+
+namespace Banshee.Hardware
+{
+    public delegate void DeviceCommandHandler (object o, DeviceCommand args);
+
+    [Flags]
+    public enum DeviceCommandAction
+    {
+        None = (0 << 0),
+        Activate = (1 << 0),
+        Play = (1 << 1)
+    }
+
+    public sealed class DeviceCommand : EventArgs
+    {
+        private DeviceCommandAction action;
+        public DeviceCommandAction Action {
+            get { return action; }
+        }
+
+        private string device_id;
+        public string DeviceId {
+            get { return device_id; }
+        }
+
+        private DeviceCommand (DeviceCommandAction action)
+        {
+            this.action = action;
+        }
+
+        internal static DeviceCommand ParseCommandLine (string command, string argument)
+        {
+            if (command == null || argument == null || !command.StartsWith ("device")) {
+                return null;
+            }
+
+            DeviceCommand d_command = null;
+            
+            switch (command) {
+                case "device-activate-play":
+                    d_command = new DeviceCommand (DeviceCommandAction.Activate | DeviceCommandAction.Play);
+                    break;
+                case "device-activate":
+                    d_command = new DeviceCommand (DeviceCommandAction.Activate);
+                    break;
+                case "device-play":
+                    d_command = new DeviceCommand (DeviceCommandAction.Play);
+                    break;
+                case "device":
+                    d_command = new DeviceCommand (DeviceCommandAction.None);
+                    break;
+                default:
+                    return null;
+            }
+
+            // FIXME: Should use GIO here to resolve UNIX device nodes or 
+            // HAL UDIs from the GIO URI that we were likely given by Nautilus
+
+            try {
+                Banshee.Base.SafeUri uri = new Banshee.Base.SafeUri (argument);
+                d_command.device_id = uri.IsLocalPath ? uri.LocalPath : argument;
+            } catch {
+                d_command.device_id = argument;
+            }
+
+            Hyena.Log.InformationFormat ("Received device command: action = {0}, device = {1}",
+                d_command.Action, d_command.DeviceId);
+
+            return d_command;
+        }
+    }
+}
+

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/HardwareManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/HardwareManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Hardware/HardwareManager.cs	Sun Jan 11 08:16:37 2009
@@ -4,7 +4,7 @@
 // Author:
 //   Aaron Bockover <abockover novell com>
 //
-// Copyright (C) 2008 Novell, Inc.
+// Copyright (C) 2008-2009 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -67,6 +67,8 @@
             manager.DeviceAdded += OnDeviceAdded;
             manager.DeviceRemoved += OnDeviceRemoved;
             
+            ServiceManager.Get<DBusCommandService> ().ArgumentPushed += OnCommandLineArgument;
+
             AddinManager.AddExtensionNodeHandler ("/Banshee/Platform/HardwareDeviceProvider", OnExtensionChanged);
         }
         
@@ -79,6 +81,8 @@
                     manager.Dispose ();
                     manager = null;
                 }
+
+                ServiceManager.Get<DBusCommandService> ().ArgumentPushed -= OnCommandLineArgument;
                 
                 if (custom_device_providers != null) {
                     foreach (ICustomDeviceProvider provider in custom_device_providers.Values) {
@@ -107,6 +111,62 @@
                 }
             }
         }
+
+#region Device Command Line Argument Handling
+
+        private event DeviceCommandHandler device_command;
+        public event DeviceCommandHandler DeviceCommand {
+            add {
+                try {
+                    device_command += value;
+                } finally {
+                    if (value != null && StartupDeviceCommand != null) {
+                        value (this, StartupDeviceCommand);
+                    }
+                }
+            }
+            
+            remove { device_command -= value; }
+        }           
+
+        private Banshee.Hardware.DeviceCommand startup_device_command;
+        private bool startup_device_command_checked = false;
+
+        public Banshee.Hardware.DeviceCommand StartupDeviceCommand {
+            get {
+                lock (this) {
+                    if (startup_device_command_checked) {
+                        return startup_device_command;
+                    }
+
+                    startup_device_command_checked = true;
+
+                    foreach (KeyValuePair<string, string> argument in Banshee.Base.ApplicationContext.CommandLine.Arguments) {
+                        startup_device_command = Banshee.Hardware.DeviceCommand.ParseCommandLine (argument.Key, argument.Value);
+                        if (startup_device_command != null) {
+                            break;
+                        }
+                    }
+
+                    return startup_device_command;
+                }
+            }
+        }
+
+        private void OnCommandLineArgument (string argument, object value, bool isFile)
+        {
+            Banshee.Hardware.DeviceCommand command = Banshee.Hardware.DeviceCommand.ParseCommandLine (argument, value.ToString ());
+            if (command == null) {
+                return;
+            }
+
+            DeviceCommandHandler handler = device_command;
+            if (handler != null) {
+                handler (this, command);
+            }
+        }
+
+#endregion
         
         private void OnDeviceAdded (object o, DeviceAddedArgs args)
         {

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	Sun Jan 11 08:16:37 2009
@@ -53,6 +53,8 @@
         private PlayerEngine active_engine;
         private PlayerEngine default_engine;
         private PlayerEngine pending_engine;
+        private object pending_playback_for_not_ready;
+        private bool pending_playback_for_not_ready_play;
 
         private string preferred_engine_id = null;
 
@@ -183,6 +185,12 @@
                         }
                     }
                 }
+
+                if (pending_playback_for_not_ready != null) {
+                    OpenCheck (pending_playback_for_not_ready, pending_playback_for_not_ready_play);
+                    pending_playback_for_not_ready = null;
+                    pending_playback_for_not_ready_play = false;
+                }
             }
             
             DBusPlayerStateHandler dbus_handler = dbus_state_changed;
@@ -272,8 +280,7 @@
             }
         
             try {
-                OpenCheck (track);
-                active_engine.Play ();
+                OpenCheck (track, true);
             } catch (Exception e) {
                 Log.Exception (e);
                 Log.Error (Catalog.GetString ("Problem with Player Engine"), e.Message, true);
@@ -281,12 +288,18 @@
                 ActiveEngine = default_engine;
             }
         }
-        
+
         private void OpenCheck (object o)
         {
+            OpenCheck (o, false);
+        }
+        
+        private void OpenCheck (object o, bool play)
+        {
             if (CurrentState == PlayerState.NotReady) {
-                throw new InvalidOperationException (String.Format ("Player engine {0} is in the NotReady state", 
-                    active_engine.GetType ().FullName));
+                pending_playback_for_not_ready = o;
+                pending_playback_for_not_ready_play = play;
+                return;
             }
         
             SafeUri uri = null;
@@ -313,6 +326,10 @@
                 active_engine.Open (uri);
                 incremented_last_played = false;
             }
+
+            if (play) {
+                active_engine.Play ();
+            }
         }
 
         private bool incremented_last_played = true;

Modified: trunk/banshee/src/Core/Banshee.Services/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Makefile.am	Sun Jan 11 08:16:37 2009
@@ -51,6 +51,7 @@
 	Banshee.Equalizer/EqualizerManager.cs \
 	Banshee.Equalizer/EqualizerSetting.cs \
 	Banshee.Equalizer/EqualizerSettingEvent.cs \
+	Banshee.Hardware/DeviceCommand.cs \
 	Banshee.Hardware/HardwareManager.cs \
 	Banshee.Hardware/IBlockDevice.cs \
 	Banshee.Hardware/ICdromDevice.cs \

Modified: trunk/banshee/src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod/IpodSource.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod/IpodSource.cs	(original)
+++ trunk/banshee/src/Dap/Banshee.Dap.Ipod/Banshee.Dap.Ipod/IpodSource.cs	Sun Jan 11 08:16:37 2009
@@ -133,6 +133,16 @@
             Dispose ();
         }
         
+        protected override bool CanHandleDeviceCommand (DeviceCommand command)
+        {
+            try {
+                SafeUri uri = new SafeUri (command.DeviceId);
+                return IpodDevice.MountPoint.StartsWith (uri.LocalPath);
+            } catch {
+                return false;
+            }
+        }
+        
         protected override IDeviceMediaCapabilities MediaCapabilities {
             get { return ipod_device.Parent.MediaCapabilities ?? base.MediaCapabilities; }
         }

Modified: trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs	(original)
+++ trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs	Sun Jan 11 08:16:37 2009
@@ -537,6 +537,16 @@
                 volume.Eject ();
             }
         }
+        
+        protected override bool CanHandleDeviceCommand (DeviceCommand command)
+        {
+            try {
+                SafeUri uri = new SafeUri (command.DeviceId);
+                return BaseDirectory.StartsWith (uri.LocalPath);
+            } catch {
+                return false;
+            }
+        }
 
         private string GetTrackPath (TrackInfo track, string ext)
         {

Modified: trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs	(original)
+++ trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs	Sun Jan 11 08:16:37 2009
@@ -47,6 +47,7 @@
     public class DapService : IExtensionService, IDelayedInitializeService, IDisposable
     {
         private Dictionary<string, DapSource> sources;
+        private List<DeviceCommand> unhandled_device_commands;
         private List<TypeExtensionNode> supported_dap_types;
         private bool initialized;
         private object sync = new object ();
@@ -68,6 +69,7 @@
                 
                 ServiceManager.HardwareManager.DeviceAdded += OnHardwareDeviceAdded;
                 ServiceManager.HardwareManager.DeviceRemoved += OnHardwareDeviceRemoved;
+                ServiceManager.HardwareManager.DeviceCommand += OnDeviceCommand;
                 ServiceManager.SourceManager.SourceRemoved += OnSourceRemoved;
                 initialized = true;
             }
@@ -114,6 +116,7 @@
                 
                 ServiceManager.HardwareManager.DeviceAdded -= OnHardwareDeviceAdded;
                 ServiceManager.HardwareManager.DeviceRemoved -= OnHardwareDeviceRemoved;
+                ServiceManager.HardwareManager.DeviceCommand -= OnDeviceCommand;
                 ServiceManager.SourceManager.SourceRemoved -= OnSourceRemoved;
                 
                 List<DapSource> dap_sources = new List<DapSource> (sources.Values);
@@ -206,6 +209,25 @@
                     ThreadAssist.ProxyToMain (delegate {
                         ServiceManager.SourceManager.AddSource (source);
                         source.NotifyUser ();
+                        
+                        // If there are any queued device commands, see if they are to be
+                        // handled by this new DAP (e.g. --device-activate=file:///media/disk)
+                        try {
+                            if (service.unhandled_device_commands != null) {
+                                foreach (DeviceCommand command in service.unhandled_device_commands) {
+                                    if (source.CanHandleDeviceCommand (command)) {
+                                        service.HandleDeviceCommand (source, command.Action);
+                                        service.unhandled_device_commands.Remove (command);
+                                        if (service.unhandled_device_commands.Count == 0) {
+                                            service.unhandled_device_commands = null;
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            Log.Exception (e);
+                        }
                     });
                 }
             }
@@ -252,6 +274,37 @@
             UnmapDevice (args.DeviceUuid);
         }
         
+      
+#region DeviceCommand Handling
+
+        private void HandleDeviceCommand (DapSource source, DeviceCommandAction action)
+        {
+            if ((action & DeviceCommandAction.Activate) != 0) {
+                ServiceManager.SourceManager.SetActiveSource (source);
+            }
+        }
+        
+        private void OnDeviceCommand (object o, DeviceCommand command)
+        {
+            lock (this) {
+                // Check to see if we have an already mapped disc volume that should
+                // handle this incoming command; if not, queue it for later devices
+                foreach (DapSource source in sources.Values) {
+                    if (source.CanHandleDeviceCommand (command)) {
+                        HandleDeviceCommand (source, command.Action);
+                        return;
+                    }
+                }
+                
+                if (unhandled_device_commands == null) {
+                    unhandled_device_commands = new List<DeviceCommand> ();
+                }
+                unhandled_device_commands.Add (command);
+            }
+        }
+        
+#endregion
+        
         string IService.ServiceName {
             get { return "DapService"; }
         }

Modified: trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs	(original)
+++ trunk/banshee/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs	Sun Jan 11 08:16:37 2009
@@ -469,6 +469,11 @@
                 return preferred_config;
             }
         }
+        
+        internal protected virtual bool CanHandleDeviceCommand (DeviceCommand command)
+        {
+            return false;
+        }
 
         private string [] acceptable_mimetypes;
         public string [] AcceptableMimeTypes {

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs	Sun Jan 11 08:16:37 2009
@@ -43,6 +43,7 @@
     public class AudioCdService : IExtensionService, IDisposable
     {
         private Dictionary<string, AudioCdSource> sources;
+        private List<DeviceCommand> unhandled_device_commands;
         private Page pref_page;
         private Section pref_section;
         private uint global_interface_id;
@@ -64,6 +65,7 @@
                 
                 ServiceManager.HardwareManager.DeviceAdded += OnHardwareDeviceAdded;
                 ServiceManager.HardwareManager.DeviceRemoved += OnHardwareDeviceRemoved;
+                ServiceManager.HardwareManager.DeviceCommand += OnDeviceCommand;
                 
                 SetupActions ();
             }
@@ -76,6 +78,7 @@
             
                 ServiceManager.HardwareManager.DeviceAdded -= OnHardwareDeviceAdded;
                 ServiceManager.HardwareManager.DeviceRemoved -= OnHardwareDeviceRemoved;
+                ServiceManager.HardwareManager.DeviceCommand -= OnDeviceCommand;
                 
                 foreach (AudioCdSource source in sources.Values) {
                     source.Dispose ();
@@ -107,6 +110,26 @@
                     AudioCdSource source = new AudioCdSource (this, new AudioCdDiscModel (volume));
                     sources.Add (volume.Uuid, source);
                     ServiceManager.SourceManager.AddSource (source);
+
+                    // If there are any queued device commands, see if they are to be
+                    // handled by this new volume (e.g. --device-activate-play=cdda://sr0/)
+                    try {
+                        if (unhandled_device_commands != null) {
+                            foreach (DeviceCommand command in unhandled_device_commands) {
+                                if (DeviceCommandMatchesSource (source, command)) {
+                                    HandleDeviceCommand (source, command.Action);
+                                    unhandled_device_commands.Remove (command);
+                                    if (unhandled_device_commands.Count == 0) {
+                                        unhandled_device_commands = null;
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    } catch (Exception e) {
+                        Log.Exception (e);
+                    }
+                    
                     Log.DebugFormat ("Mapping audio CD ({0})", volume.Uuid);
                 }
             }
@@ -142,6 +165,59 @@
             }
         }
         
+#region DeviceCommand Handling
+
+        private bool DeviceCommandMatchesSource (AudioCdSource source, DeviceCommand command)
+        {
+            if (command.DeviceId.StartsWith ("cdda:")) {
+                try {
+                    Uri uri = new Uri (command.DeviceId);
+                    string match_device_node = String.Format ("{0}{1}", uri.Host, 
+                        uri.AbsolutePath).TrimEnd ('/', '\\');
+                    string device_node = source.DiscModel.Volume.DeviceNode;
+                    return device_node.EndsWith (match_device_node);
+                } catch {
+                }
+            }
+            
+            return false;
+        }
+
+        private void HandleDeviceCommand (AudioCdSource source, DeviceCommandAction action)
+        {
+            if ((action & DeviceCommandAction.Activate) != 0) {
+                ServiceManager.SourceManager.SetActiveSource (source);
+            }
+
+            if ((action & DeviceCommandAction.Play) != 0) {
+                ServiceManager.PlaybackController.NextSource = source;
+                if (!ServiceManager.PlayerEngine.IsPlaying ()) {
+                    ServiceManager.PlaybackController.Next ();
+                }
+            }
+        }
+        
+        private void OnDeviceCommand (object o, DeviceCommand command)
+        {
+            lock (this) {
+                // Check to see if we have an already mapped disc volume that should
+                // handle this incoming command; if not, queue it for later discs
+                foreach (AudioCdSource source in sources.Values) {
+                    if (DeviceCommandMatchesSource (source, command)) {
+                        HandleDeviceCommand (source, command.Action);
+                        return;
+                    }
+                }
+                
+                if (unhandled_device_commands == null) {
+                    unhandled_device_commands = new List<DeviceCommand> ();
+                }
+                unhandled_device_commands.Add (command);
+            }
+        }
+        
+#endregion
+
 #region Preferences        
         
         private void InstallPreferences ()



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