Asked by:
Mouse Object Bug

General discussion
-
I just came across a Mouse object bug when used in combination with the GraphicsWindow.
I moved the Mouse using the Mouse object to a position on the GraphicsWindow. When I used the GW.MouseX property to return the position I got an incorrect return.
The workaround was similar to other w/arounds where some objects (like Shapes & now Mouse) seem to have a "visibility" issue with the GraphicsWindow.
The w/around often in these cases is to Hide and represent the Object (using Show) to GraphicsWindow.
Initialise() '============================================================ 'Bug is: moved mouse using Mouse Object then use GraphicsWindow Object to locate Mouse.MouseX = GraphicsWindow.Left + mouseLeftOffset + 10 Mouse.MouseY = GraphicsWindow.Top + mouseTopOffset + 10 ReturnMousePosition() Mouse.MouseX = GraphicsWindow.Left + mouseLeftOffset Mouse.MouseY = GraphicsWindow.Top + mouseTopOffset 'Workaround() '<<<< uncomment to get accurate result ReturnMousePosition() '<<<<< without w/around returns 10. With w/a returns 0. '================================================================ Sub ReturnMousePosition mouseX = GraphicsWindow.MouseX mouseY = GraphicsWindow.MouseY result = mouseX + " : " + mouseY Shapes.SetText(writeResult, result) EndSub Sub Workaround 'Bug is "fixed" by re-presenting the Mouse to GraphicsWindow Mouse.HideCursor() Mouse.ShowCursor() EndSub Sub Initialise GraphicsWindow.Show() writeResult = Shapes.AddText("") Shapes.Move(writeResult, GraphicsWindow.Width /2, GraphicsWindow.Height /2) mouseLeftOffset = 8 'window border size mouseTopOffset = 30 'window title + window border size EndSub
Friday, November 14, 2014 4:22 AM
All replies
-
Origin of Mouse.MouseX and Mouse.MouseY is the top left corner of the screen.
Origin of GraphicsWindow.MouseX and GraphicsWindow.MouseY is the top left corner of the GraphicsWindow.
So the offset should be the difference of these origins. It depends on window position on the screen and the title bar height and border size (that depends on OS version).
Following program is just showing these two mouse positions. GraphicsWindow.MouseX and GraphicsWindow.MouseY are not updated while the mouse pointer is out of the window.
GraphicsWindow.Title = "Mouse" p1 = Shapes.AddText("") Shapes.Move(p1, 10, 10) p2 = Shapes.AddText("") Shapes.Move(p2, 10, 40) Timer.Interval = 500 Timer.Tick = OnTick Sub OnTick x1 = GraphicsWindow.MouseX y1 = GraphicsWindow.MouseY Shapes.SetText(p1, x1 + "," + y1) x2 = Mouse.MouseX y2 = Mouse.MouseY Shapes.SetText(p2, x2 + "," + y2) EndSub
Nonki Takahashi
- Edited by Nonki Takahashi Friday, November 14, 2014 9:30 AM minor change
Friday, November 14, 2014 9:29 AM -
I think there's a bug. If you uncomment the sample it returns a different value.Friday, November 14, 2014 9:53 AM
-
With my machine (Windows 8.1), nothing has been changed between w/ workaround and w/o workaround. Your code always returns "0:9". You can check the effect with Mouse.ShowCursor() and Mouse.HideCursor() by pressing "S" and "H" keys.
GraphicsWindow.Title = "Mouse" p1 = Shapes.AddText("") Shapes.Move(p1, 10, 10) p2 = Shapes.AddText("") Shapes.Move(p2, 10, 40) Timer.Interval = 500 Timer.Tick = OnTick GraphicsWindow.KeyDown = OnKeyDown Sub OnTick x1 = GraphicsWindow.MouseX y1 = GraphicsWindow.MouseY Shapes.SetText(p1, x1 + "," + y1) x2 = Mouse.MouseX y2 = Mouse.MouseY Shapes.SetText(p2, x2 + "," + y2) EndSub Sub OnKeyDown If GraphicsWindow.LastKey = "S" Then Mouse.ShowCursor() ElseIf GraphicsWindow.LastKey = "H" Then Mouse.HideCursor() EndIf EndSub
Nonki Takahashi
Friday, November 14, 2014 10:11 AM -
Thanks for checking Nonki. I'm win7.
I'll post the program I'm working on here when I'm done with it.
Friday, November 14, 2014 9:16 PM -
SNL877 - runs locally only
This is the sample where I needed to workaround the bug. It's just a drag & drop program.
Saturday, November 15, 2014 10:03 PM -
Perhaps window sizing borders etc are different in W7 and W8, does this work to get the offset?
'Move Mouse to Centre of Shape mouseLeftOffset = Mouse.MouseX-GraphicsWindow.MouseX mouseTopOffset = Mouse.MouseY-GraphicsWindow.MouseY Mouse.MouseX = mouseLeftOffset + boxLeft[i] + boxSize /2 Mouse.MouseY = mouseTopOffset + boxTop[i] + boxSize /2
Or even just
Mouse.MouseX = Mouse.MouseX - GraphicsWindow.MouseX + boxLeft[i] + boxSize /2 Mouse.MouseY = Mouse.MouseY - GraphicsWindow.MouseY + boxTop[i] + boxSize /2
- Edited by litdev Saturday, November 15, 2014 10:29 PM
Saturday, November 15, 2014 10:27 PM -
Thanks for your help with this.
There does seem to be differences with the window in Win7 and 8.1.
I didn't like hard coding the offset values I needed, so i'll use the transposition code. Thanks for that.
AFAIK the workaround IS STILL needed to produce a reliable result in this case. For win7 uses you may need to run the sample more than once to see this.
I think it's reasonable to want to set the Cursor then use the GraphicsWindow object to return it's position.
Here's a screen shot of the results without the w/around:
GW.Mouse should return (0, 0).
Litdev, I pasted your code in and it required the w/around.
Here's the test that produced the above screen shot, with the w/around commented out.
'You need to initialise these as well GraphicsWindow.Left = 0 GraphicsWindow.Top = 0 'Put mouse on the GraphicsWindow so you can use GW.MouseX/Y Mouse.MouseX = 100 Mouse.MouseY = 100 For i = 1 To 2 writeMe[i] = Shapes.AddText("") Shapes.Move(writeMe[i], GraphicsWindow.Width /2, GraphicsWindow.Height /2 + (i-1) * 30) EndFor 'Move Mouse to GraphicsWindow Origin mouseLeftOffset = Mouse.MouseX-GraphicsWindow.MouseX mouseTopOffset = Mouse.MouseY-GraphicsWindow.MouseY Mouse.MouseX = GraphicsWindow.Left + mouseLeftOffset Mouse.MouseY = GraphicsWindow.Top + mouseTopOffset WorkaroundMouseBug() Shapes.SetText(writeMe[1], "GW.Mouse: " + GraphicsWindow.MouseX + " : " + GraphicsWindow.MouseY) Shapes.SetText(writeMe[2], "mouseOffsets: " + mouseLeftOffset + " : " + mouseTopOffset) Sub WorkaroundMouseBug 'Required after setting MouseXY and then using GW.MouseXY to return XY Mouse.HideCursor() Mouse.ShowCursor() EndSub
Mothballs- Edited by Jibba j Sunday, November 16, 2014 6:15 AM
Sunday, November 16, 2014 6:10 AM -
LD, Only with the w/aroundSunday, November 16, 2014 6:20 AM
-
Jibba,
Suggest you add to the bug list on technet and reference a link to this thread since it is hard to reproduce exactly.
Sunday, November 16, 2014 10:24 AM -
No worries, just did that.Sunday, November 16, 2014 10:12 PM
-
I wrote a blog on this with a possible solution - will also add a method to LDGraphicsWindow.MouseX(Y) extension to set these directly without usingMouse object for next extension release.Saturday, November 22, 2014 8:30 AM
-
So, i could not find a bug here on Win7. The distance deltas are equal if related to GW- or Desktop coordinates.
The only problem, that could be misleading is, that GW.Left and GW.Top are related to Desktop coordinates, which have an offset (a Constant depending on Style and CanResize) to the GraphicsWindow inside the ApplicationWindow. All other GW. positions are related to the so called 'GraphicsWindow' (= client area) inside the AppWindow.
When 'WinSpy II' shows an ApplicationWindowSize of 640 x 480 (DEFAULT after GW.Show()), the GW.Width is 624 and GW.Height is 442. That means, that around the GW there are offsets to the full ApplicationWindow size, which are
up: Titlebar 29 + 1 frame -> 30
bottom: border 7 + 1 frame -> upper 30 + 8 = 38 + GW.Height 442 = 480 (ApplicationWindowHeight)
left and right: border 7 + 1 frame -> 2 * 8 = 16 + GW.Width 624 = 640 (ApplicationWindowWidth)
.. this always for default GW.CanResize = t and default LDGraphicsWindow.Style = 1.
Setting LDGraphicsWindow.Style = 0 (only client area = GraphicsWindow) and placing the GW on Desktop 0;0, then both (GW- and Desktop-) coordinate systems are equal and so are the Mouse positions.
Depending on GW.CanResize and Style, the constant Offsets are:
for style... Offsets: x/y
LDGraphicsWindow.Style = 0 ' GW.CanResize = t: 7/7 ; GW.CanResize = f : 0/0
LDGraphicsWindow.Style = 1 ' GW.CanResize = t: 8/30 (DEFAULT) ; GW.CanResize = f : 3/25
LDGraphicsWindow.Style = 2 ' GW.CanResize = t: 10/32 ; GW.CanResize = f : 5/27
LDGraphicsWindow.Style = 3 ' GW.CanResize = t: 8/26 ; GW.CanResize = f : 3/21See ID: TKG211
Some WinSpy Links: WinSpy 1.7 winspy2, WinSpy17, winspy.zip, etc.
- Edited by Pappa LapubEditor Saturday, November 22, 2014 6:32 PM
Saturday, November 22, 2014 6:15 PMAnswerer -
LD thanks for writing up that blog. I actually tried a delay but not as high as 2 sec :)
I like your comments about how the async part might be out of time.
I just run the sample as an exe (not in the ide) and the bug seemed a bit more pronounced.
You might be interested in:
ages ago gotoloop helped me with a feature problem I had. Threading!
I used the Timer to do something and it turned that the Timer had some optimising feature that required a workaround. The Timer appears to run faster in the ide then as an exe.
When things like this happen you also need to test out of the ide and I think with the ide closed altogether sometimes.
Saturday, November 22, 2014 9:13 PM -
@Papa, Jibba - the issue arises I think depending on the display DPI, hence it is not a Win7 or 8 issue.
The issue about window offsets and borders (Pappa) are always there, but Jibba Jabba and me were having issues with the mouse coordinates - conversion from GraphicsWindow.MouseX to Mouse.MouseX which is a DPI scaling issue and only apparent if you don't have 96 DPI display.
In this case the latency of async update of GraphicsWindow.MouseX(Y) is I think not relevant in this case.
I have confirmed my display DPI is 120 and so the 1.25 scaling for me is right.
http://tech.pro/tutorial/893/wpf-snippet-reliably-getting-the-mouse-position
QUOTE
One important thing to note about using the results of
GetCursorPos
in WPF. You should never use those values directly, because the values are in system pixel coordinates, which are meaningless to WPF (since WPF uses DIU, or Device Independent Units, instead of pixels). Using them directly will cause a subtle problem that you won't notice until you run your application on a system that has a DPI setting other than 96 (this is because 1 pixel = 1 DIU when working on a 96 DPI screen). Before using the result, you should always pass it through something likePointFromScreen
(WPF does the translation between screen pixels and DIUs deep inside that method).http://wpf.2000things.com/tag/dpi/
QUOTE
In Windows Forms, control sizes are specified using pixels. In WPF, sizes are specified using WPF Units.
1 WPF unit = 1/96 inch. This means at 96 dpi (typical), 1 WPF Unit = 1 pixel.
But this means that at 120 dpi, 1 WPF unit = 1.25 pixels. (120/96)
Because everything in a WPF GUI using WPF units for sizing, all controls are properly resized based on the system DPI. The reason for this is so that they can appear to be the same physical size on a device that happens to have a higher pixel density (DPI). A 96 unit button will be 1″ wide on a 96 dpi device and 1″ wide on a 120 dpi device (because it’s scaled up to 120 pixels).
This same scaling could be done in Windows Forms using a form’s AutoScaleMode property. But in WPF, it’s automatic.
The full formula:
# pixels = (# WPF Units) * (DPI / 96)
- Edited by litdev Saturday, November 22, 2014 11:48 PM
Saturday, November 22, 2014 10:50 PM -
Uploaded a new LitDev extension version with bits in LDGraphicsWindow and LDUtilities - see change log and this test should work on all displays - if not then let me know (new commads in BOLD).
'Position Mouse over a ball ball = Shapes.AddEllipse(50,50) GraphicsWindow.Title = LDUtilities.DPIX+"(DPIX) : "+LDUtilities.DPIY+"(DPIY)" While ("True") xPos = 25+Math.GetRandomNumber(GraphicsWindow.Width-50) yPos = 25+Math.GetRandomNumber(GraphicsWindow.Height-50) Shapes.Move(ball,xPos-25,yPos-25) LDGraphicsWindow.MouseX = xPos LDGraphicsWindow.MouseY = yPos Program.Delay(2000) 'Plenty delay to end program! EndWhile
If you are lucky enough (in this case anyway) to have a 96 DPI display then the standard Mouse and GraphicsWIndow commands should work without extension.Saturday, November 22, 2014 11:31 PM