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) 51FILE *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 59static MouseInputDevice *sSingletonMouseDevice = NULL; 60 61 62const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 63 64//------------------------------------------------------------------------------ 65 66struct 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 80extern "C" 81BInputServerDevice* instantiate_input_device() 82{ 83 return new MouseInputDevice(); 84} 85 86//------------------------------------------------------------------------------ 87// #pragma mark - 88 89MouseInputDevice::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 100MouseInputDevice::~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 116status_t 117MouseInputDevice::InitFromSettings(void* cookie, uint32 opcode) 118{ 119 CALLED(); 120 mouse_device* device = (mouse_device*) cookie; 121 122 // retrieve current values. 123 // TODO: shouldn't we use sane values if we get errors here? 124 125 if (get_mouse_map(&device->settings.map) != B_OK) 126 LOG_ERR("error when get_mouse_map\n"); 127 128 if (get_click_speed(&device->settings.click_speed) != B_OK) 129 LOG_ERR("error when get_click_speed\n"); 130 131 if (get_mouse_speed(&device->settings.accel.speed) != B_OK) 132 LOG_ERR("error when get_mouse_speed\n"); 133 else 134 { 135 if (get_mouse_acceleration(&device->settings.accel.accel_factor) != B_OK) 136 LOG_ERR("error when get_mouse_acceleration\n"); 137 } 138 139 if (get_mouse_type(&device->settings.type) != B_OK) 140 LOG_ERR("error when get_mouse_type\n"); 141 142 return B_OK; 143} 144 145status_t 146MouseInputDevice::InitCheck() 147{ 148 CALLED(); 149 150 // TODO: We should iterate here to support more than just one mouse. 151 // (I've tried, but kept entering and endless loop or crashing 152 // the input_server :-( ). 153 154 mouse_device* device = new mouse_device(); 155 if (!device) 156 { 157 LOG("No memory\n"); 158 return B_NO_MEMORY; 159 } 160 161 if (device->init_check() <= B_OK) 162 { 163 LOG("The mouse was eaten by a rabid cat.\n"); 164 delete device; 165 return B_ERROR; 166 } 167 168 input_device_ref* devices[2]; 169 devices[0] = &device->device_ref; 170 devices[1] = NULL; 171 172 fDevices.AddItem(device); 173 174 return RegisterDevices(devices); 175} 176 177 178status_t 179MouseInputDevice::Start(const char* name, void* cookie) 180{ 181 mouse_device* device = (mouse_device*) cookie; 182 183 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 184 185 InitFromSettings(device); 186 187 char threadName[B_OS_NAME_LENGTH]; 188 // "Microsoft watcher" sounded even more akward than this... 189 snprintf(threadName, B_OS_NAME_LENGTH, "%s Mouse", name); 190 191 device->active = true; 192 device->device_watcher = spawn_thread(DeviceWatcher, threadName, 193 kMouseThreadPriority, device); 194 195 resume_thread(device->device_watcher); 196 197 return B_OK; 198} 199 200 201status_t 202MouseInputDevice::Stop(const char* name, void* cookie) 203{ 204 mouse_device* device = (mouse_device*) cookie; 205 206 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 207 208 device->active = false; 209 210 if (device->device_watcher >= 0) 211 { 212 suspend_thread(device->device_watcher); 213 resume_thread(device->device_watcher); 214 status_t dummy; 215 wait_for_thread(device->device_watcher, &dummy); 216 } 217 218 return B_OK; 219} 220 221 222status_t 223MouseInputDevice::Control(const char* name, void* cookie, 224 uint32 command, BMessage* message) 225{ 226 LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 227 228 if (command >= B_MOUSE_TYPE_CHANGED && 229 command <= B_MOUSE_ACCELERATION_CHANGED) 230 { 231 InitFromSettings(cookie, command); 232 } 233 234 return B_OK; 235} 236 237 238int32 239MouseInputDevice::DeviceWatcher(void* arg) 240{ 241 mouse_device* dev = (mouse_device*) arg; 242 243 mouse_movement movements; 244 uint32 buttons_state = 0; 245 uint8 clicks_count = 0; 246 bigtime_t last_click_time = 0; 247 BMessage* message; 248 249 CALLED(); 250 251 while (dev->active) 252 { 253 memset(&movements, 0, sizeof(movements)); 254 if (dev->sm->GetMouseEvent(&movements) != B_OK) { 255 snooze(10000); // this is a realtime thread, and something is wrong... 256 continue; 257 } 258/* 259 LOG("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, \ 260 wheel_y:%ld\n", dev->device_ref.name, movements.buttons, 261 movements.xdelta, movements.ydelta, movements.clicks, 262 movements.wheel_xdelta, movements.wheel_ydelta); 263*/ 264 // TODO: Apply buttons mapping here. 265 uint32 buttons = buttons_state ^ movements.buttons; 266 267 if (movements.buttons) { 268 269 if ((movements.timestamp - last_click_time) <= dev->settings.click_speed) 270 clicks_count = (clicks_count % 3) + 1; 271 else 272 clicks_count = 1; 273 274 last_click_time = movements.timestamp; 275 } 276 277 if (buttons != 0) { 278 message = new BMessage(B_MOUSE_UP); 279 280 message->AddInt64("when", movements.timestamp); 281 message->AddInt32("buttons", movements.buttons); 282 283 if ((buttons & movements.buttons) > 0) { 284 message->what = B_MOUSE_DOWN; 285 message->AddInt32("clicks", clicks_count); 286 LOG("B_MOUSE_DOWN\n"); 287 } else { 288 LOG("B_MOUSE_UP\n"); 289 } 290 291 message->AddInt32("x", movements.xdelta); 292 message->AddInt32("y", movements.ydelta); 293 294 sSingletonMouseDevice->EnqueueMessage(message); 295 buttons_state = movements.buttons; 296 } 297 298 if (movements.xdelta != 0 || movements.ydelta != 0) { 299 int32 xdelta = movements.xdelta; 300 int32 ydelta = movements.ydelta; 301 302 // TODO: properly scale these values. 303 // (s >> 14, a >> 8) or (s >> 15, a >> 11) feels more or less OK 304 // with the default values: yields to speed = 2; accel_factor = 32 305 // Maybe we should use floats and then round to nearest integer? 306 uint32 speed = (dev->settings.accel.speed >> 15); 307 uint32 accel_factor = (dev->settings.accel.accel_factor >> 11); 308 309// LOG("accel.enabled? = %s\n", (dev->settings.accel.enabled) ? "Yes" : "No"); 310// LOG("speed = %ld; accel_factor = %ld\n", speed, accel_factor); 311 312 if (speed && accel_factor) { 313 xdelta *= speed; 314 ydelta *= speed; 315 316 // preserve the sign. 317 bool xneg = (xdelta < 0); 318 bool yneg = (ydelta < 0); 319 320 if (movements.xdelta != 0) { 321 xdelta = (xdelta * xdelta) / accel_factor; 322 (xdelta) ? : (xdelta = 1); 323 if (xneg) xdelta *= -1; 324 } 325 326 if (movements.ydelta != 0) { 327 ydelta = (ydelta * ydelta) / accel_factor; 328 (ydelta) ? : (ydelta = 1); 329 if (yneg) ydelta *= -1; 330 } 331 } 332 333// LOG("%s: x: %ld, y: %ld\n", dev->device_ref.name, xdelta, ydelta); 334 335 message = new BMessage(B_MOUSE_MOVED); 336 if (message) { 337 message->AddInt64("when", movements.timestamp); 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->AddFloat("be:wheel_delta_x", movements.wheel_xdelta); 351 message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta); 352 353 sSingletonMouseDevice->EnqueueMessage(message); 354 } 355 } 356 } 357 358 return B_OK; 359} 360 361//============================================================================== 362// #pragma mark - 363 364// mouse_device 365mouse_device::mouse_device() 366{ 367 device_watcher = -1; 368 active = false; 369 sm = new SerialMouse(); 370 device_ref.type = B_POINTING_DEVICE; 371 device_ref.cookie = this; 372}; 373 374 375status_t 376mouse_device::init_check() 377{ 378 status_t result = sm->IsMousePresent(); 379 380 if (result > 0) // Positive values indicate a mouse present. 381 device_ref.name = (char *)sm->MouseDescription(); 382 383 return result; 384} 385 386 387mouse_device::~mouse_device() 388{ 389 delete sm; 390} 391