mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
winraw_joypad: controllers are read by the poll, in the same read
The keyboard and mouse are read in bulk by the poll (1df271577). The controllers of winraw_joypad were not: its window was still made by init, on the thread that starts the input driver - the video thread, under threaded video - so its reports were taken by that thread's pump, once per video frame, a PeekMessage() and a GetRawInputData() apiece, and not while that thread waited in a present. The poll read whatever state that had left: a stick or a button up to a frame old. The driver's own poll, a PeekMessage() for its window made from the main thread, found nothing there. Now its window is made by its first poll, on the polling thread, and its reports are read when the poll asks. The read is the one the keyboard and mouse driver already makes: a bulk read takes every report waiting on the thread, and passes a controller's to winraw_joypad_take_hid(), which has been there sincede2cc6ddbfor the case of video not threaded. So the two drivers make one read between them. winraw_joypad's poll comes first in input_driver_poll() and makes it, through winraw_queue_read(); winraw_poll() finds it made and does not read again. With an input driver that is not winraw, that read is the only one, and takes controllers' reports alone. The thread's pump leaves raw input in the queue while either driver has a window on it that is read this way (winraw_queue_claim_thread()); it was the keyboard and mouse window alone that said so. Arrivals and removals stay messages. They come through the raw input queue, which a range of messages without WM_INPUT in it does not look at, so the pump does not see them; the driver's poll takes them, with a PeekMessage() for its window as it has always made. A controller plugged in restarts the joypad driver from the main window's thread, which is not the one the window is on. Destroy then asks the window to close, and it goes when its own thread next pumps; the restart's first poll makes the new one. RETROARCH_RAWINPUT_POLL=0 puts both drivers back as they were. The keyboard and mouse driver's debug line of counts now gives the controllers' reports among its reads. Not run on Windows. samples/input/winraw_joypad_poll is the driver under Wine, started from a second thread: =0 as before; otherwise no window from init, one from the first poll on the polling thread, that thread marked, one bulk read a poll; a device change waiting is taken by the poll; destroy from another thread and from the polling thread; a restart. samples/input/winraw_poll: a read made for the controller driver takes the keyboard's reports too, this driver's poll does not repeat it and delivers what it took. Reports of a controller cannot be injected under Wine, so their way through the read is by inspection and by what a pad does on Windows.
This commit is contained in:
1 parent
6b8225a040
commit
9d908a4855
7 files changed
+492
-31
No files matched your search
@@ -526,3 +526,25 @@ jobs:
|
||||
WINEDEBUG=-all timeout 300 make check
|
||||
make clean
|
||||
echo "[pass] winraw_poll_test (wine)"
|
||||
|
||||
- name: Build and run winraw_joypad_poll_test (mingw + Wine)
|
||||
shell: bash
|
||||
working-directory: samples/input/winraw_joypad_poll
|
||||
run: |
|
||||
set -eu
|
||||
# input/drivers_joypad/winraw_joypad.c with its controllers
|
||||
# read by the poll. The real driver under Wine, started from
|
||||
# a second thread as it is from the video thread: with
|
||||
# RETROARCH_RAWINPUT_POLL=0 init makes the window as it
|
||||
# always did; otherwise init makes none and the first poll
|
||||
# does, on the polling thread, marking it as one whose pump
|
||||
# leaves raw input alone, with one bulk read a poll; a device
|
||||
# change waiting for the window is taken by the poll;
|
||||
# destroy from another thread - what a controller plugged in
|
||||
# causes - has the window gone once its own thread pumps; a
|
||||
# restart and a poll make a new one.
|
||||
trap 'wineserver -k >/dev/null 2>&1 || true' EXIT
|
||||
make clean all
|
||||
WINEDEBUG=-all timeout 300 make check
|
||||
make clean
|
||||
echo "[pass] winraw_joypad_poll_test (wine)"
|
||||
@@ -222,12 +222,14 @@ typedef struct
|
||||
* decides what is taken. */
|
||||
bool sink;
|
||||
bool window_failed;
|
||||
DWORD window_tid; /* the thread that made the window */
|
||||
/* Key events of the reports read so far, handed on at the end of
|
||||
* the poll. One thread's. */
|
||||
unsigned kev_n;
|
||||
uint32_t kev[WINRAW_KEV_SIZE];
|
||||
/* Counts, logged when the driver is freed. */
|
||||
unsigned long drained; /* reports read in bulk */
|
||||
unsigned long drained_hid; /* controllers' reports among the reads */
|
||||
unsigned long drain_reads; /* in this many reads */
|
||||
unsigned long drain_empty; /* reads that found nothing waiting */
|
||||
unsigned long by_message; /* reports that came as a message */
|
||||
@@ -804,9 +806,19 @@ static void winraw_update_mouse_state(winraw_input_t *wr,
|
||||
|
||||
extern void winraw_joypad_take_hid(HANDLE device, const BYTE *data, DWORD size);
|
||||
|
||||
/* The thread the bulk-read window was made on, and so the only one
|
||||
* that can destroy it. */
|
||||
static DWORD winraw_drain_tid;
|
||||
/* The thread whose raw input queue the poll reads in bulk, and how
|
||||
* many windows on it are read that way: this driver's, and
|
||||
* winraw_joypad's (winraw_queue_claim_thread()). */
|
||||
static DWORD winraw_drain_tid;
|
||||
static retro_atomic_int_t winraw_drain_claims;
|
||||
/* This driver, for a bulk read that winraw_joypad's poll starts: one
|
||||
* thread's, the polling thread's. */
|
||||
static void *winraw_drain_wr;
|
||||
/* Set by a bulk read made on winraw_joypad's behalf, which comes just
|
||||
* before this driver's poll: that poll does not then read again. */
|
||||
static bool winraw_queue_was_read;
|
||||
|
||||
void winraw_queue_claim_thread(bool claim);
|
||||
|
||||
/* 8 when this is a 32-bit process on 64-bit Windows: a bulk read's
|
||||
* records are then laid out for 64 bits, the payload eight bytes
|
||||
@@ -974,28 +986,38 @@ static void winraw_drain(winraw_input_t *wr)
|
||||
* left to arrive as a message */
|
||||
if (n == 0 || n == (UINT)-1)
|
||||
{
|
||||
wr->drain_empty++;
|
||||
if (wr)
|
||||
wr->drain_empty++;
|
||||
break;
|
||||
}
|
||||
wr->drain_reads++;
|
||||
if (wr)
|
||||
wr->drain_reads++;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
RAWINPUT *ri = (RAWINPUT*)p;
|
||||
BYTE *payload = (BYTE*)&ri->data + shift;
|
||||
|
||||
wr->drained++;
|
||||
if (ri->header.dwType == RIM_TYPEHID)
|
||||
{
|
||||
/* a controller's, for winraw_joypad's window on this
|
||||
* thread */
|
||||
RAWHID *hid = (RAWHID*)payload;
|
||||
if (wr)
|
||||
wr->drained_hid++;
|
||||
winraw_joypad_take_hid(ri->header.hDevice, hid->bRawData,
|
||||
hid->dwSizeHid * hid->dwCount);
|
||||
}
|
||||
/* as a sink, background input arrives as well */
|
||||
else if (!wr->sink || winraw_focus)
|
||||
winraw_take(wr, ri->header.dwType, ri->header.hDevice, payload);
|
||||
/* no keyboard and mouse driver: nothing is registered for
|
||||
* them, and there is nothing to take them */
|
||||
else if (wr)
|
||||
{
|
||||
wr->drained++;
|
||||
/* as a sink, background input arrives as well */
|
||||
if (!wr->sink || winraw_focus)
|
||||
winraw_take(wr, ri->header.dwType, ri->header.hDevice,
|
||||
payload);
|
||||
}
|
||||
|
||||
p += (ri->header.dwSize + align - 1) & ~(align - 1);
|
||||
}
|
||||
@@ -1007,13 +1029,49 @@ static void winraw_drain(winraw_input_t *wr)
|
||||
}
|
||||
}
|
||||
|
||||
/* For winraw_joypad, whose window is on the polling thread too when
|
||||
* raw input is read by the poll. A bulk read takes every report
|
||||
* waiting on the thread, its controllers' and this driver's alike, so
|
||||
* there is one read for both: winraw_joypad's poll, which
|
||||
* input_driver_poll() calls first, makes it through here, and
|
||||
* winraw_poll() then finds it made. With an input driver that is not
|
||||
* this one there is no winraw_poll(), and this is the only read. */
|
||||
void winraw_queue_read(void)
|
||||
{
|
||||
winraw_drain((winraw_input_t*)winraw_drain_wr);
|
||||
winraw_queue_was_read = true;
|
||||
}
|
||||
|
||||
/* A window on the calling thread is to have its raw input read by the
|
||||
* poll, or no longer: the thread's pump leaves raw input in the queue
|
||||
* while any is (winraw_poll_owns_thread()). */
|
||||
void winraw_queue_claim_thread(bool claim)
|
||||
{
|
||||
if (claim)
|
||||
{
|
||||
winraw_drain_tid = GetCurrentThreadId();
|
||||
retro_atomic_fetch_add_int(&winraw_drain_claims, 1);
|
||||
}
|
||||
else
|
||||
retro_atomic_fetch_add_int(&winraw_drain_claims, -1);
|
||||
}
|
||||
|
||||
/* Whether raw input is read by the poll at all: on, unless
|
||||
* RETROARCH_RAWINPUT_POLL=0. */
|
||||
bool winraw_raw_input_polled(void)
|
||||
{
|
||||
const char *env = getenv("RETROARCH_RAWINPUT_POLL");
|
||||
return !(env && env[0] == '0');
|
||||
}
|
||||
|
||||
/* For a thread's pump (ui_application_win32_process_events()): true
|
||||
* on the thread whose raw input the poll reads in bulk. The pump then
|
||||
* leaves raw input where it is - in the queue, for the poll - instead
|
||||
* of taking it out one message at a time. */
|
||||
bool winraw_poll_owns_thread(void)
|
||||
{
|
||||
return winraw_drain_tid && GetCurrentThreadId() == winraw_drain_tid;
|
||||
return retro_atomic_load_relaxed_int(&winraw_drain_claims) > 0
|
||||
&& GetCurrentThreadId() == winraw_drain_tid;
|
||||
}
|
||||
|
||||
/* Input that came while the application was in the background is not
|
||||
@@ -1095,9 +1153,11 @@ static bool winraw_poll_window_up(winraw_input_t *wr)
|
||||
if ( RegisterRawInputDevices(&rid[0], 1, sizeof(RAWINPUTDEVICE))
|
||||
&& RegisterRawInputDevices(&rid[1], 1, sizeof(RAWINPUTDEVICE)))
|
||||
{
|
||||
winraw_drain_tid = GetCurrentThreadId();
|
||||
wr->window_tid = GetCurrentThreadId();
|
||||
winraw_drain_wr = wr;
|
||||
winraw_queue_claim_thread(true);
|
||||
RARCH_LOG("[WinRaw] Keyboard and mouse are read in bulk by the poll"
|
||||
" (thread %lu)%s.\n", (unsigned long)winraw_drain_tid,
|
||||
" (thread %lu)%s.\n", (unsigned long)wr->window_tid,
|
||||
wr->sink ? ", registered as a sink" : "");
|
||||
return true;
|
||||
}
|
||||
@@ -1114,7 +1174,7 @@ static bool winraw_poll_wanted(bool *sink)
|
||||
{
|
||||
const char *env = getenv("RETROARCH_RAWINPUT_POLL");
|
||||
*sink = (env && env[0] == '2');
|
||||
return !(env && env[0] == '0');
|
||||
return winraw_raw_input_polled();
|
||||
}
|
||||
|
||||
static void *winraw_init(const char *joypad_driver)
|
||||
@@ -1250,8 +1310,11 @@ static void winraw_poll(void *data)
|
||||
wr->window_failed = true;
|
||||
RARCH_ERR("[WinRaw] Could not make the keyboard and mouse window.\n");
|
||||
}
|
||||
if (wr->window)
|
||||
/* winraw_joypad's poll, just before this one, has already made
|
||||
* the read for both when it is the joypad driver */
|
||||
if (wr->window && !winraw_queue_was_read)
|
||||
winraw_drain(wr);
|
||||
winraw_queue_was_read = false;
|
||||
}
|
||||
|
||||
/* Fix coordinates after a resolution change. Runs here rather than
|
||||
@@ -1755,20 +1818,22 @@ static void winraw_free(void *data)
|
||||
* the polling thread, which is also the one that frees the
|
||||
* driver; if it ever is not, the window is asked to close
|
||||
* and goes when its own thread next pumps. */
|
||||
if (GetCurrentThreadId() == winraw_drain_tid)
|
||||
if (GetCurrentThreadId() == wr->window_tid)
|
||||
{
|
||||
DestroyWindow(wr->window);
|
||||
UnregisterClassA("winraw-input", NULL);
|
||||
}
|
||||
else
|
||||
PostMessageA(wr->window, WM_CLOSE, 0, 0);
|
||||
winraw_queue_claim_thread(false);
|
||||
}
|
||||
winraw_drain_tid = 0;
|
||||
RARCH_DBG("[WinRaw] Read by the poll: %lu reports in %lu bulk reads"
|
||||
" and %lu as messages; %lu reads found nothing waiting"
|
||||
" (%lu key events dropped).\n",
|
||||
wr->drained, wr->drain_reads, wr->by_message, wr->drain_empty,
|
||||
wr->kev_dropped);
|
||||
if (winraw_drain_wr == wr)
|
||||
winraw_drain_wr = NULL;
|
||||
RARCH_DBG("[WinRaw] Read by the poll: %lu keyboard and mouse reports"
|
||||
" and %lu of controllers in %lu bulk reads, %lu as messages;"
|
||||
" %lu reads found nothing waiting (%lu key events dropped).\n",
|
||||
wr->drained, wr->drained_hid, wr->drain_reads, wr->by_message,
|
||||
wr->drain_empty, wr->kev_dropped);
|
||||
free(g_mice);
|
||||
free(wr->mice);
|
||||
free(data);
|
||||
|
||||
@@ -170,6 +170,18 @@ static unsigned winraw_joypad_pad_count = 0;
|
||||
static HWND winraw_joypad_msg_window = NULL;
|
||||
static bool winraw_joypad_initialised = false;
|
||||
|
||||
/* Read by the poll (see the note above winraw_joypad_joypad_poll()):
|
||||
* whether it is, the thread the window was then made on, and whether
|
||||
* making it failed. */
|
||||
static bool winraw_joypad_by_poll = false;
|
||||
static DWORD winraw_joypad_window_tid = 0;
|
||||
static bool winraw_joypad_window_failed = false;
|
||||
|
||||
/* winraw_input.c */
|
||||
extern bool winraw_raw_input_polled(void);
|
||||
extern void winraw_queue_read(void);
|
||||
extern void winraw_queue_claim_thread(bool claim);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Helpers */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -881,14 +893,23 @@ static void *winraw_joypad_joypad_init(void *data)
|
||||
memset(winraw_joypad_pads, 0, sizeof(winraw_joypad_pads));
|
||||
winraw_joypad_pad_count = 0;
|
||||
|
||||
if (!winraw_joypad_create_msg_window())
|
||||
return NULL;
|
||||
/* Read by the poll: the window is made by the first poll, so that
|
||||
* it is the polling thread's. This runs on whichever thread starts
|
||||
* the input driver - the video thread, under threaded video. */
|
||||
winraw_joypad_by_poll = winraw_raw_input_polled();
|
||||
winraw_joypad_window_failed = false;
|
||||
|
||||
if (!winraw_joypad_register_devices())
|
||||
if (!winraw_joypad_by_poll)
|
||||
{
|
||||
DestroyWindow(winraw_joypad_msg_window);
|
||||
winraw_joypad_msg_window = NULL;
|
||||
return NULL;
|
||||
if (!winraw_joypad_create_msg_window())
|
||||
return NULL;
|
||||
|
||||
if (!winraw_joypad_register_devices())
|
||||
{
|
||||
DestroyWindow(winraw_joypad_msg_window);
|
||||
winraw_joypad_msg_window = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
winraw_joypad_enumerate_devices();
|
||||
@@ -928,7 +949,18 @@ static void winraw_joypad_joypad_destroy(void)
|
||||
|
||||
if (winraw_joypad_msg_window)
|
||||
{
|
||||
DestroyWindow(winraw_joypad_msg_window);
|
||||
/* A window is destroyed by the thread that made it. Read by the
|
||||
* poll, that is the polling thread - and this can be another:
|
||||
* a controller plugged in restarts the driver from the main
|
||||
* window's thread. The window is then asked to close, and goes
|
||||
* when its own thread next pumps. */
|
||||
if ( winraw_joypad_by_poll
|
||||
&& GetCurrentThreadId() != winraw_joypad_window_tid)
|
||||
PostMessageA(winraw_joypad_msg_window, WM_CLOSE, 0, 0);
|
||||
else
|
||||
DestroyWindow(winraw_joypad_msg_window);
|
||||
if (winraw_joypad_by_poll)
|
||||
winraw_queue_claim_thread(false);
|
||||
winraw_joypad_msg_window = NULL;
|
||||
}
|
||||
|
||||
@@ -1151,9 +1183,74 @@ static int16_t winraw_joypad_joypad_state(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read by the poll
|
||||
* ----------------
|
||||
* A controller's reports used to be taken wherever this driver's
|
||||
* window happened to be pumped. The window was made by init, on the
|
||||
* thread that starts the input driver; under threaded video that is
|
||||
* the video thread, so the PeekMessage() below, called from the main
|
||||
* thread, found nothing, and the reports were taken by the video
|
||||
* thread's pump: once per video frame, a PeekMessage() and a
|
||||
* GetRawInputData() apiece, and not at all while that thread waited
|
||||
* in a present. The poll then read whatever state that had left - a
|
||||
* stick or a button up to a frame old.
|
||||
*
|
||||
* Now, as for the keyboard and mouse (winraw_input.c, "Read by the
|
||||
* poll"), the window is made by the first poll, on the polling
|
||||
* thread, and the reports waiting are read in bulk when the poll
|
||||
* asks: the state is what the controller had sent at that moment, and
|
||||
* a frame's reports cost one call instead of two each. The read is
|
||||
* the keyboard and mouse driver's own - one read takes everything
|
||||
* waiting on the thread, and hands this driver its share through
|
||||
* winraw_joypad_take_hid() - so the two drivers make one read between
|
||||
* them. With another input driver this is the only reader.
|
||||
*
|
||||
* Arrivals and removals stay messages. They come through the raw
|
||||
* input queue, which a range without WM_INPUT in it does not look at,
|
||||
* so the thread's pump - which leaves raw input alone - does not see
|
||||
* them, and they are taken here. The range also lets through a report
|
||||
* that arrived since the read; the window procedure takes that as it
|
||||
* always did.
|
||||
*
|
||||
* RETROARCH_RAWINPUT_POLL=0 puts both drivers back as they were. */
|
||||
static bool winraw_joypad_poll_window_up(void)
|
||||
{
|
||||
if (!winraw_joypad_create_msg_window())
|
||||
return false;
|
||||
if (!winraw_joypad_register_devices())
|
||||
{
|
||||
DestroyWindow(winraw_joypad_msg_window);
|
||||
winraw_joypad_msg_window = NULL;
|
||||
return false;
|
||||
}
|
||||
winraw_joypad_window_tid = GetCurrentThreadId();
|
||||
winraw_queue_claim_thread(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void winraw_joypad_joypad_poll(void)
|
||||
{
|
||||
MSG msg;
|
||||
|
||||
if (winraw_joypad_by_poll)
|
||||
{
|
||||
if ( !winraw_joypad_msg_window
|
||||
&& !winraw_joypad_window_failed
|
||||
&& !winraw_joypad_poll_window_up())
|
||||
{
|
||||
winraw_joypad_window_failed = true;
|
||||
RARCH_ERR("[RawInput Joypad] Could not make the window to read through.\n");
|
||||
}
|
||||
|
||||
winraw_queue_read();
|
||||
|
||||
if (winraw_joypad_msg_window)
|
||||
while (PeekMessageA(&msg, winraw_joypad_msg_window,
|
||||
WM_INPUT_DEVICE_CHANGE, WM_INPUT, PM_REMOVE))
|
||||
DispatchMessageA(&msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Drain all pending messages for our hidden window.
|
||||
* TranslateMessage is omitted — we only handle WM_INPUT and
|
||||
* WM_INPUT_DEVICE_CHANGE, neither of which needs key translation. */
|
||||
|
||||
@@ -147,6 +147,10 @@ bool input_autoconfigure_connect(const char *name, const char *display_name,
|
||||
}
|
||||
bool input_autoconfigure_disconnect(unsigned port, const char *name)
|
||||
{ (void)port; (void)name; return true; }
|
||||
/* winraw_input.c's side of reading by the poll: not used here */
|
||||
bool winraw_raw_input_polled(void) { return false; }
|
||||
void winraw_queue_read(void) { }
|
||||
void winraw_queue_claim_thread(bool claim) { (void)claim; }
|
||||
|
||||
/* ---- The test ------------------------------------------------------- */
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
TARGET := winraw_joypad_poll_test.exe
|
||||
|
||||
# The real winraw joypad driver, cross-built with mingw-w64 and run
|
||||
# under Wine. winraw_joypad.c is included by the test itself, with its
|
||||
# RawInput and HidP calls routed to fakes describing a device; the
|
||||
# frontend symbols it links against are stubbed in the test.
|
||||
# make && make check
|
||||
REPO_ROOT := ../../..
|
||||
CC := x86_64-w64-mingw32-gcc
|
||||
|
||||
CFLAGS += -Wall -std=gnu89 -Wdeclaration-after-statement -g -O1 \
|
||||
-DRARCH_INTERNAL \
|
||||
-I$(REPO_ROOT) -I$(REPO_ROOT)/input/include \
|
||||
-I$(REPO_ROOT)/libretro-common/include -I$(REPO_ROOT)/deps
|
||||
|
||||
LDLIBS += -lhid -lsetupapi -luser32
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): winraw_joypad_poll_test.c \
|
||||
$(REPO_ROOT)/input/drivers_joypad/winraw_joypad.c
|
||||
$(CC) $(CFLAGS) -o $@ winraw_joypad_poll_test.c \
|
||||
$(REPO_ROOT)/libretro-common/compat/compat_strl.c $(LDLIBS)
|
||||
|
||||
# wine goes by several names; the first one found is used
|
||||
WINE ?= $(shell for w in wine wine64 /usr/lib/wine/wine64; do \
|
||||
command -v $$w >/dev/null 2>&1 && { echo $$w; break; }; done)
|
||||
|
||||
check: all
|
||||
@test -n "$(WINE)" || { echo "no wine" >&2; exit 1; }
|
||||
WINEDEBUG=-all $(WINE) ./$(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
.PHONY: all check clean
|
||||
@@ -0,0 +1,201 @@
|
||||
/* winraw joypad: its window, when raw input is read by the poll.
|
||||
*
|
||||
* The real driver, cross-built with mingw-w64 and run under Wine. The
|
||||
* driver is started from a second thread, as it is from the video
|
||||
* thread under threaded video; the test's main thread polls.
|
||||
*
|
||||
* Checked here:
|
||||
*
|
||||
* - not read by the poll (RETROARCH_RAWINPUT_POLL=0): init makes the
|
||||
* window, on init's thread, as it always did, and the poll makes no
|
||||
* bulk read;
|
||||
* - read by the poll: init makes no window; the first poll does, on
|
||||
* the polling thread, and marks that thread as one whose pump leaves
|
||||
* raw input alone; every poll makes one bulk read;
|
||||
* - an arrival or removal waiting for the window is taken by the poll;
|
||||
* - destroy on the polling thread takes the window down at once;
|
||||
* destroy from another thread - a controller plugged in restarts the
|
||||
* driver from the main window's thread - has it gone once its own
|
||||
* thread has pumped; either way the thread's mark is given back;
|
||||
* - a restart from that other thread, then a poll: a new window, on
|
||||
* the polling thread.
|
||||
*
|
||||
* Reports themselves cannot be injected under Wine. That a report
|
||||
* read in bulk reaches this driver is the other half, in
|
||||
* samples/input/winraw_poll; what this driver does with one is in
|
||||
* samples/input/winraw_joypad_axes. */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include "input/drivers_joypad/winraw_joypad.c"
|
||||
|
||||
/* ---- The frontend, as far as the driver links against it ------------ */
|
||||
|
||||
void RARCH_LOG(const char *fmt, ...) { (void)fmt; }
|
||||
void RARCH_WARN(const char *fmt, ...) { (void)fmt; }
|
||||
void RARCH_ERR(const char *fmt, ...) { (void)fmt; }
|
||||
void RARCH_DBG(const char *fmt, ...) { (void)fmt; }
|
||||
bool input_autoconfigure_connect(const char *name, const char *display_name,
|
||||
const char *phys, const char *driver, unsigned port,
|
||||
unsigned vid, unsigned pid)
|
||||
{
|
||||
(void)name; (void)display_name; (void)phys; (void)driver;
|
||||
(void)port; (void)vid; (void)pid;
|
||||
return true;
|
||||
}
|
||||
bool input_autoconfigure_disconnect(unsigned port, const char *name)
|
||||
{ (void)port; (void)name; return true; }
|
||||
|
||||
/* winraw_input.c's side of reading by the poll */
|
||||
static bool polled;
|
||||
static unsigned queue_reads;
|
||||
static int claims;
|
||||
static DWORD claim_tid;
|
||||
bool winraw_raw_input_polled(void) { return polled; }
|
||||
void winraw_queue_read(void) { queue_reads++; }
|
||||
void winraw_queue_claim_thread(bool claim)
|
||||
{
|
||||
if (claim)
|
||||
{
|
||||
claims++;
|
||||
claim_tid = GetCurrentThreadId();
|
||||
}
|
||||
else
|
||||
claims--;
|
||||
}
|
||||
|
||||
static int failures;
|
||||
|
||||
#define CHECK(cond, ...) do { \
|
||||
if (!(cond)) { printf(" FAIL "); printf(__VA_ARGS__); printf("\n"); failures++; } \
|
||||
} while (0)
|
||||
|
||||
/* ---- the other thread: starts and stops the driver ------------------ */
|
||||
static DWORD other_tid;
|
||||
static void *other_result;
|
||||
|
||||
static DWORD WINAPI do_init(LPVOID arg)
|
||||
{
|
||||
(void)arg;
|
||||
other_tid = GetCurrentThreadId();
|
||||
other_result = winraw_joypad_joypad_init(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI do_destroy(LPVOID arg)
|
||||
{
|
||||
(void)arg;
|
||||
winraw_joypad_joypad_destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void on_other_thread(LPTHREAD_START_ROUTINE fn)
|
||||
{
|
||||
HANDLE t = CreateThread(NULL, 0, fn, NULL, 0, NULL);
|
||||
WaitForSingleObject(t, 5000);
|
||||
CloseHandle(t);
|
||||
}
|
||||
|
||||
/* the frontend's pump on a thread whose raw input the poll reads */
|
||||
static void pump_leaving_raw_input(void)
|
||||
{
|
||||
MSG msg;
|
||||
for (;;)
|
||||
{
|
||||
bool any = false;
|
||||
if (PeekMessageA(&msg, 0, 0, WM_INPUT - 1, PM_REMOVE))
|
||||
{
|
||||
DispatchMessageA(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (PeekMessageA(&msg, 0, WM_INPUT + 1, 0xFFFFFFFF, PM_REMOVE))
|
||||
{
|
||||
DispatchMessageA(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (!any)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DWORD main_tid = GetCurrentThreadId();
|
||||
HWND w;
|
||||
MSG probe;
|
||||
|
||||
/* ---- not read by the poll: as it was -------------------------- */
|
||||
polled = false;
|
||||
CHECK(winraw_joypad_joypad_init(NULL) != NULL, "init, not read by the poll");
|
||||
w = winraw_joypad_msg_window;
|
||||
CHECK(w && GetWindowThreadProcessId(w, NULL) == main_tid,
|
||||
"not read by the poll: init did not make the window on its own thread");
|
||||
winraw_joypad_joypad_poll();
|
||||
CHECK(queue_reads == 0 && claims == 0,
|
||||
"not read by the poll: %u bulk reads, %d threads marked", queue_reads, claims);
|
||||
winraw_joypad_joypad_destroy();
|
||||
CHECK(!IsWindow(w), "not read by the poll: destroy left the window");
|
||||
printf(" ok =0: the window is made by init, no bulk read, no thread marked\n");
|
||||
|
||||
/* ---- read by the poll ----------------------------------------- */
|
||||
polled = true;
|
||||
on_other_thread(do_init);
|
||||
CHECK(other_result != NULL, "init on the other thread");
|
||||
CHECK(!winraw_joypad_msg_window, "init made the window");
|
||||
winraw_joypad_joypad_poll();
|
||||
w = winraw_joypad_msg_window;
|
||||
CHECK(w && IsWindow(w) && GetWindowThreadProcessId(w, NULL) == main_tid
|
||||
&& main_tid != other_tid,
|
||||
"the first poll did not make the window on the polling thread");
|
||||
CHECK(claims == 1 && claim_tid == main_tid, "the polling thread was not marked (%d)", claims);
|
||||
CHECK(queue_reads == 1, "the first poll made %u bulk reads", queue_reads);
|
||||
winraw_joypad_joypad_poll();
|
||||
winraw_joypad_joypad_poll();
|
||||
CHECK(queue_reads == 3 && winraw_joypad_msg_window == w && claims == 1,
|
||||
"three polls: %u bulk reads, %d marks", queue_reads, claims);
|
||||
printf(" ok read by the poll: init makes no window, the first poll does on its own thread, one bulk read a poll\n");
|
||||
|
||||
/* ---- an arrival or removal waiting for the window ------------- */
|
||||
/* a removal of a device the driver does not have: taken, and
|
||||
* nothing to do */
|
||||
PostMessageA(w, WM_INPUT_DEVICE_CHANGE, GIDC_REMOVAL, (LPARAM)0x1234);
|
||||
pump_leaving_raw_input();
|
||||
winraw_joypad_joypad_poll();
|
||||
CHECK(!PeekMessageA(&probe, w, WM_INPUT_DEVICE_CHANGE, WM_INPUT_DEVICE_CHANGE, PM_NOREMOVE),
|
||||
"the poll left a device change waiting");
|
||||
printf(" ok a device change waiting for the window is taken by the poll\n");
|
||||
|
||||
/* ---- destroy from another thread ------------------------------ */
|
||||
on_other_thread(do_destroy);
|
||||
CHECK(winraw_joypad_msg_window == NULL && claims == 0,
|
||||
"destroy from another thread: window %p, %d marks",
|
||||
(void*)winraw_joypad_msg_window, claims);
|
||||
pump_leaving_raw_input();
|
||||
CHECK(!IsWindow(w), "destroyed from another thread, the window is still there after its thread pumped");
|
||||
printf(" ok destroy from another thread: the window goes when its own thread pumps\n");
|
||||
|
||||
/* ---- a restart from that thread, then a poll ------------------ */
|
||||
on_other_thread(do_init);
|
||||
CHECK(other_result != NULL && !winraw_joypad_msg_window, "restart from the other thread");
|
||||
winraw_joypad_joypad_poll();
|
||||
w = winraw_joypad_msg_window;
|
||||
CHECK(w && GetWindowThreadProcessId(w, NULL) == main_tid && claims == 1,
|
||||
"after a restart the poll did not make a new window on its thread");
|
||||
|
||||
/* ---- destroy on the polling thread ---------------------------- */
|
||||
winraw_joypad_joypad_destroy();
|
||||
CHECK(!IsWindow(w) && claims == 0, "destroy on the polling thread left the window or the mark");
|
||||
printf(" ok restart then poll: a new window on the polling thread; destroy there takes it down at once\n");
|
||||
|
||||
if (failures)
|
||||
{
|
||||
printf("FAIL winraw_joypad_poll_test: %d\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("PASS winraw_joypad_poll_test\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -22,6 +22,9 @@
|
||||
* message carries and reads the rest in bulk, in order, and their
|
||||
* key events still wait for the poll;
|
||||
* - with nothing waiting, a poll makes one read and a pump makes none;
|
||||
* - a read made for the controller driver, whose poll comes first, is
|
||||
* not repeated by this driver's poll, which still delivers what it
|
||||
* took;
|
||||
* - a pump that asks for the messages below WM_INPUT and above it, as
|
||||
* the frontend's does on this thread, leaves the reports in the
|
||||
* queue, and the next poll reads them all in one;
|
||||
@@ -282,7 +285,7 @@ int main(void)
|
||||
raw_window = wr->window;
|
||||
CHECK(raw_window && IsWindow(raw_window), "the first poll made no window");
|
||||
CHECK(GetWindowThreadProcessId(raw_window, NULL) == main_tid
|
||||
&& main_tid != vt_tid && winraw_drain_tid == main_tid,
|
||||
&& main_tid != vt_tid && winraw_poll_owns_thread(),
|
||||
"the window belongs to thread %lu; the poll's is %lu, init's %lu",
|
||||
(unsigned long)GetWindowThreadProcessId(raw_window, NULL),
|
||||
(unsigned long)main_tid, (unsigned long)vt_tid);
|
||||
@@ -401,6 +404,35 @@ int main(void)
|
||||
printf(" ok nothing waiting: a poll makes one read, a pump makes none\n");
|
||||
}
|
||||
|
||||
/* ---- one read for both drivers --------------------------- */
|
||||
/* winraw_joypad's poll comes first and makes the read through
|
||||
* winraw_queue_read(); this driver's poll must not make a
|
||||
* second, and still hands on what the first one took. */
|
||||
{
|
||||
unsigned long total0;
|
||||
wr->drained = wr->by_message = 0;
|
||||
key(0x21, true);
|
||||
key(0x21, false);
|
||||
Sleep(150);
|
||||
total0 = wr->drain_reads + wr->drain_empty;
|
||||
winraw_queue_read();
|
||||
CHECK(wr->drained == 2 && wr->kb_keys[0x21] == 0 && seen_n == 0,
|
||||
"the read made for the controller driver took %lu reports, delivered %u events",
|
||||
wr->drained, seen_n);
|
||||
winraw_poll(wr);
|
||||
CHECK(wr->drain_reads + wr->drain_empty - total0 == 1,
|
||||
"the two polls made %lu reads between them, not one",
|
||||
wr->drain_reads + wr->drain_empty - total0);
|
||||
CHECK(seen_n == 2 && seen[0].down && seen[0].code == 0x21 && !seen[1].down,
|
||||
"the poll did not hand on what that read took (%u events)", seen_n);
|
||||
seen_n = 0;
|
||||
total0 = wr->drain_reads + wr->drain_empty;
|
||||
winraw_poll(wr);
|
||||
CHECK(wr->drain_reads + wr->drain_empty - total0 == 1,
|
||||
"the next poll, on its own, did not make its read");
|
||||
printf(" ok one read for both drivers: made for the controller driver, not repeated by this poll\n");
|
||||
}
|
||||
|
||||
/* ---- the frontend's pump on this thread ----------------- */
|
||||
{
|
||||
unsigned long reads0;
|
||||
@@ -497,8 +529,12 @@ int main(void)
|
||||
/* ---- free ----------------------------------------------------- */
|
||||
winraw_free(wr);
|
||||
CHECK(!IsWindow(raw_window), "free left the window");
|
||||
CHECK(winraw_drain_tid == 0, "free left the bulk-read thread set");
|
||||
printf(" ok free: the window is gone\n");
|
||||
CHECK(!winraw_poll_owns_thread(), "free left the thread marked as read by the poll");
|
||||
/* a read made for the controller driver with no keyboard and mouse
|
||||
* driver there: nothing to take, nothing to touch */
|
||||
winraw_queue_read();
|
||||
winraw_queue_was_read = false;
|
||||
printf(" ok free: the window is gone, the thread's mark with it\n");
|
||||
|
||||
/* ---- restarted, as it is with every video driver restart ------ */
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user