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