xref: /haiku/src/add-ons/input_server/devices/serial_mouse/MouseInputDevice.cpp (revision 95bac3fda53a4cb21880712d7b43f8c21db32a2e)
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 "kb_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 
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 
145 status_t
146 MouseInputDevice::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 
178 status_t
179 MouseInputDevice::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 
201 status_t
202 MouseInputDevice::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 
222 status_t
223 MouseInputDevice::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 
238 int32
239 MouseInputDevice::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 
255 		if (dev->sm->GetMouseEvent(&movements) < B_OK)
256 			continue;
257 /*
258 		LOG("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, wheel_x:%ld, \
259 			wheel_y:%ld\n", dev->device_ref.name, movements.buttons,
260 			movements.xdelta, movements.ydelta, movements.clicks,
261 			movements.wheel_xdelta, movements.wheel_ydelta);
262 */
263 		// TODO: Apply buttons mapping here.
264 		uint32 buttons = buttons_state ^ movements.buttons;
265 
266 	  	if (movements.buttons) {
267 
268   			if ((movements.timestamp - last_click_time) <= dev->settings.click_speed)
269 				clicks_count = (clicks_count % 3) + 1;
270   			else
271   				clicks_count = 1;
272 
273 			last_click_time = movements.timestamp;
274   		}
275 
276 		if (buttons != 0) {
277 			message = new BMessage(B_MOUSE_UP);
278 
279 			message->AddInt64("when", movements.timestamp);
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("buttons", movements.buttons);
338 				message->AddInt32("x", xdelta);
339 				message->AddInt32("y", ydelta);
340 
341 				sSingletonMouseDevice->EnqueueMessage(message);
342 			}
343 		}
344 
345 		if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
346 			message = new BMessage(B_MOUSE_WHEEL_CHANGED);
347 			if (message) {
348 				message->AddInt64("when", movements.timestamp);
349 				message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta);
350 				message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta);
351 
352 				sSingletonMouseDevice->EnqueueMessage(message);
353 			}
354 		}
355 	}
356 
357 	return B_OK;
358 }
359 
360 //==============================================================================
361 // #pragma mark -
362 
363 // mouse_device
364 mouse_device::mouse_device()
365 {
366 	device_watcher = -1;
367 	active = false;
368 	sm = new SerialMouse();
369 	device_ref.type = B_POINTING_DEVICE;
370 	device_ref.cookie = this;
371 };
372 
373 
374 status_t
375 mouse_device::init_check()
376 {
377 	status_t result = sm->IsMousePresent();
378 
379 	if (result > 0)	// Positive values indicate a mouse present.
380 		device_ref.name = (char *)sm->MouseDescription();
381 
382 	return result;
383 }
384 
385 
386 mouse_device::~mouse_device()
387 {
388 	delete sm;
389 	free(device_ref.name);
390 }
391