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