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*
instantiate_input_device()38 instantiate_input_device()
39 {
40 return (new MasterServerDevice());
41 }
42
43 // constructor
MasterServerDevice()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
~MasterServerDevice()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
InitCheck()87 MasterServerDevice::InitCheck()
88 {
89 return BInputServerDevice::InitCheck();
90 }
91
92 // SystemShuttingDown
93 status_t
SystemShuttingDown()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
Start(const char * device,void * cookie)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
Stop(const char * device,void * cookie)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
Control(const char * device,void * cookie,uint32 code,BMessage * message)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
_SearchDevices()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
_StopAll()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
_AddDevice(const char * path)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
_HandleNodeMonitor(BMessage * message)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
_CalculateAccelerationTable()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
_LockDevices()416 MasterServerDevice::_LockDevices()
417 {
418 return fDeviceLock.Lock();
419 }
420
421 // _UnlockDevices
422 void
_UnlockDevices()423 MasterServerDevice::_UnlockDevices()
424 {
425 fDeviceLock.Unlock();
426 }
427
428