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