| Summary: | Please implement relative mouse mode | ||
|---|---|---|---|
| Product: | SDL | Reporter: | Sam Lantinga <slouken> |
| Component: | video | Assignee: | Forest Hale <lordhavoc> |
| Status: | RESOLVED FIXED | QA Contact: | Sam Lantinga <slouken> |
| Severity: | blocker | ||
| Priority: | P2 | ||
| Version: | HG 2.0 | ||
| Hardware: | All | ||
| OS: | All | ||
| Bug Depends on: | |||
| Bug Blocks: | 1052 | ||
|
Description
Sam Lantinga
2011-03-21 14:23:06 UTC
Notes from LordHavoc, who volunteered to implement this: Here are my main notes on Win32: The windows Raw Input API is ideal for relative mode on XP or above, it is an alternative to DirectInput and highly recommended, QuakeLive makes use of it, among others, its chief feature is extremely low latencies (which gamers love, naturally). http://msdn.microsoft.com/en-us/library/ms645543%28v=vs.85%29.aspx ... But I don't yet use it, with GDI mouse events there is a technique for disabling mouse acceleration, and its values differ on XP or above vs older windows versions... Currently in darkplaces I use: int newmouseparms[3]; newmouseparms[0] = 0; // threshold to double movement (only if accel level is >= 1) newmouseparms[1] = 0; // threshold to quadruple movement (only if accel level is >= 2) newmouseparms[2] = 0; // maximum level of acceleration (0 = off) restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0) != FALSE; http://msdn.microsoft.com/en-us/library/ms724947%28v=vs.85%29.aspx The basic setup is of course the expected: SetCursorPos (vid.width / 2, vid.height / 2); SetCapture (mainwindow); ClipCursor (&window_rect); cl_ignoremousemoves = 2; Here are my main notes on X11: I know nothing about XInput2 at this point so I can't speak to that, what I use in darkplaces is some interesting standard tricks: Setup: XGetPointerControl(vidx11_display, &originalmouseparms_num, &originalmouseparms_denom, &originalmouseparms_threshold); XChangePointerControl (vidx11_display, true, false, 1, 1, -1); // TODO maybe change threshold here, or remove this comment cl_ignoremousemoves = 2; XDefineCursor(vidx11_display, win, CreateNullCursor(vidx11_display, win)); Each frame: case MotionNotify: if (!event.xmotion.send_event) { in_mouse_x += event.xmotion.x - in_windowmouse_x; in_mouse_y += event.xmotion.y - in_windowmouse_y; if (vid_stick_mouse.integer || abs(vid.width/2 - event.xmotion.x) > vid.width / 4 || abs(vid.height/2 - event.xmotion.y) > vid.height / 4) dowarp = true; } in_windowmouse_x = event.xmotion.x; in_windowmouse_y = event.xmotion.y; break; After the events are parsed: if (dowarp) { /* move the mouse to the window center again */ // we'll catch the warp motion by its send_event flag, updating the // stored mouse position without adding any delta motion XEvent event; event.type = MotionNotify; event.xmotion.display = vidx11_display; event.xmotion.window = win; event.xmotion.x = vid.width / 2; event.xmotion.y = vid.height / 2; XSendEvent(vidx11_display, win, False, PointerMotionMask, &event); XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, vid.width / 2, vid.height / 2); } This relies on a clever trick - by sending a fake MotionNotify that updates the internal position, when the real MotionNotify occurs it will correctly detect the accumulated relative motion that has occurred during the roundtrip for the XWarpPointer, I.E. this properly streams over the network socket (which matters because of latencies in Xorg, even on a local machine). I don't know where this trick came from - could be from SDL for all I know :) |