diff --git a/src/Config.cs b/src/Config.cs new file mode 100755 index 0000000..0bb3734 --- /dev/null +++ b/src/Config.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using System.Xml.Serialization; +using static SpotifyMod.KeyData; +using static SpotifyMod.VirtualKeys; + +namespace SpotifyMod +{ + public class Settings + { + public Settings() { } + + public Settings(string spotifyLocation, int volumeIncreaseRate, List keyCommands) + { + SpotifyLocation = spotifyLocation; + VolumeIncreaseRate = volumeIncreaseRate; + KeyCommands = keyCommands; + } + + public string SpotifyLocation { get; set; } + public int VolumeIncreaseRate { get; set; } + public List KeyCommands { get; set; } + + + public static readonly Settings Default = new Settings( + @$"C:\Users\{Environment.UserName}\AppData\Roaming\Spotify\Spotify.exe", + 10, + new List { + new KeyCommand(CommandType.OpenSpotify, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 82, (int)VKeys.NUMPAD0) }), + new KeyCommand(CommandType.PreviousSong, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 71, (int)VKeys.NUMPAD7) }), + new KeyCommand(CommandType.NextSong, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 73, (int)VKeys.NUMPAD9) }), + new KeyCommand(CommandType.Rewind, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 75, (int)VKeys.NUMPAD4) }), + new KeyCommand(CommandType.FastForward, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 77, (int)VKeys.NUMPAD6) }), + new KeyCommand(CommandType.PlayPause, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 76, (int)VKeys.NUMPAD5) }), + new KeyCommand(CommandType.VolumeDown, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 79, (int)VKeys.NUMPAD1) }), + new KeyCommand(CommandType.VolumeUp, new List { new KeyData("[Default]", (int)RawKeyboardFlags.None, 81, (int)VKeys.NUMPAD3) }) + }); + + public static Settings Current = Default; + } + + class Config + { + public static readonly string ConfigDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs"); + public static readonly string AppConfigDir = Path.Combine(ConfigDir, "Application"); + public static readonly string ModConfigDir = Path.Combine(ConfigDir, "Mods"); + public static readonly string SpotifyModConfigDir = Path.Combine(ModConfigDir, "SpotifyMod"); + public static readonly string SpotifyModDefaultConfig = Path.Combine(SpotifyModConfigDir, "SpotifyMod.cfg"); + + public static bool Exists => File.Exists(SpotifyModDefaultConfig); + + public static void Load() + { + + if (Exists) + { + Settings.Current = ReadConfig(); + } + else + { + Directory.CreateDirectory(SpotifyModConfigDir); + SaveConfig(Settings.Current); + } + + UpdateKeyCombinations(); + } + + public static void UpdateKeyCombinations() + { + for (int i = 0; i < Settings.Current.KeyCommands.Count; i++) + { + + ModKeyCombination.ModKeyCombinations.Add(new ModKeyCombination(Settings.Current.KeyCommands[i].Keys, ModMethods.MethodDict[Settings.Current.KeyCommands[i].CommandType])); + for (int j = 0; j < Settings.Current.KeyCommands[i].Keys.Count; j++) + { + KeyData d = Settings.Current.KeyCommands[i].Keys[j]; + Mod.Tunnel.WriteDebugLine(d.Flags + " : " + d.KeyboardAlias + " : " + d.ScanCode + " : " + d.VirtualKey + " : " + Settings.Current.KeyCommands[i].CommandType); + } + } + } + + public static Settings ReadConfig() + { + XmlSerializer xs = new XmlSerializer(typeof(Settings)); + + try + { + + using (TextReader tw = new StreamReader(SpotifyModDefaultConfig)) + { + return (Settings)xs.Deserialize(tw); + } + } + catch (Exception ex) + { + new Thread(() => + { MessageBox.Show($"Type: {ex.GetType()}\r\nMessage: {ex.Message}\r\n\r\nPlease check the config file for any invalid/misplaced characters and reload the mods before continuing!", + "Spotify Mod - Execution Error @ ReadConfig (ConfigDeserialize)"); }).Start(); + } + return null; + } + + public static void SaveConfig(Settings settings, Stream saveStream) + { + XmlSerializer xs = new XmlSerializer(typeof(Settings)); + + xs.Serialize(saveStream, settings); + } + + public static void SaveConfig(Settings settings) + { + using (var f = File.Create(SpotifyModDefaultConfig)) + { + SaveConfig(settings, f); + } + } + } + + public class KeyCommand + { + public KeyCommand() { } + public KeyCommand(CommandType commandType, List keyData) + { + CommandType = commandType; + Keys = keyData; + } + + public CommandType CommandType; + public List Keys; + } + + [Flags] + public enum RawKeyboardFlags : ushort + { + None = 0, + Up = 1 << 0, + KeyE0 = 1 << 1, + KeyE1 = 1 << 2, + } + + public enum CommandType + { + OpenSpotify, + PreviousSong, + NextSong, + Rewind, + FastForward, + PlayPause, + VolumeDown, + VolumeUp, + } +} diff --git a/src/HandlerCommunications.cs b/src/HandlerCommunications.cs new file mode 100755 index 0000000..7e3a827 --- /dev/null +++ b/src/HandlerCommunications.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SpotifyMod +{ + class HandlerCommunications + { + /// + /// Tells the Mod Handler how keys should be handled. + /// + public enum HandleType + { + /// + /// Have the mod handler (Main Application) handle all of the keys. This means the mod won't recieve "Call" method calls. + /// + HandlerOnly, + + /// + /// Have the mod handle all of the keys. This means the mod handler (Main Application) won't accept any [List<KeyCombination>, delegate] dicts. + /// + ModOnly, + + /// + /// Have both the mod handler (Main Application) and mod handle the keys. This means the mod will recieve "Call" method calls, and can send [List<KeyCombination>, delegate] dicts to the mod handler. + /// + Both + } + } + + + public class ModKeyCombination + { + public ModKeyCombination() { } + public static List ModKeyCombinations = new List(); + + public ModKeyCombination(List keys, Action callbackMethod) + { + Keys = keys; + CallbackMethod = callbackMethod; + + } + + + public List Keys { get; set; } + public Action CallbackMethod { get; set; } + } + + + public class KeyData + { + public KeyData() { } + + public int Flags { get; set; } + public int ScanCode { get; set; } + public int VirtualKey { get; set; } + + public string KeyboardAlias { get; set; } + + public KeyData(string keyboardAlias, int flags, int scanCode, int virtualKey) + { + KeyboardAlias = keyboardAlias; + Flags = flags; + ScanCode = scanCode; + VirtualKey = virtualKey; + } + } +} diff --git a/src/Messages.cs b/src/Messages.cs new file mode 100755 index 0000000..a53c03a --- /dev/null +++ b/src/Messages.cs @@ -0,0 +1,768 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SpotifyMod.Native +{ + /// + /// From http://wiki.winehq.org/List_Of_Windows_Messages + /// + [Flags] + public enum WindowMessage : uint + { + WM_NULL = 0x0, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000a, + WM_SETREDRAW = 0x000b, + WM_SETTEXT = 0x000c, + WM_GETTEXT = 0x000d, + WM_GETTEXTLENGTH = 0x000e, + WM_PAINT = 0x000f, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + WM_SHOWWINDOW = 0x0018, + WM_CTLCOLOR = 0x0019, + WM_WININICHANGE = 0x001a, + WM_DEVMODECHANGE = 0x001b, + WM_ACTIVATEAPP = 0x001c, + WM_FONTCHANGE = 0x001d, + WM_TIMECHANGE = 0x001e, + WM_CANCELMODE = 0x001f, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + WM_SPOOLERSTATUS = 0x002a, + WM_DRAWITEM = 0x002b, + WM_MEASUREITEM = 0x002c, + WM_DELETEITEM = 0x002d, + WM_VKEYTOITEM = 0x002e, + WM_CHARTOITEM = 0x002f, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + WM_GETOBJECT = 0x003d, + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYGLOBALDATA = 0x0049, + WM_COPYDATA = 0x004a, + WM_CANCELJOURNAL = 0x004b, + WM_NOTIFY = 0x004e, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007b, + WM_STYLECHANGING = 0x007c, + WM_STYLECHANGED = 0x007d, + WM_DISPLAYCHANGE = 0x007e, + WM_GETICON = 0x007f, + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_SYNCPAINT = 0x0088, + WM_NCMOUSEMOVE = 0x00a0, + WM_NCLBUTTONDOWN = 0x00a1, + WM_NCLBUTTONUP = 0x00a2, + WM_NCLBUTTONDBLCLK = 0x00a3, + WM_NCRBUTTONDOWN = 0x00a4, + WM_NCRBUTTONUP = 0x00a5, + WM_NCRBUTTONDBLCLK = 0x00a6, + WM_NCMBUTTONDOWN = 0x00a7, + WM_NCMBUTTONUP = 0x00a8, + WM_NCMBUTTONDBLCLK = 0x00a9, + WM_NCXBUTTONDOWN = 0x00ab, + WM_NCXBUTTONUP = 0x00ac, + WM_NCXBUTTONDBLCLK = 0x00ad, + SBM_SETPOS = 0x00e0, + SBM_GETPOS = 0x00e1, + SBM_SETRANGE = 0x00e2, + SBM_GETRANGE = 0x00e3, + SBM_ENABLE_ARROWS = 0x00e4, + SBM_SETRANGEREDRAW = 0x00e6, + SBM_SETSCROLLINFO = 0x00e9, + SBM_GETSCROLLINFO = 0x00ea, + SBM_GETSCROLLBARINFO = 0x00eb, + WM_INPUT = 0x00ff, + WM_KEYDOWN = 0x0100, + WM_KEYFIRST = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + WM_WNT_CONVERTREQUESTEX = 0x0109, + WM_CONVERTREQUEST = 0x010a, + WM_CONVERTRESULT = 0x010b, + WM_INTERIM = 0x010c, + WM_IME_STARTCOMPOSITION = 0x010d, + WM_IME_ENDCOMPOSITION = 0x010e, + WM_IME_COMPOSITION = 0x010f, + WM_IME_KEYLAST = 0x010f, + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_SYSTIMER = 0x0118, + WM_MENUSELECT = 0x011f, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_MENURBUTTONUP = 0x0122, + WM_MENUDRAG = 0x0123, + WM_MENUGETOBJECT = 0x0124, + WM_UNINITMENUPOPUP = 0x0125, + WM_MENUCOMMAND = 0x0126, + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + WM_MOUSEFIRST = 0x0200, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_MOUSELAST = 0x0209, + WM_MOUSEWHEEL = 0x020a, + WM_XBUTTONDOWN = 0x020b, + WM_XBUTTONUP = 0x020c, + WM_XBUTTONDBLCLK = 0x020d, + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + WM_POWERBROADCAST = 0x0218, + WM_DEVICECHANGE = 0x0219, + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + WM_IME_REPORT = 0x0280, + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_CONTROL = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_REQUEST = 0x0288, + WM_IMEKEYDOWN = 0x0290, + WM_IME_KEYDOWN = 0x0290, + WM_IMEKEYUP = 0x0291, + WM_IME_KEYUP = 0x0291, + WM_NCMOUSEHOVER = 0x02a0, + WM_MOUSEHOVER = 0x02a1, + WM_NCMOUSELEAVE = 0x02a2, + WM_MOUSELEAVE = 0x02a3, + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030a, + WM_SIZECLIPBOARD = 0x030b, + WM_ASKCBFORMATNAME = 0x030c, + WM_CHANGECBCHAIN = 0x030d, + WM_HSCROLLCLIPBOARD = 0x030e, + WM_QUERYNEWPALETTE = 0x030f, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + WM_APPCOMMAND = 0x0319, + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035f, + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037f, + WM_PENWINFIRST = 0x0380, + WM_RCRESULT = 0x0381, + WM_HOOKRCRESULT = 0x0382, + WM_GLOBALRCCHANGE = 0x0383, + WM_PENMISCINFO = 0x0383, + WM_SKB = 0x0384, + WM_HEDITCTL = 0x0385, + WM_PENCTL = 0x0385, + WM_PENMISC = 0x0386, + WM_CTLINIT = 0x0387, + WM_PENEVENT = 0x0388, + WM_PENWINLAST = 0x038f, + DDM_SETFMT = 0x0400, + DM_GETDEFID = 0x0400, + NIN_SELECT = 0x0400, + TBM_GETPOS = 0x0400, + WM_PSD_PAGESETUPDLG = 0x0400, + WM_USER = 0x0400, + CBEM_INSERTITEMA = 0x0401, + DDM_DRAW = 0x0401, + DM_SETDEFID = 0x0401, + HKM_SETHOTKEY = 0x0401, + PBM_SETRANGE = 0x0401, + RB_INSERTBANDA = 0x0401, + SB_SETTEXTA = 0x0401, + TB_ENABLEBUTTON = 0x0401, + TBM_GETRANGEMIN = 0x0401, + TTM_ACTIVATE = 0x0401, + WM_CHOOSEFONT_GETLOGFONT = 0x0401, + WM_PSD_FULLPAGERECT = 0x0401, + CBEM_SETIMAGELIST = 0x0402, + DDM_CLOSE = 0x0402, + DM_REPOSITION = 0x0402, + HKM_GETHOTKEY = 0x0402, + PBM_SETPOS = 0x0402, + RB_DELETEBAND = 0x0402, + SB_GETTEXTA = 0x0402, + TB_CHECKBUTTON = 0x0402, + TBM_GETRANGEMAX = 0x0402, + WM_PSD_MINMARGINRECT = 0x0402, + CBEM_GETIMAGELIST = 0x0403, + DDM_BEGIN = 0x0403, + HKM_SETRULES = 0x0403, + PBM_DELTAPOS = 0x0403, + RB_GETBARINFO = 0x0403, + SB_GETTEXTLENGTHA = 0x0403, + TBM_GETTIC = 0x0403, + TB_PRESSBUTTON = 0x0403, + TTM_SETDELAYTIME = 0x0403, + WM_PSD_MARGINRECT = 0x0403, + CBEM_GETITEMA = 0x0404, + DDM_END = 0x0404, + PBM_SETSTEP = 0x0404, + RB_SETBARINFO = 0x0404, + SB_SETPARTS = 0x0404, + TB_HIDEBUTTON = 0x0404, + TBM_SETTIC = 0x0404, + TTM_ADDTOOLA = 0x0404, + WM_PSD_GREEKTEXTRECT = 0x0404, + CBEM_SETITEMA = 0x0405, + PBM_STEPIT = 0x0405, + TB_INDETERMINATE = 0x0405, + TBM_SETPOS = 0x0405, + TTM_DELTOOLA = 0x0405, + WM_PSD_ENVSTAMPRECT = 0x0405, + CBEM_GETCOMBOCONTROL = 0x0406, + PBM_SETRANGE32 = 0x0406, + RB_SETBANDINFOA = 0x0406, + SB_GETPARTS = 0x0406, + TB_MARKBUTTON = 0x0406, + TBM_SETRANGE = 0x0406, + TTM_NEWTOOLRECTA = 0x0406, + WM_PSD_YAFULLPAGERECT = 0x0406, + CBEM_GETEDITCONTROL = 0x0407, + PBM_GETRANGE = 0x0407, + RB_SETPARENT = 0x0407, + SB_GETBORDERS = 0x0407, + TBM_SETRANGEMIN = 0x0407, + TTM_RELAYEVENT = 0x0407, + CBEM_SETEXSTYLE = 0x0408, + PBM_GETPOS = 0x0408, + RB_HITTEST = 0x0408, + SB_SETMINHEIGHT = 0x0408, + TBM_SETRANGEMAX = 0x0408, + TTM_GETTOOLINFOA = 0x0408, + CBEM_GETEXSTYLE = 0x0409, + CBEM_GETEXTENDEDSTYLE = 0x0409, + PBM_SETBARCOLOR = 0x0409, + RB_GETRECT = 0x0409, + SB_SIMPLE = 0x0409, + TB_ISBUTTONENABLED = 0x0409, + TBM_CLEARTICS = 0x0409, + TTM_SETTOOLINFOA = 0x0409, + CBEM_HASEDITCHANGED = 0x040a, + RB_INSERTBANDW = 0x040a, + SB_GETRECT = 0x040a, + TB_ISBUTTONCHECKED = 0x040a, + TBM_SETSEL = 0x040a, + TTM_HITTESTA = 0x040a, + WIZ_QUERYNUMPAGES = 0x040a, + CBEM_INSERTITEMW = 0x040b, + RB_SETBANDINFOW = 0x040b, + SB_SETTEXTW = 0x040b, + TB_ISBUTTONPRESSED = 0x040b, + TBM_SETSELSTART = 0x040b, + TTM_GETTEXTA = 0x040b, + WIZ_NEXT = 0x040b, + CBEM_SETITEMW = 0x040c, + RB_GETBANDCOUNT = 0x040c, + SB_GETTEXTLENGTHW = 0x040c, + TB_ISBUTTONHIDDEN = 0x040c, + TBM_SETSELEND = 0x040c, + TTM_UPDATETIPTEXTA = 0x040c, + WIZ_PREV = 0x040c, + CBEM_GETITEMW = 0x040d, + RB_GETROWCOUNT = 0x040d, + SB_GETTEXTW = 0x040d, + TB_ISBUTTONINDETERMINATE = 0x040d, + TTM_GETTOOLCOUNT = 0x040d, + CBEM_SETEXTENDEDSTYLE = 0x040e, + RB_GETROWHEIGHT = 0x040e, + SB_ISSIMPLE = 0x040e, + TB_ISBUTTONHIGHLIGHTED = 0x040e, + TBM_GETPTICS = 0x040e, + TTM_ENUMTOOLSA = 0x040e, + SB_SETICON = 0x040f, + TBM_GETTICPOS = 0x040f, + TTM_GETCURRENTTOOLA = 0x040f, + RB_IDTOINDEX = 0x0410, + SB_SETTIPTEXTA = 0x0410, + TBM_GETNUMTICS = 0x0410, + TTM_WINDOWFROMPOINT = 0x0410, + RB_GETTOOLTIPS = 0x0411, + SB_SETTIPTEXTW = 0x0411, + TBM_GETSELSTART = 0x0411, + TB_SETSTATE = 0x0411, + TTM_TRACKACTIVATE = 0x0411, + RB_SETTOOLTIPS = 0x0412, + SB_GETTIPTEXTA = 0x0412, + TB_GETSTATE = 0x0412, + TBM_GETSELEND = 0x0412, + TTM_TRACKPOSITION = 0x0412, + RB_SETBKCOLOR = 0x0413, + SB_GETTIPTEXTW = 0x0413, + TB_ADDBITMAP = 0x0413, + TBM_CLEARSEL = 0x0413, + TTM_SETTIPBKCOLOR = 0x0413, + RB_GETBKCOLOR = 0x0414, + SB_GETICON = 0x0414, + TB_ADDBUTTONSA = 0x0414, + TBM_SETTICFREQ = 0x0414, + TTM_SETTIPTEXTCOLOR = 0x0414, + RB_SETTEXTCOLOR = 0x0415, + TB_INSERTBUTTONA = 0x0415, + TBM_SETPAGESIZE = 0x0415, + TTM_GETDELAYTIME = 0x0415, + RB_GETTEXTCOLOR = 0x0416, + TB_DELETEBUTTON = 0x0416, + TBM_GETPAGESIZE = 0x0416, + TTM_GETTIPBKCOLOR = 0x0416, + RB_SIZETORECT = 0x0417, + TB_GETBUTTON = 0x0417, + TBM_SETLINESIZE = 0x0417, + TTM_GETTIPTEXTCOLOR = 0x0417, + RB_BEGINDRAG = 0x0418, + TB_BUTTONCOUNT = 0x0418, + TBM_GETLINESIZE = 0x0418, + TTM_SETMAXTIPWIDTH = 0x0418, + RB_ENDDRAG = 0x0419, + TB_COMMANDTOINDEX = 0x0419, + TBM_GETTHUMBRECT = 0x0419, + TTM_GETMAXTIPWIDTH = 0x0419, + RB_DRAGMOVE = 0x041a, + TBM_GETCHANNELRECT = 0x041a, + TB_SAVERESTOREA = 0x041a, + TTM_SETMARGIN = 0x041a, + RB_GETBARHEIGHT = 0x041b, + TB_CUSTOMIZE = 0x041b, + TBM_SETTHUMBLENGTH = 0x041b, + TTM_GETMARGIN = 0x041b, + RB_GETBANDINFOW = 0x041c, + TB_ADDSTRINGA = 0x041c, + TBM_GETTHUMBLENGTH = 0x041c, + TTM_POP = 0x041c, + RB_GETBANDINFOA = 0x041d, + TB_GETITEMRECT = 0x041d, + TBM_SETTOOLTIPS = 0x041d, + TTM_UPDATE = 0x041d, + RB_MINIMIZEBAND = 0x041e, + TB_BUTTONSTRUCTSIZE = 0x041e, + TBM_GETTOOLTIPS = 0x041e, + TTM_GETBUBBLESIZE = 0x041e, + RB_MAXIMIZEBAND = 0x041f, + TBM_SETTIPSIDE = 0x041f, + TB_SETBUTTONSIZE = 0x041f, + TTM_ADJUSTRECT = 0x041f, + TBM_SETBUDDY = 0x0420, + TB_SETBITMAPSIZE = 0x0420, + TTM_SETTITLEA = 0x0420, + MSG_FTS_JUMP_VA = 0x0421, + TB_AUTOSIZE = 0x0421, + TBM_GETBUDDY = 0x0421, + TTM_SETTITLEW = 0x0421, + RB_GETBANDBORDERS = 0x0422, + MSG_FTS_JUMP_QWORD = 0x0423, + RB_SHOWBAND = 0x0423, + TB_GETTOOLTIPS = 0x0423, + MSG_REINDEX_REQUEST = 0x0424, + TB_SETTOOLTIPS = 0x0424, + MSG_FTS_WHERE_IS_IT = 0x0425, + RB_SETPALETTE = 0x0425, + TB_SETPARENT = 0x0425, + RB_GETPALETTE = 0x0426, + RB_MOVEBAND = 0x0427, + TB_SETROWS = 0x0427, + TB_GETROWS = 0x0428, + TB_GETBITMAPFLAGS = 0x0429, + TB_SETCMDID = 0x042a, + RB_PUSHCHEVRON = 0x042b, + TB_CHANGEBITMAP = 0x042b, + TB_GETBITMAP = 0x042c, + MSG_GET_DEFFONT = 0x042d, + TB_GETBUTTONTEXTA = 0x042d, + TB_REPLACEBITMAP = 0x042e, + TB_SETINDENT = 0x042f, + TB_SETIMAGELIST = 0x0430, + TB_GETIMAGELIST = 0x0431, + TB_LOADIMAGES = 0x0432, + TTM_ADDTOOLW = 0x0432, + TB_GETRECT = 0x0433, + TTM_DELTOOLW = 0x0433, + TB_SETHOTIMAGELIST = 0x0434, + TTM_NEWTOOLRECTW = 0x0434, + TB_GETHOTIMAGELIST = 0x0435, + TTM_GETTOOLINFOW = 0x0435, + TB_SETDISABLEDIMAGELIST = 0x0436, + TTM_SETTOOLINFOW = 0x0436, + TB_GETDISABLEDIMAGELIST = 0x0437, + TTM_HITTESTW = 0x0437, + TB_SETSTYLE = 0x0438, + TTM_GETTEXTW = 0x0438, + TB_GETSTYLE = 0x0439, + TTM_UPDATETIPTEXTW = 0x0439, + TB_GETBUTTONSIZE = 0x043a, + TTM_ENUMTOOLSW = 0x043a, + TB_SETBUTTONWIDTH = 0x043b, + TTM_GETCURRENTTOOLW = 0x043b, + TB_SETMAXTEXTROWS = 0x043c, + TB_GETTEXTROWS = 0x043d, + TB_GETOBJECT = 0x043e, + TB_GETBUTTONINFOW = 0x043f, + TB_SETBUTTONINFOW = 0x0440, + TB_GETBUTTONINFOA = 0x0441, + TB_SETBUTTONINFOA = 0x0442, + TB_INSERTBUTTONW = 0x0443, + TB_ADDBUTTONSW = 0x0444, + TB_HITTEST = 0x0445, + TB_SETDRAWTEXTFLAGS = 0x0446, + TB_GETHOTITEM = 0x0447, + TB_SETHOTITEM = 0x0448, + TB_SETANCHORHIGHLIGHT = 0x0449, + TB_GETANCHORHIGHLIGHT = 0x044a, + TB_GETBUTTONTEXTW = 0x044b, + TB_SAVERESTOREW = 0x044c, + TB_ADDSTRINGW = 0x044d, + TB_MAPACCELERATORA = 0x044e, + TB_GETINSERTMARK = 0x044f, + TB_SETINSERTMARK = 0x0450, + TB_INSERTMARKHITTEST = 0x0451, + TB_MOVEBUTTON = 0x0452, + TB_GETMAXSIZE = 0x0453, + TB_SETEXTENDEDSTYLE = 0x0454, + TB_GETEXTENDEDSTYLE = 0x0455, + TB_GETPADDING = 0x0456, + TB_SETPADDING = 0x0457, + TB_SETINSERTMARKCOLOR = 0x0458, + TB_GETINSERTMARKCOLOR = 0x0459, + TB_MAPACCELERATORW = 0x045a, + TB_GETSTRINGW = 0x045b, + TB_GETSTRINGA = 0x045c, + TAPI_REPLY = 0x0463, + ACM_OPENA = 0x0464, + BFFM_SETSTATUSTEXTA = 0x0464, + CDM_FIRST = 0x0464, + CDM_GETSPEC = 0x0464, + IPM_CLEARADDRESS = 0x0464, + WM_CAP_UNICODE_START = 0x0464, + ACM_PLAY = 0x0465, + BFFM_ENABLEOK = 0x0465, + CDM_GETFILEPATH = 0x0465, + IPM_SETADDRESS = 0x0465, + PSM_SETCURSEL = 0x0465, + UDM_SETRANGE = 0x0465, + WM_CHOOSEFONT_SETLOGFONT = 0x0465, + ACM_STOP = 0x0466, + BFFM_SETSELECTIONA = 0x0466, + CDM_GETFOLDERPATH = 0x0466, + IPM_GETADDRESS = 0x0466, + PSM_REMOVEPAGE = 0x0466, + UDM_GETRANGE = 0x0466, + WM_CAP_SET_CALLBACK_ERRORW = 0x0466, + WM_CHOOSEFONT_SETFLAGS = 0x0466, + ACM_OPENW = 0x0467, + BFFM_SETSELECTIONW = 0x0467, + CDM_GETFOLDERIDLIST = 0x0467, + IPM_SETRANGE = 0x0467, + PSM_ADDPAGE = 0x0467, + UDM_SETPOS = 0x0467, + WM_CAP_SET_CALLBACK_STATUSW = 0x0467, + BFFM_SETSTATUSTEXTW = 0x0468, + CDM_SETCONTROLTEXT = 0x0468, + IPM_SETFOCUS = 0x0468, + PSM_CHANGED = 0x0468, + UDM_GETPOS = 0x0468, + CDM_HIDECONTROL = 0x0469, + IPM_ISBLANK = 0x0469, + PSM_RESTARTWINDOWS = 0x0469, + UDM_SETBUDDY = 0x0469, + CDM_SETDEFEXT = 0x046a, + PSM_REBOOTSYSTEM = 0x046a, + UDM_GETBUDDY = 0x046a, + PSM_CANCELTOCLOSE = 0x046b, + UDM_SETACCEL = 0x046b, + EM_CONVPOSITION = 0x046c, + PSM_QUERYSIBLINGS = 0x046c, + UDM_GETACCEL = 0x046c, + MCIWNDM_GETZOOM = 0x046d, + PSM_UNCHANGED = 0x046d, + UDM_SETBASE = 0x046d, + PSM_APPLY = 0x046e, + UDM_GETBASE = 0x046e, + PSM_SETTITLEA = 0x046f, + UDM_SETRANGE32 = 0x046f, + PSM_SETWIZBUTTONS = 0x0470, + UDM_GETRANGE32 = 0x0470, + WM_CAP_DRIVER_GET_NAMEW = 0x0470, + PSM_PRESSBUTTON = 0x0471, + UDM_SETPOS32 = 0x0471, + WM_CAP_DRIVER_GET_VERSIONW = 0x0471, + PSM_SETCURSELID = 0x0472, + UDM_GETPOS32 = 0x0472, + PSM_SETFINISHTEXTA = 0x0473, + PSM_GETTABCONTROL = 0x0474, + PSM_ISDIALOGMESSAGE = 0x0475, + MCIWNDM_REALIZE = 0x0476, + PSM_GETCURRENTPAGEHWND = 0x0476, + MCIWNDM_SETTIMEFORMATA = 0x0477, + PSM_INSERTPAGE = 0x0477, + MCIWNDM_GETTIMEFORMATA = 0x0478, + PSM_SETTITLEW = 0x0478, + WM_CAP_FILE_SET_CAPTURE_FILEW = 0x0478, + MCIWNDM_VALIDATEMEDIA = 0x0479, + PSM_SETFINISHTEXTW = 0x0479, + WM_CAP_FILE_GET_CAPTURE_FILEW = 0x0479, + MCIWNDM_PLAYTO = 0x047b, + WM_CAP_FILE_SAVEASW = 0x047b, + MCIWNDM_GETFILENAMEA = 0x047c, + MCIWNDM_GETDEVICEA = 0x047d, + PSM_SETHEADERTITLEA = 0x047d, + WM_CAP_FILE_SAVEDIBW = 0x047d, + MCIWNDM_GETPALETTE = 0x047e, + PSM_SETHEADERTITLEW = 0x047e, + MCIWNDM_SETPALETTE = 0x047f, + PSM_SETHEADERSUBTITLEA = 0x047f, + MCIWNDM_GETERRORA = 0x0480, + PSM_SETHEADERSUBTITLEW = 0x0480, + PSM_HWNDTOINDEX = 0x0481, + PSM_INDEXTOHWND = 0x0482, + MCIWNDM_SETINACTIVETIMER = 0x0483, + PSM_PAGETOINDEX = 0x0483, + PSM_INDEXTOPAGE = 0x0484, + DL_BEGINDRAG = 0x0485, + MCIWNDM_GETINACTIVETIMER = 0x0485, + PSM_IDTOINDEX = 0x0485, + DL_DRAGGING = 0x0486, + PSM_INDEXTOID = 0x0486, + DL_DROPPED = 0x0487, + PSM_GETRESULT = 0x0487, + DL_CANCELDRAG = 0x0488, + PSM_RECALCPAGESIZES = 0x0488, + MCIWNDM_GET_SOURCE = 0x048c, + MCIWNDM_PUT_SOURCE = 0x048d, + MCIWNDM_GET_DEST = 0x048e, + MCIWNDM_PUT_DEST = 0x048f, + MCIWNDM_CAN_PLAY = 0x0490, + MCIWNDM_CAN_WINDOW = 0x0491, + MCIWNDM_CAN_RECORD = 0x0492, + MCIWNDM_CAN_SAVE = 0x0493, + MCIWNDM_CAN_EJECT = 0x0494, + MCIWNDM_CAN_CONFIG = 0x0495, + IE_GETINK = 0x0496, + IE_MSGFIRST = 0x0496, + MCIWNDM_PALETTEKICK = 0x0496, + IE_SETINK = 0x0497, + IE_GETPENTIP = 0x0498, + IE_SETPENTIP = 0x0499, + IE_GETERASERTIP = 0x049a, + IE_SETERASERTIP = 0x049b, + IE_GETBKGND = 0x049c, + IE_SETBKGND = 0x049d, + IE_GETGRIDORIGIN = 0x049e, + IE_SETGRIDORIGIN = 0x049f, + IE_GETGRIDPEN = 0x04a0, + IE_SETGRIDPEN = 0x04a1, + IE_GETGRIDSIZE = 0x04a2, + IE_SETGRIDSIZE = 0x04a3, + IE_GETMODE = 0x04a4, + IE_SETMODE = 0x04a5, + IE_GETINKRECT = 0x04a6, + WM_CAP_SET_MCI_DEVICEW = 0x04a6, + WM_CAP_GET_MCI_DEVICEW = 0x04a7, + WM_CAP_PAL_OPENW = 0x04b4, + WM_CAP_PAL_SAVEW = 0x04b5, + IE_GETAPPDATA = 0x04b8, + IE_SETAPPDATA = 0x04b9, + IE_GETDRAWOPTS = 0x04ba, + IE_SETDRAWOPTS = 0x04bb, + IE_GETFORMAT = 0x04bc, + IE_SETFORMAT = 0x04bd, + IE_GETINKINPUT = 0x04be, + IE_SETINKINPUT = 0x04bf, + IE_GETNOTIFY = 0x04c0, + IE_SETNOTIFY = 0x04c1, + IE_GETRECOG = 0x04c2, + IE_SETRECOG = 0x04c3, + IE_GETSECURITY = 0x04c4, + IE_SETSECURITY = 0x04c5, + IE_GETSEL = 0x04c6, + IE_SETSEL = 0x04c7, + CDM_LAST = 0x04c8, + IE_DOCOMMAND = 0x04c8, + MCIWNDM_NOTIFYMODE = 0x04c8, + IE_GETCOMMAND = 0x04c9, + IE_GETCOUNT = 0x04ca, + IE_GETGESTURE = 0x04cb, + MCIWNDM_NOTIFYMEDIA = 0x04cb, + IE_GETMENU = 0x04cc, + IE_GETPAINTDC = 0x04cd, + MCIWNDM_NOTIFYERROR = 0x04cd, + IE_GETPDEVENT = 0x04ce, + IE_GETSELCOUNT = 0x04cf, + IE_GETSELITEMS = 0x04d0, + IE_GETSTYLE = 0x04d1, + MCIWNDM_SETTIMEFORMATW = 0x04db, + EM_OUTLINE = 0x04dc, + MCIWNDM_GETTIMEFORMATW = 0x04dc, + EM_GETSCROLLPOS = 0x04dd, + EM_SETSCROLLPOS = 0x04de, + EM_SETFONTSIZE = 0x04df, + MCIWNDM_GETFILENAMEW = 0x04e0, + MCIWNDM_GETDEVICEW = 0x04e1, + MCIWNDM_GETERRORW = 0x04e4, + FM_GETFOCUS = 0x0600, + FM_GETDRIVEINFOA = 0x0601, + FM_GETSELCOUNT = 0x0602, + FM_GETSELCOUNTLFN = 0x0603, + FM_GETFILESELA = 0x0604, + FM_GETFILESELLFNA = 0x0605, + FM_REFRESH_WINDOWS = 0x0606, + FM_RELOAD_EXTENSIONS = 0x0607, + FM_GETDRIVEINFOW = 0x0611, + FM_GETFILESELW = 0x0614, + FM_GETFILESELLFNW = 0x0615, + WLX_WM_SAS = 0x0659, + SM_GETSELCOUNT = 0x07e8, + UM_GETSELCOUNT = 0x07e8, + WM_CPL_LAUNCH = 0x07e8, + SM_GETSERVERSELA = 0x07e9, + UM_GETUSERSELA = 0x07e9, + WM_CPL_LAUNCHED = 0x07e9, + SM_GETSERVERSELW = 0x07ea, + UM_GETUSERSELW = 0x07ea, + SM_GETCURFOCUSA = 0x07eb, + UM_GETGROUPSELA = 0x07eb, + SM_GETCURFOCUSW = 0x07ec, + UM_GETGROUPSELW = 0x07ec, + SM_GETOPTIONS = 0x07ed, + UM_GETCURFOCUSA = 0x07ed, + UM_GETCURFOCUSW = 0x07ee, + UM_GETOPTIONS = 0x07ef, + UM_GETOPTIONS2 = 0x07f0, + OCMBASE = 0x2000, + OCM_CTLCOLOR = 0x2019, + OCM_DRAWITEM = 0x202b, + OCM_MEASUREITEM = 0x202c, + OCM_DELETEITEM = 0x202d, + OCM_VKEYTOITEM = 0x202e, + OCM_CHARTOITEM = 0x202f, + OCM_COMPAREITEM = 0x2039, + OCM_NOTIFY = 0x204e, + OCM_COMMAND = 0x2111, + OCM_HSCROLL = 0x2114, + OCM_VSCROLL = 0x2115, + OCM_CTLCOLORMSGBOX = 0x2132, + OCM_CTLCOLOREDIT = 0x2133, + OCM_CTLCOLORLISTBOX = 0x2134, + OCM_CTLCOLORBTN = 0x2135, + OCM_CTLCOLORDLG = 0x2136, + OCM_CTLCOLORSCROLLBAR = 0x2137, + OCM_CTLCOLORSTATIC = 0x2138, + OCM_PARENTNOTIFY = 0x2210, + WM_APP = 0x8000, + WM_RASDIALEVENT = 0xcccd + } + + /// + /// From https://msdn.microsoft.com/en-us/library/windows/desktop/aa372716(v=vs.85).aspx + /// + [Flags] + public enum WindowMessageParameter : uint + { + PBT_APMQUERYSUSPEND = 0x0, + PBT_APMBATTERYLOW = 0x9, // Notifies applications that the battery power is low. + PBT_APMOEMEVENT = 0xb, // Notifies applications that the APM BIOS has signalled an APM OEM event. + PBT_APMQUERYSTANDBY = 0x0001, // + PBT_APMPOWERSTATUSCHANGE = 0xa, // Notifies applications of a change in the power status of the computer, such as a switch from battery power to A/C. The system also broadcasts this event when remaining battery power slipsbelowthethresholdspecified by the user or if the battery power changes by a specified percentage. + PBT_APMQUERYSUSPENDFAILED = 0x218, // Notifies applications that permission to suspend the computer was denied. + PBT_APMRESUMEAUTOMATIC = 0x12, // Notifies applications that the system is resuming from sleep or hibernation. If the system detects any user activity after broadcasting PBT_APMRESUMEAUTOMATIC, it will broadcast aPBT_APMRESUMESUSPENDeventt let applications know they can resume full interaction with the user. + PBT_APMRESUMECRITICAL = 0x6, // Notifies applications that the system has resumed operation. + PBT_APMRESUMESUSPEND = 0x7, // Notifies applications that the system has resumed operation after being suspended. + PBT_APMSUSPEND = 0x4, // Notifies applications that the computer is about to enter a suspended state. + PBT_POWERSETTINGCHANGE = 0x8013, // Notifies applications that a power setting change event occurred. + WM_POWER = 0x48, // Notifies applications that the system, typically a battery-powered personal computer, is about to enter a suspended mode. + WM_POWERBROADCAST = 0x218, // Notifies applications that a power-management event has occurred. + BROADCAST_QUERY_DENY = 0x424D5144 // + } +} diff --git a/src/Mod.cs b/src/Mod.cs new file mode 100755 index 0000000..c87e280 --- /dev/null +++ b/src/Mod.cs @@ -0,0 +1,88 @@ +using SpotifyMod.Native; +using SpotifyMod.Volume; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Forms; +using static SpotifyMod.HandlerCommunications; +using static SpotifyMod.Native.Messages; +using static SpotifyMod.Utils; +using static SpotifyMod.VirtualKeys; + +//View this to embed dlls into the mod file itself: https://denhamcoder.net/2018/08/25/embedding-net-assemblies-inside-net-assemblies/ +namespace SpotifyMod +{ + + public class Mod + { + + + public void Call(long wParam, long lParam, params dynamic[] args) + { + //HandleKey(wParam, lParam, args); + + } + + private dynamic HostWindow; + public static dynamic Tunnel; + public int Init(dynamic tunnelInstance) + { + Tunnel = tunnelInstance; + HostWindow = Tunnel.HostWindow; + + //AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + Config.Load(); + + return (int)HandleType.HandlerOnly; + } + + public dynamic ReturnKeyCombinations() + { + return ModKeyCombination.ModKeyCombinations; + } + + public void SetKeyCombinations() + { + Tunnel.UpdateKeyCombinations(this, ModKeyCombination.ModKeyCombinations); + } + + public void Reload() + { + Config.Load(); + } + + public void Closing() + { + Config.SaveConfig(Settings.Current); + } + + static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + //using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("SpotifyMod.EmbeddedAssemblies.Newtonsoft.Json.dll")) + //{ + // var assemblyData = new byte[stream.Length]; + // stream.Read(assemblyData, 0, assemblyData.Length); + // return Assembly.Load(assemblyData); + //} + + return null; + } + + public void Testing() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_KEYDOWN, (int)VKeys.LCONTROL, 0); + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_KEYDOWN, (int)VKeys.UP, 0); + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_KEYUP, (int)VKeys.LCONTROL, 1); + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_KEYUP, (int)VKeys.UP, 1); + } + + + } +} diff --git a/src/ModMethods.cs b/src/ModMethods.cs new file mode 100755 index 0000000..e3efb6c --- /dev/null +++ b/src/ModMethods.cs @@ -0,0 +1,99 @@ +using SpotifyMod; +using SpotifyMod.Native; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; +using static SpotifyMod.Native.Messages; +using static SpotifyMod.Utils; + +namespace SpotifyMod +{ + class ModMethods + { + public static ModMethods Singleton = new ModMethods(); + + public static Dictionary MethodDict = new Dictionary() + { + { CommandType.OpenSpotify, Singleton.OpenSpotify }, + { CommandType.PreviousSong, Singleton.PreviousSong }, + { CommandType.NextSong, Singleton.NextSong }, + { CommandType.Rewind, Singleton.Rewind }, + { CommandType.FastForward, Singleton.FastForward }, + { CommandType.PlayPause, Singleton.PlayPause }, + { CommandType.VolumeDown, Singleton.VolumeDown }, + { CommandType.VolumeUp, Singleton.VolumeUp } + }; + + public void OpenSpotify() + { + try + { + if (Settings.Current.SpotifyLocation.Contains(" {ARGS} ")) + { + string[] processData = Settings.Current.SpotifyLocation.Split(" {ARGS} "); + Process.Start(new ProcessStartInfo() { FileName = processData[0], Arguments = processData[1], UseShellExecute = true }); + } + else + { + Process.Start(new ProcessStartInfo() { FileName = Settings.Current.SpotifyLocation, UseShellExecute = true }); + } + + } + catch (Exception ex) + { + Mod.Tunnel.ShowMessageBox($"Type: {ex.GetType()}\r\nMessage: {ex.Message}", "Spotify Mod - Execution Error @ OpenSpotify"); + } + } + + public void PreviousSong() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_APPCOMMAND, 0, ShiftAppCommandCode(AppComandCode.MEDIA_PREVIOUSTRACK)); + } + + public void NextSong() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_APPCOMMAND, 0, ShiftAppCommandCode(AppComandCode.MEDIA_NEXTTRACK)); + } + + public void Rewind() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_APPCOMMAND, 0, ShiftAppCommandCode(AppComandCode.MEDIA_REWIND)); + } + + public void FastForward() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_APPCOMMAND, 0, ShiftAppCommandCode(AppComandCode.MEDIA_FASTFORWARD)); + } + + public void PlayPause() + { + if (Spotify.IsNull()) return; + + PostMessage(Spotify.MainWindowHandle, (int)WindowMessage.WM_APPCOMMAND, 0, ShiftAppCommandCode(AppComandCode.MEDIA_PLAY_PAUSE)); + } + + public void VolumeDown() + { + if (Spotify.IsNull()) return; + + ChangeVolume(Spotify.Id, VolumeChange.Decrease, Settings.Current.VolumeIncreaseRate); +} + + public void VolumeUp() + { + if (Spotify.IsNull()) return; + + ChangeVolume(Spotify.Id, VolumeChange.Increase, Settings.Current.VolumeIncreaseRate); + } + } +} diff --git a/src/NativeMethods.cs b/src/NativeMethods.cs new file mode 100755 index 0000000..4b4307e --- /dev/null +++ b/src/NativeMethods.cs @@ -0,0 +1,145 @@ +using System; +using System.Runtime.InteropServices; + +namespace SpotifyMod.Native +{ + public static class Messages + { + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, long wParam, long lParam); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool PostMessage(IntPtr hWnd, uint Msg, long wParam, long lParam); + + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool PeekMessage( + out NativeMessage lpMsg, + IntPtr hwnd, + uint wMsgFilterMin, + uint wMsgFilterMax, + uint wRemoveMsg); + + + [StructLayout(LayoutKind.Sequential)] + public struct NativeMessage + { + public IntPtr handle; + public uint msg; + public IntPtr wParam; + public IntPtr lParam; + public uint time; + public System.Drawing.Point p; + } + + [Flags] + public enum PeekMessageParams : uint + { + PM_NOREMOVE = 0x0000, + PM_REMOVE = 0x0001, + PM_NOYIELD = 0x0002, + PM_QS_INPUT = QueueStatusFlags.QS_INPUT << 16, + PM_QS_POSTMESSAGE = (QueueStatusFlags.QS_POSTMESSAGE | QueueStatusFlags.QS_HOTKEY | QueueStatusFlags.QS_TIMER) << 16, + PM_QS_PAINT = QueueStatusFlags.QS_PAINT << 16, + PM_QS_SENDMESSAGE = QueueStatusFlags.QS_SENDMESSAGE << 16 + } + + [Flags] + public enum QueueStatusFlags : uint + { + QS_KEY = 0x1, + QS_MOUSEMOVE = 0x2, + QS_MOUSEBUTTON = 0x4, + QS_MOUSE = (QS_MOUSEMOVE | QS_MOUSEBUTTON), + QS_INPUT = (QS_MOUSE | QS_KEY), + QS_POSTMESSAGE = 0x8, + QS_TIMER = 0x10, + QS_PAINT = 0x20, + QS_SENDMESSAGE = 0x40, + QS_HOTKEY = 0x80, + QS_REFRESH = (QS_HOTKEY | QS_KEY | QS_MOUSEBUTTON | QS_PAINT), + QS_ALLEVENTS = (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY), + QS_ALLINPUT = (QS_SENDMESSAGE | QS_PAINT | QS_TIMER | QS_POSTMESSAGE | QS_MOUSEBUTTON | QS_MOUSEMOVE | QS_HOTKEY | QS_KEY), + QS_ALLPOSTMESSAGE = 0x100, + QS_RAWINPUT = 0x400 + } + + + public enum AppComandCode : uint + { + BASS_BOOST = 20, + BASS_DOWN = 19, + BASS_UP = 21, + BROWSER_BACKWARD = 1, + BROWSER_FAVORITES = 6, + BROWSER_FORWARD = 2, + BROWSER_HOME = 7, + BROWSER_REFRESH = 3, + BROWSER_SEARCH = 5, + BROWSER_STOP = 4, + LAUNCH_APP1 = 17, + LAUNCH_APP2 = 18, + LAUNCH_MAIL = 15, + LAUNCH_MEDIA_SELECT = 16, + MEDIA_NEXTTRACK = 11, + MEDIA_PLAY_PAUSE = 14, + MEDIA_PREVIOUSTRACK = 12, + MEDIA_STOP = 13, + TREBLE_DOWN = 22, + TREBLE_UP = 23, + VOLUME_DOWN = 9, + VOLUME_MUTE = 8, + VOLUME_UP = 10, + MICROPHONE_VOLUME_MUTE = 24, + MICROPHONE_VOLUME_DOWN = 25, + MICROPHONE_VOLUME_UP = 26, + CLOSE = 31, + COPY = 36, + CORRECTION_LIST = 45, + CUT = 37, + DICTATE_OR_COMMAND_CONTROL_TOGGLE = 43, + FIND = 28, + FORWARD_MAIL = 40, + HELP = 27, + MEDIA_CHANNEL_DOWN = 52, + MEDIA_CHANNEL_UP = 51, + MEDIA_FASTFORWARD = 49, + MEDIA_PAUSE = 47, + MEDIA_PLAY = 46, + MEDIA_RECORD = 48, + MEDIA_REWIND = 50, + MIC_ON_OFF_TOGGLE = 44, + NEW = 29, + OPEN = 30, + PASTE = 38, + PRINT = 33, + REDO = 35, + REPLY_TO_MAIL = 39, + SAVE = 32, + SEND_MAIL = 41, + SPELL_CHECK = 42, + UNDO = 34, + DELETE = 53, + DWM_FLIP3D = 54 + } + } + + public static class Libraries + { + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] + public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); + + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + } + + public static class Windows + { + [DllImport("user32.dll")] + public static extern IntPtr FindWindow(string strClassName, string strWindowName); + + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); + } +} \ No newline at end of file diff --git a/src/Spotify.cs b/src/Spotify.cs new file mode 100755 index 0000000..080d5e1 --- /dev/null +++ b/src/Spotify.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace SpotifyMod +{ + + class Spotify + { + static Process Process; + + public static IntPtr MainWindowHandle => Process.MainWindowHandle; + public static int Id => Process.Id; + + + public static bool IsNull() + { + if (Process == null) + { + var processes = Process.GetProcessesByName("Spotify"); + if (processes.Length == 0) + return true; + + Process = processes[0]; + Process.EnableRaisingEvents = true; + Process.Exited += (s, eargs) => + { + SpotifyClosed(); + }; + } + return false; + } + static void SpotifyClosed() + { + Process = null; + } + } +} diff --git a/src/SpotifyMod.csproj b/src/SpotifyMod.csproj new file mode 100755 index 0000000..011f741 --- /dev/null +++ b/src/SpotifyMod.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + + + + + + + + + C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Windows.Forms.dll + + + + diff --git a/src/SpotifyMod.sln b/src/SpotifyMod.sln new file mode 100755 index 0000000..95bcf21 --- /dev/null +++ b/src/SpotifyMod.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotifyMod", "SpotifyMod.csproj", "{273EF25D-5E34-4F5A-9067-70DB779CABF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyModTesting", "..\SpotifyModTesting\SpotifyModTesting.csproj", "{86D9A235-F5ED-455A-8095-034D38239A41}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {273EF25D-5E34-4F5A-9067-70DB779CABF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {273EF25D-5E34-4F5A-9067-70DB779CABF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {273EF25D-5E34-4F5A-9067-70DB779CABF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {273EF25D-5E34-4F5A-9067-70DB779CABF0}.Release|Any CPU.Build.0 = Release|Any CPU + {86D9A235-F5ED-455A-8095-034D38239A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86D9A235-F5ED-455A-8095-034D38239A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86D9A235-F5ED-455A-8095-034D38239A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86D9A235-F5ED-455A-8095-034D38239A41}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E78B2E9F-A53D-4E9B-97A3-01B63B706C17} + EndGlobalSection +EndGlobal diff --git a/src/Utils.cs b/src/Utils.cs new file mode 100755 index 0000000..f322b42 --- /dev/null +++ b/src/Utils.cs @@ -0,0 +1,51 @@ +using SpotifyMod.Volume; +using System; +using System.Collections.Generic; +using System.Text; +using static SpotifyMod.Native.Messages; + +namespace SpotifyMod +{ + class Utils + { + public static int ShiftAppCommandCode(AppComandCode code) + { + return (int)code << 16; + } + + //public const int DefaultVolumeChange = 10; + public static void ChangeVolume(int id, VolumeChange volChange, int volChangeAmount) + { + switch (volChange) + { + case VolumeChange.Decrease: + { + + var curVol = (int)VolumeMixer.GetApplicationVolume(id); + if (curVol > 0) + { + VolumeMixer.SetApplicationVolume(id, curVol - volChangeAmount <= 0 ? 0 : curVol - volChangeAmount); + + } + } + break; + + case VolumeChange.Increase: + { + var curVol = (int)VolumeMixer.GetApplicationVolume(Spotify.Id); + if (curVol < 100) + { + VolumeMixer.SetApplicationVolume(Spotify.Id, curVol + volChangeAmount >= 100 ? 100 : curVol + volChangeAmount); + } + } + break; + } + } + + public enum VolumeChange + { + Decrease, + Increase + } + } +} diff --git a/src/VirtualKeys.cs b/src/VirtualKeys.cs new file mode 100755 index 0000000..420f294 --- /dev/null +++ b/src/VirtualKeys.cs @@ -0,0 +1,216 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace SpotifyMod +{ + public class VirtualKeys + { + /// + /// Virtual Keys + /// + public enum VKeys + { + // Losely based on http://www.pinvoke.net/default.aspx/Enums/VK.html + + LBUTTON = 0x01, // Left mouse button + RBUTTON = 0x02, // Right mouse button + CANCEL = 0x03, // Control-break processing + MBUTTON = 0x04, // Middle mouse button (three-button mouse) + XBUTTON1 = 0x05, // Windows 2000/XP: X1 mouse button + XBUTTON2 = 0x06, // Windows 2000/XP: X2 mouse button + // 0x07 // Undefined + BACK = 0x08, // BACKSPACE key + TAB = 0x09, // TAB key + // 0x0A-0x0B, // Reserved + CLEAR = 0x0C, // CLEAR key + RETURN = 0x0D, // ENTER key + // 0x0E-0x0F, // Undefined + SHIFT = 0x10, // SHIFT key + CONTROL = 0x11, // CTRL key + MENU = 0x12, // ALT key + PAUSE = 0x13, // PAUSE key + CAPITAL = 0x14, // CAPS LOCK key + KANA = 0x15, // Input Method Editor (IME) Kana mode + HANGUL = 0x15, // IME Hangul mode + // 0x16, // Undefined + JUNJA = 0x17, // IME Junja mode + FINAL = 0x18, // IME final mode + HANJA = 0x19, // IME Hanja mode + KANJI = 0x19, // IME Kanji mode + // 0x1A, // Undefined + ESCAPE = 0x1B, // ESC key + CONVERT = 0x1C, // IME convert + NONCONVERT = 0x1D, // IME nonconvert + ACCEPT = 0x1E, // IME accept + MODECHANGE = 0x1F, // IME mode change request + SPACE = 0x20, // SPACEBAR + PRIOR = 0x21, // PAGE UP key + NEXT = 0x22, // PAGE DOWN key + END = 0x23, // END key + HOME = 0x24, // HOME key + LEFT = 0x25, // LEFT ARROW key + UP = 0x26, // UP ARROW key + RIGHT = 0x27, // RIGHT ARROW key + DOWN = 0x28, // DOWN ARROW key + SELECT = 0x29, // SELECT key + PRINT = 0x2A, // PRINT key + EXECUTE = 0x2B, // EXECUTE key + SNAPSHOT = 0x2C, // PRINT SCREEN key + INSERT = 0x2D, // INS key + DELETE = 0x2E, // DEL key + HELP = 0x2F, // HELP key + KEY_0 = 0x30, // 0 key + KEY_1 = 0x31, // 1 key + KEY_2 = 0x32, // 2 key + KEY_3 = 0x33, // 3 key + KEY_4 = 0x34, // 4 key + KEY_5 = 0x35, // 5 key + KEY_6 = 0x36, // 6 key + KEY_7 = 0x37, // 7 key + KEY_8 = 0x38, // 8 key + KEY_9 = 0x39, // 9 key + // 0x3A-0x40, // Undefined + KEY_A = 0x41, // A key + KEY_B = 0x42, // B key + KEY_C = 0x43, // C key + KEY_D = 0x44, // D key + KEY_E = 0x45, // E key + KEY_F = 0x46, // F key + KEY_G = 0x47, // G key + KEY_H = 0x48, // H key + KEY_I = 0x49, // I key + KEY_J = 0x4A, // J key + KEY_K = 0x4B, // K key + KEY_L = 0x4C, // L key + KEY_M = 0x4D, // M key + KEY_N = 0x4E, // N key + KEY_O = 0x4F, // O key + KEY_P = 0x50, // P key + KEY_Q = 0x51, // Q key + KEY_R = 0x52, // R key + KEY_S = 0x53, // S key + KEY_T = 0x54, // T key + KEY_U = 0x55, // U key + KEY_V = 0x56, // V key + KEY_W = 0x57, // W key + KEY_X = 0x58, // X key + KEY_Y = 0x59, // Y key + KEY_Z = 0x5A, // Z key + LWIN = 0x5B, // Left Windows key (Microsoft Natural keyboard) + RWIN = 0x5C, // Right Windows key (Natural keyboard) + APPS = 0x5D, // Applications key (Natural keyboard) + // 0x5E, // Reserved + SLEEP = 0x5F, // Computer Sleep key + NUMPAD0 = 0x60, // Numeric keypad 0 key + NUMPAD1 = 0x61, // Numeric keypad 1 key + NUMPAD2 = 0x62, // Numeric keypad 2 key + NUMPAD3 = 0x63, // Numeric keypad 3 key + NUMPAD4 = 0x64, // Numeric keypad 4 key + NUMPAD5 = 0x65, // Numeric keypad 5 key + NUMPAD6 = 0x66, // Numeric keypad 6 key + NUMPAD7 = 0x67, // Numeric keypad 7 key + NUMPAD8 = 0x68, // Numeric keypad 8 key + NUMPAD9 = 0x69, // Numeric keypad 9 key + MULTIPLY = 0x6A, // Multiply key + ADD = 0x6B, // Add key + SEPARATOR = 0x6C, // Separator key + SUBTRACT = 0x6D, // Subtract key + DECIMAL = 0x6E, // Decimal key + DIVIDE = 0x6F, // Divide key + F1 = 0x70, // F1 key + F2 = 0x71, // F2 key + F3 = 0x72, // F3 key + F4 = 0x73, // F4 key + F5 = 0x74, // F5 key + F6 = 0x75, // F6 key + F7 = 0x76, // F7 key + F8 = 0x77, // F8 key + F9 = 0x78, // F9 key + F10 = 0x79, // F10 key + F11 = 0x7A, // F11 key + F12 = 0x7B, // F12 key + F13 = 0x7C, // F13 key + F14 = 0x7D, // F14 key + F15 = 0x7E, // F15 key + F16 = 0x7F, // F16 key + F17 = 0x80, // F17 key + F18 = 0x81, // F18 key + F19 = 0x82, // F19 key + F20 = 0x83, // F20 key + F21 = 0x84, // F21 key + F22 = 0x85, // F22 key, (PPC only) Key used to lock device. + F23 = 0x86, // F23 key + F24 = 0x87, // F24 key + // 0x88-0X8F, // Unassigned + NUMLOCK = 0x90, // NUM LOCK key + SCROLL = 0x91, // SCROLL LOCK key + // 0x92-0x96, // OEM specific + // 0x97-0x9F, // Unassigned + LSHIFT = 0xA0, // Left SHIFT key + RSHIFT = 0xA1, // Right SHIFT key + LCONTROL = 0xA2, // Left CONTROL key + RCONTROL = 0xA3, // Right CONTROL key + LMENU = 0xA4, // Left MENU key + RMENU = 0xA5, // Right MENU key + BROWSER_BACK = 0xA6, // Windows 2000/XP: Browser Back key + BROWSER_FORWARD = 0xA7, // Windows 2000/XP: Browser Forward key + BROWSER_REFRESH = 0xA8, // Windows 2000/XP: Browser Refresh key + BROWSER_STOP = 0xA9, // Windows 2000/XP: Browser Stop key + BROWSER_SEARCH = 0xAA, // Windows 2000/XP: Browser Search key + BROWSER_FAVORITES = 0xAB, // Windows 2000/XP: Browser Favorites key + BROWSER_HOME = 0xAC, // Windows 2000/XP: Browser Start and Home key + VOLUME_MUTE = 0xAD, // Windows 2000/XP: Volume Mute key + VOLUME_DOWN = 0xAE, // Windows 2000/XP: Volume Down key + VOLUME_UP = 0xAF, // Windows 2000/XP: Volume Up key + MEDIA_NEXT_TRACK = 0xB0,// Windows 2000/XP: Next Track key + MEDIA_PREV_TRACK = 0xB1,// Windows 2000/XP: Previous Track key + MEDIA_STOP = 0xB2, // Windows 2000/XP: Stop Media key + MEDIA_PLAY_PAUSE = 0xB3,// Windows 2000/XP: Play/Pause Media key + LAUNCH_MAIL = 0xB4, // Windows 2000/XP: Start Mail key + LAUNCH_MEDIA_SELECT = 0xB5, // Windows 2000/XP: Select Media key + LAUNCH_APP1 = 0xB6, // Windows 2000/XP: Start Application 1 key + LAUNCH_APP2 = 0xB7, // Windows 2000/XP: Start Application 2 key + // 0xB8-0xB9, // Reserved + OEM_1 = 0xBA, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the ';:' key + OEM_PLUS = 0xBB, // Windows 2000/XP: For any country/region, the '+' key + OEM_COMMA = 0xBC, // Windows 2000/XP: For any country/region, the ',' key + OEM_MINUS = 0xBD, // Windows 2000/XP: For any country/region, the '-' key + OEM_PERIOD = 0xBE, // Windows 2000/XP: For any country/region, the '.' key + OEM_2 = 0xBF, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the '/?' key + OEM_3 = 0xC0, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the '`~' key + // 0xC1-0xD7, // Reserved + // 0xD8-0xDA, // Unassigned + OEM_4 = 0xDB, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the '[{' key + OEM_5 = 0xDC, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the '\|' key + OEM_6 = 0xDD, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the ']}' key + OEM_7 = 0xDE, // Used for miscellaneous characters; it can vary by keyboard. + // Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key + OEM_8 = 0xDF, // Used for miscellaneous characters; it can vary by keyboard. + // 0xE0, // Reserved + // 0xE1, // OEM specific + OEM_102 = 0xE2, // Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard + // 0xE3-E4, // OEM specific + PROCESSKEY = 0xE5, // Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key + // 0xE6, // OEM specific + PACKET = 0xE7, // Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP + // 0xE8, // Unassigned + // 0xE9-F5, // OEM specific + ATTN = 0xF6, // Attn key + CRSEL = 0xF7, // CrSel key + EXSEL = 0xF8, // ExSel key + EREOF = 0xF9, // Erase EOF key + PLAY = 0xFA, // Play key + ZOOM = 0xFB, // Zoom key + NONAME = 0xFC, // Reserved + PA1 = 0xFD, // PA1 key + OEM_CLEAR = 0xFE // Clear key + } + } +} diff --git a/src/VolumeControl.cs b/src/VolumeControl.cs new file mode 100755 index 0000000..7cce334 --- /dev/null +++ b/src/VolumeControl.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SpotifyMod.Volume +{ + public class VolumeMixer + { + public static float? GetApplicationVolume(int pid) + { + ISimpleAudioVolume volume = GetVolumeObject(pid); + if (volume == null) + return null; + + float level; + volume.GetMasterVolume(out level); + Marshal.ReleaseComObject(volume); + return level * 100; + } + + public static bool? GetApplicationMute(int pid) + { + ISimpleAudioVolume volume = GetVolumeObject(pid); + if (volume == null) + return null; + + bool mute; + volume.GetMute(out mute); + Marshal.ReleaseComObject(volume); + return mute; + } + + public static void SetApplicationVolume(int pid, float level) + { + ISimpleAudioVolume volume = GetVolumeObject(pid); + if (volume == null) + return; + + Guid guid = Guid.Empty; + volume.SetMasterVolume(level / 100, ref guid); + Marshal.ReleaseComObject(volume); + } + + public static void SetApplicationMute(int pid, bool mute) + { + ISimpleAudioVolume volume = GetVolumeObject(pid); + if (volume == null) + return; + + Guid guid = Guid.Empty; + volume.SetMute(mute, ref guid); + Marshal.ReleaseComObject(volume); + } + + private static ISimpleAudioVolume GetVolumeObject(int pid) + { + // get the speakers (1st render + multimedia) device + IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator()); + IMMDevice speakers; + deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers); + + // activate the session manager. we need the enumerator + Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID; + object o; + speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o); + IAudioSessionManager2 mgr = (IAudioSessionManager2)o; + + // enumerate sessions for on this device + IAudioSessionEnumerator sessionEnumerator; + mgr.GetSessionEnumerator(out sessionEnumerator); + int count; + sessionEnumerator.GetCount(out count); + + // search for an audio session with the required name + // NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2) + ISimpleAudioVolume volumeControl = null; + for (int i = 0; i < count; i++) + { + IAudioSessionControl2 ctl; + sessionEnumerator.GetSession(i, out ctl); + int cpid; + ctl.GetProcessId(out cpid); + + if (cpid == pid) + { + volumeControl = ctl as ISimpleAudioVolume; + break; + } + Marshal.ReleaseComObject(ctl); + } + Marshal.ReleaseComObject(sessionEnumerator); + Marshal.ReleaseComObject(mgr); + Marshal.ReleaseComObject(speakers); + Marshal.ReleaseComObject(deviceEnumerator); + return volumeControl; + } + } + + [ComImport] + [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] + internal class MMDeviceEnumerator + { + } + + internal enum EDataFlow + { + eRender, + eCapture, + eAll, + EDataFlow_enum_count + } + + internal enum ERole + { + eConsole, + eMultimedia, + eCommunications, + ERole_enum_count + } + + [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMMDeviceEnumerator + { + int NotImpl1(); + + [PreserveSig] + int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice); + + // the rest is not implemented + } + + [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMMDevice + { + [PreserveSig] + int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface); + + // the rest is not implemented + } + + [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAudioSessionManager2 + { + int NotImpl1(); + int NotImpl2(); + + [PreserveSig] + int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum); + + // the rest is not implemented + } + + [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAudioSessionEnumerator + { + [PreserveSig] + int GetCount(out int SessionCount); + + [PreserveSig] + int GetSession(int SessionCount, out IAudioSessionControl2 Session); + } + + [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ISimpleAudioVolume + { + [PreserveSig] + int SetMasterVolume(float fLevel, ref Guid EventContext); + + [PreserveSig] + int GetMasterVolume(out float pfLevel); + + [PreserveSig] + int SetMute(bool bMute, ref Guid EventContext); + + [PreserveSig] + int GetMute(out bool pbMute); + } + + [Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAudioSessionControl2 + { + // IAudioSessionControl + [PreserveSig] + int NotImpl0(); + + [PreserveSig] + int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); + + [PreserveSig] + int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext); + + [PreserveSig] + int GetIconPath([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); + + [PreserveSig] + int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext); + + [PreserveSig] + int GetGroupingParam(out Guid pRetVal); + + [PreserveSig] + int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext); + + [PreserveSig] + int NotImpl1(); + + [PreserveSig] + int NotImpl2(); + + // IAudioSessionControl2 + [PreserveSig] + int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); + + [PreserveSig] + int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); + + [PreserveSig] + int GetProcessId(out int pRetVal); + + [PreserveSig] + int IsSystemSoundsSession(); + + [PreserveSig] + int SetDuckingPreference(bool optOut); + } +} +