Skip to content

Commit 9ed0479

Browse files
kakraJacob Essexatar-axis
committed
xpadneo, mouse: Implement mouse support
Add an optional "mouse profile" that turns supported Xbox controllers into a couch-friendly mouse/keyboard input device. The mode can be toggled via Guide+Select and reuses the existing input infrastructure by registering an additional (synthetic) mouse device. In mouse mode: - left stick moves the pointer (REL_X/REL_Y) - right stick scrolls (REL_WHEEL/REL_HWHEEL) - triggers act as left/right mouse buttons with hysteresis - shoulder buttons provide back/forward - D-pad generates cursor keys (keyboard interface) - A/B map to Enter/Escape (keyboard interface) - X triggers the on-screen keyboard key event (consumer control) Movement and scrolling are emitted periodically via a timer to provide a stable update rate and to keep input processing isolated from HID report timing. Co-authored-by: Jacob Essex <git@jacobessex.com> Co-authored-by: Florian Dollinger <dollinger.florian@gmx.de> Closes: atar-axis#99 Closes: atar-axis#105 Closes: atar-axis#160 Closes: atar-axis#511 Fixes: atar-axis#333 See-also: atar-axis#419 See-also: atar-axis#435 Signed-off-by: Kai Krakow <kai@kaishome.de>
1 parent 87df303 commit 9ed0479

File tree

8 files changed

+305
-17
lines changed

8 files changed

+305
-17
lines changed

docs/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ PID 0x0B22 while the other models identify with PID 0x0B13. This has some known
9999
- Supports customization through profiles (work in progress)
100100
- Optional high-precision mode for Wine/Proton users (disables dead zones so games don't apply an additional one)
101101
- Share button support on supported controllers
102+
- Works as a mouse if you are in couch-mode (press <key>Guide</key>+<key>Select</key>)
102103

103104

104105
## Unavailable Features
@@ -253,6 +254,24 @@ or Y while holding down the Xbox logo button. However, the following caveats app
253254
parameter `disable_shift_mode`).
254255

255256

257+
### Mouse Profile Support
258+
259+
The driver can switch to emulating a mouse (and limited keyboard) on all supported controllers. Press
260+
<key>Guide</key>+<key>Select</key> to switch to mouse mode or back to controller mode:
261+
262+
- Left stick moves the mouse pointer
263+
- Right stick can be used as a scrolling wheel/ball
264+
- Triggers for left and right mouse button
265+
- Shoulder buttons for back and forward button
266+
- D-pad for cursor movement
267+
- Menu to show on-screen keyboard (FIXME possible? KEY_KEYBOARD)
268+
- A for <key>Enter</key>
269+
- B for <key>Escape</key>
270+
271+
**Important:** The mouse profile won't work if you disabled the shift-mode of the Xbox logo button (module parameter
272+
`disable_shift_mode`).
273+
274+
256275
## Getting Started
257276

258277
### Distribution Packages

hid-xpadneo/src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ hid-xpadneo-y += xpadneo.o
1010
$(obj)/xpadneo.c: $(src)/hid-xpadneo.c
1111
cp $< $@
1212

13-
hid-xpadneo-y += xpadneo/core.o xpadneo/consumer.o xpadneo/keyboard.o
13+
hid-xpadneo-y += xpadneo/core.o xpadneo/consumer.o xpadneo/keyboard.o xpadneo/mouse.o

hid-xpadneo/src/hid-xpadneo.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -785,20 +785,6 @@ static const __u8 *xpadneo_report_fixup(struct hid_device *hdev, __u8 *rdesc, un
785785
return rdesc;
786786
}
787787

788-
static void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
789-
{
790-
if (xdata->mouse_mode) {
791-
xdata->mouse_mode = false;
792-
hid_info(xdata->hdev, "mouse mode disabled\n");
793-
} else {
794-
xdata->mouse_mode = true;
795-
hid_info(xdata->hdev, "mouse mode enabled\n");
796-
}
797-
798-
/* Indicate that a request was made */
799-
xdata->profile_switched = true;
800-
}
801-
802788
static void xpadneo_switch_profile(struct xpadneo_devdata *xdata, const u8 profile,
803789
const bool emulated)
804790
{
@@ -905,6 +891,9 @@ static int xpadneo_raw_event(struct hid_device *hdev, struct hid_report *report,
905891
}
906892
}
907893

894+
if (xpadneo_mouse_raw_event(xdata, report, data, reportsize))
895+
return -1;
896+
908897
return 0;
909898
}
910899

@@ -937,6 +926,7 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
937926
return 0;
938927
default:
939928
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
929+
return 0;
940930
}
941931

942932
if (param_disable_deadzones) {
@@ -990,6 +980,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
990980
struct input_dev *gamepad = xdata->gamepad;
991981
struct input_dev *keyboard = xdata->keyboard;
992982

983+
if (xpadneo_mouse_event(xdata, usage, value))
984+
goto stop_processing;
985+
993986
if ((usage->type == EV_KEY) && (usage->code == BTN_PADDLES(0))) {
994987
if (gamepad && xdata->profile == 0) {
995988
/* report the paddles individually */
@@ -1320,6 +1313,10 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
13201313
if (ret)
13211314
return ret;
13221315

1316+
ret = xpadneo_init_mouse(xdata);
1317+
if (ret)
1318+
return ret;
1319+
13231320
ret = xpadneo_init_hw(hdev);
13241321
if (ret) {
13251322
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1331,6 +1328,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
13311328
if (ret)
13321329
hid_err(hdev, "could not initialize ff, continuing anyway\n");
13331330

1331+
timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
1332+
mod_timer(&xdata->mouse_timer, jiffies);
1333+
13341334
hid_info(hdev, "%s connected\n", xdata->battery.name);
13351335

13361336
return 0;
@@ -1366,6 +1366,7 @@ static void xpadneo_remove(struct hid_device *hdev)
13661366
hdev->product = xdata->original_product;
13671367
}
13681368

1369+
timer_delete_sync(&xdata->mouse_timer);
13691370
cancel_delayed_work_sync(&xdata->ff_worker);
13701371

13711372
kfree(xdata->battery.name);

hid-xpadneo/src/xpadneo.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define XPADNEO_H_FILE
1313

1414
#include <linux/hid.h>
15+
#include <linux/timer.h>
1516
#include <linux/version.h>
1617

1718
#include "hid-ids.h"
@@ -20,6 +21,16 @@
2021
#error "kernel version 4.19.0+ required for ida_simple_{alloc,free}()"
2122
#endif
2223

24+
/* v6.15: del_timer_sync() renamed to timer_delete_sync() */
25+
#if KERNEL_VERSION(6, 15, 0) > LINUX_VERSION_CODE
26+
#define timer_delete_sync(t) del_timer_sync(t)
27+
#endif
28+
29+
/* v6.16: from_timer() renamed to timer_container_of() */
30+
#ifndef timer_container_of
31+
#define timer_container_of(v, c, t) from_timer(v, c, t)
32+
#endif
33+
2334
#ifndef VERSION
2435
#error "xpadneo version not defined"
2536
#endif
@@ -151,8 +162,8 @@ struct xpadneo_devdata {
151162

152163
/* logical device interfaces */
153164
struct hid_device *hdev;
154-
struct input_dev *consumer, *gamepad, *keyboard;
155-
bool consumer_sync, gamepad_sync, keyboard_sync;
165+
struct input_dev *consumer, *gamepad, *keyboard, *mouse;
166+
bool consumer_sync, gamepad_sync, keyboard_sync, mouse_sync;
156167
short int missing_reported;
157168

158169
/* revert fixups on removal */
@@ -169,6 +180,14 @@ struct xpadneo_devdata {
169180

170181
/* mouse mode */
171182
bool mouse_mode;
183+
struct timer_list mouse_timer;
184+
struct {
185+
s32 rel_x, rel_y, wheel_x, wheel_y;
186+
s32 rel_x_err, rel_y_err, wheel_x_err, wheel_y_err;
187+
struct {
188+
bool left, right;
189+
} analog_button;
190+
} mouse_state;
172191

173192
/* trigger scale */
174193
struct {
@@ -206,8 +225,13 @@ struct xpadneo_devdata {
206225

207226
extern int xpadneo_init_consumer(struct xpadneo_devdata *);
208227
extern int xpadneo_init_keyboard(struct xpadneo_devdata *);
228+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
209229
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
230+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
231+
extern int xpadneo_mouse_raw_event(struct xpadneo_devdata *, struct hid_report *, u8 *, int);
210232
extern void xpadneo_report(struct hid_device *, struct hid_report *);
211233
extern void xpadneo_core_missing(struct xpadneo_devdata *, u32);
234+
extern void xpadneo_mouse_report(struct timer_list *);
235+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
212236

213237
#endif

hid-xpadneo/src/xpadneo/consumer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ extern int xpadneo_init_consumer(struct xpadneo_devdata *xdata)
1818
return ret;
1919
}
2020

21+
/* enable consumer events for mouse mode */
22+
input_set_capability(xdata->consumer, EV_KEY, KEY_ONSCREEN_KEYBOARD);
23+
2124
if (synth) {
2225
ret = input_register_device(xdata->consumer);
2326
if (ret) {

hid-xpadneo/src/xpadneo/core.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ extern void xpadneo_report(struct hid_device *hdev, struct hid_report *report)
5555
xdata->keyboard_sync = false;
5656
input_sync(xdata->keyboard);
5757
}
58+
59+
if (xdata->mouse && xdata->mouse_sync) {
60+
xdata->mouse_sync = false;
61+
input_sync(xdata->mouse);
62+
}
5863
}
5964

6065
extern void xpadneo_core_missing(struct xpadneo_devdata *xdata, u32 flag)
@@ -64,6 +69,9 @@ extern void xpadneo_core_missing(struct xpadneo_devdata *xdata, u32 flag)
6469
if ((xdata->missing_reported & flag) == 0) {
6570
xdata->missing_reported |= flag;
6671
switch (flag) {
72+
case XPADNEO_MISSING_CONSUMER:
73+
hid_err(hdev, "consumer control not detected\n");
74+
break;
6775
case XPADNEO_MISSING_GAMEPAD:
6876
hid_err(hdev, "gamepad not detected\n");
6977
break;

hid-xpadneo/src/xpadneo/keyboard.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ extern int xpadneo_init_keyboard(struct xpadneo_devdata *xdata)
2121
/* enable key events for keyboard */
2222
input_set_capability(xdata->keyboard, EV_KEY, BTN_SHARE);
2323

24+
/* enable key events for mouse mode */
25+
input_set_capability(xdata->keyboard, EV_KEY, KEY_ESC);
26+
input_set_capability(xdata->keyboard, EV_KEY, KEY_ENTER);
27+
input_set_capability(xdata->keyboard, EV_KEY, KEY_UP);
28+
input_set_capability(xdata->keyboard, EV_KEY, KEY_LEFT);
29+
input_set_capability(xdata->keyboard, EV_KEY, KEY_RIGHT);
30+
input_set_capability(xdata->keyboard, EV_KEY, KEY_DOWN);
31+
2432
if (synth) {
2533
ret = input_register_device(xdata->keyboard);
2634
if (ret) {

0 commit comments

Comments
 (0)