Game Development Community

Bug in setCursorPos

by Drew Parker · in Torque Game Engine · 07/09/2004 (7:24 am) · 6 replies

Hi all,

I posted in a year old thread about this recently, but instead of posting there I'll do it here, as it's probably better for it to have it's own thread.

Looks like there may be a bug in the stock GuiCanvas function setCursorPos(). I got on the IRC yesterday and asked if anybody has ever gotten this function to work, and at least two other people active on the IRC mentioned they'd never gotten it to work. LabRat started working on it right then and also verified it didn't work, and tried a few different things to fix it. I've been working on it a while lately, and tried a bunch of different things, but can't get it to work.

What happens is after calling setCursorPos, the mouse cursor moves to the new position, then after it receives the next event (mouseMove, onMouseDown, etc) it moves back to the old position.

Is anyone else having this problem, or can verify it exists? So far we have 3 people (including me) saying it doesn't work on their build. It could be it's not a bug straight out of the HEAD, but triggered by something else. If that's the case maybe we can find out what's triggering it.

Is anyone else having this problem, or has someone already found the fix for this? Also, can anyone test this on a clean HEAD build? I don't have a clean build handy as mine is loaded with stuff. :)

It would be great to get this function working, as I'd really like to use it (and probably someone else will need to use it in the future). If it does turn out to be a bug in the HEAD, we'd probably want it fixed anyway.
Thanks!

#1
07/09/2004 (7:53 am)
Here's the version I have of the function:

GuiCanvas.cc
ConsoleMethod( GuiCanvas, setCursorPos, void, 3, 4, "(Point2I pos)")
{
   Point2I pos(0,0);
   if(argc == 4)
      pos.set(dAtoi(argv[2]), dAtoi(argv[3]));
   else
      dSscanf(argv[3], "%d %d", &pos.x, &pos.y);
   Canvas->setCursorPos(pos);
}

First off, after stepping through the function, I found that the wrong value was being passed to the Canvas->setCursorPos. This function allows 3 or 4 arguments (take off 2 for class and function name), so it actually is taking 1 or 2 arguments. When you send one, the else statement will run, but that checks argv[3] for the arguments. But argv is the 4th argument (which means the second val use passed to the console method), and that doesn't exist since you only passed one. So it should actually check argv[2], like this...

ConsoleMethod( GuiCanvas, setCursorPos, void, 3, 4, "(Point2I pos)")
{
   Point2I pos(0,0);
   if(argc == 4)
      pos.set(dAtoi(argv[2]), dAtoi(argv[3]));
   else
      [b]dSscanf(argv[2], "%d %d", &pos.x, &pos.y);[/b]   // should be argv[2], not argv[3]
   Canvas->setCursorPos(pos);
}

When Canvas->setCursorPos(pos) runs, it does this:
GuiCanvas.h
void setCursorPos(const Point2I &pt)   { cursorPt.x = F32(pt.x); cursorPt.y = F32(pt.y); }

So it sets the cursorPt values to the ones passed in. But that doesn't do anything it seems, since the mouse position is overwritten later. My current guess is that the mouse position is only being changed in the Torque GUI class, but not at the platform level.

Here is a breakdown of how I can tell things are working: The GameInterface that interacts with the system receives a mouse move event, is passes it through a bunch of stuff where it eventually lands in GuiCanvas. GuiCanvas calculates if the cursor indeed did "move", if so, it updates the mLastEvent.mousePoint var using the new values. After that, it calls some rootFunction in GuiCanvas, like rootMouseDown, or rootMouseMove. That's the only stuff I could find that would update the cursor. But, even when I made sure the game was running those steps with the updated cursor position I sent, it still didn't work.
#2
07/09/2004 (7:53 am)
Here's a more detailed breakdown:

From looking through the docs, I found that GameInterface is where the platform and system libraries interface with the game. So I looked there first. Here's where it gets mouse events:

GameInterface.cc
switch(event->type)
   {
      case PacketReceiveEventType:
         processPacketReceiveEvent((PacketReceiveEvent *) event);
         break;
      case MouseMoveEventType:
         processMouseMoveEvent((MouseMoveEvent *) event);
         break;

It calls processMouseMoveEvent(). GameInterface is re-implemented in DemoGame, it's child, which is used to implement the specifics. Here is the function:

main.cc
/// Process a mouse movement event, essentially pass to the canvas for handling
void DemoGame::processMouseMoveEvent(MouseMoveEvent * mEvent)
{
   if (Canvas) 
      Canvas->processMouseMoveEvent(mEvent);
}

Canvas processMouseMoveEvent() is called on the Canvas. Now in GuiCanvas, we have:

guiCanvas.cc
void GuiCanvas::processMouseMoveEvent(const MouseMoveEvent *event)
{
   InputEvent iEvent;
   iEvent.deviceType = MouseDeviceType;
   iEvent.objType = SI_XAXIS;
   iEvent.fValue = event->xPos - cursorPt.x;
   iEvent.modifier = event->modifier;
   processInputEvent(&iEvent);
   iEvent.objType = SI_YAXIS;
   iEvent.fValue = event->yPos - cursorPt.y;
   processInputEvent(&iEvent);
}

So it takes in the event, grabs some low-level info, then passes that data on to processInputEvent()...

bool GuiCanvas::processInputEvent(const InputEvent *event)
{
  //... cut to the good stuff
  
              pt.y += (event->fValue * mPixelsPerMickey);
            cursorPt.y = getMax(0, getMin((S32)pt.y, mBounds.extent.y - 1));
            if (oldpt.y != S32(cursorPt.y))
               moved = true;
         }
         if (moved)
         {
            mLastEvent.mousePoint.x = S32(cursorPt.x);
            mLastEvent.mousePoint.y = S32(cursorPt.y);
            
            if (mMouseButtonDown)
               rootMouseDragged(mLastEvent);
            else if (mMouseRightButtonDown)
               rootRightMouseDragged(mLastEvent);
            else
               rootMouseMove(mLastEvent);
         }
         return true;
  
  //...
}

Ok, here is where the magic is supposed to happen from what I can tell. The x or y axis is checked to see if the mouse cursor moved at platform level. They update the cursorPt (which gets overwritten... is that supposed to happen, considering it was just set with setCursorPos), then, if it's decided it changes, moved is set to true. Since moved is true, mLastEvent.mousePoint is updated with the latest cursorPt, and rootMouseMove is called with mLastEvent.

I tried setting up a function like this:
void GuiCanvas::refreshMousePosition()
{
	setUpdate();
	mLastEvent.mousePoint.x = S32(cursorPt.x);
	mLastEvent.mousePoint.y = S32(cursorPt.y);
	rootMouseMove(mLastEvent);
	
}

To make sure mLastEvent was being updated, then sent to rootMouseMove right away. I call this function right after the setCursorPos() is called in the ConsoleMethod setCursorPos. But it still does exactly the same thing. So I'm stuck. And this is where I noticed there wasn't any call (that I could find) to some function to reset the mouse at the platform layer.

I'll try to see what else I can find, but at the moment I'm currently stumped and have no new ideas. Does anyone else want to take a try at this?
#3
07/09/2004 (9:15 am)
The problem is that you can't access the mouse directly to reset the X/Y position that is being sent to the event, so when the mouse is moved it will snap to the current mouse position.
#4
07/09/2004 (9:45 am)
I saw in the Windows platform section there is a function for reposition the mouse cursor... when setCusorPos() is called, could we just call some function like

platform.setMousePos() ?

Then the platform would send the right events with the new position after that, theoritically.
#5
07/09/2004 (11:35 am)
Greetings!

I've posted my solution to this problem here:

www.garagegames.com/mg/forums/result.thread.php?qt=19875#153572

Hopefully this will help out.

- LightWave Dave
#6
07/09/2004 (11:53 am)
Dave,

This is great!! Exactly what I've been stuck on. Thanks a lot and good work! After the other platform methods for setting the cursor at hardware level are implemented, we should see about getting this into the HEAD...