xref: /haiku/src/add-ons/input_server/devices/mouse/MouseInputDevice.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 				fTarget.EnqueueMessage(message);
522 			else
523 				delete message;
524 		}
525 
526 #if !USE_REGULAR_INTERVAL
527 		snooze(kTransferDelay);
528 #endif
529 	}
530 }
531 
532 
533 void
534 MouseDevice::_ControlThreadCleanup()
535 {
536 	// NOTE: Only executed when the control thread detected an error
537 	// and from within the control thread!
538 
539 	if (fActive) {
540 		fThread = -1;
541 		fTarget._RemoveDevice(fPath.String());
542 	} else {
543 		// In case active is already false, another thread
544 		// waits for this thread to quit, and may already hold
545 		// locks that _RemoveDevice() wants to acquire. In other
546 		// words, the device is already being removed, so we simply
547 		// quit here.
548 	}
549 }
550 
551 
552 void
553 MouseDevice::_UpdateSettings()
554 {
555 	MD_CALLED();
556 	// retrieve current values
557 
558 	if (get_mouse_map(&fSettings.map) != B_OK)
559 		LOG_ERR("error when get_mouse_map\n");
560 	else {
561 		fDeviceRemapsButtons
562 			= ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK;
563 	}
564 
565 	if (get_click_speed(&fSettings.click_speed) == B_OK) {
566 		if (fIsTouchpad)
567 			fTouchpadMovementMaker.click_speed = fSettings.click_speed;
568 		ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed);
569 	} else
570 		LOG_ERR("error when get_click_speed\n");
571 
572 	if (get_mouse_speed(fDeviceRef.name, &fSettings.accel.speed) != B_OK)
573 		LOG_ERR("error when get_mouse_speed\n");
574 	else {
575 		if (get_mouse_acceleration(fDeviceRef.name, &fSettings.accel.accel_factor) != B_OK)
576 			LOG_ERR("error when get_mouse_acceleration\n");
577 		else {
578 			mouse_accel accel;
579 			ioctl(fDevice, MS_GET_ACCEL, &accel);
580 			accel.speed = fSettings.accel.speed;
581 			accel.accel_factor = fSettings.accel.accel_factor;
582 			ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel);
583 		}
584 	}
585 
586 	if (get_mouse_type(fDeviceRef.name, &fSettings.type) != B_OK)
587 		LOG_ERR("error when get_mouse_type\n");
588 	else
589 		ioctl(fDevice, MS_SET_TYPE, &fSettings.type);
590 }
591 
592 
593 status_t
594 MouseDevice::_GetTouchpadSettingsPath(BPath& path)
595 {
596 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
597 	if (status < B_OK)
598 		return status;
599 	return path.Append(TOUCHPAD_SETTINGS_FILE);
600 }
601 
602 
603 status_t
604 MouseDevice::_UpdateTouchpadSettings(BMessage* message)
605 {
606 	touchpad_settings settings;
607 	message->FindBool("scroll_twofinger", &settings.scroll_twofinger);
608 	message->FindBool("scroll_twofinger_horizontal",
609 		&settings.scroll_twofinger_horizontal);
610 	message->FindFloat("scroll_rightrange",
611 		&settings.scroll_rightrange);
612 	message->FindFloat("scroll_bottomrange",
613 		&settings.scroll_bottomrange);
614 
615 	message->FindInt16("scroll_xstepsize",
616 		(int16*)&settings.scroll_xstepsize);
617 	message->FindInt16("scroll_ystepsize",
618 		(int16*)&settings.scroll_ystepsize);
619 	message->FindInt8("scroll_acceleration",
620 		(int8*)&settings.scroll_acceleration);
621 	message->FindInt8("tapgesture_sensibility",
622 		(int8*)&settings.tapgesture_sensibility);
623 
624 	if (fIsTouchpad)
625 		fTouchpadMovementMaker.SetSettings(settings);
626 
627 	return B_OK;
628 }
629 
630 
631 BMessage*
632 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons,
633 	int32 deltaX, int32 deltaY) const
634 {
635 	BMessage* message = new BMessage(what);
636 	if (message == NULL)
637 		return NULL;
638 
639 	if (message->AddInt64("when", when) < B_OK
640 		|| message->AddInt32("buttons", buttons) < B_OK
641 		|| message->AddInt32("x", deltaX) < B_OK
642 		|| message->AddInt32("y", deltaY) < B_OK) {
643 		delete message;
644 		return NULL;
645 	}
646 
647 	return message;
648 }
649 
650 
651 void
652 MouseDevice::_ComputeAcceleration(const mouse_movement& movements,
653 	int32& _deltaX, int32& _deltaY, float& historyDeltaX,
654 	float& historyDeltaY) const
655 {
656 	// basic mouse speed
657 	float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0
658 		+ historyDeltaX;
659 	float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0
660 		+ historyDeltaY;
661 
662 	// acceleration
663 	double acceleration = 1;
664 	if (fSettings.accel.accel_factor) {
665 		acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY)
666 			* fSettings.accel.accel_factor / 524288.0;
667 	}
668 
669 	deltaX *= acceleration;
670 	deltaY *= acceleration;
671 
672 	if (deltaX >= 0)
673 		_deltaX = (int32)floorf(deltaX);
674 	else
675 		_deltaX = (int32)ceilf(deltaX);
676 
677 	if (deltaY >= 0)
678 		_deltaY = (int32)floorf(deltaY);
679 	else
680 		_deltaY = (int32)ceilf(deltaY);
681 
682 	historyDeltaX = deltaX - _deltaX;
683 	historyDeltaY = deltaY - _deltaY;
684 }
685 
686 
687 uint32
688 MouseDevice::_RemapButtons(uint32 buttons) const
689 {
690 	if (fDeviceRemapsButtons)
691 		return buttons;
692 
693 	uint32 newButtons = 0;
694 	for (int32 i = 0; buttons; i++) {
695 		if (buttons & 0x1) {
696 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO)
697 			newButtons |= fSettings.map.button[i];
698 #else
699 			if (i == 0)
700 				newButtons |= fSettings.map.left;
701 			if (i == 1)
702 				newButtons |= fSettings.map.right;
703 			if (i == 2)
704 				newButtons |= fSettings.map.middle;
705 #endif
706 		}
707 		buttons >>= 1;
708 	}
709 
710 	return newButtons;
711 }
712 
713 
714 //	#pragma mark -
715 
716 
717 MouseInputDevice::MouseInputDevice()
718 	:
719 	fDevices(2, true),
720 	fDeviceListLock("MouseInputDevice list")
721 {
722 	MID_CALLED();
723 
724 	StartMonitoringDevice(kMouseDevicesDirectory);
725 	StartMonitoringDevice(kTouchpadDevicesDirectory);
726 	_RecursiveScan(kMouseDevicesDirectory);
727 	_RecursiveScan(kTouchpadDevicesDirectory);
728 }
729 
730 
731 MouseInputDevice::~MouseInputDevice()
732 {
733 	MID_CALLED();
734 
735 	StopMonitoringDevice(kTouchpadDevicesDirectory);
736 	StopMonitoringDevice(kMouseDevicesDirectory);
737 	fDevices.MakeEmpty();
738 }
739 
740 
741 status_t
742 MouseInputDevice::InitCheck()
743 {
744 	MID_CALLED();
745 
746 	return BInputServerDevice::InitCheck();
747 }
748 
749 
750 status_t
751 MouseInputDevice::Start(const char* name, void* cookie)
752 {
753 	MID_CALLED();
754 
755 	MouseDevice* device = (MouseDevice*)cookie;
756 
757 	return device->Start();
758 }
759 
760 
761 status_t
762 MouseInputDevice::Stop(const char* name, void* cookie)
763 {
764 	TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name);
765 
766 	MouseDevice* device = (MouseDevice*)cookie;
767 	device->Stop();
768 
769 	return B_OK;
770 }
771 
772 
773 status_t
774 MouseInputDevice::Control(const char* name, void* cookie,
775 	uint32 command, BMessage* message)
776 {
777 	TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
778 
779 	MouseDevice* device = (MouseDevice*)cookie;
780 
781 	if (command == B_NODE_MONITOR)
782 		return _HandleMonitor(message);
783 
784 	if (command == B_SET_TOUCHPAD_SETTINGS)
785 		return device->UpdateTouchpadSettings(message);
786 
787 	if (command >= B_MOUSE_TYPE_CHANGED
788 		&& command <= B_MOUSE_ACCELERATION_CHANGED)
789 		return device->UpdateSettings();
790 
791 	return B_BAD_VALUE;
792 }
793 
794 
795 status_t
796 MouseInputDevice::_HandleMonitor(BMessage* message)
797 {
798 	MID_CALLED();
799 
800 	const char* path;
801 	int32 opcode;
802 	if (message->FindInt32("opcode", &opcode) != B_OK
803 		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
804 		|| message->FindString("path", &path) != B_OK)
805 		return B_BAD_VALUE;
806 
807 	if (opcode == B_ENTRY_CREATED)
808 		return _AddDevice(path);
809 
810 #if 0
811 	return _RemoveDevice(path);
812 #else
813 	// Don't handle B_ENTRY_REMOVED, let the control thread take care of it.
814 	return B_OK;
815 #endif
816 }
817 
818 
819 void
820 MouseInputDevice::_RecursiveScan(const char* directory)
821 {
822 	MID_CALLED();
823 
824 	BEntry entry;
825 	BDirectory dir(directory);
826 	while (dir.GetNextEntry(&entry) == B_OK) {
827 		BPath path;
828 		entry.GetPath(&path);
829 
830 		if (!strcmp(path.Leaf(), "serial")) {
831 			// skip serial
832 			continue;
833 		}
834 
835 		if (entry.IsDirectory())
836 			_RecursiveScan(path.Path());
837 		else
838 			_AddDevice(path.Path());
839 	}
840 }
841 
842 
843 MouseDevice*
844 MouseInputDevice::_FindDevice(const char* path) const
845 {
846 	MID_CALLED();
847 
848 	for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) {
849 		MouseDevice* device = fDevices.ItemAt(i);
850 		if (strcmp(device->Path(), path) == 0)
851 			return device;
852 	}
853 
854 	return NULL;
855 }
856 
857 
858 status_t
859 MouseInputDevice::_AddDevice(const char* path)
860 {
861 	MID_CALLED();
862 
863 	BAutolock _(fDeviceListLock);
864 
865 	_RemoveDevice(path);
866 
867 	MouseDevice* device = new(std::nothrow) MouseDevice(*this, path);
868 	if (device == NULL) {
869 		TRACE("No memory\n");
870 		return B_NO_MEMORY;
871 	}
872 
873 	if (!fDevices.AddItem(device)) {
874 		TRACE("No memory in list\n");
875 		delete device;
876 		return B_NO_MEMORY;
877 	}
878 
879 	input_device_ref* devices[2];
880 	devices[0] = device->DeviceRef();
881 	devices[1] = NULL;
882 
883 	TRACE("adding path: %s, name: %s\n", path, devices[0]->name);
884 
885 	return RegisterDevices(devices);
886 }
887 
888 
889 status_t
890 MouseInputDevice::_RemoveDevice(const char* path)
891 {
892 	MID_CALLED();
893 
894 	BAutolock _(fDeviceListLock);
895 
896 	MouseDevice* device = _FindDevice(path);
897 	if (device == NULL) {
898 		TRACE("%s not found\n", path);
899 		return B_ENTRY_NOT_FOUND;
900 	}
901 
902 	input_device_ref* devices[2];
903 	devices[0] = device->DeviceRef();
904 	devices[1] = NULL;
905 
906 	TRACE("removing path: %s, name: %s\n", path, devices[0]->name);
907 
908 	UnregisterDevices(devices);
909 
910 	fDevices.RemoveItem(device);
911 
912 	return B_OK;
913 }
914