none
Mouse Object Bug RRS feed

  • 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
    Moderator

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


    Friday, November 14, 2014 9:29 AM
    Moderator
  • I think there's a bug. If you uncomment the sample it returns a different value.
    Friday, November 14, 2014 9:53 AM
    Moderator
  • 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
    Moderator
  • 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
    Moderator
  • 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
    Moderator
  • 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
    


    Saturday, November 15, 2014 10:27 PM
    Moderator
  • 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
    Sunday, November 16, 2014 6:10 AM
    Moderator
  • LD, Only with the w/around
    Sunday, November 16, 2014 6:20 AM
    Moderator
  • 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
    Moderator
  • No worries, just did that.
    Sunday, November 16, 2014 10:12 PM
    Moderator
  • 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
    Moderator
  • 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/21

    See ID: TKG211

    Some WinSpy Links: WinSpy 1.7 winspy2, WinSpy17, winspy.zip, etc.

    Saturday, November 22, 2014 6:15 PM
    Answerer
  • 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
    Moderator
  • @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 like PointFromScreen (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)


    Saturday, November 22, 2014 10:50 PM
    Moderator
  • 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
    Moderator