Thanks, ClipCursor is
almost what I need ...
I definitely need the capturing ability of it, as even SetCapture() allows clicks to leak through to applications behind it. But the problem with it is that once the mouse hits the corners, it stops sending mouse move events.
Essentially, for SNES mouse support, I need to capture raw "moved N pixels in X, Y direction", and not "mouse is currently at X, Y pixel."
I tried a cheap mockup by detecting movement, and immediately forcing the cursor back to the center of the window. But A) I don't think this will feel very good, especially for fast movements and/or small window sizes; and B) I really don't think it'll be very portable.
Anyone have ideas on how I can get Windows to give me relative movements in each direction since the last mouse poll?
Code: Select all
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
HWND hwnd;
LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg == WM_DESTROY || msg == WM_RBUTTONUP) {
ClipCursor(0);
ShowCursor(TRUE);
PostQuitMessage(0);
}
if(msg == WM_LBUTTONUP) {
//SetCapture(hwnd);
RECT rc;
GetClientRect(hwnd, &rc);
int cx = (rc.right - rc.left) / 2;
int cy = (rc.bottom - rc.top) / 2;
POINT p = { 0, 0 };
ClientToScreen(hwnd, &p);
cx += p.x;
cy += p.y;
SetCursorPos(cx, cy);
OffsetRect(&rc, p.x, p.y);
ClipCursor(&rc);
ShowCursor(FALSE);
}
if(msg == WM_MOUSEMOVE) {
POINT p = { 0, 0 }, hp = { 0, 0 };
GetCursorPos(&p);
ClientToScreen(hwnd, &hp);
p.x -= hp.x;
p.y -= hp.y;
RECT rc;
GetClientRect(hwnd, &rc);
int cx = (rc.right - rc.left) / 2;
int cy = (rc.bottom - rc.top) / 2;
int rx = p.x - cx;
int ry = p.y - cy;
if(rx || ry) printf("x = %4d, y = %4d\n", rx, ry);
cx += hp.x;
cy += hp.y;
SetCursorPos(cx, cy);
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int main() {
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = wndproc;
wc.lpszClassName = "mousetest";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
hwnd = CreateWindow("mousetest", "mouse test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
64, 64, 320, 240,
0, 0, GetModuleHandle(0), 0);
MSG msg;
while(GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
For those who can't read the above:
How I want this to work is that by default with no mice selected on either control port, the mouse acts completely normal. But whenever you enable a mouse, it changes just a bit. By default, the mouse does nothing until you click inside the video output region (not the black borders in fullscreen). At that point, it locks the mouse and hides the cursor. You just move the mouse around and it moves the emulated mouse on the screen.
Left and right click work as expected. To release the mouse, you need to press escape.
I don't intend to support ManyMouse, at least not at this time, so I'm basically only going to allow the mouse to be on one controller port at a time. I'll disable the other option whenever one is selected, and if you try and edit the config file to assign both, I'll revert the second to "No controller" on startup. The SNES test program needs a mouse in port 2, Mario Paint needs it in port 1, so it has to be an option in both menus. For the SS / Justifier, I'll leave those as port 2 only for the time being.
Also, for Super Scope and Justifier ...
The basic idea is that they latch the counters when the laser inside them hits the beam cannon of the TV. In other words, the latch counters end up pointing to the X/Y coords of where the laser is pointing.
The problem for emulation is that it's really hackish to "fake" latch these counters, eg setting them to X=128, Y=128; when you're really at X=20, Y=10. My idea was to poll the cursor position once per frame, and then at the edge of each opcode cycle, if SS/Justifier is enabled, see if we've gone past the scope coords, and if so, latch the counters then. Maybe force the counters back, but that could probably be detected by game code.
The most precise way would be to latch the counters at the exact right moment, but that would be way too slow. I could also do it once per cycle, for a max variance of two pixels between where the laser is pointing and when the counters latch. Given that these aren't exactly sniper-class rifle scopes here, I don't think it really needs to be 100% perfect, and the games' built-in calibration features would easily compensate anyway. That should probably be good enough ...
I really wish we had better info here ... I don't even know if it's possible for the scope to trip the latch counters twice per frame or not (eg by moving the scope downward faster than the CRT beam cannon -- not an easy feat, but probably possible.)