diff --git a/extra/game-input/backend/iokit/iokit.factor b/extra/game-input/backend/iokit/iokit.factor new file mode 100644 index 0000000000..cc4cc31ffa --- /dev/null +++ b/extra/game-input/backend/iokit/iokit.factor @@ -0,0 +1,273 @@ +USING: cocoa cocoa.plists core-foundation iokit iokit.hid +kernel cocoa.enumeration destructors math.parser cocoa.application +sequences locals combinators.short-circuit game-input threads +symbols namespaces assocs hashtables arrays combinators +core-foundation.run-loop accessors sequences.private +alien.c-types math ; +IN: game-input.backend.iokit + +SINGLETON: iokit-game-input-backend + +: hid-manager-matching ( matching-seq -- alien ) + f 0 IOHIDManagerCreate + [ swap >plist IOHIDManagerSetDeviceMatchingMultiple ] + keep ; + +: devices-from-hid-manager ( manager -- vector ) + [ + IOHIDManagerCopyDevices + [ &CFRelease NSFastEnumeration>vector ] [ f ] if* + ] with-destructors ; + +: game-devices-matching-seq + { + H{ { "DeviceUsage" 4 } { "DeviceUsagePage" 1 } } ! joysticks + H{ { "DeviceUsage" 5 } { "DeviceUsagePage" 1 } } ! gamepads + H{ { "DeviceUsage" 6 } { "DeviceUsagePage" 1 } } ! keyboards + } ; inline + +: buttons-matching-hash + H{ { "UsagePage" 9 } { "Type" 2 } } ; inline +: keys-matching-hash + H{ { "UsagePage" 7 } { "Type" 2 } } ; inline +: x-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 30 } { "Type" 1 } } ; inline +: y-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 31 } { "Type" 1 } } ; inline +: z-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 32 } { "Type" 1 } } ; inline +: rx-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 33 } { "Type" 1 } } ; inline +: ry-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 34 } { "Type" 1 } } ; inline +: rz-axis-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 35 } { "Type" 1 } } ; inline +: slider-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 36 } { "Type" 1 } } ; inline +: hat-switch-matching-hash + H{ { "UsagePage" 1 } { "Usage" HEX: 39 } { "Type" 1 } } ; inline + +: device-elements-matching ( device matching-hash -- vector ) + [ + >plist 0 IOHIDDeviceCopyMatchingElements + [ &CFRelease NSFastEnumeration>vector ] [ f ] if* + ] with-destructors ; + +: button-count ( device -- button-count ) + buttons-matching-hash device-elements-matching length ; + +: ?axis ( device hash -- axis/f ) + device-elements-matching dup empty? [ drop f ] [ first ] if ; + +: ?x-axis ( device -- ? ) + x-axis-matching-hash ?axis ; +: ?y-axis ( device -- ? ) + y-axis-matching-hash ?axis ; +: ?z-axis ( device -- ? ) + z-axis-matching-hash ?axis ; +: ?rx-axis ( device -- ? ) + rx-axis-matching-hash ?axis ; +: ?ry-axis ( device -- ? ) + ry-axis-matching-hash ?axis ; +: ?rz-axis ( device -- ? ) + rz-axis-matching-hash ?axis ; +: ?slider ( device -- ? ) + slider-matching-hash ?axis ; +: ?hat-switch ( device -- ? ) + hat-switch-matching-hash ?axis ; + +: hid-manager-matching-game-devices ( -- alien ) + game-devices-matching-seq hid-manager-matching ; + +: device-property ( device key -- value ) + [ + IOHIDDeviceGetProperty + [ &CFRelease plist> ] [ f ] if* + ] with-destructors ; +: element-property ( element key -- value ) + [ + IOHIDElementGetProperty + [ &CFRelease plist> ] [ f ] if* + ] with-destructors ; +: set-element-property ( element key value -- ) + [ ] [ >plist ] bi* IOHIDElementSetProperty drop ; +: transfer-element-property ( element from-key to-key -- ) + [ dupd element-property ] dip swap set-element-property ; + +: controller-device? ( device -- ? ) + { + [ 1 4 IOHIDDeviceConformsTo ] + [ 1 5 IOHIDDeviceConformsTo ] + } 1|| ; + +: element-usage ( element -- {usage-page,usage} ) + [ IOHIDElementGetUsagePage ] [ IOHIDElementGetUsage ] bi + 2array ; + +: button? ( {usage-page,usage} -- ? ) + first 9 = ; +: keyboard-key? ( {usage-page,usage} -- ? ) + first 7 = ; +: x-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 30 } = ; +: y-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 31 } = ; +: z-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 32 } = ; +: rx-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 33 } = ; +: ry-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 34 } = ; +: rz-axis? ( {usage-page,usage} -- ? ) + { 1 HEX: 35 } = ; +: slider? ( {usage-page,usage} -- ? ) + { 1 HEX: 36 } = ; +: hat-switch? ( {usage-page,usage} -- ? ) + { 1 HEX: 39 } = ; + +: button-value ( value -- f/(0,1] ) + IOHIDValueGetIntegerValue dup zero? [ drop f ] when ; +! XXX calibration +: axis-value ( value -- [-1,1] ) + kIOHIDValueScaleTypeCalibrated IOHIDValueGetScaledValue ; +: slider-value ( value -- [0,1] ) + kIOHIDValueScaleTypeCalibrated IOHIDValueGetScaledValue + 1.0 + 0.5 * ; +: pov-value ( value -- pov-direction ) + ! XXX + IOHIDValueGetIntegerValue ; + +: record-controller ( controller-state value -- ) + dup IOHIDValueGetElement element-usage { + { [ dup button? ] [ [ button-value ] [ second 1- ] bi* rot buttons>> set-nth ] } + { [ dup x-axis? ] [ drop axis-value >>x drop ] } + { [ dup y-axis? ] [ drop axis-value >>y drop ] } + { [ dup z-axis? ] [ drop axis-value >>z drop ] } + { [ dup rx-axis? ] [ drop axis-value >>rx drop ] } + { [ dup ry-axis? ] [ drop axis-value >>ry drop ] } + { [ dup rz-axis? ] [ drop axis-value >>rz drop ] } + { [ dup slider? ] [ drop slider-value >>slider drop ] } + { [ dup hat-switch? ] [ drop pov-value >>pov drop ] } + [ 3drop ] + } cond ; + +SYMBOLS: +hid-manager+ +keyboard-state+ +controller-states+ ; + +: ?set-nth ( value nth seq -- ) + 2dup bounds-check? [ set-nth-unsafe ] [ 3drop ] if ; + +: record-keyboard ( value -- ) + dup IOHIDValueGetElement element-usage keyboard-key? [ + [ IOHIDValueGetIntegerValue c-bool> ] + [ IOHIDValueGetElement IOHIDElementGetUsage ] bi + +keyboard-state+ get ?set-nth + ] [ drop ] if ; + +: default-calibrate-saturation ( element -- ) + [ kIOHIDElementMinKey kIOHIDElementCalibrationSaturationMinKey transfer-element-property ] + [ kIOHIDElementMaxKey kIOHIDElementCalibrationSaturationMaxKey transfer-element-property ] + bi ; + +: default-calibrate-axis ( element -- ) + [ kIOHIDElementCalibrationMinKey -1.0 set-element-property ] + [ kIOHIDElementCalibrationMaxKey 1.0 set-element-property ] + [ default-calibrate-saturation ] + tri ; + +: default-calibrate-slider ( element -- ) + [ kIOHIDElementCalibrationMinKey 0.0 set-element-property ] + [ kIOHIDElementCalibrationMaxKey 1.0 set-element-property ] + [ default-calibrate-saturation ] + tri ; + +: (default) ( ? quot -- ) + [ f ] if* ; inline + +: ( device -- controller-state ) + { + [ ?x-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?y-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?z-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?rx-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?ry-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?rz-axis [ default-calibrate-axis 0.0 ] (default) ] + [ ?slider [ default-calibrate-slider 0.0 ] (default) ] + [ ?hat-switch pov-neutral and ] + [ button-count f ] + } cleave controller-state boa ; + +: device-matched-callback ( -- alien ) + [| context result sender device | + device controller-device? [ + device + device +controller-states+ get set-at + ] when + ] IOHIDDeviceCallback ; + +: device-removed-callback ( -- alien ) + [| context result sender device | + device +controller-states+ get delete-at + ] IOHIDDeviceCallback ; + +: device-input-callback ( -- alien ) + [| context result sender value | + sender controller-device? + [ sender +controller-states+ get at value record-controller ] + [ value record-keyboard ] + if + ] IOHIDValueCallback ; + +: initialize-variables ( manager -- ) + +hid-manager+ set-global + 4 +controller-states+ set-global + 256 f +keyboard-state+ set-global ; + +M: iokit-game-input-backend open-game-input + hid-manager-matching-game-devices { + [ initialize-variables ] + [ device-matched-callback f IOHIDManagerRegisterDeviceMatchingCallback ] + [ device-removed-callback f IOHIDManagerRegisterDeviceRemovalCallback ] + [ device-input-callback f IOHIDManagerRegisterInputValueCallback ] + [ 0 IOHIDManagerOpen mach-error ] + [ + CFRunLoopGetMain CFRunLoopDefaultMode + IOHIDManagerScheduleWithRunLoop + ] + } cleave ; + +! XXX while game-input is open, we need to reset-game-input and open-game-input as a +! boot image hook +M: iokit-game-input-backend close-game-input + +hid-manager+ global [ + [ 0 IOHIDManagerClose drop ] + [ CFRelease ] bi + f + ] change-at + f +keyboard-state+ set-global + f +controller-states+ set-global ; + +M: iokit-game-input-backend get-controllers ( -- sequence ) + +controller-states+ get keys [ controller boa ] map ; + +M: iokit-game-input-backend manufacturer ( controller -- string ) + handle>> kIOHIDManufacturerKey device-property ; +M: iokit-game-input-backend product ( controller -- string ) + handle>> kIOHIDProductKey device-property ; +M: iokit-game-input-backend vendor-id ( controller -- integer ) + handle>> kIOHIDVendorIDKey device-property ; +M: iokit-game-input-backend product-id ( controller -- integer ) + handle>> kIOHIDProductIDKey device-property ; +M: iokit-game-input-backend location-id ( controller -- integer ) + handle>> kIOHIDLocationIDKey device-property ; + +M: iokit-game-input-backend read-controller ( controller -- controller-state ) + handle>> +controller-states+ get at clone ; + +M: iokit-game-input-backend read-keyboard ( -- keyboard-state ) + +keyboard-state+ get clone keyboard-state boa ; + +M: iokit-game-input-backend calibrate-controller ( controller -- ) + ! XXX + drop ; + +iokit-game-input-backend game-input-backend set-global diff --git a/extra/game-input/game-input.factor b/extra/game-input/game-input.factor new file mode 100644 index 0000000000..a2884b0746 --- /dev/null +++ b/extra/game-input/game-input.factor @@ -0,0 +1,39 @@ +USING: arrays accessors continuations kernel symbols ; +IN: game-input + +SYMBOL: game-input-backend + +HOOK: open-game-input game-input-backend ( -- ) +HOOK: close-game-input game-input-backend ( -- ) + +: with-game-input ( quot -- ) + open-game-input [ close-game-input ] [ ] cleanup ; + +TUPLE: controller handle ; +TUPLE: controller-state x y z rx ry rz slider pov buttons ; + +M: controller-state clone + call-next-method dup buttons>> clone >>buttons ; + +SYMBOLS: + pov-neutral + pov-up pov-up-left pov-left pov-down-left + pov-down pov-down-right pov-right pov-up-right ; + +HOOK: get-controllers game-input-backend ( -- sequence ) + +HOOK: manufacturer game-input-backend ( controller -- string ) +HOOK: product game-input-backend ( controller -- string ) +HOOK: vendor-id game-input-backend ( controller -- integer ) +HOOK: product-id game-input-backend ( controller -- integer ) +HOOK: location-id game-input-backend ( controller -- integer ) + +HOOK: read-controller game-input-backend ( controller -- controller-state ) +HOOK: calibrate-controller game-input-backend ( controller -- ) + +TUPLE: keyboard-state keys ; + +M: keyboard-state clone + call-next-method dup keys>> clone >>keys ; + +HOOK: read-keyboard game-input-backend ( -- keyboard-state ) diff --git a/extra/iokit/hid/hid.factor b/extra/iokit/hid/hid.factor index 79c3516b4e..11ba5d3687 100644 --- a/extra/iokit/hid/hid.factor +++ b/extra/iokit/hid/hid.factor @@ -20,8 +20,8 @@ IN: iokit.hid : kIOHIDPrimaryUsageKey "PrimaryUsage" ; inline : kIOHIDPrimaryUsagePageKey "PrimaryUsagePage" ; inline : kIOHIDMaxInputReportSizeKey "MaxInputReportSize" ; inline -: kIOHIDMaxOutputReportSizeKey "MaxOutputReportSize" ; inline -: kIOHIDMaxFeatureReportSizeKey "MaxFeatureReportSize" ; inline +: kIOHIDMaxOutputReportSizeKey "MaxOutputReportSize" ; inline +: kIOHIDMaxFeatureReportSizeKey "MaxFeatureReportSize" ; inline : kIOHIDReportIntervalKey "ReportInterval" ; inline : kIOHIDElementKey "Elements" ; inline @@ -77,7 +77,7 @@ IN: iokit.hid : kIOHIDElementTypeFeature 257 ; inline : kIOHIDElementTypeCollection 513 ; inline -: kIOHIDElementCollectionTypePhysical HEX: 00 ; inline +: kIOHIDElementCollectionTypePhysical HEX: 00 ; inline : kIOHIDElementCollectionTypeApplication HEX: 01 ; inline : kIOHIDElementCollectionTypeLogical HEX: 02 ; inline : kIOHIDElementCollectionTypeReport HEX: 03 ; inline @@ -90,10 +90,10 @@ IN: iokit.hid : kIOHIDReportTypeFeature 2 ; inline : kIOHIDReportTypeCount 3 ; inline -: kIOHIDOptionsTypeNone HEX: 00 ; inline +: kIOHIDOptionsTypeNone HEX: 00 ; inline : kIOHIDOptionsTypeSeizeDevice HEX: 01 ; inline -: kIOHIDQueueOptionsTypeNone HEX: 00 ; inline +: kIOHIDQueueOptionsTypeNone HEX: 00 ; inline : kIOHIDQueueOptionsTypeEnqueueAll HEX: 01 ; inline : kIOHIDElementFlagsConstantMask HEX: 0001 ; inline @@ -269,3 +269,4 @@ FUNCTION: IOHIDValueRef IOHIDTransactionGetValue ( IOHIDTransactionRef transacti FUNCTION: IOReturn IOHIDTransactionCommit ( IOHIDTransactionRef transaction ) ; FUNCTION: IOReturn IOHIDTransactionCommitWithCallback ( IOHIDTransactionRef transaction, CFTimeInterval timeout, IOHIDCallback callback, void* context ) ; FUNCTION: void IOHIDTransactionClear ( IOHIDTransactionRef transaction ) ; +