1/*
2 * Copyright 2021, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "VirtioInputDevice.h"
8
9#include <virtio_input_driver.h>
10#include <virtio_defs.h>
11
12#include <stdio.h>
13#include <string.h>
14
15#include <Application.h>
16#include <String.h>
17
18
19//#define TRACE_VIRTIO_INPUT_DEVICE
20#ifdef TRACE_VIRTIO_INPUT_DEVICE
21#       define TRACE(x...) debug_printf("virtio_input_device: " x)
22#else
23#       define TRACE(x...) ;
24#endif
25#define ERROR(x...) debug_printf("virtio_input_device: " x)
26#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
27
28
29enum {
30	kWatcherThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4,
31};
32
33
34template<typename Type>
35inline static void SetBit(Type &val, int bit) {val |= Type(1) << bit;}
36
37template<typename Type>
38inline static void ClearBit(Type &val, int bit) {val &= ~(Type(1) << bit);}
39
40template<typename Type>
41inline static void InvertBit(Type &val, int bit) {val ^= Type(1) << bit;}
42
43template<typename Type>
44inline static void SetBitTo(Type &val, int bit, bool isSet) {
45	val ^= ((isSet? -1: 0) ^ val) & (Type(1) << bit);}
46
47template<typename Type>
48inline static bool IsBitSet(Type val, int bit) {
49	return (val & (Type(1) << bit)) != 0;}
50
51
52#ifdef TRACE_VIRTIO_INPUT_DEVICE
53static void WriteInputPacket(const VirtioInputPacket &pkt)
54{
55	switch (pkt.type) {
56		case kVirtioInputEvSyn:
57			TRACE("syn");
58			break;
59		case kVirtioInputEvKey:
60			TRACE("key, ");
61			switch (pkt.code) {
62				case kVirtioInputBtnLeft:
63					TRACE("left");
64					break;
65				case kVirtioInputBtnRight:
66					TRACE("middle");
67					break;
68				case kVirtioInputBtnMiddle:
69					TRACE("right");
70					break;
71				case kVirtioInputBtnGearDown:
72					TRACE("gearDown");
73					break;
74				case kVirtioInputBtnGearUp:
75					TRACE("gearUp");
76					break;
77				default:
78					TRACE("%d", pkt.code);
79			}
80			break;
81		case kVirtioInputEvRel:
82			TRACE("rel, ");
83			switch (pkt.code) {
84				case kVirtioInputRelX:
85					TRACE("relX");
86					break;
87				case kVirtioInputRelY:
88					TRACE("relY");
89					break;
90				case kVirtioInputRelZ:
91					TRACE("relZ");
92					break;
93				case kVirtioInputRelWheel:
94					TRACE("relWheel");
95					break;
96				default:
97					TRACE("%d", pkt.code);
98			}
99			break;
100		case kVirtioInputEvAbs:
101			TRACE("abs, ");
102			switch (pkt.code) {
103				case kVirtioInputAbsX:
104					TRACE("absX");
105					break;
106				case kVirtioInputAbsY:
107					TRACE("absY");
108					break;
109				case kVirtioInputAbsZ:
110					TRACE("absZ");
111					break;
112				default:
113					TRACE("%d", pkt.code);
114			}
115			break;
116		case kVirtioInputEvRep:
117			TRACE("rep");
118			break;
119		default:
120			TRACE("?(%d)", pkt.type);
121	}
122	switch (pkt.type) {
123		case kVirtioInputEvSyn:
124			break;
125		case kVirtioInputEvKey:
126			TRACE(", ");
127			if (pkt.value == 0) {
128				TRACE("up");
129			} else if (pkt.value == 1) {
130				TRACE("down");
131			} else {
132				TRACE("%d", pkt.value);
133			}
134			break;
135		default:
136			TRACE(", ");
137			TRACE("%d", pkt.value);
138	}
139}
140#endif /* TRACE_VIRTIO_INPUT_DEVICE */
141
142
143//#pragma mark VirtioInputDevice
144
145
146VirtioInputDevice::VirtioInputDevice()
147{
148}
149
150VirtioInputDevice::~VirtioInputDevice()
151{
152}
153
154
155status_t
156VirtioInputDevice::InitCheck()
157{
158	static input_device_ref *devices[3];
159	input_device_ref **devicesEnd = devices;
160
161	FileDescriptorCloser fd;
162
163	// TODO: dynamically scan and detect device type
164
165	ObjectDeleter<VirtioInputHandler> tablet(
166		new TabletHandler(this, "VirtIO tablet"));
167	fd.SetTo(open("/dev/input/virtio/0/raw", O_RDWR));
168	if (fd.IsSet()) {
169		tablet->SetFd(fd.Detach());
170		*devicesEnd++ = tablet->Ref();
171		tablet.Detach();
172	} else {
173		TRACE("Unable to detect tablet device!");
174	}
175
176	ObjectDeleter<VirtioInputHandler> keyboard(
177		new KeyboardHandler(this, "VirtIO keyboard"));
178	fd.SetTo(open("/dev/input/virtio/1/raw", O_RDWR));
179	if (fd.IsSet()) {
180		keyboard->SetFd(fd.Detach());
181		*devicesEnd++ = keyboard->Ref();
182		keyboard.Detach();
183	} else {
184		TRACE("Unable to detect keyboard device!");
185	}
186
187	*devicesEnd = NULL;
188
189	RegisterDevices(devices);
190	return B_OK;
191}
192
193
194status_t
195VirtioInputDevice::Start(const char* name, void* cookie)
196{
197	return ((VirtioInputHandler*)cookie)->Start();
198}
199
200
201status_t
202VirtioInputDevice::Stop(const char* name, void* cookie)
203{
204	return ((VirtioInputHandler*)cookie)->Stop();
205}
206
207
208status_t
209VirtioInputDevice::Control(const char* name, void* cookie, uint32 command,
210	BMessage* message)
211{
212	return ((VirtioInputHandler*)cookie)->Control(command, message);
213}
214
215
216//#pragma mark VirtioInputHandler
217
218
219VirtioInputHandler::VirtioInputHandler(VirtioInputDevice* dev, const char* name,
220	input_device_type type)
221	:
222	fDev(dev),
223	fWatcherThread(B_ERROR),
224	fRun(false)
225{
226	fRef.name = (char*)name; // NOTE: name should be constant data
227	fRef.type = type;
228	fRef.cookie = this;
229}
230
231
232VirtioInputHandler::~VirtioInputHandler()
233{
234
235}
236
237
238void
239VirtioInputHandler::SetFd(int fd)
240{
241	fDeviceFd.SetTo(fd);
242}
243
244
245status_t
246VirtioInputHandler::Start()
247{
248	char threadName[B_OS_NAME_LENGTH];
249	snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fRef.name);
250
251	if (fWatcherThread < 0) {
252		fWatcherThread = spawn_thread(Watcher, threadName,
253			kWatcherThreadPriority, this);
254
255		if (fWatcherThread < B_OK)
256			return fWatcherThread;
257
258		fRun = true;
259		resume_thread(fWatcherThread);
260	}
261	return B_OK;
262}
263
264
265status_t
266VirtioInputHandler::Stop()
267{
268	// TODO: Use condition variable to sync access? suspend_thread
269	// avoids a race condition so it doesn't exit before wait_for_thread
270
271	if (fWatcherThread >= B_OK) {
272		// ioctl(fDeviceFd.Get(), virtioInputCancelIO, NULL, 0);
273		suspend_thread(fWatcherThread);
274		fRun = false;
275		status_t res;
276		wait_for_thread(fWatcherThread, &res);
277		fWatcherThread = B_ERROR;
278	}
279	return B_OK;
280}
281
282
283status_t
284VirtioInputHandler::Control(uint32 command, BMessage* message)
285{
286	return B_OK;
287}
288
289
290int32
291VirtioInputHandler::Watcher(void *arg)
292{
293	VirtioInputHandler &handler = *((VirtioInputHandler*)arg);
294	handler.Reset();
295	while (handler.fRun) {
296		VirtioInputPacket pkt;
297		status_t res = ioctl(handler.fDeviceFd.Get(), virtioInputRead, &pkt,
298			sizeof(pkt));
299		// if (res == B_CANCELED) return B_OK;
300		if (res < B_OK)
301			continue;
302		handler.PacketReceived(pkt);
303	}
304	return B_OK;
305}
306
307
308//#pragma mark KeyboardHandler
309
310
311KeyboardHandler::KeyboardHandler(VirtioInputDevice* dev, const char* name)
312	:
313	VirtioInputHandler(dev, name, B_KEYBOARD_DEVICE),
314	fRepeatThread(-1),
315	fRepeatThreadSem(-1)
316{
317	TRACE("+KeyboardHandler()\n");
318	{
319		// TODO: Similar to B_KEY_MAP_CHANGED below?
320		key_map *keyMap = NULL;
321		char *chars = NULL;
322		get_key_map(&keyMap, &chars);
323		fKeyMap.SetTo(keyMap);
324		fChars.SetTo(chars);
325	}
326	TRACE("  fKeymap: %p\n", fKeyMap.Get());
327	TRACE("  fChars: %p\n", fChars.Get());
328	get_key_repeat_delay(&fRepeatDelay);
329	get_key_repeat_rate (&fRepeatRate);
330	TRACE("  fRepeatDelay: %" B_PRIdBIGTIME "\n", fRepeatDelay);
331	TRACE("  fRepeatRate: % " B_PRId32 "\n", fRepeatRate);
332
333	if (fRepeatRate < 1)
334		fRepeatRate = 1;
335}
336
337
338KeyboardHandler::~KeyboardHandler()
339{
340	_StopRepeating();
341}
342
343
344void
345KeyboardHandler::Reset()
346{
347	memset(&fNewState, 0, sizeof(KeyboardState));
348	memcpy(&fState, &fNewState, sizeof(KeyboardState));
349	_StopRepeating();
350}
351
352
353status_t
354KeyboardHandler::Control(uint32 command, BMessage* message)
355{
356	switch (command) {
357		case B_KEY_MAP_CHANGED: {
358			key_map *keyMap = NULL;
359			char *chars = NULL;
360			get_key_map(&keyMap, &chars);
361			if (keyMap == NULL || chars == NULL)
362				return B_NO_MEMORY;
363			fKeyMap.SetTo(keyMap);
364			fChars.SetTo(chars);
365			return B_OK;
366		}
367		case B_KEY_REPEAT_DELAY_CHANGED:
368			get_key_repeat_delay(&fRepeatDelay);
369			TRACE("  fRepeatDelay: %" B_PRIdBIGTIME "\n", fRepeatDelay);
370			return B_OK;
371		case B_KEY_REPEAT_RATE_CHANGED:
372			get_key_repeat_rate(&fRepeatRate);
373			TRACE("  fRepeatRate: %" B_PRId32 "\n", fRepeatRate);
374			if (fRepeatRate < 1) fRepeatRate = 1;
375			return B_OK;
376	}
377	return VirtioInputHandler::Control(command, message);
378}
379
380
381void
382KeyboardHandler::PacketReceived(const VirtioInputPacket &pkt)
383{
384#ifdef TRACE_VIRTIO_INPUT_DEVICE
385	TRACE("keyboard: ");
386	WriteInputPacket(pkt);
387	TRACE("\n");
388#endif
389	switch (pkt.type) {
390		case kVirtioInputEvKey: {
391			if (pkt.code < 256)
392				SetBitTo(fNewState.keys[pkt.code / 8], pkt.code % 8,
393					pkt.value != 0);
394			break;
395		}
396		case kVirtioInputEvSyn: {
397			fState.when = system_time();
398			_StateChanged();
399		}
400	}
401}
402
403
404bool
405KeyboardHandler::_IsKeyPressed(const KeyboardState &state, uint32 key)
406{
407	return key < 256 && IsBitSet(state.keys[key / 8], key % 8);
408}
409
410
411void
412KeyboardHandler::_KeyString(uint32 code, char *str, size_t len)
413{
414	char *ch;
415	switch (fNewState.modifiers & (
416		B_SHIFT_KEY | B_CONTROL_KEY | B_OPTION_KEY | B_CAPS_LOCK)) {
417		case B_OPTION_KEY | B_CAPS_LOCK | B_SHIFT_KEY:
418			ch = fChars.Get() + fKeyMap->option_caps_shift_map[code];
419			break;
420		case B_OPTION_KEY | B_CAPS_LOCK:
421			ch = fChars.Get() + fKeyMap->option_caps_map[code];
422			break;
423		case B_OPTION_KEY | B_SHIFT_KEY:
424			ch = fChars.Get() + fKeyMap->option_shift_map[code];
425			break;
426		case B_OPTION_KEY:
427			ch = fChars.Get() + fKeyMap->option_map[code];
428			break;
429		case B_CAPS_LOCK  | B_SHIFT_KEY:
430			ch = fChars.Get() + fKeyMap->caps_shift_map[code];
431			break;
432		case B_CAPS_LOCK:
433			ch = fChars.Get() + fKeyMap->caps_map[code];
434			break;
435		case B_SHIFT_KEY:
436			ch = fChars.Get() + fKeyMap->shift_map[code];
437			break;
438		default:
439			if ((fNewState.modifiers & B_CONTROL_KEY) != 0)
440				ch = fChars.Get() + fKeyMap->control_map[code];
441			else
442				ch = fChars.Get() + fKeyMap->normal_map[code];
443	}
444	if (len > 0) {
445		uint32 i;
446		for (i = 0; (i < (uint32)ch[0]) && (i < len - 1); i++)
447			str[i] = ch[i + 1];
448		str[i] = '\0';
449	}
450}
451
452
453void
454KeyboardHandler::_StartRepeating(BMessage* msg)
455{
456	if (fRepeatThread >= B_OK)
457		_StopRepeating();
458
459	fRepeatMsg = *msg;
460	fRepeatThread = spawn_thread(_RepeatThread, "repeat thread",
461		B_REAL_TIME_DISPLAY_PRIORITY + 4, this);
462	fRepeatThreadSem = create_sem(0, "repeat thread sem");
463	if (fRepeatThread >= B_OK)
464		resume_thread(fRepeatThread);
465}
466
467
468void
469KeyboardHandler::_StopRepeating()
470{
471	if (fRepeatThread >= B_OK) {
472		status_t res;
473		release_sem(fRepeatThreadSem);
474		wait_for_thread(fRepeatThread, &res);
475		fRepeatThread = -1;
476		delete_sem(fRepeatThreadSem);
477		fRepeatThreadSem = -1;
478	}
479}
480
481
482status_t
483KeyboardHandler::_RepeatThread(void *arg)
484{
485	status_t res;
486	KeyboardHandler *h = (KeyboardHandler*)arg;
487
488	res = acquire_sem_etc(h->fRepeatThreadSem, 1, B_RELATIVE_TIMEOUT,
489		h->fRepeatDelay);
490	if (res >= B_OK)
491		return B_OK;
492
493	while (true) {
494		int32 count;
495
496		h->fRepeatMsg.ReplaceInt64("when", system_time());
497		h->fRepeatMsg.FindInt32("be:key_repeat", &count);
498		h->fRepeatMsg.ReplaceInt32("be:key_repeat", count + 1);
499
500		ObjectDeleter<BMessage> msg(new(std::nothrow) BMessage(h->fRepeatMsg));
501		if (msg.IsSet() && h->Device()->EnqueueMessage(msg.Get()) >= B_OK)
502			msg.Detach();
503
504		res = acquire_sem_etc(h->fRepeatThreadSem, 1, B_RELATIVE_TIMEOUT,
505			(bigtime_t)10000000 / h->fRepeatRate);
506		if (res >= B_OK)
507			return B_OK;
508	}
509}
510
511
512void
513KeyboardHandler::_StateChanged()
514{
515	uint32 i, j;
516
517	fNewState.modifiers = fState.modifiers
518		& (B_CAPS_LOCK | B_SCROLL_LOCK | B_NUM_LOCK);
519	if (_IsKeyPressed(fNewState, fKeyMap->left_shift_key))
520		fNewState.modifiers |= B_SHIFT_KEY   | B_LEFT_SHIFT_KEY;
521	if (_IsKeyPressed(fNewState, fKeyMap->right_shift_key))
522		fNewState.modifiers |= B_SHIFT_KEY   | B_RIGHT_SHIFT_KEY;
523	if (_IsKeyPressed(fNewState, fKeyMap->left_command_key))
524		fNewState.modifiers |= B_COMMAND_KEY | B_LEFT_COMMAND_KEY;
525	if (_IsKeyPressed(fNewState, fKeyMap->right_command_key))
526		fNewState.modifiers |= B_COMMAND_KEY | B_RIGHT_COMMAND_KEY;
527	if (_IsKeyPressed(fNewState, fKeyMap->left_control_key))
528		fNewState.modifiers |= B_CONTROL_KEY | B_LEFT_CONTROL_KEY;
529	if (_IsKeyPressed(fNewState, fKeyMap->right_control_key))
530		fNewState.modifiers |= B_CONTROL_KEY | B_RIGHT_CONTROL_KEY;
531	if (_IsKeyPressed(fNewState, fKeyMap->caps_key))
532		fNewState.modifiers ^= B_CAPS_LOCK;
533	if (_IsKeyPressed(fNewState, fKeyMap->scroll_key))
534		fNewState.modifiers ^= B_SCROLL_LOCK;
535	if (_IsKeyPressed(fNewState, fKeyMap->num_key))
536		fNewState.modifiers ^= B_NUM_LOCK;
537	if (_IsKeyPressed(fNewState, fKeyMap->left_option_key))
538		fNewState.modifiers |= B_OPTION_KEY  | B_LEFT_OPTION_KEY;
539	if (_IsKeyPressed(fNewState, fKeyMap->right_option_key))
540		fNewState.modifiers |= B_OPTION_KEY  | B_RIGHT_OPTION_KEY;
541	if (_IsKeyPressed(fNewState, fKeyMap->menu_key))
542		fNewState.modifiers |= B_MENU_KEY;
543
544	if (fState.modifiers != fNewState.modifiers) {
545		ObjectDeleter<BMessage> msg(
546			new(std::nothrow) BMessage(B_MODIFIERS_CHANGED));
547		if (msg.IsSet()) {
548			msg->AddInt64("when", system_time());
549			msg->AddInt32("modifiers", fNewState.modifiers);
550			msg->AddInt32("be:old_modifiers", fState.modifiers);
551			msg->AddData("states", B_UINT8_TYPE, fNewState.keys, 16);
552
553			if (Device()->EnqueueMessage(msg.Get()) >= B_OK) {
554				msg.Detach();
555				fState.modifiers = fNewState.modifiers;
556			}
557		}
558	}
559
560
561	uint8 diff[16];
562	char rawCh;
563	char str[5];
564
565	for (i = 0; i < 16; ++i)
566		diff[i] = fState.keys[i] ^ fNewState.keys[i];
567
568	for (i = 0; i < 128; ++i) {
569		if (diff[i/8] & (1 << (i % 8))) {
570			ObjectDeleter<BMessage> msg(new(std::nothrow) BMessage());
571			if (msg.IsSet()) {
572				_KeyString(i, str, sizeof(str));
573
574				msg->AddInt64("when", system_time());
575				msg->AddInt32("key", i);
576				msg->AddInt32("modifiers", fNewState.modifiers);
577				msg->AddData("states", B_UINT8_TYPE, fNewState.keys, 16);
578
579				if (str[0] != '\0') {
580					if (fChars.Get()[fKeyMap->normal_map[i]] != 0)
581						rawCh = fChars.Get()[fKeyMap->normal_map[i] + 1];
582					else
583						rawCh = str[0];
584
585					for (j = 0; str[j] != '\0'; ++j)
586						msg->AddInt8("byte", str[j]);
587
588					msg->AddString("bytes", str);
589					msg->AddInt32("raw_char", rawCh);
590				}
591
592				if (fNewState.keys[i / 8] & (1 << (i % 8))) {
593					if (str[0] != '\0')
594						msg->what = B_KEY_DOWN;
595					else
596						msg->what = B_UNMAPPED_KEY_DOWN;
597
598					msg->AddInt32("be:key_repeat", 1);
599					_StartRepeating(msg.Get());
600				} else {
601					if (str[0] != '\0')
602						msg->what = B_KEY_UP;
603					else
604						msg->what = B_UNMAPPED_KEY_UP;
605
606					_StopRepeating();
607				}
608
609				if (Device()->EnqueueMessage(msg.Get()) >= B_OK) {
610					msg.Detach();
611					for (j = 0; j < 16; ++j)
612						fState.keys[j] = fNewState.keys[j];
613				}
614			}
615		}
616	}
617}
618
619
620//#pragma mark TabletHandler
621
622
623TabletHandler::TabletHandler(VirtioInputDevice* dev, const char* name)
624	:
625	VirtioInputHandler(dev, name, B_POINTING_DEVICE)
626{
627}
628
629
630void
631TabletHandler::Reset()
632{
633	memset(&fNewState, 0, sizeof(TabletState));
634	fNewState.x = 0.5f;
635	fNewState.y = 0.5f;
636	memcpy(&fState, &fNewState, sizeof(TabletState));
637	fLastClick = -1;
638	fLastClickBtn = -1;
639
640	get_click_speed(&fClickSpeed);
641	TRACE("  fClickSpeed: %" B_PRIdBIGTIME "\n", fClickSpeed);
642}
643
644
645status_t
646TabletHandler::Control(uint32 command, BMessage* message)
647{
648	switch (command) {
649		case B_CLICK_SPEED_CHANGED: {
650			get_click_speed(&fClickSpeed);
651			TRACE("  fClickSpeed: %" B_PRIdBIGTIME "\n", fClickSpeed);
652			return B_OK;
653		}
654	}
655	return VirtioInputHandler::Control(command, message);
656}
657
658
659void
660TabletHandler::PacketReceived(const VirtioInputPacket &pkt)
661{
662	switch (pkt.type) {
663		case kVirtioInputEvAbs: {
664			switch (pkt.code) {
665				case kVirtioInputAbsX:
666					fNewState.x = float(pkt.value) / 32768.0f;
667					break;
668				case kVirtioInputAbsY:
669					fNewState.y = float(pkt.value) / 32768.0f;
670					break;
671			}
672			break;
673		}
674		case kVirtioInputEvRel: {
675			switch (pkt.code) {
676				case kVirtioInputRelWheel:
677					fNewState.wheelY -= pkt.value;
678					break;
679			}
680			break;
681		}
682		case kVirtioInputEvKey: {
683			switch (pkt.code) {
684				case kVirtioInputBtnLeft:
685					SetBitTo(fNewState.buttons, 0, pkt.value != 0);
686					break;
687				case kVirtioInputBtnRight:
688					SetBitTo(fNewState.buttons, 1, pkt.value != 0);
689					break;
690				case kVirtioInputBtnMiddle:
691					SetBitTo(fNewState.buttons, 2, pkt.value != 0);
692					break;
693			}
694			break;
695		}
696		case kVirtioInputEvSyn: {
697			fState.when = system_time();
698
699			// update pos
700			if (fState.x != fNewState.x || fState.y != fNewState.y
701				|| fState.pressure != fNewState.pressure) {
702				fState.x = fNewState.x;
703				fState.y = fNewState.y;
704				fState.pressure = fNewState.pressure;
705				ObjectDeleter<BMessage> msg(
706					new(std::nothrow) BMessage(B_MOUSE_MOVED));
707				if (!msg.IsSet() || !_FillMessage(*msg.Get(), fState))
708					return;
709
710				if (Device()->EnqueueMessage(msg.Get()) >= B_OK)
711					msg.Detach();
712			}
713
714			// update buttons
715			for (int i = 0; i < 32; i++) {
716				if ((IsBitSet(fState.buttons, i)
717					!= IsBitSet(fNewState.buttons, i))) {
718					InvertBit(fState.buttons, i);
719
720					// TODO: new B_MOUSE_DOWN for every button clicked together?
721					// should be refactored to look like other input drivers.
722
723					ObjectDeleter<BMessage> msg(new(std::nothrow) BMessage());
724					if (!msg.IsSet() || !_FillMessage(*msg.Get(), fState))
725						return;
726
727					if (IsBitSet(fState.buttons, i)) {
728						msg->what = B_MOUSE_DOWN;
729						if (i == fLastClickBtn
730							&& fState.when - fLastClick <= fClickSpeed)
731							fState.clicks++;
732						else
733							fState.clicks = 1;
734						fLastClickBtn = i;
735						fLastClick = fState.when;
736						msg->AddInt32("clicks", fState.clicks);
737					} else
738						msg->what = B_MOUSE_UP;
739
740					if (Device()->EnqueueMessage(msg.Get()) >= B_OK)
741						msg.Detach();
742				}
743			}
744
745			// update wheel
746			if (fState.wheelX != fNewState.wheelX
747				|| fState.wheelY != fNewState.wheelY) {
748				ObjectDeleter<BMessage> msg(
749					new(std::nothrow) BMessage(B_MOUSE_WHEEL_CHANGED));
750				if (!msg.IsSet()
751					|| msg->AddInt64("when", fState.when) < B_OK
752					|| msg->AddFloat("be:wheel_delta_x",
753						fNewState.wheelX - fState.wheelX) < B_OK
754					|| msg->AddFloat("be:wheel_delta_y",
755						fNewState.wheelY - fState.wheelY) < B_OK) {
756					return;
757				}
758
759				fState.wheelX = fNewState.wheelX;
760				fState.wheelY = fNewState.wheelY;
761				if (Device()->EnqueueMessage(msg.Get()) >= B_OK)
762					msg.Detach();
763			}
764			break;
765		}
766	}
767}
768
769
770bool
771TabletHandler::_FillMessage(BMessage &msg, const TabletState &s)
772{
773	if (msg.AddInt64("when", s.when) < B_OK
774		|| msg.AddInt32("buttons", s.buttons) < B_OK
775		|| msg.AddFloat("x", s.x) < B_OK
776		|| msg.AddFloat("y", s.y) < B_OK) {
777		return false;
778	}
779	msg.AddFloat("be:tablet_x", s.x);
780	msg.AddFloat("be:tablet_y", s.y);
781	msg.AddFloat("be:tablet_pressure", s.pressure);
782	return true;
783}
784
785
786//#pragma mark -
787
788
789extern "C" BInputServerDevice*
790instantiate_input_device()
791{
792	return new(std::nothrow) VirtioInputDevice();
793}
794