ObjFW  Check-in [c45b04b1f8]

Overview
Comment:Add OHGamepad
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c45b04b1f8f1033d61ecd69bc28fee036e4084d8dc48d18c9b73f293621d63b2
User & Date: js on 2024-06-04 23:51:18
Other Links: manifest | tags
Context
2024-06-08
19:18
ObjFWHID: Restore XInput support check-in: 5f0cc05894 user: js tags: trunk
2024-06-04
23:51
Add OHGamepad check-in: c45b04b1f8 user: js tags: trunk
2024-06-03
19:53
Compile OHEvdevGameController separately check-in: 82da883fa6 user: js tags: trunk
Changes

Modified src/OFHostAddressResolver.m from [3978a3f46f] to [1f05972ec7].

111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125







-
+







				   object: exception
				  repeats: false];
		[[OFRunLoop currentRunLoop] addTimer: timer
					     forMode: runLoopMode];
	}
}

@implementation OFHostAddressResolver: OFObject
@implementation OFHostAddressResolver
- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (OFSocketAddressFamily)addressFamily
		    resolver: (OFDNSResolver *)resolver
		    settings: (OFDNSResolverSettings *)settings
		 runLoopMode: (OFRunLoopMode)runLoopMode
		    delegate: (id <OFDNSResolverHostDelegate>)delegate
{

Modified src/OFTarArchive.m from [300800373d] to [0a16baeb97].

73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87







-
+







}

- (instancetype)of_initWithArchive: (OFTarArchive *)archive
			    stream: (OFStream *)stream
			     entry: (OFTarArchiveEntry *)entry;
@end

@implementation OFTarArchive: OFObject
@implementation OFTarArchive
@synthesize encoding = _encoding;

+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream mode: mode] autorelease];
}

Modified src/hid/Makefile from [c92a9269e5] to [f964e9ff82].

11
12
13
14
15
16
17

18
19


20
21
22
23
24
25


26
27
28
29
30
31
32
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35







+

-
+
+





-
+
+








SRCS = OHGameController.m		\
       OHGameControllerAxis.m		\
       OHGameControllerButton.m		\
       OHGameControllerDirectionalPad.m	\
       OHGameControllerElement.m	\
       OHGameControllerProfile.m	\
       OHGamepad.m			\
       ${USE_SRCS_EVDEV}
SRCS_EVDEV = OHEvdevGameController.m
SRCS_EVDEV = OHEvdevGameController.m	\
	     OHEvdevGamepad.m

INCLUDES := ${SRCS:.m=.h}	\
	    ObjFWHID.h

SRCS += OHGameControllerEmulatedAxis.m		\
	OHGameControllerEmulatedButton.m
	OHGameControllerEmulatedButton.m	\
	OHGameControllerEmulatedTriggerButton.m

includesubdir = ObjFWHID

include ../../buildsys.mk

install-extra:
	i=ObjFWHID.oc; \

Modified src/hid/OHEvdevGameController.h from [95f3f59293] to [10dbd16290].

30
31
32
33
34
35
36
37
38


39
40
41
30
31
32
33
34
35
36


37
38
39
40
41







-
-
+
+



	bool _discardUntilReport;
	unsigned long *_evBits, *_keyBits, *_absBits;
	uint16_t _vendorID, _productID;
	OFString *_name;
	OHGameControllerProfile *_rawProfile;
}

- (instancetype)of_initWithPath: (OFString *)path OF_METHOD_FAMILY(init);
- (void)of_pollState;
- (instancetype)oh_initWithPath: (OFString *)path OF_METHOD_FAMILY(init);
- (void)oh_pollState;
@end

OF_ASSUME_NONNULL_END

Modified src/hid/OHEvdevGameController.m from [4aad12f123] to [f6abf87a47].

27
28
29
30
31
32
33

34
35
36
37
38
39
40
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41







+








#import "OFArray.h"
#import "OFDictionary.h"
#import "OFFileManager.h"
#import "OFLocale.h"
#import "OFNumber.h"

#import "OHEvdevGamepad.h"
#import "OHGameControllerAxis.h"
#import "OHGameControllerButton.h"
#import "OHGameControllerProfile.h"

#include <sys/ioctl.h>
#include <linux/input.h>

48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63







-
+







{
@public
	int32_t _minValue, _maxValue;
}
@end

@interface OHEvdevGameControllerProfile: OHGameControllerProfile
- (instancetype)of_initWithButtons: (OFDictionary *)buttons
- (instancetype)oh_initWithButtons: (OFDictionary *)buttons
			      axes: (OFDictionary *)axes OF_METHOD_FAMILY(init);
@end

static const uint16_t buttonIDs[] = {
	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,
112
113
114
115
116
117
118
119

120
121

122
123

124
125

126
127
128
129
130
131
132
113
114
115
116
117
118
119

120
121

122
123

124
125

126
127
128
129
130
131
132
133







-
+

-
+

-
+

-
+







	case BTN_MODE:
		return @"Mode";
	case BTN_THUMBL:
		return @"Thumb L";
	case BTN_THUMBR:
		return @"Thumb R";
	case BTN_DPAD_UP:
		return @"DPad Up";
		return @"D-Pad Up";
	case BTN_DPAD_DOWN:
		return @"DPad Down";
		return @"D-Pad Down";
	case BTN_DPAD_LEFT:
		return @"DPad Left";
		return @"D-Pad Left";
	case BTN_DPAD_RIGHT:
		return @"DPad Right";
		return @"D-Pad Right";
	case BTN_TRIGGER_HAPPY1:
		return @"Trigger Happy 1";
	case BTN_TRIGGER_HAPPY2:
		return @"Trigger Happy 2";
	case BTN_TRIGGER_HAPPY3:
		return @"Trigger Happy 3";
	case BTN_TRIGGER_HAPPY4:
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297







-
+







		if (![device hasPrefix: @"event"])
			continue;

		path = [@"/dev/input" stringByAppendingPathComponent: device];

		@try {
			controller = [[[OHEvdevGameController alloc]
			    of_initWithPath: path] autorelease];
			    oh_initWithPath: path] autorelease];
		} @catch (OFOpenItemFailedException *e) {
			if (e.errNo == EACCES)
				continue;

			@throw e;
		} @catch (OFInvalidArgumentException *e) {
			/* Not a game controller. */
304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
305
306
307
308
309
310
311

312
313
314
315
316
317
318
319







-
+







	[controllers makeImmutable];

	objc_autoreleasePoolPop(pool);

	return controllers;
}

- (instancetype)of_initWithPath: (OFString *)path
- (instancetype)oh_initWithPath: (OFString *)path
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFStringEncoding encoding = [OFLocale encoding];
		struct input_id inputID;
411
412
413
414
415
416
417
418

419
420
421

422
423
424
425
426
427
428
412
413
414
415
416
417
418

419
420
421

422
423
424
425
426
427
428
429







-
+


-
+







					[axes setObject: axis forKey: axisName];
				}
			}
		}
		[axes makeImmutable];

		_rawProfile = [[OHEvdevGameControllerProfile alloc]
		    of_initWithButtons: buttons
		    oh_initWithButtons: buttons
				  axes: axes];

		[self of_pollState];
		[self oh_pollState];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

452
453
454
455
456
457
458
459

460
461
462
463
464
465
466
453
454
455
456
457
458
459

460
461
462
463
464
465
466
467







-
+







}

- (OFNumber *)productID
{
	return [OFNumber numberWithUnsignedShort: _productID];
}

- (void)of_pollState
- (void)oh_pollState
{
	unsigned long keyState[OFRoundUpToPowerOf2(OF_ULONG_BIT, KEY_MAX) /
	    OF_ULONG_BIT] = { 0 };

	if (ioctl(_fd, EVIOCGKEY(sizeof(keyState)), &keyState) == -1)
		@throw [OFReadFailedException
		    exceptionWithObject: self
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
546
547
548
549
550
551
552

553
554
555
556
557
558
559
560







-
+







				requestedLength: sizeof(event)
					  errNo: errno];
		}

		if (_discardUntilReport) {
			if (event.type == EV_SYN && event.value == SYN_REPORT) {
				_discardUntilReport = false;
				[self of_pollState];
				[self oh_pollState];
			}

			continue;
		}

		switch (event.type) {
		case EV_SYN:
590
591
592
593
594
595
596










597
598
599
600
601
602
603
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614







+
+
+
+
+
+
+
+
+
+







			axis.value = scale(event.value,
			   axis->_minValue, axis->_maxValue);

			break;
		}
	}
}

- (OHGamepad *)gamepad
{
	@try {
		return [[[OHEvdevGamepad alloc]
		    initWithController: self] autorelease];
	} @catch (OFInvalidArgumentException *e) {
		return nil;
	}
}

- (OFComparisonResult)compare: (OHEvdevGameController *)otherController
{
	unsigned long long selfIndex, otherIndex;

	if (![otherController isKindOfClass: [OHEvdevGameController class]])
		@throw [OFInvalidArgumentException exception];
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
626
627
628
629
630
631
632

633
634
635
636
637
638
639
640







-
+







}
@end

@implementation OHEvdevGameControllerAxis
@end

@implementation OHEvdevGameControllerProfile
- (instancetype)of_initWithButtons: (OFDictionary *)buttons
- (instancetype)oh_initWithButtons: (OFDictionary *)buttons
			      axes: (OFDictionary *)axes
{
	self = [super init];

	@try {
		_buttons = [buttons retain];
		_axes = [axes retain];

Added src/hid/OHEvdevGamepad.h version [f433593f5a].




































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OHGamepad.h"

OF_ASSUME_NONNULL_BEGIN

@class OHEvdevGameController;
@class OHGameControllerProfile;

@interface OHEvdevGamepad: OHGamepad
{
	OHGameControllerProfile *_rawProfile;
}

- (instancetype)initWithController: (OHEvdevGameController *)controller;
@end

OF_ASSUME_NONNULL_END

Added src/hid/OHEvdevGamepad.m version [a03c43c125].












































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
230
231
232
233
234
235
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OHEvdevGamepad.h"
#import "OFDictionary.h"
#import "OHEvdevGameController.h"
#import "OHGameControllerDirectionalPad.h"
#import "OHGameControllerEmulatedTriggerButton.h"

#import "OFInvalidArgumentException.h"

@implementation OHEvdevGamepad
- (instancetype)initWithController: (OHEvdevGameController *)controller
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		_rawProfile = [controller.rawProfile retain];

		if (self.northButton == nil || self.southButton == nil ||
		    self.westButton == nil || self.eastButton == nil ||
		    self.leftShoulderButton == nil ||
		    self.rightShoulderButton == nil ||
		    self.leftTriggerButton == nil ||
		    self.rightTriggerButton == nil ||
		    self.leftThumbstickButton == nil ||
		    self.rightThumbstickButton == nil ||
		    self.menuButton == nil || self.optionsButton == nil ||
		    self.homeButton == nil || self.leftThumbstick == nil ||
		    self.rightThumbstick == nil || self.directionalPad == nil)
			@throw [OFInvalidArgumentException exception];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_rawProfile release];

	[super dealloc];
}

- (OFDictionary OF_GENERIC(OFString *, OHGameControllerButton *) *)buttons
{
	return [OFDictionary dictionaryWithKeysAndObjects:
	    @"North", self.northButton,
	    @"South", self.southButton,
	    @"West", self.westButton,
	    @"East", self.eastButton,
	    @"Left Shoulder", self.leftShoulderButton,
	    @"Right Shoulder", self.rightShoulderButton,
	    @"Left Trigger", self.leftTriggerButton,
	    @"Right Trigger", self.rightTriggerButton,
	    @"Left Thumbstick", self.leftThumbstickButton,
	    @"Right Thumbstick", self.rightThumbstickButton,
	    @"Menu", self.menuButton,
	    @"Options", self.optionsButton,
	    @"Home", self.homeButton, nil];
}

- (OFDictionary OF_GENERIC(OFString *, OHGameControllerAxis *) *)axes
{
	return [OFDictionary dictionary];
}

- (OFDictionary OF_GENERIC(OFString *, OHGameControllerDirectionalPad *) *)
    directionalPads
{
	return [OFDictionary dictionaryWithKeysAndObjects:
	    @"Left Thumbstick", self.leftThumbstick,
	    @"Right Thumbstick", self.rightThumbstick,
	    @"D-Pad", self.directionalPad, nil];
}

- (OHGameControllerButton *)northButton
{
	return [_rawProfile.buttons objectForKey: @"Y"];
}

- (OHGameControllerButton *)southButton
{
	return [_rawProfile.buttons objectForKey: @"A"];
}

- (OHGameControllerButton *)westButton
{
	return [_rawProfile.buttons objectForKey: @"X"];
}

- (OHGameControllerButton *)eastButton
{
	return [_rawProfile.buttons objectForKey: @"B"];
}

- (OHGameControllerButton *)leftShoulderButton
{
	return [_rawProfile.buttons objectForKey: @"TL"];
}

- (OHGameControllerButton *)rightShoulderButton
{
	return [_rawProfile.buttons objectForKey: @"TR"];
}

- (OHGameControllerButton *)leftTriggerButton
{
	OHGameControllerAxis *axis = [_rawProfile.axes objectForKey: @"Z"];

	if (axis != nil)
		return [[[OHGameControllerEmulatedTriggerButton alloc]
		    initWithAxis: axis] autorelease];

	return [_rawProfile.buttons objectForKey: @"TL2"];
}

- (OHGameControllerButton *)rightTriggerButton
{
	OHGameControllerAxis *axis = [_rawProfile.axes objectForKey: @"RZ"];

	if (axis != nil)
		return [[[OHGameControllerEmulatedTriggerButton alloc]
		    initWithAxis: axis] autorelease];

	return [_rawProfile.buttons objectForKey: @"TR2"];
}

- (OHGameControllerButton *)leftThumbstickButton
{
	return [_rawProfile.buttons objectForKey: @"Thumb L"];
}

- (OHGameControllerButton *)rightThumbstickButton
{
	return [_rawProfile.buttons objectForKey: @"Thumb R"];
}

- (OHGameControllerButton *)menuButton
{
	return [_rawProfile.buttons objectForKey: @"Start"];
}

- (OHGameControllerButton *)optionsButton
{
	return [_rawProfile.buttons objectForKey: @"Select"];
}

- (OHGameControllerButton *)homeButton
{
	return [_rawProfile.buttons objectForKey: @"Mode"];
}

- (OHGameControllerDirectionalPad *)leftThumbstick
{
	OHGameControllerAxis *xAxis = [_rawProfile.axes objectForKey: @"X"];
	OHGameControllerAxis *yAxis = [_rawProfile.axes objectForKey: @"Y"];

	if (xAxis == nil || yAxis == nil)
		return nil;

	return [[[OHGameControllerDirectionalPad alloc]
	    initWithName: @"Left Thumbstick"
		   xAxis: xAxis
		   yAxis: yAxis] autorelease];
}

- (OHGameControllerDirectionalPad *)rightThumbstick
{
	OHGameControllerAxis *xAxis = [_rawProfile.axes objectForKey: @"RX"];
	OHGameControllerAxis *yAxis = [_rawProfile.axes objectForKey: @"RY"];

	if (xAxis == nil || yAxis == nil)
		return nil;

	return [[[OHGameControllerDirectionalPad alloc]
	    initWithName: @"Right Thumbstick"
		   xAxis: xAxis
		   yAxis: yAxis] autorelease];
}

- (OHGameControllerDirectionalPad *)directionalPad
{
	OHGameControllerAxis *xAxis = [_rawProfile.axes objectForKey: @"HAT0X"];
	OHGameControllerAxis *yAxis = [_rawProfile.axes objectForKey: @"HAT0Y"];
	OHGameControllerButton *upButton, *downButton;
	OHGameControllerButton *leftButton, *rightButton;

	if (xAxis != nil && yAxis != nil)
		return [[[OHGameControllerDirectionalPad alloc]
		    initWithName: @"D-Pad"
			   xAxis: xAxis
			   yAxis: yAxis] autorelease];

	upButton = [_rawProfile.buttons objectForKey: @"D-Pad Up"];
	downButton = [_rawProfile.buttons objectForKey: @"D-Pad Down"];
	leftButton = [_rawProfile.buttons objectForKey: @"D-Pad Left"];
	rightButton = [_rawProfile.buttons objectForKey: @"D-Pad Right"];

	if (upButton != nil && downButton != nil &&
	    leftButton != nil && rightButton != nil)
		return [[[OHGameControllerDirectionalPad alloc]
		    initWithName: @"D-Pad"
			upButton: upButton
		      downButton: downButton
		      leftButton: leftButton
		     rightButton: rightButton] autorelease];

	return nil;
}
@end

Modified src/hid/OHGameController.h from [f4bfdcdd03] to [5443a68735].

30
31
32
33
34
35
36

37
38
39
40
41
42
43
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44







+







#endif

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFNumber;
@class OHGameControllerProfile;
@class OHGamepad;

/**
 * @class OHGameController OHGameController.h ObjFWHID/OHGameController.h
 *
 * @brief A class for reading state from a game controller.
 */
@interface OHGameController: OFObject
67
68
69
70
71
72
73






74
75
76
77
78
79
80
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87







+
+
+
+
+
+








/**
 * @brief The raw profile for the game controller, meaning no remapping is
 *	  being performed.
 */
@property (readonly, nonatomic) OHGameControllerProfile *rawProfile;

/**
 * @brief The gamepad profile for the game controller, or `nil` if not
 *	  supported.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OHGamepad *gamepad;

/**
 * @brief Returns the available controllers.
 *
 * @return The available controllers
 */
+ (OFArray OF_GENERIC(OHGameController *) *)controllers;

Modified src/hid/OHGameController.m from [350c5f09f7] to [48b11dd8fc].

19
20
21
22
23
24
25

26
27
28
29
30
31
32
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33







+








#include "config.h"

#import "OHGameController.h"
#import "OFArray.h"
#import "OFNumber.h"
#import "OFSet.h"
#import "OHGamepad.h"

#if defined(OF_LINUX) && defined(OF_HAVE_FILES)
# include "OHEvdevGameController.h"
#endif

@implementation OHGameController
@dynamic name, rawProfile;
66
67
68
69
70
71
72





73
74
75
76
77
78
79
80
81
82
83
84
85
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91







+
+
+
+
+













	return nil;
}

- (void)retrieveState
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OHGamepad *)gamepad
{
	return nil;
}

- (OFString *)description
{
	if (self.vendorID != nil && self.productID != nil)
		return [OFString stringWithFormat:
		    @"<%@: %@ [%04X:%04X]>",
		    self.class, self.name, self.vendorID.unsignedShortValue,
		    self.productID.unsignedShortValue];
	else
		return [OFString stringWithFormat: @"<%@: %@>",
						   self.class, self.name];
}
@end

Modified src/hid/OHGameControllerEmulatedButton.m from [1d9492427b] to [eefaf93827].

18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
38


39
40
41
42
43
44
45
46







-
+













-
-
+







 */

#include "config.h"

#import "OHGameControllerEmulatedButton.h"
#import "OHGameControllerAxis.h"

@implementation OHGameControllerEmulatedButton: OHGameControllerButton
@implementation OHGameControllerEmulatedButton
- (instancetype)initWithName: (OFString *)name
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithAxis: (OHGameControllerAxis *)axis
		    positive: (bool)positive
{
	void *pool = objc_autoreleasePoolPush();
	OFString *name;

	@try {
		name = [OFString stringWithFormat:
		    @"%@ %@ as emulated button",
		    (positive ? @"Positive" : @"Negative"), axis.name];
		    @"%@%c", axis.name, (positive ? '+' : '-')];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [super initWithName: name];

63
64
65
66
67
68
69





70
62
63
64
65
66
67
68
69
70
71
72
73
74







+
+
+
+
+

- (bool)isPressed
{
	if (_positive)
		return (_axis.value > 0);
	else
		return (_axis.value < 0);
}

- (float)value
{
	return (self.isPressed ? 1 : 0);
}
@end

Added src/hid/OHGameControllerEmulatedTriggerButton.h version [15a75f9011].





































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OHGameControllerButton.h"

OF_ASSUME_NONNULL_BEGIN

@class OHGameControllerAxis;

OF_SUBCLASSING_RESTRICTED
@interface OHGameControllerEmulatedTriggerButton: OHGameControllerButton
{
	OHGameControllerAxis *_axis;
}

- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithAxis: (OHGameControllerAxis *)axis;
@end

OF_ASSUME_NONNULL_END

Added src/hid/OHGameControllerEmulatedTriggerButton.m version [249698b888].





































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OHGameControllerEmulatedTriggerButton.h"
#import "OHGameControllerAxis.h"

@implementation OHGameControllerEmulatedTriggerButton
- (instancetype)initWithName: (OFString *)name
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithAxis: (OHGameControllerAxis *)axis
{
	void *pool = objc_autoreleasePoolPush();
	OFString *name;

	@try {
		name = axis.name;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [super initWithName: name];

	objc_autoreleasePoolPop(pool);

	_axis = [axis retain];

	return self;
}

- (void)dealloc
{
	[_axis release];

	[super dealloc];
}

- (bool)isPressed
{
	return (_axis.value > -1);
}

- (float)value
{
	return (_axis.value + 1) / 2;
}
@end

Modified src/hid/OHGameControllerProfile.m from [e824f05e06] to [c4be5284ad].

18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32







-
+







 */

#include "config.h"

#import "OHGameControllerProfile.h"
#import "OFDictionary.h"

@implementation OHGameControllerProfile: OFObject
@implementation OHGameControllerProfile
@synthesize buttons = _buttons, axes = _axes;
@synthesize directionalPads = _directionalPads;

- (void)dealloc
{
	[_buttons release];
	[_axes release];

Added src/hid/OHGamepad.h version [823c3e7afc].





















































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OHGameControllerProfile.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OHGamepad OHGamepad.h ObjFWHID/OHGamepad.h
 *
 * @brief A game controller profile representing a gamepad.
 */
@interface OHGamepad: OHGameControllerProfile
{
	OF_RESERVE_IVARS(OHGamepad, 4)
}

@property (readonly, nonatomic) OHGameControllerButton *northButton;
@property (readonly, nonatomic) OHGameControllerButton *southButton;
@property (readonly, nonatomic) OHGameControllerButton *westButton;
@property (readonly, nonatomic) OHGameControllerButton *eastButton;
@property (readonly, nonatomic) OHGameControllerButton *leftShoulderButton;
@property (readonly, nonatomic) OHGameControllerButton *rightShoulderButton;
@property (readonly, nonatomic) OHGameControllerButton *leftTriggerButton;
@property (readonly, nonatomic) OHGameControllerButton *rightTriggerButton;
@property (readonly, nonatomic) OHGameControllerButton *leftThumbstickButton;
@property (readonly, nonatomic) OHGameControllerButton *rightThumbstickButton;
@property (readonly, nonatomic) OHGameControllerButton *menuButton;
@property (readonly, nonatomic) OHGameControllerButton *optionsButton;
@property (readonly, nonatomic) OHGameControllerButton *homeButton;
@property (readonly, nonatomic) OHGameControllerDirectionalPad *leftThumbstick;
@property (readonly, nonatomic) OHGameControllerDirectionalPad *rightThumbstick;
@property (readonly, nonatomic) OHGameControllerDirectionalPad *directionalPad;
@end

OF_ASSUME_NONNULL_END

Added src/hid/OHGamepad.m version [422aa06dbe].






























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OHGamepad.h"

@implementation OHGamepad
@dynamic northButton, southButton, westButton, eastButton, leftShoulderButton;
@dynamic rightShoulderButton, leftTriggerButton, rightTriggerButton;
@dynamic leftThumbstickButton, rightThumbstickButton, menuButton, optionsButton;
@dynamic homeButton, leftThumbstick, rightThumbstick, directionalPad;
@end

Modified src/test/OTTestCase.m from [b5276549e5] to [8b6fdc9a7d].

17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31







-
+







 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OTTestCase.h"

@implementation OTTestCase: OFObject
@implementation OTTestCase
+ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary
{
	return nil;
}

- (void)setUp
{

Modified tests/gamecontroller/GameControllerTests.m from [049667648c] to [d73797114c].

27
28
29
30
31
32
33

34

35
36
37
38
39
40
41
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43







+

+







#import "OFNumber.h"
#import "OFStdIOStream.h"
#import "OFThread.h"

#import "OHGameController.h"
#import "OHGameControllerAxis.h"
#import "OHGameControllerButton.h"
#import "OHGameControllerDirectionalPad.h"
#import "OHGameControllerProfile.h"
#import "OHGamepad.h"

#import "OFReadFailedException.h"

#if defined(OF_NINTENDO_DS)
static size_t buttonsPerLine = 2;
#elif defined(OF_NINTENDO_3DS)
static size_t buttonsPerLine = 3;
57
58
59
60
61
62
63


64
65
66



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


88
89
90
91


92
93
94
95

96
97
98
99
100
101

102
103
104
105
106
107
108
109
110

111


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142

143
144



145
146
147
148
149
150
151














152






153

154
155
156
157
158
159
160
161
162
163
164
165
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92


93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109

110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137





138
139
140
141
142
143
144
145
146

147
148
149


150
151
152
153






154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187







+
+



+
+
+



















-
-
+
+




+
+



-
+





-
+








-
+

+
+














-
+
-
-
-
-
-









-
+

+
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
-
+












@end

OF_APPLICATION_DELEGATE(GameControllerTests)

@implementation GameControllerTests
- (void)applicationDidFinishLaunching: (OFNotification *)notification
{
	bool gamepad = false;

#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS)
	[OFStdIOStream setUpConsole];
#endif

	if ([[OFApplication arguments].firstObject isEqual: @"gamepad"])
		gamepad = true;

	for (;;) {
		void *pool = objc_autoreleasePoolPush();

		if (_lastControllersUpdate == nil ||
		    -[_lastControllersUpdate timeIntervalSinceNow] > 1) {
			[_controllers release];
			[_lastControllersUpdate release];

			_controllers = [[OHGameController controllers]
			    mutableCopy];
			_lastControllersUpdate = [[OFDate alloc] init];

			[OFStdOut clear];
		}

		[OFStdOut setCursorPosition: OFMakePoint(0, 0)];

		for (OHGameController *controller in _controllers) {
			OHGameControllerProfile *profile =
			    controller.rawProfile;
			OHGameControllerProfile *profile = (gamepad
			    ? controller.gamepad : controller.rawProfile);
			OFArray OF_GENERIC(OFString *) *buttons =
			    profile.buttons.allKeys.sortedArray;
			OFArray OF_GENERIC(OFString *) *axes =
			    profile.axes.allKeys.sortedArray;
			OFArray OF_GENERIC(OFString *) *directionalPads =
			    profile.directionalPads.allKeys.sortedArray;
			size_t i;

			[OFStdOut setForegroundColor: [OFColor green]];
			[OFStdOut writeString: controller.description];
			[OFStdOut writeLine: controller.description];

			@try {
				[controller retrieveState];
			} @catch (OFReadFailedException *e) {
				[OFStdOut setForegroundColor: [OFColor red]];
				[OFStdOut writeFormat: @"\n%@", e.description];
				[OFStdOut writeString: e.description];
				continue;
			}

			i = 0;
			for (OFString *name in buttons) {
				OHGameControllerButton *button =
				    [profile.buttons objectForKey: name];

				if (i == 0)
				if (i++ == buttonsPerLine) {
					[OFStdOut writeString: @"\n"];
					i = 0;
				}

				if (button.value == 1)
					[OFStdOut setForegroundColor:
					    [OFColor red]];
				else if (button.value > 0.5)
					[OFStdOut setForegroundColor:
					    [OFColor yellow]];
				else if (button.value > 0)
					[OFStdOut setForegroundColor:
					    [OFColor green]];
				else
					[OFStdOut setForegroundColor:
					    [OFColor gray]];

				[OFStdOut writeFormat: @"[%@]", button.name];
				[OFStdOut writeFormat: @"[%@] ", name];

				if (++i == buttonsPerLine) {
					i = 0;
				} else
					[OFStdOut writeString: @" "];
			}
			[OFStdOut setForegroundColor: [OFColor gray]];
			[OFStdOut writeString: @"\n"];

			i = 0;
			for (OFString *name in axes) {
				OHGameControllerAxis *axis =
				    [profile.axes objectForKey: name];

				if (i == 0)
				if (i++ == buttonsPerLine) {
					[OFStdOut writeString: @"\n"];
					i = 0;

				[OFStdOut writeFormat: @"%@: %5.2f ",
				}

				[OFStdOut writeFormat: @"%@: %5.2f  ",
						       name, axis.value];

				if (++i == buttonsPerLine) {
					i = 0;
				} else
					[OFStdOut writeString: @" "];
			}
			}
			if (axes.count > 0)
				[OFStdOut writeString: @"\n"];

			i = 0;
			for (OFString *name in directionalPads) {
				OHGameControllerDirectionalPad *directionalPad =
				    [profile.directionalPads
				    objectForKey: name];

				if (i++ == 2) {
					[OFStdOut writeString: @"\n"];
					i = 0;
				}

				[OFStdOut writeFormat:
				    @"%@: (%5.2f, %5.2f)  ",
				    name, directionalPad.xAxis.value,
				    directionalPad.yAxis.value];
			}
			if (directionalPads.count > 0)
			[OFStdOut writeString: @"\n"];
				[OFStdOut writeString: @"\n"];
		}

#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS)
		[OFThread waitForVerticalBlank];
#else
		[OFThread sleepForTimeInterval: 1.f / 60.f];
#endif

		objc_autoreleasePoolPop(pool);
	}
}
@end