├── LICENSE
├── Toastify
├── spotify.ico
├── SpotifyLogo.png
├── ManagedWinapi.dll
├── Resources
│ ├── spotify.ico
│ ├── InfoLarge.png
│ ├── thumbs_up.png
│ ├── WarningLarge.png
│ ├── WarningSmall.png
│ ├── thumbs_down.png
│ └── ManagedWinapiNativeHelper.dll
├── SpotifyToastifyLogo.png
├── ToastifyAccessDenied.png
├── SpotifyToastifyUpdateLogo.png
├── app.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Toastify.csproj.vspscc
├── packages.config
├── App.xaml
├── Spotify
│ ├── SpotifyAction.cs
│ ├── Song.cs
│ ├── SpotifyApiClient.cs
│ └── Spotify.cs
├── Toastify.csproj.user
├── BoolToVisibleConverter.cs
├── OppositeBoolConverter.cs
├── OppositeBoolToVisibleConverter.cs
├── App.xaml.cs
├── ScreenHelper.cs
├── LastInputDebug.cs
├── About.xaml.cs
├── VersionChecker.cs
├── WinHelper.cs
├── Toast.xaml
├── About.xaml
├── Telemetry.cs
├── Win32.cs
├── Settings.xaml.cs
├── HotKey.cs
├── Toastify.csproj
├── VolumeHelper.cs
└── Settings.xaml
├── InstallationScript
├── KillProcWMI.dll
├── killproc_README.txt
├── Install.nsi
└── DotNET.nsh
├── AutoHotkey.Interop
├── x64
│ └── AutoHotkey.dll
├── x86
│ └── AutoHotkey.dll
├── AutoHotkey.Interop.csproj.vspscc
├── SafeLibraryHandle.cs
├── Properties
│ └── AssemblyInfo.cs
├── Util.cs
├── AutoHotkey.Interop.csproj
├── AutoHotkeyEngine.cs
└── AutoHotkeyDll.cs
├── packages
└── repositories.config
├── Toastify.vssscc
├── PluginBase
├── PluginBase.csproj.vspscc
├── PluginBase.cs
├── Properties
│ └── AssemblyInfo.cs
└── Toastify.Plugin.csproj
├── ExamplePlugin
├── ExamplePlugin.csproj.vspscc
├── Properties
│ └── AssemblyInfo.cs
├── ExamplePlugin.cs
└── ExamplePlugin.csproj
├── Toastify.sln
├── BuildProcessTemplates
└── UpgradeTemplate.xaml
└── .gitignore
/LICENSE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/LICENSE
--------------------------------------------------------------------------------
/Toastify/spotify.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/spotify.ico
--------------------------------------------------------------------------------
/Toastify/SpotifyLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/SpotifyLogo.png
--------------------------------------------------------------------------------
/Toastify/ManagedWinapi.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/ManagedWinapi.dll
--------------------------------------------------------------------------------
/Toastify/Resources/spotify.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/spotify.ico
--------------------------------------------------------------------------------
/Toastify/Resources/InfoLarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/InfoLarge.png
--------------------------------------------------------------------------------
/Toastify/Resources/thumbs_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/thumbs_up.png
--------------------------------------------------------------------------------
/Toastify/SpotifyToastifyLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/SpotifyToastifyLogo.png
--------------------------------------------------------------------------------
/InstallationScript/KillProcWMI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/InstallationScript/KillProcWMI.dll
--------------------------------------------------------------------------------
/Toastify/Resources/WarningLarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/WarningLarge.png
--------------------------------------------------------------------------------
/Toastify/Resources/WarningSmall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/WarningSmall.png
--------------------------------------------------------------------------------
/Toastify/Resources/thumbs_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/thumbs_down.png
--------------------------------------------------------------------------------
/Toastify/ToastifyAccessDenied.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/ToastifyAccessDenied.png
--------------------------------------------------------------------------------
/AutoHotkey.Interop/x64/AutoHotkey.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/AutoHotkey.Interop/x64/AutoHotkey.dll
--------------------------------------------------------------------------------
/AutoHotkey.Interop/x86/AutoHotkey.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/AutoHotkey.Interop/x86/AutoHotkey.dll
--------------------------------------------------------------------------------
/Toastify/SpotifyToastifyUpdateLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/SpotifyToastifyUpdateLogo.png
--------------------------------------------------------------------------------
/Toastify/Resources/ManagedWinapiNativeHelper.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nachmore/toastify/HEAD/Toastify/Resources/ManagedWinapiNativeHelper.dll
--------------------------------------------------------------------------------
/packages/repositories.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Toastify/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Toastify/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Toastify.vssscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
10 | }
11 |
--------------------------------------------------------------------------------
/Toastify/Toastify.csproj.vspscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 | }
11 |
--------------------------------------------------------------------------------
/Toastify/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PluginBase/PluginBase.csproj.vspscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 | }
11 |
--------------------------------------------------------------------------------
/ExamplePlugin/ExamplePlugin.csproj.vspscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 | }
11 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/AutoHotkey.Interop.csproj.vspscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 | }
11 |
--------------------------------------------------------------------------------
/Toastify/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/InstallationScript/killproc_README.txt:
--------------------------------------------------------------------------------
1 | Copy to [NSIS install]\Plugins to use
2 |
3 | KillProcWMI , is an NSIS plugin based on the original KillProc plugin. KillProcWMI uses WMI to acheive the same results, which avoids problems with 32bit processes not being able to kill 64 bit processes.
4 |
5 | All Code is freely provided, no guarantees or warranties about its quality, use at your own risk.
6 |
7 | Jared Allen.
8 | ChironexSoftware.com
--------------------------------------------------------------------------------
/Toastify/Spotify/SpotifyAction.cs:
--------------------------------------------------------------------------------
1 | namespace Toastify
2 | {
3 | public enum SpotifyAction : long
4 | {
5 | None = 0,
6 | ShowToast = 1,
7 | ShowSpotify = 2,
8 | CopyTrackInfo = 3,
9 | SettingsSaved = 4,
10 | PasteTrackInfo = 5,
11 | ThumbsUp = 6, // not usable, left in for future (hopefully!)
12 | ThumbsDown = 7, // not usable, left in for future (hopefully!)
13 | PlayPause = 917504,
14 | Mute = 524288,
15 | VolumeDown = 589824,
16 | VolumeUp = 655360,
17 | Stop = 851968,
18 | PreviousTrack = 786432,
19 | NextTrack = 720896,
20 | FastForward = 49 << 16,
21 | Rewind = 50 << 16,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Toastify/Toastify.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ProjectFiles
5 | publish\
6 | http://toastify.codeplex.com/
7 |
8 |
9 |
10 |
11 | en-US
12 | false
13 |
14 |
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Toastify/BoolToVisibleConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Data;
3 | using System.Windows;
4 |
5 | namespace Toastify
6 | {
7 | public class BoolToVisibleConverter : IValueConverter
8 | {
9 |
10 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
11 | {
12 | // Note: will throw a cast exception if you throw the wrong type at it. Good :)
13 | return ((bool)value ? Visibility.Visible : Visibility.Collapsed);
14 | }
15 |
16 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
17 | {
18 | throw new NotImplementedException();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Toastify/OppositeBoolConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Data;
3 |
4 | namespace Toastify
5 | {
6 | public class OppositeBoolConverter : IValueConverter
7 | {
8 |
9 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
10 | {
11 | // Note: don't check validity etc, since we'll need to throw an exception anyway, may as well let
12 | // that exception be here and not waste time :)
13 | return !(bool)value;
14 | }
15 |
16 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
17 | {
18 | throw new NotImplementedException();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Toastify/OppositeBoolToVisibleConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Data;
3 | using System.Windows;
4 |
5 | namespace Toastify
6 | {
7 | public class OppositeBoolToVisibleConverter : IValueConverter
8 | {
9 |
10 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
11 | {
12 | // Note: will throw a cast exception if you throw the wrong type at it. Good :)
13 | return (!(bool)value ? Visibility.Visible : Visibility.Collapsed);
14 | }
15 |
16 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
17 | {
18 | throw new NotImplementedException();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/PluginBase/PluginBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Toastify.Plugin
4 | {
5 | public interface IPluginBase : IDisposable
6 | {
7 | ///
8 | /// Is called directly after the constructor.
9 | ///
10 | /// Data from the Settings element in the xml.
11 | void Init(string settings);
12 | ///
13 | /// Is called when Toastify is first started. After Init().
14 | ///
15 | void Started();
16 | ///
17 | /// Is called when Toastify is closing.
18 | ///
19 | void Closing();
20 | ///
21 | /// Is called on track change.
22 | ///
23 | ///
24 | ///
25 | void TrackChanged(string artist, string title);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Toastify/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Toastify.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/SafeLibraryHandle.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32.SafeHandles;
2 | using System;
3 | using System.Runtime.ConstrainedExecution;
4 | using System.Runtime.InteropServices;
5 | using System.Security;
6 | using System.Security.Permissions;
7 |
8 | namespace AutoHotkey.Interop
9 | {
10 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
11 | internal sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
12 | {
13 | #region Native Calls
14 |
15 | [DllImport("kernel32.dll", SetLastError = true)]
16 | public static extern SafeLibraryHandle LoadLibrary(string lpFileName);
17 |
18 | [SuppressUnmanagedCodeSecurity]
19 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
20 | [DllImport("kernel32.dll", SetLastError = true)]
21 | private static extern bool FreeLibrary(IntPtr hModule);
22 |
23 | #endregion
24 |
25 | private SafeLibraryHandle() : base(true) { }
26 |
27 | protected override bool ReleaseHandle()
28 | {
29 | return FreeLibrary(handle);
30 | }
31 |
32 | protected override void Dispose(bool disposing)
33 | {
34 | base.Dispose(disposing);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Toastify/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Threading;
4 |
5 | namespace Toastify
6 | {
7 | //Special entry point to allow for single instance check
8 | public class EntryPoint
9 | {
10 | [STAThread]
11 | public static void Main(string[] args)
12 | {
13 | string appSpecificGuid = "{B8F3CA50-CE27-4ffa-A812-BBE1435C9485}";
14 | using (Mutex m = new Mutex(true, appSpecificGuid, out bool exclusive))
15 | {
16 | if (exclusive)
17 | {
18 | App app = new App();
19 | app.InitializeComponent();
20 |
21 | LastInputDebug.Start();
22 |
23 | app.Run();
24 | }
25 | else
26 | {
27 | MessageBox.Show("Toastify is already running!\n\nLook for the blue icon in your system tray.", "Toastify Already Running", MessageBoxButton.OK, MessageBoxImage.Information);
28 | }
29 | }
30 | }
31 | }
32 |
33 | ///
34 | /// Interaction logic for App.xaml
35 | ///
36 | public partial class App : Application
37 | {
38 | private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
39 | {
40 | Telemetry.TrackException(e.Exception);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Toastify/ScreenHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace Toastify
4 | {
5 | public static class ScreenHelper
6 | {
7 |
8 | private const int SCREEN_RIGHT_MARGIN = 5;
9 | private const int SCREEN_TOP_MARGIN = 5;
10 |
11 | public static Point GetDPIRatios()
12 | {
13 | var presentationSource = PresentationSource.FromVisual(Toast.Current);
14 |
15 | // if we hit this then Settings were loaded before the Toast window was
16 | System.Diagnostics.Debug.Assert(presentationSource != null);
17 |
18 | if (presentationSource == null)
19 | {
20 | // return a default dpi ratio of 1 (96dpi)
21 | return new Point(1, 1);
22 | }
23 |
24 | return new Point(
25 | presentationSource.CompositionTarget.TransformToDevice.M11,
26 | presentationSource.CompositionTarget.TransformToDevice.M22
27 | );
28 | }
29 |
30 | public static System.Windows.Point GetDefaultToastPosition(double width, double height)
31 | {
32 | var screenRect = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
33 |
34 | var dpiRatio = GetDPIRatios();
35 |
36 | return new System.Windows.Point
37 | (
38 | (screenRect.Width / dpiRatio.X) - width - SCREEN_RIGHT_MARGIN,
39 | (screenRect.Height / dpiRatio.Y) - height - SCREEN_TOP_MARGIN
40 | );
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Toastify/Spotify/Song.cs:
--------------------------------------------------------------------------------
1 | namespace Toastify
2 | {
3 | public class Song
4 | {
5 | ///
6 | /// Is this a real Song or is Spotify not playing anything?
7 | ///
8 | /// Note: Window title doesn't change regardless of UI language
9 | ///
10 | public bool IsValid =>
11 | (!string.IsNullOrEmpty(Artist) && Artist != "Spotify Free" && Artist != "Spotify Premium") ||
12 | (!string.IsNullOrEmpty(Track));
13 |
14 | public string Artist { get; set; }
15 | public string Track { get; set; }
16 | public string Album { get; set; }
17 |
18 | public string CoverArtUrl { get; set; }
19 |
20 | public Song(string artist, string title, string album = null)
21 | {
22 | Artist = artist;
23 | Track = title;
24 | Album = album;
25 | }
26 |
27 | public override string ToString()
28 | {
29 | if (Artist == null)
30 | return Track;
31 |
32 | return string.Format("{0} - {1}", Artist, Track);
33 | }
34 |
35 | public override bool Equals(object obj)
36 | {
37 | if (!(obj is Song target))
38 | return false;
39 |
40 | return (target.Artist == this.Artist && target.Track == this.Track);
41 | }
42 |
43 | // overriding GetHashCode is "required" when overriding Equals
44 | public override int GetHashCode()
45 | {
46 | return base.GetHashCode();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("AutoHotkey.Interop")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("AutoHotkey.Interop")]
12 | [assembly: AssemblyCopyright("Copyright © 2014")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("8a7cbbfe-ccb2-42ef-b90f-d937ae6ed741")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/PluginBase/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("PluginBase")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Microsoft")]
11 | [assembly: AssemblyProduct("PluginBase")]
12 | [assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("d7229ff5-9d92-4bd3-876c-642ee9e4321e")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/ExamplePlugin/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("ExamplePlugin")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Microsoft")]
11 | [assembly: AssemblyProduct("ExamplePlugin")]
12 | [assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("1f81d14b-7a11-4a3f-92a9-d54530dabf00")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Toastify/LastInputDebug.cs:
--------------------------------------------------------------------------------
1 | #if DEBUG
2 |
3 | using System;
4 | using System.Runtime.InteropServices;
5 | using System.Threading;
6 |
7 | namespace Toastify
8 | {
9 | internal static class LastInputDebug
10 | {
11 |
12 | [StructLayout(LayoutKind.Sequential)]
13 | struct LASTINPUTINFO
14 | {
15 | public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
16 |
17 | [MarshalAs(UnmanagedType.U4)]
18 | public UInt32 cbSize;
19 | [MarshalAs(UnmanagedType.U4)]
20 | public UInt32 dwTime;
21 | }
22 |
23 | [DllImport("user32.dll")]
24 | static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
25 |
26 | public static void Start()
27 | {
28 |
29 | var t = new Thread(LastInputCheck);
30 | t.Start();
31 |
32 | }
33 |
34 | private static void LastInputCheck()
35 | {
36 | var lastInputInfo = new LASTINPUTINFO();
37 | lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
38 |
39 | while (true)
40 | {
41 | GetLastInputInfo(ref lastInputInfo);
42 |
43 | var idleTime = (uint)Environment.TickCount - lastInputInfo.dwTime;
44 |
45 | System.Diagnostics.Debug.WriteLine("Idle Time: " + ((idleTime > 0) ? (idleTime / 1000) : 0) + "secs (dwTime: " + lastInputInfo.dwTime + ")");
46 |
47 | Thread.Sleep(10000);
48 | }
49 | }
50 | }
51 | }
52 | #else
53 |
54 | namespace Toastify
55 | {
56 | internal static class LastInputDebug
57 | {
58 | public static void Start() { }
59 | }
60 | }
61 |
62 | #endif
--------------------------------------------------------------------------------
/Toastify/Spotify/SpotifyApiClient.cs:
--------------------------------------------------------------------------------
1 | using SpotifyAPI.Web;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace Toastify
6 | {
7 | internal static partial class SpotifyApiClient
8 | {
9 | // override these in SpotifyApiClient.private.cs.
10 | // If you don't have such a file, create one using the following template:
11 | // Note: missing " on purpose so that it doesn't build if you just dump it in :)
12 | //
13 | /*
14 | namespace Toastify
15 | {
16 | internal static partial class SpotifyApiClient
17 | {
18 | static SpotifyApiClient()
19 | {
20 | _CLIENT_ID = INSERT_REAL_VALUE;
21 | _CLIENT_SECRET = INSERT_REAL_VALUE;
22 | }
23 | }
24 | }
25 | */
26 | private static readonly string _CLIENT_ID = "CHANGEME";
27 | private static readonly string _CLIENT_SECRET = "CHANGEME";
28 |
29 | private static readonly SpotifyClientConfig _spotifyClientConfig = SpotifyClientConfig.CreateDefault();
30 | private static ClientCredentialsTokenResponse _tokenResponse;
31 |
32 | private static SpotifyClient _spotifyClient;
33 |
34 | public static async Task GetAsync()
35 | {
36 |
37 | if (_spotifyClient == null || _tokenResponse == null || _tokenResponse.IsExpired)
38 | {
39 | if (_CLIENT_ID == "CHANGEME" || _CLIENT_SECRET == "CHANGEME")
40 | {
41 | throw new Exception("You need to override _CLIENT_ID and _CLIENT_SECRET with the values from your Spotify dev account");
42 | }
43 |
44 | var request = new ClientCredentialsRequest(_CLIENT_ID, _CLIENT_SECRET);
45 | _tokenResponse = await new OAuthClient(_spotifyClientConfig).RequestToken(request);
46 |
47 | _spotifyClient = new SpotifyClient(_spotifyClientConfig.WithToken(_tokenResponse.AccessToken));
48 | }
49 |
50 |
51 | return _spotifyClient;
52 | }
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ExamplePlugin/ExamplePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ExamplePlugin
4 | {
5 | //Sample Plugin. Simply logs the songs being played to a text file.
6 | //A reference is added to the ToastifyApi.dll(PluginBase)
7 | //The plugin class below implements the PluginBase interface
8 |
9 |
10 | //Toastify.xml
11 | //
12 | //
13 | // ExamplePlugin.dll Plugin filename
14 | // ExamplePlugin.ExamplePlugin/TypeName> Plugin type name (Namespace + "." + ClassName)
15 | // Plugin settings data
16 | //
17 | //
18 |
19 | public class ExamplePlugin : Toastify.Plugin.IPluginBase
20 | {
21 | public ExamplePlugin()
22 | {
23 | }
24 |
25 | string logFilename;
26 | public void Init(string settings)
27 | {
28 | //Init is called direcly after the constructor.
29 | //Any data from the element in Toastify.xml is passed on.
30 | //For multiple values, use a seperated list of some sort. Eg. "value1|value2|value3"... settings.Split('|')...
31 |
32 | this.logFilename = settings;
33 | }
34 |
35 | public void Started()
36 | {
37 | System.IO.File.AppendAllText(logFilename, "ExamplePlugin Started - " + DateTime.Now.ToLongTimeString() + Environment.NewLine);
38 | }
39 |
40 | public void Closing()
41 | {
42 | System.IO.File.AppendAllText(logFilename, "ExamplePlugin Closing - " + DateTime.Now.ToLongTimeString() + Environment.NewLine);
43 | }
44 |
45 | public void TrackChanged(string artist, string title)
46 | {
47 | System.IO.File.AppendAllText(logFilename, string.Format("{0} - {1}{2}", artist, title, Environment.NewLine));
48 | }
49 |
50 | public void Dispose()
51 | {
52 | //Dispose of any resources here.
53 | //This is called direcly after Closing()
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Toastify/About.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Documents;
4 | using System.Windows.Input;
5 | using System.Diagnostics;
6 |
7 | namespace Toastify
8 | {
9 | ///
10 | /// Interaction logic for About.xaml
11 | ///
12 | public partial class About : Window
13 | {
14 | readonly VersionChecker versionChecker;
15 |
16 | public About()
17 | {
18 | InitializeComponent();
19 |
20 | versionChecker = new VersionChecker();
21 | versionChecker.CheckVersionComplete += new EventHandler(versionChecker_CheckVersionComplete);
22 |
23 | this.DataContext = versionChecker;
24 | }
25 |
26 | void versionChecker_CheckVersionComplete(object sender, CheckVersionCompleteEventArgs e)
27 | {
28 | string latestVersionText = "";
29 |
30 | if (string.IsNullOrEmpty(e.Version))
31 | latestVersionText = "Unable to check version.";
32 | else if (e.New)
33 | latestVersionText = "New version available!";
34 | else
35 | latestVersionText = "You have the latest version.";
36 |
37 | this.Dispatcher.Invoke(new Action(() =>
38 | {
39 | Run run = new Run(latestVersionText);
40 | LatestVersion.Inlines.Clear();
41 | LatestVersion.Inlines.Add(run);
42 | }), System.Windows.Threading.DispatcherPriority.Normal);
43 | }
44 |
45 | private void CodeplexLink_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
46 | {
47 | System.Diagnostics.Process.Start("http://toastify.codeplex.com");
48 | }
49 |
50 | private void Border_MouseUp(object sender, MouseButtonEventArgs e)
51 | {
52 | this.Close();
53 | }
54 |
55 | private void Window_Loaded(object sender, RoutedEventArgs e)
56 | {
57 | versionChecker.BeginCheckVersion();
58 | }
59 |
60 | private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
61 | {
62 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
63 | e.Handled = true;
64 |
65 | this.Close();
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Toastify/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using System.Windows;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Toastify")]
9 | [assembly: AssemblyDescription("Toast for Spotify")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Jesper Palm")]
12 | [assembly: AssemblyProduct("Toastify")]
13 | [assembly: AssemblyCopyright("Copyright © 2009")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | //In order to begin building localizable applications, set
23 | //CultureYouAreCodingWith in your .csproj file
24 | //inside a . For example, if you are using US english
25 | //in your source files, set the to en-US. Then uncomment
26 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
27 | //the line below to match the UICulture setting in the project file.
28 |
29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
30 |
31 |
32 | [assembly: ThemeInfo(
33 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
34 | //(used if a resource is not found in the page,
35 | // or application resource dictionaries)
36 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
37 | //(used if a resource is not found in the page,
38 | // app, or any theme specific resource dictionaries)
39 | )]
40 |
41 |
42 | // Version information for an assembly consists of the following four values:
43 | //
44 | // Major Version
45 | // Minor Version
46 | // Build Number
47 | // Revision
48 | //
49 | // You can specify all the values or you can default the Build and Revision Numbers
50 | // by using the '*' as shown below:
51 | // [assembly: AssemblyVersion("1.0.*")]
52 | [assembly: AssemblyVersion("1.8.3.*")]
53 |
--------------------------------------------------------------------------------
/Toastify/VersionChecker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Text.RegularExpressions;
4 | using System.Threading;
5 | using System.Reflection;
6 | using System.Diagnostics;
7 |
8 | namespace Toastify
9 | {
10 | class VersionChecker
11 | {
12 | private static string _version;
13 |
14 | public static string Version
15 | {
16 | get
17 | {
18 | if (_version == null)
19 | {
20 | var assembly = Assembly.GetExecutingAssembly();
21 | var fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
22 |
23 | _version = fileVersionInfo.FileVersion;
24 |
25 | var thirdDot = _version.LastIndexOf('.');
26 |
27 | _version = _version.Substring(0, thirdDot);
28 | }
29 |
30 | return _version;
31 | }
32 | }
33 |
34 | public string UpdateUrl { get { return "https://toastify.codeplex.com/releases/view/24273"; } }
35 |
36 | readonly WebClient wc;
37 |
38 | public event EventHandler CheckVersionComplete;
39 |
40 | public VersionChecker()
41 | {
42 | wc = new WebClient();
43 | wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
44 | }
45 |
46 | void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
47 | {
48 | string version = string.Empty;
49 | bool newVersion = false;
50 |
51 | if (e.Cancelled == false && e.Error == null)
52 | {
53 | var match = Regex.Match(e.Result, "Version: (?[\\d+\\.]+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
54 |
55 | if (match.Success)
56 | {
57 | version = match.Groups["ver"].Value.Trim();
58 | newVersion = Version != version;
59 | }
60 | }
61 |
62 | this.CheckVersionComplete?.Invoke(this, new CheckVersionCompleteEventArgs { Version = version, New = newVersion });
63 | }
64 |
65 | public void BeginCheckVersion()
66 | {
67 | Thread t = new Thread(ThreadedBeginCheckVersion)
68 | {
69 | IsBackground = true
70 | };
71 | t.Start();
72 | }
73 |
74 | private void ThreadedBeginCheckVersion()
75 | {
76 | //WebClients XXXAsync isn't as async as I wanted...
77 | wc.DownloadStringAsync(new Uri("http://toastify.codeplex.com/wikipage?title=Version"));
78 | }
79 | }
80 |
81 | class CheckVersionCompleteEventArgs : EventArgs
82 | {
83 | public string Version { get; set; }
84 | public bool New { get; set; }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Toastify/WinHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Windows.Interop;
4 |
5 | namespace Toastify
6 | {
7 | class WinHelper
8 | {
9 | [Flags]
10 | public enum ExtendedWindowStyles
11 | {
12 | // ...
13 | WS_EX_TOOLWINDOW = 0x00000080,
14 | // ...
15 | }
16 |
17 | public enum GetWindowLongFields
18 | {
19 | // ...
20 | GWL_EXSTYLE = (-20),
21 | // ...
22 | }
23 |
24 | [DllImport("user32.dll")]
25 | private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
26 |
27 | private static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
28 | {
29 | int error = 0;
30 | IntPtr result = IntPtr.Zero;
31 | // Win32 SetWindowLong doesn't clear error on success
32 | SetLastError(0);
33 |
34 | if (IntPtr.Size == 4)
35 | {
36 | // use SetWindowLong
37 | Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
38 | error = Marshal.GetLastWin32Error();
39 | result = new IntPtr(tempResult);
40 | }
41 | else
42 | {
43 | // use SetWindowLongPtr
44 | result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
45 | error = Marshal.GetLastWin32Error();
46 | }
47 |
48 | if ((result == IntPtr.Zero) && (error != 0))
49 | {
50 | throw new System.ComponentModel.Win32Exception(error);
51 | }
52 |
53 | return result;
54 | }
55 |
56 | [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
57 | private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
58 |
59 | [DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
60 | private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);
61 |
62 | private static int IntPtrToInt32(IntPtr intPtr)
63 | {
64 | return unchecked((int)intPtr.ToInt64());
65 | }
66 |
67 | [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
68 | public static extern void SetLastError(int dwErrorCode);
69 |
70 | public static void AddToolWindowStyle(System.Windows.Window window)
71 | {
72 | WindowInteropHelper wndHelper = new WindowInteropHelper(window);
73 | int exStyle = (int)GetWindowLong(wndHelper.Handle, (int)WinHelper.GetWindowLongFields.GWL_EXSTYLE);
74 | exStyle |= (int)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
75 | SetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
76 | }
77 |
78 | [FlagsAttribute]
79 | public enum EXECUTION_STATE : uint
80 | {
81 | ES_AWAYMODE_REQUIRED = 0x00000040,
82 | ES_CONTINUOUS = 0x80000000,
83 | ES_DISPLAY_REQUIRED = 0x00000002,
84 | ES_SYSTEM_REQUIRED = 0x00000001
85 | // Legacy flag, should not be used.
86 | // ES_USER_PRESENT = 0x00000004
87 | }
88 |
89 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
90 | public static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Toastify/Toast.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Body (feat. DJ Rush)
47 | Si Begg
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Toastify/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Toastify.Properties
12 | {
13 | using System;
14 |
15 |
16 | ///
17 | /// A strongly-typed resource class, for looking up localized strings, etc.
18 | ///
19 | // This class was auto-generated by the StronglyTypedResourceBuilder
20 | // class via a tool like ResGen or Visual Studio.
21 | // To add or remove a member, edit your .ResX file then rerun ResGen
22 | // with the /str option, or rebuild your VS project.
23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
26 | internal class Resources
27 | {
28 |
29 | private static global::System.Resources.ResourceManager resourceMan;
30 |
31 | private static global::System.Globalization.CultureInfo resourceCulture;
32 |
33 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
34 | internal Resources()
35 | {
36 | }
37 |
38 | ///
39 | /// Returns the cached ResourceManager instance used by this class.
40 | ///
41 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
42 | internal static global::System.Resources.ResourceManager ResourceManager
43 | {
44 | get
45 | {
46 | if (object.ReferenceEquals(resourceMan, null))
47 | {
48 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Toastify.Properties.Resources", typeof(Resources).Assembly);
49 | resourceMan = temp;
50 | }
51 | return resourceMan;
52 | }
53 | }
54 |
55 | ///
56 | /// Overrides the current thread's CurrentUICulture property for all
57 | /// resource lookups using this strongly typed resource class.
58 | ///
59 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
60 | internal static global::System.Globalization.CultureInfo Culture
61 | {
62 | get
63 | {
64 | return resourceCulture;
65 | }
66 | set
67 | {
68 | resourceCulture = value;
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
74 | ///
75 | internal static System.Drawing.Icon spotifyicon
76 | {
77 | get
78 | {
79 | object obj = ResourceManager.GetObject("spotifyicon", resourceCulture);
80 | return ((System.Drawing.Icon)(obj));
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/Util.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace AutoHotkey.Interop
7 | {
8 | internal static class Util
9 | {
10 | public static string FindEmbededResourceName(Assembly assembly, string path)
11 | {
12 | path = Regex.Replace(path, @"[/\\]", ".");
13 |
14 | if (!path.StartsWith("."))
15 | path = "." + path;
16 |
17 | var names = assembly.GetManifestResourceNames();
18 |
19 | foreach (var name in names)
20 | {
21 | if (name.EndsWith(path))
22 | {
23 | return name;
24 | }
25 | }
26 |
27 | return null;
28 | }
29 |
30 | public static void ExtractEmbededResourceToFile(Assembly assembly, string embededResourcePath, string targetFileName)
31 | {
32 | //ensure directory exists
33 | var dir = Path.GetDirectoryName(targetFileName);
34 |
35 | if (!Directory.Exists(dir))
36 | Directory.CreateDirectory(dir);
37 |
38 | using (var readStream = assembly.GetManifestResourceStream(embededResourcePath))
39 | using (var writeStream = File.Open(targetFileName, FileMode.Create))
40 | {
41 | readStream.CopyTo(writeStream);
42 | readStream.Flush();
43 | }
44 | }
45 |
46 | public static bool Is64Bit()
47 | {
48 | return IntPtr.Size == 8;
49 | }
50 | public static bool Is32Bit()
51 | {
52 | return IntPtr.Size == 4;
53 | }
54 |
55 |
56 |
57 | public static void EnsureAutoHotkeyLoaded()
58 | {
59 | if (dllHandle.IsValueCreated)
60 | return;
61 |
62 | var handle = dllHandle.Value;
63 | }
64 |
65 | private static readonly Lazy dllHandle = new Lazy(
66 | () => Util.LoadAutoHotKeyDll());
67 | private static SafeLibraryHandle LoadAutoHotKeyDll()
68 | {
69 | //Locate and Load 32bit or 64bit version of AutoHotkey.dll
70 | string tempFolderPath = Path.Combine(Path.GetTempPath(), "AutoHotkey.Interop");
71 | string path32 = @"x86\AutoHotkey.dll";
72 | string path64 = @"x64\AutoHotkey.dll";
73 |
74 | var loadDllFromFileOrResource = new Func(relativePath =>
75 | {
76 | if (File.Exists(relativePath))
77 | {
78 | return SafeLibraryHandle.LoadLibrary(relativePath);
79 | }
80 | else
81 | {
82 | var assembly = typeof(AutoHotkeyEngine).Assembly;
83 | var resource = Util.FindEmbededResourceName(assembly, relativePath);
84 |
85 | if (resource != null)
86 | {
87 | var target = Path.Combine(tempFolderPath, relativePath);
88 | Util.ExtractEmbededResourceToFile(assembly, resource, target);
89 | return SafeLibraryHandle.LoadLibrary(target);
90 | }
91 |
92 | return null;
93 | }
94 | });
95 |
96 |
97 | if (Util.Is32Bit())
98 | {
99 | return loadDllFromFileOrResource(path32);
100 | }
101 | else if (Util.Is64Bit())
102 | {
103 | return loadDllFromFileOrResource(path64);
104 | }
105 | else
106 | {
107 | return null;
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Toastify/About.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Toastify
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | toastify.codeplex.com
51 |
52 | Checking for updates...
53 | Credits to: Linus, Maaaackan, Aqualize, ncahmore...
54 |
55 | Created by Jesper Palm 2009, licensed under GPLv2.
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Toastify.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29709.97
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugin", "Plugin", "{64E51A59-02A2-4423-8DE1-F337D479E3B7}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Installation", "Installation", "{FCD4BE87-8354-47A1-85DE-709E35559000}"
9 | ProjectSection(SolutionItems) = preProject
10 | InstallationScript\DotNET.nsh = InstallationScript\DotNET.nsh
11 | InstallationScript\Install.nsi = InstallationScript\Install.nsi
12 | EndProjectSection
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toastify", "Toastify\Toastify.csproj", "{CCC4A761-56D2-4188-807F-F7A547DFB031}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toastify.Plugin", "PluginBase\Toastify.Plugin.csproj", "{4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExamplePlugin", "ExamplePlugin\ExamplePlugin.csproj", "{68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoHotkey.Interop", "AutoHotkey.Interop\AutoHotkey.Interop.csproj", "{2869DBFE-7762-4930-95EA-2B0C111CF353}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Debug|x64 = Debug|x64
26 | Release|Any CPU = Release|Any CPU
27 | Release|x64 = Release|x64
28 | EndGlobalSection
29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
30 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Debug|x64.ActiveCfg = Debug|Any CPU
33 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {CCC4A761-56D2-4188-807F-F7A547DFB031}.Release|x64.ActiveCfg = Release|Any CPU
36 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Debug|x64.ActiveCfg = Debug|Any CPU
39 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}.Release|x64.ActiveCfg = Release|Any CPU
42 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Debug|x64.ActiveCfg = Debug|Any CPU
45 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}.Release|x64.ActiveCfg = Release|Any CPU
48 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Debug|x64.ActiveCfg = Debug|x64
51 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Debug|x64.Build.0 = Debug|x64
52 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Release|x64.ActiveCfg = Release|x64
55 | {2869DBFE-7762-4930-95EA-2B0C111CF353}.Release|x64.Build.0 = Release|x64
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(NestedProjects) = preSolution
61 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8} = {64E51A59-02A2-4423-8DE1-F337D479E3B7}
62 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40} = {64E51A59-02A2-4423-8DE1-F337D479E3B7}
63 | EndGlobalSection
64 | GlobalSection(ExtensibilityGlobals) = postSolution
65 | SolutionGuid = {0602AE4C-26D4-4C96-9DA1-BF2F49D20381}
66 | EndGlobalSection
67 | EndGlobal
68 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/AutoHotkey.Interop.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2869DBFE-7762-4930-95EA-2B0C111CF353}
8 | Library
9 | Properties
10 | AutoHotkey.Interop
11 | AutoHotkey.Interop
12 | v4.0
13 | 512
14 | SAK
15 | SAK
16 | SAK
17 | SAK
18 |
19 |
20 |
21 | AnyCPU
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE
36 | prompt
37 | 4
38 |
39 |
40 |
41 |
42 |
43 | true
44 | bin\x64\Debug\
45 | DEBUG;TRACE
46 | full
47 | x64
48 | prompt
49 | MinimumRecommendedRules.ruleset
50 |
51 |
52 | bin\x64\Release\
53 | TRACE
54 | true
55 | pdbonly
56 | x64
57 | prompt
58 | MinimumRecommendedRules.ruleset
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Always
79 |
80 |
81 | Always
82 |
83 |
84 |
85 |
92 |
--------------------------------------------------------------------------------
/Toastify/Telemetry.cs:
--------------------------------------------------------------------------------
1 | using Garlic;
2 | using System;
3 | using System.Linq;
4 | using System.Management;
5 |
6 | namespace Toastify
7 | {
8 | public enum TelemetryCategory
9 | {
10 | General,
11 | Action,
12 | SpotifyWebService,
13 | }
14 |
15 | public static class Telemetry
16 | {
17 | private static AnalyticsSession _session;
18 | private static IAnalyticsPageViewRequest _client;
19 |
20 | static Telemetry()
21 | {
22 | Init();
23 | }
24 |
25 | private static void Init()
26 | {
27 | _session = new AnalyticsSession("http://toastify.nachmore.com/app", "UA-61123985-2");
28 |
29 | var settings = SettingsXml.Current;
30 |
31 | // abort asap if we are surpressing analytics
32 | if (settings.PreventAnalytics)
33 | return;
34 |
35 | _session.SetCustomVariable(1, "OS Version", GetOS());
36 |
37 | _client = _session.CreatePageViewRequest("/", "Global");
38 |
39 | if (SettingsXml.Current.FirstRun)
40 | {
41 | TrackEvent(TelemetryCategory.General, "Install", GetOS());
42 |
43 | SettingsXml.Current.FirstRun = false;
44 | }
45 | }
46 |
47 | public static void TrackEvent(TelemetryCategory category, string action, object label = null, int value = 0)
48 | {
49 | if (_client != null)
50 | _client.SendEvent(category.ToString(), action, (label != null ? label.ToString() : null), value.ToString());
51 | }
52 |
53 | internal static void TrackException(Exception exception)
54 | {
55 | // The exception will be truncated to 500bytes (GA limit for Labels), at some point it may be better to extract more pertinant information
56 | TrackEvent(TelemetryCategory.General, TelemetryEvent.Exception, exception.ToString());
57 | }
58 |
59 | internal static void TrackEvent(TelemetryCategory general, object settingsLaunched)
60 | {
61 | throw new NotImplementedException();
62 | }
63 |
64 | public static string GetOS()
65 | {
66 | return Environment.OSVersion.VersionString +
67 | " (" + GetFriendlyOS() + ")" +
68 | " (" + (Environment.Is64BitOperatingSystem ? "x64" : "x86") + ")";
69 | }
70 |
71 | private static string GetFriendlyOS()
72 | {
73 | var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType()
74 | select x.GetPropertyValue("Caption")).FirstOrDefault();
75 | return name != null ? name.ToString() : "Unknown";
76 | }
77 |
78 | ///
79 | /// Poor mans enum -> expanded string.
80 | ///
81 | /// Once I've been using this for a while I may change this to a pure enum if
82 | /// spaces in names prove to be annoying for querying / sorting the data
83 | ///
84 | public static class TelemetryEvent
85 | {
86 | public const string Exception = "Toastify.General.Exception";
87 |
88 | public const string AppLaunch = "Toastify.General.AppLaunched";
89 | public const string AppUpgraded = "Toastify.General.AppUpgraded";
90 |
91 | public const string SettingsLaunched = "Toastify.General.SettingsLaunched";
92 |
93 | public static class SpotifyWebService
94 | {
95 | public const string NetworkError = "Toastify.SpotifyWebService.NetworkError";
96 | public const string ResponseError = "Toastify.SpotifyWebService.ResponseError";
97 | }
98 |
99 | public static class Action
100 | {
101 | public const string Mute = "Toastify.Action.Mute";
102 | public const string VolumeDown = "Toastify.Action.VolumeDown";
103 | public const string VolumeUp = "Toasitfy.Action.VolumeUp";
104 | public const string ShowToast = "Toastify.Action.ShowToast";
105 | public const string ShowSpotify = "Toastify.Action.ShowSpotify";
106 | public const string CopyTrackInfo = "Toastify.Action.CopyTrackInfo";
107 | public const string PasteTrackInfo = "Toastify.Action.PasteTrackInfo";
108 | public const string FastForward = "Toastify.Action.FastForward";
109 | public const string Rewind = "Toastify.Action.Rewind";
110 | public const string Default = "Toastify.Action.";
111 | }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/InstallationScript/Install.nsi:
--------------------------------------------------------------------------------
1 | !include "DotNET.nsh"
2 | !include LogicLib.nsh
3 | !define DOTNET_VERSION "3.5"
4 | !include "MUI.nsh"
5 |
6 | ; The name of the installer
7 | Name "Toastify Installer"
8 |
9 | ; The file to write
10 | OutFile "ToastifyInstaller.exe"
11 |
12 | ; The default installation directory
13 | InstallDir $PROGRAMFILES\Toastify
14 |
15 | ; Request application privileges for Windows Vista
16 | RequestExecutionLevel admin
17 |
18 | ;--------------------------------
19 |
20 | ; Pages
21 |
22 | Page components
23 | Page directory
24 | Page instfiles
25 |
26 | # These indented statements modify settings for MUI_PAGE_FINISH
27 | !define MUI_FINISHPAGE_AUTOCLOSE
28 | !define MUI_FINISHPAGE_RUN
29 | !define MUI_FINISHPAGE_RUN_CHECKED
30 | !define MUI_FINISHPAGE_RUN_TEXT "Launch Toastify Now"
31 | !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
32 | !insertmacro MUI_PAGE_FINISH
33 |
34 | ;Languages
35 | !insertmacro MUI_LANGUAGE "English"
36 |
37 | ;--------------------------------
38 | Function LaunchLink
39 | ExecShell "" "$INSTDIR\Toastify.exe"
40 | FunctionEnd
41 |
42 | UninstPage uninstConfirm
43 | UninstPage instfiles
44 |
45 | ;--------------------------------
46 |
47 | Section "Toastify (required)"
48 | SectionIn RO
49 |
50 | ; Since process termination is non-destructive for Toastify, just kill it
51 | DetailPrint "Shutting down Toastify..."
52 | KillProcWMI::KillProc "Toastify.exe"
53 |
54 | ; Let the process shutdown
55 | Sleep 1000
56 |
57 | ; Set output path to the installation directory.
58 | SetOutPath $INSTDIR
59 |
60 | ; Put file there
61 | File "Toastify.exe"
62 | File "ToastifyApi.dll"
63 | File "ManagedWinapi.dll"
64 | File "Resources\ManagedWinapiNativeHelper.dll"
65 | File "AutoHotkey.Interop.dll"
66 | File "Garlic.dll"
67 | File "Newtonsoft.Json.dll"
68 | File "LICENSE"
69 |
70 | ; Write the uninstall keys for Windows
71 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "DisplayName" "Toastify"
72 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "UninstallString" '"$INSTDIR\uninstall.exe"'
73 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "DisplayIcon" "$INSTDIR\Toastify.exe,0"
74 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "Publisher" "Jesper Palm"
75 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "Version" "1.6"
76 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "DisplayVersion" "1.6"
77 | WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "NoModify" 1
78 | WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify" "NoRepair" 1
79 | WriteUninstaller "uninstall.exe"
80 | SectionEnd
81 |
82 | Section "Desktop icon"
83 | CreateShortCut "$DESKTOP\Toastify.lnk" "$INSTDIR\Toastify.exe" "" "$INSTDIR\Toastify.exe" 0
84 | SectionEnd
85 |
86 | Section "Start Menu icon"
87 | # Start Menu
88 | CreateShortCut "$SMPROGRAMS\Toastify.lnk" "$INSTDIR\Toastify.exe" "" "$INSTDIR\Toastify.exe" 0
89 |
90 | SectionEnd
91 |
92 | Section "Autostart"
93 | WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Toastify" '"$INSTDIR\Toastify.exe"'
94 | SectionEnd
95 |
96 | ;--------------------------------
97 |
98 | ; Uninstaller
99 |
100 | Section "Uninstall"
101 |
102 | # Remove Start Menu launcher
103 | Delete "$SMPROGRAMS\Toastify.lnk"
104 |
105 | ; Remove registry keys
106 | DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Toastify"
107 | DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Toastify"
108 |
109 | ; Remove files and uninstaller
110 | Delete "$INSTDIR\Toastify.exe"
111 | Delete "$INSTDIR\ToastifyApi.dll"
112 | Delete "$INSTDIR\ManagedWinapi.dll"
113 | Delete "$INSTDIR\ManagedWinapiNativeHelper.dll"
114 | Delete "$INSTDIR\AutoHotkey.Interop.dll"
115 | Delete "$INSTDIR\Garlic.dll"
116 | Delete "$INSTDIR\Newtonsoft.Json.dll"
117 | Delete "$INSTDIR\LICENSE"
118 |
119 | ; remove the settings directory
120 | Delete "$APPDATA\Toastify.xml"
121 | RMDir "$APPDATA\Toastify"
122 |
123 | ; Remove shortcuts, if any
124 | Delete "$DESKTOP\Toastify.lnk"
125 |
126 | ; Remove directories used
127 | RMDir "$INSTDIR"
128 | SectionEnd
129 |
130 | Function .onInit
131 | FunctionEnd
132 |
--------------------------------------------------------------------------------
/PluginBase/Toastify.Plugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}
9 | Library
10 | Properties
11 | Toastify.Plugin
12 | ToastifyApi
13 | v4.6.1
14 | 512
15 |
16 |
17 | 3.5
18 |
19 | publish\
20 | true
21 | Disk
22 | false
23 | Foreground
24 | 7
25 | Days
26 | false
27 | false
28 | true
29 | 0
30 | 1.0.0.%2a
31 | false
32 | false
33 | true
34 | SAK
35 | SAK
36 | SAK
37 | SAK
38 |
39 |
40 |
41 | true
42 | full
43 | false
44 | bin\Debug\
45 | DEBUG;TRACE
46 | prompt
47 | 4
48 | AllRules.ruleset
49 | false
50 |
51 |
52 | pdbonly
53 | true
54 | bin\Release\
55 | TRACE
56 | prompt
57 | 4
58 | AllRules.ruleset
59 | false
60 |
61 |
62 |
63 |
64 | 3.5
65 |
66 |
67 | 3.5
68 |
69 |
70 | 3.5
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | False
82 | .NET Framework 3.5 SP1 Client Profile
83 | false
84 |
85 |
86 | False
87 | .NET Framework 3.5 SP1
88 | true
89 |
90 |
91 | False
92 | Windows Installer 3.1
93 | true
94 |
95 |
96 |
97 |
104 |
--------------------------------------------------------------------------------
/ExamplePlugin/ExamplePlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {68F1A17D-4930-4EEC-AEEF-5DD9C9033B40}
9 | Library
10 | Properties
11 | ExamplePlugin
12 | ExamplePlugin
13 | v4.6.1
14 | 512
15 |
16 |
17 | 3.5
18 |
19 | publish\
20 | true
21 | Disk
22 | false
23 | Foreground
24 | 7
25 | Days
26 | false
27 | false
28 | true
29 | 0
30 | 1.0.0.%2a
31 | false
32 | false
33 | true
34 | SAK
35 | SAK
36 | SAK
37 | SAK
38 |
39 |
40 |
41 | true
42 | full
43 | false
44 | bin\Debug\
45 | DEBUG;TRACE
46 | prompt
47 | 4
48 | AllRules.ruleset
49 | false
50 |
51 |
52 | pdbonly
53 | true
54 | bin\Release\
55 | TRACE
56 | prompt
57 | 4
58 | AllRules.ruleset
59 | false
60 |
61 |
62 |
63 |
64 | 3.5
65 |
66 |
67 | 3.5
68 |
69 |
70 | 3.5
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | {4F92BE1F-A5CC-4604-9185-1F09DDAFC7B8}
82 | Toastify.Plugin
83 |
84 |
85 |
86 |
87 | False
88 | .NET Framework 3.5 SP1 Client Profile
89 | false
90 |
91 |
92 | False
93 | .NET Framework 3.5 SP1
94 | true
95 |
96 |
97 | False
98 | Windows Installer 3.1
99 | true
100 |
101 |
102 |
103 |
110 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/AutoHotkeyEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace AutoHotkey.Interop
5 | {
6 | ///
7 | /// This class expects an AutoHotkey.dll to be available on the machine. (UNICODE) version.
8 | ///
9 | public class AutoHotkeyEngine
10 | {
11 | public AutoHotkeyEngine()
12 | {
13 | Util.EnsureAutoHotkeyLoaded();
14 |
15 | //ensure that a thread is started
16 | AutoHotkeyDll.ahktextdll("", "", "");
17 | }
18 |
19 | ///
20 | /// Gets the value for a varible or an empty string if the variable does not exist.
21 | ///
22 | /// Name of the variable.
23 | /// Returns the value of the variable, or an empty string if the variable does not exist.
24 | public string GetVar(string variableName)
25 | {
26 | var p = AutoHotkeyDll.ahkgetvar(variableName, 0);
27 | return Marshal.PtrToStringUni(p);
28 | }
29 |
30 | ///
31 | /// Sets the value of a variable.
32 | ///
33 | /// Name of the variable.
34 | /// The value to set.
35 | public void SetVar(string variableName, string value)
36 | {
37 | if (value == null)
38 | value = "";
39 |
40 | AutoHotkeyDll.ahkassign(variableName, value);
41 | }
42 |
43 | ///
44 | /// Evaulates an expression or function and returns the results
45 | ///
46 | /// The code to execute
47 | /// Returns the result of an expression
48 | public string Eval(string code)
49 | {
50 | var codeToRun = "A__EVAL:=" + code;
51 | AutoHotkeyDll.ahkExec(codeToRun);
52 | return GetVar("A__EVAL");
53 | }
54 |
55 | ///
56 | /// Loads a file into the running script
57 | ///
58 | /// The filepath of the script
59 | public void Load(string filePath)
60 | {
61 | AutoHotkeyDll.addFile(filePath, 1, 1);
62 | }
63 |
64 | ///
65 | /// Executes raw ahk code.
66 | ///
67 | /// The code to execute
68 | public void ExecRaw(string code)
69 | {
70 | AutoHotkeyDll.ahkExec(code);
71 | }
72 |
73 | ///
74 | /// Terminates the running scripts
75 | ///
76 | public void Terminate()
77 | {
78 | AutoHotkeyDll.ahkTerminate(1000);
79 | }
80 |
81 | ///
82 | /// Suspends the scripts
83 | ///
84 | public void Suspend()
85 | {
86 | ExecRaw("Suspend, On");
87 | }
88 |
89 | ///
90 | /// Unsuspends the scripts
91 | ///
92 | public void UnSuspend()
93 | {
94 | ExecRaw("Suspend, Off");
95 | }
96 |
97 | ///
98 | /// Executes an already defined function.
99 | ///
100 | /// The name of the function to execute.
101 | /// The 1st parameter
102 | /// The 2nd parameter
103 | /// The 3rd parameter
104 | /// The 4th parameter
105 | /// The 5th parameter
106 | /// The 6th parameter
107 | /// The 7th parameter
108 | /// The 8th parameter
109 | /// The 9th parameter
110 | /// The 10 parameter
111 | public string ExecFunction(string functionName,
112 | string param1 = null,
113 | string param2 = null,
114 | string param3 = null,
115 | string param4 = null,
116 | string param5 = null,
117 | string param6 = null,
118 | string param7 = null,
119 | string param8 = null,
120 | string param9 = null,
121 | string param10 = null)
122 | {
123 | IntPtr ret = AutoHotkeyDll.ahkFunction(functionName, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
124 |
125 | if (ret == IntPtr.Zero)
126 | return null;
127 | else
128 | return Marshal.PtrToStringUni(ret);
129 | }
130 |
131 |
132 | ///
133 | /// Determines if the function exists.
134 | ///
135 | /// Name of the function.
136 | /// Returns true if the function exists, otherwise false.
137 | public bool FunctionExists(string functionName)
138 | {
139 | IntPtr funcptr = AutoHotkeyDll.ahkFindFunc(functionName);
140 | return funcptr != IntPtr.Zero;
141 | }
142 |
143 | ///
144 | /// Executes a label
145 | ///
146 | /// Name of the label.
147 | public void ExecLabel(string labelName)
148 | {
149 | AutoHotkeyDll.ahkLabel(labelName, false);
150 | }
151 |
152 | ///
153 | /// Determines if the label exists.
154 | ///
155 | /// Name of the label.
156 | /// Returns true if the label exists, otherwise false
157 | public bool LabelExists(string labelName)
158 | {
159 | IntPtr labelptr = AutoHotkeyDll.ahkFindLabel(labelName);
160 | return labelptr != IntPtr.Zero;
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/Toastify/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\spotify.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
--------------------------------------------------------------------------------
/InstallationScript/DotNET.nsh:
--------------------------------------------------------------------------------
1 | # DotNET version checking macro.
2 | # Written by AnarkiNet(AnarkiNet@gmail.com) originally, modified by eyal0 (for use in http://www.sourceforge.net/projects/itwister)
3 | # Downloads and runs the Microsoft .NET Framework version 2.0 Redistributable and runs it if the user does not have the correct version.
4 | # To use, call the macro with a string:
5 | # !insertmacro CheckDotNET "2"
6 | # !insertmacro CheckDotNET "2.0.9"
7 | # (Version 2.0.9 is less than version 2.0.10.)
8 | # All register variables are saved and restored by CheckDotNet
9 | # No output
10 |
11 | !macro CheckDotNET DotNetReqVer
12 | !define DOTNET_URL "http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=en&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe"
13 | DetailPrint "Checking your .NET Framework version..."
14 | ;callee register save
15 | Push $0
16 | Push $1
17 | Push $2
18 | Push $3
19 | Push $4
20 | Push $5
21 | Push $6 ;backup of intsalled ver
22 | Push $7 ;backup of DoNetReqVer
23 |
24 | StrCpy $7 ${DotNetReqVer}
25 |
26 | System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i r2r2) i .r1 ?u"
27 |
28 | ${If} $0 == 0
29 | DetailPrint ".NET Framework not found, download is required for program to run."
30 | Goto NoDotNET
31 | ${EndIf}
32 |
33 | DetailPrint $0
34 |
35 | ;at this point, $0 has maybe v2.345.678.
36 | StrCpy $0 $0 $2 1 ;remove the starting "v", $0 has the installed version num as a string
37 | StrCpy $6 $0
38 | StrCpy $1 $7 ;$1 has the requested verison num as a string
39 |
40 | ;MessageBox MB_OKCANCEL "found $0" IDCANCEL GiveUpDotNET
41 |
42 | ;MessageBox MB_OKCANCEL "looking for $1" IDCANCEL GiveUpDotNET
43 |
44 | ;now let's compare the versions, installed against required ...
45 | ${Do}
46 | StrCpy $2 "" ;clear out the installed part
47 | StrCpy $3 "" ;clear out the required part
48 |
49 | ${Do}
50 | ${If} $0 == "" ;if there are no more characters in the version
51 | StrCpy $4 "." ;fake the end of the version string
52 | ${Else}
53 | StrCpy $4 $0 1 0 ;$4 = character from the installed ver
54 | ${If} $4 != "."
55 | StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
56 | ${EndIf}
57 | ${EndIf}
58 |
59 | ${If} $1 == "" ;if there are no more characters in the version
60 | StrCpy $5 "." ;fake the end of the version string
61 | ${Else}
62 | StrCpy $5 $1 1 0 ;$5 = character from the required ver
63 | ${If} $5 != "."
64 | StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
65 | ${EndIf}
66 | ${EndIf}
67 | ;MessageBox MB_OKCANCEL "installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
68 | ${If} $4 == "."
69 | ${AndIf} $5 == "."
70 | ${ExitDo} ;we're at the end of the part
71 | ${EndIf}
72 |
73 | ${If} $4 == "." ;if we're at the end of the current installed part
74 | StrCpy $2 "0$2" ;put a zero on the front
75 | ${Else} ;we have another character
76 | StrCpy $2 "$2$4" ;put the next character on the back
77 | ${EndIf}
78 | ${If} $5 == "." ;if we're at the end of the current required part
79 | StrCpy $3 "0$3" ;put a zero on the front
80 | ${Else} ;we have another character
81 | StrCpy $3 "$3$5" ;put the next character on the back
82 | ${EndIf}
83 | ${Loop}
84 | ;MessageBox MB_OKCANCEL "finished parts: installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
85 |
86 | ${If} $0 != "" ;let's remove the leading period on installed part if it exists
87 | StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1
88 | ${EndIf}
89 | ${If} $1 != "" ;let's remove the leading period on required part if it exists
90 | StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1
91 | ${EndIf}
92 |
93 | ;$2 has the installed part, $3 has the required part
94 | ${If} $2 S< $3
95 | IntOp $0 0 - 1 ;$0 = -1, installed less than required
96 | ${ExitDo}
97 | ${ElseIf} $2 S> $3
98 | IntOp $0 0 + 1 ;$0 = 1, installed greater than required
99 | ${ExitDo}
100 | ${ElseIf} $2 == ""
101 | ${AndIf} $3 == ""
102 | IntOp $0 0 + 0 ;$0 = 0, the versions are identical
103 | ${ExitDo}
104 | ${EndIf} ;otherwise we just keep looping through the parts
105 | ${Loop}
106 |
107 | ${If} $0 < 0
108 | DetailPrint ".NET Framework Version found: $6, but is older than the required version: $7"
109 | Goto OldDotNET
110 | ${Else}
111 | DetailPrint ".NET Framework Version found: $6, equal or newer to required version: $7."
112 | Goto NewDotNET
113 | ${EndIf}
114 |
115 | NoDotNET:
116 | MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
117 | ".NET Framework not installed.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
118 | /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
119 | goto GiveUpDotNET ;IDCANCEL
120 | OldDotNET:
121 | MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
122 | "Your .NET Framework version: $6.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
123 | /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
124 | goto GiveUpDotNET ;IDCANCEL
125 |
126 | DownloadDotNET:
127 | DetailPrint "Beginning download of latest .NET Framework version."
128 | NSISDL::download ${DOTNET_URL} "$TEMP\dotnetfx.exe"
129 | DetailPrint "Completed download."
130 | Pop $0
131 | ${If} $0 == "cancel"
132 | MessageBox MB_YESNO|MB_ICONEXCLAMATION \
133 | "Download cancelled. Continue Installation?" \
134 | IDYES NewDotNET IDNO GiveUpDotNET
135 | ${ElseIf} $0 != "success"
136 | MessageBox MB_YESNO|MB_ICONEXCLAMATION \
137 | "Download failed:$\n$0$\n$\nContinue Installation?" \
138 | IDYES NewDotNET IDNO GiveUpDotNET
139 | ${EndIf}
140 | DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
141 | ExecWait '$TEMP\dotnetfx.exe /q /c:"install /q"'
142 | DetailPrint "Completed .NET Framework install/update. Removing .NET Framework installer."
143 | Delete "$TEMP\dotnetfx.exe"
144 | DetailPrint ".NET Framework installer removed."
145 | goto NewDotNet
146 |
147 | GiveUpDotNET:
148 | Abort "Installation cancelled by user."
149 |
150 | NewDotNET:
151 | DetailPrint "Proceeding with remainder of installation."
152 | Pop $0
153 | Pop $1
154 | Pop $2
155 | Pop $3
156 | Pop $4
157 | Pop $5
158 | Pop $6 ;backup of intsalled ver
159 | Pop $7 ;backup of DoNetReqVer
160 | !macroend
--------------------------------------------------------------------------------
/BuildProcessTemplates/UpgradeTemplate.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | [New Microsoft.TeamFoundation.Build.Workflow.Activities.AgentSettings() With {.MaxWaitTime = New System.TimeSpan(4, 0, 0), .MaxExecutionTime = New System.TimeSpan(0, 0, 0), .TagComparison = Microsoft.TeamFoundation.Build.Workflow.Activities.TagComparison.MatchExactly }]
21 |
22 |
23 |
24 | [Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.Auto]
25 | [False]
26 | [False]
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | [Microsoft.TeamFoundation.VersionControl.Client.RecursionType.OneLevel]
37 | [Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]
38 |
39 |
40 |
41 | All
42 | Assembly references and imported namespaces serialized as XML namespaces
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Toastify/Win32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Text;
4 |
5 | namespace Toastify
6 | {
7 | internal class Win32
8 | {
9 | [DllImport("user32.dll", SetLastError = true)]
10 | internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
11 |
12 | internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
13 |
14 | [DllImport("user32.dll")]
15 | internal static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
16 |
17 | [DllImport("user32", SetLastError = true)]
18 | [return: MarshalAs(UnmanagedType.Bool)]
19 | internal static extern bool EnumThreadWindows(int threadId, EnumWindowsProc callback, IntPtr lParam);
20 |
21 | [DllImport("user32.dll")]
22 | internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
23 |
24 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
25 | internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
26 |
27 | [DllImport("user32.dll", CharSet = CharSet.Auto)]
28 | internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
29 |
30 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
31 | internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
32 |
33 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
34 | internal static extern int GetWindowTextLength(IntPtr hWnd);
35 |
36 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
37 | internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
38 |
39 | [DllImport("user32.dll")]
40 | internal static extern IntPtr SetFocus(IntPtr hWnd);
41 |
42 | [DllImport("user32.dll")]
43 | internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
44 |
45 | [DllImport("user32.dll")]
46 | internal static extern bool SetForegroundWindow(IntPtr hWnd);
47 |
48 | [DllImport("user32.dll")]
49 | internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
50 |
51 | [DllImport("user32.dll", SetLastError = true)]
52 | internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
53 |
54 | [Flags()]
55 | internal enum SetWindowPosFlags : uint
56 | {
57 | /// If the calling thread and the thread that owns the window are attached to different input queues,
58 | /// the system posts the request to the thread that owns the window. This prevents the calling thread from
59 | /// blocking its execution while other threads process the request.
60 | /// SWP_ASYNCWINDOWPOS
61 | AsynchronousWindowPosition = 0x4000,
62 | /// Prevents generation of the WM_SYNCPAINT message.
63 | /// SWP_DEFERERASE
64 | DeferErase = 0x2000,
65 | /// Draws a frame (defined in the window's class description) around the window.
66 | /// SWP_DRAWFRAME
67 | DrawFrame = 0x0020,
68 | /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
69 | /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
70 | /// is sent only when the window's size is being changed.
71 | /// SWP_FRAMECHANGED
72 | FrameChanged = 0x0020,
73 | /// Hides the window.
74 | /// SWP_HIDEWINDOW
75 | HideWindow = 0x0080,
76 | /// Does not activate the window. If this flag is not set, the window is activated and moved to the
77 | /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
78 | /// parameter).
79 | /// SWP_NOACTIVATE
80 | DoNotActivate = 0x0010,
81 | /// Discards the entire contents of the client area. If this flag is not specified, the valid
82 | /// contents of the client area are saved and copied back into the client area after the window is sized or
83 | /// repositioned.
84 | /// SWP_NOCOPYBITS
85 | DoNotCopyBits = 0x0100,
86 | /// Retains the current position (ignores X and Y parameters).
87 | /// SWP_NOMOVE
88 | IgnoreMove = 0x0002,
89 | /// Does not change the owner window's position in the Z order.
90 | /// SWP_NOOWNERZORDER
91 | DoNotChangeOwnerZOrder = 0x0200,
92 | /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
93 | /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
94 | /// window uncovered as a result of the window being moved. When this flag is set, the application must
95 | /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
96 | /// SWP_NOREDRAW
97 | DoNotRedraw = 0x0008,
98 | /// Same as the SWP_NOOWNERZORDER flag.
99 | /// SWP_NOREPOSITION
100 | DoNotReposition = 0x0200,
101 | /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
102 | /// SWP_NOSENDCHANGING
103 | DoNotSendChangingEvent = 0x0400,
104 | /// Retains the current size (ignores the cx and cy parameters).
105 | /// SWP_NOSIZE
106 | IgnoreResize = 0x0001,
107 | /// Retains the current Z order (ignores the hWndInsertAfter parameter).
108 | /// SWP_NOZORDER
109 | IgnoreZOrder = 0x0004,
110 | /// Displays the window.
111 | /// SWP_SHOWWINDOW
112 | ShowWindow = 0x0040,
113 | }
114 |
115 | internal struct WINDOWPLACEMENT
116 | {
117 | public int length;
118 | public int flags;
119 | public int showCmd;
120 | public System.Drawing.Point ptMinPosition;
121 | public System.Drawing.Point ptMaxPosition;
122 | public System.Drawing.Rectangle rcNormalPosition;
123 | }
124 |
125 | internal class Constants
126 | {
127 | internal const uint WM_APPCOMMAND = 0x0319;
128 |
129 | internal const int SW_SHOWMINIMIZED = 2;
130 | internal const int SW_SHOWNOACTIVATE = 4;
131 | internal const int SW_SHOWMINNOACTIVE = 7;
132 | internal const int SW_SHOW = 5;
133 | internal const int SW_RESTORE = 9;
134 |
135 | internal const int WM_CLOSE = 0x10;
136 | internal const int WM_QUIT = 0x12;
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore private files
2 |
3 | *.private.*
4 |
5 | ## Ignore Visual Studio temporary files, build results, and
6 | ## files generated by popular Visual Studio add-ons.
7 | ##
8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
9 |
10 | # User-specific files
11 | *.rsuser
12 | *.suo
13 | *.user
14 | *.userosscache
15 | *.sln.docstates
16 |
17 | # User-specific files (MonoDevelop/Xamarin Studio)
18 | *.userprefs
19 |
20 | # Mono auto generated files
21 | mono_crash.*
22 |
23 | # Build results
24 | [Dd]ebug/
25 | [Dd]ebugPublic/
26 | [Rr]elease/
27 | [Rr]eleases/
28 | x64/
29 | x86/
30 | [Aa][Rr][Mm]/
31 | [Aa][Rr][Mm]64/
32 | bld/
33 | [Bb]in/
34 | [Oo]bj/
35 | [Ll]og/
36 | [Ll]ogs/
37 |
38 | # Visual Studio 2015/2017 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | #wwwroot/
42 |
43 | # Visual Studio 2017 auto generated files
44 | Generated\ Files/
45 |
46 | # MSTest test Results
47 | [Tt]est[Rr]esult*/
48 | [Bb]uild[Ll]og.*
49 |
50 | # NUnit
51 | *.VisualState.xml
52 | TestResult.xml
53 | nunit-*.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # Benchmark Results
61 | BenchmarkDotNet.Artifacts/
62 |
63 | # .NET Core
64 | project.lock.json
65 | project.fragment.lock.json
66 | artifacts/
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.vspscc
94 | *.vssscc
95 | .builds
96 | *.pidb
97 | *.svclog
98 | *.scc
99 |
100 | # Chutzpah Test files
101 | _Chutzpah*
102 |
103 | # Visual C++ cache files
104 | ipch/
105 | *.aps
106 | *.ncb
107 | *.opendb
108 | *.opensdf
109 | *.sdf
110 | *.cachefile
111 | *.VC.db
112 | *.VC.VC.opendb
113 |
114 | # Visual Studio profiler
115 | *.psess
116 | *.vsp
117 | *.vspx
118 | *.sap
119 |
120 | # Visual Studio Trace Files
121 | *.e2e
122 |
123 | # TFS 2012 Local Workspace
124 | $tf/
125 |
126 | # Guidance Automation Toolkit
127 | *.gpState
128 |
129 | # ReSharper is a .NET coding add-in
130 | _ReSharper*/
131 | *.[Rr]e[Ss]harper
132 | *.DotSettings.user
133 |
134 | # TeamCity is a build add-in
135 | _TeamCity*
136 |
137 | # DotCover is a Code Coverage Tool
138 | *.dotCover
139 |
140 | # AxoCover is a Code Coverage Tool
141 | .axoCover/*
142 | !.axoCover/settings.json
143 |
144 | # Visual Studio code coverage results
145 | *.coverage
146 | *.coveragexml
147 |
148 | # NCrunch
149 | _NCrunch_*
150 | .*crunch*.local.xml
151 | nCrunchTemp_*
152 |
153 | # MightyMoose
154 | *.mm.*
155 | AutoTest.Net/
156 |
157 | # Web workbench (sass)
158 | .sass-cache/
159 |
160 | ChimeHelperSetup.exe
161 |
162 | # Installshield output folder
163 | [Ee]xpress/
164 |
165 | # DocProject is a documentation generator add-in
166 | DocProject/buildhelp/
167 | DocProject/Help/*.HxT
168 | DocProject/Help/*.HxC
169 | DocProject/Help/*.hhc
170 | DocProject/Help/*.hhk
171 | DocProject/Help/*.hhp
172 | DocProject/Help/Html2
173 | DocProject/Help/html
174 |
175 | # Click-Once directory
176 | publish/
177 |
178 | # Publish Web Output
179 | *.[Pp]ublish.xml
180 | *.azurePubxml
181 | # Note: Comment the next line if you want to checkin your web deploy settings,
182 | # but database connection strings (with potential passwords) will be unencrypted
183 | *.pubxml
184 | *.publishproj
185 |
186 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
187 | # checkin your Azure Web App publish settings, but sensitive information contained
188 | # in these scripts will be unencrypted
189 | PublishScripts/
190 |
191 | # NuGet Packages
192 | *.nupkg
193 | # NuGet Symbol Packages
194 | *.snupkg
195 | # The packages folder can be ignored because of Package Restore
196 | **/[Pp]ackages/*
197 | # except build/, which is used as an MSBuild target.
198 | !**/[Pp]ackages/build/
199 | # Uncomment if necessary however generally it will be regenerated when needed
200 | #!**/[Pp]ackages/repositories.config
201 | # NuGet v3's project.json files produces more ignorable files
202 | *.nuget.props
203 | *.nuget.targets
204 |
205 | # Microsoft Azure Build Output
206 | csx/
207 | *.build.csdef
208 |
209 | # Microsoft Azure Emulator
210 | ecf/
211 | rcf/
212 |
213 | # Windows Store app package directories and files
214 | AppPackages/
215 | BundleArtifacts/
216 | Package.StoreAssociation.xml
217 | _pkginfo.txt
218 | *.appx
219 | *.appxbundle
220 | *.appxupload
221 |
222 | # Visual Studio cache files
223 | # files ending in .cache can be ignored
224 | *.[Cc]ache
225 | # but keep track of directories ending in .cache
226 | !?*.[Cc]ache/
227 |
228 | # Others
229 | ClientBin/
230 | ~$*
231 | *~
232 | *.dbmdl
233 | *.dbproj.schemaview
234 | *.jfm
235 | *.pfx
236 | *.publishsettings
237 | orleans.codegen.cs
238 |
239 | # Including strong name files can present a security risk
240 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
241 | #*.snk
242 |
243 | # Since there are multiple workflows, uncomment next line to ignore bower_components
244 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
245 | #bower_components/
246 |
247 | # RIA/Silverlight projects
248 | Generated_Code/
249 |
250 | # Backup & report files from converting an old project file
251 | # to a newer Visual Studio version. Backup files are not needed,
252 | # because we have git ;-)
253 | _UpgradeReport_Files/
254 | Backup*/
255 | UpgradeLog*.XML
256 | UpgradeLog*.htm
257 | ServiceFabricBackup/
258 | *.rptproj.bak
259 |
260 | # SQL Server files
261 | *.mdf
262 | *.ldf
263 | *.ndf
264 |
265 | # Business Intelligence projects
266 | *.rdl.data
267 | *.bim.layout
268 | *.bim_*.settings
269 | *.rptproj.rsuser
270 | *- [Bb]ackup.rdl
271 | *- [Bb]ackup ([0-9]).rdl
272 | *- [Bb]ackup ([0-9][0-9]).rdl
273 |
274 | # Microsoft Fakes
275 | FakesAssemblies/
276 |
277 | # GhostDoc plugin setting file
278 | *.GhostDoc.xml
279 |
280 | # Node.js Tools for Visual Studio
281 | .ntvs_analysis.dat
282 | node_modules/
283 |
284 | # Visual Studio 6 build log
285 | *.plg
286 |
287 | # Visual Studio 6 workspace options file
288 | *.opt
289 |
290 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
291 | *.vbw
292 |
293 | # Visual Studio LightSwitch build output
294 | **/*.HTMLClient/GeneratedArtifacts
295 | **/*.DesktopClient/GeneratedArtifacts
296 | **/*.DesktopClient/ModelManifest.xml
297 | **/*.Server/GeneratedArtifacts
298 | **/*.Server/ModelManifest.xml
299 | _Pvt_Extensions
300 |
301 | # Paket dependency manager
302 | .paket/paket.exe
303 | paket-files/
304 |
305 | # FAKE - F# Make
306 | .fake/
307 |
308 | # CodeRush personal settings
309 | .cr/personal
310 |
311 | # Python Tools for Visual Studio (PTVS)
312 | __pycache__/
313 | *.pyc
314 |
315 | # Cake - Uncomment if you are using it
316 | # tools/**
317 | # !tools/packages.config
318 |
319 | # Tabs Studio
320 | *.tss
321 |
322 | # Telerik's JustMock configuration file
323 | *.jmconfig
324 |
325 | # BizTalk build output
326 | *.btp.cs
327 | *.btm.cs
328 | *.odx.cs
329 | *.xsd.cs
330 |
331 | # OpenCover UI analysis results
332 | OpenCover/
333 |
334 | # Azure Stream Analytics local run output
335 | ASALocalRun/
336 |
337 | # MSBuild Binary and Structured Log
338 | *.binlog
339 |
340 | # NVidia Nsight GPU debugger configuration file
341 | *.nvuser
342 |
343 | # MFractors (Xamarin productivity tool) working folder
344 | .mfractor/
345 |
346 | # Local History for Visual Studio
347 | .localhistory/
348 |
349 | # BeatPulse healthcheck temp database
350 | healthchecksdb
351 |
352 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
353 | MigrationBackup/
354 |
355 | # Ionide (cross platform F# VS Code tools) working folder
356 | .ionide/
--------------------------------------------------------------------------------
/Toastify/Settings.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Input;
6 | using System.Windows.Forms;
7 | using System.Globalization;
8 |
9 | namespace Toastify
10 | {
11 | ///
12 | /// Logique d'interaction pour Window1.xaml
13 | ///
14 | public partial class Settings : Window
15 | {
16 | public SettingsXml settings;
17 | private readonly Toast toast;
18 |
19 | private readonly List modifierKeys = new List { System.Windows.Input.Key.LeftCtrl, System.Windows.Input.Key.RightCtrl, System.Windows.Input.Key.LeftAlt, System.Windows.Input.Key.Right, System.Windows.Input.Key.LeftShift, System.Windows.Input.Key.RightShift, System.Windows.Input.Key.LWin, System.Windows.Input.Key.RWin, System.Windows.Input.Key.System };
20 |
21 | private static Settings _current;
22 |
23 | public static void Launch(Toast toast)
24 | {
25 | if (_current != null)
26 | {
27 | _current.Activate();
28 | }
29 | else
30 | {
31 | new Settings(toast).ShowDialog();
32 | }
33 | }
34 |
35 | private Settings(Toast toast)
36 | {
37 | Telemetry.TrackEvent(TelemetryCategory.General, Telemetry.TelemetryEvent.SettingsLaunched);
38 |
39 | this.settings = SettingsXml.Current.Clone();
40 | this.toast = toast;
41 |
42 | InitializeComponent();
43 |
44 | //Data context initialisation
45 | GeneralGrid.DataContext = this.settings;
46 |
47 | //Slider initialisation
48 | try
49 | {
50 | slTopColor.Value = byte.Parse(settings.ToastColorTop.Substring(1, 2), NumberStyles.AllowHexSpecifier);
51 | slBottomColor.Value = byte.Parse(settings.ToastColorBottom.Substring(1, 2), NumberStyles.AllowHexSpecifier);
52 | slBorderColor.Value = byte.Parse(settings.ToastBorderColor.Substring(1, 2), NumberStyles.AllowHexSpecifier);
53 | }
54 | catch { }
55 |
56 | if (_current == null)
57 | _current = this;
58 | }
59 |
60 | private void Window_Closed(object sender, EventArgs e)
61 | {
62 | if (_current == this)
63 | _current = null;
64 | }
65 |
66 | //Change Color button click events
67 | private void bChangeColorTop_Click(object sender, RoutedEventArgs e)
68 | {
69 | ColorDialog MyDialog = new ColorDialog();
70 | string alpha = settings.ToastColorTop.Substring(1, 2);
71 | MyDialog.Color = HexToColor(settings.ToastColorTop);
72 | MyDialog.ShowDialog();
73 | settings.ToastColorTop = "#" + alpha + MyDialog.Color.R.ToString("X2") + MyDialog.Color.G.ToString("X2") + MyDialog.Color.B.ToString("X2");
74 | }
75 |
76 | private void bChangeColorBottom_Click(object sender, RoutedEventArgs e)
77 | {
78 | ColorDialog MyDialog = new ColorDialog();
79 | string alpha = settings.ToastColorBottom.Substring(1, 2);
80 | MyDialog.Color = HexToColor(settings.ToastColorBottom);
81 | MyDialog.ShowDialog();
82 | settings.ToastColorBottom = "#" + alpha + MyDialog.Color.R.ToString("X2") + MyDialog.Color.G.ToString("X2") + MyDialog.Color.B.ToString("X2");
83 | }
84 |
85 | private void bChangeBorderColor_Click(object sender, RoutedEventArgs e)
86 | {
87 | ColorDialog MyDialog = new ColorDialog();
88 | string alpha = settings.ToastBorderColor.Substring(1, 2);
89 | MyDialog.Color = HexToColor(settings.ToastBorderColor);
90 | MyDialog.ShowDialog();
91 | settings.ToastBorderColor = "#" + alpha + MyDialog.Color.R.ToString("X2") + MyDialog.Color.G.ToString("X2") + MyDialog.Color.B.ToString("X2");
92 | }
93 |
94 | //Default and Save blick events
95 | private void bDefault_Click(object sender, RoutedEventArgs e)
96 | {
97 | settings.Default();
98 | SaveAndApplySettings();
99 | }
100 |
101 | private void bSave_Click(object sender, RoutedEventArgs e)
102 | {
103 | SaveAndApplySettings();
104 | }
105 |
106 | private void SaveAndApplySettings()
107 | {
108 | settings.Save(replaceCurrent: true);
109 |
110 | toast.InitToast();
111 | toast.DisplayAction(SpotifyAction.SettingsSaved, null);
112 | }
113 |
114 | //Text box Mouse Wheel events
115 | private void tbCornerTopLeft_MouseWheel(object sender, MouseWheelEventArgs e)
116 | {
117 | if (e.Delta > 0)
118 | {
119 | settings.ToastBorderCornerRadiusTopLeft += 0.1;
120 | }
121 | else if (settings.ToastBorderCornerRadiusTopLeft >= 0.1)
122 | settings.ToastBorderCornerRadiusTopLeft -= 0.1;
123 | }
124 |
125 | private void tbCornerTopRight_MouseWheel(object sender, MouseWheelEventArgs e)
126 | {
127 | if (e.Delta > 0)
128 | settings.ToastBorderCornerRadiusTopRight += 0.1;
129 | else if (settings.ToastBorderCornerRadiusTopLeft >= 0.1)
130 | settings.ToastBorderCornerRadiusTopRight -= 0.1;
131 | }
132 |
133 | private void tbCornerBottomRight_MouseWheel(object sender, MouseWheelEventArgs e)
134 | {
135 | if (e.Delta > 0)
136 | settings.ToastBorderCornerRadiusBottomRight += 0.1;
137 | else if (settings.ToastBorderCornerRadiusBottomRight >= 0.1)
138 | settings.ToastBorderCornerRadiusBottomRight -= 0.1;
139 | }
140 |
141 | private void tbCornerBottomLeft_MouseWheel(object sender, MouseWheelEventArgs e)
142 | {
143 | if (e.Delta > 0)
144 | settings.ToastBorderCornerRadiusBottomLeft += 0.1;
145 | else if (settings.ToastBorderCornerRadiusBottomLeft >= 0.1)
146 | settings.ToastBorderCornerRadiusBottomLeft -= 0.1;
147 | }
148 |
149 | private void tbFadeOutTime_MouseWheel(object sender, MouseWheelEventArgs e)
150 | {
151 | if (e.Delta > 0)
152 | settings.FadeOutTime += 10;
153 | else if (settings.FadeOutTime >= 10)
154 | settings.FadeOutTime -= 10;
155 | }
156 |
157 | private void tbBorderThickness_MouseWheel(object sender, MouseWheelEventArgs e)
158 | {
159 | if (e.Delta > 0)
160 | settings.ToastBorderThickness++;
161 | else if (settings.ToastBorderThickness >= 1)
162 | settings.ToastBorderThickness--;
163 | }
164 |
165 | private void tbToastWidth_MouseWheel(object sender, MouseWheelEventArgs e)
166 | {
167 | if (e.Delta > 0)
168 | settings.ToastWidth += 5;
169 | else if (settings.ToastWidth >= 205)
170 | settings.ToastWidth -= 5;
171 | }
172 |
173 | private void tbToastHeight_MouseWheel(object sender, MouseWheelEventArgs e)
174 | {
175 | if (e.Delta > 0)
176 | settings.ToastHeight += 5;
177 | else if (settings.ToastHeight >= 70)
178 | settings.ToastHeight -= 5;
179 | }
180 |
181 | private void tbPositionLeft_MouseWheel(object sender, MouseWheelEventArgs e)
182 | {
183 | if (e.Delta > 0)
184 | settings.PositionLeft++;
185 | else if (settings.PositionLeft > 0)
186 | settings.PositionLeft--;
187 | }
188 |
189 | private void tbPositionTop_MouseWheel(object sender, MouseWheelEventArgs e)
190 | {
191 | if (e.Delta > 0)
192 | settings.PositionTop++;
193 | else if (settings.PositionTop > 0)
194 | settings.PositionTop--;
195 | }
196 |
197 | //Slider value changed events
198 | private void slTopColor_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
199 | {
200 | string transparency = Convert.ToByte(slTopColor.Value).ToString("X2");
201 | settings.ToastColorTop = "#" + transparency + settings.ToastColorTop.Substring(3);
202 | }
203 |
204 | private void slBottomColor_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
205 | {
206 | string transparency = Convert.ToByte(slBottomColor.Value).ToString("X2");
207 | settings.ToastColorBottom = "#" + transparency + settings.ToastColorBottom.Substring(3);
208 | }
209 |
210 | private void slBorderColor_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
211 | {
212 | string transparency = Convert.ToByte(slBorderColor.Value).ToString("X2");
213 | settings.ToastBorderColor = "#" + transparency + settings.ToastBorderColor.Substring(3);
214 | }
215 |
216 | // Hexadecimal to Color converter
217 | public static System.Drawing.Color HexToColor(string hexColor)
218 | {
219 | //Remove # if present
220 | if (hexColor.IndexOf('#') != -1)
221 | hexColor = hexColor.Replace("#", "");
222 |
223 | byte alpha = 0;
224 | byte red = 0;
225 | byte green = 0;
226 | byte blue = 0;
227 |
228 | if (hexColor.Length == 8)
229 | {
230 | //#RRGGBB
231 | alpha = byte.Parse(hexColor.Substring(0, 2), NumberStyles.AllowHexSpecifier);
232 | red = byte.Parse(hexColor.Substring(2, 2), NumberStyles.AllowHexSpecifier);
233 | green = byte.Parse(hexColor.Substring(4, 2), NumberStyles.AllowHexSpecifier);
234 | blue = byte.Parse(hexColor.Substring(6, 2), NumberStyles.AllowHexSpecifier);
235 | }
236 |
237 | return System.Drawing.Color.FromArgb(alpha, red, green, blue);
238 | }
239 |
240 | private void txtSingleKey_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
241 | {
242 | e.Handled = true;
243 |
244 | var key = e.Key;
245 |
246 | if (key == Key.System)
247 | {
248 | key = e.SystemKey;
249 | }
250 |
251 | txtSingleKey.Text = key.ToString();
252 |
253 | Hotkey hotkey = lstHotKeys.SelectedItem as Hotkey;
254 |
255 | if (hotkey != null)
256 | {
257 | hotkey.Key = key;
258 | }
259 | }
260 |
261 | private void lstHotKeys_SelectionChanged(object sender, SelectionChangedEventArgs e)
262 | {
263 | Hotkey hotkey = lstHotKeys.SelectedItem as Hotkey;
264 |
265 | if (hotkey != null)
266 | {
267 | txtSingleKey.Text = hotkey.Key.ToString();
268 | }
269 | }
270 |
271 | private void btnSaveTrackToFilePath_Click(object sender, RoutedEventArgs e)
272 | {
273 | var dialog = new OpenFileDialog();
274 |
275 | if (SettingsXml.Current.SaveTrackToFilePath != null)
276 | {
277 | dialog.FileName = SettingsXml.Current.SaveTrackToFilePath;
278 | }
279 |
280 | dialog.CheckPathExists = true;
281 | dialog.CheckFileExists = false;
282 | dialog.ShowReadOnly = false;
283 |
284 | if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
285 | {
286 | settings.SaveTrackToFilePath = dialog.FileName;
287 | }
288 | }
289 |
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/Toastify/HotKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.ComponentModel;
6 | using System.Windows.Forms;
7 | using System.Windows.Input;
8 | using ManagedWinapi;
9 | using System.Xml.Serialization;
10 |
11 | namespace Toastify
12 | {
13 | public class Hotkey : INotifyPropertyChanged
14 | {
15 | private bool _enabled;
16 | ///
17 | /// Specifies whether or not the hotkey is enabled or disabled from a user's
18 | /// perspective. Does not actually enable or disable the hotkey, use Activate()
19 | /// and Deactivate().
20 | ///
21 | /// Why do we have these two schemes? We need a way to be able to deactivate a
22 | /// Hotkey (for example when unloading settings) without changing the Enabled
23 | /// property (which only indicates the user's preference)
24 | ///
25 | public bool Enabled
26 | {
27 | get { return _enabled; }
28 | set
29 | {
30 | if (_enabled != value)
31 | {
32 | _enabled = value;
33 |
34 | NotifyPropertyChanged("Enabled");
35 | }
36 | }
37 | }
38 |
39 | private bool _windowsKey;
40 | public bool WindowsKey
41 | {
42 | get { return _windowsKey; }
43 | set
44 | {
45 | if (_windowsKey != value)
46 | {
47 | _windowsKey = value;
48 |
49 | NotifyPropertyChanged("WindowsKey");
50 |
51 | CheckIfValid();
52 | }
53 | }
54 | }
55 |
56 |
57 | private bool _ctrl;
58 | public bool Ctrl
59 | {
60 | get { return _ctrl; }
61 | set
62 | {
63 | if (_ctrl != value)
64 | {
65 | _ctrl = value;
66 |
67 | NotifyPropertyChanged("Notify");
68 |
69 | CheckIfValid();
70 | }
71 | }
72 | }
73 |
74 | private bool _alt;
75 | public bool Alt
76 | {
77 | get { return _alt; }
78 | set
79 | {
80 | if (_alt != value)
81 | {
82 | _alt = value;
83 |
84 | NotifyPropertyChanged("Alt");
85 |
86 | CheckIfValid();
87 | }
88 | }
89 | }
90 |
91 | private bool _shift;
92 | public bool Shift
93 | {
94 | get { return _shift; }
95 | set
96 | {
97 | if (_shift != value)
98 | {
99 | _shift = value;
100 |
101 | NotifyPropertyChanged("Shift");
102 |
103 | CheckIfValid();
104 | }
105 | }
106 | }
107 |
108 |
109 | private Key _key;
110 | public Key Key
111 | {
112 | get { return _key; }
113 | set
114 | {
115 | if (_key != value)
116 | {
117 | _key = value;
118 |
119 | NotifyPropertyChanged("Key");
120 |
121 | CheckIfValid();
122 | }
123 | }
124 | }
125 |
126 | private SpotifyAction _action;
127 | public SpotifyAction Action
128 | {
129 | get { return _action; }
130 | set
131 | {
132 | if (_action != value)
133 | {
134 | _action = value;
135 |
136 | NotifyPropertyChanged("Action");
137 | }
138 | }
139 | }
140 |
141 | [XmlIgnore]
142 | public string HumanReadableAction
143 | {
144 | get
145 | {
146 | switch (Action)
147 | {
148 | case SpotifyAction.CopyTrackInfo:
149 | return "Copy Track Name";
150 |
151 | case SpotifyAction.PasteTrackInfo:
152 | return "Paste Track Name";
153 |
154 | case SpotifyAction.Mute:
155 | return "Mute";
156 |
157 | case SpotifyAction.NextTrack:
158 | return "Next Track";
159 |
160 | case SpotifyAction.None:
161 | return "None";
162 |
163 | case SpotifyAction.PlayPause:
164 | return "Play / Pause";
165 |
166 | case SpotifyAction.PreviousTrack:
167 | return "Previous Track";
168 |
169 | case SpotifyAction.SettingsSaved:
170 | return "Settings Saved";
171 |
172 | case SpotifyAction.ShowSpotify:
173 | return "Show / Hide Spotify";
174 |
175 | case SpotifyAction.ShowToast:
176 | return "Show Toast";
177 |
178 | case SpotifyAction.Stop:
179 | return "Stop";
180 |
181 | case SpotifyAction.VolumeDown:
182 | return "Volume Down";
183 |
184 | case SpotifyAction.VolumeUp:
185 | return "Volume Up";
186 |
187 | case SpotifyAction.FastForward:
188 | return "Fast Forward";
189 |
190 | case SpotifyAction.Rewind:
191 | return "Rewind";
192 |
193 | case SpotifyAction.ThumbsUp:
194 | return "Thumbs Up";
195 |
196 | case SpotifyAction.ThumbsDown:
197 | return "Thunbs Down";
198 | }
199 |
200 | return "No Action";
201 | }
202 | }
203 |
204 | private bool _isValid;
205 | [XmlIgnore]
206 | public bool IsValid
207 | {
208 | get { return _isValid; }
209 | set
210 | {
211 | if (_isValid != value)
212 | {
213 | _isValid = value;
214 |
215 | NotifyPropertyChanged("IsValid");
216 | }
217 | }
218 |
219 | }
220 |
221 | private string _invalidReason;
222 | [XmlIgnore]
223 | public string InvalidReason
224 | {
225 | get { return _invalidReason; }
226 | set
227 | {
228 | if (_invalidReason != value)
229 | {
230 | _invalidReason = value;
231 |
232 | NotifyPropertyChanged("InvalidReason");
233 | }
234 | }
235 | }
236 |
237 | private bool _active = false;
238 | private ManagedWinapi.Hotkey _globalKey;
239 |
240 | public Hotkey Clone()
241 | {
242 | Hotkey clone = MemberwiseClone() as Hotkey;
243 |
244 | // regardless of whether or not the original hotkey was active
245 | // the cloned one should not start in an active state
246 | clone._active = false;
247 |
248 | return clone;
249 | }
250 |
251 | ///
252 | /// Turn this HotKey off
253 | ///
254 | public void Deactivate()
255 | {
256 | SetActive(false);
257 | }
258 |
259 | ///
260 | /// Turn this hotkey on. Does nothing if this Hotkey is not enabled
261 | ///
262 | public void Activate()
263 | {
264 | SetActive(true);
265 | }
266 |
267 | private void SetActive(bool value)
268 | {
269 | if (_active != value)
270 | {
271 | _active = value;
272 |
273 | InitGlobalKey();
274 | }
275 | }
276 |
277 | private void InitGlobalKey()
278 | {
279 | // If we're not enabled shut everything done asap
280 | if (!Enabled || !_active)
281 | {
282 | if (_globalKey != null)
283 | {
284 | _globalKey.Enabled = false;
285 | _globalKey = null; // may as well collect the memory
286 | }
287 |
288 | // may not be false if !Enabled
289 | _active = false;
290 |
291 | return;
292 | }
293 |
294 | if (_globalKey == null)
295 | _globalKey = new ManagedWinapi.Hotkey();
296 |
297 | // make sure that we don't try to reregister the key midway updating
298 | // the combination
299 | if (_globalKey.Enabled)
300 | _globalKey.Enabled = false;
301 |
302 | _globalKey.WindowsKey = this.WindowsKey;
303 | _globalKey.Alt = this.Alt;
304 | _globalKey.Ctrl = this.Ctrl;
305 | _globalKey.Shift = this.Shift;
306 | _globalKey.KeyCode = ConvertInputKeyToFormsKeys(this.Key);
307 |
308 | _globalKey.HotkeyPressed += (s, e) => { Toast.ActionHookCallback(this); };
309 |
310 | try
311 | {
312 | _globalKey.Enabled = true;
313 | }
314 | catch (HotkeyAlreadyInUseException)
315 | {
316 | IsValid = false;
317 | InvalidReason = "Hotkey is already in use by a different program";
318 | }
319 | }
320 |
321 | ///
322 | /// Validity rules are:
323 | ///
324 | /// 1. Ctrl or Alt must be selected
325 | /// 2. a key must be specified
326 | ///
327 | private void CheckIfValid()
328 | {
329 | if (Key == Key.None)
330 | {
331 | IsValid = false;
332 | InvalidReason = "You must select a valid key for your hotkey combination";
333 |
334 | return;
335 | }
336 |
337 | IsValid = true;
338 | InvalidReason = "";
339 | }
340 |
341 | #region Static Functions
342 |
343 | private static readonly List _hotkeys = new List();
344 |
345 | public static void ClearAll()
346 | {
347 | // disable will be called by the destructors, but we want to force a disable
348 | // now so that we don't wait for the GC to clean up the objects
349 | foreach (Hotkey hotkey in _hotkeys)
350 | {
351 | hotkey.Deactivate();
352 | }
353 |
354 | _hotkeys.Clear();
355 | }
356 |
357 | private static System.Windows.Forms.Keys ConvertInputKeyToFormsKeys(System.Windows.Input.Key key)
358 | {
359 | if (Enum.GetNames(typeof(System.Windows.Forms.Keys)).Contains(key.ToString()))
360 | return (System.Windows.Forms.Keys)Enum.Parse(typeof(System.Windows.Forms.Keys), key.ToString());
361 | else
362 | return Keys.None;
363 | }
364 |
365 | #endregion
366 |
367 | private readonly ManagedWinapi.Hotkey key = new ManagedWinapi.Hotkey();
368 |
369 | public Hotkey()
370 | {
371 | _hotkeys.Add(this);
372 | }
373 |
374 | ~Hotkey()
375 | {
376 | if (key != null)
377 | key.Enabled = false;
378 | }
379 |
380 | [XmlIgnore]
381 | public string HumanReadableKey
382 | {
383 | get
384 | {
385 | StringBuilder sb = new StringBuilder();
386 | if (this.WindowsKey) sb.Append("Win+");
387 | if (this.Ctrl) sb.Append("Ctrl+");
388 | if (this.Alt) sb.Append("Alt+");
389 | if (this.Shift) sb.Append("Shift+");
390 | sb.Append(this.Key.ToString());
391 | return sb.ToString();
392 | }
393 | }
394 |
395 | #region INotifyPropertyChanged
396 |
397 | public event PropertyChangedEventHandler PropertyChanged;
398 |
399 | private void NotifyPropertyChanged(String info)
400 | {
401 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
402 | }
403 |
404 | #endregion
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/Toastify/Toastify.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | Debug
6 | AnyCPU
7 | 9.0.30729
8 | 2.0
9 | {CCC4A761-56D2-4188-807F-F7A547DFB031}
10 | WinExe
11 | Properties
12 | Toastify
13 | Toastify
14 | v4.6.1
15 | 512
16 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
17 | 4
18 | spotify.ico
19 | Toastify.EntryPoint
20 |
21 |
22 | 3.5
23 |
24 | true
25 |
26 |
27 | SAK
28 | SAK
29 | SAK
30 | SAK
31 | publish\
32 | true
33 | Web
34 | true
35 | Foreground
36 | 7
37 | Days
38 | false
39 | false
40 | true
41 | http://toastify.codeplex.com/
42 | true
43 | publish.htm
44 | 1
45 | 1.0.0.%2a
46 | false
47 | true
48 | true
49 |
50 |
51 | true
52 | full
53 | false
54 | bin\Debug\
55 | DEBUG;TRACE
56 | prompt
57 | 4
58 | false
59 | AllRules.ruleset
60 | false
61 |
62 |
63 | pdbonly
64 | true
65 | ..\InstallationScript\
66 | TRACE
67 | prompt
68 | 4
69 | AllRules.ruleset
70 | false
71 |
72 |
73 | D38DAF47B3CBFC190231922089F056B55D662088
74 |
75 |
76 | Toastify_TemporaryKey.pfx
77 |
78 |
79 | true
80 |
81 |
82 | false
83 |
84 |
85 |
86 | ..\packages\Garlic.2.1.0.0\lib\net35\Garlic.dll
87 |
88 |
89 | .\ManagedWinapi.dll
90 |
91 |
92 |
93 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
94 |
95 |
96 | ..\packages\SpotifyAPI.Web.6.0.0-beta.9\lib\netstandard2.0\SpotifyAPI.Web.dll
97 |
98 |
99 |
100 | 3.5
101 |
102 |
103 |
104 |
105 |
106 |
107 | 3.5
108 |
109 |
110 | 3.5
111 |
112 |
113 |
114 |
115 | 3.0
116 |
117 |
118 | 3.0
119 |
120 |
121 | 3.0
122 |
123 |
124 | 3.0
125 |
126 |
127 |
128 |
129 | MSBuild:Compile
130 | Designer
131 | MSBuild:Compile
132 | Designer
133 |
134 |
135 | Designer
136 | MSBuild:Compile
137 | MSBuild:Compile
138 | Designer
139 |
140 |
141 | Designer
142 | MSBuild:Compile
143 |
144 |
145 | MSBuild:Compile
146 | Designer
147 | MSBuild:Compile
148 | Designer
149 |
150 |
151 | App.xaml
152 | Code
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | Settings.xaml
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | Toast.xaml
169 | Code
170 |
171 |
172 |
173 |
174 | About.xaml
175 |
176 |
177 |
178 |
179 |
180 | Code
181 |
182 |
183 | True
184 | True
185 | Resources.resx
186 |
187 |
188 | True
189 | Settings.settings
190 | True
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | ResXFileCodeGenerator
199 | Resources.Designer.cs
200 |
201 |
202 |
203 | Always
204 |
205 |
206 |
207 | SettingsSingleFileGenerator
208 | Settings.Designer.cs
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 | {2869dbfe-7762-4930-95ea-2b0c111cf353}
227 | AutoHotkey.Interop
228 |
229 |
230 | {4f92be1f-a5cc-4604-9185-1f09ddafc7b8}
231 | Toastify.Plugin
232 |
233 |
234 |
235 |
236 | False
237 | .NET Framework 3.5 SP1 Client Profile
238 | false
239 |
240 |
241 | False
242 | .NET Framework 3.5 SP1
243 | true
244 |
245 |
246 | False
247 | Windows Installer 3.1
248 | true
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 | Always
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
279 |
--------------------------------------------------------------------------------
/Toastify/VolumeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace Toastify
6 | {
7 | class VolumeHelper
8 | {
9 | // base code from: http://stackoverflow.com/a/14322736
10 |
11 | public static void IncrementVolume(string name)
12 | {
13 | var curVolume = GetApplicationVolume(name);
14 |
15 | if (curVolume != null && curVolume < 100)
16 | SetApplicationVolume(name, (float)curVolume + 2);
17 | }
18 |
19 | public static void DecrementVolume(string name)
20 | {
21 | var curVolume = GetApplicationVolume(name);
22 |
23 | if (curVolume != null && curVolume > 0)
24 | SetApplicationVolume(name, (float)curVolume - 2);
25 | }
26 |
27 | public static float? GetApplicationVolume(string name)
28 | {
29 | ISimpleAudioVolume volume = GetVolumeObject(name);
30 | if (volume == null)
31 | return null;
32 |
33 | volume.GetMasterVolume(out float level);
34 | return level * 100;
35 | }
36 |
37 | public static bool? GetApplicationMute(string name)
38 | {
39 | ISimpleAudioVolume volume = GetVolumeObject(name);
40 | if (volume == null)
41 | return null;
42 |
43 | volume.GetMute(out bool mute);
44 | return mute;
45 | }
46 |
47 | public static void SetApplicationVolume(string name, float level)
48 | {
49 | ISimpleAudioVolume volume = GetVolumeObject(name);
50 | if (volume == null)
51 | return;
52 |
53 | Guid guid = Guid.Empty;
54 | volume.SetMasterVolume(level / 100, ref guid);
55 | }
56 |
57 | internal static void ToggleApplicationMute(string name)
58 | {
59 |
60 | var muted = GetApplicationMute(name);
61 |
62 | if (muted == null)
63 | return;
64 |
65 | SetApplicationMute(name, !(bool)muted);
66 | }
67 |
68 |
69 | public static void SetApplicationMute(string name, bool mute)
70 | {
71 | ISimpleAudioVolume volume = GetVolumeObject(name);
72 | if (volume == null)
73 | return;
74 |
75 | Guid guid = Guid.Empty;
76 | volume.SetMute(mute, ref guid);
77 | }
78 |
79 | public static IEnumerable EnumerateApplications()
80 | {
81 | // get the speakers (1st render + multimedia) device
82 | IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
83 | deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice speakers);
84 |
85 | // activate the session manager. we need the enumerator
86 | Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
87 | speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out object o);
88 | IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
89 |
90 | // enumerate sessions for on this device
91 | mgr.GetSessionEnumerator(out IAudioSessionEnumerator sessionEnumerator);
92 | sessionEnumerator.GetCount(out int count);
93 |
94 | for (int i = 0; i < count; i++)
95 | {
96 | IAudioSessionControl2 ctl2;
97 |
98 | sessionEnumerator.GetSession(i, out IAudioSessionControl ctl);
99 |
100 | ctl2 = ctl as IAudioSessionControl2;
101 |
102 | if (ctl2 != null)
103 | {
104 |
105 | ctl2.GetSessionIdentifier(out string sout1);
106 | ctl2.GetProcessId(out uint pid);
107 | ctl2.GetSessionInstanceIdentifier(out string sout2);
108 |
109 | }
110 |
111 | ctl.GetDisplayName(out string dn);
112 | yield return dn;
113 | Marshal.ReleaseComObject(ctl);
114 | }
115 | Marshal.ReleaseComObject(sessionEnumerator);
116 | Marshal.ReleaseComObject(mgr);
117 | Marshal.ReleaseComObject(speakers);
118 | Marshal.ReleaseComObject(deviceEnumerator);
119 | }
120 |
121 | private static ISimpleAudioVolume GetVolumeObject(string name)
122 | {
123 | // get the speakers (1st render + multimedia) device
124 | IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
125 | deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice speakers);
126 |
127 | // activate the session manager. we need the enumerator
128 | Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
129 | speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out object o);
130 | IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
131 |
132 | // enumerate sessions for on this device
133 | mgr.GetSessionEnumerator(out IAudioSessionEnumerator sessionEnumerator);
134 | sessionEnumerator.GetCount(out int count);
135 |
136 | // lower case name for easier comparison with the Session ID later on
137 | name = name.ToLower();
138 |
139 | // search for an audio session with the required name
140 | // Note: Since GetDisplayName only returns a real name if the application bothered to call SetDisplayName
141 | // (which apps like Spotify do not), we instead use the SessionID (which usually includes the exe name)
142 | ISimpleAudioVolume volumeControl = null;
143 |
144 | for (int i = 0; i < count; i++)
145 | {
146 |
147 | IAudioSessionControl2 ctl2;
148 |
149 | sessionEnumerator.GetSession(i, out IAudioSessionControl ctl);
150 |
151 | ctl2 = ctl as IAudioSessionControl2;
152 |
153 | ctl.GetDisplayName(out string dn);
154 |
155 | if (ctl2 != null)
156 | {
157 |
158 | ctl2.GetSessionIdentifier(out string sessionID);
159 |
160 | if (sessionID.ToLower().Contains(name))
161 | {
162 | volumeControl = ctl as ISimpleAudioVolume;
163 | break;
164 | }
165 | }
166 |
167 | if (ctl != null)
168 | Marshal.ReleaseComObject(ctl);
169 |
170 | if (ctl2 != null)
171 | Marshal.ReleaseComObject(ctl2);
172 | }
173 |
174 | Marshal.ReleaseComObject(sessionEnumerator);
175 | Marshal.ReleaseComObject(mgr);
176 | Marshal.ReleaseComObject(speakers);
177 | Marshal.ReleaseComObject(deviceEnumerator);
178 |
179 | return volumeControl;
180 | }
181 |
182 | //
183 | // Should probably be in the central WinHelper (or a NativeMethods) but it seemed cleaner to
184 | // keep it all together.
185 | //
186 |
187 | [ComImport]
188 | [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
189 | internal class MMDeviceEnumerator
190 | {
191 | }
192 |
193 | internal enum EDataFlow
194 | {
195 | eRender,
196 | eCapture,
197 | eAll,
198 | EDataFlow_enum_count
199 | }
200 |
201 | internal enum ERole
202 | {
203 | eConsole,
204 | eMultimedia,
205 | eCommunications,
206 | ERole_enum_count
207 | }
208 |
209 | [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
210 | internal interface IMMDeviceEnumerator
211 | {
212 | int NotImpl1();
213 |
214 | [PreserveSig]
215 | int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
216 |
217 | // the rest is not implemented
218 | }
219 |
220 | [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
221 | internal interface IMMDevice
222 | {
223 | [PreserveSig]
224 | int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
225 |
226 | // the rest is not implemented
227 | }
228 |
229 | [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
230 | internal interface IAudioSessionManager2
231 | {
232 | int NotImpl1();
233 | int NotImpl2();
234 |
235 | [PreserveSig]
236 | int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);
237 |
238 | // the rest is not implemented
239 | }
240 |
241 | [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
242 | internal interface IAudioSessionEnumerator
243 | {
244 | [PreserveSig]
245 | int GetCount(out int SessionCount);
246 |
247 | [PreserveSig]
248 | int GetSession(int SessionCount, out IAudioSessionControl Session);
249 | }
250 |
251 | [Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
252 | internal interface IAudioSessionControl
253 | {
254 | int NotImpl1();
255 |
256 | [PreserveSig]
257 | int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
258 |
259 | // the rest is not implemented
260 | }
261 |
262 | public enum AudioSessionState
263 | {
264 | AudioSessionStateInactive = 0,
265 | AudioSessionStateActive = 1,
266 | AudioSessionStateExpired = 2
267 | }
268 |
269 | public enum AudioSessionDisconnectReason
270 | {
271 | DisconnectReasonDeviceRemoval = 0,
272 | DisconnectReasonServerShutdown = (DisconnectReasonDeviceRemoval + 1),
273 | DisconnectReasonFormatChanged = (DisconnectReasonServerShutdown + 1),
274 | DisconnectReasonSessionLogoff = (DisconnectReasonFormatChanged + 1),
275 | DisconnectReasonSessionDisconnected = (DisconnectReasonSessionLogoff + 1),
276 | DisconnectReasonExclusiveModeOverride = (DisconnectReasonSessionDisconnected + 1)
277 | }
278 |
279 | [Guid("24918ACC-64B3-37C1-8CA9-74A66E9957A8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
280 | public interface IAudioSessionEvents
281 | {
282 | [PreserveSig]
283 | int OnDisplayNameChanged([MarshalAs(UnmanagedType.LPWStr)] string NewDisplayName, Guid EventContext);
284 | [PreserveSig]
285 | int OnIconPathChanged([MarshalAs(UnmanagedType.LPWStr)] string NewIconPath, Guid EventContext);
286 | [PreserveSig]
287 | int OnSimpleVolumeChanged(float NewVolume, bool newMute, Guid EventContext);
288 | [PreserveSig]
289 | int OnChannelVolumeChanged(UInt32 ChannelCount, IntPtr NewChannelVolumeArray, UInt32 ChangedChannel, Guid EventContext);
290 | [PreserveSig]
291 | int OnGroupingParamChanged(Guid NewGroupingParam, Guid EventContext);
292 | [PreserveSig]
293 | int OnStateChanged(AudioSessionState NewState);
294 | [PreserveSig]
295 | int OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason);
296 | }
297 |
298 | [Guid("BFB7FF88-7239-4FC9-8FA2-07C950BE9C6D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
299 | public interface IAudioSessionControl2
300 | {
301 | [PreserveSig]
302 | int GetState(out AudioSessionState state);
303 | [PreserveSig]
304 | int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] out string name);
305 | [PreserveSig]
306 | int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string value, Guid EventContext);
307 | [PreserveSig]
308 | int GetIconPath([Out(), MarshalAs(UnmanagedType.LPWStr)] out string Path);
309 | [PreserveSig]
310 | int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, Guid EventContext);
311 | [PreserveSig]
312 | int GetGroupingParam(out Guid GroupingParam);
313 | [PreserveSig]
314 | int SetGroupingParam(Guid Override, Guid Eventcontext);
315 | [PreserveSig]
316 | int RegisterAudioSessionNotification(IAudioSessionEvents NewNotifications);
317 | [PreserveSig]
318 | int UnregisterAudioSessionNotification(IAudioSessionEvents NewNotifications);
319 | [PreserveSig]
320 | int GetSessionIdentifier([Out(), MarshalAs(UnmanagedType.LPWStr)] out string retVal);
321 | [PreserveSig]
322 | int GetSessionInstanceIdentifier([Out(), MarshalAs(UnmanagedType.LPWStr)] out string retVal);
323 | [PreserveSig]
324 | int GetProcessId(out UInt32 retvVal);
325 | [PreserveSig]
326 | int IsSystemSoundsSession();
327 | [PreserveSig]
328 | int SetDuckingPreference(bool optOut);
329 | }
330 |
331 |
332 | [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
333 | internal interface ISimpleAudioVolume
334 | {
335 | [PreserveSig]
336 | int SetMasterVolume(float fLevel, ref Guid EventContext);
337 |
338 | [PreserveSig]
339 | int GetMasterVolume(out float pfLevel);
340 |
341 | [PreserveSig]
342 | int SetMute(bool bMute, ref Guid EventContext);
343 |
344 | [PreserveSig]
345 | int GetMute(out bool pbMute);
346 | }
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/AutoHotkey.Interop/AutoHotkeyDll.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace AutoHotkey.Interop
5 | {
6 | ///
7 | /// These functions serve as a flat wrapper for AutoHotkey.dll.
8 | /// They assume AutoHotkey.dll is in the same directory as your
9 | /// executable.
10 | ///
11 | internal class AutoHotkeyDll
12 | {
13 | private const string DLLPATH = "AutoHotkey.dll";
14 |
15 | #region Create Thread
16 |
17 | ///
18 | /// Start new thread from ahk file.
19 | ///
20 | /// This parameter must be a path to existing ahk file.
21 | /// Additional parameter passed to AutoHotkey.dll (not available in Version 2 alpha).
22 | /// Parameters passed to dll.
23 | /// ahkdll returns a thread handle.
24 | /// ahktextdll is available in AutoHotkey[Mini].dll only, not in AutoHotkey.exe.
25 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
26 | public static extern uint ahkdll(
27 | [MarshalAs(UnmanagedType.LPWStr)] string Path,
28 | [MarshalAs(UnmanagedType.LPWStr)] string Options,
29 | [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
30 |
31 | ///
32 | /// ahktextdll is used to launch a script in a separate thread from text/variable.
33 | ///
34 | /// This parameter must be a string with ahk script.
35 | /// Additional parameter passed to AutoHotkey.dll (not available in Version 2 alpha).
36 | /// Parameters passed to dll.
37 | /// ahkdll returns a thread handle.
38 | /// ahktextdll is available in AutoHotkey[Mini].dll only, not in AutoHotkey.exe.
39 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
40 | public static extern uint ahktextdll(
41 | [MarshalAs(UnmanagedType.LPWStr)] string Code,
42 | [MarshalAs(UnmanagedType.LPWStr)] string Options,
43 | [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
44 |
45 | #endregion
46 |
47 | #region Determine Thread's State
48 |
49 | ///
50 | /// ahkReady is used to check if a dll script is running or not.
51 | ///
52 | /// 1 if a thread is running or 0 otherwise.
53 | /// Available in AutoHotkey[Mini].dll only, not in AutoHotkey.exe.
54 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
55 | public static extern bool ahkReady();
56 |
57 | #endregion
58 |
59 | #region Control Thread
60 |
61 | ///
62 | /// ahkTerminate is used to stop and exit a running script.
63 | ///
64 | /// Time in milliseconds to wait until thread exits.
65 | /// Returns always 0.
66 | /// Available in AutoHotkey[Mini].dll only, not in AutoHotkey.exe.
67 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
68 | public static extern void ahkTerminate(uint timeout);
69 |
70 | ///
71 | /// ahkReload is used to terminate and start a running script again.
72 | ///
73 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
74 | public static extern void ahkReload();
75 |
76 | ///
77 | /// ahkPause will pause/un-pause a thread and run traditional AutoHotkey Sleep internally.
78 | ///
79 | /// Should be "On" or "Off" as a string
80 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
81 | public static extern void ahkPause(
82 | [MarshalAs(UnmanagedType.LPWStr)] string strState);
83 |
84 | #endregion
85 |
86 | #region Add New Code
87 |
88 | ///
89 | /// addFile includes additional script from a file to the running script.
90 | ///
91 | /// Path to a file that will be added to a running script.
92 | /// Allow duplicate includes.
93 | /// Ignore if loading a file failed.
94 | /// addFile returns a pointer to the first line of new created code.
95 | /// pointerLine can be used in ahkExecuteLine to execute one line only or until a return is encountered.
96 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
97 | public static extern uint addFile(
98 | [MarshalAs(UnmanagedType.LPWStr)]string FilePath,
99 | byte AllowDuplicateInclude,
100 | byte IgnoreLoadFailure);
101 |
102 | // Constant values for the execute parameter of addScript
103 | public struct Execute
104 | {
105 | public const byte Add = 0, Run = 1, RunWait = 2;
106 | }
107 |
108 | ///
109 | /// addScript includes additional script from a string to the running script.
110 | ///
111 | /// cript that will be added to a running script.
112 | /// Determines whether the added script should be executed.
113 | /// addScript returns a pointer to the first line of new created code.
114 | /// pointerLine can be used in ahkExecuteLine to execute one line only or until a return is encountered.
115 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
116 | public static extern uint addScript(
117 | [MarshalAs(UnmanagedType.LPWStr)]string code,
118 | byte execute);
119 |
120 | #endregion
121 |
122 | #region Execute Some Code
123 |
124 | ///
125 | /// Execute a script from a string that contains ahk script.
126 | ///
127 | /// Script as string/text or variable containing script that will be executed.
128 | /// Returns true if script was executed and false if there was an error.
129 | /// ahkExec will execute the code and delete it before it returns.
130 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
131 | public static extern bool ahkExec(
132 | [MarshalAs(UnmanagedType.LPWStr)] string code);
133 |
134 | //TODO: ahkExecuteLine
135 |
136 | ///
137 | /// ahkLabel is used to launch a Goto/GoSub routine in script.
138 | ///
139 | /// Name of label to execute.
140 | /// Do not to wait until execution finished.
141 | /// 1 if label exists 0 otherwise.
142 | /// Default is 0 = wait for code to finish execution.
143 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
144 | public static extern bool ahkLabel(
145 | [MarshalAs(UnmanagedType.LPWStr)] string labelName,
146 | bool noWait);
147 |
148 |
149 | ///
150 | /// ahkFunction is used to launch a function in script.
151 | ///
152 | /// Name of function to call.
153 | /// The 1st parameter, or null
154 | /// The 2nd parameter, or null
155 | /// The 3rd parameter, or null
156 | /// The 4th parameter, or null
157 | /// The 5th parameter, or null
158 | /// The 6th parameter, or null
159 | /// The 7th parameter, or null
160 | /// The 8th parameter, or null
161 | /// The 9th parameter, or null
162 | /// The 10th parameter, or null
163 | /// Return value is always a string/text, add 0 to make sure it resolves to digit if necessary.
164 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
165 | public static extern IntPtr ahkFunction(
166 | [MarshalAs(UnmanagedType.LPWStr)] string functionName,
167 | [MarshalAs(UnmanagedType.LPWStr)] string parameter1,
168 | [MarshalAs(UnmanagedType.LPWStr)] string parameter2,
169 | [MarshalAs(UnmanagedType.LPWStr)] string parameter3,
170 | [MarshalAs(UnmanagedType.LPWStr)] string parameter4,
171 | [MarshalAs(UnmanagedType.LPWStr)] string parameter5,
172 | [MarshalAs(UnmanagedType.LPWStr)] string parameter6,
173 | [MarshalAs(UnmanagedType.LPWStr)] string parameter7,
174 | [MarshalAs(UnmanagedType.LPWStr)] string parameter8,
175 | [MarshalAs(UnmanagedType.LPWStr)] string parameter9,
176 | [MarshalAs(UnmanagedType.LPWStr)] string parameter10);
177 |
178 |
179 | ///
180 | /// ahkFunction is used to launch a function in script.
181 | ///
182 | /// Name of function to call.
183 | /// Parameters to pass to function.
184 | /// 0 if function exists else -1.
185 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
186 | public static extern bool ahkPostFunction(
187 | [MarshalAs(UnmanagedType.LPWStr)] string functionName,
188 | [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
189 |
190 | #endregion
191 |
192 | #region Working With Variables
193 |
194 | ///
195 | /// ahkassign is used to assign a string to a variable in script.
196 | ///
197 | /// Name of a variable.
198 | /// Value to assign to variable.
199 | /// Returns value is 0 on success and -1 on failure.
200 | /// ahkassign will create the variable if it does not exist.
201 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
202 | public static extern bool ahkassign(
203 | [MarshalAs(UnmanagedType.LPWStr)] string VariableName,
204 | [MarshalAs(UnmanagedType.LPWStr)] string NewValue);
205 |
206 | ///
207 | /// ahkgetvar is used to get a value from a variable in script.
208 | ///
209 | /// Name of variable to get value from.
210 | /// Get value or pointer.
211 | /// Returned value is always a string, add 0 to convert to integer if necessary, especially when using getPointer.
212 | /// ahkgetvar returns empty string if variable does not exist or is empty.
213 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
214 | public static extern IntPtr ahkgetvar(
215 | [MarshalAs(UnmanagedType.LPWStr)] string VariableName,
216 | [MarshalAs(UnmanagedType.I4)] int GetPointer);
217 |
218 | #endregion
219 |
220 | #region Advanced
221 |
222 | ///
223 | /// ahkFundFunc is used to get function its pointer
224 | ///
225 | /// Name of function to call.
226 | /// Function pointer.
227 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
228 | public static extern IntPtr ahkFindFunc(
229 | [MarshalAs(UnmanagedType.LPWStr)] string FuncName);
230 |
231 | ///
232 | /// ahkFindLabel is used to get a pointer to the label.
233 | ///
234 | /// Name of label.
235 | /// ahkFindLabel returns a pointer to a line where label points to.
236 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
237 | public static extern IntPtr ahkFindLabel(
238 | [MarshalAs(UnmanagedType.LPWStr)] string LabelName);
239 |
240 | ///
241 | /// Build in function to get a pointer to the structure of a user-defined variable.
242 | ///
243 | /// the name of the variable
244 | /// The pointer to the variable.
245 | [DllImport(DLLPATH, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "getVar")]
246 | public static extern IntPtr GetVar(
247 | [MarshalAs(UnmanagedType.LPWStr)] string Variable);
248 |
249 | #endregion
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/Toastify/Spotify/Spotify.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 | using System.Threading;
5 | using System.IO;
6 | using System.Diagnostics;
7 | using SpotifyAPI.Web;
8 | using System.Threading.Tasks;
9 | using System.Text.RegularExpressions;
10 |
11 | namespace Toastify
12 | {
13 | internal static class Spotify
14 | {
15 | ///
16 | /// The number of seconds for which the last GetSpotify() result is immediately returned
17 | ///
18 | private const int _GET_SPOTIFY_RETURN_LAST_SEC = 5;
19 |
20 | private static AutoHotkey.Interop.AutoHotkeyEngine _ahk;
21 |
22 | private static DateTime _lastGetSpotifyCall = DateTime.MinValue;
23 | private static int _cachedProcId;
24 | private static IntPtr _cachedHWnd;
25 |
26 | public static void StartSpotify()
27 | {
28 | if (IsRunning())
29 | return;
30 |
31 | // spotify installs a protocol handler, "spotify:", use that to launch spotify
32 |
33 | Process.Start("spotify:");
34 |
35 | if (SettingsXml.Current.MinimizeSpotifyOnStartup)
36 | {
37 | Minimize();
38 | }
39 | else
40 | {
41 |
42 | // we need to let Spotify start up before interacting with it fully. 2 seconds is a relatively
43 | // safe amount of time to wait, even if the pattern is gross. (Minimize() doesn't need it since
44 | // it waits for the Window to appear before minimizing)
45 | var remainingSleep = 2000;
46 |
47 | while (Spotify.GetSpotify() == IntPtr.Zero && remainingSleep > 0)
48 | {
49 | Thread.Sleep(100);
50 | remainingSleep -= 100;
51 | }
52 | }
53 | }
54 |
55 | private static void Minimize()
56 | {
57 | var remainingSleep = 2000;
58 |
59 | IntPtr hWnd;
60 |
61 | // Since Minimize is often called during startup, the hWnd is often not created yet
62 | // wait a maximum of remainingSleep for it to appear and then minimize it if it did.
63 | while ((hWnd = Spotify.GetSpotify()) == IntPtr.Zero && remainingSleep > 0)
64 | {
65 | Thread.Sleep(100);
66 | remainingSleep -= 100;
67 | }
68 |
69 | if (hWnd != IntPtr.Zero)
70 | {
71 | // disgusting but sadly neccessary. Let Spotify initialize a bit before minimizing it
72 | // otherwise the window hides itself and doesn't respond to taskbar clicks.
73 | // I tried to work around this by waiting for the window size to initialize (via GetWindowRect)
74 | // but that didn't work, there is some internal initialization that needs to occur.
75 | Thread.Sleep(500);
76 | Win32.ShowWindow(hWnd, Win32.Constants.SW_SHOWMINIMIZED);
77 | }
78 | }
79 |
80 | private static void KillProc(string name)
81 | {
82 | // let's play nice and try to gracefully clear out all Sync processes
83 | var procs = System.Diagnostics.Process.GetProcessesByName(name);
84 |
85 | foreach (var proc in procs)
86 | {
87 | // lParam == Band Process Id, passed in below
88 | Win32.EnumWindows(delegate (IntPtr hWnd, IntPtr lParam)
89 | {
90 | Win32.GetWindowThreadProcessId(hWnd, out uint processId);
91 |
92 | // Essentially: Find every hWnd associated with this process and ask it to go away
93 | if (processId == (uint)lParam)
94 | {
95 | Win32.SendMessage(hWnd, Win32.Constants.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
96 | Win32.SendMessage(hWnd, Win32.Constants.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
97 | }
98 |
99 | return true;
100 | },
101 | (IntPtr)proc.Id);
102 | }
103 |
104 | // let everything calm down
105 | Thread.Sleep(1000);
106 |
107 | procs = System.Diagnostics.Process.GetProcessesByName(name);
108 |
109 | // ok, no more mister nice guy. Sadly.
110 | foreach (var proc in procs)
111 | {
112 | try
113 | {
114 | proc.Kill();
115 | }
116 | catch { } // ignore exceptions (usually due to trying to kill non-existant child processes
117 | }
118 | }
119 |
120 | public static void KillSpotify()
121 | {
122 | KillProc("spotify");
123 | }
124 |
125 | ///
126 | /// Find a running instance of Spotify
127 | ///
128 | /// HWND of the Spotify window
129 | private static IntPtr GetSpotify()
130 | {
131 | if (DateTime.Now.Subtract(_lastGetSpotifyCall).TotalSeconds < _GET_SPOTIFY_RETURN_LAST_SEC)
132 | return _cachedHWnd;
133 |
134 | _lastGetSpotifyCall = DateTime.Now;
135 | var rv = IntPtr.Zero;
136 |
137 | // Spotify have made things a little harder with their use of electron
138 | // In order to not pick up on other Electron windows, first find the process and then the window
139 | var procs = Process.GetProcessesByName("spotify");
140 |
141 | if (Array.Exists(procs, proc => proc.Id == _cachedProcId))
142 | return _cachedHWnd;
143 |
144 | foreach (var proc in procs)
145 | {
146 | foreach (ProcessThread thread in proc.Threads)
147 | {
148 | Win32.EnumThreadWindows(thread.Id, (hWnd, lParam) =>
149 | {
150 | var sb = new StringBuilder(256);
151 |
152 | // get the class name to check if it's of type Chome_WidgetWin_0
153 | var ret = Win32.GetClassName(hWnd, sb, sb.Capacity);
154 |
155 | if (ret != 0)
156 | {
157 | if (sb.ToString() == "Chrome_WidgetWin_0")
158 | {
159 |
160 | // now check to make sure that it has a title (Spotify has a couple of windows
161 | // that it uses for specific controls, we don't want those
162 | ret = Win32.GetWindowText(hWnd, sb, sb.Capacity);
163 | if (ret != 0)
164 | {
165 | if (!string.IsNullOrWhiteSpace(sb.ToString()))
166 | {
167 | rv = hWnd;
168 |
169 | _cachedProcId = proc.Id;
170 | _cachedHWnd = hWnd;
171 |
172 | // stop the enumeration immediately
173 | return false;
174 | }
175 | }
176 | }
177 | }
178 |
179 | return true;
180 | }, IntPtr.Zero);
181 |
182 | if (rv != IntPtr.Zero)
183 | {
184 | return rv;
185 | }
186 | }
187 | }
188 |
189 | // couldn't find the window
190 | return rv;
191 | }
192 |
193 | public static bool IsRunning()
194 | {
195 | return (GetSpotify() != IntPtr.Zero);
196 | }
197 |
198 | public static Song GetCurrentSong()
199 | {
200 | if (!Spotify.IsRunning())
201 | return null;
202 |
203 | IntPtr hWnd = GetSpotify();
204 | int length = Win32.GetWindowTextLength(hWnd);
205 | StringBuilder sb = new StringBuilder(length + 1);
206 | Win32.GetWindowText(hWnd, sb, sb.Capacity);
207 |
208 | string title = sb.ToString();
209 |
210 | if (!string.IsNullOrWhiteSpace(title) && title != "Spotify")
211 | {
212 | // Unfortunately we don't have a great way to get the title from Spotify
213 | // so we need to do some gymnastics.
214 | // Music played from an artist's page is usually in the format "artist - song"
215 | // while music played from a playlist is often in the format "artist - song - album"
216 | // unfortunately this means that some songs that actually have a " - " in either the artist name
217 | // or in the song name will potentially display incorrectly
218 | var portions = title.Split(new string[] { " - " }, StringSplitOptions.None);
219 |
220 | var song = (portions.Length > 1 ? portions[1] : null);
221 | var artist = portions[0];
222 | var album = (portions.Length > 2 ? string.Join(" ", portions.Skip(2).ToArray()) : null); // take everything else that's left
223 |
224 | return new Song(artist, song, album);
225 | }
226 |
227 | return null;
228 | }
229 |
230 | private static async Task GetCoverArt(string artist, string track)
231 | {
232 | string imageUrl = null;
233 |
234 | var spotifyWeb = await SpotifyApiClient.GetAsync();
235 |
236 | var searchRequest = new SearchRequest(SearchRequest.Types.Track, $"{track} artist:{artist}");
237 | var searchResponse = await spotifyWeb.Search.Item(searchRequest);
238 |
239 | if (searchResponse.Tracks.Items.Count > 0)
240 | {
241 | var images = searchResponse.Tracks.Items[0].Album.Images;
242 |
243 | // iterate through all of the images, finding the smallest ones. This is usually the last
244 | // one, but there is no guarantee in the docs.
245 | var smallestWidth = int.MaxValue;
246 |
247 | foreach (var image in images)
248 | {
249 | if (image.Width < smallestWidth)
250 | {
251 | imageUrl = image.Url;
252 | }
253 | }
254 | }
255 |
256 | return imageUrl;
257 | }
258 |
259 | public static async Task SetCoverArt(Song song)
260 | {
261 | // probably an ad, don't bother looking for an image
262 | if (string.IsNullOrWhiteSpace(song.Track) || string.IsNullOrWhiteSpace(song.Artist))
263 | return;
264 |
265 | // remove characters known to intefere with searching
266 | var reRemoveChars = new Regex("[/()\"]");
267 |
268 | var artist = reRemoveChars.Replace(song.Artist, "");
269 | var track = reRemoveChars.Replace(song.Track, "");
270 |
271 | var imageUrl = await GetCoverArt(artist, track);
272 |
273 | // sometimes the words in brackets of a song name throw the search off, so if we couldn't find
274 | // any songs, try removing the extra words completely
275 | if (string.IsNullOrWhiteSpace(imageUrl) && (song.Artist.Contains("(") || song.Track.Contains("(")))
276 | {
277 | var reRemoveBrackets = new Regex(@"\(.*\)");
278 |
279 | artist = reRemoveChars.Replace(reRemoveBrackets.Replace(song.Artist, ""), "");
280 | track = reRemoveChars.Replace(reRemoveBrackets.Replace(song.Track, ""), "");
281 |
282 | imageUrl = await GetCoverArt(artist, track);
283 |
284 | }
285 |
286 | if (imageUrl != null)
287 | {
288 | song.CoverArtUrl = imageUrl;
289 | }
290 |
291 | }
292 |
293 | private static bool IsMinimized()
294 | {
295 | if (!Spotify.IsRunning())
296 | return false;
297 |
298 | var hWnd = Spotify.GetSpotify();
299 |
300 | // check Spotify's current window state
301 | var placement = new Win32.WINDOWPLACEMENT();
302 | Win32.GetWindowPlacement(hWnd, ref placement);
303 |
304 | return (placement.showCmd == Win32.Constants.SW_SHOWMINIMIZED);
305 | }
306 |
307 | /*
308 | *TODO: Establish is this is needed. Could be useful to bring Spotify to the front without changing focus?
309 | private static void ShowSpotifyWithNoActivate()
310 | {
311 | var hWnd = Spotify.GetSpotify();
312 |
313 | // check Spotify's current window state
314 | var placement = new Win32.WINDOWPLACEMENT();
315 | Win32.GetWindowPlacement(hWnd, ref placement);
316 |
317 | var flags = Win32.SetWindowPosFlags.DoNotActivate | Win32.SetWindowPosFlags.DoNotChangeOwnerZOrder | Win32.SetWindowPosFlags.ShowWindow;
318 |
319 | Win32.SetWindowPos(hWnd, (IntPtr)0, placement.rcNormalPosition.Left, placement.rcNormalPosition.Top, 0, 0, flags);
320 | }
321 | */
322 |
323 | private static void ShowSpotify()
324 | {
325 | if (Spotify.IsRunning())
326 | {
327 | var hWnd = Spotify.GetSpotify();
328 |
329 | // check Spotify's current window state
330 | var placement = new Win32.WINDOWPLACEMENT();
331 | Win32.GetWindowPlacement(hWnd, ref placement);
332 |
333 | int showCommand = Win32.Constants.SW_SHOW;
334 |
335 | // if Spotify is minimzed we need to send a restore so that the window
336 | // will come back exactly like it was before being minimized (i.e. maximized
337 | // or otherwise) otherwise if we call SW_RESTORE on a currently maximized window
338 | // then instead of staying maximized it will return to normal size.
339 | if (placement.showCmd == Win32.Constants.SW_SHOWMINIMIZED)
340 | {
341 | showCommand = Win32.Constants.SW_RESTORE;
342 | }
343 |
344 | Win32.ShowWindow(hWnd, showCommand);
345 |
346 | Win32.SetForegroundWindow(hWnd);
347 | Win32.SetFocus(hWnd);
348 | }
349 | }
350 |
351 | public static void SendAction(SpotifyAction a)
352 | {
353 | if (!Spotify.IsRunning())
354 | return;
355 |
356 | // bah. Because control cannot fall through cases we need to special case volume
357 | if (SettingsXml.Current.ChangeSpotifyVolumeOnly)
358 | {
359 | if (a == SpotifyAction.VolumeUp)
360 | {
361 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.VolumeUp);
362 |
363 | VolumeHelper.IncrementVolume("Spotify");
364 | return;
365 | }
366 | else if (a == SpotifyAction.VolumeDown)
367 | {
368 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.VolumeDown);
369 |
370 | VolumeHelper.DecrementVolume("Spotify");
371 | return;
372 | }
373 | else if (a == SpotifyAction.Mute)
374 | {
375 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.Mute);
376 |
377 | VolumeHelper.ToggleApplicationMute("Spotify");
378 | return;
379 | }
380 | }
381 |
382 | switch (a)
383 | {
384 | case SpotifyAction.CopyTrackInfo:
385 | case SpotifyAction.ShowToast:
386 | //Nothing
387 | break;
388 | case SpotifyAction.ShowSpotify:
389 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.ShowSpotify);
390 |
391 |
392 | if (Spotify.IsMinimized())
393 | {
394 | ShowSpotify();
395 | }
396 | else
397 | {
398 | Minimize();
399 | }
400 |
401 | break;
402 | case SpotifyAction.FastForward:
403 |
404 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.FastForward);
405 |
406 | SendComplexKeys("+{Right}");
407 | break;
408 |
409 | case SpotifyAction.Rewind:
410 |
411 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.Rewind);
412 |
413 | SendComplexKeys("+{Left}");
414 | break;
415 |
416 | default:
417 |
418 | Telemetry.TrackEvent(TelemetryCategory.Action, Telemetry.TelemetryEvent.Action.Default + a.ToString());
419 |
420 | Win32.SendMessage(GetSpotify(), Win32.Constants.WM_APPCOMMAND, IntPtr.Zero, new IntPtr((long)a));
421 | break;
422 | }
423 | }
424 |
425 | ///
426 | /// Some commands require sending keys directly to Spotify (for example, Fast Forward and Rewind which
427 | /// are not handled by Spotify). We can't inject keys directly with WM_KEYDOWN/UP since we need a keyboard
428 | /// hook to actually change the state of various modifier keys (for example, Shift + Right for Fast Forward).
429 | ///
430 | /// AutoHotKey has that hook and can modify the state for us, so let's take advantge of it.
431 | ///
432 | ///
433 | private static void SendComplexKeys(string keys)
434 | {
435 | // Is this nicer?
436 | // _ahk = _ahk ?? new AutoHotkey.Interop.AutoHotkeyEngine();
437 |
438 | // only initialize AHK when needed as it can be expensive (dll copy etc) if not actually needed
439 | if (_ahk == null)
440 | {
441 | _ahk = new AutoHotkey.Interop.AutoHotkeyEngine();
442 | }
443 |
444 | _ahk.ExecRaw("SetTitleMatchMode 2");
445 |
446 | _ahk.ExecRaw("DetectHiddenWindows, On");
447 | _ahk.ExecRaw("ControlSend, ahk_parent, " + keys + ", ahk_class SpotifyMainWindow");
448 |
449 | _ahk.ExecRaw("DetectHiddenWindows, Off");
450 | }
451 | }
452 | }
453 |
--------------------------------------------------------------------------------
/Toastify/Settings.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Launch Toastify with Windows
21 | Minimize Spotify on launch
22 | Close Spotify when you close Toastify
23 | When changing volume, change volume only for Spotify
24 |
25 |
26 |
27 | This is the text that will be copied to the clipboard when you hit the "Copy to Clipboard" hotkey. "{0}" will be replaced with the name of the currently playing song.
28 |
29 |
30 |
31 |
32 |
33 | Do not collect anonymous usage data
34 |
35 | Data is never shared, is anonymous, and is used to inform future features
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Enable Global Hotkeys
48 |
49 |
83 |
84 |
85 |
86 |
87 | Disable Toast
88 |
89 | Only Show Toast when Hotkey is pressed
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | The best way to tweak the position of the toast is to hold down Control and then drag the toast around. The raw numbers are still accessible below. Note that the Toast will display at the default position (lower corner above the system time) if you position the toast off-screen.
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------