xref: /haiku/src/add-ons/input_server/devices/mouse/MouseInputDevice.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2004-2009, 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 "kb_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 < B_OK)
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 		close(fDevice);
232 		return status;
233 	}
234 
235 	return fDevice >= 0 ? B_OK : B_ERROR;
236 }
237 
238 
239 void
240 MouseDevice::Stop()
241 {
242 	MD_CALLED();
243 
244 	fActive = false;
245 		// this will stop the thread as soon as it reads the next packet
246 
247 	close(fDevice);
248 	fDevice = -1;
249 
250 	if (fThread >= B_OK) {
251 		// unblock the thread, which might wait on a semaphore.
252 		suspend_thread(fThread);
253 		resume_thread(fThread);
254 
255 		status_t dummy;
256 		wait_for_thread(fThread, &dummy);
257 	}
258 }
259 
260 
261 status_t
262 MouseDevice::UpdateSettings()
263 {
264 	MD_CALLED();
265 
266 	if (fThread < 0)
267 		return B_ERROR;
268 
269 	// trigger updating the settings in the control thread
270 	fUpdateSettings = true;
271 
272 	return B_OK;
273 }
274 
275 
276 status_t
277 MouseDevice::UpdateTouchpadSettings(const BMessage* message)
278 {
279 	if (!fIsTouchpad)
280 		return B_BAD_TYPE;
281 	if (fThread < 0)
282 		return B_ERROR;
283 
284 	BAutolock _(fTouchpadSettingsLock);
285 
286 	// trigger updating the settings in the control thread
287 	fUpdateSettings = true;
288 
289 	delete fTouchpadSettingsMessage;
290 	fTouchpadSettingsMessage = new BMessage(*message);
291 
292 	return B_OK;
293 }
294 
295 
296 char*
297 MouseDevice::_BuildShortName() const
298 {
299 	BString string(fPath);
300 	BString name;
301 
302 	int32 slash = string.FindLast("/");
303 	string.CopyInto(name, slash + 1, string.Length() - slash);
304 	int32 index = atoi(name.String()) + 1;
305 
306 	int32 previousSlash = string.FindLast("/", slash);
307 	string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1);
308 
309 	if (name == "ps2")
310 		name = "PS/2";
311 
312 	if (name.Length() < 4)
313 		name.ToUpper();
314 	else
315 		name.Capitalize();
316 
317 	if (string.FindFirst("touchpad") >= 0) {
318 		name << " Touchpad ";
319 	} else {
320 		if (string.FindFirst("intelli") >= 0)
321 			name.Prepend("Extended ");
322 
323 		name << " Mouse ";
324 	}
325 	name << index;
326 
327 	return strdup(name.String());
328 }
329 
330 
331 // #pragma mark - control thread
332 
333 
334 status_t
335 MouseDevice::_ControlThreadEntry(void* arg)
336 {
337 	MouseDevice* device = (MouseDevice*)arg;
338 	device->_ControlThread();
339 	return B_OK;
340 }
341 
342 
343 void
344 MouseDevice::_ControlThread()
345 {
346 	MD_CALLED();
347 
348 	if (fDevice < 0) {
349 		_ControlThreadCleanup();
350 		// TOAST!
351 		return;
352 	}
353 
354 	// touchpad settings
355 	if (ioctl(fDevice, MS_IS_TOUCHPAD, NULL) == B_OK) {
356 		TRACE("is touchpad %s\n", fPath.String());
357 		fIsTouchpad = true;
358 
359 		fTouchpadSettings = kDefaultTouchpadSettings;
360 
361 		BPath path;
362 		status_t status = _GetTouchpadSettingsPath(path);
363 		BFile settingsFile(path.Path(), B_READ_ONLY);
364 		if (status == B_OK && settingsFile.InitCheck() == B_OK) {
365 			if (settingsFile.Read(&fTouchpadSettings, sizeof(touchpad_settings))
366 					!= sizeof(touchpad_settings)) {
367 				TRACE("failed to load settings\n");
368 			}
369 		}
370 		_UpdateTouchpadSettings();
371 	}
372 
373 	_UpdateSettings();
374 
375 	uint32 lastButtons = 0;
376 	float historyDeltaX = 0.0;
377 	float historyDeltaY = 0.0;
378 
379 	static const bigtime_t kTransferDelay = 1000000 / 125;
380 		// 125 transfers per second should be more than enough
381 #define USE_REGULAR_INTERVAL 1
382 #if USE_REGULAR_INTERVAL
383 	bigtime_t nextTransferTime = system_time() + kTransferDelay;
384 #endif
385 
386 	while (fActive) {
387 		mouse_movement movements;
388 
389 #if USE_REGULAR_INTERVAL
390 		snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE);
391 		nextTransferTime += kTransferDelay;
392 #endif
393 
394 		if (ioctl(fDevice, MS_READ, &movements) != B_OK) {
395 			_ControlThreadCleanup();
396 			// TOAST!
397 			return;
398 		}
399 
400 		// take care of updating the settings first, if necessary
401 		if (fUpdateSettings) {
402 			fUpdateSettings = false;
403 			if (fIsTouchpad) {
404 				BAutolock _(fTouchpadSettingsLock);
405 				if (fTouchpadSettingsMessage) {
406 					_ReadTouchpadSettingsMsg(fTouchpadSettingsMessage);
407 					_UpdateTouchpadSettings();
408 					delete fTouchpadSettingsMessage;
409 					fTouchpadSettingsMessage = NULL;
410 				} else
411 					_UpdateSettings();
412 			} else
413 				_UpdateSettings();
414 		}
415 
416 		uint32 buttons = lastButtons ^ movements.buttons;
417 
418 		uint32 remappedButtons = _RemapButtons(movements.buttons);
419 		int32 deltaX, deltaY;
420 		_ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX,
421 			historyDeltaY);
422 
423 		LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, "
424 			"wheel_x:%ld, wheel_y:%ld\n",
425 			fDeviceRef.name, movements.buttons,
426 			movements.xdelta, movements.ydelta, movements.clicks,
427 			movements.wheel_xdelta, movements.wheel_ydelta);
428 		LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name,
429 			deltaX, deltaY, historyDeltaX, historyDeltaY);
430 
431 		// Send single messages for each event
432 
433 		if (buttons != 0) {
434 			bool pressedButton = (buttons & movements.buttons) > 0;
435 			BMessage* message = _BuildMouseMessage(
436 				pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP,
437 				movements.timestamp, remappedButtons, deltaX, deltaY);
438 			if (message != NULL) {
439 				if (pressedButton) {
440 					message->AddInt32("clicks", movements.clicks);
441 					LOG_EVENT("B_MOUSE_DOWN\n");
442 				} else
443 					LOG_EVENT("B_MOUSE_UP\n");
444 
445 				fTarget.EnqueueMessage(message);
446 				lastButtons = movements.buttons;
447 			}
448 		}
449 
450 		if (movements.xdelta != 0 || movements.ydelta != 0) {
451 			BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED,
452 				movements.timestamp, remappedButtons, deltaX, deltaY);
453 			if (message != NULL)
454 				fTarget.EnqueueMessage(message);
455 		}
456 
457 		if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
458 			BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED);
459 			if (message == NULL)
460 				continue;
461 
462 			if (message->AddInt64("when", movements.timestamp) == B_OK
463 				&& message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta) == B_OK
464 				&& message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta) == B_OK)
465 				fTarget.EnqueueMessage(message);
466 			else
467 				delete message;
468 		}
469 
470 #if !USE_REGULAR_INTERVAL
471 		snooze(kTransferDelay);
472 #endif
473 	}
474 }
475 
476 
477 void
478 MouseDevice::_ControlThreadCleanup()
479 {
480 	// NOTE: Only executed when the control thread detected an error
481 	// and from within the control thread!
482 
483 	if (fActive) {
484 		fThread = -1;
485 		fTarget._RemoveDevice(fPath.String());
486 	} else {
487 		// In case active is already false, another thread
488 		// waits for this thread to quit, and may already hold
489 		// locks that _RemoveDevice() wants to acquire. In another
490 		// words, the device is already being removed, so we simply
491 		// quit here.
492 	}
493 }
494 
495 
496 void
497 MouseDevice::_UpdateSettings()
498 {
499 	MD_CALLED();
500 
501 	// retrieve current values
502 
503 	if (get_mouse_map(&fSettings.map) != B_OK)
504 		LOG_ERR("error when get_mouse_map\n");
505 	else {
506 		fDeviceRemapsButtons
507 			= ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK;
508 	}
509 
510 	if (get_click_speed(&fSettings.click_speed) != B_OK)
511 		LOG_ERR("error when get_click_speed\n");
512 	else
513 		ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed);
514 
515 	if (get_mouse_speed(&fSettings.accel.speed) != B_OK)
516 		LOG_ERR("error when get_mouse_speed\n");
517 	else {
518 		if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK)
519 			LOG_ERR("error when get_mouse_acceleration\n");
520 		else {
521 			mouse_accel accel;
522 			ioctl(fDevice, MS_GET_ACCEL, &accel);
523 			accel.speed = fSettings.accel.speed;
524 			accel.accel_factor = fSettings.accel.accel_factor;
525 			ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel);
526 		}
527 	}
528 
529 	if (get_mouse_type(&fSettings.type) != B_OK)
530 		LOG_ERR("error when get_mouse_type\n");
531 	else
532 		ioctl(fDevice, MS_SET_TYPE, &fSettings.type);
533 }
534 
535 
536 status_t
537 MouseDevice::_GetTouchpadSettingsPath(BPath& path)
538 {
539 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
540 	if (status < B_OK)
541 		return status;
542 	return path.Append(TOUCHPAD_SETTINGS_FILE);
543 }
544 
545 
546 status_t
547 MouseDevice::_ReadTouchpadSettingsMsg(BMessage* message)
548 {
549 	message->FindBool("scroll_twofinger",
550 		&(fTouchpadSettings.scroll_twofinger));
551 	message->FindBool("scroll_multifinger",
552 		&(fTouchpadSettings.scroll_multifinger));
553 	message->FindFloat("scroll_rightrange",
554 		&(fTouchpadSettings.scroll_rightrange));
555 	message->FindFloat("scroll_bottomrange",
556 		&(fTouchpadSettings.scroll_bottomrange));
557 	message->FindInt16("scroll_xstepsize",
558 		(int16*)&(fTouchpadSettings.scroll_xstepsize));
559 	message->FindInt16("scroll_ystepsize",
560 		(int16*)&(fTouchpadSettings.scroll_ystepsize));
561 	message->FindInt8("scroll_acceleration",
562 		(int8*)&(fTouchpadSettings.scroll_acceleration));
563 	message->FindInt8("tapgesture_sensibility",
564 		(int8*)&(fTouchpadSettings.tapgesture_sensibility));
565 
566 	return B_OK;
567 }
568 
569 
570 status_t
571 MouseDevice::_UpdateTouchpadSettings()
572 {
573 	if (fIsTouchpad) {
574 		ioctl(fDevice, MS_SET_TOUCHPAD_SETTINGS, &fTouchpadSettings);
575 		return B_OK;
576 	}
577 	return B_ERROR;
578 }
579 
580 
581 BMessage*
582 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons,
583 	int32 deltaX, int32 deltaY) const
584 {
585 	BMessage* message = new BMessage(what);
586 	if (message == NULL)
587 		return NULL;
588 
589 	if (message->AddInt64("when", when) < B_OK
590 		|| message->AddInt32("buttons", buttons) < B_OK
591 		|| message->AddInt32("x", deltaX) < B_OK
592 		|| message->AddInt32("y", deltaY) < B_OK) {
593 		delete message;
594 		return NULL;
595 	}
596 
597 	return message;
598 }
599 
600 
601 void
602 MouseDevice::_ComputeAcceleration(const mouse_movement& movements,
603 	int32& _deltaX, int32& _deltaY, float& historyDeltaX,
604 	float& historyDeltaY) const
605 {
606 	// basic mouse speed
607 	float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0
608 		+ historyDeltaX;
609 	float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0
610 		+ historyDeltaY;
611 
612 	// acceleration
613 	double acceleration = 1;
614 	if (fSettings.accel.accel_factor) {
615 		acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY)
616 			* fSettings.accel.accel_factor / 524288.0;
617 	}
618 
619 	deltaX *= acceleration;
620 	deltaY *= acceleration;
621 
622 	if (deltaX >= 0)
623 		_deltaX = (int32)floorf(deltaX);
624 	else
625 		_deltaX = (int32)ceilf(deltaX);
626 
627 	if (deltaY >= 0)
628 		_deltaY = (int32)floorf(deltaY);
629 	else
630 		_deltaY = (int32)ceilf(deltaY);
631 
632 	historyDeltaX = deltaX - _deltaX;
633 	historyDeltaY = deltaY - _deltaY;
634 }
635 
636 
637 uint32
638 MouseDevice::_RemapButtons(uint32 buttons) const
639 {
640 	if (fDeviceRemapsButtons)
641 		return buttons;
642 
643 	uint32 newButtons = 0;
644 	for (int32 i = 0; buttons; i++) {
645 		if (buttons & 0x1) {
646 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO)
647 			newButtons |= fSettings.map.button[i];
648 #else
649 			if (i == 0)
650 				newButtons |= fSettings.map.left;
651 			if (i == 1)
652 				newButtons |= fSettings.map.right;
653 			if (i == 2)
654 				newButtons |= fSettings.map.middle;
655 #endif
656 		}
657 		buttons >>= 1;
658 	}
659 
660 	return newButtons;
661 }
662 
663 
664 //	#pragma mark -
665 
666 
667 MouseInputDevice::MouseInputDevice()
668 	:
669 	fDevices(2, true),
670 	fDeviceListLock("MouseInputDevice list")
671 {
672 	MID_CALLED();
673 
674 	StartMonitoringDevice(kMouseDevicesDirectory);
675 	StartMonitoringDevice(kTouchpadDevicesDirectory);
676 	_RecursiveScan(kMouseDevicesDirectory);
677 	_RecursiveScan(kTouchpadDevicesDirectory);
678 }
679 
680 
681 MouseInputDevice::~MouseInputDevice()
682 {
683 	MID_CALLED();
684 
685 	StopMonitoringDevice(kTouchpadDevicesDirectory);
686 	StopMonitoringDevice(kMouseDevicesDirectory);
687 	fDevices.MakeEmpty();
688 }
689 
690 
691 status_t
692 MouseInputDevice::InitCheck()
693 {
694 	MID_CALLED();
695 
696 	return BInputServerDevice::InitCheck();
697 }
698 
699 
700 status_t
701 MouseInputDevice::Start(const char* name, void* cookie)
702 {
703 	MID_CALLED();
704 
705 	MouseDevice* device = (MouseDevice*)cookie;
706 
707 	return device->Start();
708 }
709 
710 
711 status_t
712 MouseInputDevice::Stop(const char* name, void* cookie)
713 {
714 	TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name);
715 
716 	MouseDevice* device = (MouseDevice*)cookie;
717 	device->Stop();
718 
719 	return B_OK;
720 }
721 
722 
723 status_t
724 MouseInputDevice::Control(const char* name, void* cookie,
725 	uint32 command, BMessage* message)
726 {
727 	TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
728 
729 	MouseDevice* device = (MouseDevice*)cookie;
730 
731 	if (command == B_NODE_MONITOR)
732 		return _HandleMonitor(message);
733 
734 	if (command == MS_SET_TOUCHPAD_SETTINGS)
735 		return device->UpdateTouchpadSettings(message);
736 
737 	if (command >= B_MOUSE_TYPE_CHANGED
738 		&& command <= B_MOUSE_ACCELERATION_CHANGED)
739 		return device->UpdateSettings();
740 
741 	return B_BAD_VALUE;
742 }
743 
744 
745 status_t
746 MouseInputDevice::_HandleMonitor(BMessage* message)
747 {
748 	MID_CALLED();
749 
750 	const char* path;
751 	int32 opcode;
752 	if (message->FindInt32("opcode", &opcode) != B_OK
753 		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
754 		|| message->FindString("path", &path) != B_OK)
755 		return B_BAD_VALUE;
756 
757 	if (opcode == B_ENTRY_CREATED)
758 		return _AddDevice(path);
759 
760 #if 0
761 	return _RemoveDevice(path);
762 #else
763 	// Don't handle B_ENTRY_REMOVED, let the control thread take care of it.
764 	return B_OK;
765 #endif
766 }
767 
768 
769 void
770 MouseInputDevice::_RecursiveScan(const char* directory)
771 {
772 	MID_CALLED();
773 
774 	BEntry entry;
775 	BDirectory dir(directory);
776 	while (dir.GetNextEntry(&entry) == B_OK) {
777 		BPath path;
778 		entry.GetPath(&path);
779 
780 		if (!strcmp(path.Leaf(), "serial")) {
781 			// skip serial
782 			continue;
783 		}
784 
785 		if (entry.IsDirectory())
786 			_RecursiveScan(path.Path());
787 		else
788 			_AddDevice(path.Path());
789 	}
790 }
791 
792 
793 MouseDevice*
794 MouseInputDevice::_FindDevice(const char* path) const
795 {
796 	MID_CALLED();
797 
798 	for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) {
799 		MouseDevice* device = fDevices.ItemAt(i);
800 		if (strcmp(device->Path(), path) == 0)
801 			return device;
802 	}
803 
804 	return NULL;
805 }
806 
807 
808 status_t
809 MouseInputDevice::_AddDevice(const char* path)
810 {
811 	MID_CALLED();
812 
813 	BAutolock _(fDeviceListLock);
814 
815 	_RemoveDevice(path);
816 
817 	MouseDevice* device = new(std::nothrow) MouseDevice(*this, path);
818 	if (!device) {
819 		TRACE("No memory\n");
820 		return B_NO_MEMORY;
821 	}
822 
823 	if (!fDevices.AddItem(device)) {
824 		TRACE("No memory in list\n");
825 		delete device;
826 		return B_NO_MEMORY;
827 	}
828 
829 	input_device_ref* devices[2];
830 	devices[0] = device->DeviceRef();
831 	devices[1] = NULL;
832 
833 	TRACE("adding path: %s, name: %s\n", path, devices[0]->name);
834 
835 	return RegisterDevices(devices);
836 }
837 
838 
839 status_t
840 MouseInputDevice::_RemoveDevice(const char* path)
841 {
842 	MID_CALLED();
843 
844 	BAutolock _(fDeviceListLock);
845 
846 	MouseDevice* device = _FindDevice(path);
847 	if (device == NULL) {
848 		TRACE("%s not found\n", path);
849 		return B_ENTRY_NOT_FOUND;
850 	}
851 
852 	input_device_ref* devices[2];
853 	devices[0] = device->DeviceRef();
854 	devices[1] = NULL;
855 
856 	TRACE("removing path: %s, name: %s\n", path, devices[0]->name);
857 
858 	UnregisterDevices(devices);
859 
860 	fDevices.RemoveItem(device);
861 
862 	return B_OK;
863 }
864 
865 
866 
867