xref: /haiku/src/add-ons/input_server/devices/wacom/MasterServerDevice.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 
31 //static const char* kPS2MouseThreadName	= "PS/2 Mouse";
32 
33 // instantiate_input_device
34 //
35 // this is where it all starts make sure this function is exported!
36 BInputServerDevice*
37 instantiate_input_device()
38 {
39 	return (new MasterServerDevice());
40 }
41 
42 // constructor
43 MasterServerDevice::MasterServerDevice()
44 	: BInputServerDevice(),
45 	  fDevices(1),
46 	  fActive(false),
47 	  fDblClickSpeed(DEFAULT_CLICK_SPEED),
48 	  fPS2DisablerThread(B_ERROR),
49 	  fDeviceLock("device list lock")
50 {
51 	get_mouse_speed(&fSpeed);
52 	get_mouse_acceleration(&fAcceleration);
53 	get_click_speed(&fDblClickSpeed);
54 	_CalculateAccelerationTable();
55 
56 	if (_LockDevices()) {
57 		// do an initial scan of the devfs folder, after that, we should receive
58 		// node monitor messages for hotplugging support
59 		_SearchDevices();
60 
61 		fActive = true;
62 
63 		for (int32 i = 0; PointingDevice* device = (PointingDevice*)fDevices.ItemAt(i); i++) {
64 			device->Start();
65 		}
66 		_UnlockDevices();
67 	}
68 
69 	StartMonitoringDevice(kWatchFolder);
70 }
71 
72 // destructor
73 MasterServerDevice::~MasterServerDevice()
74 {
75 	// cleanup
76 	_StopAll();
77 	if (_LockDevices()) {
78 		while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem((int32)0))
79 			delete device;
80 		_UnlockDevices();
81 	}
82 }
83 
84 // InitCheck
85 status_t
86 MasterServerDevice::InitCheck()
87 {
88 	return BInputServerDevice::InitCheck();
89 }
90 
91 // SystemShuttingDown
92 status_t
93 MasterServerDevice::SystemShuttingDown()
94 {
95 	// the devices should be stopped anyways,
96 	// but this is just defensive programming.
97 	// the pointing devices will have spawned polling threads,
98 	// we make sure that we block until every thread has bailed out.
99 
100 	_StopAll();
101 
102 	PRINT(("---------------------------------\n\n"));
103 
104 	return (BInputServerDevice::SystemShuttingDown());
105 }
106 
107 // Start
108 //
109 // start generating events
110 status_t
111 MasterServerDevice::Start(const char* device, void* cookie)
112 {
113 	// TODO: make this configurable
114 //	_StartPS2DisablerThread();
115 
116 	return B_OK;
117 }
118 
119 // Stop
120 status_t
121 MasterServerDevice::Stop(const char* device, void* cookie)
122 {
123 	// stop generating events
124 	fActive = false;
125 
126 	_StopAll();
127 
128 //	_StopPS2DisablerThread();
129 
130 	return StopMonitoringDevice(kWatchFolder);
131 }
132 
133 // Control
134 status_t
135 MasterServerDevice::Control(const char* device, void* cookie, uint32 code, BMessage* message)
136 {
137 	// respond to changes in the system
138 	switch (code) {
139 		case B_MOUSE_SPEED_CHANGED:
140 			get_mouse_speed(device, &fSpeed);
141 			_CalculateAccelerationTable();
142 			break;
143 		case B_CLICK_SPEED_CHANGED:
144 			get_click_speed(&fDblClickSpeed);
145 			break;
146 		case B_MOUSE_ACCELERATION_CHANGED:
147 			get_mouse_acceleration(device, &fAcceleration);
148 			_CalculateAccelerationTable();
149 			break;
150 		case B_NODE_MONITOR:
151 			_HandleNodeMonitor(message);
152 			break;
153 		default:
154 			BInputServerDevice::Control(device, cookie, code, message);
155 			break;
156 	}
157 	return B_OK;
158 }
159 
160 // #pragma mark -
161 
162 // _SearchDevices
163 void
164 MasterServerDevice::_SearchDevices()
165 {
166 	if (_LockDevices()) {
167 		// construct a PointingDevice for every devfs entry we find
168 		BDirectory dir(kDeviceFolder);
169 		if (dir.InitCheck() >= B_OK) {
170 			// traverse the contents of the folder to see if the
171 			// entry of that device still exists
172 			entry_ref ref;
173 			while (dir.GetNextRef(&ref) >= B_OK) {
174 				PRINT(("examining devfs entry '%s'\n", ref.name));
175 				// don't add the control device
176 				if (strcmp(ref.name, "control") != 0) {
177 					BPath path(&ref);
178 					if (path.InitCheck() >= B_OK) {
179 						// add the device
180 						_AddDevice(path.Path());
181 					}
182 				}
183 			}
184 		} else
185 			PRINT(("folder '%s' not found\n", kDeviceFolder));
186 
187 		PRINT(("done examing devfs\n"));
188 		_UnlockDevices();
189 	}
190 }
191 
192 // _StopAll
193 void
194 MasterServerDevice::_StopAll()
195 {
196 	if (_LockDevices()) {
197 		for (int32 i = 0; PointingDevice* device
198 				= (PointingDevice*)fDevices.ItemAt(i); i++)
199 			device->Stop();
200 		_UnlockDevices();
201 	}
202 }
203 
204 // _AddDevice
205 void
206 MasterServerDevice::_AddDevice(const char* path)
207 {
208 	if (_LockDevices()) {
209 		// get the device from the factory
210 		PointingDevice* device = PointingDeviceFactory::DeviceFor(this, path);
211 		// add it to our list
212 		if (device && device->InitCheck() >= B_OK
213 			&& fDevices.AddItem((void*)device)) {
214 			PRINT(("pointing device added (%s)\n", path));
215 			// start device polling only if we're started
216 			if (fActive)
217 				device->Start();
218 			input_device_ref device = { (char *)"Wacom Tablet",
219 				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