The Pedigree Project 0.1
InputManager.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/compiler.h"
23#include "pedigree/kernel/machine/InputManager.h"
24#include "pedigree/kernel/process/Scheduler.h"
25#include "pedigree/kernel/process/TerminationDeferral.h"
26#include "pedigree/kernel/process/Thread.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/ProcessorInformation.h"
29#include "pedigree/kernel/utilities/Iterator.h"
30#include "pedigree/kernel/utilities/utility.h"
31
32// Incoming relative mouse movements are divided by this
33#define MOUSE_REDUCE_FACTOR 1
34
36
37#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
38InputManager::CallbackPinHook InputManager::m_CallbackPinHook = nullptr;
39#endif
40
42 : m_InputQueue(),
43 m_QueueLock(),
44 m_Callbacks()
45#if THREADS
46 ,
47 m_InputQueueSize(0),
48 m_pThread(0),
49 m_pCallbackDispatchThread(nullptr)
50#endif
51 ,
52 m_bActive(false) {
53}
54
56
58 m_bActive = true;
59
60// Start the worker thread.
61#if THREADS
62 m_pThread = new Thread(Processor::information().getCurrentThread()->getParent(), &trampoline,
63 reinterpret_cast<void*>(this));
64 m_pThread->setName("InputManager worker thread");
65#else
66 WARNING("InputManager: No thread support, no worker thread will be active");
67#endif
68}
69
71 m_bActive = false;
72
73#if THREADS
75 if (m_pThread) {
77 m_pThread = nullptr;
78 }
79#endif
80
81 // Clean up lists, in case anything came in while we were canceling.
82 Vector<CallbackItem*> callbacks;
83 Vector<InputNotification*> notifications;
85 while (m_Callbacks.count()) {
86 CallbackItem* item = m_Callbacks.popFront();
87#if THREADS
88 if (item->inFlight) {
89 FATAL("InputManager shut down with a pinned callback.");
90 }
91#endif
92 callbacks.pushBack(item);
93 }
94 while (m_InputQueue.count()) {
95 notifications.pushBack(m_InputQueue.popFront());
96 }
98
99 for (auto item : callbacks) {
100 delete item;
101 }
102 for (auto notification : notifications) {
103 delete notification;
104 }
105}
106
107void InputManager::keyPressed(uint64_t key) {
109 note->type = Key;
110 note->data.key.key = key;
111
112 putNotification(note);
113}
114
115void InputManager::rawKeyUpdate(uint8_t scancode, bool bKeyUp) {
117 note->type = RawKey;
118 note->data.rawkey.scancode = scancode;
119 note->data.rawkey.keyUp = bKeyUp;
120
121 putNotification(note);
122}
123
124void InputManager::machineKeyUpdate(uint8_t scancode, bool bKeyUp) {
126 note->type = MachineKey;
127 note->data.machinekey.scancode = scancode;
128 note->data.machinekey.keyUp = bKeyUp;
129
130 putNotification(note);
131}
132
133void InputManager::mouseUpdate(ssize_t relX, ssize_t relY, ssize_t relZ, uint32_t buttonBitmap) {
134 // Smooth input out
135 relX /= MOUSE_REDUCE_FACTOR;
136 relY /= MOUSE_REDUCE_FACTOR;
137 relZ /= MOUSE_REDUCE_FACTOR;
138
140 note->type = Mouse;
141 note->data.pointy.relx = relX;
142 note->data.pointy.rely = relY;
143 note->data.pointy.relz = relZ;
144 for (size_t i = 0; i < 64; i++)
145 note->data.pointy.buttons[i] = static_cast<uint64_t>(buttonBitmap) & (uint64_t{1} << i);
146
147 putNotification(note);
148}
149
150void InputManager::absoluteMouseUpdate(uint32_t x, uint32_t y, ssize_t wheel,
151 uint32_t buttonBitmap) {
153 note->type = AbsoluteMouse;
154 note->data.absolute.x = x > 0x7fff ? 0x7fff : x;
155 note->data.absolute.y = y > 0x7fff ? 0x7fff : y;
156 note->data.absolute.wheel = wheel;
157 for (size_t i = 0; i < 64; i++)
158 note->data.absolute.buttons[i] = static_cast<uint64_t>(buttonBitmap) & (uint64_t{1} << i);
159
160 putNotification(note);
161}
162
163void InputManager::joystickUpdate(ssize_t relX, ssize_t relY, ssize_t relZ, uint32_t buttonBitmap) {
165 note->type = Joystick;
166 note->data.pointy.relx = relX;
167 note->data.pointy.rely = relY;
168 note->data.pointy.relz = relZ;
169 for (size_t i = 0; i < 64; i++)
170 note->data.pointy.buttons[i] = static_cast<uint64_t>(buttonBitmap) & (uint64_t{1} << i);
171
172 putNotification(note);
173}
174
176#if THREADS
177 bool merged = false;
178 bool accepted = false;
180 if (m_bActive && m_Callbacks.count()) {
181 // Mitigation keeps at most one queued relative-mouse notification.
182 if (note->type == Mouse) {
183 for (auto queued : m_InputQueue) {
184 if (queued->type != Mouse) {
185 continue;
186 }
187
188 queued->data.pointy.relx += note->data.pointy.relx;
189 queued->data.pointy.rely += note->data.pointy.rely;
190 queued->data.pointy.relz += note->data.pointy.relz;
191 // Coalescing must preserve the newest button bitmap; ORing it would
192 // make a release disappear behind an earlier press.
193 for (size_t i = 0; i < 64; ++i)
194 queued->data.pointy.buttons[i] = note->data.pointy.buttons[i];
195 merged = true;
196 break;
197 }
198 } else if (note->type == AbsoluteMouse) {
199 InputNotification* latest = nullptr;
200 for (auto queued : m_InputQueue) {
201 latest = queued;
202 }
203 bool sameButtons = latest && latest->type == AbsoluteMouse;
204 for (size_t i = 0; i < 64 && sameButtons; ++i) {
205 sameButtons = latest->data.absolute.buttons[i] == note->data.absolute.buttons[i];
206 }
207 if (sameButtons) {
208 latest->data.absolute.x = note->data.absolute.x;
209 latest->data.absolute.y = note->data.absolute.y;
210 latest->data.absolute.wheel += note->data.absolute.wheel;
211 merged = true;
212 }
213 }
214
215 if (!merged) {
216 m_InputQueue.pushBack(note);
217 accepted = true;
218 }
219 }
221
222 if (merged) {
223 delete note;
224 return;
225 }
226 if (!accepted) {
227 if (note->type == Key) {
228 WARNING("InputManager: dropping key event with no consumer");
229 }
230 delete note;
231 return;
232 }
233
235#else
236 struct SynchronousCallback {
237 callback_t func;
238 void* meta;
239 };
241
243 for (auto item : m_Callbacks) {
244 if (item && (item->filter & note->type)) {
245 callbacks.pushBack({item->func, item->meta});
246 }
247 }
249
250 for (auto callback : callbacks) {
251 note->meta = callback.meta;
252 callback.func(*note);
253 }
254 delete note;
255#endif
256}
257
258void InputManager::installCallback(CallbackType filter, callback_t callback, void* meta) {
259 if (!callback) {
260 return;
261 }
262
263 CallbackItem* item = new CallbackItem;
264 item->func = callback;
265 item->filter = filter;
266 item->meta = meta;
267#if THREADS
268 item->inFlight = 0;
269 item->enabled = true;
270 item->draining = false;
271 item->removers = 0;
272 item->deferredRemoval = false;
273#endif
274
276 m_Callbacks.pushBack(item);
277}
278
279void InputManager::removeCallback(callback_t callback, void* meta) {
280#if THREADS
281 removeCallbacks(callback, meta);
282#else
284 for (List<CallbackItem*>::Iterator it = m_Callbacks.begin(); it != m_Callbacks.end();) {
285 if (*it) {
286 if ((callback == (*it)->func) && (meta == (*it)->meta)) {
287 delete *it;
288 it = m_Callbacks.erase(it);
289 continue;
290 }
291 }
292
293 ++it;
294 }
295#endif
296}
297
298#if THREADS
299void InputManager::removeCallbacks(callback_t callback, void* meta) {
300 TerminationDeferral terminationDeferral;
302 Vector<CallbackItem*> deleteNow;
303
304 Thread* current = Processor::information().getCurrentThread();
306 const bool callbackContext = current && current == m_pCallbackDispatchThread;
307
308 for (List<CallbackItem*>::Iterator it = m_Callbacks.begin(); it != m_Callbacks.end();) {
309 CallbackItem* item = *it;
310 const bool matches = item->func == callback && item->meta == meta;
311 if (!matches) {
312 ++it;
313 continue;
314 }
315
316 item->enabled = false;
317
318 if (callbackContext) {
319 if (!item->inFlight) {
320 it = m_Callbacks.erase(it);
321 deleteNow.pushBack(item);
322 } else {
323 // A callback cannot drain its own snapshot pin. Keep the
324 // disabled item discoverable so an external remover can take
325 // over the synchronous drain before the callback returns.
326 if (!item->draining) {
327 item->deferredRemoval = true;
328 }
329 ++it;
330 }
331 } else {
332 item->deferredRemoval = false;
333 item->draining = true;
334 ++item->removers;
335 drain.pushBack(item);
336 ++it;
337 }
338 }
340
341 for (auto item : deleteNow) {
342 delete item;
343 }
344 for (auto item : drain) {
345 drainCallback(item);
346 }
347}
348
349void InputManager::drainCallback(CallbackItem* item) {
350 while (true) {
351 bool complete = false;
352 {
353 auto waitGuard = item->drainWaiters.acquire();
355 if (!item->inFlight) {
356 complete = true;
358 } else {
360 const WaitQueue::WakeReason reason =
361 waitGuard.waitForCompletion(WaitQueue::Channel(item), Thread::CallbackDrain,
362 reinterpret_cast<uintptr_t>(item->func));
363 (void)reason;
364 }
365 }
366
367 if (complete) {
368 break;
369 }
370 }
371
372 bool deleteItem = false;
374 if (!item->removers) {
375 FATAL("InputManager callback remover underflow.");
376 }
377 --item->removers;
378 if (!item->removers) {
379 for (List<CallbackItem*>::Iterator it = m_Callbacks.begin(); it != m_Callbacks.end(); ++it) {
380 if (*it == item) {
381 m_Callbacks.erase(it);
382 deleteItem = true;
383 break;
384 }
385 }
386 }
388
389 if (deleteItem) {
390 delete item;
391 }
392}
393#endif
394
396 InputManager* p = reinterpret_cast<InputManager*>(ptr);
397 p->mainThread();
398 return 0;
399}
400
402#if THREADS
403 while (isActive()) {
404 if (!m_InputQueueSize.acquire()) {
405 if (Processor::information().getCurrentThread()->getUnwindState() != Thread::Continue) {
406 return;
407 }
408 continue;
409 }
410
411 Vector<CallbackItem*> callbacks;
412 InputNotification* note = nullptr;
413
415 if (m_InputQueue.count()) {
416 note = m_InputQueue.popFront();
417 }
418 if (note) {
419 for (auto item : m_Callbacks) {
420 if (item && item->enabled && (item->filter & note->type)) {
421 ++item->inFlight;
422 callbacks.pushBack(item);
423 }
424 }
425 }
427
428 if (!note) {
429 continue;
430 }
431
432 if (!callbacks.count()) {
433 if (note->type == Key) {
434 WARNING("InputManager: dropping key event with no consumer");
435 }
436 delete note;
437 continue;
438 }
439
440 TerminationDeferral dispatchDeferral;
441 Thread* current = Processor::information().getCurrentThread();
442 for (auto item : callbacks) {
443 callback_t func = nullptr;
444 void* meta = nullptr;
445
447 if (item->enabled) {
448 func = item->func;
449 meta = item->meta;
451 }
453
454 if (func) {
455#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
456 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
457 if (hook) {
458 hook(func, meta);
459 }
460#endif
461
462 note->meta = meta;
463 func(*note);
464 }
465
466 bool deleteDeferred = false;
467 {
468 auto completionGuard = item->drainWaiters.acquire();
469 bool wakeDrainer = false;
471 if (func) {
473 }
474 if (!item->inFlight) {
475 FATAL("InputManager callback pin underflow.");
476 }
477 --item->inFlight;
478 if (!item->inFlight) {
479 wakeDrainer = item->draining;
480 deleteDeferred = item->deferredRemoval;
481 if (deleteDeferred) {
482 for (List<CallbackItem*>::Iterator it = m_Callbacks.begin(); it != m_Callbacks.end();
483 ++it) {
484 if (*it == item) {
485 m_Callbacks.erase(it);
486 break;
487 }
488 }
489 }
490 }
492
493 if (wakeDrainer) {
494 completionGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(item));
495 }
496 }
497 if (deleteDeferred) {
498 delete item;
499 }
500 }
501
502 // Yield before processing the next notification.
504
505 delete note;
506 }
507#endif
508}
509
510#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
511void InputManager::setCallbackPinHook(CallbackPinHook hook) {
512 __atomic_store_n(&m_CallbackPinHook, hook, __ATOMIC_RELEASE);
513}
514#endif
Spinlock m_QueueLock
static const int Key
void shutdown()
Shuts down the worker thread, clears queues, and removes callbacks.
Thread * m_pCallbackDispatchThread
Set only while the input worker is executing a committed callback.
static InputManager m_Instance
Static instance.
Atomic< bool > m_bActive
Are we active?
void machineKeyUpdate(uint8_t scancode, bool bKeyUp)
Called whenever a machine-specific key scancode comes in.
void(* callback_t)(InputNotification &)
Callback function type.
bool isActive() const
Returns whether the instance is creating notifications.
Semaphore m_InputQueueSize
Key press queue Semaphore.
void absoluteMouseUpdate(uint32_t x, uint32_t y, ssize_t wheel, uint32_t buttonBitmap)
Position coordinates use the evdev 0..0x7fff absolute pointer range.
void installCallback(CallbackType filter, callback_t callback, void *meta=0)
Installs a callback.
void putNotification(InputNotification *note)
InputManager()
Default constructor.
void removeCallback(callback_t callback, void *meta=0)
Removes a callback.
Thread * m_pThread
Thread object for our worker thread.
List< InputNotification * > m_InputQueue
Input queue (for distribution to applications)
void rawKeyUpdate(uint8_t scancode, bool bKeyUp)
void mouseUpdate(ssize_t relX, ssize_t relY, ssize_t relZ, uint32_t buttonBitmap)
Called whenever mouse input comes in.
static int trampoline(void *ptr)
Thread trampoline.
void mainThread()
Main worker thread.
void initialise()
Begins the worker thread.
List< CallbackItem * > m_Callbacks
Callback list.
virtual ~InputManager()
Default destructor.
void keyPressed(uint64_t key)
Called whenever a key is pressed and needs to be added to the queue.
void joystickUpdate(ssize_t relX, ssize_t relY, ssize_t relZ, uint32_t buttonBitmap)
Called whenever joystick input comes in.
::Iterator< T, node_t > Iterator
Definition List.h:67
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
void release(size_t n=1)
Definition Semaphore.cc:549
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:355
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
bool joinForCompletion()
Definition Thread.cc:2750
A vector / dynamic array.
Definition Vector.h:33
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270
Item in the kernel callback list.
void * meta
Meta pointer for the InputNotifications we generate.
bool draining
An external remover owns final deletion after draining inFlight.
callback_t func
The handler function.
CallbackType filter
Filter for this callback.
size_t inFlight
Pins held by a worker snapshot or an active callback invocation.
size_t removers
External removers which must all finish before final deletion.
bool deferredRemoval
The input worker must delete this item after self-removal drains.
bool enabled
No new invocation may begin after this becomes false.
bool keyUp
Whether this is a keyUp event or not.
uint8_t scancode
Machine-specific scancode for the key.