예제 #1
0
        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);
                }
        }
예제 #2
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;
 }
예제 #3
0
 void Event_DisplaySettingsChanged(object sender, EventArgs e)
 {
     UpdatingDisplaySettings = true;
     //Console.WriteLine("\nDisplay Settings Changed...");
     //ShowScreens ();
     SnagScreen.Init(Screen.AllScreens);
     SnagScreen.ShowAll();
     UpdatingDisplaySettings = false;
 }
예제 #4
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.
        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);
        }
예제 #5
0
        // 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);
            }
        }
예제 #6
0
        // 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);
        }
예제 #7
0
 // 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);
     }
 }
예제 #8
0
        // 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);
        }
예제 #9
0
        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);
        }
예제 #10
0
        // 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]);
        }
예제 #11
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);
        }
예제 #12
0
        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;
        }
예제 #13
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.
        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);
        }
예제 #14
0
 void Event_DisplaySettingsChanged(object sender, EventArgs e)
 {
     UpdatingDisplaySettings = true;
     SnagScreen.Init(Screen.AllScreens);
     UpdatingDisplaySettings = false;
 }