1 /*****************************************************************************/
2 // Mouse input server device addon
3 // Written by Stefano Ceccherini
4 // Adapted for serial mice by Oscar Lesta
5 //
6 // MouseInputDevice.cpp
7 //
8 // Copyright (c) 2004 Haiku Project
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal in the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 // DEALINGS IN THE SOFTWARE.
27 /*****************************************************************************/
28
29 #include "MouseInputDevice.h"
30 #include "kb_mouse_settings.h"
31 #include "keyboard_mouse_driver.h"
32
33 #include <stdlib.h>
34 #include <unistd.h>
35
36 #include <Debug.h>
37 #include <Directory.h>
38 #include <Entry.h>
39 #include <NodeMonitor.h>
40 #include <Path.h>
41 #include <String.h>
42
43 #include "SerialMouse.h"
44
45 //------------------------------------------------------------------------------
46
47 #if DEBUG
LOG(const char * fmt,...)48 inline void LOG(const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); \
49 fputs(buf, MouseInputDevice::sLogFile); fflush(MouseInputDevice::sLogFile); }
50 #define LOG_ERR(text...) LOG(text)
51 FILE *MouseInputDevice::sLogFile = NULL;
52 #else
53 #define LOG(text...)
54 #define LOG_ERR(text...) fprintf(stderr, text)
55 #endif
56
57 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
58
59 static MouseInputDevice *sSingletonMouseDevice = NULL;
60
61
62 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
63
64 //------------------------------------------------------------------------------
65
66 struct mouse_device {
67 mouse_device();
68 ~mouse_device();
69
70 status_t init_check(); // > 0 if a mouse was detected.
71
72 input_device_ref device_ref;
73 SerialMouse* sm;
74 thread_id device_watcher;
75 mouse_settings settings;
76 bool active;
77 };
78
79
80 extern "C"
instantiate_input_device()81 BInputServerDevice* instantiate_input_device()
82 {
83 return new MouseInputDevice();
84 }
85
86 //------------------------------------------------------------------------------
87 // #pragma mark -
88
MouseInputDevice()89 MouseInputDevice::MouseInputDevice()
90 {
91 ASSERT(sSingletonMouseDevice == NULL);
92 sSingletonMouseDevice = this;
93
94 #if DEBUG
95 sLogFile = fopen("/var/log/serial_mouse.log", "w");
96 #endif
97 CALLED();
98 }
99
~MouseInputDevice()100 MouseInputDevice::~MouseInputDevice()
101 {
102 CALLED();
103
104 for (int32 i = 0; i < fDevices.CountItems(); i++)
105 delete (mouse_device*) fDevices.ItemAt(i);
106
107 #if DEBUG
108 fclose(sLogFile);
109 #endif
110 }
111
112 // SerialMouse does not know anything about mouse_settings, I choose to let
113 // mouse_device hold that instead (bercause mice type, button mapping,
114 // speed/accel, etc., are all handled here, not in SerialMouse).
115
116 status_t
InitFromSettings(void * cookie,uint32 opcode)117 MouseInputDevice::InitFromSettings(void* cookie, uint32 opcode)
118 {
119 CALLED();
120 mouse_device* device = (mouse_device*) cookie;
121 const char* name = device->device_ref.name;
122
123 // retrieve current values.
124 // TODO: shouldn't we use sane values if we get errors here?
125
126 if (get_mouse_map(name, &device->settings.map) != B_OK)
127 LOG_ERR("error when get_mouse_map\n");
128
129 if (get_click_speed(name, &device->settings.click_speed) != B_OK)
130 LOG_ERR("error when get_click_speed\n");
131
132 if (get_mouse_speed(name, &device->settings.accel.speed) != B_OK)
133 LOG_ERR("error when get_mouse_speed\n");
134 else if (get_mouse_acceleration(name, &device->settings.accel.accel_factor) != B_OK)
135 LOG_ERR("error when get_mouse_acceleration\n");
136
137 if (get_mouse_type(name, &device->settings.type) != B_OK)
138 LOG_ERR("error when get_mouse_type\n");
139
140 return B_OK;
141 }
142
143 status_t
InitCheck()144 MouseInputDevice::InitCheck()
145 {
146 CALLED();
147
148 // TODO: We should iterate here to support more than just one mouse.
149 // (I've tried, but kept entering and endless loop or crashing
150 // the input_server :-( ).
151
152 mouse_device* device = new mouse_device();
153 if (!device)
154 {
155 LOG("No memory\n");
156 return B_NO_MEMORY;
157 }
158
159 if (device->init_check() <= B_OK)
160 {
161 LOG("The mouse was eaten by a rabid cat.\n");
162 delete device;
163 return B_ERROR;
164 }
165
166 input_device_ref* devices[2];
167 devices[0] = &device->device_ref;
168 devices[1] = NULL;
169
170 fDevices.AddItem(device);
171
172 return RegisterDevices(devices);
173 }
174
175
176 status_t
Start(const char * name,void * cookie)177 MouseInputDevice::Start(const char* name, void* cookie)
178 {
179 mouse_device* device = (mouse_device*) cookie;
180
181 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
182
183 InitFromSettings(device);
184
185 char threadName[B_OS_NAME_LENGTH];
186 // "Microsoft watcher" sounded even more akward than this...
187 snprintf(threadName, B_OS_NAME_LENGTH, "%s Mouse", name);
188
189 device->active = true;
190 device->device_watcher = spawn_thread(DeviceWatcher, threadName,
191 kMouseThreadPriority, device);
192
193 resume_thread(device->device_watcher);
194
195 return B_OK;
196 }
197
198
199 status_t
Stop(const char * name,void * cookie)200 MouseInputDevice::Stop(const char* name, void* cookie)
201 {
202 mouse_device* device = (mouse_device*) cookie;
203
204 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
205
206 device->active = false;
207
208 if (device->device_watcher >= 0)
209 {
210 suspend_thread(device->device_watcher);
211 resume_thread(device->device_watcher);
212 status_t dummy;
213 wait_for_thread(device->device_watcher, &dummy);
214 }
215
216 return B_OK;
217 }
218
219
220 status_t
Control(const char * name,void * cookie,uint32 command,BMessage * message)221 MouseInputDevice::Control(const char* name, void* cookie,
222 uint32 command, BMessage* message)
223 {
224 LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
225
226 if (command >= B_MOUSE_TYPE_CHANGED &&
227 command <= B_MOUSE_ACCELERATION_CHANGED)
228 {
229 InitFromSettings(cookie, command);
230 }
231
232 return B_OK;
233 }
234
235
236 int32
DeviceWatcher(void * arg)237 MouseInputDevice::DeviceWatcher(void* arg)
238 {
239 mouse_device* dev = (mouse_device*) arg;
240
241 mouse_movement movements;
242 uint32 buttons_state = 0;
243 uint8 clicks_count = 0;
244 bigtime_t last_click_time = 0;
245 BMessage* message;
246
247 CALLED();
248
249 while (dev->active)
250 {
251 memset(&movements, 0, sizeof(movements));
252 if (dev->sm->GetMouseEvent(&movements) != B_OK) {
253 snooze(10000); // this is a realtime thread, and something is wrong...
254 continue;
255 }
256 /*
257 LOG("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, \
258 wheel_y:%ld\n", dev->device_ref.name, movements.buttons,
259 movements.xdelta, movements.ydelta, movements.clicks,
260 movements.wheel_xdelta, movements.wheel_ydelta);
261 */
262 // TODO: Apply buttons mapping here.
263 uint32 buttons = buttons_state ^ movements.buttons;
264
265 if (movements.buttons) {
266
267 if ((movements.timestamp - last_click_time) <= dev->settings.click_speed)
268 clicks_count = (clicks_count % 3) + 1;
269 else
270 clicks_count = 1;
271
272 last_click_time = movements.timestamp;
273 }
274
275 if (buttons != 0) {
276 message = new BMessage(B_MOUSE_UP);
277
278 message->AddInt64("when", movements.timestamp);
279 message->AddInt32("be:device_subtype", B_MOUSE_DEVICE_SUBTYPE);
280 message->AddInt32("buttons", movements.buttons);
281
282 if ((buttons & movements.buttons) > 0) {
283 message->what = B_MOUSE_DOWN;
284 message->AddInt32("clicks", clicks_count);
285 LOG("B_MOUSE_DOWN\n");
286 } else {
287 LOG("B_MOUSE_UP\n");
288 }
289
290 message->AddInt32("x", movements.xdelta);
291 message->AddInt32("y", movements.ydelta);
292
293 sSingletonMouseDevice->EnqueueMessage(message);
294 buttons_state = movements.buttons;
295 }
296
297 if (movements.xdelta != 0 || movements.ydelta != 0) {
298 int32 xdelta = movements.xdelta;
299 int32 ydelta = movements.ydelta;
300
301 // TODO: properly scale these values.
302 // (s >> 14, a >> 8) or (s >> 15, a >> 11) feels more or less OK
303 // with the default values: yields to speed = 2; accel_factor = 32
304 // Maybe we should use floats and then round to nearest integer?
305 uint32 speed = (dev->settings.accel.speed >> 15);
306 uint32 accel_factor = (dev->settings.accel.accel_factor >> 11);
307
308 // LOG("accel.enabled? = %s\n", (dev->settings.accel.enabled) ? "Yes" : "No");
309 // LOG("speed = %ld; accel_factor = %ld\n", speed, accel_factor);
310
311 if (speed && accel_factor) {
312 xdelta *= speed;
313 ydelta *= speed;
314
315 // preserve the sign.
316 bool xneg = (xdelta < 0);
317 bool yneg = (ydelta < 0);
318
319 if (movements.xdelta != 0) {
320 xdelta = (xdelta * xdelta) / accel_factor;
321 (xdelta) ? : (xdelta = 1);
322 if (xneg) xdelta *= -1;
323 }
324
325 if (movements.ydelta != 0) {
326 ydelta = (ydelta * ydelta) / accel_factor;
327 (ydelta) ? : (ydelta = 1);
328 if (yneg) ydelta *= -1;
329 }
330 }
331
332 // LOG("%s: x: %ld, y: %ld\n", dev->device_ref.name, xdelta, ydelta);
333
334 message = new BMessage(B_MOUSE_MOVED);
335 if (message) {
336 message->AddInt64("when", movements.timestamp);
337 message->AddInt32("be:device_subtype", B_MOUSE_DEVICE_SUBTYPE);
338 message->AddInt32("buttons", movements.buttons);
339 message->AddInt32("x", xdelta);
340 message->AddInt32("y", ydelta);
341
342 sSingletonMouseDevice->EnqueueMessage(message);
343 }
344 }
345
346 if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
347 message = new BMessage(B_MOUSE_WHEEL_CHANGED);
348 if (message) {
349 message->AddInt64("when", movements.timestamp);
350 message->AddInt32("be:device_subtype", B_MOUSE_DEVICE_SUBTYPE);
351 message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta);
352 message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta);
353
354 sSingletonMouseDevice->EnqueueMessage(message);
355 }
356 }
357 }
358
359 return B_OK;
360 }
361
362 //==============================================================================
363 // #pragma mark -
364
365 // mouse_device
mouse_device()366 mouse_device::mouse_device()
367 {
368 device_watcher = -1;
369 active = false;
370 sm = new SerialMouse();
371 device_ref.type = B_POINTING_DEVICE;
372 device_ref.cookie = this;
373 };
374
375
376 status_t
init_check()377 mouse_device::init_check()
378 {
379 status_t result = sm->IsMousePresent();
380
381 if (result > 0) // Positive values indicate a mouse present.
382 device_ref.name = (char *)sm->MouseDescription();
383
384 return result;
385 }
386
387
~mouse_device()388 mouse_device::~mouse_device()
389 {
390 delete sm;
391 }
392