xref: /haiku/src/kits/device/Joystick.cpp (revision 9b83a2a0439bb79cb4a52cbe33e95842b9539464)
1 /*
2  * Copyright 2002-2008, Marcus Overhagen, Stefano Ceccherini, Fredrik Modéen.
3  * All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <Joystick.h>
9 #include <JoystickTweaker.h>
10 
11 #include <new>
12 #include <stdio.h>
13 #include <sys/ioctl.h>
14 
15 #include <Debug.h>
16 #include <Directory.h>
17 #include <List.h>
18 #include <Path.h>
19 #include <String.h>
20 
21 
22 #if DEBUG
23 static FILE *sLogFile = NULL;
24 
25 inline void
LOG(const char * fmt,...)26 LOG(const char *fmt, ...)
27 {
28 	char buf[1024];
29 	va_list ap;
30 	va_start(ap, fmt);
31 	vsprintf(buf, fmt, ap);
32 	va_end(ap);
33 	fputs(buf, sLogFile); fflush(sLogFile);
34 }
35 
36 #	define LOG_ERR(text...) LOG(text)
37 
38 #else
39 #	define LOG(text...)
40 #	define LOG_ERR(text...) fprintf(stderr, text)
41 #endif
42 
43 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
44 
45 
BJoystick()46 BJoystick::BJoystick()
47 	:
48 	// legacy members for standard mode
49 	timestamp(0),
50 	horizontal(0),
51 	vertical(0),
52 	button1(true),
53 	button2(true),
54 
55 	fBeBoxMode(false),
56 	fFD(-1),
57 	fDevices(new(std::nothrow) BList),
58 	fJoystickInfo(new(std::nothrow) joystick_info),
59 	fJoystickData(new(std::nothrow) BList)
60 {
61 #if DEBUG
62 	sLogFile = fopen("/var/log/joystick.log", "a");
63 #endif
64 
65 	if (fJoystickInfo != NULL) {
66 		memset(&fJoystickInfo->module_info, 0, sizeof(joystick_module_info));
67 		fJoystickInfo->calibration_enable = false;
68 		fJoystickInfo->max_latency = 0;
69 	}
70 
71 	RescanDevices();
72 }
73 
74 
~BJoystick()75 BJoystick::~BJoystick()
76 {
77 	if (fFD >= 0)
78 		close(fFD);
79 
80 	if (fDevices != NULL) {
81 		for (int32 i = 0; i < fDevices->CountItems(); i++)
82 			delete (BString *)fDevices->ItemAt(i);
83 
84 		delete fDevices;
85 	}
86 
87 	delete fJoystickInfo;
88 
89 	if (fJoystickData != NULL) {
90 		for (int32 i = 0; i < fJoystickData->CountItems(); i++) {
91 			variable_joystick *variableJoystick
92 				= (variable_joystick *)fJoystickData->ItemAt(i);
93 			if (variableJoystick == NULL)
94 				continue;
95 
96 			free(variableJoystick->data);
97 			delete variableJoystick;
98 		}
99 
100 		delete fJoystickData;
101 	}
102 }
103 
104 
105 status_t
Open(const char * portName)106 BJoystick::Open(const char *portName)
107 {
108 	CALLED();
109 	return Open(portName, true);
110 }
111 
112 
113 status_t
Open(const char * portName,bool enhanced)114 BJoystick::Open(const char *portName, bool enhanced)
115 {
116 	CALLED();
117 
118 	if (portName == NULL)
119 		return B_BAD_VALUE;
120 
121 	if (fJoystickInfo == NULL || fJoystickData == NULL)
122 		return B_NO_INIT;
123 
124 	fBeBoxMode = !enhanced;
125 
126 	char nameBuffer[64];
127 	if (portName[0] != '/') {
128 		snprintf(nameBuffer, sizeof(nameBuffer), DEVICE_BASE_PATH"/%s",
129 			portName);
130 	} else
131 		snprintf(nameBuffer, sizeof(nameBuffer), "%s", portName);
132 
133 	if (fFD >= 0)
134 		close(fFD);
135 
136 	// TODO: BeOS don't use O_EXCL, and this seems to lead to some issues. I
137 	// added this flag having read some comments by Marco Nelissen on the
138 	// annotated BeBook. I think BeOS uses O_RDWR | O_NONBLOCK here.
139 	fFD = open(nameBuffer, O_RDWR | O_NONBLOCK | O_EXCL);
140 	if (fFD < 0)
141 		return B_ERROR;
142 
143 	// read the Joystick Description file for this port/joystick
144 	_BJoystickTweaker joystickTweaker(*this);
145 	joystickTweaker.GetInfo(fJoystickInfo, portName);
146 
147 	// signal that we support variable reads
148 	fJoystickInfo->module_info.flags |= js_flag_variable_size_reads;
149 
150 	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
151 	ioctl(fFD, B_JOYSTICK_SET_DEVICE_MODULE, &fJoystickInfo->module_info,
152 		sizeof(joystick_module_info));
153 	ioctl(fFD, B_JOYSTICK_GET_DEVICE_MODULE, &fJoystickInfo->module_info,
154 		sizeof(joystick_module_info));
155 	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
156 
157 	// Allocate the variable_joystick structures to hold the info for each
158 	// "stick". Note that the whole num_sticks thing seems a bit bogus, as
159 	// all sticks would be required to have exactly the same attributes,
160 	// i.e. axis, hat and button counts, since there is only one global
161 	// joystick_info for the whole device. What's implemented here is a
162 	// "best guess", using the read position in Update() to select the
163 	// stick for which data shall be returned.
164 	bool supportsVariable
165 		= (fJoystickInfo->module_info.flags & js_flag_variable_size_reads) != 0;
166 	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
167 		variable_joystick *variableJoystick
168 			= new(std::nothrow) variable_joystick;
169 		if (variableJoystick == NULL)
170 			return B_NO_MEMORY;
171 
172 		status_t result;
173 		if (supportsVariable) {
174 			// The driver supports arbitrary controls.
175 			result = variableJoystick->initialize(
176 				fJoystickInfo->module_info.num_axes,
177 				fJoystickInfo->module_info.num_hats,
178 				fJoystickInfo->module_info.num_buttons);
179 		} else {
180 			// The driver doesn't support our variable requests so we construct
181 			// a data structure that is compatible with extended_joystick and
182 			// just use that in reads. This allows us to use a single data
183 			// format internally but be compatible with both inputs.
184 			result = variableJoystick->initialize_to_extended_joystick();
185 
186 			// Also ensure that we don't read over those boundaries.
187 			if (fJoystickInfo->module_info.num_axes > MAX_AXES)
188 				fJoystickInfo->module_info.num_axes = MAX_AXES;
189 			if (fJoystickInfo->module_info.num_hats > MAX_HATS)
190 				fJoystickInfo->module_info.num_hats = MAX_HATS;
191 			if (fJoystickInfo->module_info.num_buttons > MAX_BUTTONS)
192 				fJoystickInfo->module_info.num_buttons = MAX_BUTTONS;
193 		}
194 
195 		if (result != B_OK) {
196 			delete variableJoystick;
197 			return result;
198 		}
199 
200 		if (!fJoystickData->AddItem(variableJoystick)) {
201 			free(variableJoystick->data);
202 			delete variableJoystick;
203 			return B_NO_MEMORY;
204 		}
205 	}
206 
207 	return fFD;
208 }
209 
210 
211 void
Close(void)212 BJoystick::Close(void)
213 {
214 	CALLED();
215 	if (fFD >= 0) {
216 		close(fFD);
217 		fFD = -1;
218 	}
219 }
220 
221 
222 status_t
Update()223 BJoystick::Update()
224 {
225 	CALLED();
226 	if (fJoystickInfo == NULL || fJoystickData == NULL || fFD < 0)
227 		return B_NO_INIT;
228 
229 	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
230 		variable_joystick *values
231 			= (variable_joystick *)fJoystickData->ItemAt(i);
232 		if (values == NULL)
233 			return B_NO_INIT;
234 
235 		ssize_t result = read_pos(fFD, i, values->data,
236 			values->data_size);
237 		if (result < 0)
238 			return result;
239 
240 		if ((size_t)result != values->data_size)
241 			return B_ERROR;
242 
243 		if (i > 0)
244 			continue;
245 
246 		// fill in the legacy values for the first stick
247 		timestamp = *values->timestamp;
248 
249 		if (values->axis_count >= 1)
250 			horizontal = values->axes[0];
251 		else
252 			horizontal = 0;
253 
254 		if (values->axis_count >= 2)
255 			vertical = values->axes[1];
256 		else
257 			vertical = 0;
258 
259 		if (values->button_blocks > 0) {
260 			button1 = (*values->buttons & 1) == 0;
261 			button2 = (*values->buttons & 2) == 0;
262 		} else {
263 			button1 = true;
264 			button2 = true;
265 		}
266 	}
267 
268 	return B_OK;
269 }
270 
271 
272 status_t
SetMaxLatency(bigtime_t maxLatency)273 BJoystick::SetMaxLatency(bigtime_t maxLatency)
274 {
275 	CALLED();
276 	if (fJoystickInfo == NULL || fFD < 0)
277 		return B_NO_INIT;
278 
279 	status_t result = ioctl(fFD, B_JOYSTICK_SET_MAX_LATENCY, &maxLatency,
280 		sizeof(maxLatency));
281 	if (result == B_OK)
282 		fJoystickInfo->max_latency = maxLatency;
283 
284 	return result;
285 }
286 
287 
288 int32
CountDevices()289 BJoystick::CountDevices()
290 {
291 	CALLED();
292 
293 	if (fDevices == NULL)
294 		return 0;
295 
296 	int32 count = fDevices->CountItems();
297 
298 	LOG("Count = %d\n", count);
299 	return count;
300 }
301 
302 
303 status_t
GetDeviceName(int32 index,char * name,size_t bufSize)304 BJoystick::GetDeviceName(int32 index, char *name, size_t bufSize)
305 {
306 	CALLED();
307 	if (fDevices == NULL)
308 		return B_NO_INIT;
309 
310 	if (index >= fDevices->CountItems())
311 		return B_BAD_INDEX;
312 
313 	if (name == NULL)
314 		return B_BAD_VALUE;
315 
316 	BString *deviceName = (BString *)fDevices->ItemAt(index);
317 	if (deviceName->Length() > (int32)bufSize)
318 		return B_NAME_TOO_LONG;
319 
320 	strlcpy(name, deviceName->String(), bufSize);
321 	LOG("Device Name = %s\n", name);
322 	return B_OK;
323 }
324 
325 
326 status_t
RescanDevices()327 BJoystick::RescanDevices()
328 {
329 	CALLED();
330 
331 	if (fDevices == NULL)
332 		return B_NO_INIT;
333 
334 	ScanDevices(true);
335 	return B_OK;
336 }
337 
338 
339 bool
EnterEnhancedMode(const entry_ref * ref)340 BJoystick::EnterEnhancedMode(const entry_ref *ref)
341 {
342 	CALLED();
343 	fBeBoxMode = false;
344 	return !fBeBoxMode;
345 }
346 
347 
348 int32
CountSticks()349 BJoystick::CountSticks()
350 {
351 	CALLED();
352 	if (fJoystickInfo == NULL)
353 		return 0;
354 
355 	return fJoystickInfo->module_info.num_sticks;
356 }
357 
358 
359 int32
CountAxes()360 BJoystick::CountAxes()
361 {
362 	CALLED();
363 	if (fJoystickInfo == NULL)
364 		return 0;
365 
366 	return fJoystickInfo->module_info.num_axes;
367 }
368 
369 
370 status_t
GetAxisValues(int16 * outValues,int32 forStick)371 BJoystick::GetAxisValues(int16 *outValues, int32 forStick)
372 {
373 	CALLED();
374 
375 	if (fJoystickInfo == NULL || fJoystickData == NULL)
376 		return B_NO_INIT;
377 
378 	if (forStick < 0
379 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
380 		return B_BAD_INDEX;
381 
382 	variable_joystick *variableJoystick
383 		= (variable_joystick *)fJoystickData->ItemAt(forStick);
384 	if (variableJoystick == NULL)
385 		return B_NO_INIT;
386 
387 	memcpy(outValues, variableJoystick->axes,
388 		fJoystickInfo->module_info.num_axes * sizeof(uint16));
389 	return B_OK;
390 }
391 
392 
393 status_t
GetAxisNameAt(int32 index,BString * outName)394 BJoystick::GetAxisNameAt(int32 index, BString *outName)
395 {
396 	CALLED();
397 
398 	if (index >= CountAxes())
399 		return B_BAD_INDEX;
400 
401 	if (outName == NULL)
402 		return B_BAD_VALUE;
403 
404 	// TODO: actually retrieve the name from the driver (via a new ioctl)
405 	*outName = "Axis ";
406 	*outName << index;
407 	return B_OK;
408 }
409 
410 
411 int32
CountHats()412 BJoystick::CountHats()
413 {
414 	CALLED();
415 	if (fJoystickInfo == NULL)
416 		return 0;
417 
418 	return fJoystickInfo->module_info.num_hats;
419 }
420 
421 
422 status_t
GetHatValues(uint8 * outHats,int32 forStick)423 BJoystick::GetHatValues(uint8 *outHats, int32 forStick)
424 {
425 	CALLED();
426 
427 	if (fJoystickInfo == NULL || fJoystickData == NULL)
428 		return B_NO_INIT;
429 
430 	if (forStick < 0
431 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
432 		return B_BAD_INDEX;
433 
434 	variable_joystick *variableJoystick
435 		= (variable_joystick *)fJoystickData->ItemAt(forStick);
436 	if (variableJoystick == NULL)
437 		return B_NO_INIT;
438 
439 	memcpy(outHats, variableJoystick->hats,
440 		fJoystickInfo->module_info.num_hats);
441 	return B_OK;
442 }
443 
444 
445 status_t
GetHatNameAt(int32 index,BString * outName)446 BJoystick::GetHatNameAt(int32 index, BString *outName)
447 {
448 	CALLED();
449 
450 	if (index >= CountHats())
451 		return B_BAD_INDEX;
452 
453 	if (outName == NULL)
454 		return B_BAD_VALUE;
455 
456 	// TODO: actually retrieve the name from the driver (via a new ioctl)
457 	*outName = "Hat ";
458 	*outName << index;
459 	return B_OK;
460 }
461 
462 
463 int32
CountButtons()464 BJoystick::CountButtons()
465 {
466 	CALLED();
467 	if (fJoystickInfo == NULL)
468 		return 0;
469 
470 	return fJoystickInfo->module_info.num_buttons;
471 }
472 
473 
474 uint32
ButtonValues(int32 forStick)475 BJoystick::ButtonValues(int32 forStick)
476 {
477 	CALLED();
478 
479 	if (fJoystickInfo == NULL || fJoystickData == NULL)
480 		return 0;
481 
482 	if (forStick < 0
483 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
484 		return 0;
485 
486 	variable_joystick *variableJoystick
487 		= (variable_joystick *)fJoystickData->ItemAt(forStick);
488 	if (variableJoystick == NULL || variableJoystick->button_blocks == 0)
489 		return 0;
490 
491 	return *variableJoystick->buttons;
492 }
493 
494 
495 status_t
GetButtonValues(bool * outButtons,int32 forStick)496 BJoystick::GetButtonValues(bool *outButtons, int32 forStick)
497 {
498 	CALLED();
499 
500 	if (fJoystickInfo == NULL || fJoystickData == NULL)
501 		return B_NO_INIT;
502 
503 	if (forStick < 0
504 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
505 		return B_BAD_INDEX;
506 
507 	variable_joystick *variableJoystick
508 		= (variable_joystick *)fJoystickData->ItemAt(forStick);
509 	if (variableJoystick == NULL)
510 		return B_NO_INIT;
511 
512 	int16 buttonCount = fJoystickInfo->module_info.num_buttons;
513 	for (int16 i = 0; i < buttonCount; i++) {
514 		outButtons[i]
515 			= (variableJoystick->buttons[i / 32] & (1 << (i % 32))) != 0;
516 	}
517 
518 	return B_OK;
519 }
520 
521 
522 status_t
GetButtonNameAt(int32 index,BString * outName)523 BJoystick::GetButtonNameAt(int32 index, BString *outName)
524 {
525 	CALLED();
526 
527 	if (index >= CountButtons())
528 		return B_BAD_INDEX;
529 
530 	if (outName == NULL)
531 		return B_BAD_VALUE;
532 
533 	// TODO: actually retrieve the name from the driver (via a new ioctl)
534 	*outName = "Button ";
535 	*outName << index;
536 	return B_OK;
537 }
538 
539 
540 status_t
GetControllerModule(BString * outName)541 BJoystick::GetControllerModule(BString *outName)
542 {
543 	CALLED();
544 	if (fJoystickInfo == NULL || fFD < 0)
545 		return B_NO_INIT;
546 
547 	if (outName == NULL)
548 		return B_BAD_VALUE;
549 
550 	outName->SetTo(fJoystickInfo->module_info.module_name);
551 	return B_OK;
552 }
553 
554 
555 status_t
GetControllerName(BString * outName)556 BJoystick::GetControllerName(BString *outName)
557 {
558 	CALLED();
559 	if (fJoystickInfo == NULL || fFD < 0)
560 		return B_NO_INIT;
561 
562 	if (outName == NULL)
563 		return B_BAD_VALUE;
564 
565 	outName->SetTo(fJoystickInfo->module_info.device_name);
566 	return B_OK;
567 }
568 
569 
570 bool
IsCalibrationEnabled()571 BJoystick::IsCalibrationEnabled()
572 {
573 	CALLED();
574 	if (fJoystickInfo == NULL)
575 		return false;
576 
577 	return fJoystickInfo->calibration_enable;
578 }
579 
580 
581 status_t
EnableCalibration(bool calibrates)582 BJoystick::EnableCalibration(bool calibrates)
583 {
584 	CALLED();
585 	if (fJoystickInfo == NULL || fFD < 0)
586 		return B_NO_INIT;
587 
588 	status_t result = ioctl(fFD, B_JOYSTICK_SET_RAW_MODE, &calibrates,
589 		sizeof(calibrates));
590 	if (result == B_OK)
591 		fJoystickInfo->calibration_enable = calibrates;
592 
593 	return result;
594 }
595 
596 
597 void
Calibrate(struct _extended_joystick * reading)598 BJoystick::Calibrate(struct _extended_joystick *reading)
599 {
600 	CALLED();
601 }
602 
603 
604 void
ScanDevices(bool useDisabled)605 BJoystick::ScanDevices(bool useDisabled)
606 {
607 	CALLED();
608 	if (useDisabled) {
609 		_BJoystickTweaker joystickTweaker(*this);
610 		joystickTweaker.scan_including_disabled();
611 	}
612 }
613 
614 
615 //	#pragma mark - FBC protection
616 
617 
_ReservedJoystick1()618 void BJoystick::_ReservedJoystick1() {}
_ReservedJoystick2()619 void BJoystick::_ReservedJoystick2() {}
_ReservedJoystick3()620 void BJoystick::_ReservedJoystick3() {}
_Reserved_Joystick_4(void *,...)621 status_t BJoystick::_Reserved_Joystick_4(void*, ...) { return B_ERROR; }
_Reserved_Joystick_5(void *,...)622 status_t BJoystick::_Reserved_Joystick_5(void*, ...) { return B_ERROR; }
_Reserved_Joystick_6(void *,...)623 status_t BJoystick::_Reserved_Joystick_6(void*, ...) { return B_ERROR; }
624