Skip to content
This repository was archived by the owner on Sep 24, 2025. It is now read-only.

Commit 82c1483

Browse files
authored
Merge branch 'VICE-Team:main' into main
2 parents 6c4d120 + 506c6ab commit 82c1483

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4566
-1501
lines changed

vice/doc/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ MISC_DOX = \
66
Documentation-Howto.txt \
77
Doxygen-Howto.txt \
88
iec-bus.txt \
9+
joystick.md \
910
Release-Howto.txt \
1011
vice.texi \
1112
gpl.texi

vice/doc/joystick.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# VICE Joystick API
2+
3+
> To get a nicely formatted HTML version of this document, including syntax
4+
> highlighting, use:
5+
>
6+
> `pandoc -s -t html -f gfm joystick.md > joystick.html`
7+
8+
9+
## Preface
10+
11+
This document describes the updated joystick API, which currently is a **work in
12+
progress**. All information herein is subject to change while the joystick code
13+
is being worked on. The inner workings of the actual emulation of the I/O system
14+
will not be described, just the translation of host device input to emulated
15+
joystick device, so no actual CIA/VIA emulation.
16+
17+
18+
## Overview of the joystick system in VICE
19+
20+
The joystick system in VICE is split into two parts: **common code** and
21+
**driver code**. The driver code is specific to an OS/UI, while the common code,
22+
as the name implies, is used for every OS/UI.
23+
24+
### Common code
25+
26+
The common code (in `src/joyport/`) is responsible for interpreting data from
27+
the drivers and passing that to the emulation, as well as handling mapping and
28+
calibration of host inputs to emulated inputs. It is also responsible for
29+
providing the UI with information on host and emulated devices, and at a later
30+
point, passing host input to the UI for mapping and calibration dialogs.
31+
32+
### Driver code
33+
34+
The driver code is responsible for reading data from a host device and passing
35+
that back to the common code, as well as providing the common code with a list
36+
of available host devices and their properties.
37+
38+
39+
## Changes in the separation of driver and common code
40+
41+
I've tried to keep the code required to implement a driver as small as possible,
42+
moving a number of responsibilities from the drivers to the common code.
43+
44+
* The old code would let the driver interpret raw axis and button values and
45+
send that back to the emulation (during the `poll()` callback). The drivers
46+
now simply pass the raw values to the common code, and the common code
47+
interprets those values with the help of the information on the host devices
48+
provided by the driver (while also doing calibration).
49+
50+
* A driver no longer needs to concern itself with ordering inputs, the common
51+
code handles that.
52+
53+
* Every input now has a unique *code*, which can be an event code (like with
54+
Linux' evdev), a simple index of the input (as in SDL) or a HID usage code
55+
(as on FreeBSD/NetBSD). The API provides drivers with methods of looking up
56+
axis, button and hat objects through their respective code.
57+
58+
* Event handlers in the common code now refer to inputs by instance, not index.
59+
So for an axis event a driver would call `joy_axis_event()` with a host device
60+
instance, axis instance and raw axis value.
61+
62+
63+
**TODO**: Proper (simple) description of `joystick_device_t` and its members
64+
`joystick_axis_t`, `joystick_button_t` and `joystick_hat_t`.
65+
66+
**TODO**: Explain ownership of objects (container assumes ownership of its
67+
elements and is responsible for freeing them after use, etc).
68+
69+
70+
## Implementing a driver
71+
72+
Implementing a driver should be fairly straightforward. A driver registers
73+
itself with the joystick system and adds host devices it has detected.
74+
75+
During joystick system initialization an arch-specific initialization function
76+
is called (and expected to be implemented by the driver), where the driver
77+
registers itself and adds host devices:
78+
79+
```C
80+
void joystick_arch_init(void)
81+
```
82+
83+
The function to register the driver is:
84+
85+
```C
86+
void joystick_driver_register(const joystick_driver_t *driver)
87+
```
88+
89+
Where `joystick_driver_t` is defined as:
90+
```C
91+
typedef struct joystick_driver_s {
92+
/** \brief Open host device for use */
93+
bool (*open) (joystick_device_t *);
94+
95+
/** \brief Poll host device */
96+
void (*poll) (joystick_device_t *);
97+
98+
/** \brief Close host device */
99+
void (*close) (joystick_device_t *);
100+
101+
/** \brief Optional method to free arch-specific device data */
102+
void (*priv_free)(void *);
103+
104+
/** \brief Function to call after registering a device
105+
*
106+
* This function is called after #joystick_device_register has processed
107+
* its argument. It can be used to customize mappings or calibration if so
108+
* required.
109+
*/
110+
void (*customize)(joystick_device_t *);
111+
112+
} joystick_driver_t;
113+
```
114+
115+
> Currently (re)opening a device hasn't been implemented yet, so the `open()`
116+
> method can be ignored, for now.
117+
118+
### Driver methods
119+
120+
The `poll()` method is called by the emulation at the end of *every emulated
121+
scanline*, and is expected to process any pending event data and pass that
122+
along to `joy_axis_event()`, `joy_button_event()` or `joy_hat_event()`.
123+
124+
The `close()` method should close the host device (e.g. close file descriptor)
125+
and put the device in a proper state for opening again. It should **not** free
126+
its private data in the `priv` member of the `joystick_device_t`, that is done
127+
in the `priv_free()` method, called by the joystick system on shutdown.
128+
It should also **not** free the joystick device instance, that again is done by
129+
the joystick system.
130+
131+
The `priv_free()` method (if used) is, as mentioned above, called on emulator
132+
shutdown (or once we implement plug-n-pray, on device unplugging), and can be
133+
used to free any arch-specific resources that cannot be contained in the
134+
`joystick_device_t` instance or its members.
135+
> For example: the DirectInput driver for Windows stores a `GUID` and an
136+
> `LPDIRECTINPUTDEVICE8` instance in `priv`.
137+
138+
The `customize()` method can be used to customize the default mapping and
139+
calibration applied by the joystick system when `joystick_device_register()` is
140+
called.
141+
142+
143+
### Example of driver implementation
144+
145+
The basic structure of a driver is the following:
146+
147+
```C
148+
149+
/* Some arch-specific data of a device (obvious pseudo code) */
150+
typedef struct foo_priv_s {
151+
FOO_DEVICE *foodev;
152+
} foo_priv_t;
153+
154+
155+
/* Declaration of driver methods */
156+
static joystick_driver_t foo_driver = {
157+
.poll = foo_poll,
158+
.close = foo_close
159+
.priv_free = foo_priv_free
160+
};
161+
162+
163+
/*
164+
* Called after the joystick system has initialized during emulator boot
165+
*/
166+
void joystick_arch_init(void)
167+
{
168+
/* Arch-specific initialization, if required */
169+
FOO_JOYSTICK_SYSTEM_INIT();
170+
171+
/* Register driver */
172+
joystick_driver_register(&foo_driver);
173+
174+
/* Iterate devices and register them with the joystick system */
175+
for (int i = 0; i < NUM_HOST_DEVICES; i++) {
176+
177+
joystick_device_t *joydev = joystick_device_new();
178+
179+
FOO_DEVICE *foodev = OPEN_FOO_DEVICE(i);
180+
181+
joystick_device_set_name(joydev, foodev->name);
182+
joystick_device_set_node(joydev, foodev->...); /* filesystem node of
183+
device, GUID string,
184+
whatever */
185+
joydev->vendor = foodev->vendor_id; /* USB HID vendor ID */
186+
joydev->product = foodev->product_id; /* USB HID product ID */
187+
188+
/* Iterate axes, buttons and perhaps hats of a device and add them */
189+
for (int a = 0; a < NUM_AXES(foodev); a++) {
190+
191+
joystick_axis_t *axis = joystick_axis_new(foodev->AXES[a].name);
192+
axis->code = foodev->AXES[a].code; /* some unique event code, can
193+
be HID usage, or just index
194+
of axis */
195+
/* set limits if available */
196+
axis->minimum = foodev->AXES[a].min; /* default is INT16_MIN */
197+
axis->maximum = foodev->AXES[a].max; /* default is INT16_MAX */
198+
199+
/* store arch-specific data in `priv` member */
200+
foo_priv_t *priv = lib_malloc(sizeof *priv);
201+
priv->foodev = foodev;
202+
joydev->priv = priv;
203+
204+
/* add axis to device: device takes ownership */
205+
joystick_device_add_axis(joydev, axis);
206+
}
207+
208+
/*
209+
* ... Do the same for buttons and hats, if available ...
210+
*/
211+
212+
/* Now register the device with the joystick system: the joystick
213+
* system takes ownership of the device and its members
214+
*/
215+
joystick_device_register(joydev);
216+
}
217+
}
218+
219+
220+
/*
221+
* Clean up any arch-specific resources here on emulator shutdown
222+
*/
223+
void joystick_arch_shutdown(void)
224+
{
225+
FOO_JOYSTICK_SYSTEM_CLOSE();
226+
}
227+
228+
229+
static void foo_poll(joystick_device_t *joydev)
230+
{
231+
foo_priv_t *priv = joydev->priv;
232+
233+
while (HAS_EVENT_PENDING(priv->foodev) {
234+
FOO_EVENT event = GET_EVENT(priv->foodev);
235+
236+
switch (event.type) {
237+
case FOO_AXIS:
238+
joystick_axis_t *axis = joystick_axis_from_code(joydev, event.code);
239+
joy_axis_event(axis, event.value);
240+
break;
241+
case FOO_BUTTON:
242+
joystick_button_t *button = joystick_button_from_code(joydev, event.code);
243+
joy_button_event(button, event.value);
244+
break;
245+
}
246+
}
247+
}
248+
249+
250+
static void foo_close(joystick_device_t *joydev)
251+
{
252+
foo_priv_t *priv = joydev->priv;
253+
254+
if (priv->foodev != NULL) {
255+
FOO_DEVICE_CLOSE(priv->foodev);
256+
priv->foodev = NULL;
257+
}
258+
}
259+
260+
261+
static void foo_priv_free(void *priv)
262+
{
263+
foo_priv_t *p = priv;
264+
265+
FOO_DEVICE_FREE(p->foodev);
266+
lib_free(p);
267+
}
268+
```
269+

vice/doc/mkdoxy.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ ARCH_INPUT+=" ../src/arch/shared/sounddrv"
7373
ARCH_GTK3_INPUT=" ../src/arch/gtk3"
7474
ARCH_GTK3_INPUT+=" ../src/arch/gtk3/widgets"
7575
ARCH_GTK3_INPUT+=" ../src/arch/gtk3/widgets/base"
76+
ARCH_GTK3_INPUT+=" ../src/arch/gtk3/joystickdrv"
7677

7778
ARCH_SDL_INPUT=" ../src/arch/sdl"
7879

@@ -87,6 +88,7 @@ INPUT+=" ../src/gfxoutputdrv"
8788
INPUT+=" ../src/hwsiddrv"
8889
INPUT+=" ../src/iecbus"
8990
INPUT+=" ../src/imagecontents"
91+
INPUT+=" ../src/joyport"
9092
INPUT+=" ../src/monitor"
9193
INPUT+=" ../src/parallel"
9294
INPUT+=" ../src/printerdrv"

0 commit comments

Comments
 (0)