Set mouse position??
by Jason McIntosh · in Torque Game Builder · 08/09/2005 (4:17 pm) · 20 replies
Is there a way to set the mouse to a specific point? I know how to read the position, but I don't know how to set the position.
It's very VERY frustrating to search for these topics and be denied information because I didn't buy TGE.
It's very VERY frustrating to search for these topics and be denied information because I didn't buy TGE.
About the author
#2
08/09/2005 (5:22 pm)
I tried that already. Thanks...
#3
%screenCoords = convertWorldCoordsToScreen(%posX SPC %posY);
Canvas.setCursorPos(%screenCoords);
There is a bug in the engine code however that causes there to be odd behavior (the cursor moves to the new spot but then jumps back to its old spot right away.)
There is a pretty long thread about this in the TGE private forums with code fixes for all three platforms (Win32/Linux/Mac) and posts from several GG employees saying they'd put it into head, but 1.0.2 of T2D (based on TGE 1.3 I believe) is still broken.
I asked about this in the T2D forum awhile back and got no reply.
The original answer thread has a whole lot of small code snippets in it for the different platforms. Here's the code changes for Win32 though, courtesy of David Wyand:
--------------------------------------------------
Open up platform/platformInput.h and go to the Input class definition. Just before the getManager() method definition, add the following (the new code is bolded):
Thats it for this file. Next, open up platformWin32/wininput.cc and go to just after the void Input::process() and before the InputManager* Input::getManager() code. Add the following function:
This new setCursorPos() function will move the mouse at the hardware level to the new position (by using the Windows PDK setCursorPos() function). The coordinates passed to it are relative to the client area of the window (the part of the window the TGE draws into).
Finally, well need to change how the Canvas.setCursorPos() method works. Open gui/guiCanvas.h and add the following to the top of the file (new code is bolded):
This will make sure the Canvas has access to the new Input::setCursorPos() method through the platform layer. Next, find the Canvas setCursorPos() method and replace it with the following:
This new function will make sure that the passed coordinates are relative to the Canvas origin. So, by calling Canvas.setCursorPos(0,0); in script should now place the mouse pointer in the upper left corner of the Canvas drawing area.
08/09/2005 (6:20 pm)
I use:%screenCoords = convertWorldCoordsToScreen(%posX SPC %posY);
Canvas.setCursorPos(%screenCoords);
There is a bug in the engine code however that causes there to be odd behavior (the cursor moves to the new spot but then jumps back to its old spot right away.)
There is a pretty long thread about this in the TGE private forums with code fixes for all three platforms (Win32/Linux/Mac) and posts from several GG employees saying they'd put it into head, but 1.0.2 of T2D (based on TGE 1.3 I believe) is still broken.
I asked about this in the T2D forum awhile back and got no reply.
The original answer thread has a whole lot of small code snippets in it for the different platforms. Here's the code changes for Win32 though, courtesy of David Wyand:
--------------------------------------------------
Open up platform/platformInput.h and go to the Input class definition. Just before the getManager() method definition, add the following (the new code is bolded):
static void process();
[b]static void setCursorPos(S32 x, S32 y); // DAW: Set the cursor position relative to the windows client space[/b]
static InputManager* getManager();Thats it for this file. Next, open up platformWin32/wininput.cc and go to just after the void Input::process() and before the InputManager* Input::getManager() code. Add the following function:
//------------------------------------------------------------------------------
// DAW: Set the cursor position relative to the windows client space
void Input::setCursorPos(S32 x, S32 y)
{
POINT pt;
pt.x = x;
pt.y = y;
ClientToScreen(winState.appWindow, &pt);
SetCursorPos(pt.x, pt.y);
}This new setCursorPos() function will move the mouse at the hardware level to the new position (by using the Windows PDK setCursorPos() function). The coordinates passed to it are relative to the client area of the window (the part of the window the TGE draws into).
Finally, well need to change how the Canvas.setCursorPos() method works. Open gui/guiCanvas.h and add the following to the top of the file (new code is bolded):
#ifndef _EVENT_H_ #include "platform/event.h" #endif [b]#ifndef _PLATFORMINPUT_H_ #include "platform/platformInput.h" #endif[/b] #ifndef _GUICONTROL_H_ #include "gui/guiControl.h" #endif
This will make sure the Canvas has access to the new Input::setCursorPos() method through the platform layer. Next, find the Canvas setCursorPos() method and replace it with the following:
void setCursorPos(const Point2I &pt)
{
Input::setCursorPos(mBounds.point.x+pt.x, mBounds.point.y+pt.y);
} //DAW: Was: {cursorPt.x = F32(pt.x); cursorPt.y = F32(pt.y); }This new function will make sure that the passed coordinates are relative to the Canvas origin. So, by calling Canvas.setCursorPos(0,0); in script should now place the mouse pointer in the upper left corner of the Canvas drawing area.
#4
Then there was an addendum by Drew Parker fixing another apect of it:
-----------------------------------------------------
Great work Dave! One other problem I had was with the ConsoleMethod not working correctly (it wasn't passing the correct argv). Here's how I fixed it:
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 doesnt exist since you only passed one. So it should actually check argv[2], like this...
GuiCanvas.cc
---------------------------------------------------------
I made those changes and it has worked great for me in T2D 1.0.2. I hate to proxy a cross-post but It's better than nothing. Hope the formatting is readable.
08/09/2005 (6:20 pm)
(continued from previous...)Then there was an addendum by Drew Parker fixing another apect of it:
-----------------------------------------------------
Great work Dave! One other problem I had was with the ConsoleMethod not working correctly (it wasn't passing the correct argv). Here's how I fixed it:
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 doesnt exist since you only passed one. So it should actually check argv[2], like this...
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[2], "%d %d", &pos.x, &pos.y); // should be argv[2], not argv[3]
Canvas->setCursorPos(pos);
}---------------------------------------------------------
I made those changes and it has worked great for me in T2D 1.0.2. I hate to proxy a cross-post but It's better than nothing. Hope the formatting is readable.
#5
Thanks for the info, Luke.
08/09/2005 (7:24 pm)
Ah, so it is setCursorPos(), but it's mostly broken and after a couple versions it's still not fixed in the main engine. *sigh* I'll bite my tongue here.Thanks for the info, Luke.
#6
I give you HUGE thanks for that. This is very important for a game I'm currently working on - it was on tomorrow's homework list. I'll give it a run and see how it goes.
Cheers.
08/09/2005 (9:22 pm)
Hi Luke,I give you HUGE thanks for that. This is very important for a game I'm currently working on - it was on tomorrow's homework list. I'll give it a run and see how it goes.
Cheers.
#7
08/10/2005 (7:28 pm)
Okay, I do believe the setCursorPos() works, however, I get a message in my console saying "unable to find function convertWorldCoordsToScreen". Was this a true function or filler? I do believe the code I have works as expected, but my cursor keeps flying up to the upper left corner of the screen. How do I go about converting the world coords to screen coords?
#8
edit: (the reason I wrap it is I manually trim the max screen extents by one or I get funny boundary behaviors when the mouse is near the edge of the game window which I attributed to world-to-screen conversion precision loss)
08/10/2005 (8:05 pm)
Ha, oops. That's my own function that wraps a call to sceneWindow2D.getWindowPoint(%position), sorry.edit: (the reason I wrap it is I manually trim the max screen extents by one or I get funny boundary behaviors when the mouse is near the edge of the game window which I attributed to world-to-screen conversion precision loss)
#9
This can be dropped at the very bottom of example\T2D\client\client.cs (past the closing brace of setupT2DScene()) in a clean install of T2D:
A few things to note: the angle which comes back from angleBetween2D is within a -180 to 180 range and needs to be offset so it's unsigned; the %y axis needs to be sign-flipped; and that the result of sceneWindow2D.getWindowPoint is absolute, not taking window extents into account (e.g. you can't assume Canvas.getCursorPos() and sceneWindow2D.getWindowPoint(%worldPos) will equal the same thing) so you have to add them in.
08/10/2005 (10:34 pm)
Having tinkered a bit with this, what follows is what should be a drop-in-and-go functioning routine for this behavior, in case others find it useful. Keep in mind that without recompiling in the setCursorPos() fix, this will result in the cursor being put in the upper-left corner of the window instead of where it belongs. This can be dropped at the very bottom of example\T2D\client\client.cs (past the closing brace of setupT2DScene()) in a clean install of T2D:
function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
%constrainLimit = 15;
%constrainCenter = "0 0";
%distance = vectorLength2D(vectorDistance2D(%worldPos, %constrainCenter));
if (%distance > %constrainLimit)
{
%angle = angleBetween2D(%worldPos, %constrainCenter) + 180;
%x = mSin(mDegToRad(%angle)) * (%constrainLimit * 0.95);
%y = mCos(mDegToRad(%angle)) * -(%constrainLimit * 0.95);
%worldX = mFloor(getWord(sceneWindow2D.getWindowPoint(vectorAdd2D(%constrainCenter, %x SPC %y)), 0)) + getWord(sceneWindow2D.getWindowExtents(), 0);
%worldY = mFloor(getWord(sceneWindow2D.getWindowPoint(vectorAdd2D(%constrainCenter, %x SPC %y)), 1)) + getWord(sceneWindow2D.getWindowExtents(), 1);
Canvas.setCursorPos(%worldX SPC %worldY);
}
}A few things to note: the angle which comes back from angleBetween2D is within a -180 to 180 range and needs to be offset so it's unsigned; the %y axis needs to be sign-flipped; and that the result of sceneWindow2D.getWindowPoint is absolute, not taking window extents into account (e.g. you can't assume Canvas.getCursorPos() and sceneWindow2D.getWindowPoint(%worldPos) will equal the same thing) so you have to add them in.
#10
08/10/2005 (10:40 pm)
Why is such a simple thing so much work? Good heavens....
#11
10/23/2005 (7:55 pm)
Let's hope there is a simple setMousePosition() that takes world coordinates, in T2D 1.1.
#12
- Melv.
10/24/2005 (5:19 am)
T2D v1.1 does have a "t2dSceneWindow.setMousePosition()" call that takes world coordinates.- Melv.
#13
10/24/2005 (5:22 am)
Thanks Melv!
#14
i'll add this to the bugs forum.
the above fix DOES make setMousePosition work! cool! thanks guys
12/17/2005 (12:23 am)
There is indeed a setmouseposition function, but the mouse jumps back to the old location, as per the above posts... i'll add this to the bugs forum.
the above fix DOES make setMousePosition work! cool! thanks guys
#15
Does anyone experience this? I'm with the tgb 1.7.4 and all those changes were already in the engine.
Can anyone Help me on this?
02/06/2009 (4:52 am)
I did all that and still the mouse jumps back to the old position, but just when i'm moving to the positive coordinates (moving to the right and/or upwards).Does anyone experience this? I'm with the tgb 1.7.4 and all those changes were already in the engine.
Can anyone Help me on this?
#16
A) A dialog was shown, and the mouse was moved, then the dialog dismissed, and then getMousePosition() was called before it was moved again,
or
B) You go from windowed to fullscreen (and the mouse has not been moved since the change.)
I think I reported both of these about a year ago. No fix yet in 1.7.4. You aware of any fixes for these from the TGE threads?
-Vern
02/09/2009 (5:38 pm)
Luke, do you have any info on sceneWindow2D.getMousePosition() fixes? I don't have access to the TGE forums, so I'm not sure if it's been discussed there. In short, getMousePosition() does not return the correct position if:A) A dialog was shown, and the mouse was moved, then the dialog dismissed, and then getMousePosition() was called before it was moved again,
or
B) You go from windowed to fullscreen (and the mouse has not been moved since the change.)
I think I reported both of these about a year ago. No fix yet in 1.7.4. You aware of any fixes for these from the TGE threads?
-Vern
#17
I created a new blank project, created a single object that bounced around a world-limit.
I then did two tests, the first was to set the cursor to the objects position when the object bounced off a world-limit. The cursor behaved correctly. I tried going in/out of full-screen and it continued to work okay. I brought up the console and the cursor continued to move around as usual.
I then set the cursor to update more frequently by updating the cursor position every scene tick. Again, this worked perfectly.
This was on v1.7.4.
I am not saying that you are not experiencing a problem, it's just that I can't duplicate it or that I don't understand the problem!
If you have a quick bit of test script that I can run against v1.7.4 that duplicates the issue I will endeavour to produce a fix for you.
Melv.
02/11/2009 (9:12 am)
I am not sure I can duplicate the problem you guys are seeing here.I created a new blank project, created a single object that bounced around a world-limit.
I then did two tests, the first was to set the cursor to the objects position when the object bounced off a world-limit. The cursor behaved correctly. I tried going in/out of full-screen and it continued to work okay. I brought up the console and the cursor continued to move around as usual.
I then set the cursor to update more frequently by updating the cursor position every scene tick. Again, this worked perfectly.
This was on v1.7.4.
I am not saying that you are not experiencing a problem, it's just that I can't duplicate it or that I don't understand the problem!
If you have a quick bit of test script that I can run against v1.7.4 that duplicates the issue I will endeavour to produce a fix for you.
Melv.
#18
I got your post. What you're seeing here is a rounding issue. If you programmatically position the mouse using world-coordinates which are "real" values e.g. floating-point then you need to consider that if you then set the cursor position, that will be rounded-down because the cursor is an integer-only coordinate.
This accounts for why you can move up/left but not down/right simply because of the rounding down (right/down are increasing values). Change your code to move much larger steps (at least 0.5 per interval) and it should work.
An alternative way is to maintain an internal position and use that when updating the actual mouse position e.g. don't "get" the mouse position, just update it but maintain what you want the position to be. In this way, the float/integer problem goes away.
In the example code you sent me, I made the above changes and the cursor starting moving right/down.
Also, I would caution the use of "OnUpdateScene" as it is called every frame and is therefore unsuitable if you want to provide the same speed movement from computer to computer. Consider using "OnUpdateSceneTick" which is called at a constant rate independent of the frame-rate.
Hope this helps,
Melv.
02/13/2009 (5:02 am)
Gilberto,I got your post. What you're seeing here is a rounding issue. If you programmatically position the mouse using world-coordinates which are "real" values e.g. floating-point then you need to consider that if you then set the cursor position, that will be rounded-down because the cursor is an integer-only coordinate.
This accounts for why you can move up/left but not down/right simply because of the rounding down (right/down are increasing values). Change your code to move much larger steps (at least 0.5 per interval) and it should work.
An alternative way is to maintain an internal position and use that when updating the actual mouse position e.g. don't "get" the mouse position, just update it but maintain what you want the position to be. In this way, the float/integer problem goes away.
In the example code you sent me, I made the above changes and the cursor starting moving right/down.
Also, I would caution the use of "OnUpdateScene" as it is called every frame and is therefore unsuitable if you want to provide the same speed movement from computer to computer. Consider using "OnUpdateSceneTick" which is called at a constant rate independent of the frame-rate.
Hope this helps,
Melv.
#19
Thx again
PS- Nice tip with the "OnUpdateSceneTick" wasn't aware of that callback.
02/13/2009 (7:56 am)
Thanks alot Melv. I wasn't picturing that the mouse was integer values. Now it's solved. :)Thx again
PS- Nice tip with the "OnUpdateSceneTick" wasn't aware of that callback.
Torque Owner Stefan
:)