xref: /haiku/src/add-ons/input_server/devices/mouse/MouseInputDevice.cpp (revision d12bb8b14803d030b4a8fba91131e4bb96c4f406)
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 
38 #undef TRACE
39 //#define TRACE_MOUSE_DEVICE
40 #ifdef TRACE_MOUSE_DEVICE
41 
42 	class FunctionTracer {
43 	public:
44 		FunctionTracer(const void* pointer, const char* className,
45 				const char* functionName,
46 				int32& depth)
47 			: fFunctionName(),
48 			  fPrepend(),
49 			  fFunctionDepth(depth),
50 			  fPointer(pointer)
51 		{
52 			fFunctionDepth++;
53 			fPrepend.Append(' ', fFunctionDepth * 2);
54 			fFunctionName << className << "::" << functionName << "()";
55 
56 			debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(),
57 				fFunctionName.String());
58 		}
59 
60 		 ~FunctionTracer()
61 		{
62 			debug_printf("%p -> %s}\n", fPointer, fPrepend.String());
63 			fFunctionDepth--;
64 		}
65 
66 	private:
67 		BString	fFunctionName;
68 		BString	fPrepend;
69 		int32&	fFunctionDepth;
70 		const void* fPointer;
71 	};
72 
73 
74 	static int32 sFunctionDepth = -1;
75 #	define MD_CALLED(x...)	FunctionTracer _ft(this, "MouseDevice", \
76 								__FUNCTION__, sFunctionDepth)
77 #	define MID_CALLED(x...)	FunctionTracer _ft(this, "MouseInputDevice", \
78 								__FUNCTION__, sFunctionDepth)
79 #	define TRACE(x...)	do { BString _to; \
80 							_to.Append(' ', (sFunctionDepth + 1) * 2); \
81 							debug_printf("%p -> %s", this, _to.String()); \
82 							debug_printf(x); } while (0)
83 #	define LOG_EVENT(text...) do {} while (0)
84 #	define LOG_ERR(text...) TRACE(text)
85 #else
86 #	define TRACE(x...) do {} while (0)
87 #	define MD_CALLED(x...) TRACE(x)
88 #	define MID_CALLED(x...) TRACE(x)
89 #	define LOG_ERR(x...) debug_printf(x)
90 #	define LOG_EVENT(x...) TRACE(x)
91 #endif
92 
93 
94 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
95 const static char* kMouseDevicesDirectory = "/dev/input/mouse";
96 const static char* kTouchpadDevicesDirectory = "/dev/input/touchpad";
97 
98 
99 class MouseDevice {
100 public:
101 								MouseDevice(MouseInputDevice& target,
102 									const char* path);
103 								~MouseDevice();
104 
105 			status_t			Start();
106 			void				Stop();
107 
108 			status_t			UpdateSettings();
109 			status_t			UpdateTouchpadSettings(const BMessage* message);
110 
111 			const char*			Path() const { return fPath.String(); }
112 			input_device_ref*	DeviceRef() { return &fDeviceRef; }
113 
114 private:
115 			char*				_BuildShortName() const;
116 
117 	static	status_t			_ControlThreadEntry(void* arg);
118 			void				_ControlThread();
119 			void				_ControlThreadCleanup();
120 			void				_UpdateSettings();
121 
122 			status_t			_GetTouchpadSettingsPath(BPath& path);
123 			status_t			_ReadTouchpadSettingsMsg(BMessage* message);
124 			status_t			_UpdateTouchpadSettings();
125 
126 			BMessage*			_BuildMouseMessage(uint32 what,
127 									uint64 when, uint32 buttons,
128 									int32 deltaX, int32 deltaY) const;
129 			void				_ComputeAcceleration(
130 									const mouse_movement& movements,
131 									int32& deltaX, int32& deltaY,
132 									float& historyDeltaX,
133 									float& historyDeltaY) const;
134 			uint32				_RemapButtons(uint32 buttons) const;
135 
136 private:
137 			MouseInputDevice&	fTarget;
138 			BString				fPath;
139 			int					fDevice;
140 
141 			input_device_ref	fDeviceRef;
142 			mouse_settings		fSettings;
143 			bool				fDeviceRemapsButtons;
144 
145 			thread_id			fThread;
146 	volatile bool				fActive;
147 	volatile bool				fUpdateSettings;
148 
149 			bool				fIsTouchpad;
150 			touchpad_settings	fTouchpadSettings;
151 			BMessage*			fTouchpadSettingsMessage;
152 			BLocker				fTouchpadSettingsLock;
153 };
154 
155 
156 extern "C" BInputServerDevice*
157 instantiate_input_device()
158 {
159 	return new(std::nothrow) MouseInputDevice();
160 }
161 
162 
163 //	#pragma mark -
164 
165 
166 MouseDevice::MouseDevice(MouseInputDevice& target, const char* driverPath)
167 	:
168 	fTarget(target),
169 	fPath(driverPath),
170 	fDevice(-1),
171 	fDeviceRemapsButtons(false),
172 	fThread(-1),
173 	fActive(false),
174 	fUpdateSettings(false),
175 	fIsTouchpad(false),
176 	fTouchpadSettingsMessage(NULL),
177 	fTouchpadSettingsLock("Touchpad settings lock")
178 {
179 	MD_CALLED();
180 
181 	fDeviceRef.name = _BuildShortName();
182 	fDeviceRef.type = B_POINTING_DEVICE;
183 	fDeviceRef.cookie = this;
184 
185 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
186 	fSettings.map.button[0] = B_PRIMARY_MOUSE_BUTTON;
187 	fSettings.map.button[1] = B_SECONDARY_MOUSE_BUTTON;
188 	fSettings.map.button[2] = B_TERTIARY_MOUSE_BUTTON;
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 	if (deviceName.FindFirst("touchpad") >= 0) {
328 		name << " Touchpad ";
329 	} else if (deviceName.FindFirst("trackpoint") >= 0) {
330 		// That's always PS/2, so don't keep the bus name
331 		name = "Trackpoint ";
332 	} else {
333 		if (deviceName.FindFirst("intelli") >= 0)
334 			name.Prepend("Extended ");
335 
336 		name << " Mouse ";
337 	}
338 	name << index;
339 
340 	return strdup(name.String());
341 }
342 
343 
344 // #pragma mark - control thread
345 
346 
347 status_t
348 MouseDevice::_ControlThreadEntry(void* arg)
349 {
350 	MouseDevice* device = (MouseDevice*)arg;
351 	device->_ControlThread();
352 	return B_OK;
353 }
354 
355 
356 void
357 MouseDevice::_ControlThread()
358 {
359 	MD_CALLED();
360 
361 	if (fDevice < 0) {
362 		_ControlThreadCleanup();
363 		// TOAST!
364 		return;
365 	}
366 
367 	// touchpad settings
368 	if (ioctl(fDevice, MS_IS_TOUCHPAD, NULL) == B_OK) {
369 		TRACE("is touchpad %s\n", fPath.String());
370 		fIsTouchpad = true;
371 
372 		fTouchpadSettings = kDefaultTouchpadSettings;
373 
374 		BPath path;
375 		status_t status = _GetTouchpadSettingsPath(path);
376 		BFile settingsFile(path.Path(), B_READ_ONLY);
377 		if (status == B_OK && settingsFile.InitCheck() == B_OK) {
378 			if (settingsFile.Read(&fTouchpadSettings, sizeof(touchpad_settings))
379 					!= sizeof(touchpad_settings)) {
380 				TRACE("failed to load settings\n");
381 			}
382 		}
383 		_UpdateTouchpadSettings();
384 	}
385 
386 	_UpdateSettings();
387 
388 	uint32 lastButtons = 0;
389 	float historyDeltaX = 0.0;
390 	float historyDeltaY = 0.0;
391 
392 	static const bigtime_t kTransferDelay = 1000000 / 125;
393 		// 125 transfers per second should be more than enough
394 #define USE_REGULAR_INTERVAL 1
395 #if USE_REGULAR_INTERVAL
396 	bigtime_t nextTransferTime = system_time() + kTransferDelay;
397 #endif
398 
399 	while (fActive) {
400 		mouse_movement movements;
401 
402 #if USE_REGULAR_INTERVAL
403 		snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE);
404 		nextTransferTime += kTransferDelay;
405 #endif
406 
407 		if (ioctl(fDevice, MS_READ, &movements, sizeof(movements)) != B_OK) {
408 			LOG_ERR("Mouse device exiting, %s\n", strerror(errno));
409 			_ControlThreadCleanup();
410 			// TOAST!
411 			return;
412 		}
413 
414 		// take care of updating the settings first, if necessary
415 		if (fUpdateSettings) {
416 			fUpdateSettings = false;
417 			if (fIsTouchpad) {
418 				BAutolock _(fTouchpadSettingsLock);
419 				if (fTouchpadSettingsMessage != NULL) {
420 					_ReadTouchpadSettingsMsg(fTouchpadSettingsMessage);
421 					_UpdateTouchpadSettings();
422 					delete fTouchpadSettingsMessage;
423 					fTouchpadSettingsMessage = NULL;
424 				} else
425 					_UpdateSettings();
426 			} else
427 				_UpdateSettings();
428 		}
429 
430 		uint32 buttons = lastButtons ^ movements.buttons;
431 
432 		uint32 remappedButtons = _RemapButtons(movements.buttons);
433 		int32 deltaX, deltaY;
434 		_ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX,
435 			historyDeltaY);
436 
437 		LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, "
438 			"wheel_x:%ld, wheel_y:%ld\n",
439 			fDeviceRef.name, movements.buttons,
440 			movements.xdelta, movements.ydelta, movements.clicks,
441 			movements.wheel_xdelta, movements.wheel_ydelta);
442 		LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name,
443 			deltaX, deltaY, historyDeltaX, historyDeltaY);
444 
445 		// Send single messages for each event
446 
447 		if (buttons != 0) {
448 			bool pressedButton = (buttons & movements.buttons) > 0;
449 			BMessage* message = _BuildMouseMessage(
450 				pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP,
451 				movements.timestamp, remappedButtons, deltaX, deltaY);
452 			if (message != NULL) {
453 				if (pressedButton) {
454 					message->AddInt32("clicks", movements.clicks);
455 					LOG_EVENT("B_MOUSE_DOWN\n");
456 				} else
457 					LOG_EVENT("B_MOUSE_UP\n");
458 
459 				fTarget.EnqueueMessage(message);
460 				lastButtons = movements.buttons;
461 			}
462 		}
463 
464 		if (movements.xdelta != 0 || movements.ydelta != 0) {
465 			BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED,
466 				movements.timestamp, remappedButtons, deltaX, deltaY);
467 			if (message != NULL)
468 				fTarget.EnqueueMessage(message);
469 		}
470 
471 		if (movements.wheel_ydelta != 0 || movements.wheel_xdelta != 0) {
472 			BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED);
473 			if (message == NULL)
474 				continue;
475 
476 			if (message->AddInt64("when", movements.timestamp) == B_OK
477 				&& message->AddFloat("be:wheel_delta_x",
478 					movements.wheel_xdelta) == B_OK
479 				&& message->AddFloat("be:wheel_delta_y",
480 					movements.wheel_ydelta) == B_OK)
481 				fTarget.EnqueueMessage(message);
482 			else
483 				delete message;
484 		}
485 
486 #if !USE_REGULAR_INTERVAL
487 		snooze(kTransferDelay);
488 #endif
489 	}
490 }
491 
492 
493 void
494 MouseDevice::_ControlThreadCleanup()
495 {
496 	// NOTE: Only executed when the control thread detected an error
497 	// and from within the control thread!
498 
499 	if (fActive) {
500 		fThread = -1;
501 		fTarget._RemoveDevice(fPath.String());
502 	} else {
503 		// In case active is already false, another thread
504 		// waits for this thread to quit, and may already hold
505 		// locks that _RemoveDevice() wants to acquire. In other
506 		// words, the device is already being removed, so we simply
507 		// quit here.
508 	}
509 }
510 
511 
512 void
513 MouseDevice::_UpdateSettings()
514 {
515 	MD_CALLED();
516 
517 	// retrieve current values
518 
519 	if (get_mouse_map(&fSettings.map) != B_OK)
520 		LOG_ERR("error when get_mouse_map\n");
521 	else {
522 		fDeviceRemapsButtons
523 			= ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK;
524 	}
525 
526 	if (get_click_speed(&fSettings.click_speed) != B_OK)
527 		LOG_ERR("error when get_click_speed\n");
528 	else
529 		ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed);
530 
531 	if (get_mouse_speed(&fSettings.accel.speed) != B_OK)
532 		LOG_ERR("error when get_mouse_speed\n");
533 	else {
534 		if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK)
535 			LOG_ERR("error when get_mouse_acceleration\n");
536 		else {
537 			mouse_accel accel;
538 			ioctl(fDevice, MS_GET_ACCEL, &accel);
539 			accel.speed = fSettings.accel.speed;
540 			accel.accel_factor = fSettings.accel.accel_factor;
541 			ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel);
542 		}
543 	}
544 
545 	if (get_mouse_type(&fSettings.type) != B_OK)
546 		LOG_ERR("error when get_mouse_type\n");
547 	else
548 		ioctl(fDevice, MS_SET_TYPE, &fSettings.type);
549 }
550 
551 
552 status_t
553 MouseDevice::_GetTouchpadSettingsPath(BPath& path)
554 {
555 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
556 	if (status < B_OK)
557 		return status;
558 	return path.Append(TOUCHPAD_SETTINGS_FILE);
559 }
560 
561 
562 status_t
563 MouseDevice::_ReadTouchpadSettingsMsg(BMessage* message)
564 {
565 	message->FindBool("scroll_twofinger", &fTouchpadSettings.scroll_twofinger);
566 	message->FindBool("scroll_twofinger_horizontal",
567 		&fTouchpadSettings.scroll_twofinger_horizontal);
568 	message->FindFloat("scroll_rightrange",
569 		&fTouchpadSettings.scroll_rightrange);
570 	message->FindFloat("scroll_bottomrange",
571 		&fTouchpadSettings.scroll_bottomrange);
572 
573 	message->FindInt16("scroll_xstepsize",
574 		(int16*)&fTouchpadSettings.scroll_xstepsize);
575 	message->FindInt16("scroll_ystepsize",
576 		(int16*)&fTouchpadSettings.scroll_ystepsize);
577 	message->FindInt8("scroll_acceleration",
578 		(int8*)&fTouchpadSettings.scroll_acceleration);
579 	message->FindInt8("tapgesture_sensibility",
580 		(int8*)&fTouchpadSettings.tapgesture_sensibility);
581 
582 	return B_OK;
583 }
584 
585 
586 status_t
587 MouseDevice::_UpdateTouchpadSettings()
588 {
589 	if (fIsTouchpad) {
590 		ioctl(fDevice, MS_SET_TOUCHPAD_SETTINGS, &fTouchpadSettings);
591 		return B_OK;
592 	}
593 	return B_ERROR;
594 }
595 
596 
597 BMessage*
598 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons,
599 	int32 deltaX, int32 deltaY) const
600 {
601 	BMessage* message = new BMessage(what);
602 	if (message == NULL)
603 		return NULL;
604 
605 	if (message->AddInt64("when", when) < B_OK
606 		|| message->AddInt32("buttons", buttons) < B_OK
607 		|| message->AddInt32("x", deltaX) < B_OK
608 		|| message->AddInt32("y", deltaY) < B_OK) {
609 		delete message;
610 		return NULL;
611 	}
612 
613 	return message;
614 }
615 
616 
617 void
618 MouseDevice::_ComputeAcceleration(const mouse_movement& movements,
619 	int32& _deltaX, int32& _deltaY, float& historyDeltaX,
620 	float& historyDeltaY) const
621 {
622 	// basic mouse speed
623 	float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0
624 		+ historyDeltaX;
625 	float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0
626 		+ historyDeltaY;
627 
628 	// acceleration
629 	double acceleration = 1;
630 	if (fSettings.accel.accel_factor) {
631 		acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY)
632 			* fSettings.accel.accel_factor / 524288.0;
633 	}
634 
635 	deltaX *= acceleration;
636 	deltaY *= acceleration;
637 
638 	if (deltaX >= 0)
639 		_deltaX = (int32)floorf(deltaX);
640 	else
641 		_deltaX = (int32)ceilf(deltaX);
642 
643 	if (deltaY >= 0)
644 		_deltaY = (int32)floorf(deltaY);
645 	else
646 		_deltaY = (int32)ceilf(deltaY);
647 
648 	historyDeltaX = deltaX - _deltaX;
649 	historyDeltaY = deltaY - _deltaY;
650 }
651 
652 
653 uint32
654 MouseDevice::_RemapButtons(uint32 buttons) const
655 {
656 	if (fDeviceRemapsButtons)
657 		return buttons;
658 
659 	uint32 newButtons = 0;
660 	for (int32 i = 0; buttons; i++) {
661 		if (buttons & 0x1) {
662 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO)
663 			newButtons |= fSettings.map.button[i];
664 #else
665 			if (i == 0)
666 				newButtons |= fSettings.map.left;
667 			if (i == 1)
668 				newButtons |= fSettings.map.right;
669 			if (i == 2)
670 				newButtons |= fSettings.map.middle;
671 #endif
672 		}
673 		buttons >>= 1;
674 	}
675 
676 	return newButtons;
677 }
678 
679 
680 //	#pragma mark -
681 
682 
683 MouseInputDevice::MouseInputDevice()
684 	:
685 	fDevices(2, true),
686 	fDeviceListLock("MouseInputDevice list")
687 {
688 	MID_CALLED();
689 
690 	StartMonitoringDevice(kMouseDevicesDirectory);
691 	StartMonitoringDevice(kTouchpadDevicesDirectory);
692 	_RecursiveScan(kMouseDevicesDirectory);
693 	_RecursiveScan(kTouchpadDevicesDirectory);
694 }
695 
696 
697 MouseInputDevice::~MouseInputDevice()
698 {
699 	MID_CALLED();
700 
701 	StopMonitoringDevice(kTouchpadDevicesDirectory);
702 	StopMonitoringDevice(kMouseDevicesDirectory);
703 	fDevices.MakeEmpty();
704 }
705 
706 
707 status_t
708 MouseInputDevice::InitCheck()
709 {
710 	MID_CALLED();
711 
712 	return BInputServerDevice::InitCheck();
713 }
714 
715 
716 status_t
717 MouseInputDevice::Start(const char* name, void* cookie)
718 {
719 	MID_CALLED();
720 
721 	MouseDevice* device = (MouseDevice*)cookie;
722 
723 	return device->Start();
724 }
725 
726 
727 status_t
728 MouseInputDevice::Stop(const char* name, void* cookie)
729 {
730 	TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name);
731 
732 	MouseDevice* device = (MouseDevice*)cookie;
733 	device->Stop();
734 
735 	return B_OK;
736 }
737 
738 
739 status_t
740 MouseInputDevice::Control(const char* name, void* cookie,
741 	uint32 command, BMessage* message)
742 {
743 	TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
744 
745 	MouseDevice* device = (MouseDevice*)cookie;
746 
747 	if (command == B_NODE_MONITOR)
748 		return _HandleMonitor(message);
749 
750 	if (command == MS_SET_TOUCHPAD_SETTINGS)
751 		return device->UpdateTouchpadSettings(message);
752 
753 	if (command >= B_MOUSE_TYPE_CHANGED
754 		&& command <= B_MOUSE_ACCELERATION_CHANGED)
755 		return device->UpdateSettings();
756 
757 	return B_BAD_VALUE;
758 }
759 
760 
761 status_t
762 MouseInputDevice::_HandleMonitor(BMessage* message)
763 {
764 	MID_CALLED();
765 
766 	const char* path;
767 	int32 opcode;
768 	if (message->FindInt32("opcode", &opcode) != B_OK
769 		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
770 		|| message->FindString("path", &path) != B_OK)
771 		return B_BAD_VALUE;
772 
773 	if (opcode == B_ENTRY_CREATED)
774 		return _AddDevice(path);
775 
776 #if 0
777 	return _RemoveDevice(path);
778 #else
779 	// Don't handle B_ENTRY_REMOVED, let the control thread take care of it.
780 	return B_OK;
781 #endif
782 }
783 
784 
785 void
786 MouseInputDevice::_RecursiveScan(const char* directory)
787 {
788 	MID_CALLED();
789 
790 	BEntry entry;
791 	BDirectory dir(directory);
792 	while (dir.GetNextEntry(&entry) == B_OK) {
793 		BPath path;
794 		entry.GetPath(&path);
795 
796 		if (!strcmp(path.Leaf(), "serial")) {
797 			// skip serial
798 			continue;
799 		}
800 
801 		if (entry.IsDirectory())
802 			_RecursiveScan(path.Path());
803 		else
804 			_AddDevice(path.Path());
805 	}
806 }
807 
808 
809 MouseDevice*
810 MouseInputDevice::_FindDevice(const char* path) const
811 {
812 	MID_CALLED();
813 
814 	for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) {
815 		MouseDevice* device = fDevices.ItemAt(i);
816 		if (strcmp(device->Path(), path) == 0)
817 			return device;
818 	}
819 
820 	return NULL;
821 }
822 
823 
824 status_t
825 MouseInputDevice::_AddDevice(const char* path)
826 {
827 	MID_CALLED();
828 
829 	BAutolock _(fDeviceListLock);
830 
831 	_RemoveDevice(path);
832 
833 	MouseDevice* device = new(std::nothrow) MouseDevice(*this, path);
834 	if (device == NULL) {
835 		TRACE("No memory\n");
836 		return B_NO_MEMORY;
837 	}
838 
839 	if (!fDevices.AddItem(device)) {
840 		TRACE("No memory in list\n");
841 		delete device;
842 		return B_NO_MEMORY;
843 	}
844 
845 	input_device_ref* devices[2];
846 	devices[0] = device->DeviceRef();
847 	devices[1] = NULL;
848 
849 	TRACE("adding path: %s, name: %s\n", path, devices[0]->name);
850 
851 	return RegisterDevices(devices);
852 }
853 
854 
855 status_t
856 MouseInputDevice::_RemoveDevice(const char* path)
857 {
858 	MID_CALLED();
859 
860 	BAutolock _(fDeviceListLock);
861 
862 	MouseDevice* device = _FindDevice(path);
863 	if (device == NULL) {
864 		TRACE("%s not found\n", path);
865 		return B_ENTRY_NOT_FOUND;
866 	}
867 
868 	input_device_ref* devices[2];
869 	devices[0] = device->DeviceRef();
870 	devices[1] = NULL;
871 
872 	TRACE("removing path: %s, name: %s\n", path, devices[0]->name);
873 
874 	UnregisterDevices(devices);
875 
876 	fDevices.RemoveItem(device);
877 
878 	return B_OK;
879 }
880