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