1 /* 2 * Copyright 2004-2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini 7 */ 8 9 10 #include "MouseInputDevice.h" 11 #include "kb_mouse_settings.h" 12 #include "kb_mouse_driver.h" 13 14 #include <Debug.h> 15 #include <Directory.h> 16 #include <Entry.h> 17 #include <NodeMonitor.h> 18 #include <Path.h> 19 #include <String.h> 20 #include <View.h> 21 22 #include <errno.h> 23 #include <new> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 28 #if DEBUG 29 FILE *MouseInputDevice::sLogFile = NULL; 30 # define LOG_ERR(text...) LOG(text) 31 #else 32 # define LOG(text...) 33 # define LOG_ERR(text...) fprintf(stderr, text) 34 #endif 35 36 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__) 37 38 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 39 const static char *kMouseDevicesDirectory = "/dev/input/mouse"; 40 41 // "/dev/" is automatically prepended by StartMonitoringDevice() 42 const static char *kMouseDevicesDirectoryPS2 = "input/mouse/ps2"; 43 const static char *kMouseDevicesDirectoryUSB = "input/mouse/usb"; 44 45 class MouseDevice { 46 public: 47 MouseDevice(BInputServerDevice& target, const char* path); 48 ~MouseDevice(); 49 50 status_t Start(); 51 void Stop(); 52 53 status_t UpdateSettings(); 54 55 const char* Path() const { return fPath.String(); } 56 input_device_ref* DeviceRef() { return &fDeviceRef; } 57 58 private: 59 void _Run(); 60 static status_t _ThreadFunction(void *arg); 61 62 BMessage* _BuildMouseMessage(uint32 what, uint64 when, uint32 buttons, 63 int32 deltaX, int32 deltaY) const; 64 void _ComputeAcceleration(const mouse_movement& movements, 65 int32& deltaX, int32& deltaY) const; 66 uint32 _RemapButtons(uint32 buttons) const; 67 68 char* _BuildShortName() const; 69 70 private: 71 BInputServerDevice& fTarget; 72 BString fPath; 73 int fDevice; 74 75 input_device_ref fDeviceRef; 76 mouse_settings fSettings; 77 bool fDeviceRemapsButtons; 78 79 thread_id fThread; 80 volatile bool fActive; 81 }; 82 83 84 #if DEBUG 85 inline void 86 LOG(const char *fmt, ...) 87 { 88 char buf[1024]; 89 va_list ap; 90 va_start(ap, fmt); 91 vsprintf(buf, fmt, ap); va_end(ap); 92 fputs(buf, MouseInputDevice::sLogFile); fflush(MouseInputDevice::sLogFile); 93 } 94 #endif 95 96 97 extern "C" BInputServerDevice * 98 instantiate_input_device() 99 { 100 return new MouseInputDevice(); 101 } 102 103 104 // #pragma mark - 105 106 107 MouseDevice::MouseDevice(BInputServerDevice& target, const char *driverPath) 108 : 109 fTarget(target), 110 fDevice(-1), 111 fThread(-1), 112 fActive(false) 113 { 114 fPath = driverPath; 115 116 fDeviceRef.name = _BuildShortName(); 117 fDeviceRef.type = B_POINTING_DEVICE; 118 fDeviceRef.cookie = this; 119 120 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 121 fSettings.map.button[0] = B_PRIMARY_MOUSE_BUTTON; 122 fSettings.map.button[1] = B_SECONDARY_MOUSE_BUTTON; 123 fSettings.map.button[2] = B_TERTIARY_MOUSE_BUTTON; 124 #endif 125 126 fDeviceRemapsButtons = false; 127 }; 128 129 130 MouseDevice::~MouseDevice() 131 { 132 if (fActive) 133 Stop(); 134 135 free(fDeviceRef.name); 136 } 137 138 139 status_t 140 MouseDevice::Start() 141 { 142 fDevice = open(fPath.String(), O_RDWR); 143 if (fDevice < 0) 144 return errno; 145 146 UpdateSettings(); 147 148 char threadName[B_OS_NAME_LENGTH]; 149 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name); 150 151 fThread = spawn_thread(_ThreadFunction, threadName, 152 kMouseThreadPriority, (void *)this); 153 154 status_t status; 155 if (fThread < B_OK) 156 status = fThread; 157 else { 158 fActive = true; 159 status = resume_thread(fThread); 160 } 161 162 if (status < B_OK) { 163 LOG_ERR("%s: can't spawn/resume watching thread: %s\n", 164 fDeviceRef.name, strerror(status)); 165 close(fDevice); 166 return status; 167 } 168 169 return B_OK; 170 } 171 172 173 void 174 MouseDevice::Stop() 175 { 176 fActive = false; 177 // this will stop the thread as soon as it reads the next packet 178 179 if (fThread >= B_OK) { 180 // unblock the thread, which might wait on a semaphore. 181 suspend_thread(fThread); 182 resume_thread(fThread); 183 184 status_t dummy; 185 wait_for_thread(fThread, &dummy); 186 } 187 188 close(fDevice); 189 } 190 191 192 status_t 193 MouseDevice::UpdateSettings() 194 { 195 CALLED(); 196 197 // retrieve current values 198 199 if (get_mouse_map(&fSettings.map) != B_OK) 200 LOG_ERR("error when get_mouse_map\n"); 201 else 202 fDeviceRemapsButtons = ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK; 203 204 if (get_click_speed(&fSettings.click_speed) != B_OK) 205 LOG_ERR("error when get_click_speed\n"); 206 else 207 ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed); 208 209 if (get_mouse_speed(&fSettings.accel.speed) != B_OK) 210 LOG_ERR("error when get_mouse_speed\n"); 211 else { 212 if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK) 213 LOG_ERR("error when get_mouse_acceleration\n"); 214 else { 215 mouse_accel accel; 216 ioctl(fDevice, MS_GET_ACCEL, &accel); 217 accel.speed = fSettings.accel.speed; 218 accel.accel_factor = fSettings.accel.accel_factor; 219 ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel); 220 } 221 } 222 223 if (get_mouse_type(&fSettings.type) != B_OK) 224 LOG_ERR("error when get_mouse_type\n"); 225 else 226 ioctl(fDevice, MS_SET_TYPE, &fSettings.type); 227 228 return B_OK; 229 230 } 231 232 233 void 234 MouseDevice::_Run() 235 { 236 uint32 lastButtons = 0; 237 238 while (fActive) { 239 mouse_movement movements; 240 memset(&movements, 0, sizeof(movements)); 241 242 if (ioctl(fDevice, MS_READ, &movements) != B_OK) 243 return; 244 245 uint32 buttons = lastButtons ^ movements.buttons; 246 247 uint32 remappedButtons = _RemapButtons(movements.buttons); 248 int32 deltaX, deltaY; 249 _ComputeAcceleration(movements, deltaX, deltaY); 250 251 LOG("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, wheel_y:%ld\n", 252 device->device_ref.name, movements.buttons, movements.xdelta, movements.ydelta, 253 movements.clicks, movements.wheel_xdelta, movements.wheel_ydelta); 254 LOG("%s: x: %ld, y: %ld\n", device->device_ref.name, deltaX, deltaY); 255 256 BMessage *message = NULL; 257 258 // Send single messages for each event 259 260 if (movements.xdelta != 0 || movements.ydelta != 0) { 261 BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED, movements.timestamp, 262 remappedButtons, deltaX, deltaY); 263 if (message != NULL) 264 fTarget.EnqueueMessage(message); 265 } 266 267 if (buttons != 0) { 268 bool pressedButton = (buttons & movements.buttons) > 0; 269 BMessage* message = _BuildMouseMessage( 270 pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP, 271 movements.timestamp, remappedButtons, deltaX, deltaY); 272 if (message != NULL) { 273 if (pressedButton) { 274 message->AddInt32("clicks", movements.clicks); 275 LOG("B_MOUSE_DOWN\n"); 276 } else 277 LOG("B_MOUSE_UP\n"); 278 279 fTarget.EnqueueMessage(message); 280 lastButtons = movements.buttons; 281 } 282 } 283 284 if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) { 285 message = new BMessage(B_MOUSE_WHEEL_CHANGED); 286 if (message == NULL) 287 continue; 288 289 if (message->AddInt64("when", movements.timestamp) == B_OK 290 && message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta) == B_OK 291 && message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta) == B_OK) 292 fTarget.EnqueueMessage(message); 293 else 294 delete message; 295 } 296 } 297 } 298 299 300 status_t 301 MouseDevice::_ThreadFunction(void* arg) 302 { 303 MouseDevice* device = (MouseDevice *)arg; 304 device->_Run(); 305 return B_OK; 306 } 307 308 309 BMessage* 310 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons, 311 int32 deltaX, int32 deltaY) const 312 { 313 BMessage* message = new BMessage(what); 314 if (message == NULL) 315 return NULL; 316 317 if (message->AddInt64("when", when) < B_OK 318 || message->AddInt32("buttons", buttons) < B_OK 319 || message->AddInt32("x", deltaX) < B_OK 320 || message->AddInt32("y", deltaY) < B_OK) { 321 delete message; 322 return NULL; 323 } 324 325 return message; 326 } 327 328 329 void 330 MouseDevice::_ComputeAcceleration(const mouse_movement& movements, 331 int32& deltaX, int32& deltaY) const 332 { 333 // basic mouse speed 334 deltaX = movements.xdelta * fSettings.accel.speed >> 16; 335 deltaY = movements.ydelta * fSettings.accel.speed >> 16; 336 337 // acceleration 338 double acceleration = 1; 339 if (fSettings.accel.accel_factor) { 340 acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY) 341 * fSettings.accel.accel_factor / 524288.0; 342 } 343 344 // make sure that we move at least one pixel (if there was a movement) 345 if (deltaX > 0) 346 deltaX = (int32)floor(deltaX * acceleration); 347 else 348 deltaX = (int32)ceil(deltaX * acceleration); 349 350 if (deltaY > 0) 351 deltaY = (int32)floor(deltaY * acceleration); 352 else 353 deltaY = (int32)ceil(deltaY * acceleration); 354 } 355 356 357 uint32 358 MouseDevice::_RemapButtons(uint32 buttons) const 359 { 360 if (fDeviceRemapsButtons) 361 return buttons; 362 363 uint32 newButtons = 0; 364 for (int32 i = 0; buttons; i++) { 365 if (buttons & 0x1) { 366 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 367 newButtons |= fSettings.map.button[i]; 368 #else 369 if (i == 0) 370 newButtons |= fSettings.map.left; 371 if (i == 1) 372 newButtons |= fSettings.map.right; 373 if (i == 2) 374 newButtons |= fSettings.map.middle; 375 #endif 376 } 377 buttons >>= 1; 378 } 379 380 return newButtons; 381 } 382 383 384 char * 385 MouseDevice::_BuildShortName() const 386 { 387 BString string(fPath); 388 BString name; 389 390 int32 slash = string.FindLast("/"); 391 string.CopyInto(name, slash + 1, string.Length() - slash); 392 int32 index = atoi(name.String()) + 1; 393 394 int32 previousSlash = string.FindLast("/", slash); 395 string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1); 396 397 if (name == "ps2") 398 name = "PS/2"; 399 else 400 name.Capitalize(); 401 402 name << " Mouse " << index; 403 404 return strdup(name.String()); 405 } 406 407 408 // #pragma mark - 409 410 411 MouseInputDevice::MouseInputDevice() 412 { 413 #if DEBUG 414 sLogFile = fopen("/var/log/mouse_device_log.log", "a"); 415 #endif 416 CALLED(); 417 418 StartMonitoringDevice(kMouseDevicesDirectoryPS2); 419 StartMonitoringDevice(kMouseDevicesDirectoryUSB); 420 421 _RecursiveScan(kMouseDevicesDirectory); 422 } 423 424 425 MouseInputDevice::~MouseInputDevice() 426 { 427 CALLED(); 428 StopMonitoringDevice(kMouseDevicesDirectoryUSB); 429 StopMonitoringDevice(kMouseDevicesDirectoryPS2); 430 431 int32 count = fDevices.CountItems(); 432 while (count-- > 0) { 433 delete (MouseDevice *)fDevices.RemoveItem(count); 434 } 435 436 #if DEBUG 437 fclose(sLogFile); 438 #endif 439 } 440 441 442 status_t 443 MouseInputDevice::InitCheck() 444 { 445 CALLED(); 446 return B_OK; 447 } 448 449 450 status_t 451 MouseInputDevice::Start(const char *name, void *cookie) 452 { 453 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 454 MouseDevice* device = (MouseDevice*)cookie; 455 456 return device->Start(); 457 } 458 459 460 status_t 461 MouseInputDevice::Stop(const char *name, void *cookie) 462 { 463 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 464 MouseDevice* device = (MouseDevice*)cookie; 465 466 device->Stop(); 467 return B_OK; 468 } 469 470 471 status_t 472 MouseInputDevice::Control(const char* name, void* cookie, 473 uint32 command, BMessage* message) 474 { 475 LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 476 MouseDevice* device = (MouseDevice*)cookie; 477 478 if (command == B_NODE_MONITOR) 479 return _HandleMonitor(message); 480 481 if (command >= B_MOUSE_TYPE_CHANGED 482 && command <= B_MOUSE_ACCELERATION_CHANGED) 483 return device->UpdateSettings(); 484 485 return B_BAD_VALUE; 486 } 487 488 489 // TODO: Test this. USB doesn't work on my machine 490 status_t 491 MouseInputDevice::_HandleMonitor(BMessage* message) 492 { 493 CALLED(); 494 495 int32 opcode; 496 if (message->FindInt32("opcode", &opcode) < B_OK) 497 return B_BAD_VALUE; 498 499 if (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 500 return B_OK; 501 502 BEntry entry; 503 BPath path; 504 dev_t device; 505 ino_t directory; 506 const char *name; 507 508 if (message->FindInt32("device", &device) < B_OK 509 || message->FindInt64("directory", &directory) < B_OK 510 || message->FindString("name", &name) < B_OK) 511 return B_BAD_VALUE; 512 513 entry_ref ref(device, directory, name); 514 status_t status; 515 516 if ((status = entry.SetTo(&ref)) != B_OK) 517 return status; 518 if ((status = entry.GetPath(&path)) != B_OK) 519 return status; 520 if ((status = path.InitCheck()) != B_OK) 521 return status; 522 523 if (opcode == B_ENTRY_CREATED) 524 status = _AddDevice(path.Path()); 525 else 526 status = _RemoveDevice(path.Path()); 527 528 return status; 529 } 530 531 532 MouseDevice* 533 MouseInputDevice::_FindDevice(const char *path) 534 { 535 CALLED(); 536 537 for (int32 i = fDevices.CountItems(); i-- > 0;) { 538 MouseDevice* device = (MouseDevice*)fDevices.ItemAt(i); 539 if (!strcmp(device->Path(), path)) 540 return device; 541 } 542 543 return NULL; 544 } 545 546 547 status_t 548 MouseInputDevice::_AddDevice(const char *path) 549 { 550 CALLED(); 551 552 MouseDevice* device = new (std::nothrow) MouseDevice(*this, path); 553 if (!device) { 554 LOG("No memory\n"); 555 return B_NO_MEMORY; 556 } 557 558 if (!fDevices.AddItem(device)) { 559 delete device; 560 return B_NO_MEMORY; 561 } 562 563 input_device_ref *devices[2]; 564 devices[0] = device->DeviceRef(); 565 devices[1] = NULL; 566 567 return RegisterDevices(devices); 568 } 569 570 571 status_t 572 MouseInputDevice::_RemoveDevice(const char *path) 573 { 574 CALLED(); 575 576 MouseDevice* device = _FindDevice(path); 577 if (device == NULL) 578 return B_ENTRY_NOT_FOUND; 579 580 fDevices.RemoveItem(device); 581 582 input_device_ref *devices[2]; 583 devices[0] = device->DeviceRef(); 584 devices[1] = NULL; 585 586 UnregisterDevices(devices); 587 588 delete device; 589 return B_OK; 590 } 591 592 593 void 594 MouseInputDevice::_RecursiveScan(const char* directory) 595 { 596 CALLED(); 597 598 BEntry entry; 599 BDirectory dir(directory); 600 while (dir.GetNextEntry(&entry) == B_OK) { 601 BPath path; 602 entry.GetPath(&path); 603 604 if (!strcmp(path.Leaf(), "serial")) { 605 // skip serial 606 continue; 607 } 608 609 if (entry.IsDirectory()) 610 _RecursiveScan(path.Path()); 611 else 612 _AddDevice(path.Path()); 613 } 614 } 615 616