/* * Copyright (c) 2023 Julien Nadeau Carriere * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Interface to joystick / gamepads / accelerometers. */ #include #include #include #if defined(AG_WIDGETS) && defined(AG_DEBUG) #include #include #endif /* * Create and attach a new joystick device. * The agInputDevices VFS must be locked. */ AG_Joystick * AG_JoystickNew(void *obj, const char *desc) { AG_Driver *drv = obj; AG_Joystick *joy, **joysNew; AG_InputDevice *idev; AG_OBJECT_ISA(drv, "AG_Driver:*"); if ((joy = TryMalloc(sizeof(AG_Joystick))) == NULL) { return (NULL); } AG_ObjectInit(joy, &agJoystickClass); idev = AGINPUTDEVICE(joy); idev->drv = drv; if ((idev->desc = TryStrdup(desc)) == NULL) { goto fail; } joysNew = TryRealloc(drv->joys, (drv->nJoys + 1)*sizeof(AG_Joystick *)); if (joysNew == NULL) { free(idev->desc); goto fail; } drv->joys = joysNew; drv->joys[drv->nJoys++] = joy; AG_ObjectAttach(&agInputDevices, joy); return (joy); fail: AG_ObjectDestroy(joy); return (NULL); } void AG_JoystickDestroy(void *obj, AG_Joystick *joy) { AG_Driver *drv = obj; int i; AG_OBJECT_ISA(drv, "AG_Driver:*"); for (i = 0; i < drv->nJoys; i++) { if (drv->joys[i] == joy) break; } if (i == drv->nJoys) { return; } if (i < drv->nJoys-1) { memmove(&drv->joys[i], &drv->joys[i+1], (drv->nJoys-i-1)*sizeof(AG_Joystick *)); } drv->nJoys--; AG_ObjectDestroy(joy); } void AG_ProcessJoystick(AG_Window *win, const AG_DriverEvent *dev) { AG_Widget *wFoc; AG_InputDevice *idev; int instanceID; if ((instanceID = AG_GetJoystickInstanceID(dev)) == -1) return; AG_LockVFS(&agInputDevices); OBJECT_FOREACH_CHILD(idev, &agInputDevices, ag_input_device) { int i; if (!AG_JOYSTICK_ISA(idev)) { continue; } if (instanceID != AGJOYSTICK(idev)->instanceID) { continue; } for (i = 0; i < idev->nWidGrab; i++) { AG_Widget *wGrab = idev->widGrab[i]; AG_OBJECT_ISA(wGrab, "AG_Widget:*"); AG_ObjectLock(wGrab); if (WIDGET_OPS(wGrab)->joy != NULL) { WIDGET_OPS(wGrab)->joy(wGrab, idev, dev); } AG_ObjectUnlock(wGrab); } break; } if (win != NULL) { if (!AG_WindowIsFocused(win) || (wFoc = AG_WidgetFindFocused(win)) == NULL) { goto out; } AG_ObjectLock(wFoc); if (WIDGET_OPS(wFoc)->joy != NULL) { WIDGET_OPS(wFoc)->joy(wFoc, idev, dev); } AG_ObjectUnlock(wFoc); } else { AG_Driver *drv; AG_LockVFS(&agDrivers); OBJECT_FOREACH_CHILD(drv, &agDrivers, ag_driver) { AG_FOREACH_WINDOW(win, drv) { if (!AG_WindowIsFocused(win) || (wFoc = AG_WidgetFindFocused(win)) == NULL) { continue; } AG_ObjectLock(wFoc); if (WIDGET_OPS(wFoc)->joy != NULL) { WIDGET_OPS(wFoc)->joy(wFoc, idev, dev); } AG_ObjectUnlock(wFoc); } } AG_UnlockVFS(&agDrivers); } out: AG_UnlockVFS(&agInputDevices); } static void Init(void *_Nonnull obj) { AG_Joystick *joy = obj; OBJECT(joy)->flags |= AG_OBJECT_NAME_ONATTACH; } #if defined(AG_WIDGETS) && defined(AG_DEBUG) static void * Edit(void *obj) { AG_Joystick *joy = obj; AG_Box *box; AG_Label *lbl; box = AG_BoxNewVert(NULL, AG_BOX_HFILL); lbl = AG_LabelNewS(box, 0, AGINPUTDEVICE(joy)->desc); AG_SetFontFamily(lbl, "league-spartan"); AG_SetFontSize(lbl, "130%"); AG_LabelNew(box, AG_LABEL_HFILL, _("Name: " AGSI_BOLD "%s" AGSI_RST "\n" "Vendor: " AGSI_CODE "0x%04x" AGSI_RST " | " "Product: " AGSI_CODE "0x%04x" AGSI_RST "\n" "GUID: " AGSI_CODE "%s" AGSI_RST "\n" "Device Index: %d | Player Index: %d\n" "Instance ID: %d\n\n" "Buttons: " AGSI_BOLD "%u" AGSI_RST " | " "Axes: " AGSI_BOLD "%u" AGSI_RST "\n" "Balls: " AGSI_BOLD "%u" AGSI_RST " | " "Hats: " AGSI_BOLD "%u" AGSI_RST "\n"), joy->name, joy->vendorID, joy->productID, joy->guid, joy->deviceIdx, joy->playerIdx, joy->instanceID, joy->nButtons, joy->nAxes, joy->nBalls, joy->nHats); return (box); } #endif /* AG_WIDGETS and AG_DEBUG */ AG_ObjectClass agJoystickClass = { "Agar(InputDevice:Joystick)", sizeof(AG_Joystick), { 1,0, AGC_JOYSTICK, 0x1F579 }, Init, NULL, /* reset */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ #if defined(AG_WIDGETS) && defined(AG_DEBUG) Edit #else NULL /* edit */ #endif };