On Thu, Jan 21, 2021 at 12:05:59PM -0700, Thomas Frohwein wrote: > On Sat, Jan 09, 2021 at 10:46:17AM -0700, Thomas Frohwein wrote: > > Hi, > > > > The next stepping stone in my project to improve out-of-the-box > > gamecontroller support. > > Haven't gotten much feedback since I sent this out. Below is a slightly > adjusted diff that generates the GUID in BSD_JoystickOpen rather than > BSD_JoystickInit, avoiding a duplicate open(2) - close(2) dance. > > Some testing would be appreciated - I tested it on 4 controllers, but > it would be good to see that this works with some more controllers. > > Here the Cliff notes of the diff: > - Enables using GUID to get joystick mapping from the included (or > game-specific) controller mapping. > - This replaces the prior workaround that was much more primitive, with > a fallback to standard XBox360 mapping and customization with a hand- > crafter SDL_GAMECONTROLLERCONFIG environment variable. > - The entire patch for SDL_gamecontroller.c can now be dropped. > - This gets us much closer to what's done on other platforms (Linux, > MacOS) to appropriately map a large number of gamecontrollers. > - The solution is functional, though not yet 100% ideal in regards to > the creation and storage of the GUID, as it's stored as the string > which results in conversion to the numerical and then back to the > string. We will figure out the solution to this eventually, but I > don't think this should hold up the transition as the current > solution does not adversely affect performance. > > More details in the original email. > > Any of {testing,feedback,oks} would be appreciated. > > > > > Background: On other platforms (Linux, MacOSX), SDL2 uses a GUID for > > gamecontrollers combined with a database [1] to provide the default > > button/axis mappings. This hasn't worked on OpenBSD (or apparently any > > *BSD) and the SDL2 code resorts to simply converting the first 16 chars > > of the device name to the GUID, which doesn't help with making use of > > the gamecontrollerdb [2]. Therefore, the way we have been handling this > > so far is that the env var SDL_GAMECONTROLLERCONFIG was available to > > hold a mapping string [3]. This poses a significant barrier, as users > > have to learn the syntax of the mapping strings and find one fitting > > their controller by trial and error mostly. > > > > This diff fixes the issue. To accomplish that, a couple of tasks are > > added: > > > > - Calculate and store the GUID (Credit goes to brynet@ for finding the > > GUID calculation and writing the initial proof-of-concept code). > > - Remove the workaround code in SDL_gamecontroller.c that would either > > read the mapping from SDL_GAMECONTROLLERCONFIG if present, or > > otherwise default to the (commonly used) XBox 360 controller layout. > > - Enable the mappings in SDL_gamecontrollerdb.h for OpenBSD. > > - Assign buttons correctly - not in the order that they appear in the > > HID report, but based on their numbering in the HID report descriptor. > > - Invert Y/Ry axes for the newer XInput-type gamecontrollers. This has > > been a particularly annoying problem, as USB standards recommend the > > positive directions to be left to right and far to near, but these > > controllers just use near-to-far as the positive direction. [4] > > > > The problem with the last bullet point is that the Y/Ry axis inversion > > needs to be applied *only* to the XInput devices, and *not* to the > > older DInput devices. I can't check for XBox360 and other interface > > subclasses (as used by the kernel in uhidev.c), as > > USB_GET_INTERFACE_DESC is not exposed in the uhid(4) driver (see > > uhid_do_ioctl() in uhid.c). > > The solution I came up with is admittedly a (minor) hack. It turns out > > that the old DInput devices report their axis values as an unsigned > > 8bit integer, while the newer ones use a signed 16bit integer. With my > > collection of gamecontrollers, checking if hitem.logical_maximum is > > greater than 255 allows to reliably pick the correct handling of the > > Y/Ry axes. > > > > Other caveats: > > > > - With the newly enabled GUID detection, SDL2 checks for a matching > > controller in gamecontrollerdb.txt in the application directory. > > I found that with one game (Cryptark), this contained bogus mappings > > for the XBox One controller (see the post on tech@), so some buttons > > and the Dpad didn't work until I removed that file. > > - For some reason, the GUID for DInput devices matches the MACOSX > > entries in SDL_gamecontrollerdb.h, but the XInput devices match the > > LINUX entries. It's gonna take me a bit of time to get to the bottom > > of this; in the meantime just allowing both sets of GUID mappings > > works for the devices here on my testing > > - The BSD_JoystickGetDeviceGUID() is not ideal at this point, as it > > takes the GUID stored as a string in char **joyguids and converts it > > into the raw numerical. I haven't figured out how to do the GUID > > calculation to avoid this additional transformation step yet. In the > > meantime, I don't notice performance issues and I don't think it's > > worth holding up the GUID mapping support over this. > > > > Testing: > > > > I tested this with sdl2-jstest (from sdl-jstest package) and few > > games (Owlboy, Cryptark) with these controllers that I have: > > XBox 360 controller (XInput), XBox One controller (XInput) (see separate > > mail on tech@), Logitech F310 (both DInput and XInput), Logitech Dual > > Action (DInput)). This maps everything correctly without additional > > configuration (as long as faulty gamecontrollerdb.txt in the game > > directory is removed). > > > > Testing with a variety of controllers would be useful to see if this > > really works across most of them as intended. If you do that, please > > let me know the mapping/functioning before and after the diff is > > applied. > > > > [1] > > https://hg.libsdl.org/SDL/file/2370522dac4c/src/joystick/SDL_gamecontrollerdb.h > > [2] > > https://hg.libsdl.org/SDL/file/2370522dac4c/src/joystick/bsd/SDL_bsdjoystick.c#l700 > > [3] https://marc.info/?l=openbsd-ports-cvs&m=152080802204959&w=2 > > [4] https://www.usb.org/sites/default/files/documents/hid1_11.pdf (page 20) > > Here the updated diff: > > Index: Makefile > =================================================================== > RCS file: /cvs/ports/devel/sdl2/Makefile,v > retrieving revision 1.32 > diff -u -p -r1.32 Makefile > --- Makefile 6 Jan 2021 22:32:08 -0000 1.32 > +++ Makefile 21 Jan 2021 18:47:48 -0000 > @@ -5,6 +5,7 @@ COMMENT= cross-platform multimedia libra > V= 2.0.14 > DISTNAME= SDL2-${V} > PKGNAME= sdl2-${V} > +REVISION= 0 > CATEGORIES= devel > MASTER_SITES= https://www.libsdl.org/release/ > > Index: patches/patch-src_joystick_SDL_gamecontroller_c > =================================================================== > RCS file: patches/patch-src_joystick_SDL_gamecontroller_c > diff -N patches/patch-src_joystick_SDL_gamecontroller_c > --- patches/patch-src_joystick_SDL_gamecontroller_c 6 Jan 2021 22:32:08 > -0000 1.7 > +++ /dev/null 1 Jan 1970 00:00:00 -0000 > @@ -1,50 +0,0 @@ > -$OpenBSD: patch-src_joystick_SDL_gamecontroller_c,v 1.7 2021/01/06 22:32:08 > thfr Exp $ > - > -enable GameController API the Linux fallback way (by posing as Xbox360 > -controller) > -also disable checking string "Xbox 360 Wireless Receiver", so for now > -everything will be Xbox360 controller (works with generic joysticks) > -map to SDL_GAMECONTROLLERCONFIG envvar if available > -Use layout for XBox360 controller to maximize compatibility because > -many controllers use this mapping > - > -Index: src/joystick/SDL_gamecontroller.c > ---- src/joystick/SDL_gamecontroller.c.orig > -+++ src/joystick/SDL_gamecontroller.c > -@@ -968,7 +968,7 @@ static char *SDL_PrivateGetControllerGUIDFromMappingSt > - SDL_memcpy(&pchGUID[8], &pchGUID[0], 4); > - SDL_memcpy(&pchGUID[0], "03000000", 8); > - } > --#elif __MACOSX__ > -+#elif defined(__MACOSX__) || defined(__OpenBSD__) > - if (SDL_strlen(pchGUID) == 32 && > - SDL_memcmp(&pchGUID[4], "000000000000", 12) == 0 && > - SDL_memcmp(&pchGUID[20], "000000000000", 12) == 0) { > -@@ -1131,17 +1131,21 @@ static ControllerMapping_t > *SDL_PrivateGetControllerMa > - ControllerMapping_t *mapping; > - > - mapping = SDL_PrivateGetControllerMappingForGUID(guid, SDL_FALSE); > --#ifdef __LINUX__ > -+#if defined(__LINUX__) || defined(__OpenBSD__) > - if (!mapping && name) { > -- if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) { > -- /* The Linux driver xpad.c maps the wireless dpad to buttons */ > -- SDL_bool existing; > -+ /* The Linux driver xpad.c maps the wireless dpad to buttons */ > -+ SDL_bool existing; > -+ char guid_str[1024]; > -+ SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); > -+ if (SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG) == NULL) { > - mapping = SDL_PrivateAddMappingForGUID(guid, > --"none,X360 Wireless > Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3", > -+"none,XBox360 > Controller,a:b7,b:b8,back:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b2,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b3,righttrigger:a5,rightx:a3,righty:a4~,start:b0,x:b9,y:b10", > - &existing, > SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT); > -+ } else { > -+ mapping = SDL_PrivateAddMappingForGUID(guid, > SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG), &existing, > SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT); > - } > - } > --#endif /* __LINUX__ */ > -+#endif /* __LINUX__ || __OpenBSD__ */ > - > - if (!mapping && name && !SDL_IsJoystickWGI(guid)) { > - if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || > SDL_strstr(name, "XBOX")) { > Index: patches/patch-src_joystick_SDL_gamecontrollerdb_h > =================================================================== > RCS file: patches/patch-src_joystick_SDL_gamecontrollerdb_h > diff -N patches/patch-src_joystick_SDL_gamecontrollerdb_h > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-src_joystick_SDL_gamecontrollerdb_h 21 Jan 2021 18:47:48 > -0000 > @@ -0,0 +1,26 @@ > +$OpenBSD$ > + > +enable controller detection by GUID on OpenBSD > +use both LINUX and MACOSX guids to match both XInput and DInput devices > + > +Index: src/joystick/SDL_gamecontrollerdb.h > +--- src/joystick/SDL_gamecontrollerdb.h.orig > ++++ src/joystick/SDL_gamecontrollerdb.h > +@@ -338,7 +338,7 @@ static const char *s_ControllerMappings [] = > + > "030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", > + "03000000101c0000171c000000000000,uRage > Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", > + #endif > +-#if defined(__MACOSX__) > ++#if defined(__MACOSX__) || defined(__OpenBSD__) > + "03000000c82d00000090000001000000,8BitDo FC30 > Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > + "03000000c82d00000090000001000000,8BitDo FC30 > Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > + "03000000c82d00001038000000010000,8BitDo FC30 > Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > +@@ -479,7 +479,7 @@ static const char *s_ControllerMappings [] = > + "03000000830500006020000000010000,iBuffalo SNES > Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > + "03000000830500006020000000000000,iBuffalo USB 2-axis 8-button > Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,", > + #endif > +-#if defined(__LINUX__) > ++#if defined(__LINUX__) || defined(__OpenBSD__) > + "03000000c82d00000090000011010000,8BitDo FC30 > Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > + "03000000c82d00000090000011010000,8BitDo FC30 > Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > + "05000000c82d00001038000000010000,8BitDo FC30 > Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", > Index: patches/patch-src_joystick_bsd_SDL_bsdjoystick_c > =================================================================== > RCS file: patches/patch-src_joystick_bsd_SDL_bsdjoystick_c > diff -N patches/patch-src_joystick_bsd_SDL_bsdjoystick_c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-src_joystick_bsd_SDL_bsdjoystick_c 21 Jan 2021 18:47:48 > -0000 > @@ -0,0 +1,125 @@ > +$OpenBSD$ > + > +assign buttons correctly > +get GUID using USB_GET_DEVICEINFO > +detect newer (XInput-style) gamecontroller if hitem.logical_maximum is > +> 255; if so invert y axes > + > +Index: src/joystick/bsd/SDL_bsdjoystick.c > +--- src/joystick/bsd/SDL_bsdjoystick.c.orig > ++++ src/joystick/bsd/SDL_bsdjoystick.c > +@@ -83,6 +83,9 @@ > + > + #ifdef __OpenBSD__ > + > ++#define DEV_USB 3 /* needed to get GUID from > USB_GET_DEVICEINFO */ > ++#define GUID_LEN 32 /* GUID string has length 32 */ > ++ > + #define HUG_DPAD_UP 0x90 > + #define HUG_DPAD_DOWN 0x91 > + #define HUG_DPAD_RIGHT 0x92 > +@@ -192,6 +195,9 @@ struct joystick_hwdata > + > + static char *joynames[MAX_JOYS]; > + static char *joydevnames[MAX_JOYS]; > ++#ifdef __OpenBSD__ > ++static char joyguids[MAX_JOYS][GUID_LEN]; > ++#endif > + > + static int report_alloc(struct report *, struct report_desc *, int); > + static void report_free(struct report *); > +@@ -222,6 +228,9 @@ BSD_JoystickInit(void) > + > + SDL_memset(joynames, 0, sizeof(joynames)); > + SDL_memset(joydevnames, 0, sizeof(joydevnames)); > ++#ifdef __OpenBSD__ > ++ SDL_memset(joyguids, 0, sizeof(char) * MAX_JOYS * GUID_LEN); > ++#endif > + > + for (i = 0; i < MAX_UHID_JOYS; i++) { > + SDL_Joystick nj; > +@@ -356,6 +365,9 @@ BSD_JoystickOpen(SDL_Joystick *joy, int device_index) > + #endif > + int fd; > + int i; > ++#ifdef __OpenBSD__ > ++ struct usb_device_info di; > ++#endif > + > + fd = open(path, O_RDONLY); > + if (fd == -1) { > +@@ -434,6 +446,17 @@ BSD_JoystickOpen(SDL_Joystick *joy, int device_index) > + } > + desc_failed: > + #endif > ++#if defined(__OpenBSD__) > ++ if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1) { > ++ SDL_snprintf(joyguids[numjoysticks], > ++ SDL_arraysize(joyguids[device_index]), > ++ "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", > ++ DEV_USB & 0xFF, DEV_USB >> 8, > ++ di.udi_vendorNo & 0xFF, di.udi_vendorNo >> 8, > ++ di.udi_productNo & 0xFF, di.udi_productNo >> 8, > ++ di.udi_releaseNo & 0xFF, di.udi_releaseNo >> 8); > ++ } > ++#endif > + if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) { > + goto usberr; > + } > +@@ -544,6 +567,7 @@ BSD_JoystickUpdate(SDL_Joystick *joy) > + Sint32 v; > + #ifdef __OpenBSD__ > + Sint32 dpad[4] = {0, 0, 0, 0}; > ++ int actualbutton; > + #endif > + > + #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || > defined(__FreeBSD_kernel__) || defined(__DragonFly_) > +@@ -618,6 +642,18 @@ BSD_JoystickUpdate(SDL_Joystick *joy) > + naxe = joy->hwdata->axis_map[joyaxe]; > + /* scaleaxe */ > + v = (Sint32) hid_get_data(REP_BUF_DATA(rep), > &hitem); > ++#ifdef __OpenBSD__ > ++ /* XInput controllermapping relies on inverted > Y axes. > ++ * These devices have a 16bit signed space, as > opposed > ++ * to older DInput devices (8bit unsigned), so > ++ * hitem.logical_maximum can be used to > differentiate them. > ++ */ > ++ if ((joyaxe == JOYAXE_Y || joyaxe == JOYAXE_RY) > ++ && hitem.logical_maximum > 255) { > ++ if (v != 0) > ++ v = ~v; > ++ } > ++#endif > + v -= (hitem.logical_maximum + > + hitem.logical_minimum + 1) / 2; > + v *= 32768 / > +@@ -652,7 +688,12 @@ BSD_JoystickUpdate(SDL_Joystick *joy) > + } > + case HUP_BUTTON: > + v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); > ++#ifdef __OpenBSD__ > ++ actualbutton = HID_USAGE(hitem.usage) - 1; /* sdl > buttons are zero-based */ > ++ SDL_PrivateJoystickButton(joy, actualbutton, v); > ++#else > + SDL_PrivateJoystickButton(joy, nbutton, v); > ++#endif > + nbutton++; > + break; > + default: > +@@ -697,11 +738,16 @@ static SDL_JoystickGUID > + BSD_JoystickGetDeviceGUID( int device_index ) > + { > + SDL_JoystickGUID guid; > ++#ifdef __OpenBSD__ > ++ guid = SDL_JoystickGetGUIDFromString(joyguids[device_index]); > ++ return guid; > ++#else > + /* the GUID is just the first 16 chars of the name for now */ > + const char *name = BSD_JoystickGetDeviceName( device_index ); > + SDL_zero( guid ); > + SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); > + return guid; > ++#endif > + } > + > + static int
I tested this on my generic USB SNES controller (along with the ujoy patch), and it works perfectly. It even finds a better GUID than the one I had been using in my local SDL_GAMECONTROLLERCONFIG override. Found 1 joystick(s) Joystick Name: 'Joystick (0)' Joystick GUID: 030000001008000001e5000006010000 Joystick Number: 0 Number of Axes: 2 Number of Buttons: 10 Number of Hats: 0 Number of Balls: 0 GameController: Name: 'NEXT SNES Controller' Mapping: '030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,' Tested with: uhidev4 at uhub0 port 12 configuration 1 interface 0 "vendor 0x0810 usb gamepad" rev 1.00/1.06 addr 2 uhidev4: iclass 3/0, 1 report id ujoy0 at uhidev4 reportid 1: input=7, output=0, feature=0 ok brynet@ (along with the /dev/ujoy/%d patch). -Bryan.