blob: 45bc6b2c2eff090cc5be4698780a8c1b0c3ee51c [file] [log] [blame]
/* 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);
}