xref: /haiku/src/add-ons/input_server/devices/mouse/MouseInputDevice.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
1 /*
2  * Copyright 2004-2011, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
7  *		Jérôme Duval
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		Clemens Zeidler, haiku@clemens-zeidler.de
10  *		Stephan Aßmus, superstippi@gmx.de
11  */
12 
13 
14 #include "MouseInputDevice.h"
15 
16 #include <errno.h>
17 #include <new>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 
22 #include <Autolock.h>
23 #include <Debug.h>
24 #include <Directory.h>
25 #include <Entry.h>
26 #include <File.h>
27 #include <FindDirectory.h>
28 #include <NodeMonitor.h>
29 #include <Path.h>
30 #include <String.h>
31 #include <View.h>
32 
33 #include <kb_mouse_settings.h>
34 #include <keyboard_mouse_driver.h>
35 #include <touchpad_settings.h>
36 
37 #include "movement_maker.h"
38 
39 
40 #undef TRACE
41 //#define TRACE_MOUSE_DEVICE
42 #ifdef TRACE_MOUSE_DEVICE
43 
44 	class FunctionTracer {
45 	public:
46 		FunctionTracer(const void* pointer, const char* className,
47 				const char* functionName,
48 				int32& depth)
49 			: fFunctionName(),
50 			  fPrepend(),
51 			  fFunctionDepth(depth),
52 			  fPointer(pointer)
53 		{
54 			fFunctionDepth++;
55 			fPrepend.Append(' ', fFunctionDepth * 2);
56 			fFunctionName << className << "::" << functionName << "()";
57 
58 			debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(),
59 				fFunctionName.String());
60 		}
61 
62 		 ~FunctionTracer()
63 		{
64 			debug_printf("%p -> %s}\n", fPointer, fPrepend.String());
65 			fFunctionDepth--;
66 		}
67 
68 	private:
69 		BString	fFunctionName;
70 		BString	fPrepend;
71 		int32&	fFunctionDepth;
72 		const void* fPointer;
73 	};
74 
75 
76 	static int32 sFunctionDepth = -1;
77 #	define MD_CALLED(x...)	FunctionTracer _ft(this, "MouseDevice", \
78 								__FUNCTION__, sFunctionDepth)
79 #	define MID_CALLED(x...)	FunctionTracer _ft(this, "MouseInputDevice", \
80 								__FUNCTION__, sFunctionDepth)
81 #	define TRACE(x...)	do { BString _to; \
82 							_to.Append(' ', (sFunctionDepth + 1) * 2); \
83 							debug_printf("%p -> %s", this, _to.String()); \
84 							debug_printf(x); } while (0)
85 #	define LOG_EVENT(text...) do {} while (0)
86 #	define LOG_ERR(text...) TRACE(text)
87 #else
88 #	define TRACE(x...) do {} while (0)
89 #	define MD_CALLED(x...) TRACE(x)
90 #	define MID_CALLED(x...) TRACE(x)
91 #	define LOG_ERR(x...) debug_printf(x)
92 #	define LOG_EVENT(x...) TRACE(x)
93 #endif
94 
95 
96 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
97 const static char* kMouseDevicesDirectory = "/dev/input/mouse";
98 const static char* kTouchpadDevicesDirectory = "/dev/input/touchpad";
99 
100 
101 class MouseDevice {
102 public:
103 								MouseDevice(MouseInputDevice& target,
104 									const char* path);
105 								~MouseDevice();
106 
107 			status_t			Start();
108 			void				Stop();
109 
110 			status_t			UpdateSettings();
111 			status_t			UpdateTouchpadSettings(const BMessage* message);
112 
113 			const char*			Path() const { return fPath.String(); }
114 			input_device_ref*	DeviceRef() { return &fDeviceRef; }
115 
116 private:
117 			char*				_BuildShortName() const;
118 
119 	static	status_t			_ControlThreadEntry(void* arg);
120 			void				_ControlThread();
121 			void				_ControlThreadCleanup();
122 			void				_UpdateSettings();
123 
124 			status_t			_GetTouchpadSettingsPath(BPath& path);
125 			status_t			_UpdateTouchpadSettings(BMessage* message);
126 
127 			BMessage*			_BuildMouseMessage(uint32 what,
128 									uint64 when, uint32 buttons,
129 									int32 deltaX, int32 deltaY) const;
130 			void				_ComputeAcceleration(
131 									const mouse_movement& movements,
132 									int32& deltaX, int32& deltaY,
133 									float& historyDeltaX,
134 									float& historyDeltaY) const;
135 			uint32				_RemapButtons(uint32 buttons) const;
136 
137 private:
138 			MouseInputDevice&	fTarget;
139 			BString				fPath;
140 			int					fDevice;
141 
142 			input_device_ref	fDeviceRef;
143 			mouse_settings		fSettings;
144 			bool				fDeviceRemapsButtons;
145 
146 			thread_id			fThread;
147 	volatile bool				fActive;
148 	volatile bool				fUpdateSettings;
149 
150 			bool				fIsTouchpad;
151 			TouchpadMovement	fTouchpadMovementMaker;
152 			BMessage*			fTouchpadSettingsMessage;
153 			BLocker				fTouchpadSettingsLock;
154 };
155 
156 
157 extern "C" BInputServerDevice*
158 instantiate_input_device()
159 {
160 	return new(std::nothrow) MouseInputDevice();
161 }
162 
163 
164 //	#pragma mark -
165 
166 
167 MouseDevice::MouseDevice(MouseInputDevice& target, const char* driverPath)
168 	:
169 	fTarget(target),
170 	fPath(driverPath),
171 	fDevice(-1),
172 	fDeviceRemapsButtons(false),
173 	fThread(-1),
174 	fActive(false),
175 	fUpdateSettings(false),
176 	fIsTouchpad(false),
177 	fTouchpadSettingsMessage(NULL),
178 	fTouchpadSettingsLock("Touchpad settings lock")
179 {
180 	MD_CALLED();
181 
182 	fDeviceRef.name = _BuildShortName();
183 	fDeviceRef.type = B_POINTING_DEVICE;
184 	fDeviceRef.cookie = this;
185 
186 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
187 	for (int i = 0; i < B_MAX_MOUSE_BUTTONS; i++)
188 		fSettings.map.button[i] = B_MOUSE_BUTTON(i + 1);
189 #endif
190 };
191 
192 
193 MouseDevice::~MouseDevice()
194 {
195 	MD_CALLED();
196 	TRACE("delete\n");
197 
198 	if (fActive)
199 		Stop();
200 
201 	free(fDeviceRef.name);
202 	delete fTouchpadSettingsMessage;
203 }
204 
205 
206 status_t
207 MouseDevice::Start()
208 {
209 	MD_CALLED();
210 
211 	fDevice = open(fPath.String(), O_RDWR);
212 		// let the control thread handle any error on opening the device
213 
214 	char threadName[B_OS_NAME_LENGTH];
215 	snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name);
216 
217 	fThread = spawn_thread(_ControlThreadEntry, threadName,
218 		kMouseThreadPriority, (void*)this);
219 
220 	status_t status;
221 	if (fThread < 0)
222 		status = fThread;
223 	else {
224 		fActive = true;
225 		status = resume_thread(fThread);
226 	}
227 
228 	if (status < B_OK) {
229 		LOG_ERR("%s: can't spawn/resume watching thread: %s\n",
230 			fDeviceRef.name, strerror(status));
231 		if (fDevice >= 0)
232 			close(fDevice);
233 
234 		return status;
235 	}
236 
237 	return fDevice >= 0 ? B_OK : B_ERROR;
238 }
239 
240 
241 void
242 MouseDevice::Stop()
243 {
244 	MD_CALLED();
245 
246 	fActive = false;
247 		// this will stop the thread as soon as it reads the next packet
248 
249 	close(fDevice);
250 	fDevice = -1;
251 
252 	if (fThread >= 0) {
253 		// unblock the thread, which might wait on a semaphore.
254 		suspend_thread(fThread);
255 		resume_thread(fThread);
256 
257 		status_t dummy;
258 		wait_for_thread(fThread, &dummy);
259 	}
260 }
261 
262 
263 status_t
264 MouseDevice::UpdateSettings()
265 {
266 	MD_CALLED();
267 
268 	if (fThread < 0)
269 		return B_ERROR;
270 
271 	// trigger updating the settings in the control thread
272 	fUpdateSettings = true;
273 
274 	return B_OK;
275 }
276 
277 
278 status_t
279 MouseDevice::UpdateTouchpadSettings(const BMessage* message)
280 {
281 	if (!fIsTouchpad)
282 		return B_BAD_TYPE;
283 	if (fThread < 0)
284 		return B_ERROR;
285 
286 	BAutolock _(fTouchpadSettingsLock);
287 
288 	// trigger updating the settings in the control thread
289 	fUpdateSettings = true;
290 
291 	delete fTouchpadSettingsMessage;
292 	fTouchpadSettingsMessage = new BMessage(*message);
293 	if (fTouchpadSettingsMessage == NULL)
294 		return B_NO_MEMORY;
295 
296 	return B_OK;
297 }
298 
299 
300 char*
301 MouseDevice::_BuildShortName() const
302 {
303 	// TODO It would be simpler and better to use B_GET_DEVICE_NAME, but...
304 	// - This is currently called before the device is open
305 	// - We need to implement that in our input drivers first
306 	BString string(fPath);
307 	BString deviceName;
308 	BString name;
309 
310 	int32 slash = string.FindLast("/");
311 	string.CopyInto(deviceName, slash + 1, string.Length() - slash);
312 	// FIXME the device name may be more than just a number (for example
313 	// ibm_trackpoint_0)
314 	int32 index = atoi(deviceName.String()) + 1;
315 
316 	int32 previousSlash = string.FindLast("/", slash);
317 	string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1);
318 
319 	if (name == "ps2")
320 		name = "PS/2";
321 
322 	if (name.Length() <= 4)
323 		name.ToUpper();
324 	else
325 		name.Capitalize();
326 
327 	// Check the whole string for "touchpad" because it's a different directory
328 	// FIXME use MS_IS_TOUCHPAD ioctl instead (or fIsTouchpad which caches its
329 	// result) but this can only be done after the control thread is running
330 	if (string.FindFirst("touchpad") >= 0) {
331 		name << " Touchpad ";
332 	} else if (deviceName.FindFirst("trackpoint") >= 0) {
333 		// That's always PS/2, so don't keep the bus name
334 		name = "Trackpoint ";
335 	} else {
336 		if (deviceName.FindFirst("intelli") >= 0)
337 			name.Prepend("Extended ");
338 
339 		name << " Mouse ";
340 	}
341 	name << index;
342 
343 	return strdup(name.String());
344 }
345 
346 
347 // #pragma mark - control thread
348 
349 
350 status_t
351 MouseDevice::_ControlThreadEntry(void* arg)
352 {
353 	MouseDevice* device = (MouseDevice*)arg;
354 	device->_ControlThread();
355 	return B_OK;
356 }
357 
358 
359 void
360 MouseDevice::_ControlThread()
361 {
362 	MD_CALLED();
363 
364 	if (fDevice < 0) {
365 		_ControlThreadCleanup();
366 		// TOAST!
367 		return;
368 	}
369 
370 	// touchpad settings
371 	touchpad_specs touchpadSpecs;
372 	if (ioctl(fDevice, MS_IS_TOUCHPAD, &touchpadSpecs, sizeof(touchpadSpecs)) == B_OK) {
373 		TRACE("is touchpad %s\n", fPath.String());
374 		fIsTouchpad = true;
375 
376 		touchpad_settings settings;
377 		settings = kDefaultTouchpadSettings;
378 
379 		BPath path;
380 		status_t status = _GetTouchpadSettingsPath(path);
381 		BFile settingsFile(path.Path(), B_READ_ONLY);
382 		if (status == B_OK && settingsFile.InitCheck() == B_OK) {
383 			if (settingsFile.Read(&settings, sizeof(touchpad_settings))
384 					!= sizeof(touchpad_settings)) {
385 				TRACE("failed to load settings\n");
386 			}
387 		}
388 
389 		fTouchpadMovementMaker.SetSpecs(touchpadSpecs);
390 		fTouchpadMovementMaker.SetSettings(settings);
391 	}
392 
393 	_UpdateSettings();
394 
395 	uint32 lastButtons = 0;
396 	float historyDeltaX = 0.0;
397 	float historyDeltaY = 0.0;
398 
399 	static const bigtime_t kTransferDelay = 1000000 / 125;
400 		// 125 transfers per second should be more than enough
401 #define USE_REGULAR_INTERVAL 1
402 #if USE_REGULAR_INTERVAL
403 	bigtime_t nextTransferTime = system_time() + kTransferDelay;
404 #endif
405 
406 	// touchpads only
407 	touchpad_movement lastTouchpadMovement;
408 	bigtime_t touchpadEventTimeout = B_INFINITE_TIMEOUT;
409 
410 	while (fActive) {
411 		mouse_movement movements;
412 
413 #if USE_REGULAR_INTERVAL
414 		snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE);
415 		nextTransferTime += kTransferDelay;
416 #endif
417 
418 		if (!fIsTouchpad) {
419 			if (ioctl(fDevice, MS_READ, &movements, sizeof(movements)) != B_OK) {
420 				LOG_ERR("Mouse device exiting, %s\n", strerror(errno));
421 				_ControlThreadCleanup();
422 				// TOAST!
423 				return;
424 			}
425 		} else {
426 			touchpad_read read;
427 			read.timeout = touchpadEventTimeout;
428 
429 			status_t status = ioctl(fDevice, MS_READ_TOUCHPAD, &read, sizeof(read));
430 			if (status < 0)
431 				status = errno;
432 			if (status != B_OK && status != B_TIMED_OUT) {
433 				LOG_ERR("Mouse (touchpad) device exiting, %s\n", strerror(errno));
434 				_ControlThreadCleanup();
435 				// TOAST!
436 				return;
437 			} else if (status == B_TIMED_OUT) {
438 				read.event = MS_READ_TOUCHPAD;
439 				read.u.touchpad = lastTouchpadMovement;
440 			}
441 
442 			if (read.event == MS_READ_TOUCHPAD) {
443 				lastTouchpadMovement = read.u.touchpad;
444 				status = fTouchpadMovementMaker.EventToMovement(&read.u.touchpad,
445 					&movements, touchpadEventTimeout);
446 			} else if (read.event == MS_READ) {
447 				movements = read.u.mouse;
448 				touchpadEventTimeout = -1;
449 			}
450 
451 			if (status != B_OK)
452 				continue;
453 		}
454 
455 		// take care of updating the settings first, if necessary
456 		if (fUpdateSettings) {
457 			fUpdateSettings = false;
458 			if (fIsTouchpad) {
459 				BAutolock _(fTouchpadSettingsLock);
460 				if (fTouchpadSettingsMessage != NULL) {
461 					_UpdateTouchpadSettings(fTouchpadSettingsMessage);
462 					delete fTouchpadSettingsMessage;
463 					fTouchpadSettingsMessage = NULL;
464 				} else
465 					_UpdateSettings();
466 			} else
467 				_UpdateSettings();
468 		}
469 
470 		uint32 buttons = lastButtons ^ movements.buttons;
471 
472 		uint32 remappedButtons = _RemapButtons(movements.buttons);
473 		int32 deltaX, deltaY;
474 		_ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX,
475 			historyDeltaY);
476 
477 		LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, "
478 			"wheel_x:%ld, wheel_y:%ld\n",
479 			fDeviceRef.name, movements.buttons,
480 			movements.xdelta, movements.ydelta, movements.clicks,
481 			movements.wheel_xdelta, movements.wheel_ydelta);
482 		LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name,
483 			deltaX, deltaY, historyDeltaX, historyDeltaY);
484 
485 		// Send single messages for each event
486 
487 		if (buttons != 0) {
488 			bool pressedButton = (buttons & movements.buttons) > 0;
489 			BMessage* message = _BuildMouseMessage(
490 				pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP,
491 				movements.timestamp, remappedButtons, deltaX, deltaY);
492 			if (message != NULL) {
493 				if (pressedButton) {
494 					message->AddInt32("clicks", movements.clicks);
495 					LOG_EVENT("B_MOUSE_DOWN\n");
496 				} else
497 					LOG_EVENT("B_MOUSE_UP\n");
498 
499 				fTarget.EnqueueMessage(message);
500 				lastButtons = movements.buttons;
501 			}
502 		}
503 
504 		if (movements.xdelta != 0 || movements.ydelta != 0) {
505 			BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED,
506 				movements.timestamp, remappedButtons, deltaX, deltaY);
507 			if (message != NULL)
508 				fTarget.EnqueueMessage(message);
509 		}
510 
511 		if (movements.wheel_ydelta != 0 || movements.wheel_xdelta != 0) {
512 			BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED);
513 			if (message == NULL)
514 				continue;
515 
516 			if (message->AddInt64("when", movements.timestamp) == B_OK
517 				&& message->AddFloat("be:wheel_delta_x",
518 					movements.wheel_xdelta) == B_OK
519 				&& message->AddFloat("be:wheel_delta_y",
520 					movements.wheel_ydelta) == B_OK
521 				&& message->AddInt32("be:device_subtype",
522 					fIsTouchpad
523 						? B_TOUCHPAD_DEVICE_SUBTYPE
524 						: B_MOUSE_DEVICE_SUBTYPE) == B_OK)
525 				fTarget.EnqueueMessage(message);
526 			else
527 				delete message;
528 		}
529 
530 #if !USE_REGULAR_INTERVAL
531 		snooze(kTransferDelay);
532 #endif
533 	}
534 }
535 
536 
537 void
538 MouseDevice::_ControlThreadCleanup()
539 {
540 	// NOTE: Only executed when the control thread detected an error
541 	// and from within the control thread!
542 
543 	if (fActive) {
544 		fThread = -1;
545 		fTarget._RemoveDevice(fPath.String());
546 	} else {
547 		// In case active is already false, another thread
548 		// waits for this thread to quit, and may already hold
549 		// locks that _RemoveDevice() wants to acquire. In other
550 		// words, the device is already being removed, so we simply
551 		// quit here.
552 	}
553 }
554 
555 
556 void
557 MouseDevice::_UpdateSettings()
558 {
559 	MD_CALLED();
560 	// retrieve current values
561 
562 	if (get_mouse_map(fDeviceRef.name, &fSettings.map) != B_OK)
563 		LOG_ERR("error when get_mouse_map\n");
564 	else
565 		fDeviceRemapsButtons = ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK;
566 
567 	if (get_click_speed(fDeviceRef.name, &fSettings.click_speed) == B_OK) {
568 		if (fIsTouchpad)
569 			fTouchpadMovementMaker.click_speed = fSettings.click_speed;
570 		ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed);
571 	} else
572 		LOG_ERR("error when get_click_speed\n");
573 
574 	if (get_mouse_speed(fDeviceRef.name, &fSettings.accel.speed) != B_OK)
575 		LOG_ERR("error when get_mouse_speed\n");
576 	else {
577 		if (get_mouse_acceleration(fDeviceRef.name, &fSettings.accel.accel_factor) != B_OK)
578 			LOG_ERR("error when get_mouse_acceleration\n");
579 		else {
580 			mouse_accel accel;
581 			ioctl(fDevice, MS_GET_ACCEL, &accel);
582 			accel.speed = fSettings.accel.speed;
583 			accel.accel_factor = fSettings.accel.accel_factor;
584 			ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel);
585 		}
586 	}
587 
588 	if (get_mouse_type(fDeviceRef.name, &fSettings.type) != B_OK)
589 		LOG_ERR("error when get_mouse_type\n");
590 	else
591 		ioctl(fDevice, MS_SET_TYPE, &fSettings.type);
592 }
593 
594 
595 status_t
596 MouseDevice::_GetTouchpadSettingsPath(BPath& path)
597 {
598 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
599 	if (status < B_OK)
600 		return status;
601 	return path.Append(TOUCHPAD_SETTINGS_FILE);
602 }
603 
604 
605 status_t
606 MouseDevice::_UpdateTouchpadSettings(BMessage* message)
607 {
608 	touchpad_settings settings;
609 	message->FindBool("scroll_twofinger", &settings.scroll_twofinger);
610 	message->FindBool("scroll_twofinger_horizontal",
611 		&settings.scroll_twofinger_horizontal);
612 	message->FindFloat("scroll_rightrange",
613 		&settings.scroll_rightrange);
614 	message->FindFloat("scroll_bottomrange",
615 		&settings.scroll_bottomrange);
616 
617 	message->FindInt16("scroll_xstepsize",
618 		(int16*)&settings.scroll_xstepsize);
619 	message->FindInt16("scroll_ystepsize",
620 		(int16*)&settings.scroll_ystepsize);
621 	message->FindInt8("scroll_acceleration",
622 		(int8*)&settings.scroll_acceleration);
623 	message->FindInt8("tapgesture_sensibility",
624 		(int8*)&settings.tapgesture_sensibility);
625 
626 	if (fIsTouchpad)
627 		fTouchpadMovementMaker.SetSettings(settings);
628 
629 	return B_OK;
630 }
631 
632 
633 BMessage*
634 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons,
635 	int32 deltaX, int32 deltaY) const
636 {
637 	BMessage* message = new BMessage(what);
638 	if (message == NULL)
639 		return NULL;
640 
641 	if (message->AddInt64("when", when) < B_OK
642 		|| message->AddInt32("buttons", buttons) < B_OK
643 		|| message->AddInt32("x", deltaX) < B_OK
644 		|| message->AddInt32("y", deltaY) < B_OK
645 		|| message->AddInt32("be:device_subtype",
646 			fIsTouchpad ? B_TOUCHPAD_DEVICE_SUBTYPE : B_MOUSE_DEVICE_SUBTYPE) < B_OK) {
647 		delete message;
648 		return NULL;
649 	}
650 
651 	return message;
652 }
653 
654 
655 void
656 MouseDevice::_ComputeAcceleration(const mouse_movement& movements,
657 	int32& _deltaX, int32& _deltaY, float& historyDeltaX,
658 	float& historyDeltaY) const
659 {
660 	// basic mouse speed
661 	float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0
662 		+ historyDeltaX;
663 	float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0
664 		+ historyDeltaY;
665 
666 	// acceleration
667 	double acceleration = 1;
668 	if (fSettings.accel.accel_factor) {
669 		acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY)
670 			* fSettings.accel.accel_factor / 524288.0;
671 	}
672 
673 	deltaX *= acceleration;
674 	deltaY *= acceleration;
675 
676 	if (deltaX >= 0)
677 		_deltaX = (int32)floorf(deltaX);
678 	else
679 		_deltaX = (int32)ceilf(deltaX);
680 
681 	if (deltaY >= 0)
682 		_deltaY = (int32)floorf(deltaY);
683 	else
684 		_deltaY = (int32)ceilf(deltaY);
685 
686 	historyDeltaX = deltaX - _deltaX;
687 	historyDeltaY = deltaY - _deltaY;
688 }
689 
690 
691 uint32
692 MouseDevice::_RemapButtons(uint32 buttons) const
693 {
694 	if (fDeviceRemapsButtons)
695 		return buttons;
696 
697 	uint32 newButtons = 0;
698 	for (int32 i = 0; buttons; i++) {
699 		if (buttons & 0x1) {
700 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO)
701 			newButtons |= fSettings.map.button[i];
702 #else
703 			if (i == 0)
704 				newButtons |= fSettings.map.left;
705 			if (i == 1)
706 				newButtons |= fSettings.map.right;
707 			if (i == 2)
708 				newButtons |= fSettings.map.middle;
709 #endif
710 		}
711 		buttons >>= 1;
712 	}
713 
714 	return newButtons;
715 }
716 
717 
718 //	#pragma mark -
719 
720 
721 MouseInputDevice::MouseInputDevice()
722 	:
723 	fDevices(2, true),
724 	fDeviceListLock("MouseInputDevice list")
725 {
726 	MID_CALLED();
727 
728 	StartMonitoringDevice(kMouseDevicesDirectory);
729 	StartMonitoringDevice(kTouchpadDevicesDirectory);
730 	_RecursiveScan(kMouseDevicesDirectory);
731 	_RecursiveScan(kTouchpadDevicesDirectory);
732 }
733 
734 
735 MouseInputDevice::~MouseInputDevice()
736 {
737 	MID_CALLED();
738 
739 	StopMonitoringDevice(kTouchpadDevicesDirectory);
740 	StopMonitoringDevice(kMouseDevicesDirectory);
741 	fDevices.MakeEmpty();
742 }
743 
744 
745 status_t
746 MouseInputDevice::InitCheck()
747 {
748 	MID_CALLED();
749 
750 	return BInputServerDevice::InitCheck();
751 }
752 
753 
754 status_t
755 MouseInputDevice::Start(const char* name, void* cookie)
756 {
757 	MID_CALLED();
758 
759 	MouseDevice* device = (MouseDevice*)cookie;
760 
761 	return device->Start();
762 }
763 
764 
765 status_t
766 MouseInputDevice::Stop(const char* name, void* cookie)
767 {
768 	TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name);
769 
770 	MouseDevice* device = (MouseDevice*)cookie;
771 	device->Stop();
772 
773 	return B_OK;
774 }
775 
776 
777 status_t
778 MouseInputDevice::Control(const char* name, void* cookie,
779 	uint32 command, BMessage* message)
780 {
781 	TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
782 
783 	MouseDevice* device = (MouseDevice*)cookie;
784 
785 	if (command == B_NODE_MONITOR)
786 		return _HandleMonitor(message);
787 
788 	if (command == B_SET_TOUCHPAD_SETTINGS)
789 		return device->UpdateTouchpadSettings(message);
790 
791 	if (command >= B_MOUSE_TYPE_CHANGED
792 		&& command <= B_MOUSE_ACCELERATION_CHANGED)
793 		return device->UpdateSettings();
794 
795 	return B_BAD_VALUE;
796 }
797 
798 
799 status_t
800 MouseInputDevice::_HandleMonitor(BMessage* message)
801 {
802 	MID_CALLED();
803 
804 	const char* path;
805 	int32 opcode;
806 	if (message->FindInt32("opcode", &opcode) != B_OK
807 		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
808 		|| message->FindString("path", &path) != B_OK)
809 		return B_BAD_VALUE;
810 
811 	if (opcode == B_ENTRY_CREATED)
812 		return _AddDevice(path);
813 
814 #if 0
815 	return _RemoveDevice(path);
816 #else
817 	// Don't handle B_ENTRY_REMOVED, let the control thread take care of it.
818 	return B_OK;
819 #endif
820 }
821 
822 
823 void
824 MouseInputDevice::_RecursiveScan(const char* directory)
825 {
826 	MID_CALLED();
827 
828 	BEntry entry;
829 	BDirectory dir(directory);
830 	while (dir.GetNextEntry(&entry) == B_OK) {
831 		BPath path;
832 		entry.GetPath(&path);
833 
834 		if (!strcmp(path.Leaf(), "serial")) {
835 			// skip serial
836 			continue;
837 		}
838 
839 		if (entry.IsDirectory())
840 			_RecursiveScan(path.Path());
841 		else
842 			_AddDevice(path.Path());
843 	}
844 }
845 
846 
847 MouseDevice*
848 MouseInputDevice::_FindDevice(const char* path) const
849 {
850 	MID_CALLED();
851 
852 	for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) {
853 		MouseDevice* device = fDevices.ItemAt(i);
854 		if (strcmp(device->Path(), path) == 0)
855 			return device;
856 	}
857 
858 	return NULL;
859 }
860 
861 
862 status_t
863 MouseInputDevice::_AddDevice(const char* path)
864 {
865 	MID_CALLED();
866 
867 	BAutolock _(fDeviceListLock);
868 
869 	_RemoveDevice(path);
870 
871 	MouseDevice* device = new(std::nothrow) MouseDevice(*this, path);
872 	if (device == NULL) {
873 		TRACE("No memory\n");
874 		return B_NO_MEMORY;
875 	}
876 
877 	if (!fDevices.AddItem(device)) {
878 		TRACE("No memory in list\n");
879 		delete device;
880 		return B_NO_MEMORY;
881 	}
882 
883 	input_device_ref* devices[2];
884 	devices[0] = device->DeviceRef();
885 	devices[1] = NULL;
886 
887 	TRACE("adding path: %s, name: %s\n", path, devices[0]->name);
888 
889 	return RegisterDevices(devices);
890 }
891 
892 
893 status_t
894 MouseInputDevice::_RemoveDevice(const char* path)
895 {
896 	MID_CALLED();
897 
898 	BAutolock _(fDeviceListLock);
899 
900 	MouseDevice* device = _FindDevice(path);
901 	if (device == NULL) {
902 		TRACE("%s not found\n", path);
903 		return B_ENTRY_NOT_FOUND;
904 	}
905 
906 	input_device_ref* devices[2];
907 	devices[0] = device->DeviceRef();
908 	devices[1] = NULL;
909 
910 	TRACE("removing path: %s, name: %s\n", path, devices[0]->name);
911 
912 	UnregisterDevices(devices);
913 
914 	fDevices.RemoveItem(device);
915 
916 	return B_OK;
917 }
918