--- dlls/winejoystick.drv/Makefile.in | 3 +- dlls/winejoystick.drv/joystick_freebsd.c | 498 +++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 dlls/winejoystick.drv/joystick_freebsd.c diff --git a/dlls/winejoystick.drv/Makefile.in b/dlls/winejoystick.drv/Makefile.in index f6d3052..afc6f5f 100644 --- a/dlls/winejoystick.drv/Makefile.in +++ b/dlls/winejoystick.drv/Makefile.in @@ -1,8 +1,9 @@ MODULE = winejoystick.drv IMPORTS = winmm user32 -EXTRALIBS = $(IOKIT_LIBS) +EXTRALIBS = $(IOKIT_LIBS) -lusbhid C_SRCS = \ joystick.c \ + joystick_freebsd.c \ joystick_linux.c \ joystick_osx.c diff --git a/dlls/winejoystick.drv/joystick_freebsd.c b/dlls/winejoystick.drv/joystick_freebsd.c new file mode 100644 index 0000000..e1ce2d4 --- /dev/null +++ b/dlls/winejoystick.drv/joystick_freebsd.c @@ -0,0 +1,498 @@ +/* + * WinMM joystick driver implementation for FreeBSD + * + * Copyright 1997 Andreas Mohr + * Copyright 2000 Wolfgang Schwotzer + * Copyright 2002 David Hagood + * Copyright 2015 Bruno Jesus + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * NOTES: + * + * - supports only USB HID joysticks. + */ + +//#ifdef HAVE_USBHID_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "joystick.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(joystick); + +#define MAX_JOYSTICKS 8 +#define MAX_HID_DEVICE_INDEX 32 /* limit of /dev/uhidX devices to check */ +#define HID_INPUT_BIT (1 << hid_input) +#define CENTERED_AXIS_VALUE 32767 +#define HID_DEV_PATH "/dev/uhid%d" + +static struct JOYDATA +{ + DWORD driver_id; /* the number of the requested joystick */ + int hid_id; /* the number of the device in /dev/uhidX */ + BOOL in_use; + int dev; + struct + { + struct report_desc *desc; + char *buffer; + int buf_sz; + int rid; /* report id */ + } hid; + struct + { + int buttons, hats, balls, axes; + ULONG bits; + } caps; + struct + { + UINT buttons; + int button_count; + int axes[6], pov; + } stat; +} joydata[MAX_JOYSTICKS]; + +static int init; + +/* return the struct for the desired joystick index */ +static struct JOYDATA* joyget(DWORD_PTR id) +{ + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + { + if (id == (DWORD_PTR) &joydata[i] && joydata[i].in_use) + return &joydata[i]; + } + return NULL; +} + +/* check if the requested /dev/uhidX is already in use */ +static BOOL is_uhid_used(int hid_id) +{ + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + { + if (joydata[i].in_use && joydata[i].dev >= 0 && joydata[i].hid_id == hid_id) + return TRUE; + } + return FALSE; +} + +/* set defaults for a joystick that was not activated yet */ +static void init_stat_data(struct JOYDATA *js) +{ + int i; + js->stat.button_count = 0; + js->stat.buttons = 0; + js->stat.pov = 0; + js->stat.axes[0] = CENTERED_AXIS_VALUE; + js->stat.axes[1] = CENTERED_AXIS_VALUE; + for (i = 2; i < sizeof(js->stat.axes) / sizeof(js->stat.axes[0]); i++) + js->stat.axes[i] = 0; + if(js->caps.hats) + js->stat.pov = JOY_POVCENTERED; +} + +/* set a value to an axis */ +static BOOL set_axis(struct JOYDATA *js, int axis, int value) +{ + switch (axis) + { + case HUG_X: + js->stat.axes[0] = value; + break; + case HUG_Y: + js->stat.axes[1] = value; + break; + case HUG_Z: + case HUG_SLIDER: + js->stat.axes[2] = value; + break; + case HUG_RX: + js->stat.axes[3] = value; + break; + case HUG_RY: + js->stat.axes[4] = value; + break; + case HUG_RZ: + case HUG_WHEEL: + js->stat.axes[5] = value; + break; + default: + return FALSE; + } + return TRUE; +} + +/* convert the current read value from the old range to a new desired range */ +int adjust_axis_value(int v, int cur_min, int cur_max, int new_min, int new_max) +{ + int cur_range, new_range; + + cur_range = cur_max - cur_min + 1; + if (!cur_range || v < 0) + v = 0; + else + { + new_range = new_max - new_min; + printf("OLD %d (%d-%d) - ",v,cur_min,cur_max); + v = (((v - cur_min) * new_range) / cur_range) + new_min; + printf("NEW %d (%d-%d)\n",v,new_min,new_max); + } + return v; +} + +/* find out if the opened HID descriptor is really a joystick and get + * the informations required from it */ +static BOOL hid_parse(struct JOYDATA *js) +{ + struct hid_item item; + struct hid_data *data; + BOOL is_js = FALSE; + + js->hid.rid = hid_get_report_id(js->dev); + js->hid.buf_sz = hid_report_size(js->hid.desc, hid_input, js->hid.rid); + if (js->hid.buf_sz <= 0 || !(js->hid.buffer = HeapAlloc(GetProcessHeap(), 0, js->hid.buf_sz))) + { + ERR("joystick[%u] failed to alloc %d bytes.\n", js->driver_id, js->hid.buf_sz); + return FALSE; + } + + data = hid_start_parse(js->hid.desc, HID_INPUT_BIT, js->hid.rid); + if (!data) + { + ERR("joystick[%u] failed to get HID data.\n", js->driver_id); + return FALSE; + } + + memset(&js->caps, 0, sizeof(js->caps)); + + /* loop all items from the report and find the joystick ones */ + while(hid_get_item(data, &item) > 0) + { + int page = HID_PAGE(item.usage), usage = HID_USAGE(item.usage); + if (item.kind == hid_collection) + { + /* is this really a joystick/gamepad */ + if (page == HUP_GENERIC_DESKTOP && + (usage == HUG_JOYSTICK || usage == HUG_GAME_PAD)) + is_js = TRUE; + } + else if (item.kind == hid_input) + { + if (page == HUP_GENERIC_DESKTOP) + { + if (usage == HUG_HAT_SWITCH) + { + js->caps.hats++; + js->caps.bits |= JOYCAPS_HASPOV | JOYCAPS_POV4DIR; + } + else if (set_axis(js, usage, 0)) + { + js->caps.axes++; + if (usage == HUG_Z || usage == HUG_SLIDER) + js->caps.bits |= JOYCAPS_HASZ; + if (usage == HUG_RZ || usage == HUG_WHEEL) + js->caps.bits |= JOYCAPS_HASR; + if (usage == HUG_RX) + js->caps.bits |= JOYCAPS_HASU; + if (usage == HUG_RY) + js->caps.bits |= JOYCAPS_HASV; + } + else + WARN("joystick[%u] unknown HID usage %d, skipped.\n", js->driver_id, usage); + } + else if (page == HUP_BUTTON) + js->caps.buttons++; + } + } + hid_end_parse(data); + + if (is_js) + TRACE("joystick[%u] buttons: %d, axes %d, hats %d\n", js->driver_id, + js->caps.buttons, js->caps.axes, js->caps.hats); + return is_js; +} + +/* update the cache with current joystick state */ +static BOOL update_joystick(struct JOYDATA *js) +{ + struct hid_item item; + struct hid_data *data; + + /* read ALL pending messages in the queue */ + while (read(js->dev, js->hid.buffer, js->hid.buf_sz) == js->hid.buf_sz) + { + data = hid_start_parse(js->hid.desc, HID_INPUT_BIT, js->hid.rid); + if (!data) + { + ERR("joystick[%u] failed to get HID data.\n", js->driver_id); + return FALSE; + } + + /* find what changed and update our internal structure */ + while(hid_get_item(data, &item) > 0) + { + int page = HID_PAGE(item.usage), usage = HID_USAGE(item.usage), v; + if (item.kind != hid_input) continue; + + if (page == HUP_GENERIC_DESKTOP) + { + if (usage == HUG_HAT_SWITCH) + { + v = hid_get_data(js->hid.buffer, &item); + if (v >= 8) + js->stat.pov = JOY_POVCENTERED; + else + js->stat.pov = v * 4500; + } + else + { + v = hid_get_data(js->hid.buffer, &item); + v = adjust_axis_value(v, item.logical_minimum, item.logical_maximum, 0, 65534); + set_axis(js, usage, v); + } + } + else if (page == HUP_BUTTON) + { + /* enable or disable button pressed */ + v = hid_get_data(js->hid.buffer, &item); + if (v) + { + js->stat.buttons |= 1 << (usage - 1); + js->stat.button_count++; + } + else + { + js->stat.buttons &= ~(1 << (usage - 1)); + js->stat.button_count--; + } + } + } + hid_end_parse(data); + } + return errno == EAGAIN; +} + +/* close descriptor and free related structures */ +static BOOL close_joystick(struct JOYDATA *js) +{ + int dev = js->dev; + + TRACE("joystick[%u] close descriptor %d\n", js->driver_id, js->dev); + if (js->dev >= 0) + { + close(js->dev); + js->dev = 0; + js->hid_id = -1; + HeapFree(GetProcessHeap(), 0, js->hid.buffer); + js->hid.buffer = NULL; + hid_dispose_report_desc(js->hid.desc); + } + return dev >= 0; +} + +/* get the handle for an opened joystick or open and parse its data now */ +static BOOL open_joystick(struct JOYDATA *js) +{ + if (!init) + { + hid_init(NULL); + init++; + TRACE("hid_init() called\n"); + } + + if (js->dev == -1) + { + char buf[32]; + BOOL ok = FALSE; + int hid_id; + + /* loop the HID devices and find the next joystick */ + for (hid_id = 0; hid_id < MAX_HID_DEVICE_INDEX; hid_id++) + { + if (is_uhid_used(hid_id)) continue; + + sprintf(buf, HID_DEV_PATH, hid_id); + js->dev = open(buf, O_RDONLY | O_NONBLOCK); + if (js->dev != -1) + { + TRACE("joystick[%u] open %s returned descriptor %d\n", js->driver_id, buf, js->dev); + js->hid.desc = hid_get_report_desc(js->dev); + if (js->hid.desc) + ok = hid_parse(js); + else + ERR("joystick[%u] failed to get HID report\n", js->driver_id); + + if (ok) + { + js->hid_id = hid_id; + /* set defaults for a not activated joystick, this is required + * at least in the joysticks I tested because the first attempt + * to update the joystick state may not return any data as the + * user may not have pressed any button yet. */ + init_stat_data(js); + update_joystick(js); + break; + } + close_joystick(js); + } + } + } + return js->dev != -1; +} + +/************************************************************************** + * driver_open + */ +LRESULT driver_open(LPSTR str, DWORD id) +{ + struct JOYDATA *js; + if(id >= MAX_JOYSTICKS || joyget(id)) return 0; + js = &joydata[id]; + + memset(js, 0, sizeof(*js)); + js->driver_id = id; + js->hid_id = -1; + js->dev = -1; + js->in_use = TRUE; + return (LRESULT) js; +} + +/************************************************************************** + * driver_close + */ +LRESULT driver_close(DWORD_PTR id) +{ + struct JOYDATA* js = joyget(id); + if (!js) return 0; + + close_joystick(js); + js->in_use = FALSE; + return 1; +} + +/************************************************************************** + * JoyGetDevCaps [MMSYSTEM.102] + */ +LRESULT driver_joyGetDevCaps(DWORD_PTR id, LPJOYCAPSW caps, DWORD size) +{ + static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0}; + struct JOYDATA *js = joyget(id); + + if (!js) return MMSYSERR_NODRIVER; + if (!open_joystick(js)) return JOYERR_PARMS; + + caps->wMid = MM_MICROSOFT; + caps->wPid = MM_PC_JOYSTICK; + strcpyW(caps->szPname, ini); /* joystick product name */ + caps->wXmin = 0; + caps->wXmax = 0xFFFF; + caps->wYmin = 0; + caps->wYmax = 0xFFFF; + caps->wZmin = 0; + caps->wZmax = (js->caps.axes >= 3) ? 0xFFFF : 0; + caps->wNumButtons = js->caps.buttons; + + if (size == sizeof(JOYCAPSW)) + { + /* complete 95 structure */ + caps->wRmin = 0; + caps->wRmax = 0xFFFF; + caps->wUmin = 0; + caps->wUmax = 0xFFFF; + caps->wVmin = 0; + caps->wVmax = 0xFFFF; + caps->wMaxAxes = 6; /* same as MS Joystick Driver */ + caps->wNumAxes = js->caps.axes; + caps->wMaxButtons = 32; /* same as MS Joystick Driver */ + caps->szRegKey[0] = 0; + caps->szOEMVxD[0] = 0; + caps->wCaps = js->caps.bits; + } + + return JOYERR_NOERROR; +} + +/************************************************************************** + * driver_joyGetPos + */ +LRESULT driver_joyGetPosEx(DWORD_PTR id, LPJOYINFOEX info) +{ + struct JOYDATA *js = joyget(id); + + if (!js) return MMSYSERR_NODRIVER; + if (!open_joystick(js)) return JOYERR_PARMS; + if (!update_joystick(js)) return JOYERR_UNPLUGGED; + + /* Now, copy the cached values into Window's structure... */ + if (info->dwFlags & JOY_RETURNBUTTONS) + { + info->dwButtons = js->stat.buttons; + info->dwButtonNumber = js->stat.button_count; + } + if (info->dwFlags & JOY_RETURNX) info->dwXpos = js->stat.axes[0]; + if (info->dwFlags & JOY_RETURNY) info->dwYpos = js->stat.axes[1]; + if (info->dwFlags & JOY_RETURNZ) info->dwZpos = js->stat.axes[2]; + if (info->dwFlags & JOY_RETURNR) info->dwRpos = js->stat.axes[3]; + if (info->dwFlags & JOY_RETURNU) info->dwUpos = js->stat.axes[4]; + if (info->dwFlags & JOY_RETURNV) info->dwVpos = js->stat.axes[5]; + if (info->dwFlags & JOY_RETURNPOV) info->dwPOV = js->stat.pov; + + TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, flags: 0x%04x (fd %d)\n", + info->dwXpos, info->dwYpos, info->dwZpos, + info->dwRpos, info->dwUpos, info->dwVpos, + info->dwButtons, info->dwFlags, js->dev + ); + + return JOYERR_NOERROR; +} + +/************************************************************************** + * driver_joyGetPos + */ +LRESULT driver_joyGetPos(DWORD_PTR id, LPJOYINFO info) +{ + JOYINFOEX ji; + LONG ret; + + memset(&ji, 0, sizeof(ji)); + ji.dwSize = sizeof(ji); + ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS; + + ret = driver_joyGetPosEx(id, &ji); + if (ret == JOYERR_NOERROR) + { + info->wXpos = ji.dwXpos; + info->wYpos = ji.dwYpos; + info->wZpos = ji.dwZpos; + info->wButtons = ji.dwButtons; + } + + return ret; +} + +//#endif /* HAVE_USBHID_H */ -- 2.3.3