xref: /haiku/src/add-ons/input_server/devices/mouse/MouseInputDevice.cpp (revision cc44241143f3ac2997e50f09b56a851ec5c1bdc0)
123b47e0bSJérôme Duval /*
2d246d9beSStefano Ceccherini  * Copyright 2004-2007, Haiku.
323b47e0bSJérôme Duval  * Distributed under the terms of the MIT License.
423b47e0bSJérôme Duval  *
523b47e0bSJérôme Duval  * Authors:
6d246d9beSStefano Ceccherini  *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
7d246d9beSStefano Ceccherini  *		Jérôme Duval
8d246d9beSStefano Ceccherini  *		Axel Dörfler, axeld@pinc-software.de
9239fd643SStephan Aßmus  *		Stephan Aßmus, superstippi@gmx.de
1023b47e0bSJérôme Duval  */
1133efb919SStefano Ceccherini 
1233efb919SStefano Ceccherini 
1361d7deeeSJérôme Duval #include "MouseInputDevice.h"
14b6a7b204SAxel Dörfler 
15b6a7b204SAxel Dörfler #include <errno.h>
16b6a7b204SAxel Dörfler #include <new>
17b6a7b204SAxel Dörfler #include <stdio.h>
18b6a7b204SAxel Dörfler #include <stdlib.h>
19b6a7b204SAxel Dörfler #include <unistd.h>
2061d7deeeSJérôme Duval 
215125eae2SStephan Aßmus #include <Autolock.h>
22c2fbfb71SJérôme Duval #include <Debug.h>
2333efb919SStefano Ceccherini #include <Directory.h>
2433efb919SStefano Ceccherini #include <Entry.h>
2533efb919SStefano Ceccherini #include <NodeMonitor.h>
2633efb919SStefano Ceccherini #include <Path.h>
2733efb919SStefano Ceccherini #include <String.h>
2861604e48SAxel Dörfler #include <View.h>
2961604e48SAxel Dörfler 
30b6a7b204SAxel Dörfler #include "kb_mouse_settings.h"
31b6a7b204SAxel Dörfler #include "kb_mouse_driver.h"
3233efb919SStefano Ceccherini 
33b6a7b204SAxel Dörfler 
34e01a0431SStephan Aßmus #undef TRACE
35b6a7b204SAxel Dörfler //#define TRACE_MOUSE_DEVICE
36b6a7b204SAxel Dörfler #ifdef TRACE_MOUSE_DEVICE
37e01a0431SStephan Aßmus 
38e01a0431SStephan Aßmus 	class FunctionTracer {
39e01a0431SStephan Aßmus 	public:
40e01a0431SStephan Aßmus 		FunctionTracer(const void* pointer, const char* className,
41e01a0431SStephan Aßmus 				const char* functionName,
42e01a0431SStephan Aßmus 				int32& depth)
43e01a0431SStephan Aßmus 			: fFunctionName(),
44e01a0431SStephan Aßmus 			  fPrepend(),
45e01a0431SStephan Aßmus 			  fFunctionDepth(depth),
46e01a0431SStephan Aßmus 			  fPointer(pointer)
47e01a0431SStephan Aßmus 		{
48e01a0431SStephan Aßmus 			fFunctionDepth++;
49e01a0431SStephan Aßmus 			fPrepend.Append(' ', fFunctionDepth * 2);
50e01a0431SStephan Aßmus 			fFunctionName << className << "::" << functionName << "()";
51e01a0431SStephan Aßmus 
52e01a0431SStephan Aßmus 			debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(),
53e01a0431SStephan Aßmus 				fFunctionName.String());
54e01a0431SStephan Aßmus 		}
55e01a0431SStephan Aßmus 
56e01a0431SStephan Aßmus 		 ~FunctionTracer()
57e01a0431SStephan Aßmus 		{
58e01a0431SStephan Aßmus 			debug_printf("%p -> %s}\n", fPointer, fPrepend.String());
59e01a0431SStephan Aßmus 			fFunctionDepth--;
60e01a0431SStephan Aßmus 		}
61e01a0431SStephan Aßmus 
62e01a0431SStephan Aßmus 	private:
63e01a0431SStephan Aßmus 		BString	fFunctionName;
64e01a0431SStephan Aßmus 		BString	fPrepend;
65e01a0431SStephan Aßmus 		int32&	fFunctionDepth;
66e01a0431SStephan Aßmus 		const void* fPointer;
67e01a0431SStephan Aßmus 	};
68e01a0431SStephan Aßmus 
69e01a0431SStephan Aßmus 
70e01a0431SStephan Aßmus 	static int32 sFunctionDepth = -1;
71e01a0431SStephan Aßmus #	define MD_CALLED(x...)	FunctionTracer _ft(this, "MouseDevice", \
72e01a0431SStephan Aßmus 								__FUNCTION__, sFunctionDepth)
73e01a0431SStephan Aßmus #	define MID_CALLED(x...)	FunctionTracer _ft(this, "MouseInputDevice", \
74e01a0431SStephan Aßmus 								__FUNCTION__, sFunctionDepth)
75e01a0431SStephan Aßmus #	define TRACE(x...)	{ BString _to; \
76e01a0431SStephan Aßmus 							_to.Append(' ', (sFunctionDepth + 1) * 2); \
77e01a0431SStephan Aßmus 							debug_printf("%p -> %s", this, _to.String()); \
78e01a0431SStephan Aßmus 							debug_printf(x); }
79e01a0431SStephan Aßmus #	define LOG_EVENT(text...) do {} while (0)
805125eae2SStephan Aßmus #	define LOG_ERR(text...) TRACE(text)
813aa69c78SStefano Ceccherini #else
82e01a0431SStephan Aßmus #	define TRACE(x...) do {} while (0)
83e01a0431SStephan Aßmus #	define MD_CALLED(x...) TRACE(x)
84e01a0431SStephan Aßmus #	define MID_CALLED(x...) TRACE(x)
85b6a7b204SAxel Dörfler #	define LOG_ERR(text...) debug_printf(text)
86e01a0431SStephan Aßmus #	define LOG_EVENT(text...) TRACE(x)
873aa69c78SStefano Ceccherini #endif
883aa69c78SStefano Ceccherini 
89e01a0431SStephan Aßmus 
9033efb919SStefano Ceccherini const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
9133efb919SStefano Ceccherini const static char* kMouseDevicesDirectory = "/dev/input/mouse";
9233efb919SStefano Ceccherini 
9333efb919SStefano Ceccherini 
9461604e48SAxel Dörfler class MouseDevice {
9561604e48SAxel Dörfler public:
965125eae2SStephan Aßmus 								MouseDevice(MouseInputDevice& target,
975125eae2SStephan Aßmus 									const char* path);
9861604e48SAxel Dörfler 								~MouseDevice();
9933efb919SStefano Ceccherini 
10061604e48SAxel Dörfler 			status_t			Start();
10161604e48SAxel Dörfler 			void				Stop();
10261604e48SAxel Dörfler 
10361604e48SAxel Dörfler 			status_t			UpdateSettings();
10461604e48SAxel Dörfler 
10561604e48SAxel Dörfler 			const char*			Path() const { return fPath.String(); }
10661604e48SAxel Dörfler 			input_device_ref*	DeviceRef() { return &fDeviceRef; }
10761604e48SAxel Dörfler 
10861604e48SAxel Dörfler private:
1095125eae2SStephan Aßmus 			char*				_BuildShortName() const;
11061604e48SAxel Dörfler 
1115125eae2SStephan Aßmus 	static	status_t			_ControlThreadEntry(void* arg);
1125125eae2SStephan Aßmus 			void				_ControlThread();
1135125eae2SStephan Aßmus 			void				_ControlThreadCleanup();
1145125eae2SStephan Aßmus 			void				_UpdateSettings();
1155125eae2SStephan Aßmus 
1165125eae2SStephan Aßmus 			BMessage*			_BuildMouseMessage(uint32 what,
1175125eae2SStephan Aßmus 									uint64 when, uint32 buttons,
11861604e48SAxel Dörfler 									int32 deltaX, int32 deltaY) const;
1195125eae2SStephan Aßmus 			void				_ComputeAcceleration(
1205125eae2SStephan Aßmus 									const mouse_movement& movements,
12161604e48SAxel Dörfler 									int32& deltaX, int32& deltaY) const;
12261604e48SAxel Dörfler 			uint32				_RemapButtons(uint32 buttons) const;
12361604e48SAxel Dörfler 
12461604e48SAxel Dörfler private:
125c5555ad3SStephan Aßmus 			MouseInputDevice&	fTarget;
12661604e48SAxel Dörfler 			BString				fPath;
12761604e48SAxel Dörfler 			int					fDevice;
12861604e48SAxel Dörfler 
12961604e48SAxel Dörfler 			input_device_ref	fDeviceRef;
13061604e48SAxel Dörfler 			mouse_settings		fSettings;
13161604e48SAxel Dörfler 			bool				fDeviceRemapsButtons;
13261604e48SAxel Dörfler 
13361604e48SAxel Dörfler 			thread_id			fThread;
13461604e48SAxel Dörfler 	volatile bool				fActive;
1355125eae2SStephan Aßmus 	volatile bool				fUpdateSettings;
1360dbc4befSStefano Ceccherini };
1370dbc4befSStefano Ceccherini 
1380dbc4befSStefano Ceccherini 
13961604e48SAxel Dörfler extern "C" BInputServerDevice*
14061d7deeeSJérôme Duval instantiate_input_device()
14161d7deeeSJérôme Duval {
142d246d9beSStefano Ceccherini 	return new(std::nothrow) MouseInputDevice();
14361d7deeeSJérôme Duval }
14461d7deeeSJérôme Duval 
14561d7deeeSJérôme Duval 
14661604e48SAxel Dörfler //	#pragma mark -
14761604e48SAxel Dörfler 
14861604e48SAxel Dörfler 
149c5555ad3SStephan Aßmus MouseDevice::MouseDevice(MouseInputDevice& target, const char* driverPath)
15061604e48SAxel Dörfler 	:
15161604e48SAxel Dörfler 	fTarget(target),
152c5555ad3SStephan Aßmus 	fPath(driverPath),
15361604e48SAxel Dörfler 	fDevice(-1),
154e01a0431SStephan Aßmus 	fDeviceRemapsButtons(false),
15561604e48SAxel Dörfler 	fThread(-1),
1565125eae2SStephan Aßmus 	fActive(false),
1575125eae2SStephan Aßmus 	fUpdateSettings(false)
15861d7deeeSJérôme Duval {
159e01a0431SStephan Aßmus 	MD_CALLED();
160e01a0431SStephan Aßmus 
16161604e48SAxel Dörfler 	fDeviceRef.name = _BuildShortName();
16261604e48SAxel Dörfler 	fDeviceRef.type = B_POINTING_DEVICE;
16361604e48SAxel Dörfler 	fDeviceRef.cookie = this;
16461604e48SAxel Dörfler 
16561604e48SAxel Dörfler #ifdef HAIKU_TARGET_PLATFORM_HAIKU
16661604e48SAxel Dörfler 	fSettings.map.button[0] = B_PRIMARY_MOUSE_BUTTON;
16761604e48SAxel Dörfler 	fSettings.map.button[1] = B_SECONDARY_MOUSE_BUTTON;
16861604e48SAxel Dörfler 	fSettings.map.button[2] = B_TERTIARY_MOUSE_BUTTON;
1693aa69c78SStefano Ceccherini #endif
17061604e48SAxel Dörfler };
17161d7deeeSJérôme Duval 
17261d7deeeSJérôme Duval 
17361604e48SAxel Dörfler MouseDevice::~MouseDevice()
17461d7deeeSJérôme Duval {
175e01a0431SStephan Aßmus 	MD_CALLED();
176e01a0431SStephan Aßmus 	TRACE("delete\n");
177e01a0431SStephan Aßmus 
17861604e48SAxel Dörfler 	if (fActive)
17961604e48SAxel Dörfler 		Stop();
18061d7deeeSJérôme Duval 
18161604e48SAxel Dörfler 	free(fDeviceRef.name);
18261d7deeeSJérôme Duval }
18361d7deeeSJérôme Duval 
18461d7deeeSJérôme Duval 
18561d7deeeSJérôme Duval status_t
18661604e48SAxel Dörfler MouseDevice::Start()
18753d77642SJérôme Duval {
188e01a0431SStephan Aßmus 	MD_CALLED();
189e01a0431SStephan Aßmus 
19061604e48SAxel Dörfler 	fDevice = open(fPath.String(), O_RDWR);
1915125eae2SStephan Aßmus 		// let the control thread handle any error on opening the device
19233efb919SStefano Ceccherini 
19333efb919SStefano Ceccherini 	char threadName[B_OS_NAME_LENGTH];
19461604e48SAxel Dörfler 	snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name);
19533efb919SStefano Ceccherini 
1965125eae2SStephan Aßmus 	fThread = spawn_thread(_ControlThreadEntry, threadName,
19761604e48SAxel Dörfler 		kMouseThreadPriority, (void*)this);
1984c0af4a8SStefano Ceccherini 
19933efb919SStefano Ceccherini 	status_t status;
20061604e48SAxel Dörfler 	if (fThread < B_OK)
20161604e48SAxel Dörfler 		status = fThread;
20261604e48SAxel Dörfler 	else {
20361604e48SAxel Dörfler 		fActive = true;
20461604e48SAxel Dörfler 		status = resume_thread(fThread);
20561604e48SAxel Dörfler 	}
20633efb919SStefano Ceccherini 
20761604e48SAxel Dörfler 	if (status < B_OK) {
20861604e48SAxel Dörfler 		LOG_ERR("%s: can't spawn/resume watching thread: %s\n",
20961604e48SAxel Dörfler 			fDeviceRef.name, strerror(status));
21061604e48SAxel Dörfler 		return status;
21161604e48SAxel Dörfler 	}
21261604e48SAxel Dörfler 
213c2fbfb71SJérôme Duval 	return B_OK;
21461d7deeeSJérôme Duval }
21561d7deeeSJérôme Duval 
21661d7deeeSJérôme Duval 
217c2fbfb71SJérôme Duval void
21861604e48SAxel Dörfler MouseDevice::Stop()
21961604e48SAxel Dörfler {
220e01a0431SStephan Aßmus 	MD_CALLED();
2211ccc2f2dSJérôme Duval 
22261604e48SAxel Dörfler 	fActive = false;
22361604e48SAxel Dörfler 		// this will stop the thread as soon as it reads the next packet
22461604e48SAxel Dörfler 
225e01a0431SStephan Aßmus 	close(fDevice);
226e01a0431SStephan Aßmus 	fDevice = -1;
227e01a0431SStephan Aßmus 
22861604e48SAxel Dörfler 	if (fThread >= B_OK) {
22961604e48SAxel Dörfler 		// unblock the thread, which might wait on a semaphore.
23061604e48SAxel Dörfler 		suspend_thread(fThread);
23161604e48SAxel Dörfler 		resume_thread(fThread);
23261604e48SAxel Dörfler 
23361604e48SAxel Dörfler 		status_t dummy;
23461604e48SAxel Dörfler 		wait_for_thread(fThread, &dummy);
23561604e48SAxel Dörfler 	}
23661604e48SAxel Dörfler }
23761604e48SAxel Dörfler 
23861604e48SAxel Dörfler 
23942b505fbSAxel Dörfler status_t
24042b505fbSAxel Dörfler MouseDevice::UpdateSettings()
24142b505fbSAxel Dörfler {
242e01a0431SStephan Aßmus 	MD_CALLED();
24342b505fbSAxel Dörfler 
2445125eae2SStephan Aßmus 	if (fThread < 0)
2455125eae2SStephan Aßmus 		return B_ERROR;
24642b505fbSAxel Dörfler 
2475125eae2SStephan Aßmus 	fUpdateSettings = true;
2485125eae2SStephan Aßmus 		// This will provoke the control thread to update the settings.
24942b505fbSAxel Dörfler 
25042b505fbSAxel Dörfler 	return B_OK;
2515125eae2SStephan Aßmus }
25242b505fbSAxel Dörfler 
2535125eae2SStephan Aßmus 
2545125eae2SStephan Aßmus char*
2555125eae2SStephan Aßmus MouseDevice::_BuildShortName() const
2565125eae2SStephan Aßmus {
2575125eae2SStephan Aßmus 	BString string(fPath);
2585125eae2SStephan Aßmus 	BString name;
2595125eae2SStephan Aßmus 
2605125eae2SStephan Aßmus 	int32 slash = string.FindLast("/");
2615125eae2SStephan Aßmus 	string.CopyInto(name, slash + 1, string.Length() - slash);
2625125eae2SStephan Aßmus 	int32 index = atoi(name.String()) + 1;
2635125eae2SStephan Aßmus 
2645125eae2SStephan Aßmus 	int32 previousSlash = string.FindLast("/", slash);
2655125eae2SStephan Aßmus 	string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1);
2665125eae2SStephan Aßmus 
2675125eae2SStephan Aßmus 	if (name == "ps2")
2685125eae2SStephan Aßmus 		name = "PS/2";
2695125eae2SStephan Aßmus 	else
2705125eae2SStephan Aßmus 		name.Capitalize();
2715125eae2SStephan Aßmus 
272*cc442411SStephan Aßmus 	if (string.FindFirst("intelli") >= 0)
273*cc442411SStephan Aßmus 		name.Prepend("Extended ");
274*cc442411SStephan Aßmus 
2755125eae2SStephan Aßmus 	name << " Mouse " << index;
2765125eae2SStephan Aßmus 
2775125eae2SStephan Aßmus 	return strdup(name.String());
2785125eae2SStephan Aßmus }
2795125eae2SStephan Aßmus 
2805125eae2SStephan Aßmus 
2815125eae2SStephan Aßmus // #pragma mark - control thread
2825125eae2SStephan Aßmus 
2835125eae2SStephan Aßmus 
2845125eae2SStephan Aßmus status_t
2855125eae2SStephan Aßmus MouseDevice::_ControlThreadEntry(void* arg)
2865125eae2SStephan Aßmus {
2875125eae2SStephan Aßmus 	MouseDevice* device = (MouseDevice*)arg;
2885125eae2SStephan Aßmus 	device->_ControlThread();
2895125eae2SStephan Aßmus 	return B_OK;
29042b505fbSAxel Dörfler }
29142b505fbSAxel Dörfler 
29242b505fbSAxel Dörfler 
29361604e48SAxel Dörfler void
2945125eae2SStephan Aßmus MouseDevice::_ControlThread()
29561604e48SAxel Dörfler {
2965125eae2SStephan Aßmus 	if (fDevice < 0) {
2975125eae2SStephan Aßmus 		_ControlThreadCleanup();
2985125eae2SStephan Aßmus 		// TOAST!
2995125eae2SStephan Aßmus 		return;
3005125eae2SStephan Aßmus 	}
3015125eae2SStephan Aßmus 
3025125eae2SStephan Aßmus 	_UpdateSettings();
3035125eae2SStephan Aßmus 
30461604e48SAxel Dörfler 	uint32 lastButtons = 0;
30561604e48SAxel Dörfler 
30661604e48SAxel Dörfler 	while (fActive) {
30761604e48SAxel Dörfler 		mouse_movement movements;
30861604e48SAxel Dörfler 		memset(&movements, 0, sizeof(movements));
30961604e48SAxel Dörfler 
310c5555ad3SStephan Aßmus 		if (ioctl(fDevice, MS_READ, &movements) != B_OK) {
3115125eae2SStephan Aßmus 			_ControlThreadCleanup();
312c5555ad3SStephan Aßmus 			// TOAST!
31361604e48SAxel Dörfler 			return;
314c5555ad3SStephan Aßmus 		}
31561604e48SAxel Dörfler 
3165125eae2SStephan Aßmus 		// take care of updating the settings first, if necessary
3175125eae2SStephan Aßmus 		if (fUpdateSettings) {
3185125eae2SStephan Aßmus 			_UpdateSettings();
3195125eae2SStephan Aßmus 			fUpdateSettings = false;
3205125eae2SStephan Aßmus 		}
3215125eae2SStephan Aßmus 
32261604e48SAxel Dörfler 		uint32 buttons = lastButtons ^ movements.buttons;
32361604e48SAxel Dörfler 
32461604e48SAxel Dörfler 		uint32 remappedButtons = _RemapButtons(movements.buttons);
32561604e48SAxel Dörfler 		int32 deltaX, deltaY;
32661604e48SAxel Dörfler 		_ComputeAcceleration(movements, deltaX, deltaY);
32761604e48SAxel Dörfler 
328e01a0431SStephan Aßmus 		LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, wheel_y:%ld\n",
3291ccc2f2dSJérôme Duval 			fDeviceRef.name, movements.buttons, movements.xdelta, movements.ydelta,
33061604e48SAxel Dörfler 			movements.clicks, movements.wheel_xdelta, movements.wheel_ydelta);
331e01a0431SStephan Aßmus 		LOG_EVENT("%s: x: %ld, y: %ld\n", fDeviceRef.name, deltaX, deltaY);
33261604e48SAxel Dörfler 
33361604e48SAxel Dörfler 		// Send single messages for each event
33461604e48SAxel Dörfler 
33561604e48SAxel Dörfler 		if (buttons != 0) {
33661604e48SAxel Dörfler 			bool pressedButton = (buttons & movements.buttons) > 0;
33761604e48SAxel Dörfler 			BMessage* message = _BuildMouseMessage(
33861604e48SAxel Dörfler 				pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP,
33961604e48SAxel Dörfler 				movements.timestamp, remappedButtons, deltaX, deltaY);
34061604e48SAxel Dörfler 			if (message != NULL) {
34161604e48SAxel Dörfler 				if (pressedButton) {
34261604e48SAxel Dörfler 					message->AddInt32("clicks", movements.clicks);
343e01a0431SStephan Aßmus 					LOG_EVENT("B_MOUSE_DOWN\n");
34461604e48SAxel Dörfler 				} else
345e01a0431SStephan Aßmus 					LOG_EVENT("B_MOUSE_UP\n");
34661604e48SAxel Dörfler 
34761604e48SAxel Dörfler 				fTarget.EnqueueMessage(message);
34861604e48SAxel Dörfler 				lastButtons = movements.buttons;
34961604e48SAxel Dörfler 			}
35061604e48SAxel Dörfler 		}
35161604e48SAxel Dörfler 
35245f11ce8SStephan Aßmus 		if (movements.xdelta != 0 || movements.ydelta != 0) {
35345f11ce8SStephan Aßmus 			BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED,
35445f11ce8SStephan Aßmus 				movements.timestamp, remappedButtons, deltaX, deltaY);
35545f11ce8SStephan Aßmus 			if (message != NULL)
35645f11ce8SStephan Aßmus 				fTarget.EnqueueMessage(message);
35745f11ce8SStephan Aßmus 		}
35845f11ce8SStephan Aßmus 
35961604e48SAxel Dörfler 		if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
360c5555ad3SStephan Aßmus 			BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED);
36161604e48SAxel Dörfler 			if (message == NULL)
36261604e48SAxel Dörfler 				continue;
36361604e48SAxel Dörfler 
36461604e48SAxel Dörfler 			if (message->AddInt64("when", movements.timestamp) == B_OK
36561604e48SAxel Dörfler 				&& message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta) == B_OK
36661604e48SAxel Dörfler 				&& message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta) == B_OK)
36761604e48SAxel Dörfler 				fTarget.EnqueueMessage(message);
36861604e48SAxel Dörfler 			else
36961604e48SAxel Dörfler 				delete message;
37061604e48SAxel Dörfler 		}
37161604e48SAxel Dörfler 	}
37261604e48SAxel Dörfler }
37361604e48SAxel Dörfler 
37461604e48SAxel Dörfler 
3755125eae2SStephan Aßmus void
3765125eae2SStephan Aßmus MouseDevice::_ControlThreadCleanup()
37761604e48SAxel Dörfler {
3785125eae2SStephan Aßmus 	// NOTE: Only executed when the control thread detected an error
3795125eae2SStephan Aßmus 	// and from within the control thread!
3805125eae2SStephan Aßmus 
3815125eae2SStephan Aßmus 	if (fActive) {
3825125eae2SStephan Aßmus 		fThread = -1;
3835125eae2SStephan Aßmus 		fTarget._RemoveDevice(fPath.String());
3845125eae2SStephan Aßmus 	} else {
3855125eae2SStephan Aßmus 		// In case active is already false, another thread
3865125eae2SStephan Aßmus 		// waits for this thread to quit, and may already hold
3875125eae2SStephan Aßmus 		// locks that _RemoveDevice() wants to acquire. In another
3885125eae2SStephan Aßmus 		// words, the device is already being removed, so we simply
3895125eae2SStephan Aßmus 		// quit here.
3905125eae2SStephan Aßmus 	}
3915125eae2SStephan Aßmus }
3925125eae2SStephan Aßmus 
3935125eae2SStephan Aßmus 
3945125eae2SStephan Aßmus void
3955125eae2SStephan Aßmus MouseDevice::_UpdateSettings()
3965125eae2SStephan Aßmus {
3975125eae2SStephan Aßmus 	MD_CALLED();
3985125eae2SStephan Aßmus 
3995125eae2SStephan Aßmus 	// retrieve current values
4005125eae2SStephan Aßmus 
4015125eae2SStephan Aßmus 	if (get_mouse_map(&fSettings.map) != B_OK)
4025125eae2SStephan Aßmus 		LOG_ERR("error when get_mouse_map\n");
4035125eae2SStephan Aßmus 	else {
4045125eae2SStephan Aßmus 		fDeviceRemapsButtons
4055125eae2SStephan Aßmus 			= ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK;
4065125eae2SStephan Aßmus 	}
4075125eae2SStephan Aßmus 
4085125eae2SStephan Aßmus 	if (get_click_speed(&fSettings.click_speed) != B_OK)
4095125eae2SStephan Aßmus 		LOG_ERR("error when get_click_speed\n");
4105125eae2SStephan Aßmus 	else
4115125eae2SStephan Aßmus 		ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed);
4125125eae2SStephan Aßmus 
4135125eae2SStephan Aßmus 	if (get_mouse_speed(&fSettings.accel.speed) != B_OK)
4145125eae2SStephan Aßmus 		LOG_ERR("error when get_mouse_speed\n");
4155125eae2SStephan Aßmus 	else {
4165125eae2SStephan Aßmus 		if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK)
4175125eae2SStephan Aßmus 			LOG_ERR("error when get_mouse_acceleration\n");
4185125eae2SStephan Aßmus 		else {
4195125eae2SStephan Aßmus 			mouse_accel accel;
4205125eae2SStephan Aßmus 			ioctl(fDevice, MS_GET_ACCEL, &accel);
4215125eae2SStephan Aßmus 			accel.speed = fSettings.accel.speed;
4225125eae2SStephan Aßmus 			accel.accel_factor = fSettings.accel.accel_factor;
4235125eae2SStephan Aßmus 			ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel);
4245125eae2SStephan Aßmus 		}
4255125eae2SStephan Aßmus 	}
4265125eae2SStephan Aßmus 
4275125eae2SStephan Aßmus 	if (get_mouse_type(&fSettings.type) != B_OK)
4285125eae2SStephan Aßmus 		LOG_ERR("error when get_mouse_type\n");
4295125eae2SStephan Aßmus 	else
4305125eae2SStephan Aßmus 		ioctl(fDevice, MS_SET_TYPE, &fSettings.type);
43161604e48SAxel Dörfler }
43261604e48SAxel Dörfler 
43361604e48SAxel Dörfler 
43461604e48SAxel Dörfler BMessage*
43561604e48SAxel Dörfler MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons,
43661604e48SAxel Dörfler 	int32 deltaX, int32 deltaY) const
43761604e48SAxel Dörfler {
43861604e48SAxel Dörfler 	BMessage* message = new BMessage(what);
43961604e48SAxel Dörfler 	if (message == NULL)
44061604e48SAxel Dörfler 		return NULL;
44161604e48SAxel Dörfler 
44261604e48SAxel Dörfler 	if (message->AddInt64("when", when) < B_OK
44361604e48SAxel Dörfler 		|| message->AddInt32("buttons", buttons) < B_OK
44461604e48SAxel Dörfler 		|| message->AddInt32("x", deltaX) < B_OK
44561604e48SAxel Dörfler 		|| message->AddInt32("y", deltaY) < B_OK) {
44661604e48SAxel Dörfler 		delete message;
44761604e48SAxel Dörfler 		return NULL;
44861604e48SAxel Dörfler 	}
44961604e48SAxel Dörfler 
45061604e48SAxel Dörfler 	return message;
45161604e48SAxel Dörfler }
45261604e48SAxel Dörfler 
45361604e48SAxel Dörfler 
45461604e48SAxel Dörfler void
45561604e48SAxel Dörfler MouseDevice::_ComputeAcceleration(const mouse_movement& movements,
45661604e48SAxel Dörfler 	int32& deltaX, int32& deltaY) const
45761604e48SAxel Dörfler {
45842b505fbSAxel Dörfler 	// basic mouse speed
45942b505fbSAxel Dörfler 	deltaX = movements.xdelta * fSettings.accel.speed >> 16;
46042b505fbSAxel Dörfler 	deltaY = movements.ydelta * fSettings.accel.speed >> 16;
46142b505fbSAxel Dörfler 
46242b505fbSAxel Dörfler 	// acceleration
46342b505fbSAxel Dörfler 	double acceleration = 1;
46442b505fbSAxel Dörfler 	if (fSettings.accel.accel_factor) {
465fafab827SAxel Dörfler 		acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY)
46674ef1621SAxel Dörfler 			* fSettings.accel.accel_factor / 524288.0;
46742b505fbSAxel Dörfler 	}
46842b505fbSAxel Dörfler 
46942b505fbSAxel Dörfler 	// make sure that we move at least one pixel (if there was a movement)
47042b505fbSAxel Dörfler 	if (deltaX > 0)
47142b505fbSAxel Dörfler 		deltaX = (int32)floor(deltaX * acceleration);
472fafab827SAxel Dörfler 	else
473fafab827SAxel Dörfler 		deltaX = (int32)ceil(deltaX * acceleration);
47442b505fbSAxel Dörfler 
47542b505fbSAxel Dörfler 	if (deltaY > 0)
47642b505fbSAxel Dörfler 		deltaY = (int32)floor(deltaY * acceleration);
477fafab827SAxel Dörfler 	else
478fafab827SAxel Dörfler 		deltaY = (int32)ceil(deltaY * acceleration);
47961604e48SAxel Dörfler }
48061604e48SAxel Dörfler 
48133efb919SStefano Ceccherini 
48223b47e0bSJérôme Duval uint32
48361604e48SAxel Dörfler MouseDevice::_RemapButtons(uint32 buttons) const
48423b47e0bSJérôme Duval {
48561604e48SAxel Dörfler 	if (fDeviceRemapsButtons)
48623b47e0bSJérôme Duval 		return buttons;
48723b47e0bSJérôme Duval 
48861604e48SAxel Dörfler 	uint32 newButtons = 0;
48923b47e0bSJérôme Duval 	for (int32 i = 0; buttons; i++) {
49023b47e0bSJérôme Duval 		if (buttons & 0x1) {
49121631085SStephan Aßmus #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO)
49261604e48SAxel Dörfler 			newButtons |= fSettings.map.button[i];
49323b47e0bSJérôme Duval #else
49423b47e0bSJérôme Duval 			if (i == 0)
49561604e48SAxel Dörfler 				newButtons |= fSettings.map.left;
49623b47e0bSJérôme Duval 			if (i == 1)
49761604e48SAxel Dörfler 				newButtons |= fSettings.map.right;
49823b47e0bSJérôme Duval 			if (i == 2)
49961604e48SAxel Dörfler 				newButtons |= fSettings.map.middle;
50023b47e0bSJérôme Duval #endif
50123b47e0bSJérôme Duval 		}
50223b47e0bSJérôme Duval 		buttons >>= 1;
50323b47e0bSJérôme Duval 	}
50423b47e0bSJérôme Duval 
50561604e48SAxel Dörfler 	return newButtons;
50623b47e0bSJérôme Duval }
50723b47e0bSJérôme Duval 
50823b47e0bSJérôme Duval 
50961604e48SAxel Dörfler //	#pragma mark -
51061604e48SAxel Dörfler 
51161604e48SAxel Dörfler 
51261604e48SAxel Dörfler MouseInputDevice::MouseInputDevice()
513b6a7b204SAxel Dörfler 	:
5145125eae2SStephan Aßmus 	fDevices(2, true),
5155125eae2SStephan Aßmus 	fDeviceListLock("MouseInputDevice list")
51661604e48SAxel Dörfler {
517e01a0431SStephan Aßmus 	MID_CALLED();
51861604e48SAxel Dörfler 
519c5555ad3SStephan Aßmus 	StartMonitoringDevice(kMouseDevicesDirectory);
5209dd2d40eSStephan Aßmus 	_RecursiveScan(kMouseDevicesDirectory);
52161604e48SAxel Dörfler }
52261604e48SAxel Dörfler 
52361604e48SAxel Dörfler 
52461604e48SAxel Dörfler MouseInputDevice::~MouseInputDevice()
52561604e48SAxel Dörfler {
526e01a0431SStephan Aßmus 	MID_CALLED();
527e01a0431SStephan Aßmus 
528b6a7b204SAxel Dörfler 	StopMonitoringDevice(kMouseDevicesDirectory);
529e01a0431SStephan Aßmus 	fDevices.MakeEmpty();
53061604e48SAxel Dörfler }
53161604e48SAxel Dörfler 
53261604e48SAxel Dörfler 
53361604e48SAxel Dörfler status_t
53461604e48SAxel Dörfler MouseInputDevice::InitCheck()
53561604e48SAxel Dörfler {
536e01a0431SStephan Aßmus 	MID_CALLED();
537e01a0431SStephan Aßmus 
538d246d9beSStefano Ceccherini 	return BInputServerDevice::InitCheck();
53961604e48SAxel Dörfler }
54061604e48SAxel Dörfler 
54161604e48SAxel Dörfler 
54261604e48SAxel Dörfler status_t
54361604e48SAxel Dörfler MouseInputDevice::Start(const char* name, void* cookie)
54461604e48SAxel Dörfler {
545e01a0431SStephan Aßmus 	MID_CALLED();
546e01a0431SStephan Aßmus 
54761604e48SAxel Dörfler 	MouseDevice* device = (MouseDevice*)cookie;
54861604e48SAxel Dörfler 
54961604e48SAxel Dörfler 	return device->Start();
55061604e48SAxel Dörfler }
55161604e48SAxel Dörfler 
55261604e48SAxel Dörfler 
55361604e48SAxel Dörfler status_t
55461604e48SAxel Dörfler MouseInputDevice::Stop(const char* name, void* cookie)
55561604e48SAxel Dörfler {
556e01a0431SStephan Aßmus 	TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name);
55761604e48SAxel Dörfler 
558e01a0431SStephan Aßmus 	MouseDevice* device = (MouseDevice*)cookie;
55961604e48SAxel Dörfler 	device->Stop();
560e01a0431SStephan Aßmus 
56161604e48SAxel Dörfler 	return B_OK;
56261604e48SAxel Dörfler }
56361604e48SAxel Dörfler 
56461604e48SAxel Dörfler 
56561604e48SAxel Dörfler status_t
56661604e48SAxel Dörfler MouseInputDevice::Control(const char* name, void* cookie,
56761604e48SAxel Dörfler 	uint32 command, BMessage* message)
56861604e48SAxel Dörfler {
569e01a0431SStephan Aßmus 	TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
570e01a0431SStephan Aßmus 
57161604e48SAxel Dörfler 	MouseDevice* device = (MouseDevice*)cookie;
57261604e48SAxel Dörfler 
57361604e48SAxel Dörfler 	if (command == B_NODE_MONITOR)
57461604e48SAxel Dörfler 		return _HandleMonitor(message);
57561604e48SAxel Dörfler 
57661604e48SAxel Dörfler 	if (command >= B_MOUSE_TYPE_CHANGED
57761604e48SAxel Dörfler 		&& command <= B_MOUSE_ACCELERATION_CHANGED)
57861604e48SAxel Dörfler 		return device->UpdateSettings();
57961604e48SAxel Dörfler 
58061604e48SAxel Dörfler 	return B_BAD_VALUE;
58161604e48SAxel Dörfler }
58261604e48SAxel Dörfler 
58361604e48SAxel Dörfler 
58461604e48SAxel Dörfler status_t
58561604e48SAxel Dörfler MouseInputDevice::_HandleMonitor(BMessage* message)
58661604e48SAxel Dörfler {
587e01a0431SStephan Aßmus 	MID_CALLED();
58861604e48SAxel Dörfler 
589b6a7b204SAxel Dörfler 	const char* path;
59061604e48SAxel Dörfler 	int32 opcode;
591b6a7b204SAxel Dörfler 	if (message->FindInt32("opcode", &opcode) != B_OK
592c5555ad3SStephan Aßmus 		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
593b6a7b204SAxel Dörfler 		|| message->FindString("path", &path) != B_OK)
59461604e48SAxel Dörfler 		return B_BAD_VALUE;
59561604e48SAxel Dörfler 
59661604e48SAxel Dörfler 	if (opcode == B_ENTRY_CREATED)
597b6a7b204SAxel Dörfler 		return _AddDevice(path);
59861604e48SAxel Dörfler 
599c5555ad3SStephan Aßmus #if 0
600b6a7b204SAxel Dörfler 	return _RemoveDevice(path);
601c5555ad3SStephan Aßmus #else
602c5555ad3SStephan Aßmus 	// Don't handle B_ENTRY_REMOVED, let the control thread take care of it.
603c5555ad3SStephan Aßmus 	return B_OK;
604c5555ad3SStephan Aßmus #endif
60561604e48SAxel Dörfler }
60661604e48SAxel Dörfler 
60761604e48SAxel Dörfler 
60861604e48SAxel Dörfler void
60961604e48SAxel Dörfler MouseInputDevice::_RecursiveScan(const char* directory)
61061604e48SAxel Dörfler {
611e01a0431SStephan Aßmus 	MID_CALLED();
61261604e48SAxel Dörfler 
61361604e48SAxel Dörfler 	BEntry entry;
61461604e48SAxel Dörfler 	BDirectory dir(directory);
61561604e48SAxel Dörfler 	while (dir.GetNextEntry(&entry) == B_OK) {
61661604e48SAxel Dörfler 		BPath path;
61761604e48SAxel Dörfler 		entry.GetPath(&path);
61861604e48SAxel Dörfler 
61961604e48SAxel Dörfler 		if (!strcmp(path.Leaf(), "serial")) {
62061604e48SAxel Dörfler 			// skip serial
62161604e48SAxel Dörfler 			continue;
62261604e48SAxel Dörfler 		}
62361604e48SAxel Dörfler 
62461604e48SAxel Dörfler 		if (entry.IsDirectory())
62561604e48SAxel Dörfler 			_RecursiveScan(path.Path());
62661604e48SAxel Dörfler 		else
62761604e48SAxel Dörfler 			_AddDevice(path.Path());
62861604e48SAxel Dörfler 	}
62961604e48SAxel Dörfler }
63061604e48SAxel Dörfler 
631e01a0431SStephan Aßmus 
632e01a0431SStephan Aßmus MouseDevice*
6335125eae2SStephan Aßmus MouseInputDevice::_FindDevice(const char* path) const
634e01a0431SStephan Aßmus {
635e01a0431SStephan Aßmus 	MID_CALLED();
636e01a0431SStephan Aßmus 
637e01a0431SStephan Aßmus 	for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) {
638e01a0431SStephan Aßmus 		MouseDevice* device = fDevices.ItemAt(i);
639e01a0431SStephan Aßmus 		if (strcmp(device->Path(), path) == 0)
640e01a0431SStephan Aßmus 			return device;
641e01a0431SStephan Aßmus 	}
642e01a0431SStephan Aßmus 
643e01a0431SStephan Aßmus 	return NULL;
644e01a0431SStephan Aßmus }
645e01a0431SStephan Aßmus 
646e01a0431SStephan Aßmus 
647e01a0431SStephan Aßmus status_t
648e01a0431SStephan Aßmus MouseInputDevice::_AddDevice(const char* path)
649e01a0431SStephan Aßmus {
650e01a0431SStephan Aßmus 	MID_CALLED();
651e01a0431SStephan Aßmus 
6525125eae2SStephan Aßmus 	BAutolock _(fDeviceListLock);
6535125eae2SStephan Aßmus 
654e01a0431SStephan Aßmus 	_RemoveDevice(path);
655e01a0431SStephan Aßmus 
656e01a0431SStephan Aßmus 	MouseDevice* device = new(std::nothrow) MouseDevice(*this, path);
657e01a0431SStephan Aßmus 	if (!device) {
658e01a0431SStephan Aßmus 		TRACE("No memory\n");
659e01a0431SStephan Aßmus 		return B_NO_MEMORY;
660e01a0431SStephan Aßmus 	}
661e01a0431SStephan Aßmus 
662e01a0431SStephan Aßmus 	if (!fDevices.AddItem(device)) {
663e01a0431SStephan Aßmus 		TRACE("No memory in list\n");
664e01a0431SStephan Aßmus 		delete device;
665e01a0431SStephan Aßmus 		return B_NO_MEMORY;
666e01a0431SStephan Aßmus 	}
667e01a0431SStephan Aßmus 
668e01a0431SStephan Aßmus 	input_device_ref* devices[2];
669e01a0431SStephan Aßmus 	devices[0] = device->DeviceRef();
670e01a0431SStephan Aßmus 	devices[1] = NULL;
671e01a0431SStephan Aßmus 
672e01a0431SStephan Aßmus 	TRACE("adding path: %s, name: %s\n", path, devices[0]->name);
673e01a0431SStephan Aßmus 
674e01a0431SStephan Aßmus 	return RegisterDevices(devices);
675e01a0431SStephan Aßmus }
676e01a0431SStephan Aßmus 
677e01a0431SStephan Aßmus 
678e01a0431SStephan Aßmus status_t
679e01a0431SStephan Aßmus MouseInputDevice::_RemoveDevice(const char* path)
680e01a0431SStephan Aßmus {
681e01a0431SStephan Aßmus 	MID_CALLED();
682e01a0431SStephan Aßmus 
6835125eae2SStephan Aßmus 	BAutolock _(fDeviceListLock);
6845125eae2SStephan Aßmus 
685e01a0431SStephan Aßmus 	MouseDevice* device = _FindDevice(path);
686e01a0431SStephan Aßmus 	if (device == NULL) {
687e01a0431SStephan Aßmus 		TRACE("%s not found\n", path);
688e01a0431SStephan Aßmus 		return B_ENTRY_NOT_FOUND;
689e01a0431SStephan Aßmus 	}
690e01a0431SStephan Aßmus 
691e01a0431SStephan Aßmus 	input_device_ref* devices[2];
692e01a0431SStephan Aßmus 	devices[0] = device->DeviceRef();
693e01a0431SStephan Aßmus 	devices[1] = NULL;
694e01a0431SStephan Aßmus 
695e01a0431SStephan Aßmus 	TRACE("removing path: %s, name: %s\n", path, devices[0]->name);
696e01a0431SStephan Aßmus 
697e01a0431SStephan Aßmus 	UnregisterDevices(devices);
698e01a0431SStephan Aßmus 
699e01a0431SStephan Aßmus 	fDevices.RemoveItem(device);
700e01a0431SStephan Aßmus 
701e01a0431SStephan Aßmus 	return B_OK;
702e01a0431SStephan Aßmus }
703e01a0431SStephan Aßmus 
704e01a0431SStephan Aßmus 
705e01a0431SStephan Aßmus 
706