From defd833f468a031a01f3b6a771dbdf80a11e6b74 Mon Sep 17 00:00:00 2001 From: Baal Evan Date: Fri, 31 Jul 2015 22:50:40 +0200 Subject: [PATCH] Added HotCorners Close #1 --- FloatingClock/App.xaml | 3 +- FloatingClock/App.xaml.cs | 8 ++ FloatingClock/FloatingClock.csproj | 1 + FloatingClock/MainWindow.xaml.cs | 11 +- FloatingClock/MouseHook.cs | 192 +++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 FloatingClock/MouseHook.cs diff --git a/FloatingClock/App.xaml b/FloatingClock/App.xaml index 1c69a64..4ad0244 100644 --- a/FloatingClock/App.xaml +++ b/FloatingClock/App.xaml @@ -2,7 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FloatingClock" - StartupUri="MainWindow.xaml"> + StartupUri="MainWindow.xaml" + Exit="App_OnExit"> diff --git a/FloatingClock/App.xaml.cs b/FloatingClock/App.xaml.cs index 2b94f3d..71d894d 100644 --- a/FloatingClock/App.xaml.cs +++ b/FloatingClock/App.xaml.cs @@ -7,5 +7,13 @@ namespace FloatingClock /// public partial class App : Application { + /// + /// Unhook Mouse On Application Exit + /// + private void App_OnExit(object sender, ExitEventArgs e) + { + MouseHook.UnhookWindowsHookEx(MouseHook._hookID); + + } } } \ No newline at end of file diff --git a/FloatingClock/FloatingClock.csproj b/FloatingClock/FloatingClock.csproj index 9cc7738..40eefb5 100644 --- a/FloatingClock/FloatingClock.csproj +++ b/FloatingClock/FloatingClock.csproj @@ -77,6 +77,7 @@ + Code diff --git a/FloatingClock/MainWindow.xaml.cs b/FloatingClock/MainWindow.xaml.cs index fbbb536..3c1cdba 100644 --- a/FloatingClock/MainWindow.xaml.cs +++ b/FloatingClock/MainWindow.xaml.cs @@ -17,6 +17,8 @@ public partial class MainWindow { private NotifyIcon notifyIcon; private DispatcherTimer refreshDispatcher; + public static MainWindow current; + public static bool IsVisible; /// /// Initialize Application and Main Window @@ -24,18 +26,22 @@ public partial class MainWindow public MainWindow() { InitializeComponent(); + current = this; Refresh(); ShowClock(); InitializeRefreshDispatcher(); WaitToFullMinuteAndRefresh(); new HotKey(Key.C, KeyModifier.Alt, key => ShowClock()); TrayIcon(); + + MouseHook._hookID = MouseHook.SetHook(MouseHook._proc); + } /// /// Prepare Clock to Show /// - private void ShowClock() + public void ShowClock() { SetPositionOnCurrentDisplay(); Refresh(); @@ -131,7 +137,9 @@ private void InitializeAnimationIn() dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 5); Application.Current.MainWindow.Visibility = Visibility.Visible; + IsVisible = true; dispatcherTimer.Start(); + } /// @@ -157,6 +165,7 @@ private void Window_Deactivated(object sender, EventArgs e) dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 15); dispatcherTimer.Start(); + IsVisible = false; refreshDispatcher.Stop(); } diff --git a/FloatingClock/MouseHook.cs b/FloatingClock/MouseHook.cs new file mode 100644 index 0000000..ccca594 --- /dev/null +++ b/FloatingClock/MouseHook.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace FloatingClock +{ + public static class MouseHook + { + + + /// + /// The _proc type defines a pointer to this callback function. + /// + public static readonly LowLevelMouseProc _proc = HookCallback; + + /// + /// Pointer Hook ID + /// + public static IntPtr _hookID = IntPtr.Zero; + + /// + /// Bool with information about state of corner + /// + private static bool cornerIsActive; + + /// + /// If Corner is Active Wait for 2 sec and Disable it + /// + private static async void DisableCorner() + { + if (!cornerIsActive) return; + await Task.Delay(2 * 1000); + cornerIsActive = false; + } + + /// + /// Get Current Process and Module and Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread + /// + /// HookCallback + /// + public static IntPtr SetHook(LowLevelMouseProc proc) + { + using (var curProcess = Process.GetCurrentProcess()) + using (var curModule = curProcess.MainModule) + { + return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0); + } + } + + public delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); + + + + /// + /// Low Level Mouse Proc - callback function used with the SetWindowsHookEx function. The system calls this function every time a new mouse input event is about to be posted into a thread input queue. + /// + /// A code the hook procedure uses to determine how to process the message. If nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx. This parameter can be one of the following values. + /// The identifier of the mouse message. This parameter can be one of the following messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP. + /// A pointer to an MSLLHOOKSTRUCT structure. + /// + private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) + { + if (MainWindow.IsVisible || nCode < 0) return CallNextHookEx(_hookID, nCode, wParam, lParam); + var hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); + var activeScreen = Screen.FromPoint(Control.MousePosition); + if (hookStruct.pt.x >= activeScreen.Bounds.X + activeScreen.Bounds.Width - 25) + { + if ( + (hookStruct.pt.y <= activeScreen.Bounds.Y + 25) + || + (hookStruct.pt.y >= activeScreen.Bounds.Y + activeScreen.Bounds.Height - 25) + ) + { + cornerIsActive = true; + } + else + { + DisableCorner(); + } + if (!cornerIsActive || (hookStruct.pt.y < activeScreen.Bounds.Y + (activeScreen.Bounds.Height / 5)) || + (hookStruct.pt.y > + activeScreen.Bounds.Y + activeScreen.Bounds.Height - (activeScreen.Bounds.Height / 5))) + return CallNextHookEx(_hookID, nCode, wParam, lParam); + MainWindow.current.ShowClock(); + cornerIsActive = false; + } + else + { + DisableCorner(); + } + return CallNextHookEx(_hookID, nCode, wParam, lParam); + } + + + /// + /// The WH_MOUSE_LL (id 14) hook enables you to monitor mouse input events about to be posted in a thread input queue. + /// + private const int WH_MOUSE_LL = 14; + + /// + /// Type of MouseEvent + /// + public enum MouseMessages + { + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_MOUSEMOVE = 0x0200, + WM_MOUSEWHEEL = 0x020A, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205 + } + + + /// + /// The POINT structure defines the x- and y- coordinates of a point. + /// + [StructLayout(LayoutKind.Sequential)] + private struct POINT + { + public int x; + public int y; + } + + + /// + /// Contains information about a low-level mouse input event. + /// + [StructLayout(LayoutKind.Sequential)] + private struct MSLLHOOKSTRUCT + { + public POINT pt; + private uint mouseData; + private uint flags; + private uint time; + private IntPtr dwExtraInfo; + } + + + /// + /// Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread. + /// + /// The type of hook procedure to be installed. This parameter can be one of the following values. + /// A pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process. + /// A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process. + /// The identifier of the thread with which the hook procedure is to be associated. For desktop apps, if this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. For Windows Store apps, see the Remarks section. + /// If the function succeeds, the return value is the handle to the hook procedure. If the function fails, the return value is NULL.To get extended error information, call GetLastError. + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr SetWindowsHookEx(int idHook, + LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); + + + + /// + /// Removes a hook procedure installed in a hook chain by the SetWindowsHookEx function. + /// + /// A handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx. + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UnhookWindowsHookEx(IntPtr hhk); + + + + /// + /// Passes the hook information to the next hook procedure in the current hook chain. A hook procedure can call this function either before or after processing the hook information. + /// + /// This parameter is ignored. + /// The hook code passed to the current hook procedure. The next hook procedure uses this code to determine how to process the hook information. + /// The wParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain. + /// The lParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain. + /// This value is returned by the next hook procedure in the chain. The current hook procedure must also return this value. The meaning of the return value depends on the hook type. For more information, see the descriptions of the individual hook procedures. + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, + IntPtr wParam, IntPtr lParam); + + + /// + /// Retrieves a module handle for the specified module. The module must have been loaded by the calling process. + /// + /// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default library extension .dll is appended. The file name string can include a trailing point character (.) to indicate that the module name has no extension. The string does not have to specify a path. When specifying a path, be sure to use backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules currently mapped into the address space of the calling process. + /// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return value is NULL.To get extended error information, call GetLastError. + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr GetModuleHandle(string lpModuleName); + } + + +}