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
				   object: exception
				  repeats: false];
		[[OFRunLoop currentRunLoop] addTimer: timer
					     forMode: runLoopMode];
	}
}

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







|







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
- (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
}

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

@implementation OFTarArchive: OFObject
@synthesize encoding = _encoding;

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








|







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
@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

SRCS = OHGameController.m		\
       OHGameControllerAxis.m		\
       OHGameControllerButton.m		\
       OHGameControllerDirectionalPad.m	\
       OHGameControllerElement.m	\
       OHGameControllerProfile.m	\

       ${USE_SRCS_EVDEV}
SRCS_EVDEV = OHEvdevGameController.m


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

SRCS += OHGameControllerEmulatedAxis.m		\
	OHGameControllerEmulatedButton.m


includesubdir = ObjFWHID

include ../../buildsys.mk

install-extra:
	i=ObjFWHID.oc; \







>

|
>





|
>







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	\
	     OHEvdevGamepad.m

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

SRCS += OHGameControllerEmulatedAxis.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
	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;
@end

OF_ASSUME_NONNULL_END







|
|



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)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

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


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

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








>







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
{
@public
	int32_t _minValue, _maxValue;
}
@end

@interface OHEvdevGameControllerProfile: OHGameControllerProfile
- (instancetype)of_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,







|







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)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
	case BTN_MODE:
		return @"Mode";
	case BTN_THUMBL:
		return @"Thumb L";
	case BTN_THUMBR:
		return @"Thumb R";
	case BTN_DPAD_UP:
		return @"DPad Up";
	case BTN_DPAD_DOWN:
		return @"DPad Down";
	case BTN_DPAD_LEFT:
		return @"DPad Left";
	case BTN_DPAD_RIGHT:
		return @"DPad 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:







|

|

|

|







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 @"D-Pad Up";
	case BTN_DPAD_DOWN:
		return @"D-Pad Down";
	case BTN_DPAD_LEFT:
		return @"D-Pad Left";
	case BTN_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
		if (![device hasPrefix: @"event"])
			continue;

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

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

			@throw e;
		} @catch (OFInvalidArgumentException *e) {
			/* Not a game controller. */







|







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]
			    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
	[controllers makeImmutable];

	objc_autoreleasePoolPop(pool);

	return controllers;
}

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

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFStringEncoding encoding = [OFLocale encoding];
		struct input_id inputID;







|







305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
	[controllers makeImmutable];

	objc_autoreleasePoolPop(pool);

	return controllers;
}

- (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
					[axes setObject: axis forKey: axisName];
				}
			}
		}
		[axes makeImmutable];

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

		[self of_pollState];

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








|


|







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]
		    oh_initWithButtons: buttons
				  axes: axes];

		[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
}

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

- (void)of_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







|







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

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

- (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
				requestedLength: sizeof(event)
					  errNo: errno];
		}

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

			continue;
		}

		switch (event.type) {
		case EV_SYN:







|







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 oh_pollState];
			}

			continue;
		}

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










597
598
599
600
601
602
603
			axis.value = scale(event.value,
			   axis->_minValue, axis->_maxValue);

			break;
		}
	}
}











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

	if (![otherController isKindOfClass: [OHEvdevGameController class]])
		@throw [OFInvalidArgumentException exception];







>
>
>
>
>
>
>
>
>
>







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
}
@end

@implementation OHEvdevGameControllerAxis
@end

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

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







|







626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
}
@end

@implementation OHEvdevGameControllerAxis
@end

@implementation OHEvdevGameControllerProfile
- (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
#endif

OF_ASSUME_NONNULL_BEGIN

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


/**
 * @class OHGameController OHGameController.h ObjFWHID/OHGameController.h
 *
 * @brief A class for reading state from a game controller.
 */
@interface OHGameController: OFObject







>







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

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







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








>
>
>
>
>
>







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

#include "config.h"

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


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

@implementation OHGameController
@dynamic name, rawProfile;







>







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
	return nil;
}

- (void)retrieveState
{
	OF_UNRECOGNIZED_SELECTOR
}






- (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







>
>
>
>
>













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
 */

#include "config.h"

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

@implementation OHGameControllerEmulatedButton: OHGameControllerButton
- (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];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [super initWithName: name];








|













<
|







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
- (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:

		    @"%@%c", axis.name, (positive ? '+' : '-')];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [super initWithName: name];

63
64
65
66
67
68
69





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





@end







>
>
>
>
>

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
 */

#include "config.h"

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

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

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







|







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
@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
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OTTestCase.h"

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

- (void)setUp
{







|







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
+ (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
#import "OFNumber.h"
#import "OFStdIOStream.h"
#import "OFThread.h"

#import "OHGameController.h"
#import "OHGameControllerAxis.h"
#import "OHGameControllerButton.h"

#import "OHGameControllerProfile.h"


#import "OFReadFailedException.h"

#if defined(OF_NINTENDO_DS)
static size_t buttonsPerLine = 2;
#elif defined(OF_NINTENDO_3DS)
static size_t buttonsPerLine = 3;







>

>







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
@end

OF_APPLICATION_DELEGATE(GameControllerTests)

@implementation GameControllerTests
- (void)applicationDidFinishLaunching: (OFNotification *)notification
{


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




	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;
			OFArray OF_GENERIC(OFString *) *buttons =
			    profile.buttons.allKeys.sortedArray;
			OFArray OF_GENERIC(OFString *) *axes =
			    profile.axes.allKeys.sortedArray;


			size_t i;

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

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

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

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



				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];

				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)
					[OFStdOut writeString: @"\n"];



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



				if (++i == buttonsPerLine) {
					i = 0;




				} else

					[OFStdOut writeString: @" "];

			}







			[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







>
>



>
>
>



















|
|




>
>



|





|








|

>
>














|
<
<
<
<
<









|

>
|
>
|

|
>
>
|
|
>
>
>
>
|
>
|
>
|

>
>
>
>
>
>
|












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 = (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 writeLine: controller.description];

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

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

				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: @"[%@] ", name];





			}
			[OFStdOut setForegroundColor: [OFColor gray]];
			[OFStdOut writeString: @"\n"];

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

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

				[OFStdOut writeFormat: @"%@: %5.2f  ",
						       name, axis.value];
			}
			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"];
		}

#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