Detecting mouse-cursor in screen corners in High-DPI

Hi everyone,

I’m writing an application that follows the coordinates of the mouse cursor and takes various actions when it is in one of the corners of the screen.

I try to detect the corners by getting the sum of the widths of the displays and the sum of the heights of the displays:

Function GetDisplaysWidth as integer
dim i as integer, w as integer
for i=1 to ScreenCount
next i
Return w
End Function

I’m getting tripped up by variable DPIs and screen positions, however.

On one of my test systems, I have two monitors, one is a high-DPI 4K monitor, the other is a regular 96dpi 1600x1200 monitor. I’m not able to calculate points for my mouse position when the mouse tracks into a second monitor because ScaleX and ScaleY of a picture only seem to return values for the monitor the application was started on.

Secondly, the “arrangement” of the monitors is messing me up as well. On this system, the second screen is smaller than the first, so its max y coordinate is less than the max y coordinate of the first monitor, which means the visible area is not a perfect rectangle. Unfortunately, my algorithm above for calculating the sum of the resolutions of the displays only works if both screens form a perfect rectangle.

However on some systems, the monitor arrangement could be such that the second monitor is actually positioned above the first, meaning the top of the visible screen actually has a negative point/pixel value. How do I detect for this?

This is getting exceedingly complicated for something that seems like it should be easy: Detecting when the mouse pointer is in the corner of a screen.

Could anyone recommend a different approach to this?

Thanks in advance!

Here you go:

This is just a quick demo. It looks like there’s a rounding issue with System.MouseX and System.MouseY so you might need to “fudge” the width and height check by another pixel. This can be seen if you set a monitor to a scale that ends up as a decimal width, the right sides are never “hit”. Screen sizes that scale to whole numbers work perfectly. This might be worth a feedback ticket if there’s not one already.

Oh there’s LOTS of really fun ones like

|            |
|            |
|            |
|            |
             |            |
             |            |
             |            |
             |            |
                          |            |
                          |            |
                          |            |
                          |            |
|            |            |
|            |            |
|            |            |
|            |            |
             |            |
             |            |
             |            |
             |            |

and just about everything else you can imagine

My project above works for the most part except for an edge case where a hidpi resolution is selected that returns a decimal dimension that is >0.5, e.g. 2560x1600 @ 150%.

This is a bug in the Xojo framework, I have reported it here: <>

The project will work flawlessly when this problem is fixed (fingers crossed) :slight_smile:

This is fantastic, thank you so much! It does seem to work for the most part, with a few oddities.

On my test system, the “hotspot size” variable you put in there indeed compensates for the bug at the expense of a bit of precision.

The timer event reports that the mouse cursor crosses from Screen 0 into Screen 1 waaaaay too early (after 1920 points when there are actually 2200 on screen 0), I assume this is due to the same bug?

Here is video of the issue I’m getting.

It appears Screen(0).width is returning too small of a value. You can see that “Corner=1” is triggered when the mouse has not yet reached the right side of the screen.

Scaling on this screen is set to 175%, but the monitor is 3840x2160 native resolution. Screen(0).width returns 1920. Not sure whose error this is.

Hmm, it shouldnt be that far out. I have the rez of your screens from the OP, could you let me know what scaling you have set for each and what is your primary?

3840x2160 @ 175
1600x1200 @ 100

Can you take a screenshot of your “customize your displays” screen?

what version of xojo are you using?

edit: I see your rez from the post above.

Your video is really odd. That should not be happening.

Have you tried rebooting since you altered the scaling?
Whats the utility you’re using to show the window position and size?
What graphics card do you have?
How are you setting the scaling, through windows 10 “Customize your display” or the graphics card?

Running Xojo 2017 R1.1 The configuration in the OP was my work test system, this is at home.

Both monitors are 4K resolution but one is physically smaller than the other. Monitor 0 has a scale-factor of 175 and Monitor 1 has a scale-factor of 200. I think that discrepancy might be where this is coming from.

Edit: Confirmed. When I change Monitor 1’s scaling to 175% to match Monitor 0, your code works as expected. This was one of the problems I ran into with my own code which prompted this thread. Now seeing the same issue in yours it seems as if the Xojo Screen.Width and Screen.Height functions can return incorrect values when screens have different DPI’s on the same system.

The math works out too: 3840 / 1.75 = 2194 which is around the point value that your code said I crossed into monitor 1 (“around” because of the hotspot size).

Any workarounds for this?

Yup, there’s a bug in System.MouseX and System.MouseY.

Are you just coding for windows? I can get around until its fixed by Xojo it if you are.

At this point I think this will only run on Windows. I don’t even have a multi-monitor Mac system to see if the same issues occur there (although I could probably plug a TV into my MacBook Pro to find out).

Thanks for all the help, if I can contribute anything I’d be happy to!

Its never easy…

GetCursorPos should return a dpi aware position of the mouse, however it isn’t which would lead me to believe that the app manifest isn’t being set to hdpi correctly.

After using mt.exe (part of the windows sdk) there’s no entry in there to set the dpi awareness of the exe :frowning:

I’ll have a play around with calculating it all manually.

Here you go Christian, sorry for the delay, I had some errands to run:

Its a complete rewrite using windows api calls. Looks good at all sizes I have tested.

There’s a lot of interesting things happening here, I’ll need to raise a few tickets about problems with Xojo.

Could someone with a mac and two screens please test the program here:

Its all Xojo code, I’d like to know if it has the same problems as windows, i.e.

If you have a monitor layout like this:

Where screen 2 is your primary screen, and is set to 125% and monitor 1 is set to 200% do you see issues with the horizontal position of screen 1 being somewhere near the right 3/4 of screen 2. I.e. like Christians video above.

[quote=332657:@JulianS]Could someone with a mac and two screens please test the program here:

Its all Xojo code, I’d like to know if it has the same problems as windows, i.e.

If you have a monitor layout like this:

Where screen 2 is your primary screen, and is set to 125% and monitor 1 is set to 200% do you see issues with the horizontal position of screen 1 being somewhere near the right 3/4 of screen 2. I.e. like Christians video above.[/quote]

I have two external monitors, running under OSX 10.11.6 (El Capitan) but both are native 2560x1440. Running both in my normal default resolution and side by side, the project accurately reports both the current screen and corner. I think the closest I can get to the above arrangement is setting #1 to scaled at 1366x768 and aligning the bottoms. It still seems to accurately report the current screen and each corner of each monitor. I cannot replicate the issue shown in the video using the above project.

Trying various monitor resolutions and arrangements, I never found a corner that was not reported right. The only anomaly was when I offset the displays horizontally then moved the mouse to the extreme edge as in location “a” below. If I moved even slightly off the edge of display2 it was correct. And the corner was correct. But along the right edge of display 2 and above the top edge of display 1, it did not report it was still on display 2. It showed only the mousex and mousey values (Label2 in project) and Label1 and Label3 were blank.

To fix edge cases, I added 1 in two places in the IF statement on line 16 of Timer1.Action:

if System.MouseX >= screen(c).left and System.MouseX < screen(c).left + screen(c).width + 1 and System.MouseY >= screen(c).top and System.MouseY < screen(c).top + screen(c).height + 1 then

Hmm, looks like it would have been easier to just change the < test to <= in both places…

Thanks Douglas. I played around with that fix also, it looks like you’re seeing the issue I’ve noticed where the mouse position is floor’d when it should be round’d. I’ll feedback it when I get the chance.

My main concern was that the cursor position in monitor 1 was scaling incorrectly and bleeding into part of the right side of 2.

What happens if you zoom in (double the size/scaling) on monitor 1 and leave monitor 2 at “default”. Do you see a mouse pointer issue around about position a when the two windows are aligned side by side?

Both my external monitors are a native 2560x1440. If I hold Alt while clicking Scaled in the OSX display preferences, none of the choices are a clean 1280x720. But based on the thread you are seeing problems elsewhere when the resolutions are not an even multiple. So I tried a few odd sizes, such as 2048x1152 and 1366x768 and everything seems to work fine (provided I either use the <= or the + 1 fix on Timer1.Action line 16).

I am on El Capitan, not Sierra or Windows, and not quite sure if by zoom you just mean a scaled resolution in the display preferences (I can’t choose by percentage).

Thank you SO MUCH for your help, @JulianS

I have made some additions to cover Windows 7 and 8, which do not ship with Shcore.dll. Thankfully these older OS’s only support a single DPI for all monitors.

This new file uses MBS to detect the Windows version, so for anyone who does not have the paid version of MBS (it’s totally worth it, btw), you’ll need to write your own Windows version detection code (shelling to VER and parsing the output should be sufficient).