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 = WhichScreen(cursor); SnagScreen mouseScreen = 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 = 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) { NewCursor = mouse; } else if (jumpScreen != null) { NewCursor = jumpScreen.R.ClosestBoundaryPoint(cursor); } else if (StuckDirection.X != 0) { NewCursor = 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); } }
// 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); } }
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 { 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 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". 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))); } // We should never get here, but if we do, just return the current // Cursor location. return(Cursor); }