106d748c45
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14476 a95241bf-73f2-0310-859d-f6bbb57e9c96
391 lines
9.9 KiB
C++
391 lines
9.9 KiB
C++
/*****************************************************************************/
|
|
// Mouse input server device addon
|
|
// Written by Stefano Ceccherini
|
|
// Adapted for serial mice by Oscar Lesta
|
|
//
|
|
// MouseInputDevice.cpp
|
|
//
|
|
// Copyright (c) 2004 Haiku Project
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
/*****************************************************************************/
|
|
|
|
#include "MouseInputDevice.h"
|
|
#include "kb_mouse_settings.h"
|
|
#include "kb_mouse_driver.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <Debug.h>
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <NodeMonitor.h>
|
|
#include <Path.h>
|
|
#include <String.h>
|
|
|
|
#include "SerialMouse.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if DEBUG
|
|
inline void LOG(const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); \
|
|
fputs(buf, MouseInputDevice::sLogFile); fflush(MouseInputDevice::sLogFile); }
|
|
#define LOG_ERR(text...) LOG(text)
|
|
FILE *MouseInputDevice::sLogFile = NULL;
|
|
#else
|
|
#define LOG(text...)
|
|
#define LOG_ERR(text...) fprintf(stderr, text)
|
|
#endif
|
|
|
|
#define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
|
|
|
|
static MouseInputDevice *sSingletonMouseDevice = NULL;
|
|
|
|
|
|
const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
struct mouse_device {
|
|
mouse_device();
|
|
~mouse_device();
|
|
|
|
status_t init_check(); // > 0 if a mouse was detected.
|
|
|
|
input_device_ref device_ref;
|
|
SerialMouse* sm;
|
|
thread_id device_watcher;
|
|
mouse_settings settings;
|
|
bool active;
|
|
};
|
|
|
|
|
|
extern "C"
|
|
BInputServerDevice* instantiate_input_device()
|
|
{
|
|
return new MouseInputDevice();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// #pragma mark -
|
|
|
|
MouseInputDevice::MouseInputDevice()
|
|
{
|
|
ASSERT(sSingletonMouseDevice == NULL);
|
|
sSingletonMouseDevice = this;
|
|
|
|
#if DEBUG
|
|
sLogFile = fopen("/var/log/serial_mouse.log", "w");
|
|
#endif
|
|
CALLED();
|
|
}
|
|
|
|
MouseInputDevice::~MouseInputDevice()
|
|
{
|
|
CALLED();
|
|
|
|
for (int32 i = 0; i < fDevices.CountItems(); i++)
|
|
delete (mouse_device*) fDevices.ItemAt(i);
|
|
|
|
#if DEBUG
|
|
fclose(sLogFile);
|
|
#endif
|
|
}
|
|
|
|
// SerialMouse does not know anything about mouse_settings, I choose to let
|
|
// mouse_device hold that instead (bercause mice type, button mapping,
|
|
// speed/accel, etc., are all handled here, not in SerialMouse).
|
|
|
|
status_t
|
|
MouseInputDevice::InitFromSettings(void* cookie, uint32 opcode)
|
|
{
|
|
CALLED();
|
|
mouse_device* device = (mouse_device*) cookie;
|
|
|
|
// retrieve current values.
|
|
// TODO: shouldn't we use sane values if we get errors here?
|
|
|
|
if (get_mouse_map(&device->settings.map) != B_OK)
|
|
LOG_ERR("error when get_mouse_map\n");
|
|
|
|
if (get_click_speed(&device->settings.click_speed) != B_OK)
|
|
LOG_ERR("error when get_click_speed\n");
|
|
|
|
if (get_mouse_speed(&device->settings.accel.speed) != B_OK)
|
|
LOG_ERR("error when get_mouse_speed\n");
|
|
else
|
|
{
|
|
if (get_mouse_acceleration(&device->settings.accel.accel_factor) != B_OK)
|
|
LOG_ERR("error when get_mouse_acceleration\n");
|
|
}
|
|
|
|
if (get_mouse_type(&device->settings.type) != B_OK)
|
|
LOG_ERR("error when get_mouse_type\n");
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
status_t
|
|
MouseInputDevice::InitCheck()
|
|
{
|
|
CALLED();
|
|
|
|
// TODO: We should iterate here to support more than just one mouse.
|
|
// (I've tried, but kept entering and endless loop or crashing
|
|
// the input_server :-( ).
|
|
|
|
mouse_device* device = new mouse_device();
|
|
if (!device)
|
|
{
|
|
LOG("No memory\n");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
if (device->init_check() <= B_OK)
|
|
{
|
|
LOG("The mouse was eaten by a rabid cat.\n");
|
|
delete device;
|
|
return B_ERROR;
|
|
}
|
|
|
|
input_device_ref* devices[2];
|
|
devices[0] = &device->device_ref;
|
|
devices[1] = NULL;
|
|
|
|
fDevices.AddItem(device);
|
|
|
|
return RegisterDevices(devices);
|
|
}
|
|
|
|
|
|
status_t
|
|
MouseInputDevice::Start(const char* name, void* cookie)
|
|
{
|
|
mouse_device* device = (mouse_device*) cookie;
|
|
|
|
LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
|
|
|
|
InitFromSettings(device);
|
|
|
|
char threadName[B_OS_NAME_LENGTH];
|
|
// "Microsoft watcher" sounded even more akward than this...
|
|
snprintf(threadName, B_OS_NAME_LENGTH, "%s Mouse", name);
|
|
|
|
device->active = true;
|
|
device->device_watcher = spawn_thread(DeviceWatcher, threadName,
|
|
kMouseThreadPriority, device);
|
|
|
|
resume_thread(device->device_watcher);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
MouseInputDevice::Stop(const char* name, void* cookie)
|
|
{
|
|
mouse_device* device = (mouse_device*) cookie;
|
|
|
|
LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
|
|
|
|
device->active = false;
|
|
|
|
if (device->device_watcher >= 0)
|
|
{
|
|
suspend_thread(device->device_watcher);
|
|
resume_thread(device->device_watcher);
|
|
status_t dummy;
|
|
wait_for_thread(device->device_watcher, &dummy);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
MouseInputDevice::Control(const char* name, void* cookie,
|
|
uint32 command, BMessage* message)
|
|
{
|
|
LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
|
|
|
|
if (command >= B_MOUSE_TYPE_CHANGED &&
|
|
command <= B_MOUSE_ACCELERATION_CHANGED)
|
|
{
|
|
InitFromSettings(cookie, command);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
int32
|
|
MouseInputDevice::DeviceWatcher(void* arg)
|
|
{
|
|
mouse_device* dev = (mouse_device*) arg;
|
|
|
|
mouse_movement movements;
|
|
uint32 buttons_state = 0;
|
|
uint8 clicks_count = 0;
|
|
bigtime_t last_click_time = 0;
|
|
BMessage* message;
|
|
|
|
CALLED();
|
|
|
|
while (dev->active)
|
|
{
|
|
memset(&movements, 0, sizeof(movements));
|
|
if (dev->sm->GetMouseEvent(&movements) != B_OK) {
|
|
snooze(10000); // this is a realtime thread, and something is wrong...
|
|
continue;
|
|
}
|
|
/*
|
|
LOG("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, \
|
|
wheel_y:%ld\n", dev->device_ref.name, movements.buttons,
|
|
movements.xdelta, movements.ydelta, movements.clicks,
|
|
movements.wheel_xdelta, movements.wheel_ydelta);
|
|
*/
|
|
// TODO: Apply buttons mapping here.
|
|
uint32 buttons = buttons_state ^ movements.buttons;
|
|
|
|
if (movements.buttons) {
|
|
|
|
if ((movements.timestamp - last_click_time) <= dev->settings.click_speed)
|
|
clicks_count = (clicks_count % 3) + 1;
|
|
else
|
|
clicks_count = 1;
|
|
|
|
last_click_time = movements.timestamp;
|
|
}
|
|
|
|
if (buttons != 0) {
|
|
message = new BMessage(B_MOUSE_UP);
|
|
|
|
message->AddInt64("when", movements.timestamp);
|
|
message->AddInt32("buttons", movements.buttons);
|
|
|
|
if ((buttons & movements.buttons) > 0) {
|
|
message->what = B_MOUSE_DOWN;
|
|
message->AddInt32("clicks", clicks_count);
|
|
LOG("B_MOUSE_DOWN\n");
|
|
} else {
|
|
LOG("B_MOUSE_UP\n");
|
|
}
|
|
|
|
message->AddInt32("x", movements.xdelta);
|
|
message->AddInt32("y", movements.ydelta);
|
|
|
|
sSingletonMouseDevice->EnqueueMessage(message);
|
|
buttons_state = movements.buttons;
|
|
}
|
|
|
|
if (movements.xdelta != 0 || movements.ydelta != 0) {
|
|
int32 xdelta = movements.xdelta;
|
|
int32 ydelta = movements.ydelta;
|
|
|
|
// TODO: properly scale these values.
|
|
// (s >> 14, a >> 8) or (s >> 15, a >> 11) feels more or less OK
|
|
// with the default values: yields to speed = 2; accel_factor = 32
|
|
// Maybe we should use floats and then round to nearest integer?
|
|
uint32 speed = (dev->settings.accel.speed >> 15);
|
|
uint32 accel_factor = (dev->settings.accel.accel_factor >> 11);
|
|
|
|
// LOG("accel.enabled? = %s\n", (dev->settings.accel.enabled) ? "Yes" : "No");
|
|
// LOG("speed = %ld; accel_factor = %ld\n", speed, accel_factor);
|
|
|
|
if (speed && accel_factor) {
|
|
xdelta *= speed;
|
|
ydelta *= speed;
|
|
|
|
// preserve the sign.
|
|
bool xneg = (xdelta < 0);
|
|
bool yneg = (ydelta < 0);
|
|
|
|
if (movements.xdelta != 0) {
|
|
xdelta = (xdelta * xdelta) / accel_factor;
|
|
(xdelta) ? : (xdelta = 1);
|
|
if (xneg) xdelta *= -1;
|
|
}
|
|
|
|
if (movements.ydelta != 0) {
|
|
ydelta = (ydelta * ydelta) / accel_factor;
|
|
(ydelta) ? : (ydelta = 1);
|
|
if (yneg) ydelta *= -1;
|
|
}
|
|
}
|
|
|
|
// LOG("%s: x: %ld, y: %ld\n", dev->device_ref.name, xdelta, ydelta);
|
|
|
|
message = new BMessage(B_MOUSE_MOVED);
|
|
if (message) {
|
|
message->AddInt64("when", movements.timestamp);
|
|
message->AddInt32("buttons", movements.buttons);
|
|
message->AddInt32("x", xdelta);
|
|
message->AddInt32("y", ydelta);
|
|
|
|
sSingletonMouseDevice->EnqueueMessage(message);
|
|
}
|
|
}
|
|
|
|
if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
|
|
message = new BMessage(B_MOUSE_WHEEL_CHANGED);
|
|
if (message) {
|
|
message->AddInt64("when", movements.timestamp);
|
|
message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta);
|
|
message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta);
|
|
|
|
sSingletonMouseDevice->EnqueueMessage(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
//==============================================================================
|
|
// #pragma mark -
|
|
|
|
// mouse_device
|
|
mouse_device::mouse_device()
|
|
{
|
|
device_watcher = -1;
|
|
active = false;
|
|
sm = new SerialMouse();
|
|
device_ref.type = B_POINTING_DEVICE;
|
|
device_ref.cookie = this;
|
|
};
|
|
|
|
|
|
status_t
|
|
mouse_device::init_check()
|
|
{
|
|
status_t result = sm->IsMousePresent();
|
|
|
|
if (result > 0) // Positive values indicate a mouse present.
|
|
device_ref.name = (char *)sm->MouseDescription();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
mouse_device::~mouse_device()
|
|
{
|
|
delete sm;
|
|
}
|