|
| 1 | +package gamepad |
| 2 | + |
| 3 | +/* |
| 4 | +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc |
| 5 | +
|
| 6 | +#import <Foundation/Foundation.h> |
| 7 | +#import <GameController/GameController.h> |
| 8 | +
|
| 9 | +static CFTypeRef getGamepads() { |
| 10 | + if (@available(iOS 15, macOS 12, *)) { |
| 11 | + NSArray<GCController *> * Controllers = [GCController controllers]; |
| 12 | + return (CFTypeRef)CFBridgingRetain(Controllers); |
| 13 | + } |
| 14 | + return 0; |
| 15 | +} |
| 16 | +
|
| 17 | +static CFTypeRef getState(CFTypeRef gamepads, int64_t player) { |
| 18 | + if (@available(iOS 15, macOS 12, *)) { |
| 19 | + NSArray<GCController *> * Controllers = (__bridge NSArray<GCController *> *)gamepads; |
| 20 | + if ([Controllers count] <= player) { |
| 21 | + return 0; |
| 22 | + } |
| 23 | +
|
| 24 | + GCExtendedGamepad * Gamepad = [[Controllers objectAtIndex:player] extendedGamepad]; |
| 25 | + if (Gamepad == nil) { |
| 26 | + return 0; |
| 27 | + } |
| 28 | +
|
| 29 | + GCPhysicalInputProfile* Inputs = (GCPhysicalInputProfile*)Gamepad; |
| 30 | + return (CFTypeRef)CFBridgingRetain(Inputs); |
| 31 | + } |
| 32 | + return 0; |
| 33 | +} |
| 34 | +
|
| 35 | +static double getLastEventFrom(CFTypeRef inputs) { |
| 36 | + if (@available(iOS 15, macOS 12, *)) { |
| 37 | + return (double)(((__bridge GCPhysicalInputProfile*)(inputs)).lastEventTimestamp); |
| 38 | + } |
| 39 | + return 0; |
| 40 | +} |
| 41 | +
|
| 42 | +static NSString * getKeyName(GCPhysicalInputProfile * Inputs, void * button) { |
| 43 | + if (@available(iOS 15, macOS 12, *)) { |
| 44 | + NSString * name = *((__unsafe_unretained NSString **)(button)); |
| 45 | + if ([Inputs hasRemappedElements] == false) { |
| 46 | + return name; |
| 47 | + } |
| 48 | + return [Inputs mappedElementAliasForPhysicalInputName:name]; |
| 49 | + } |
| 50 | + return nil; |
| 51 | +} |
| 52 | +
|
| 53 | +static float getButtonFrom(CFTypeRef inputs, void * button) { |
| 54 | + if (@available(iOS 15, macOS 12, *)) { |
| 55 | + GCPhysicalInputProfile * Inputs = ((__bridge GCPhysicalInputProfile*)(inputs)); |
| 56 | + return Inputs.buttons[getKeyName(Inputs, button)].value; |
| 57 | + } |
| 58 | + return 0; |
| 59 | +} |
| 60 | +
|
| 61 | +static void getAxesFrom(CFTypeRef inputs, void * button, void * x, void * y) { |
| 62 | + if (@available(iOS 15, macOS 12, *)) { |
| 63 | + GCPhysicalInputProfile * Inputs = ((__bridge GCPhysicalInputProfile*)(inputs)); |
| 64 | + GCControllerDirectionPad * Pad = Inputs.dpads[getKeyName(Inputs, button)]; |
| 65 | +
|
| 66 | + *((float *)(x)) = Pad.xAxis.value; |
| 67 | + *((float *)(y)) = -Pad.yAxis.value; |
| 68 | + } |
| 69 | +} |
| 70 | +*/ |
| 71 | +import "C" |
| 72 | +import ( |
| 73 | + "gioui.org/app" |
| 74 | + "gioui.org/io/event" |
| 75 | + "gioui.org/io/system" |
| 76 | + "unsafe" |
| 77 | +) |
| 78 | + |
| 79 | +var mappingButton = map[unsafe.Pointer]int{ |
| 80 | + unsafe.Pointer(&C.GCInputButtonA): buttonA, |
| 81 | + unsafe.Pointer(&C.GCInputButtonB): buttonB, |
| 82 | + unsafe.Pointer(&C.GCInputButtonX): buttonX, |
| 83 | + unsafe.Pointer(&C.GCInputButtonY): buttonY, |
| 84 | + unsafe.Pointer(&C.GCInputLeftThumbstickButton): buttonLeftThumb, |
| 85 | + unsafe.Pointer(&C.GCInputRightThumbstickButton): buttonRightThumb, |
| 86 | + unsafe.Pointer(&C.GCInputLeftShoulder): buttonLB, |
| 87 | + unsafe.Pointer(&C.GCInputRightShoulder): buttonRB, |
| 88 | + unsafe.Pointer(&C.GCInputLeftTrigger): buttonLT, |
| 89 | + unsafe.Pointer(&C.GCInputRightTrigger): buttonRT, |
| 90 | + unsafe.Pointer(&C.GCInputButtonMenu): buttonStart, |
| 91 | + unsafe.Pointer(&C.GCInputButtonOptions): buttonBack, |
| 92 | +} |
| 93 | + |
| 94 | +type gamepad struct{} |
| 95 | + |
| 96 | +func newGamepad(_ *app.Window) *gamepad { |
| 97 | + return &gamepad{} |
| 98 | +} |
| 99 | + |
| 100 | +func (g *Gamepad) listenEvents(evt event.Event) { |
| 101 | + switch evt.(type) { |
| 102 | + case system.FrameEvent: |
| 103 | + g.getState() |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +func (g *Gamepad) getState() { |
| 108 | + gamepads := C.getGamepads() |
| 109 | + defer C.CFRelease(gamepads) |
| 110 | + for player, controller := range g.Controllers { |
| 111 | + controller.updateState(C.getState(gamepads, C.int64_t(player))) |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +func (controller *Controller) updateState(state C.CFTypeRef) { |
| 116 | + if state == 0 { |
| 117 | + controller.Connected = false |
| 118 | + controller.Changed = false |
| 119 | + return |
| 120 | + } |
| 121 | + defer C.CFRelease(state) |
| 122 | + |
| 123 | + packet := float64(C.getLastEventFrom(state)) |
| 124 | + if controller.packet == packet { |
| 125 | + controller.Changed = false |
| 126 | + return |
| 127 | + } |
| 128 | + |
| 129 | + controller.packet = packet |
| 130 | + controller.Connected = true |
| 131 | + controller.Changed = true |
| 132 | + |
| 133 | + // Buttons |
| 134 | + for name, button := range mappingButton { |
| 135 | + controller.Buttons.setButtonForce(button, float32(C.getButtonFrom(state, name))) |
| 136 | + } |
| 137 | + |
| 138 | + // D-Pads |
| 139 | + var x, y float32 |
| 140 | + C.getAxesFrom(state, unsafe.Pointer(&C.GCInputDirectionPad), unsafe.Pointer(&x), unsafe.Pointer(&y)) |
| 141 | + controller.Buttons.setButtonPressed(buttonLeft, x < 0) |
| 142 | + controller.Buttons.setButtonPressed(buttonRight, x > 0) |
| 143 | + controller.Buttons.setButtonPressed(buttonUp, y < 0) |
| 144 | + controller.Buttons.setButtonPressed(buttonDown, y > 0) |
| 145 | + |
| 146 | + // Joysticks |
| 147 | + C.getAxesFrom(state, unsafe.Pointer(&C.GCInputLeftThumbstick), |
| 148 | + unsafe.Pointer(&controller.Joysticks.LeftThumb.X), |
| 149 | + unsafe.Pointer(&controller.Joysticks.LeftThumb.Y), |
| 150 | + ) |
| 151 | + C.getAxesFrom(state, unsafe.Pointer(&C.GCInputRightThumbstick), |
| 152 | + unsafe.Pointer(&controller.Joysticks.RightThumb.X), |
| 153 | + unsafe.Pointer(&controller.Joysticks.RightThumb.Y), |
| 154 | + ) |
| 155 | +} |
0 commit comments