1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 - 2012 Research In Motion <blackberry-qt@qnx.com>
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 //#define QBBSCREENEVENTHANDLER_DEBUG
43
44 #include "qbbscreeneventhandler.h"
45
46 #include "qbbscreen.h"
47 #include "qbbintegration.h"
48 #include "qbbinputcontext.h"
49 #include "qbbkeytranslator.h"
50
51 #include <QApplication>
52 #include <QDebug>
53
54 #include <errno.h>
55 #include <sys/keycodes.h>
56
57 QT_BEGIN_NAMESPACE
58
QBBScreenEventHandler(QBBIntegration * integration)59 QBBScreenEventHandler::QBBScreenEventHandler(QBBIntegration *integration)
60 : mBBIntegration(integration)
61 , mLastButtonState(Qt::NoButton)
62 , mLastMouseWindow(0)
63 {
64 // initialize array of touch points
65 for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
66
67 // map array index to id
68 mTouchPoints[i].id = i;
69
70 // first finger is primary
71 mTouchPoints[i].isPrimary = (i == 0);
72
73 // pressure is not supported - use default
74 mTouchPoints[i].pressure = 1.0;
75
76 // nothing touching
77 mTouchPoints[i].state = Qt::TouchPointReleased;
78 }
79 }
80
handleEvent(screen_event_t event)81 bool QBBScreenEventHandler::handleEvent(screen_event_t event)
82 {
83 // get the event type
84 errno = 0;
85 int qnxType;
86 int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType);
87 if (result)
88 qFatal("QBB: failed to query event type, errno=%d", errno);
89
90 return handleEvent(event, qnxType);
91 }
92
93
handleEvent(screen_event_t event,int qnxType)94 bool QBBScreenEventHandler::handleEvent(screen_event_t event, int qnxType)
95 {
96 switch (qnxType) {
97 case SCREEN_EVENT_MTOUCH_TOUCH:
98 case SCREEN_EVENT_MTOUCH_MOVE:
99 case SCREEN_EVENT_MTOUCH_RELEASE:
100 handleTouchEvent(event, qnxType);
101 break;
102
103 case SCREEN_EVENT_KEYBOARD:
104 handleKeyboardEvent(event);
105 break;
106
107 case SCREEN_EVENT_POINTER:
108 handlePointerEvent(event);
109 break;
110
111 case SCREEN_EVENT_CLOSE:
112 handleCloseEvent(event);
113 break;
114
115 case SCREEN_EVENT_CREATE:
116 handleCreateEvent(event);
117 break;
118
119 case SCREEN_EVENT_DISPLAY:
120 handleDisplayEvent(event);
121 break;
122
123 default:
124 // event ignored
125 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
126 qDebug() << "QBB: QNX unknown event";
127 #endif
128 return false;
129 }
130
131 return true;
132 }
133
handleKeyboardEvent(screen_event_t event)134 void QBBScreenEventHandler::handleKeyboardEvent(screen_event_t event)
135 {
136 // get flags of key event
137 errno = 0;
138 int flags;
139 int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_FLAGS, &flags);
140 if (result) {
141 qFatal("QBB: failed to query event flags, errno=%d", errno);
142 }
143
144 // get key code
145 errno = 0;
146 int sym;
147 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SYM, &sym);
148 if (result) {
149 qFatal("QBB: failed to query event sym, errno=%d", errno);
150 }
151
152 int modifiers;
153 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_MODIFIERS, &modifiers);
154 if (result) {
155 qFatal("QBB: failed to query event modifiers, errno=%d", errno);
156 }
157
158 int scan;
159 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SCAN, &scan);
160 if (result) {
161 qFatal("QBB: failed to query event modifiers, errno=%d", errno);
162 }
163
164 int cap;
165 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap);
166 if (result) {
167 qFatal("QBB: failed to query event cap, errno=%d", errno);
168 }
169
170 // Let the input context have a stab at it first.
171 QWidget *focusWidget = QApplication::focusWidget();
172 if (focusWidget) {
173 QInputContext* inputContext = focusWidget->inputContext();
174 if (inputContext) {
175 QBBInputContext *qbbInputContext = dynamic_cast<QBBInputContext *>(inputContext);
176
177 if (qbbInputContext && qbbInputContext->handleKeyboardEvent(flags, sym, modifiers, scan, cap))
178 return;
179 }
180 }
181
182 injectKeyboardEvent(flags, sym, modifiers, scan, cap);
183 }
184
injectKeyboardEvent(int flags,int sym,int modifiers,int scan,int cap)185 void QBBScreenEventHandler::injectKeyboardEvent(int flags, int sym, int modifiers, int scan, int cap)
186 {
187 Q_UNUSED(scan);
188
189 Qt::KeyboardModifiers qtMod = Qt::NoModifier;
190 if (modifiers & KEYMOD_SHIFT)
191 qtMod |= Qt::ShiftModifier;
192 if (modifiers & KEYMOD_CTRL)
193 qtMod |= Qt::ControlModifier;
194 if (modifiers & KEYMOD_ALT)
195 qtMod |= Qt::AltModifier;
196
197 // determine event type
198 QEvent::Type type = (flags & KEY_DOWN) ? QEvent::KeyPress : QEvent::KeyRelease;
199
200 // Check if the key cap is valid
201 if (flags & KEY_CAP_VALID) {
202 Qt::Key key;
203 QString keyStr;
204
205 if (cap >= 0x20 && cap <= 0x0ff) {
206 key = Qt::Key(std::toupper(cap)); // Qt expects the CAP to be upper case.
207
208 if ( qtMod & Qt::ControlModifier ) {
209 keyStr = QChar((int)(key & 0x3f));
210 } else {
211 if (flags & KEY_SYM_VALID) {
212 keyStr = QChar(sym);
213 }
214 }
215 } else if ((cap > 0x0ff && cap < UNICODE_PRIVATE_USE_AREA_FIRST) || cap > UNICODE_PRIVATE_USE_AREA_LAST) {
216 key = (Qt::Key)cap;
217 keyStr = QChar(sym);
218 } else {
219 if (isKeypadKey(cap))
220 qtMod |= Qt::KeypadModifier; // Is this right?
221 key = keyTranslator(cap);
222 }
223
224 QWindowSystemInterface::handleExtendedKeyEvent(QApplication::activeWindow(), type, key, qtMod,
225 scan, sym, modifiers, keyStr);
226 #if defined(QBBScreenEventHandler_DEBUG)
227 qDebug() << "QBB: Qt key t=" << type << ", k=" << key << ", s=" << keyStr;
228 #endif
229 }
230 }
231
injectPointerMoveEvent(int x,int y)232 void QBBScreenEventHandler::injectPointerMoveEvent(int x, int y)
233 {
234 #if defined(QBBScreenEventHandler_DEBUG)
235 qDebug() << "Injecting mouse event..." << x << y;
236 #endif
237
238 QWidget *w = qApp->topLevelAt(x, y);
239 void *qnxWindow = w ? w->platformWindow() : 0;
240
241 // Generate enter and leave events as needed.
242 if (qnxWindow != mLastMouseWindow) {
243 QWidget* wOld = QWidget::find( (WId)mLastMouseWindow );
244
245 if (wOld) {
246 QWindowSystemInterface::handleLeaveEvent(wOld);
247 #if defined(QBBScreenEventHandler_DEBUG)
248 qDebug() << "QBB: Qt leave, w=" << wOld;
249 #endif
250 }
251
252 if (w) {
253 QWindowSystemInterface::handleEnterEvent(w);
254 #if defined(QBBScreenEventHandler_DEBUG)
255 qDebug() << "QBB: Qt enter, w=" << w;
256 #endif
257 }
258 }
259
260 mLastMouseWindow = qnxWindow;
261
262 // convert point to local coordinates
263 QPoint globalPoint(x, y);
264 QPoint localPoint(x, y);
265
266 if (w)
267 localPoint = QPoint(x - w->x(), y - w->y());
268
269 // Convert buttons.
270 Qt::MouseButtons buttons = mLastButtonState;
271
272 if (w) {
273 // Inject mouse event into Qt only if something has changed.
274 if (mLastGlobalMousePoint != globalPoint ||
275 mLastLocalMousePoint != localPoint ||
276 mLastButtonState != buttons) {
277 QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons);
278 #if defined(QBBScreenEventHandler_DEBUG)
279 qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << (int)buttons;
280 #endif
281 }
282 }
283
284 mLastGlobalMousePoint = globalPoint;
285 mLastLocalMousePoint = localPoint;
286 mLastButtonState = buttons;
287 }
288
handlePointerEvent(screen_event_t event)289 void QBBScreenEventHandler::handlePointerEvent(screen_event_t event)
290 {
291 errno = 0;
292
293 // Query the window that was clicked
294 void *qnxWindow;
295 int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &qnxWindow);
296 if (result) {
297 qFatal("QBB: failed to query event window, errno=%d", errno);
298 }
299
300 // Query the button states
301 int buttonState = 0;
302 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttonState);
303 if (result) {
304 qFatal("QBB: failed to query event button state, errno=%d", errno);
305 }
306
307 // Query the window position
308 int windowPos[2];
309 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos);
310 if (result) {
311 qFatal("QBB: failed to query event window position, errno=%d", errno);
312 }
313
314 // Query the screen position
315 int pos[2];
316 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos);
317 if (result) {
318 qFatal("QBB: failed to query event position, errno=%d", errno);
319 }
320
321 // Query the wheel delta
322 int wheelDelta = 0;
323 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheelDelta);
324 if (result) {
325 qFatal("QBB: failed to query event wheel delta, errno=%d", errno);
326 }
327
328 // map window to top-level widget
329 QWidget* w = QWidget::find( (WId)qnxWindow );
330
331 // Generate enter and leave events as needed.
332 if (qnxWindow != mLastMouseWindow) {
333 QWidget* wOld = QWidget::find( (WId)mLastMouseWindow );
334
335 if (wOld) {
336 QWindowSystemInterface::handleLeaveEvent(wOld);
337 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
338 qDebug() << "QBB: Qt leave, w=" << wOld;
339 #endif
340 }
341
342 if (w) {
343 QWindowSystemInterface::handleEnterEvent(w);
344 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
345 qDebug() << "QBB: Qt enter, w=" << w;
346 #endif
347 }
348 }
349 mLastMouseWindow = qnxWindow;
350
351 // Apply scaling to wheel delta and invert value for Qt. We'll probably want to scale
352 // this via a system preference at some point. But for now this is a sane value and makes
353 // the wheel usable.
354 wheelDelta *= -10;
355
356 // convert point to local coordinates
357 QPoint globalPoint(pos[0], pos[1]);
358 QPoint localPoint(windowPos[0], windowPos[1]);
359
360 // Convert buttons.
361 // Some QNX header files invert 'Right Button versus "Left Button' ('Right' == 0x01). But they also offer a 'Button Swap' bit,
362 // so we may receive events as shown. (If this is wrong, the fix is easy.)
363 // QNX Button mask is 8 buttons wide, with a maximum value of 0x80.
364 Qt::MouseButtons buttons = Qt::NoButton;
365 if (buttonState & 0x01)
366 buttons |= Qt::LeftButton;
367 if (buttonState & 0x02)
368 buttons |= Qt::MidButton;
369 if (buttonState & 0x04)
370 buttons |= Qt::RightButton;
371 if (buttonState & 0x08)
372 buttons |= Qt::XButton1;
373 if (buttonState & 0x10)
374 buttons |= Qt::XButton2;
375
376 if (w) {
377 // Inject mouse event into Qt only if something has changed.
378 if (mLastGlobalMousePoint != globalPoint ||
379 mLastLocalMousePoint != localPoint ||
380 mLastButtonState != buttons) {
381 QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons);
382 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
383 qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << (int)buttons;
384 #endif
385 }
386
387 if (wheelDelta) {
388 // Screen only supports a single wheel, so we will assume Vertical orientation for
389 // now since that is pretty much standard.
390 QWindowSystemInterface::handleWheelEvent(w, localPoint, globalPoint, wheelDelta, Qt::Vertical);
391 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
392 qDebug() << "QBB: Qt wheel, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), d=" << (int)wheelDelta;
393 #endif
394 }
395 }
396
397 mLastGlobalMousePoint = globalPoint;
398 mLastLocalMousePoint = localPoint;
399 mLastButtonState = buttons;
400 }
401
handleTouchEvent(screen_event_t event,int qnxType)402 void QBBScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
403 {
404 // get display coordinates of touch
405 errno = 0;
406 int pos[2];
407 int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos);
408 if (result) {
409 qFatal("QBB: failed to query event position, errno=%d", errno);
410 }
411
412 QCursor::setPos(pos[0], pos[1]);
413
414 // get window coordinates of touch
415 errno = 0;
416 int windowPos[2];
417 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos);
418 if (result) {
419 qFatal("QBB: failed to query event window position, errno=%d", errno);
420 }
421
422 // determine which finger touched
423 errno = 0;
424 int touchId;
425 result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touchId);
426 if (result) {
427 qFatal("QBB: failed to query event touch id, errno=%d", errno);
428 }
429
430 // determine which window was touched
431 errno = 0;
432 void *qnxWindow;
433 result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &qnxWindow);
434 if (result) {
435 qFatal("QBB: failed to query event window, errno=%d", errno);
436 }
437
438 // check if finger is valid
439 if (touchId < MAX_TOUCH_POINTS) {
440
441 // map window to top-level widget
442 QWidget* w = QWidget::find( (WId)qnxWindow );
443
444 // Generate enter and leave events as needed.
445 if (qnxWindow != mLastMouseWindow) {
446 QWidget* wOld = QWidget::find( (WId)mLastMouseWindow );
447
448 if (wOld) {
449 QWindowSystemInterface::handleLeaveEvent(wOld);
450 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
451 qDebug() << "QBB: Qt leave, w=" << wOld;
452 #endif
453 }
454
455 if (w) {
456 QWindowSystemInterface::handleEnterEvent(w);
457 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
458 qDebug() << "QBB: Qt enter, w=" << w;
459 #endif
460 }
461 }
462 mLastMouseWindow = qnxWindow;
463
464 if (w) {
465 // convert primary touch to mouse event
466 if (touchId == 0) {
467
468 // convert point to local coordinates
469 QPoint globalPoint(pos[0], pos[1]);
470 QPoint localPoint(windowPos[0], windowPos[1]);
471
472 // map touch state to button state
473 Qt::MouseButtons buttons = (qnxType == SCREEN_EVENT_MTOUCH_RELEASE) ? Qt::NoButton : Qt::LeftButton;
474
475 // inject event into Qt
476 QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons);
477 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
478 qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << buttons;
479 #endif
480 }
481
482 // get size of screen which contains window
483 QPlatformScreen* platformScreen = QPlatformScreen::platformScreenForWidget(w);
484 QSize screenSize = platformScreen->physicalSize();
485
486 // update cached position of current touch point
487 mTouchPoints[touchId].normalPosition = QPointF( ((qreal)pos[0]) / screenSize.width(), ((qreal)pos[1]) / screenSize.height() );
488 mTouchPoints[touchId].area = QRectF( pos[0], pos[1], 0.0, 0.0 );
489
490 // determine event type and update state of current touch point
491 QEvent::Type type = QEvent::None;
492 switch (qnxType) {
493 case SCREEN_EVENT_MTOUCH_TOUCH:
494 mTouchPoints[touchId].state = Qt::TouchPointPressed;
495 type = QEvent::TouchBegin;
496 break;
497 case SCREEN_EVENT_MTOUCH_MOVE:
498 mTouchPoints[touchId].state = Qt::TouchPointMoved;
499 type = QEvent::TouchUpdate;
500 break;
501 case SCREEN_EVENT_MTOUCH_RELEASE:
502 mTouchPoints[touchId].state = Qt::TouchPointReleased;
503 type = QEvent::TouchEnd;
504 break;
505 }
506
507 // build list of active touch points
508 QList<QWindowSystemInterface::TouchPoint> pointList;
509 for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
510 if (i == touchId) {
511 // current touch point is always active
512 pointList.append(mTouchPoints[i]);
513 } else if (mTouchPoints[i].state != Qt::TouchPointReleased) {
514 // finger is down but did not move
515 mTouchPoints[i].state = Qt::TouchPointStationary;
516 pointList.append(mTouchPoints[i]);
517 }
518 }
519
520 // inject event into Qt
521 QWindowSystemInterface::handleTouchEvent(w, type, QTouchEvent::TouchScreen, pointList);
522 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
523 qDebug() << "QBB: Qt touch, w=" << w << ", p=(" << pos[0] << "," << pos[1] << "), t=" << type;
524 #endif
525 }
526 }
527 }
528
handleCloseEvent(screen_event_t event)529 void QBBScreenEventHandler::handleCloseEvent(screen_event_t event)
530 {
531 screen_window_t window = 0;
532 if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0)
533 qFatal("QBB: failed to query event window property, errno=%d", errno);
534
535 emit windowClosed(window);
536
537 // map window to top-level widget
538 QWidget* w = QWidget::find( (WId)window );
539 if (w != NULL)
540 QWindowSystemInterface::handleCloseEvent(w);
541 }
542
handleCreateEvent(screen_event_t event)543 void QBBScreenEventHandler::handleCreateEvent(screen_event_t event)
544 {
545 screen_window_t window = 0;
546 if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0)
547 qFatal("QBB: failed to query event window property, errno=%d", errno);
548
549 emit newWindowCreated(window);
550 }
551
handleDisplayEvent(screen_event_t event)552 void QBBScreenEventHandler::handleDisplayEvent(screen_event_t event)
553 {
554 screen_display_t nativeDisplay = 0;
555 if (screen_get_event_property_pv(event, SCREEN_PROPERTY_DISPLAY, (void **)&nativeDisplay) != 0) {
556 qWarning("QBB: failed to query display property, errno=%d", errno);
557 return;
558 }
559
560 int isAttached = 0;
561 if (screen_get_event_property_iv(event, SCREEN_PROPERTY_ATTACHED, &isAttached) != 0) {
562 qWarning("QBB: failed to query display attached property, errno=%d", errno);
563 return;
564 }
565
566 #if defined(QBBSCREENEVENTHANDLER_DEBUG)
567 qDebug() << Q_FUNC_INFO << "display attachment is now:" << isAttached;
568 #endif
569 QBBScreen *screen = mBBIntegration->screenForNative(nativeDisplay);
570 if (!screen) {
571 if (isAttached)
572 mBBIntegration->createDisplay(nativeDisplay, false /* not primary, we assume */);
573 } else if (!isAttached) {
574 // libscreen display is deactivated, let's remove the QBBScreen / QScreen
575 mBBIntegration->removeDisplay(screen);
576 }
577 }
578
579 #include "moc_qbbscreeneventhandler.cpp"
580
581 QT_END_NAMESPACE
582