xref: /haiku/src/add-ons/input_server/devices/wacom/MasterServerDevice.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  */
5 #include "MasterServerDevice.h"
6 
7 #include <fstream>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <InterfaceDefs.h>
15 #include <Message.h>
16 #include <NodeMonitor.h>
17 #include <OS.h>
18 #include <Path.h>
19 #include <Screen.h>
20 #include <View.h>
21 #include <File.h>
22 
23 #include "PointingDevice.h"
24 #include "PointingDeviceFactory.h"
25 
26 #define DEFAULT_CLICK_SPEED 250000
27 
28 static const char* kWatchFolder			= "input/wacom/usb";
29 static const char* kDeviceFolder		= "/dev/input/wacom/usb";
30 static const char* kDeviceName			= "Wacom Tablet";
31 
32 //static const char* kPS2MouseThreadName	= "PS/2 Mouse";
33 
34 // instantiate_input_device
35 //
36 // this is where it all starts make sure this function is exported!
37 BInputServerDevice*
38 instantiate_input_device()
39 {
40 	return (new MasterServerDevice());
41 }
42 
43 // constructor
44 MasterServerDevice::MasterServerDevice()
45 	: BInputServerDevice(),
46 	  fDevices(1),
47 	  fActive(false),
48 	  fDblClickSpeed(DEFAULT_CLICK_SPEED),
49 	  fPS2DisablerThread(B_ERROR),
50 	  fDeviceLock("device list lock")
51 {
52 	get_mouse_speed(kDeviceName, &fSpeed);
53 	get_mouse_acceleration(kDeviceName, &fAcceleration);
54 	get_click_speed(kDeviceName, &fDblClickSpeed);
55 	_CalculateAccelerationTable();
56 
57 	if (_LockDevices()) {
58 		// do an initial scan of the devfs folder, after that, we should receive
59 		// node monitor messages for hotplugging support
60 		_SearchDevices();
61 
62 		fActive = true;
63 
64 		for (int32 i = 0; PointingDevice* device = (PointingDevice*)fDevices.ItemAt(i); i++) {
65 			device->Start();
66 		}
67 		_UnlockDevices();
68 	}
69 
70 	StartMonitoringDevice(kWatchFolder);
71 }
72 
73 // destructor
74 MasterServerDevice::~MasterServerDevice()
75 {
76 	// cleanup
77 	_StopAll();
78 	if (_LockDevices()) {
79 		while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem((int32)0))
80 			delete device;
81 		_UnlockDevices();
82 	}
83 }
84 
85 // InitCheck
86 status_t
87 MasterServerDevice::InitCheck()
88 {
89 	return BInputServerDevice::InitCheck();
90 }
91 
92 // SystemShuttingDown
93 status_t
94 MasterServerDevice::SystemShuttingDown()
95 {
96 	// the devices should be stopped anyways,
97 	// but this is just defensive programming.
98 	// the pointing devices will have spawned polling threads,
99 	// we make sure that we block until every thread has bailed out.
100 
101 	_StopAll();
102 
103 	PRINT(("---------------------------------\n\n"));
104 
105 	return (BInputServerDevice::SystemShuttingDown());
106 }
107 
108 // Start
109 //
110 // start generating events
111 status_t
112 MasterServerDevice::Start(const char* device, void* cookie)
113 {
114 	// TODO: make this configurable
115 //	_StartPS2DisablerThread();
116 
117 	return B_OK;
118 }
119 
120 // Stop
121 status_t
122 MasterServerDevice::Stop(const char* device, void* cookie)
123 {
124 	// stop generating events
125 	fActive = false;
126 
127 	_StopAll();
128 
129 //	_StopPS2DisablerThread();
130 
131 	return StopMonitoringDevice(kWatchFolder);
132 }
133 
134 // Control
135 status_t
136 MasterServerDevice::Control(const char* device, void* cookie, uint32 code, BMessage* message)
137 {
138 	// respond to changes in the system
139 	switch (code) {
140 		case B_MOUSE_SPEED_CHANGED:
141 			get_mouse_speed(device, &fSpeed);
142 			_CalculateAccelerationTable();
143 			break;
144 		case B_CLICK_SPEED_CHANGED:
145 			get_click_speed(device, &fDblClickSpeed);
146 			break;
147 		case B_MOUSE_ACCELERATION_CHANGED:
148 			get_mouse_acceleration(device, &fAcceleration);
149 			_CalculateAccelerationTable();
150 			break;
151 		case B_NODE_MONITOR:
152 			_HandleNodeMonitor(message);
153 			break;
154 		default:
155 			BInputServerDevice::Control(device, cookie, code, message);
156 			break;
157 	}
158 	return B_OK;
159 }
160 
161 // #pragma mark -
162 
163 // _SearchDevices
164 void
165 MasterServerDevice::_SearchDevices()
166 {
167 	if (_LockDevices()) {
168 		// construct a PointingDevice for every devfs entry we find
169 		BDirectory dir(kDeviceFolder);
170 		if (dir.InitCheck() >= B_OK) {
171 			// traverse the contents of the folder to see if the
172 			// entry of that device still exists
173 			entry_ref ref;
174 			while (dir.GetNextRef(&ref) >= B_OK) {
175 				PRINT(("examining devfs entry '%s'\n", ref.name));
176 				// don't add the control device
177 				if (strcmp(ref.name, "control") != 0) {
178 					BPath path(&ref);
179 					if (path.InitCheck() >= B_OK) {
180 						// add the device
181 						_AddDevice(path.Path());
182 					}
183 				}
184 			}
185 		} else
186 			PRINT(("folder '%s' not found\n", kDeviceFolder));
187 
188 		PRINT(("done examing devfs\n"));
189 		_UnlockDevices();
190 	}
191 }
192 
193 // _StopAll
194 void
195 MasterServerDevice::_StopAll()
196 {
197 	if (_LockDevices()) {
198 		for (int32 i = 0; PointingDevice* device
199 				= (PointingDevice*)fDevices.ItemAt(i); i++)
200 			device->Stop();
201 		_UnlockDevices();
202 	}
203 }
204 
205 // _AddDevice
206 void
207 MasterServerDevice::_AddDevice(const char* path)
208 {
209 	if (_LockDevices()) {
210 		// get the device from the factory
211 		PointingDevice* device = PointingDeviceFactory::DeviceFor(this, path);
212 		// add it to our list
213 		if (device && device->InitCheck() >= B_OK
214 			&& fDevices.AddItem((void*)device)) {
215 			PRINT(("pointing device added (%s)\n", path));
216 			// start device polling only if we're started
217 			if (fActive)
218 				device->Start();
219 			input_device_ref device = { (char*)kDeviceName, B_POINTING_DEVICE, (void*)this };
220 			input_device_ref* deviceList[2] = { &device, NULL };
221 			RegisterDevices(deviceList);
222 		} else {
223 
224 			PRINT(("pointing device not added (%s)\n", path));
225 			if (device) {
226 				PRINT(("  vendor: %0*x, product: %0*x\n", 4, device->VendorID(),
227 					4, device->ProductID()));
228 			}
229 
230 			delete device;
231 		}
232 		_UnlockDevices();
233 	}
234 }
235 
236 // _HandleNodeMonitor
237 void
238 MasterServerDevice::_HandleNodeMonitor(BMessage* message)
239 {
240 	int32 opcode;
241 	if (message->FindInt32("opcode", &opcode) < B_OK)
242 		return;
243 
244 	switch (opcode) {
245 		case B_ENTRY_CREATED:
246 			// extract info to create an entry_ref structure
247 			const char* name;
248 			ino_t directory;
249 			dev_t device;
250 			if (message->FindString("name", &name) >= B_OK
251 				&& strcmp(name, "control") != 0
252 				&& message->FindInt64("directory", (int64*)&directory) >= B_OK
253 				&& message->FindInt32("device", (int32*)&device) >= B_OK) {
254 				// make a path from that info
255 				entry_ref ref(device, directory, name);
256 				BPath path(&ref);
257 				if (path.InitCheck() >= B_OK) {
258 					// add the device
259 					_AddDevice(path.Path());
260 				}
261 			}
262 			break;
263 		case B_ENTRY_REMOVED: {
264 			// I cannot figure out how to see if this is actually
265 			// the directory that we're node monitoring, and even if it is,
266 			// we would have to look at the directories contents to
267 			// see which PointingDevice we might want to remove
268 			BDirectory dir(kDeviceFolder);
269 			if (dir.InitCheck() >= B_OK) {
270 				// for each device in our list, see if the corresponding
271 				// entry in the device folder still exists
272 				for (int32 i = 0; PointingDevice* pointingDevice
273 						= (PointingDevice*)fDevices.ItemAt(i); i++) {
274 					// traverse the contents of the folder
275 					// if the entry for this device is there,
276 					// we can abort the search
277 					entry_ref ref;
278 					dir.Rewind();
279 					bool found = false;
280 					while (dir.GetNextRef(&ref) >= B_OK) {
281 						BPath path(&ref);
282 						if (strcmp(pointingDevice->DevicePath(),
283 								path.Path()) == 0) {
284 							found = true;
285 							break;
286 						}
287 					}
288 					// remove the device if the devfs entry was not found
289 					if (!found) {
290 
291 						PRINT(("removing device '%s'\n", pointingDevice->DevicePath()));
292 
293 						if (_LockDevices()) {
294 							if (fDevices.RemoveItem((void*)pointingDevice)) {
295 
296 								delete pointingDevice;
297 								i--;
298 							}
299 							_UnlockDevices();
300 						}
301 					}
302 				}
303 			}
304 			break;
305 		}
306 	}
307 }
308 
309 // _CalculateAccelerationTable
310 //
311 // calculates the acceleration table. This is recalculated anytime we find out that
312 // the current acceleration or speed has changed.
313 void
314 MasterServerDevice::_CalculateAccelerationTable()
315 {
316 	// This seems to work alright.
317 	for (int x = 1; x <= 128; x++){
318 		fAccelerationTable[x] = (x / (128 * (1 - (((float)fSpeed + 8192) / 262144))))
319 			* (x / (128 * (1 - (((float)fSpeed + 8192) / 262144))))
320 			+ ((((float)fAcceleration + 4000) / 262144)
321 			* (x / (128 * (1 - (((float)fSpeed + 8192) / 262144)))));
322 	}
323 
324 	// sets the bottom of the table to be the inverse of the first half.
325 	// position 0 -> 128 positive movement, 255->129 negative movement
326 	for (int x = 255; x > 128; x--){
327 		fAccelerationTable[x] = -(fAccelerationTable[(255 - x)]);
328 	}
329 
330 	// Location 0 will be 0.000, which is unacceptable, otherwise, single step moves are lost
331 	// To work around this, we'll make it 1/2 of the smallest increment.
332 	fAccelerationTable[0] = fAccelerationTable[1] / 2;
333 	fAccelerationTable[255] = -fAccelerationTable[0];
334 }
335 
336 // _ps2_disabler
337 //
338 // find the PS/2 device thread and suspend its execution
339 // TODO: make this configurable
340 // In case you're wondering... this behaviour is useful for notebook
341 // users who are annoyed by accidentally hitting their touchpad while
342 // typing. "Turning it off entirely" by suspending the PS/2 thread is
343 // just a compfort thing, but should of course be made configurable...
344 //int32
345 //MasterServerDevice::_ps2_disabler(void* cookie)
346 //{
347 //	MasterServerDevice* master = (MasterServerDevice*)cookie;
348 //
349 //	thread_id haltedThread = B_ERROR;
350 //
351 //	while (master->fActive) {
352 //		// search for at least one running device
353 //		bool suspendPS2 = false;
354 //		if (master->_LockDevices()) {
355 //			for (int32 i = 0; PointingDevice* device = (PointingDevice*)master->fDevices.ItemAt(i); i++) {
356 //				if (device->DisablePS2()) {
357 //					suspendPS2 = true;
358 //					break;
359 //				}
360 //			}
361 //			master->_UnlockDevices();
362 //		}
363 //
364 //		if (suspendPS2){
365 //			// find and suspend PS/2 thread (if not already done)
366 //			if (haltedThread < B_OK) {
367 //				haltedThread = find_thread(kPS2MouseThreadName);
368 //				if (haltedThread >= B_OK) {
369 //					suspend_thread(haltedThread);
370 //				}
371 //			}
372 //		} else {
373 //			// reenable PS/2 thread
374 //			if (haltedThread >= B_OK) {
375 //				resume_thread(haltedThread);
376 //				haltedThread = B_ERROR;
377 //			}
378 //		}
379 //
380 //		// sleep a little while
381 //		snooze(2000000);
382 //	}
383 //
384 //	// reenable PS/2 thread in any case before we die
385 //	if (haltedThread >= B_OK) {
386 //		resume_thread(haltedThread);
387 //	}
388 //
389 //	return B_OK;
390 //}
391 //
392 //// _StartPS2DisablerThread
393 //void
394 //MasterServerDevice::_StartPS2DisablerThread()
395 //{
396 //	if (fPS2DisablerThread < B_OK) {
397 //		fPS2DisablerThread = spawn_thread(_ps2_disabler, "PS/2 Mouse disabler", B_LOW_PRIORITY, this);
398 //		if (fPS2DisablerThread >= B_OK)
399 //			resume_thread(fPS2DisablerThread);
400 //	}
401 //}
402 //
403 //// _StopPS2DisablerThread
404 //void
405 //MasterServerDevice::_StopPS2DisablerThread()
406 //{
407 //	if (fPS2DisablerThread >= B_OK) {
408 //		status_t err;
409 //		wait_for_thread(fPS2DisablerThread, &err);
410 //	}
411 //	fPS2DisablerThread = B_ERROR;
412 //}
413 
414 // _LockDevices
415 bool
416 MasterServerDevice::_LockDevices()
417 {
418 	return fDeviceLock.Lock();
419 }
420 
421 // _UnlockDevices
422 void
423 MasterServerDevice::_UnlockDevices()
424 {
425 	fDeviceLock.Unlock();
426 }
427 
428