[fixed?]CursorControl sets position 5 pixels off?

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
BakeMyCake
Posts: 23
Joined: Sat Jun 15, 2013 3:13 am

[fixed?]CursorControl sets position 5 pixels off?

Post by BakeMyCake »

Preliminary info:
  • OS: WIn7
    driver mode: OpenGL
    windowed mode
    irrlicht 1.8.3
Hi, I just want to know if this is normal or not. Can anyone else confirm?

Basically if I use cursor control setPosition method to set my cursor to a specific position in windowed mode, the values I pass to the method are not going to be where the cursor is going to actually end up. Instead, the final position of the cursor seems to always be exactly 5 pixels to the left and 5 pixels up from the point I specify.

If I have a 800x600 window and I do device->getCursorControl()->setPosition(400, 300) (which would be the centre of the window in my logic) and then check the position of the cursor immediately after the cursor is positioned, it will be at x:395 y:295. I don't think this is a matter of my bad coding, because I can even see that the cursor is not perfectly centred by the eye, and my custom camera uses cursor delta from the screen centre to rotate, so it spins up and left non-stop. If setPosition would set the cursor dead in the middle of the screen then there would be no camera rotation.

However, if I change the code to device->getCursorControl()->setPosition(400+5, 300+5) everything works the way it should. For the sake of being thorough I tested various window resolutions and it always seems to be 5px.

Can anyone else confirm this behaviour in windowed mode? If this isn't a bug then this is definitely not how I expected the function to work. Why would it be this way?

Copy paste code that demonstrates the problem:

Code: Select all

int SCREEN_WIDTH, SCREEN_HEIGHT;
 
ICameraSceneNode*camera;
IrrlichtDevice *device;
IVideoDriver* driver;
 
class MyEventReceiver : public IEventReceiver
{
public:
 
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_MOUSE_INPUT_EVENT)
        {
            if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
            {
                device->getCursorControl()->setPosition(event.MouseInput.X, event.MouseInput.Y);
            }
        }
 
        return false;
    }
 
    MyEventReceiver()
    {
 
    }
 } eventReceiver;
 
void initSettings() {
    SCREEN_WIDTH = 640;
    SCREEN_HEIGHT = 480;
}
 
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd, int show)
{
    initSettings();
 
    device = createDevice(video::EDT_OPENGL, dimension2d<u32>(SCREEN_WIDTH, SCREEN_HEIGHT), 32, false, false, false, &eventReceiver);
 
    if (!device)
        return 1;
 
    device->setWindowCaption(L"Cursor Position Bug?");
 
    driver = device->getVideoDriver();
 
    ISceneManager* smgr = device->getSceneManager();
 
    camera = smgr->addCameraSceneNode(0, vector3df(0, 30, -40), vector3df(0, 5, 0));
 
    while (device->run())
    {
 
        if (!device->isWindowActive()) {
            Sleep(1);
            continue;
        }
 
        driver->beginScene(true, true, SColor(255, 100, 101, 140));
        smgr->drawAll();
        driver->endScene();
    }
    device->drop();
 
    return 0;
}
As you can see in this simple code the event receiver puts the cursor in the same position where the cursor already is. This should do absolutely nothing, but since the method actually places the cursor 5px off, the cursor jumps to the top-left corner and never leaves that position.

I don't know If this happens on other systems though. What do you guys think?
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: CursorControl sets position 5 pixels off?

Post by hendu »

Sounds like a Windows-specific border issue. Try changing your theme to thick window borders, if it's that, the offset will change.
BakeMyCake
Posts: 23
Joined: Sat Jun 15, 2013 3:13 am

Re: CursorControl sets position 5 pixels off?

Post by BakeMyCake »

hendu wrote:Sounds like a Windows-specific border issue. Try changing your theme to thick window borders, if it's that, the offset will change.
It is indeed related to the windows border thickness. As you suggested the offset did change.

I guess it's a big question if anyone is going to do anything about this problem, considering how difficult it will probably be to be border thickness aware for a cross-platform engine.

Just for the sake of a thought experiment:
Say that you're making a game where you have to navigate a maze with your cursor. And the cursor has to be blocked by the walls. Pretty simple concept.
How would you make such a game if you know that once a cursor collides you will not be able to place it back accurately because the window border can be any value? The place where the cursor ends up can easily overshoot or undershoot the bounds of a wall.
I would appreciate a suggestion implementation, because I am on my way to facing this problem further down my project.
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: CursorControl sets position 5 pixels off?

Post by Cube_ »

mmm, if the window borders pose a problem wouldn't a deadzone solve the problem? [say a 10px square in the center of the screen where mouse input does not rotate the screen, and only outside it moves]

(I have no idea how hard that may be to implement using irrlicht, I don't do things like this with irrlicht's input subsystem)
"this is not the bottleneck you are looking for"
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: CursorControl sets position 5 pixels off?

Post by hendu »

Yeah, it's indeed an irr bug, but it needs someone with Windows and winapi experience to fix. The same issue has been seen across many toolkits, MS keeps changing how the borders affect windows, hence why I guessed at that.

edit: Though it's surprising you're on Windows 7. It's 8 and 10 where the most issues were reported.
BakeMyCake
Posts: 23
Joined: Sat Jun 15, 2013 3:13 am

Re: CursorControl sets position 5 pixels off?

Post by BakeMyCake »

As an update to this case I'd like to state that it appears that my initial claim was incorrect. It is not the cursor control that places the cursor incorrectly. It is more like the event receiver event.MouseInput is giving this mismatching data. When I call cursorcontrol->getPosition it gives me the right position, but it is the event.MouseInput that is giving a different output. I don't know why they give different readings, but if I avoid using event.MouseInput from the eventReceiver class I'm good. There is still a reason to take a look at why these two values diverge, but now I can narrow it down to the event receiver MouseInput data, rather then the cursor control. Hope it helps.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CursorControl sets position 5 pixels off?

Post by CuteAlien »

Thanks for report and testcase, it sure helps. Unfortunately I'm not sure right now when I'm back to Irrlicht coding, so can't do much currently. If someone has figured it out and got a patch I'll apply it.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
BakeMyCake
Posts: 23
Joined: Sat Jun 15, 2013 3:13 am

Re: CursorControl sets position 5 pixels off?

Post by BakeMyCake »

CuteAlien wrote:Thanks for report and testcase, it sure helps. Unfortunately I'm not sure right now when I'm back to Irrlicht coding, so can't do much currently. If someone has figured it out and got a patch I'll apply it.
To no surprise at all, when I looked into the source CIrrDeviceWin32.cpp and it's header it was confusing as heck :P. I can tell that the mouse position is using WM_MOUSEMOVE event. Turns out the eventReceiver draws values directly from the Windows itself. Windows kindly gives you all the data in client coordinates (meaning that the borders should not matter). However, the CursorControl code for setPosition and getPosition do some additional calculations. Windows sets the cursor position using screen coordinates, not window coordinates. There are API functions ClientToScreen and ScreenToClient to translate between window coordinates and screen coordinates, but they are not used anywhere in CursorControl. Instead Irrlicht stores the position of the window on the screen and adds it to the window coordinates given by the user. Example:

Code: Select all

if (UseReferenceRect)
{
    SetCursorPos(ReferenceRect.UpperLeftCorner.X + x,
     ReferenceRect.UpperLeftCorner.Y + y);
}
else
{
    RECT rect;
    if (GetWindowRect(HWnd, &rect))
    SetCursorPos(x + rect.left + BorderX, y + rect.top + BorderY);
}
I don't know why the choice was made to avoid the API functions. Maybe there is something about this whole thing that I don't know, but I think the eventReceiver and the cursorControl should either both deal in non-modified client coordinates or both do this weird border addition.

My shallow fix for this problem would be this.
CIrrDeviceWin32.h

Code: Select all

//! Sets the new position of the cursor.
virtual void setPosition(s32 x, s32 y)
{
    POINT point;
    point.x = x;
    point.y = y;
 
    if (ClientToScreen(HWnd, &point)) {
        SetCursorPos(point.x, point.y);
    }
    else{
        if (UseReferenceRect)
        {
            SetCursorPos(ReferenceRect.UpperLeftCorner.X + x,
                ReferenceRect.UpperLeftCorner.Y + y);
        }
        else
        {
            RECT rect;
            if (GetWindowRect(HWnd, &rect))
                SetCursorPos(x + rect.left + BorderX, y + rect.top + BorderY);
            }
        }
 
        CursorPos.X = x;
        CursorPos.Y = y;
}
 
//! Updates the internal cursor position
void updateInternalCursorPosition()
{
    POINT p;
    if (!GetCursorPos(&p))
    {
        DWORD xy = GetMessagePos();
        p.x = GET_X_LPARAM(xy);
        p.y = GET_Y_LPARAM(xy);
    }
 
    if (ScreenToClient(HWnd, &p)) {
        CursorPos.X = p.x;
        CursorPos.Y = p.y;
    }
    else {
        if (UseReferenceRect)
        {
            CursorPos.X = p.x - ReferenceRect.UpperLeftCorner.X;
            CursorPos.Y = p.y - ReferenceRect.UpperLeftCorner.Y;
        }
        else
        {
            RECT rect;
            if (GetWindowRect(HWnd, &rect))
            {
                CursorPos.X = p.x - rect.left - BorderX;
                CursorPos.Y = p.y - rect.top - BorderY;
            }
            else
            {
                // window seems not to be existent, so set cursor to
                // a negative value
                CursorPos.X = -1;
                CursorPos.Y = -1;
            }
        }
    }
}
Disclaimer: This code should probably be tested/double checked/triple checked/optimized. I don't lay claim to this being a good solution, I merely claim that it works on my setup. As a side-note the ClientToScreen requires Windows 2000 Professional or higher to work.

Also I suggest a slight change in CIrrDeviceWin32.cpp

Code: Select all

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    #ifndef WM_MOUSEWHEEL
    #define WM_MOUSEWHEEL 0x020A
    #endif
    #ifndef WHEEL_DELTA
    #define WHEEL_DELTA 120
    #endif
 
    irr::CIrrDeviceWin32* dev = 0;
    irr::SEvent event;
 
    static irr::s32 ClickCount=0;
    if (GetCapture() != hWnd && ClickCount > 0)
        ClickCount = 0;
 
 
    struct messageMap
    {
        irr::s32 group;
        UINT winMessage;
        irr::s32 irrMessage;
    };
 
    static messageMap mouseMap[] =
    {
        {0, WM_LBUTTONDOWN, irr::EMIE_LMOUSE_PRESSED_DOWN},
        {1, WM_LBUTTONUP,   irr::EMIE_LMOUSE_LEFT_UP},
        {0, WM_RBUTTONDOWN, irr::EMIE_RMOUSE_PRESSED_DOWN},
        {1, WM_RBUTTONUP,   irr::EMIE_RMOUSE_LEFT_UP},
        {0, WM_MBUTTONDOWN, irr::EMIE_MMOUSE_PRESSED_DOWN},
        {1, WM_MBUTTONUP,   irr::EMIE_MMOUSE_LEFT_UP},
        {2, WM_MOUSEMOVE,   irr::EMIE_MOUSE_MOVED},
        {3, WM_MOUSEWHEEL,  irr::EMIE_MOUSE_WHEEL},
        {-1, 0, 0}
    };
 
    // handle grouped events
    messageMap * m = mouseMap;
    while ( m->group >=0 && m->winMessage != message )
        m += 1;
 
    if ( m->group >= 0 )
    {
        if ( m->group == 0 )    // down
        {
            ClickCount++;
            SetCapture(hWnd);
        }
        else
        if ( m->group == 1 )    // up
        {
            ClickCount--;
            if (ClickCount<1)
            {
                ClickCount=0;
                ReleaseCapture();
            }
        }
 
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT) m->irrMessage;
        /* these are the two lines I've changed
        event.MouseInput.X = (short)LOWORD(lParam);
        event.MouseInput.Y = (short)HIWORD(lParam);
        */
        event.MouseInput.X = GET_X_LPARAM(lParam);
        event.MouseInput.Y = GET_Y_LPARAM(lParam);
        ...etc...
 
 
 
This is more of a cosmetic thing. Instead of using the low level calls Microsoft advises to use this marco. Does the same thing but... You know... Macro! Docs:https://msdn.microsoft.com/en-us/librar ... s.85).aspx

Also the method GetVersionEx used in CIrrDeviceWin32.cpp is deprecated. Docs:https://msdn.microsoft.com/en-us/librar ... s.85).aspx
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CursorControl sets position 5 pixels off?

Post by CuteAlien »

I received a patch which likely fixes this problem (at least I think it's the same bug). This happened when Irrlicht was compiled with newer SDK's (starting with v110 I think). In this case mouse-coordinates need a different border calculation in windowed mode since Windows Vista (needs to add SM_CXPADDEDBORDER). It's applied in Irrlicht svn release branch 1.8 (r5387). svn trunk will follow soon (next merge).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
LunaRebirth
Posts: 386
Joined: Sun May 11, 2014 12:13 am

Re: [fixed?]CursorControl sets position 5 pixels off?

Post by LunaRebirth »

Sorry for the bump, just letting you know.. Using Irrlicht svn 5460, it seems the border for mine is 8 pixels off on the X axis, and 30 off on the Y axis.
However, if you get the position using the event receiver (event.MouseInput.X/Y), the position is correct without being off. That might be an easier workaround for anyone with troubles.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: [fixed?]CursorControl sets position 5 pixels off?

Post by CuteAlien »

@ LunaRebirth: So ICursorControl::getPosition() is still wrong for you? Can you give me some more information. What would be of interest to me:
- We are still talking about windowed? Fullscreen it works correct?
- Which compiler are you using - and if VisualStudio - which SDK (not on Windows right now - I think it's called target solution or so.. the stuff where it says v110, v120 or maybe just "current version"?
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
LunaRebirth
Posts: 386
Joined: Sun May 11, 2014 12:13 am

Re: [fixed?]CursorControl sets position 5 pixels off?

Post by LunaRebirth »

CuteAlien wrote:@ LunaRebirth: So ICursorControl::getPosition() is still wrong for you?

Yes. When doing mouse camera movement, I had to use ICursorControl::getPosition().X-8 and ICursorControl::getPoition().Y-30. When I made a new class for the mouse that uses event.TouchInput.X to set a variable named X, and event.TouchInput.Y to set variable Y, I no longer needed -8 or -30.

Can you give me some more information. What would be of interest to me:


- We are still talking about windowed? Fullscreen it works correct?

Correct

- Which compiler are you using - and if VisualStudio - which SDK (not on Windows right now - I think it's called target solution or so.. the stuff where it says v110, v120 or maybe just "current version"?

codeblocks-16.01mingw
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: [fixed?]CursorControl sets position 5 pixels off?

Post by CuteAlien »

@LunaRebirth: Thanks. Guess I have to test C::B again.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: [fixed?]CursorControl sets position 5 pixels off?

Post by CuteAlien »

I tried reproducing it on MinGW today for a while, but no success - works here. This is with MinGW32 (not 64). Test I used was mouse_test.cpp from https://bitbucket.org/mzeilfelder/irr-p ... -micha/src
I suspect it somehow behaves different in CIrrDeviceWin32::CCursorControl::updateBorderSize on your system. Debugging it here - it does not enter the part with #ifdef SM_CXPADDEDBORDER at all. Which is a little strange as I have no idea how it still can get correct results on Windows 10 that way. But endresult here is still correct for whatever reason.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply