Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using NAudio.CoreAudioApi;
- using OpenQA.Selenium.Firefox;
- using OpenQA.Selenium.Remote;
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- namespace AvaloniaTray.Services.GlobalKeyboardHook
- {
- public class GlobalKeyboardHookService : IDisposable
- {
- private static IntPtr keyboardHookId = IntPtr.Zero;
- private static IntPtr mouseHookId = IntPtr.Zero;
- private static IntPtr swallowEvent = (IntPtr)1;
- private static LowLevelKeyboardProc keyboardProc = KeyboardHookCallback;
- private static LowLevelMouseProc mouseProc = MouseHookCallback;
- private static MMDeviceEnumerator deviceEnumerator = new MMDeviceEnumerator();
- private static MMDevice device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- private static bool isXButton1Down = false;
- private static bool isXButton2Down = false;
- private static DateTime lastCapsLockPress = DateTime.MinValue;
- private static DateTime lastXButton1Press = DateTime.MinValue;
- private static DateTime lastXButton2Press = DateTime.MinValue;
- private static readonly TimeSpan DoubleTapThreshold = TimeSpan.FromMilliseconds(300);
- private static string processName_Firefox = "firefox";
- public GlobalKeyboardHookService()
- {
- keyboardHookId = SetHook(keyboardProc, WH_KEYBOARD_LL);
- mouseHookId = SetHook(mouseProc, WH_MOUSE_LL);
- }
- public void Dispose()
- {
- UnhookWindowsHookEx(keyboardHookId);
- UnhookWindowsHookEx(mouseHookId);
- }
- private static IntPtr SetHook(Delegate proc, int hookType)
- {
- using (Process curProcess = Process.GetCurrentProcess())
- using (ProcessModule curModule = curProcess.MainModule)
- {
- return SetWindowsHookEx(hookType, proc, GetModuleHandle(curModule.ModuleName), 0);
- }
- }
- private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
- private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
- private static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
- {
- // Check if the hook code is valid and the key event is a key down or system key down event
- if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
- {
- // Read the virtual key code from the lParam
- int vkCode = Marshal.ReadInt32(lParam);
- // Check if the key pressed is Caps Lock
- if (vkCode == VK_CAPITAL)
- {
- // Get the current time
- DateTime now = DateTime.Now;
- // Check if the time difference between the current press and the last press is within the double tap threshold
- if (IsDoubleTap(now) && IsCapsLockHeld() == false)
- {
- WindowMaximizeOrMakeActive("firefox");
- }
- // Update the last Caps Lock press time
- lastCapsLockPress = now;
- }
- // Check if the key pressed is 'B' and Caps Lock is held
- if (vkCode == VK_B && IsCapsLockHeld())
- {
- // Open the Bluetooth devices window
- OpenBluetoothDevices();
- }
- }
- // Call the next hook in the chain
- return CallNextHookEx(keyboardHookId, nCode, wParam, lParam);
- }
- private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
- {
- if (nCode >= 0)
- {
- MSLLHOOKSTRUCT hookStruct = Marshal.PtrToStructure<MSLLHOOKSTRUCT>(lParam);
- if (IsXButtonEvent(wParam))
- {
- HandleXButtonEvent(wParam, hookStruct);
- }
- else if (IsMouseWheelEvent(wParam) && isXButton1Down)
- {
- HandleMouseWheelEvent_WhenXButton1Down(hookStruct);
- return swallowEvent;
- }
- else if (IsMouseWheelEvent(wParam) && isXButton2Down)
- {
- HandleMouseWheelEvent_WhenXButton2Down(hookStruct);
- return swallowEvent;
- }
- else if (IsScrollWheelButtonEvent(wParam) && isXButton1Down)
- {
- ToggleMuteAllWindowInstances(processName_Firefox);
- // desired function here
- }
- else if (IsScrollWheelButtonEvent(wParam) && isXButton2Down)
- {
- ToggleSystemMute();
- return swallowEvent;
- // this needs to be adjusted and thought through
- //HandleScrollWheelButtonEvent(hookStruct);
- //return swallowEvent;
- }
- }
- return CallNextHookEx(mouseHookId, nCode, wParam, lParam);
- }
- private static bool IsXButtonEvent(IntPtr wParam)
- {
- return wParam == (IntPtr)WM_XBUTTONDOWN || wParam == (IntPtr)WM_XBUTTONUP;
- }
- private static bool IsMouseWheelEvent(IntPtr wParam)
- {
- return wParam == (IntPtr)WM_MOUSEWHEEL;
- }
- private static bool IsScrollWheelButtonEvent(IntPtr wParam)
- {
- return wParam == (IntPtr)WM_MBUTTONDOWN;
- }
- private static bool IsDoubleTap(DateTime now)
- {
- return now - lastCapsLockPress < DoubleTapThreshold;
- }
- private static void HandleXButtonEvent(IntPtr wParam, MSLLHOOKSTRUCT hookStruct)
- {
- int mouseButton = HIWORD((int)hookStruct.mouseData);
- if (mouseButton == XBUTTON1)
- {
- if (wParam == (IntPtr)WM_XBUTTONDOWN)
- {
- DateTime now = DateTime.Now;
- if (now - lastXButton1Press < DoubleTapThreshold)
- {
- WindowMaximizeOrMakeActive(processName_Firefox);
- }
- lastXButton1Press = now;
- isXButton1Down = true;
- }
- else if (wParam == (IntPtr)WM_XBUTTONUP)
- {
- isXButton1Down = false;
- }
- }
- else if (mouseButton == XBUTTON2)
- {
- if (wParam == (IntPtr)WM_XBUTTONDOWN)
- {
- DateTime now = DateTime.Now;
- if (now - lastXButton2Press < DoubleTapThreshold)
- {
- // Add any double-tap logic for XBUTTON2 here if needed
- }
- lastXButton2Press = now;
- isXButton2Down = true;
- }
- else if (wParam == (IntPtr)WM_XBUTTONUP)
- {
- isXButton2Down = false;
- }
- if (isXButton1Down)
- {
- //desired function here
- isXButton1Down = false;
- }
- }
- isXButton1Down = IsX1ButtonHeld();
- isXButton2Down = IsX2ButtonHeld();
- }
- private static void HandleMouseWheelEvent_WhenXButton1Down(MSLLHOOKSTRUCT hookStruct)
- {
- int delta = (short)HIWORD((int)hookStruct.mouseData);
- // Adjust process volume
- if (delta > 0)
- {
- AdjustProcessVolume(4, processName_Firefox);
- }
- else if (delta < 0)
- {
- AdjustProcessVolume(-4, processName_Firefox);
- }
- }
- private static void HandleMouseWheelEvent_WhenXButton2Down(MSLLHOOKSTRUCT hookStruct)
- {
- int delta = (short)HIWORD((int)hookStruct.mouseData);
- // Show the small volume control by simulating a left-click on the speaker icon
- const int WM_APPCOMMAND = 0x319;
- const int APPCOMMAND_VOLUME_UP = 0xA0000;
- const int APPCOMMAND_VOLUME_DOWN = 0x90000;
- IntPtr hWnd = FindWindow("Shell_TrayWnd", null);
- if (hWnd != IntPtr.Zero)
- {
- SendMessage(hWnd, WM_APPCOMMAND, IntPtr.Zero, (IntPtr)(delta > 0 ? APPCOMMAND_VOLUME_UP : APPCOMMAND_VOLUME_DOWN));
- }
- // Adjust the system volume
- if (delta > 0)
- {
- AdjustSystemVolume(1);
- }
- else if (delta < 0)
- {
- AdjustSystemVolume(-2);
- }
- }
- private static bool IsCapsLockHeld()
- {
- return (GetKeyState(VK_CAPITAL) & 0x8000) != 0;
- }
- private static bool IsX1ButtonHeld()
- {
- return isXButton1Down;
- }
- private static bool IsX2ButtonHeld()
- {
- return isXButton2Down;
- }
- private static void OpenBluetoothDevices()
- {
- Process.Start(new ProcessStartInfo("ms-settings:bluetooth") { UseShellExecute = true });
- }
- private static void AdjustSystemVolume(int volumeChange)
- {
- float newVolume = device.AudioEndpointVolume.MasterVolumeLevelScalar + (volumeChange / 100.0f);
- device.AudioEndpointVolume.MasterVolumeLevelScalar = Math.Clamp(newVolume, 0.0f, 1.0f);
- }
- private static void AdjustProcessVolume(int volumeChange, string processName)
- {
- var deviceEnumerator = new MMDeviceEnumerator();
- var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- var sessions = defaultDevice.AudioSessionManager.Sessions;
- for (int i = 0; i < sessions.Count; i++)
- {
- var session = sessions[i];
- if (session.GetProcessID == Process.GetProcessesByName(processName).FirstOrDefault()?.Id)
- {
- float newVolume = session.SimpleAudioVolume.Volume + (volumeChange / 100.0f);
- newVolume = Math.Max(0, Math.Min(1, newVolume)); // Ensure volume is between 0 and 1
- session.SimpleAudioVolume.Volume = newVolume;
- }
- }
- }
- private static void ToggleSystemMute()
- {
- device.AudioEndpointVolume.Mute = !device.AudioEndpointVolume.Mute;
- }
- #region Projection mode
- public static void SetProjectionMode(string mode)
- {
- string argument = mode == "extend" ? "/extend" : "/clone";
- Process.Start(new ProcessStartInfo("DisplaySwitch.exe", argument) { UseShellExecute = true });
- }
- #endregion
- private static void WindowMaximizeOrMakeActive(string processName)
- {
- var firefoxWindow = Process.GetProcessesByName(processName).FirstOrDefault();
- if (firefoxWindow != null)
- {
- IntPtr handle = firefoxWindow.MainWindowHandle;
- if (handle != IntPtr.Zero)
- {
- // Check if the window is already maximized
- WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
- GetWindowPlacement(handle, ref placement);
- if (placement.showCmd == SW_SHOWMAXIMIZED)
- {
- // If the window is already maximized, bring it to the foreground
- SetForegroundWindow(handle);
- }
- else
- {
- // Otherwise, maximize the window and bring it to the foreground
- ShowWindow(handle, SW_SHOWMAXIMIZED);
- SetForegroundWindow(handle);
- }
- }
- }
- else
- {
- // If the Firefox window doesn't exist, create a new one
- StartFirefox();
- }
- }
- private static void ToggleMuteAllWindowInstances(string processName)
- {
- var firefoxProcesses = Process.GetProcessesByName(processName);
- foreach (var firefoxProcess in firefoxProcesses)
- {
- IntPtr handle = firefoxProcess.MainWindowHandle;
- if (handle != IntPtr.Zero)
- {
- ToggleMuteForProcess(firefoxProcess);
- }
- }
- }
- private static void ToggleMuteForProcess(Process process)
- {
- var deviceEnumerator = new MMDeviceEnumerator();
- var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- var sessions = defaultDevice.AudioSessionManager.Sessions;
- for (int i = 0; i < sessions.Count; i++)
- {
- var session = sessions[i];
- if (session.GetProcessID == process.Id)
- {
- session.SimpleAudioVolume.Mute = !session.SimpleAudioVolume.Mute;
- }
- }
- }
- private static void StartFirefox()
- {
- string firefoxPath = @"C:\Program Files\Mozilla Firefox\firefox.exe";
- if (File.Exists(firefoxPath))
- {
- Process.Start(firefoxPath);
- }
- }
- public static void ShutdownSystem(int delay)
- {
- Process.Start("shutdown", $"/s /t {delay}");
- }
- public static void ShutdownSystemCancel()
- {
- Process.Start("shutdown", "/a");
- }
- public static int ShutdownSystemGetTimer()
- {
- //doesn't work
- int remainingTime = (int)GetSystemMetrics(SM_SHUTDOWNTIMEOUT);
- return remainingTime;
- }
- #region Selenium firefox control
- private static void HandleScrollWheelButtonEvent(MSLLHOOKSTRUCT hookStruct)
- {
- IntPtr hWnd = WindowFromPoint(hookStruct.pt);
- if (hWnd != IntPtr.Zero)
- {
- uint processId;
- GetWindowThreadProcessId(hWnd, out processId);
- var process = Process.GetProcessById((int)processId);
- if (process.ProcessName.ToLower() == "firefox")
- {
- // Inject JavaScript to mute/unmute the video or active tab
- InjectMuteUnmuteScript(process);
- }
- }
- }
- private static void InjectMuteUnmuteScript(Process process)
- {
- // Connect to the existing Firefox instance with remote debugging enabled
- var options = new FirefoxOptions();
- options.BrowserExecutableLocation = @"C:\Program Files\Mozilla Firefox\firefox.exe";
- options.AddArgument("--remote-debugging-port=9222");
- var driverService = FirefoxDriverService.CreateDefaultService();
- driverService.FirefoxBinaryPath = options.BrowserExecutableLocation;
- driverService.HideCommandPromptWindow = true;
- using (var driver = new RemoteWebDriver(new Uri("http://localhost:9222"), options))
- {
- driver.Navigate().GoToUrl("about:blank"); // Ensure the driver is connected to the Firefox instance
- string script = @"
- var video = document.querySelector('video:hover');
- if (video) {
- if (video.paused) {
- video.play();
- video.muted = false;
- } else {
- video.muted = !video.muted;
- }
- } else {
- var activeTab = document.querySelector('video');
- if (activeTab) {
- if (activeTab.paused) {
- activeTab.play();
- activeTab.muted = false;
- } else {
- activeTab.muted = !activeTab.muted;
- }
- }
- }
- ";
- driver.ExecuteScript(script);
- }
- }
- #endregion
- #region WinAPI
- // Hook types
- private const int WH_KEYBOARD_LL = 13; // Low-level keyboard input events
- private const int WH_MOUSE_LL = 14; // Low-level mouse input events
- // Window messages
- private const int WM_KEYDOWN = 0x0100; // Key down
- private const int WM_SYSKEYDOWN = 0x0104; // System key down
- private const int WM_XBUTTONDOWN = 0x020B; // X button down
- private const int WM_XBUTTONUP = 0x020C; // X button up
- private const int WM_MOUSEWHEEL = 0x020A; // Mouse wheel
- private const int WM_MBUTTONDOWN = 0x0207; // Middle mouse button down
- // X button constants
- private const int XBUTTON1 = 0x0001; // X button 1 (typically the "Back" button on a mouse)
- private const int XBUTTON2 = 0x0002; // X button 2 (typically the "Forward" button on a mouse)
- // Virtual key codes
- private const int VK_CAPITAL = 0x14; // Caps Lock key
- private const int VK_B = 0x42; // 'B' key
- // Show window commands
- private const int SW_HIDE = 0; // Hides the window and activates another window.
- private const int SW_SHOWNORMAL = 1; // Activates and displays a window. Restores it to its original size and position if minimized or maximized.
- private const int SW_SHOWMINIMIZED = 2; // Activates the window and displays it as a minimized window.
- private const int SW_SHOWMAXIMIZED = 3; // Activates the window and displays it as a maximized window.
- private const int SW_SHOWNOACTIVATE = 4; // Displays a window in its most recent size and position without activating it.
- private const int SW_SHOW = 5; // Activates the window and displays it in its current size and position.
- private const int SW_MINIMIZE = 6; // Minimizes the specified window and activates the next top-level window in the Z order.
- private const int SW_SHOWMINNOACTIVE = 7; // Displays the window as a minimized window without activating it.
- private const int SW_SHOWNA = 8; // Displays the window in its current size and position without activating it.
- private const int SW_RESTORE = 9; // Activates and displays the window. Restores it to its original size and position if minimized or maximized.
- private const int SW_SHOWDEFAULT = 10; // Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to CreateProcess.
- private const int SW_FORCEMINIMIZE = 11; // Minimizes a window, even if the thread that owns the window is not responding. Used only when minimizing windows from a different thread.
- // shutdown timer
- const int SM_SHUTTINGDOWN = 0x2000;
- const int SM_SHUTDOWNTIMEOUT = 0x2001;
- // Helper method to extract high-order word
- private static int HIWORD(int n)
- {
- return (n >> 16) & 0xffff;
- }
- [DllImport("user32.dll", CharSet = CharSet.Auto)]
- private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr SetWindowsHookEx(int idHook, Delegate lpfn, IntPtr hMod, uint dwThreadId);
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool UnhookWindowsHookEx(IntPtr hhk);
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
- [DllImport("user32.dll", CharSet = CharSet.Auto)]
- private static extern short GetKeyState(int nVirtKey);
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr GetModuleHandle(string lpModuleName);
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool SetForegroundWindow(IntPtr hWnd);
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
- [DllImport("user32.dll")]
- private static extern IntPtr WindowFromPoint(POINT Point);
- [DllImport("user32.dll")]
- private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
- [DllImport("user32")]
- static extern int GetSystemMetrics(int nIndex);
- // Struct for mouse hook
- [StructLayout(LayoutKind.Sequential)]
- private struct MSLLHOOKSTRUCT
- {
- public POINT pt; // Mouse coordinates
- public uint mouseData; // Mouse data
- public uint flags; // Event-injected flags
- public uint time; // Timestamp
- public IntPtr dwExtraInfo; // Extra information
- }
- // Struct for point coordinates
- [StructLayout(LayoutKind.Sequential)]
- private struct POINT
- {
- public int x; // X coordinate
- public int y; // Y coordinate
- }
- [StructLayout(LayoutKind.Sequential)]
- private struct WINDOWPLACEMENT
- {
- public int length;
- public int flags;
- public int showCmd;
- public POINT ptMinPosition;
- public POINT ptMaxPosition;
- public RECT rcNormalPosition;
- }
- [StructLayout(LayoutKind.Sequential)]
- private struct RECT
- {
- public int left;
- public int top;
- public int right;
- public int bottom;
- }
- #endregion
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment