/* * 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 managed controllers (subclass of Joystick). */ #include #include #include #if defined(AG_WIDGETS) && defined(AG_DEBUG) #include #include #endif /* * Create and attach a new controller device. * The agInputDevices VFS must be locked. */ AG_Controller * AG_ControllerNew(void *obj, const char *desc) { AG_Driver *drv = obj; AG_Controller *ctrl; AG_Joystick **joysNew; AG_InputDevice *idev; AG_OBJECT_ISA(drv, "AG_Driver:*"); if ((ctrl = TryMalloc(sizeof(AG_Controller))) == NULL) { return (NULL); } AG_ObjectInit(ctrl, &agControllerClass); idev = AGINPUTDEVICE(ctrl); 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++] = AGJOYSTICK(ctrl); AG_ObjectAttach(&agInputDevices, ctrl); return (ctrl); fail: AG_ObjectDestroy(ctrl); return (NULL); } static __inline__ void PostCtrl(AG_Widget *_Nonnull wid, AG_InputDevice *_Nonnull id, const AG_DriverEvent *dev) { AG_ObjectLock(wid); /* * TODO: Allow remapping focus-switching triggers to different * buttons or axes. */ if ((wid->flags & AG_WIDGET_CATCH_SHOULDER) == 0) { switch (dev->type) { case AG_DRIVER_CTRL_BUTTON_DOWN: switch (dev->ctrlButton.which) { case AG_CTRL_BUTTON_LEFT_SHOULDER: AG_WindowCycleFocus(AG_ParentWindow(wid), 1); goto out; case AG_CTRL_BUTTON_RIGHT_SHOULDER: AG_WindowCycleFocus(AG_ParentWindow(wid), 0); goto out; default: break; } break; default: break; } } if (WIDGET_OPS(wid)->ctrl != NULL) WIDGET_OPS(wid)->ctrl(wid, id, dev); out: AG_ObjectUnlock(wid); } void AG_ProcessController(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); /* Post event to any widget with an active matching input device grab. */ OBJECT_FOREACH_CHILD(idev, &agInputDevices, ag_input_device) { int i; if (!AG_JOYSTICK_ISA(idev) || instanceID != AGJOYSTICK(idev)->instanceID) { continue; } for (i = 0; i < idev->nWidGrab; i++) { AG_Widget *wGrab = idev->widGrab[i]; AG_OBJECT_ISA(wGrab, "AG_Widget:*"); PostCtrl(wGrab, idev, dev); } break; } /* Post the event to the focused widget in the focused window. */ if (win != NULL) { if (!AG_WindowIsFocused(win) || (wFoc = AG_WidgetFindFocused(win)) == NULL) { goto out; } PostCtrl(wFoc, idev, dev); } 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; } PostCtrl(wFoc, idev, dev); } } AG_UnlockVFS(&agDrivers); } out: AG_UnlockVFS(&agInputDevices); } static void Init(void *_Nonnull obj) { AG_Controller *ctrl = obj; OBJECT(ctrl)->flags |= AG_OBJECT_NAME_ONATTACH; } #if defined(AG_WIDGETS) && defined(AG_DEBUG) static void * Edit(void *obj) { AG_Controller *ctrl = obj; AG_Box *box; AG_Label *lbl; box = AG_BoxNewVert(NULL, AG_BOX_HFILL); lbl = AG_LabelNewS(box, 0, AGINPUTDEVICE(ctrl)->desc); AG_SetFontFamily(lbl, "league-spartan"); AG_SetFontSize(lbl, "130%"); AG_LabelNew(box, AG_LABEL_HFILL, _("Controller Type: " AGSI_BOLD "%d" AGSI_RST "\n"), ctrl->type); return (box); } #endif /* AG_WIDGETS and AG_DEBUG */ AG_ObjectClass agControllerClass = { "Agar(InputDevice:Joystick:Controller)", sizeof(AG_Controller), { 1,0, AGC_CONTROLLER, 0xE062 }, Init, NULL, /* reset */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ #if defined(AG_WIDGETS) && defined(AG_DEBUG) Edit #else NULL /* edit */ #endif };