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 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" 81 BInputServerDevice* instantiate_input_device() 82 { 83 return new MouseInputDevice(); 84 } 85 86 //------------------------------------------------------------------------------ 87 // #pragma mark - 88 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 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 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 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 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 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 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 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 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 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 388 mouse_device::~mouse_device() 389 { 390 delete sm; 391 } 392