ObjFW  Check-in [8c81efc528]

Overview
Comment:OFGameController: Improve right stick emulation
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8c81efc52846002d4b4f904cf14d991e1690c541fa4f6177e9b507fadd4df6dd
User & Date: js on 2024-05-20 21:48:26
Other Links: manifest | tags
Context
2024-05-21
22:55
configure: Add -no-integrated-as to MIPS ASFLAGS check-in: 633a7efff5 user: js tags: trunk
2024-05-20
21:48
OFGameController: Improve right stick emulation check-in: 8c81efc528 user: js tags: trunk
19:41
tests/gamecontroller: Print read errors check-in: f8b2baa0f5 user: js tags: trunk
Changes

Modified src/hid/OFEvdevGameController.m from [9e74768eff] to [be8e76e300].

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
static const uint16_t productIDDualSense = 0x0CE6;
static const uint16_t productIDDualShock4 = 0x09CC;

/* Google controllers */
static const uint16_t productIDStadia = 0x9400;

static const uint16_t buttons[] = {
	BTN_A, BTN_B, BTN_X, BTN_Y, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2,
	BTN_SELECT, BTN_START, BTN_MODE, BTN_THUMBL, BTN_THUMBR, BTN_DPAD_UP,
	BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_TRIGGER_HAPPY1,
	BTN_TRIGGER_HAPPY2
};

static OFGameControllerButton
buttonToName(uint16_t button, uint16_t vendorID, uint16_t productID)
{
	if (vendorID == vendorIDNintendo &&
	    productID == productIDLeftJoycon) {







|
|
|
|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
static const uint16_t productIDDualSense = 0x0CE6;
static const uint16_t productIDDualShock4 = 0x09CC;

/* Google controllers */
static const uint16_t productIDStadia = 0x9400;

static const uint16_t buttons[] = {
	BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2,
	BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_THUMBL, BTN_THUMBR,
	BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
	BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2
};

static OFGameControllerButton
buttonToName(uint16_t button, uint16_t vendorID, uint16_t productID)
{
	if (vendorID == vendorIDNintendo &&
	    productID == productIDLeftJoycon) {
106
107
108
109
110
111
112

113

114

115
116
117
118
119
120
121
122
123
124
		}
	} else if (vendorID == vendorIDNintendo &&
	    productID == productIDN64Controller) {
		switch (button) {
		case BTN_B:
			return OFGameControllerWestButton;
		case BTN_SELECT:

		case BTN_X:

		case BTN_Y:

		case BTN_C:
			/* Used to emulate right analog stick. */
			return nil;
		case BTN_Z:
			return OFGameControllerCaptureButton;
		}
	} else if (vendorID == vendorIDSony &&
	    (productID == productIDDualSense ||
	    productID == productIDDualShock4)) {
		switch (button) {







>

>

>

<
|







106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
		}
	} else if (vendorID == vendorIDNintendo &&
	    productID == productIDN64Controller) {
		switch (button) {
		case BTN_B:
			return OFGameControllerWestButton;
		case BTN_SELECT:
			return @"_C-Pad Up";
		case BTN_X:
			return @"_C-Pad Down";
		case BTN_Y:
			return @"_C-Pad Left";
		case BTN_C:

			return @"_C-Pad Right";
		case BTN_Z:
			return OFGameControllerCaptureButton;
		}
	} else if (vendorID == vendorIDSony &&
	    (productID == productIDDualSense ||
	    productID == productIDDualShock4)) {
		switch (button) {
182
183
184
185
186
187
188
































189
190
191
192
193
194
195
	if (value < min)
		value = min;
	if (value > max)
		value = max;

	return ((value - min) / (max - min) * 2) - 1;
}

































@implementation OFEvdevGameController
@synthesize name = _name, buttons = _buttons;
@synthesize hasLeftAnalogStick = _hasLeftAnalogStick;
@synthesize hasRightAnalogStick = _hasRightAnalogStick;
@synthesize leftAnalogStickPosition = _leftAnalogStickPosition;
@synthesize rightAnalogStickPosition = _rightAnalogStickPosition;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
	if (value < min)
		value = min;
	if (value > max)
		value = max;

	return ((value - min) / (max - min) * 2) - 1;
}

static bool
emulateRightAnalogStick(uint16_t vendorID, uint16_t productID,
    OFMutableSet *pressedButtons, OFPoint *rightAnalogStickPosition)
{
	if (vendorID == vendorIDNintendo &&
	    productID == productIDN64Controller) {
		if ([pressedButtons containsObject: @"_C-Pad Left"] &&
		    [pressedButtons containsObject: @"_C-Pad Right"])
			rightAnalogStickPosition->x = -0.f;
		else if ([pressedButtons containsObject: @"_C-Pad Left"])
			rightAnalogStickPosition->x = -1.f;
		else if ([pressedButtons containsObject: @"_C-Pad Right"])
			rightAnalogStickPosition->x = 1.f;
		else
			rightAnalogStickPosition->x = 0.f;

		if ([pressedButtons containsObject: @"_C-Pad Up"] &&
		    [pressedButtons containsObject: @"_C-Pad Down"])
			rightAnalogStickPosition->y = -0.f;
		else if ([pressedButtons containsObject: @"_C-Pad Up"])
			rightAnalogStickPosition->y = -1.f;
		else if ([pressedButtons containsObject: @"_C-Pad Down"])
			rightAnalogStickPosition->y = 1.f;
		else
			rightAnalogStickPosition->y = 0.f;

		return true;
	}

	return false;
}

@implementation OFEvdevGameController
@synthesize name = _name, buttons = _buttons;
@synthesize hasLeftAnalogStick = _hasLeftAnalogStick;
@synthesize hasRightAnalogStick = _hasRightAnalogStick;
@synthesize leftAnalogStickPosition = _leftAnalogStickPosition;
@synthesize rightAnalogStickPosition = _rightAnalogStickPosition;
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
		_buttons = [[OFMutableSet alloc] init];
		for (size_t i = 0; i < sizeof(buttons) / sizeof(*buttons);
		    i++) {
			if (OFBitSetIsSet(_keyBits, buttons[i])) {
				OFGameControllerButton button = buttonToName(
				    buttons[i], _vendorID, _productID);

				if (button != nil)
					[_buttons addObject: button];
			}
		}

		_pressedButtons = [[OFMutableSet alloc] init];

		if (OFBitSetIsSet(evBits, EV_ABS)) {







|







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
		_buttons = [[OFMutableSet alloc] init];
		for (size_t i = 0; i < sizeof(buttons) / sizeof(*buttons);
		    i++) {
			if (OFBitSetIsSet(_keyBits, buttons[i])) {
				OFGameControllerButton button = buttonToName(
				    buttons[i], _vendorID, _productID);

				if (button != nil && ![button hasPrefix: @"_"])
					[_buttons addObject: button];
			}
		}

		_pressedButtons = [[OFMutableSet alloc] init];

		if (OFBitSetIsSet(evBits, EV_ABS)) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
		_leftAnalogStickMaxY = infoY.maximum;
		_leftAnalogStickPosition.x = scale(infoX.value,
		    _leftAnalogStickMinX, _leftAnalogStickMaxX);
		_leftAnalogStickPosition.y = scale(infoY.value,
		    _leftAnalogStickMinY, _leftAnalogStickMaxY);
	}

	if (_vendorID == vendorIDNintendo &&
	    _productID == productIDN64Controller &&
	    OFBitSetIsSet(_keyBits, BTN_Y) && OFBitSetIsSet(_keyBits, BTN_C) &&
	    OFBitSetIsSet(_keyBits, BTN_SELECT) &&
	    OFBitSetIsSet(_keyBits, BTN_X))
		_rightAnalogStickPosition = OFMakePoint(
		    -OFBitSetIsSet(keyState, BTN_Y) +
		    OFBitSetIsSet(keyState, BTN_C),
		    -OFBitSetIsSet(keyState, BTN_SELECT) +
		    OFBitSetIsSet(keyState, BTN_X));
	else if (_hasRightAnalogStick) {
		struct input_absinfo infoX, infoY;

		if (ioctl(_fd, EVIOCGABS(_rightAnalogStickXBit), &infoX) == -1)
			@throw [OFReadFailedException
			    exceptionWithObject: self
				requestedLength: sizeof(infoX)
					  errNo: errno];







<
<
<
<
<
|
<
<
<
<
|







516
517
518
519
520
521
522





523




524
525
526
527
528
529
530
531
		_leftAnalogStickMaxY = infoY.maximum;
		_leftAnalogStickPosition.x = scale(infoX.value,
		    _leftAnalogStickMinX, _leftAnalogStickMaxX);
		_leftAnalogStickPosition.y = scale(infoY.value,
		    _leftAnalogStickMinY, _leftAnalogStickMaxY);
	}






	if (!emulateRightAnalogStick(_vendorID, _productID, _pressedButtons,




	    &_rightAnalogStickPosition) && _hasRightAnalogStick) {
		struct input_absinfo infoX, infoY;

		if (ioctl(_fd, EVIOCGABS(_rightAnalogStickXBit), &infoX) == -1)
			@throw [OFReadFailedException
			    exceptionWithObject: self
				requestedLength: sizeof(infoX)
					  errNo: errno];
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
		case EV_KEY:
			if ((button = buttonToName(event.code, _vendorID,
			    _productID)) != nil) {
				if (event.value)
					[_pressedButtons addObject: button];
				else
					[_pressedButtons removeObject: button];
			}

			/* Use C buttons to emulate right analog stick */
			if (_vendorID == vendorIDNintendo &&
			    _productID == productIDN64Controller) {
				switch (event.code) {
				case BTN_Y:
					_rightAnalogStickPosition.x +=
					    (event.value ? -1 : 1);
					break;
				case BTN_C:
					_rightAnalogStickPosition.x +=
					    (event.value ? 1 : -1);
					break;
				case BTN_SELECT:
					_rightAnalogStickPosition.y +=
					    (event.value ? -1 : 1);
					break;
				case BTN_X:
					_rightAnalogStickPosition.y +=
					    (event.value ? 1 : -1);
					break;
				}
			}

			break;
		case EV_ABS:
			if (event.code == ABS_X)
				_leftAnalogStickPosition.x = scale(event.value,
				    _leftAnalogStickMinX, _leftAnalogStickMaxX);
			else if (event.code == ABS_Y)
				_leftAnalogStickPosition.y = scale(event.value,







|
<
<
<
<
<
<
|
<
|
<
|
<
<
<
<
<
<
<
<
<
<
|
<
<







617
618
619
620
621
622
623
624






625

626

627










628


629
630
631
632
633
634
635
		case EV_KEY:
			if ((button = buttonToName(event.code, _vendorID,
			    _productID)) != nil) {
				if (event.value)
					[_pressedButtons addObject: button];
				else
					[_pressedButtons removeObject: button];







				emulateRightAnalogStick(_vendorID, _productID,

				    _pressedButtons,

				    &_rightAnalogStickPosition);










			}


			break;
		case EV_ABS:
			if (event.code == ABS_X)
				_leftAnalogStickPosition.x = scale(event.value,
				    _leftAnalogStickMinX, _leftAnalogStickMaxX);
			else if (event.code == ABS_Y)
				_leftAnalogStickPosition.y = scale(event.value,
716
717
718
719
720
721
722









723
724
725
726
727
728
729
730
		return OFOrderedAscending;

	return OFOrderedSame;
}

- (OFSet *)pressedButtons
{









	return [[_pressedButtons copy] autorelease];
}

- (float)pressureForButton: (OFGameControllerButton)button
{
	if (button == OFGameControllerLeftTriggerButton &&
	    _hasLeftTriggerPressure)
		return _leftTriggerPressure;







>
>
>
>
>
>
>
>
>
|







721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
		return OFOrderedAscending;

	return OFOrderedSame;
}

- (OFSet *)pressedButtons
{
	OFMutableSet *pressedButtons =
	    [OFMutableSet setWithCapacity: _pressedButtons.count];

	for (OFGameControllerButton button in _pressedButtons)
		if (![button hasPrefix: @"_"])
			[pressedButtons addObject: button];

	[pressedButtons makeImmutable];

	return pressedButtons;
}

- (float)pressureForButton: (OFGameControllerButton)button
{
	if (button == OFGameControllerLeftTriggerButton &&
	    _hasLeftTriggerPressure)
		return _leftTriggerPressure;

Modified src/hid/OFGameController.h from [db97df8138] to [3ca0d701fd].

208
209
210
211
212
213
214
215
216



217
218
219
220
221
222
223
 * The range is from (-1, -1) to (1, 1).
 */
@property (readonly, nonatomic) OFPoint leftAnalogStickPosition;

/**
 * @brief Whether the controller has a right analog stick.
 *
 * @note The Nintendo 64 controller has no right analog stick, however, the C
 *	 buttons are used to emulate one.



 */
@property (readonly, nonatomic) bool hasRightAnalogStick;

/**
 * @brief The position of the right analog stick.
 *
 * The range is from (-1, -1) to (1, 1).







|
|
>
>
>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
 * The range is from (-1, -1) to (1, 1).
 */
@property (readonly, nonatomic) OFPoint leftAnalogStickPosition;

/**
 * @brief Whether the controller has a right analog stick.
 *
 * @note The Nintendo 64 controller has no right analog stick. However, the C
 *	 buttons are used to emulate one. As C buttons allow pressing buttons
 *	 for two opposite directions at the same time while an analog stick
 *	 does not allow for this, the value -0.0 is used instead of 0.0 to
 *	 indicate both opposite directions are pressed at the same time.
 */
@property (readonly, nonatomic) bool hasRightAnalogStick;

/**
 * @brief The position of the right analog stick.
 *
 * The range is from (-1, -1) to (1, 1).