| /* Copyright (C) 2015 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| |
| #include <QtCore> |
| #include <QDesktopWidget> |
| #include <QIcon> |
| #include <QMouseEvent> |
| #include <QPainter> |
| #include <QPushButton> |
| #include <QScreen> |
| #include <QSemaphore> |
| |
| #include "android/skin/event.h" |
| #include "android/skin/keycode.h" |
| #include "android/skin/qt/emulator-window.h" |
| #include "android/skin/qt/winsys-qt.h" |
| |
| #if defined(__APPLE__) |
| #include "android/skin/qt/mac-native-window.h" |
| #endif |
| |
| #define DEBUG 1 |
| |
| #if DEBUG |
| #include "android/utils/debug.h" |
| #define D(...) VERBOSE_PRINT(surface,__VA_ARGS__) |
| #else |
| #define D(...) ((void)0) |
| #endif |
| |
| #define MIN(a,b) (a < b ? a : b) |
| |
| static EmulatorWindow *instance; |
| |
| EmulatorWindow::EmulatorWindow(QWidget *parent) : |
| QFrame(parent) |
| { |
| instance = this; |
| backing_surface = NULL; |
| tool_window = new ToolWindow(this); |
| |
| QObject::connect(this, &EmulatorWindow::blit, this, &EmulatorWindow::slot_blit); |
| QObject::connect(this, &EmulatorWindow::createBitmap, this, &EmulatorWindow::slot_createBitmap); |
| QObject::connect(this, &EmulatorWindow::fill, this, &EmulatorWindow::slot_fill); |
| QObject::connect(this, &EmulatorWindow::getBitmapInfo, this, &EmulatorWindow::slot_getBitmapInfo); |
| QObject::connect(this, &EmulatorWindow::getMonitorDpi, this, &EmulatorWindow::slot_getMonitorDpi); |
| QObject::connect(this, &EmulatorWindow::getScreenDimensions, this, &EmulatorWindow::slot_getScreenDimensions); |
| QObject::connect(this, &EmulatorWindow::getWindowId, this, &EmulatorWindow::slot_getWindowId); |
| QObject::connect(this, &EmulatorWindow::getWindowPos, this, &EmulatorWindow::slot_getWindowPos); |
| QObject::connect(this, &EmulatorWindow::isWindowFullyVisible, this, &EmulatorWindow::slot_isWindowFullyVisible); |
| QObject::connect(this, &EmulatorWindow::pollEvent, this, &EmulatorWindow::slot_pollEvent); |
| QObject::connect(this, &EmulatorWindow::queueEvent, this, &EmulatorWindow::slot_queueEvent); |
| QObject::connect(this, &EmulatorWindow::releaseBitmap, this, &EmulatorWindow::slot_releaseBitmap); |
| QObject::connect(this, &EmulatorWindow::requestClose, this, &EmulatorWindow::slot_requestClose); |
| QObject::connect(this, &EmulatorWindow::requestUpdate, this, &EmulatorWindow::slot_requestUpdate); |
| QObject::connect(this, &EmulatorWindow::setWindowIcon, this, &EmulatorWindow::slot_setWindowIcon); |
| QObject::connect(this, &EmulatorWindow::setWindowPos, this, &EmulatorWindow::slot_setWindowPos); |
| QObject::connect(this, &EmulatorWindow::setTitle, this, &EmulatorWindow::slot_setWindowTitle); |
| QObject::connect(this, &EmulatorWindow::showWindow, this, &EmulatorWindow::slot_showWindow); |
| QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, this, &EmulatorWindow::slot_clearInstance); |
| } |
| |
| EmulatorWindow::~EmulatorWindow() |
| { |
| delete tool_window; |
| } |
| |
| EmulatorWindow *EmulatorWindow::getInstance() |
| { |
| return instance; |
| } |
| |
| void EmulatorWindow::keyPressEvent(QKeyEvent *event) |
| { |
| handleKeyEvent(kEventKeyDown, event); |
| } |
| |
| void EmulatorWindow::keyReleaseEvent(QKeyEvent *event) |
| { |
| handleKeyEvent(kEventKeyUp, event); |
| if (event->text().length() > 0) { |
| SkinEvent *skin_event = createSkinEvent(kEventTextInput); |
| skin_event->u.text.down = false; |
| strncpy((char*)skin_event->u.text.text, (const char*)event->text().constData(), 32); |
| queueEvent(skin_event); |
| } |
| } |
| |
| void EmulatorWindow::mouseMoveEvent(QMouseEvent *event) |
| { |
| handleEvent(kEventMouseMotion, event); |
| } |
| |
| void EmulatorWindow::mousePressEvent(QMouseEvent *event) |
| { |
| handleEvent(kEventMouseButtonDown, event); |
| } |
| |
| void EmulatorWindow::mouseReleaseEvent(QMouseEvent *event) |
| { |
| handleEvent(kEventMouseButtonUp, event); |
| } |
| |
| void EmulatorWindow::paintEvent(QPaintEvent *) |
| { |
| if (backing_surface) { |
| QPainter painter(this); |
| QRect r(0, 0, backing_surface->w, backing_surface->h); |
| painter.drawImage(r, *backing_surface->bitmap); |
| } else { |
| D("Painting emulator window, but no backing bitmap"); |
| } |
| } |
| |
| void EmulatorWindow::show() |
| { |
| QFrame::show(); |
| tool_window->show(); |
| } |
| |
| void EmulatorWindow::startThread(StartFunction f, int argc, char **argv) |
| { |
| MainLoopThread *t = new MainLoopThread(f, argc, argv); |
| t->start(); |
| } |
| |
| void EmulatorWindow::wheelEvent(QWheelEvent *event) |
| { |
| int delta = event->delta(); |
| SkinEvent *skin_event = createSkinEvent(kEventMouseButtonDown); |
| skin_event->u.mouse.button = delta >= 0 ? kMouseButtonScrollUp : kMouseButtonScrollDown; |
| skin_event->u.mouse.x = event->globalX(); |
| skin_event->u.mouse.y = event->globalY(); |
| skin_event->u.mouse.xrel = event->x(); |
| skin_event->u.mouse.yrel = event->y(); |
| queueEvent(skin_event); |
| } |
| |
| void EmulatorWindow::slot_blit(QImage *src, QRect *srcRect, QImage *dst, QPoint *dstPos, QPainter::CompositionMode *op, QSemaphore *semaphore) |
| { |
| QPainter painter(dst); |
| painter.setCompositionMode(*op); |
| painter.drawImage(*dstPos, *src, *srcRect); |
| |
| painter.setCompositionMode(QPainter::CompositionMode_Source); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_clearInstance() |
| { |
| skin_winsys_save_window_pos(); |
| instance = NULL; |
| } |
| |
| void EmulatorWindow::slot_createBitmap(SkinSurface *s, int w, int h, QSemaphore *semaphore) { |
| s->bitmap = new QImage(w, h, QImage::Format_ARGB32); |
| s->bitmap->fill(0); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_fill(SkinSurface *s, const QRect *rect, const QColor *color, QSemaphore *semaphore) |
| { |
| QPainter painter(s->bitmap); |
| painter.fillRect(*rect, *color); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_getBitmapInfo(SkinSurface *s, SkinSurfacePixels *pix, QSemaphore *semaphore) |
| { |
| pix->pixels = (uint32_t*)s->bitmap->bits(); |
| pix->w = s->original_w; |
| pix->h = s->original_h; |
| pix->pitch = s->bitmap->bytesPerLine(); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_getMonitorDpi(int *out_dpi, QSemaphore *semaphore) |
| { |
| *out_dpi = QApplication::screens().at(0)->logicalDotsPerInch(); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_getScreenDimensions(QRect *out_rect, QSemaphore *semaphore) |
| { |
| QRect rect = ((QApplication*)QApplication::instance())->desktop()->screenGeometry(); |
| out_rect->setX(rect.x()); |
| out_rect->setY(rect.y()); |
| out_rect->setWidth(rect.width()); |
| out_rect->setHeight(rect.height()); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_getWindowId(WId *out_id, QSemaphore *semaphore) |
| { |
| WId wid = effectiveWinId(); |
| D("Effective win ID is %lx", wid); |
| #if defined(__APPLE__) |
| wid = (WId)getNSWindow((void*)wid); |
| D("After finding parent, win ID is %lx", wid); |
| #endif |
| *out_id = wid; |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_getWindowPos(int *xx, int *yy, QSemaphore *semaphore) |
| { |
| *xx = x(); |
| *yy = y(); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_isWindowFullyVisible(bool *out_value, QSemaphore *semaphore) |
| { |
| *out_value = ((QApplication*)QApplication::instance())->desktop()->screenGeometry().contains(geometry()); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_pollEvent(SkinEvent *event, bool *hasEvent, QSemaphore *semaphore) |
| { |
| if (event_queue.isEmpty()) { |
| *hasEvent = false; |
| } else { |
| *hasEvent = true; |
| SkinEvent *newEvent = event_queue.dequeue(); |
| memcpy(event, newEvent, sizeof(SkinEvent)); |
| delete newEvent; |
| } |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_queueEvent(SkinEvent *event, QSemaphore *semaphore) |
| { |
| event_queue.enqueue(event); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_releaseBitmap(SkinSurface *s, QSemaphore *semaphore) |
| { |
| if (backing_surface == s) { |
| backing_surface = NULL; |
| } |
| delete s->bitmap; |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_requestClose(QSemaphore *semaphore) |
| { |
| close(); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_requestUpdate(const QRect *rect, QSemaphore *semaphore) |
| { |
| QRect r(rect->x() *backing_surface->w / backing_surface->original_w, |
| rect->y() *backing_surface->h / backing_surface->original_h, |
| rect->width() *backing_surface->w / backing_surface->original_w, |
| rect->height() *backing_surface->h / backing_surface->original_h); |
| update(r); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_setWindowPos(int x, int y, QSemaphore *semaphore) |
| { |
| move(x, y); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_setWindowIcon(const unsigned char *, int, QSemaphore *semaphore) |
| { |
| // QPixmap image; |
| // image.loadFromData(data, size); |
| // QIcon icon(image); |
| // setWindowIcon(icon); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_setWindowTitle(const QString *title, QSemaphore *semaphore) |
| { |
| setWindowTitle(*title); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_showWindow(SkinSurface* surface, const QRect* rect, int is_fullscreen, QSemaphore *semaphore) |
| { |
| backing_surface = surface; |
| if (is_fullscreen) { |
| showFullScreen(); |
| } else { |
| showNormal(); |
| setGeometry(*rect); |
| } |
| show(); |
| if (semaphore != NULL) semaphore->release(); |
| } |
| |
| void EmulatorWindow::slot_back() |
| { |
| simulateKeyPress(KEY_ESC, 0); |
| } |
| |
| void EmulatorWindow::slot_battery() |
| { |
| } |
| |
| void EmulatorWindow::slot_camera() |
| { |
| } |
| |
| void EmulatorWindow::slot_cellular() |
| { |
| } |
| |
| void EmulatorWindow::slot_down() |
| { |
| simulateKeyPress(KEY_KP8, 0); |
| } |
| |
| void EmulatorWindow::slot_fullscreen() |
| { |
| simulateKeyPress(KEY_F9, 0); |
| } |
| |
| void EmulatorWindow::slot_gps() |
| { |
| } |
| |
| void EmulatorWindow::slot_home() |
| { |
| simulateKeyPress(KEY_HOME, 0); |
| } |
| |
| void EmulatorWindow::slot_left() |
| { |
| simulateKeyPress(KEY_KP4, 0); |
| } |
| |
| void EmulatorWindow::slot_menu() |
| { |
| simulateKeyPress(KEY_F2, 0); |
| } |
| |
| void EmulatorWindow::slot_phone() |
| { |
| } |
| |
| void EmulatorWindow::slot_power() |
| { |
| simulateKeyPress(KEY_F7, 0); |
| } |
| |
| void EmulatorWindow::slot_recents() |
| { |
| simulateKeyPress(KEY_F2, kKeyModLShift); |
| } |
| |
| void EmulatorWindow::slot_right() |
| { |
| simulateKeyPress(KEY_KP6, 0); |
| } |
| |
| void EmulatorWindow::slot_rotate() |
| { |
| simulateKeyPress(KEY_F12, kKeyModLCtrl); |
| } |
| |
| void EmulatorWindow::slot_screenrecord() |
| { |
| } |
| |
| void EmulatorWindow::slot_screenshot() |
| { |
| } |
| |
| void EmulatorWindow::slot_sdcard() |
| { |
| } |
| |
| void EmulatorWindow::slot_sensors() |
| { |
| } |
| |
| void EmulatorWindow::slot_up() |
| { |
| simulateKeyPress(KEY_KP2, 0); |
| } |
| |
| void EmulatorWindow::slot_volumeDown() |
| { |
| simulateKeyPress(KEY_F6, kKeyModLCtrl); |
| } |
| |
| void EmulatorWindow::slot_voice() |
| { |
| } |
| |
| void EmulatorWindow::slot_volumeUp() |
| { |
| simulateKeyPress(KEY_F5, kKeyModLCtrl); |
| } |
| |
| void EmulatorWindow::slot_zoom() |
| { |
| } |
| |
| // Convert a Qt::Key_XXX code into the corresponding Linux keycode value. |
| // On failure, return -1. |
| static int convertKeyCode(int sym) |
| { |
| #define KK(x,y) { Qt::Key_ ## x, KEY_ ## y } |
| #define K1(x) KK(x,x) |
| static const struct { |
| int qt_sym; |
| int keycode; |
| } kConvert[] = { |
| KK(Left, LEFT), |
| KK(Right, RIGHT), |
| KK(Up, UP), |
| KK(Down, DOWN), |
| K1(0), |
| K1(1), |
| K1(2), |
| K1(3), |
| K1(4), |
| K1(5), |
| K1(6), |
| K1(7), |
| K1(8), |
| K1(9), |
| K1(F1), |
| K1(F2), |
| K1(F3), |
| K1(F4), |
| K1(F5), |
| K1(F6), |
| K1(F7), |
| K1(F8), |
| K1(F9), |
| K1(F10), |
| K1(F11), |
| K1(F12), |
| K1(A), |
| K1(B), |
| K1(C), |
| K1(D), |
| K1(E), |
| K1(F), |
| K1(G), |
| K1(H), |
| K1(I), |
| K1(J), |
| K1(K), |
| K1(L), |
| K1(M), |
| K1(N), |
| K1(O), |
| K1(P), |
| K1(Q), |
| K1(R), |
| K1(S), |
| K1(T), |
| K1(U), |
| K1(V), |
| K1(W), |
| K1(X), |
| K1(Y), |
| K1(Z), |
| KK(Minus, MINUS), |
| KK(Equal, EQUAL), |
| KK(Backspace, BACKSPACE), |
| KK(Home, HOME), |
| KK(Escape, ESC), |
| KK(Comma, COMMA), |
| KK(Period,DOT), |
| KK(Space, SPACE), |
| KK(Slash, SLASH), |
| KK(Return,ENTER), |
| KK(Tab, TAB), |
| KK(BracketLeft, LEFTBRACE), |
| KK(BracketRight, RIGHTBRACE), |
| KK(Backslash, BACKSLASH), |
| KK(Semicolon, SEMICOLON), |
| KK(Apostrophe, APOSTROPHE), |
| }; |
| const size_t kConvertSize = sizeof(kConvert) / sizeof(kConvert[0]); |
| size_t nn; |
| for (nn = 0; nn < kConvertSize; ++nn) { |
| if (sym == kConvert[nn].qt_sym) { |
| return kConvert[nn].keycode; |
| } |
| } |
| return -1; |
| } |
| |
| SkinEvent *EmulatorWindow::createSkinEvent(SkinEventType type) |
| { |
| SkinEvent *skin_event = new SkinEvent(); |
| skin_event->type = type; |
| return skin_event; |
| } |
| |
| void EmulatorWindow::handleEvent(SkinEventType type, QMouseEvent *event) |
| { |
| SkinEvent *skin_event = createSkinEvent(type); |
| skin_event->u.mouse.button = event->button() == Qt::RightButton ? kMouseButtonRight : kMouseButtonLeft; |
| skin_event->u.mouse.x = event->x(); |
| skin_event->u.mouse.y = event->y(); |
| skin_event->u.mouse.xrel = 0; |
| skin_event->u.mouse.yrel = 0; |
| queueEvent(skin_event); |
| } |
| |
| void EmulatorWindow::handleKeyEvent(SkinEventType type, QKeyEvent *pEvent) |
| { |
| SkinEvent *skin_event = createSkinEvent(type); |
| SkinEventKeyData *keyData = &skin_event->u.key; |
| keyData->keycode = convertKeyCode(pEvent->key()); |
| Qt::KeyboardModifiers modifiers = pEvent->modifiers(); |
| if (modifiers & Qt::ShiftModifier) keyData->mod |= kKeyModLShift; |
| if (modifiers & Qt::ControlModifier) keyData->mod |= kKeyModLCtrl; |
| if (modifiers & Qt::AltModifier) keyData->mod |= kKeyModLAlt; |
| queueEvent(skin_event); |
| } |
| |
| void EmulatorWindow::simulateKeyPress(int keyCode, int modifiers) |
| { |
| SkinEvent *event = new SkinEvent(); |
| event->type = kEventKeyDown; |
| event->u.key.keycode = keyCode; |
| event->u.key.mod = modifiers; |
| slot_queueEvent(event); |
| event = new SkinEvent(); |
| event->type = kEventKeyUp; |
| event->u.key.keycode = keyCode; |
| event->u.key.mod = modifiers; |
| slot_queueEvent(event); |
| } |