public void Start() { // DPI Awareness API is not available on older OS's, but they work in // physical pixels anyway, so we just ignore if the call fails. try { SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware); } catch (DllNotFoundException) { } SnagScreen.Init(Screen.AllScreens); // Get notified of any screen configuration changes. SystemEvents.DisplaySettingsChanged += Event_DisplaySettingsChanged; // Keep a reference to the delegate, so it does not get garbage collected. MouseHookDelegate = LLMouseHookCallback; // Set the mouse hook delegate using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { if (ThisModHandle == IntPtr.Zero) { ThisModHandle = GetModuleHandle(curModule.ModuleName); } LLMouse_hookhand = SetWindowsHookEx(WH_MOUSE_LL, MouseHookDelegate, ThisModHandle, 0); } }
private void Event_DisplaySettingsChanged(object sender, EventArgs e) { UpdatingDisplaySettings = true; Debug.WriteLine("\nDisplay Settings Changed..."); SnagScreen.Init(Screen.AllScreens); Debug.WriteLine(SnagScreen.GetScreenInformation()); UpdatingDisplaySettings = false; }
void Event_DisplaySettingsChanged(object sender, EventArgs e) { UpdatingDisplaySettings = true; //Console.WriteLine("\nDisplay Settings Changed..."); //ShowScreens (); SnagScreen.Init(Screen.AllScreens); SnagScreen.ShowAll(); UpdatingDisplaySettings = false; }
// CheckJumpCursor() returns TRUE, ONLY if the cursor is "stuck". By "stuck" we // specifically mean that the user is trying to move the mouse beyond the boundaries of // the screen currently containing the cursor. This is determined when the *current* // cursor position does not equal the *previous* mouse position. If there is another // adjacent screen (or a "wrap around" screen), then we can consider moving the mouse // onto that screen. // // Note that this is ENTIRELY a *GEOMETRIC* method. Screens are "rectangles", and the // cursor and mouse are "points." The mouse/cursor hardware interaction (obtaining // current mouse and cursor information) is handled in routines further below, and any // Screen changes are handled by the DisplaySettingsChanged event. There are no // hardware or OS/Win32 references or interactions here. bool CheckJumpCursor(Point mouse, Point cursor, out Point NewCursor) { NewCursor = cursor; // Default is to not move cursor. // Gather pertinent information about cursor, mouse, screens. Point Dir = Direction(cursor, mouse); SnagScreen cursorScreen = SnagScreen.WhichScreen(cursor); SnagScreen mouseScreen = SnagScreen.WhichScreen(mouse); bool IsStuck = (cursor != LastMouse) && (mouseScreen != cursorScreen); Point StuckDirection = OutsideDirection(cursorScreen.R, mouse); string StuckString = IsStuck ? "--STUCK--" : " "; // Console.Write ($" FarOut{StuckDirection}/{OutsideDis//tance(cursorScreen.R, mouse)} " + // $"mouse:{mouse} cursor:{cursor} (OnMon#{cursorScreen}/{mouseScreen}) last:{LastMouse} " + // $"#UnSnags {NJumps} {StuckString} \r"); Console.Write($" StuckDirection/Distance{StuckDirection}/{OutsideDistance(cursorScreen.R, mouse)} " + $"cur_mouse:{mouse} prev_mouse:{LastMouse} ==? cursor:{cursor} (OnMon#{cursorScreen}/{mouseScreen}) " + $"#UnSnags {NJumps} {StuckString} \r"); LastMouse = mouse; // Let caller know we did NOT jump the cursor. if (!IsStuck) { return(false); } SnagScreen jumpScreen = SnagScreen.ScreenInDirection(StuckDirection, cursorScreen.R); // If the mouse "location" (which can take on a value beyond the current // cursor screen) has a value, then it is "within" another valid screen // bounds, so just jump to it! if (EnableWrap && mouseScreen != null) { NewCursor = mouse; } else if (jumpScreen != null) { NewCursor = jumpScreen.R.ClosestBoundaryPoint(cursor); } else if (EnableWrap && StuckDirection.X != 0) { NewCursor = SnagScreen.WrapPoint(StuckDirection, cursor); } else { return(false); } ++NJumps; Console.Write($"\n -- JUMPED!!! --\n"); return(true); }
// Loop through Screen.AllScreens[] array to initialize ourselves. public static void Init(Screen[] AllScreens) { var N = AllScreens.Length; TopMost = new List <SnagScreen>(); BottomMost = new List <SnagScreen>(); LeftMost = new List <SnagScreen>(); RightMost = new List <SnagScreen>(); BoundingBox = new Rectangle(0, 0, 0, 0); // First pass, populate our All[] array with all the screens. All = new SnagScreen[N]; for (int i = 0; i < N; ++i) { All[i] = new SnagScreen(Screen.AllScreens[i], i); } // Now determine their geometric relationships. Yes this is O(N^2), but // usually N (number of monitors) is not too large. There may be more // efficient approaches, but this is very simple, clear, and // straightforward, and it is not called often (only when program // starts, and after any change in monitor configuration). foreach (var SN in All) { // Add direction from this SN screen to each of the other screens. foreach (var s in All) { SN.AddDirectionTo(s); } // Where appropriate, add ourselves to the lists of outermost screens. if (SN.IsLeftmost) { LeftMost.Add(SN); } if (SN.IsRightmost) { RightMost.Add(SN); } if (SN.IsTopmost) { TopMost.Add(SN); } if (SN.IsBottommost) { BottomMost.Add(SN); } BoundingBox = Rectangle.Union(BoundingBox, SN.R); } }
// Find the first monitor (first one we come across in the for() loop) // that is in the direction of the point. public static SnagScreen ScreenInDirection(Point mouse, Rectangle CurScreen, out Point Dir) { Dir = OutsideDirection(CurScreen, mouse); // Screen must be strictly above/below/beside. For instance, for a monitor to be // "above", the monitor's Bottom equal to the current screen's Top ("current // screen" is where the Cursor (NOT the mouse!!) is currently). List <SnagScreen> ScreensInDirection = new List <SnagScreen>(); foreach (var S in All) { if (Dir.X == 1 && CurScreen.Right == S.R.Left) { ScreensInDirection.Add(S); } if (Dir.X == -1 && CurScreen.Left == S.R.Right) { ScreensInDirection.Add(S); } if (Dir.Y == 1 && CurScreen.Bottom == S.R.Top) { ScreensInDirection.Add(S); } if (Dir.Y == -1 && CurScreen.Top == S.R.Bottom) { ScreensInDirection.Add(S); } } SnagScreen ClosestScreenInDirection = null; double DistanceToClosestScreen = float.MaxValue; foreach (var S in ScreensInDirection) { double DistanceToScreen = S.DistanceToPoint(mouse); if (DistanceToScreen < DistanceToClosestScreen) { DistanceToClosestScreen = DistanceToScreen; ClosestScreenInDirection = S; } } return(ClosestScreenInDirection); }
// If s is immediately adjacent to (shares a border with) us, then add it to the // appropriate direction list. If s is not "touching" us, then it will not get added to // any list. s can be added to at most one list (hence use of "else if" instead of just // a sequence of "if's"). public void AddDirectionTo(SnagScreen s) { if ((R.Right == s.R.Left) && OverlapY(R, s.R)) { ToRight.Add(s); } else if ((R.Left == s.R.Right) && OverlapY(R, s.R)) { ToLeft.Add(s); } else if ((R.Top == s.R.Bottom) && OverlapX(R, s.R)) { Above.Add(s); } else if ((R.Bottom == s.R.Top) && OverlapX(R, s.R)) { Below.Add(s); } }
// Find the best point to "wrap" around the cursor, either horizontally or // vertically. We consider only the "OuterMost" screens. For instance, if // the mouse is moving to the left, we consider only the screens in the // RightMost[] array. public static Point WrapPoint(Point Dir, Point Cursor) { int DistClosest = int.MaxValue; SnagScreen WS = null; // Our "wrap screen". // Wrap horizontally if (Dir.X != 0) { // Find closest Left- or Right-most screen, in Y direction. foreach (var S in (Dir.X == 1 ? LeftMost : RightMost)) { int dist = Math.Abs(OutsideYDistance(S.R, Cursor)); if (dist < DistClosest) { DistClosest = dist; WS = S; } } return(WS.R.ClosestBoundaryPoint(new Point(Dir.X == 1 ? WS.R.Left : WS.R.Right, Cursor.Y))); } // Wrap vertically if (Dir.Y != 0) { // Find closest Top- or Bottom-most screen, in X direction foreach (var S in (Dir.Y == 1 ? TopMost : BottomMost)) { int dist = Math.Abs(OutsideXDistance(S.R, Cursor)); if (dist < DistClosest) { DistClosest = dist; WS = S; } } return(WS.R.ClosestBoundaryPoint(new Point(Cursor.X, Dir.Y == 1 ? WS.R.Top : WS.R.Bottom))); } // We should never get here, but if we do, just return the current // Cursor location. return(Cursor); }
public void Run(string[] args) { // DPI Awareness API is not available on older OS's, but they work in // physical pixels anyway, so we just ignore if the call fails. try { SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware); } catch (System.DllNotFoundException) { //Console.WriteLine("No SHCore.DLL. No problem."); } // Make sure we catch CTRL-C hard-exit of program. CTRL_C_handler = new ConsoleEventDelegate(ConsoleEventCallback); SetConsoleCtrlHandler(CTRL_C_handler, true); //ShowScreens (); SnagScreen.Init(Screen.AllScreens); SnagScreen.ShowAll(); // Get notified of any screen configuration changes. SystemEvents.DisplaySettingsChanged += Event_DisplaySettingsChanged; //ShowWindow(GetConsoleWindow(), SW_HIDE); // Keep a reference to the delegate, so it does not get garbage collected. MouseHookDelegate = LLMouseHookCallback; LLMouse_hookhand = SetHook(WH_MOUSE_LL, MouseHookDelegate); //Console.WriteLine(""); // This is the one that runs "forever" while the application is alive, and handles // events, etc. This application is ABSOLUTELY ENTIRELY driven by the LLMouseHook // and DisplaySettingsChanged events. Application.Run(); //Console.WriteLine("Exiting!!!"); UnsetHook(ref LLMouse_hookhand); }
// May want to update the above routine, which arbitrarily selects the monitor that // happens to come first in the for() loop. We should probably do a little extra work, // and select the monitor that is closest to the mouse position. // Find the monitor that is closest to the point. //public static SnagScreen ScreenInDirection() //{ //} // Find the best screen to "wrap" around the cursor, either horizontally or // vertically. We consider only the "OuterMost" screens. For instance, if // the mouse is moving to the left, we consider only the screens in the // RightMost[] array. public static SnagScreen WrapScreen(Point Dir, Point Cursor) { int DistClosest = int.MaxValue; SnagScreen WS = null; // Our "wrap screen". if (Dir.X != 0) { // Find closest Left- or Right-most screen, in Y direction. foreach (var S in (Dir.X == 1 ? LeftMost : RightMost)) { int dist = Math.Abs(GeometryUtil.OutsideYDistance(S.R, Cursor)); if (dist < DistClosest) { DistClosest = dist; WS = S; } } return(WS); } // We should never get here, but if we do, just return the first screen. return(All[0]); }
// CheckJumpCursor() returns TRUE, ONLY if the cursor is "stuck". By "stuck" we // specifically mean that the user is trying to move the mouse beyond the boundaries of // the screen currently containing the cursor. This is determined when the *current* // cursor position does not equal the *previous* mouse position. If there is another // adjacent screen (or a "wrap around" screen), then we can consider moving the mouse // onto that screen. // // Note that this is ENTIRELY a *GEOMETRIC* method. Screens are "rectangles", and the // cursor and mouse are "points." The mouse/cursor hardware interaction (obtaining // current mouse and cursor information) is handled in routines further below, and any // Screen changes are handled by the DisplaySettingsChanged event. There are no // hardware or OS/Win32 references or interactions here. private bool CheckJumpCursor(Point mouse, Point cursor, out Point NewCursor) { NewCursor = cursor; // Default is to not move cursor. // Gather pertinent information about cursor, mouse, screens. SnagScreen cursorScreen = SnagScreen.WhichScreen(cursor); SnagScreen mouseScreen = SnagScreen.WhichScreen(mouse); bool IsStuck = (cursor != LastMouse) && (mouseScreen != cursorScreen); Point StuckDirection = GeometryUtil.OutsideDirection(cursorScreen.R, mouse); Debug.WriteLine($" StuckDirection/Distance{StuckDirection}/{GeometryUtil.OutsideDistance(cursorScreen.R, mouse)} " + $"cur_mouse:{mouse} prev_mouse:{LastMouse} ==? cursor:{cursor} (OnMon#{cursorScreen}/{mouseScreen}) " + $"#UnSnags {NJumps} {(IsStuck ? "--STUCK--" : " ")} "); LastMouse = mouse; // Let caller know we did NOT jump the cursor. if (!IsStuck) { return(false); } SnagScreen jumpScreen = SnagScreen.ScreenInDirection(StuckDirection, cursorScreen.R); // If the mouse "location" (which can take on a value beyond the current // cursor screen) has a value, then it is "within" another valid screen // bounds, so just jump to it! if (mouseScreen != null) { if (!IsUnstickEnabled) { return(false); } NewCursor = mouse; } else if (jumpScreen != null) { if (!IsJumpEnabled) { return(false); } NewCursor = jumpScreen.R.ClosestBoundaryPoint(cursor); } else if (StuckDirection.X != 0) { if (!IsScreenWrapEnabled) { return(false); } SnagScreen wrapScreen = SnagScreen.WrapScreen(StuckDirection, cursor); Point wrapPoint = new Point( StuckDirection.X == 1?wrapScreen.R.Left:wrapScreen.R.Right - 1, cursor.Y); // Don't wrap cursor if jumping is disabled and it would need to jump. if (!IsJumpEnabled && !wrapScreen.R.Contains(wrapPoint)) { return(false); } NewCursor = wrapScreen.R.ClosestBoundaryPoint(wrapPoint); } else { return(false); } ++NJumps; Debug.WriteLine("\n -- JUMPED!!! --"); return(true); }
private void Run(string[] args) { // DPI Awareness API is not available on older OS's, but they work in // physical pixels anyway, so we just ignore if the call fails. try { NativeMethods.SetProcessDpiAwareness(NativeMethods.PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware); } catch (DllNotFoundException) { Debug.WriteLine("No SHCore.DLL. No problem."); } // Parse command line arguments foreach (string arg in args) { switch (arg) { case "-s": IsUnstickEnabled = false; break; case "+s": IsUnstickEnabled = true; break; case "-j": IsJumpEnabled = false; break; case "+j": IsJumpEnabled = true; break; case "-w": IsScreenWrapEnabled = false; break; case "+w": IsScreenWrapEnabled = true; break; default: string exeName = Environment.GetCommandLineArgs()[0]; Console.WriteLine($"Usage: {exeName} [options ...]"); Console.WriteLine("\t-s Disables mouse un-sticking."); Console.WriteLine("\t+s Enables mouse un-sticking. Default."); Console.WriteLine("\t-j Disables mouse jumping."); Console.WriteLine("\t+j Enables mouse jumping. Default."); Console.WriteLine("\t-w Disables mouse wrapping."); Console.WriteLine("\t+w Enables mouse wrapping. Default."); Environment.Exit(1); break; } } SnagScreen.Init(Screen.AllScreens); Debug.WriteLine(SnagScreen.GetScreenInformation()); // Get notified of any screen configuration changes. SystemEvents.DisplaySettingsChanged += Event_DisplaySettingsChanged; // Keep a reference to the delegate, so it does not get garbage collected. MouseHookDelegate = LLMouseHookCallback; LLMouse_hookhand = SetHook(NativeMethods.WH_MOUSE_LL, MouseHookDelegate); // This is the one that runs "forever" while the application is alive, and handles // events, etc. This application is ABSOLUTELY ENTIRELY driven by the LLMouseHook // and DisplaySettingsChanged events. Application.Run(new MyCustomApplicationContext(this)); Debug.WriteLine("Exiting!!!"); UnsetHook(ref LLMouse_hookhand); SystemEvents.DisplaySettingsChanged -= Event_DisplaySettingsChanged; }
// CheckJumpCursor() returns TRUE, ONLY if the cursor is "stuck". By "stuck" we // specifically mean that the user is trying to move the mouse beyond the boundaries of // the screen currently containing the cursor. This is determined when the *current* // cursor position does not equal the *previous* mouse position. If there is another // adjacent screen (or a "wrap around" screen), then we can consider moving the mouse // onto that screen. // // Note that this is ENTIRELY a *GEOMETRIC* method. Screens are "rectangles", and the // cursor and mouse are "points." The mouse/cursor hardware interaction (obtaining // current mouse and cursor information) is handled in routines further below, and any // Screen changes are handled by the DisplaySettingsChanged event. There are no // hardware or OS/Win32 references or interactions here. bool CheckJumpCursor(Point mouse, Point cursor, out Point NewCursor) { NewCursor = cursor; // Default is to not move cursor. // Gather pertinent information about cursor, mouse, screens. SnagScreen cursorScreen = SnagScreen.WhichScreen(cursor); SnagScreen mouseScreen = SnagScreen.WhichScreen(mouse); // If the screen hasn't changed then we don't need to do anything if (mouseScreen == cursorScreen) { return(false); } // If the mouse "location" (which can take on a value beyond the current // cursor screen) has a value, then it is "within" another valid screen // bounds, so just jump to it! Point StuckDirection = Point.Empty; if (mouseScreen == null) { mouseScreen = SnagScreen.ScreenInDirection( mouse, cursorScreen.R, out StuckDirection ); } // If the screen is stuck in a direction off the screen we'll need to check the // config for if the user wants to wrap it in that direction if ( mouseScreen == null && StuckDirection.X != 0 && bool.TryParse(Config.Read(Config.CONFIG_HORIZONTAL_WRAP), out bool hWrap) && hWrap ) { mouse = SnagScreen.WrapPoint(StuckDirection, cursor); mouseScreen = SnagScreen.WhichScreen(NewCursor); } else if ( mouseScreen == null && StuckDirection.Y != 0 && bool.TryParse(Config.Read(Config.CONFIG_VERTICAL_WRAP), out bool vWrap) && vWrap ) { mouse = SnagScreen.WrapPoint(StuckDirection, cursor); mouseScreen = SnagScreen.WhichScreen(NewCursor); } else if ( mouseScreen == null ) { return(false); } // We'll move the curesor smoothly between screens of different resolution int scaledCursorX = mouse.X, scaledCursorY = mouse.Y; if ( cursorScreen.ToLeft.Contains(mouseScreen) || cursorScreen.ToRight.Contains(mouseScreen) ) { scaledCursorY = (int)(((double)(cursor.Y - cursorScreen.R.Top) * mouseScreen.R.Height / cursorScreen.R.Height) + mouseScreen.R.Top); } if ( cursorScreen.Above.Contains(mouseScreen) || cursorScreen.Below.Contains(mouseScreen) ) { scaledCursorX = (int)(((double)(cursor.X - cursorScreen.R.Left) * mouseScreen.R.Width / cursorScreen.R.Width) + mouseScreen.R.Left); } NewCursor = new Point(scaledCursorX, scaledCursorY); return(true); }
void Event_DisplaySettingsChanged(object sender, EventArgs e) { UpdatingDisplaySettings = true; SnagScreen.Init(Screen.AllScreens); UpdatingDisplaySettings = false; }