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