Index: src/gfx_type.h
===================================================================
--- src/gfx_type.h (revision 27164)
+++ src/gfx_type.h (working copy)
@@ -138,6 +138,12 @@
bool in_window; ///< mouse inside this window, determines drawing logic
bool vehchain; ///< vehicle chain is dragged
+
+ bool UpdateCursorPosition(int x, int y, bool queued_warp);
+
+private:
+ bool queued_warp;
+ Point last_position;
};
/** Data about how and where to blit pixels. */
Index: src/video/sdl_v.cpp
===================================================================
--- src/video/sdl_v.cpp (revision 27164)
+++ src/video/sdl_v.cpp (working copy)
@@ -545,20 +545,8 @@
switch (ev.type) {
case SDL_MOUSEMOTION:
- if (_cursor.fix_at) {
- int dx = ev.motion.x - _cursor.pos.x;
- int dy = ev.motion.y - _cursor.pos.y;
- if (dx != 0 || dy != 0) {
- _cursor.delta.x = dx;
- _cursor.delta.y = dy;
- SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
- }
- } else {
- _cursor.delta.x = ev.motion.x - _cursor.pos.x;
- _cursor.delta.y = ev.motion.y - _cursor.pos.y;
- _cursor.pos.x = ev.motion.x;
- _cursor.pos.y = ev.motion.y;
- _cursor.dirty = true;
+ if (_cursor.UpdateCursorPosition(ev.motion.x, ev.motion.y, true)) {
+ SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
}
HandleMouseEvents();
break;
Index: src/video/win32_v.cpp
===================================================================
--- src/video/win32_v.cpp (revision 27164)
+++ src/video/win32_v.cpp (working copy)
@@ -747,25 +747,11 @@
SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
}
- if (_cursor.fix_at) {
- int dx = x - _cursor.pos.x;
- int dy = y - _cursor.pos.y;
- if (dx != 0 || dy != 0) {
- _cursor.delta.x = dx;
- _cursor.delta.y = dy;
-
- pt.x = _cursor.pos.x;
- pt.y = _cursor.pos.y;
-
- ClientToScreen(hwnd, &pt);
- SetCursorPos(pt.x, pt.y);
- }
- } else {
- _cursor.delta.x = x - _cursor.pos.x;
- _cursor.delta.y = y - _cursor.pos.y;
- _cursor.pos.x = x;
- _cursor.pos.y = y;
- _cursor.dirty = true;
+ if (_cursor.UpdateCursorPosition(x, y, true)) {
+ pt.x = _cursor.pos.x;
+ pt.y = _cursor.pos.y;
+ ClientToScreen(hwnd, &pt);
+ SetCursorPos(pt.x, pt.y);
}
MyShowCursor(false);
HandleMouseEvents();
Index: src/video/allegro_v.cpp
===================================================================
--- src/video/allegro_v.cpp (revision 27164)
+++ src/video/allegro_v.cpp (working copy)
@@ -388,22 +388,10 @@
}
/* Mouse movement */
- int dx = mouse_x - _cursor.pos.x;
- int dy = mouse_y - _cursor.pos.y;
- if (dx != 0 || dy != 0) {
- if (_cursor.fix_at) {
- _cursor.delta.x = dx;
- _cursor.delta.y = dy;
- position_mouse(_cursor.pos.x, _cursor.pos.y);
- } else {
- _cursor.delta.x = dx;
- _cursor.delta.y = dy;
- _cursor.pos.x = mouse_x;
- _cursor.pos.y = mouse_y;
- _cursor.dirty = true;
- }
- mouse_action = true;
+ if (_cursor.UpdateCursorPosition(mouse_x, mouse_y, false)) {
+ position_mouse(_cursor.pos.x, _cursor.pos.y);
}
+ if (_cursor.delta.x != 0 || _cursor.delta.y) mouse_action = true;
static int prev_mouse_z = 0;
if (prev_mouse_z != mouse_z) {
Index: src/video/cocoa/event.mm
===================================================================
--- src/video/cocoa/event.mm (revision 27164)
+++ src/video/cocoa/event.mm (working copy)
@@ -362,22 +362,8 @@
static void QZ_MouseMovedEvent(int x, int y)
{
- if (_cursor.fix_at) {
- int dx = x - _cursor.pos.x;
- int dy = y - _cursor.pos.y;
-
- if (dx != 0 || dy != 0) {
- _cursor.delta.x += dx;
- _cursor.delta.y += dy;
-
- QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y);
- }
- } else {
- _cursor.delta.x = x - _cursor.pos.x;
- _cursor.delta.y = y - _cursor.pos.y;
- _cursor.pos.x = x;
- _cursor.pos.y = y;
- _cursor.dirty = true;
+ if (_cursor.UpdateCursorPosition(x, y, false)) {
+ QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y);
}
HandleMouseEvents();
}
Index: src/gfx.cpp
===================================================================
--- src/gfx.cpp (revision 27164)
+++ src/gfx.cpp (working copy)
@@ -1603,6 +1603,53 @@
SwitchAnimatedCursor();
}
+/**
+ * Update cursor position on mouse movement.
+ * @param x New X position.
+ * @param y New Y position.
+ * @param queued True, if the OS queues mouse warps after pending mouse movement events.
+ * False, if the warp applies instantaneous.
+ * @return true, when the OS cursor position should be warped back to this->pos.
+ */
+bool CursorVars::UpdateCursorPosition(int x, int y, bool queued_warp)
+{
+ /* Detecting relative mouse movement is somewhat tricky.
+ * - There may be multiple mouse move events in the video driver queue (esp. when OpenTTD lags a bit).
+ * - When we request warping the mouse position (return true), a mouse move event is appended at the end of the queue.
+ *
+ * So, when this->fix_at is active, we use the following strategy:
+ * - The first movement triggers the warp to reset the mouse position.
+ * - Subsequent events have to compute movement relative to the previous event.
+ * - The relative movement is finished, when we receive the event matching the warp.
+ */
+
+ if (x == this->pos.x && y == this->pos.y) {
+ /* Warp finished. */
+ this->queued_warp = false;
+ }
+
+ this->delta.x = x - (this->queued_warp ? this->last_position.x : this->pos.x);
+ this->delta.y = y - (this->queued_warp ? this->last_position.y : this->pos.y);
+
+ this->last_position.x = x;
+ this->last_position.y = y;
+
+ bool need_warp = false;
+ if (this->fix_at) {
+ if (!this->queued_warp && (this->delta.x != 0 || this->delta.y != 0)) {
+ /* Trigger warp. */
+ this->queued_warp = queued_warp;
+ need_warp = true;
+ }
+ } else if (this->pos.x != x || this->pos.y != y) {
+ this->queued_warp = false; // Cancel warping, we are no longer confining the position.
+ this->dirty = true;
+ this->pos.x = x;
+ this->pos.y = y;
+ }
+ return need_warp;
+}
+
bool ChangeResInGame(int width, int height)
{
return (_screen.width == width && _screen.height == height) || VideoDriver::GetInstance()->ChangeResolution(width, height);