display: improve mouselook smoothness significantly, esp if low FPS

This problem occurs when movePointer is used to reset the mouse back to the center every frame, a very common way to implement mouselook on Windows (which has no relative mouse mode).  Any movement between the last event loop run and the call to movePointer is destroyed, resulting in quite choppy mouselook in most implementations.

The solution is for getPointer to always return the latest mouse cursor position.  This goes a long way but is not 100% perfect; see the discussion in #359 for other solutions.

Fixes #359
This commit is contained in:
rdb 2018-06-22 23:52:49 +02:00
parent 927fcda817
commit 29a08932ea
5 changed files with 61 additions and 1 deletions

View File

@ -93,7 +93,7 @@ PUBLISHED:
void enable_pointer_mode(int device, double speed);
void disable_pointer_mode(int device);
MouseData get_pointer(int device) const;
virtual MouseData get_pointer(int device) const;
virtual bool move_pointer(int device, int x, int y);
virtual void close_ime();

View File

@ -120,6 +120,34 @@ WinGraphicsWindow::
}
}
/**
* Returns the MouseData associated with the nth input device's pointer.
*/
MouseData WinGraphicsWindow::
get_pointer(int device) const {
MouseData result;
{
LightMutexHolder holder(_input_lock);
nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
result = _input_devices[device].get_pointer();
// We recheck this immediately to get the most up-to-date value.
POINT cpos;
if (device == 0 && GetCursorPos(&cpos) && ScreenToClient(_hWnd, &cpos)) {
double time = ClockObject::get_global_clock()->get_real_time();
RECT view_rect;
if (GetClientRect(_hWnd, &view_rect)) {
result._in_window = PtInRect(&view_rect, cpos);
result._xpos = cpos.x;
result._ypos = cpos.y;
((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer(result._in_window, result._xpos, result._ypos, time);
}
}
}
return result;
}
/**
* Forces the pointer to the indicated position within the window, if
* possible.

View File

@ -72,6 +72,7 @@ public:
GraphicsOutput *host);
virtual ~WinGraphicsWindow();
virtual MouseData get_pointer(int device) const;
virtual bool move_pointer(int device, int x, int y);
virtual void close_ime();

View File

@ -143,6 +143,36 @@ x11GraphicsWindow::
}
}
/**
* Returns the MouseData associated with the nth input device's pointer. This
* is deprecated; use get_pointer_device().get_pointer() instead, or for raw
* mice, use the InputDeviceManager interface.
*/
MouseData x11GraphicsWindow::
get_pointer(int device) const {
MouseData result;
{
LightMutexHolder holder(_input_lock);
nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
result = _input_devices[device].get_pointer();
// We recheck this immediately to get the most up-to-date value.
if (device == 0 && !_dga_mouse_enabled && result._in_window) {
XEvent event;
if (XQueryPointer(_display, _xwindow, &event.xbutton.root,
&event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
&event.xbutton.x, &event.xbutton.y, &event.xbutton.state)) {
double time = ClockObject::get_global_clock()->get_real_time();
result._xpos = event.xbutton.x;
result._ypos = event.xbutton.y;
((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer(result._in_window, result._xpos, result._ypos, time);
}
}
}
return result;
}
/**
* Forces the pointer to the indicated position within the window, if
* possible.

View File

@ -34,6 +34,7 @@ public:
GraphicsOutput *host);
virtual ~x11GraphicsWindow();
virtual MouseData get_pointer(int device) const;
virtual bool move_pointer(int device, int x, int y);
virtual bool begin_frame(FrameMode mode, Thread *current_thread);
virtual void end_frame(FrameMode mode, Thread *current_thread);