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