blob: d20337adb13b4a9fae4036afaa20b888ea69c7cb [file] [log] [blame]
/* Copyright (C) 2015-2016 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.
*/
#pragma once
#include "android/base/containers/CircularBuffer.h"
#include "android/base/StringView.h"
#include "android/base/synchronization/Lock.h"
#include "android/emulation/control/ApkInstaller.h"
#include "android/emulation/control/FilePusher.h"
#include "android/emulation/control/ScreenCapturer.h"
#include "android/globals.h"
#include "android/metrics/PeriodicReporter.h"
#include "android/skin/event.h"
#include "android/skin/qt/emulator-container.h"
#include "android/skin/qt/emulator-overlay.h"
#include "android/skin/qt/error-dialog.h"
#include "android/skin/qt/tool-window.h"
#include "android/skin/qt/ui-event-recorder.h"
#include "android/skin/qt/user-actions-counter.h"
#include "android/skin/surface.h"
#include "android/skin/winsys.h"
#include <QApplication>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QFrame>
#include <QImage>
#include <QMessageBox>
#include <QMouseEvent>
#include <QMoveEvent>
#include <QObject>
#include <QPainter>
#include <QProgressDialog>
#include <QResizeEvent>
#include <QWidget>
#include <QtCore>
#include <memory>
#include <string>
#include <vector>
namespace Ui {
class EmulatorWindow;
}
typedef struct SkinSurface SkinSurface;
class MainLoopThread : public QThread {
Q_OBJECT
public:
MainLoopThread(StartFunction f, int argc, char** argv)
: start_function(f), argc(argc), argv(argv) {}
void run() Q_DECL_OVERRIDE {
if (start_function)
start_function(argc, argv);
}
private:
StartFunction start_function;
int argc;
char** argv;
};
class EmulatorQtWindow final : public QFrame {
Q_OBJECT
public:
using Ptr = std::shared_ptr<EmulatorQtWindow>;
private:
explicit EmulatorQtWindow(QWidget* parent = 0);
public:
static void create();
static EmulatorQtWindow* getInstance();
static Ptr getInstancePtr();
virtual ~EmulatorQtWindow();
void queueQuitEvent();
void closeEvent(QCloseEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragLeaveEvent(QDragLeaveEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void startThread(StartFunction f, int argc, char** argv);
/*
In Qt, signals are normally events of interest that a class can emit, which
can be hooked up to arbitrary slots. Here
we use this mechanism for a different purpose: it's to allow the QEMU
thread
to request an operation be performed on
the Qt thread; Qt allows signals to be emitted from any thread. When used
in
this fashion, the signal is queued and
handled asyncrhonously. Since we sometimes will call these signals from
Qt's
thread as well, we can't use
BlockingQueuedConnections for these signals, since this connection type
will
deadlock if called from the same thread.
For that reason, we use a normal non-blocking connection type, and allow
all
of the signals to pass an optional semaphore
that will be released by the slot when it is done processing. If you want
to
block on the completion of the signal, simply
pass in the semaphore to the signal and acquire it after the call returns.
If
you're passing in pointers to data structures
that could change or go away, you will need to make sure you block to
maintain the integrity of the data while the signal runs.
TODO: allow nonblocking calls to these signals by having the signal take
ownership of object pointers. This would allow QEMU
to do things like update the screen without blocking, which would make it
run
faster.
*/
signals:
void blit(QImage* src,
QRect* srcRect,
QImage* dst,
QPoint* dstPos,
QPainter::CompositionMode* op,
QSemaphore* semaphore = NULL);
void createBitmap(SkinSurface* s,
int w,
int h,
QSemaphore* semaphore = NULL);
void fill(SkinSurface* s,
const QRect* rect,
const QColor* color,
QSemaphore* semaphore = NULL);
void getBitmapInfo(SkinSurface* s,
SkinSurfacePixels* pix,
QSemaphore* semaphore = NULL);
void getDevicePixelRatio(double* out_dpr, QSemaphore* semaphore = NULL);
void getScreenDimensions(QRect* out_rect, QSemaphore* semaphore = NULL);
void getWindowId(WId* out_id, QSemaphore* semaphore = NULL);
void getWindowPos(int* x, int* y, QSemaphore* semaphore = NULL);
void isWindowFullyVisible(bool* out_value, QSemaphore* semaphore = NULL);
void releaseBitmap(SkinSurface* s, QSemaphore* sempahore = NULL);
void requestClose(QSemaphore* semaphore = NULL);
void requestUpdate(const QRect* rect, QSemaphore* semaphore = NULL);
void setDeviceGeometry(const QRect* rect, QSemaphore* sempahore = NULL);
void setWindowIcon(const unsigned char* data,
int size,
QSemaphore* semaphore = NULL);
void setWindowPos(int x, int y, QSemaphore* semaphore = NULL);
void setTitle(const QString* title, QSemaphore* semaphore = NULL);
void showWindow(SkinSurface* surface,
const QRect* rect,
QSemaphore* semaphore = NULL);
// Qt doesn't support function pointers in signals/slots natively, but
// pointer to function pointer works fine
void runOnUiThread(SkinGenericFunction* f,
void* data,
QSemaphore* semaphore = NULL);
void layoutChanged(bool next);
public:
void pollEvent(SkinEvent* event,
bool* hasEvent);
android::emulation::AdbInterface* getAdbInterface() const;
bool isInZoomMode() const;
ToolWindow* toolWindow() const;
void showZoomIfNotUserHidden();
QSize containerSize() const;
QRect deviceGeometry() const;
void doResize(const QSize& size,
bool isKbdShortcut = false,
bool flipDimensions = false);
void handleMouseEvent(SkinEventType type,
SkinMouseButtonType button,
const QPoint& pos,
bool skipSync = false);
void panHorizontal(bool left);
void panVertical(bool up);
void queueSkinEvent(SkinEvent* event);
void recenterFocusPoint();
void saveZoomPoints(const QPoint& focus, const QPoint& viewportFocus);
void scaleDown();
void scaleUp();
void screenshot();
void setOnTop(bool onTop);
void simulateKeyPress(int keyCode, int modifiers);
void simulateScrollBarChanged(int x, int y);
void simulateSetScale(double scale);
void simulateSetZoom(double zoom);
void simulateWindowMoved(const QPoint& pos);
void simulateZoomedWindowResized(const QSize& size);
void toggleZoomMode();
void zoomIn();
void zoomIn(const QPoint& focus, const QPoint& viewportFocus);
void zoomOut();
void zoomOut(const QPoint& focus, const QPoint& viewportFocus);
void zoomReset();
void zoomTo(const QPoint& focus, const QSize& rectSize);
public slots:
void rotateSkin(SkinRotation rot);
private slots:
void slot_adbWarningMessageAccepted();
void slot_blit(QImage* src,
QRect* srcRect,
QImage* dst,
QPoint* dstPos,
QPainter::CompositionMode* op,
QSemaphore* semaphore = NULL);
void slot_clearInstance();
void slot_createBitmap(SkinSurface* s,
int w,
int h,
QSemaphore* semaphore = NULL);
void slot_fill(SkinSurface* s,
const QRect* rect,
const QColor* color,
QSemaphore* semaphore = NULL);
void slot_getBitmapInfo(SkinSurface* s,
SkinSurfacePixels* pix,
QSemaphore* semaphore = NULL);
void slot_getDevicePixelRatio(double* out_dpr,
QSemaphore* semaphore = NULL);
void slot_getScreenDimensions(QRect* out_rect,
QSemaphore* semaphore = NULL);
void slot_getWindowId(WId* out_id, QSemaphore* semaphore = NULL);
void slot_getWindowPos(int* x, int* y, QSemaphore* semaphore = NULL);
void slot_isWindowFullyVisible(bool* out_value,
QSemaphore* semaphore = NULL);
void slot_releaseBitmap(SkinSurface* s, QSemaphore* sempahore = NULL);
void slot_requestClose(QSemaphore* semaphore = NULL);
void slot_requestUpdate(const QRect* rect, QSemaphore* semaphore = NULL);
void slot_setDeviceGeometry(const QRect* rect,
QSemaphore* semaphore = NULL);
void slot_setWindowIcon(const unsigned char* data,
int size,
QSemaphore* semaphore = NULL);
void slot_setWindowPos(int x, int y, QSemaphore* semaphore = NULL);
void slot_setWindowTitle(const QString* title,
QSemaphore* semaphore = NULL);
void slot_showWindow(SkinSurface* surface,
const QRect* rect,
QSemaphore* semaphore = NULL);
void slot_runOnUiThread(SkinGenericFunction* f,
void* data,
QSemaphore* semaphore = NULL);
void slot_horizontalScrollChanged(int value);
void slot_verticalScrollChanged(int value);
void slot_scrollRangeChanged(int min, int max);
void slot_startupTick();
void slot_avdArchWarningMessageAccepted();
void slot_gpuWarningMessageAccepted();
void slot_installCanceled();
void slot_adbPushCanceled();
/*
Here are conventional slots that perform interesting high-level functions
in
the emulator. These can be hooked up to signals
from UI elements or called independently.
*/
public slots:
void raise();
void setForwardShortcutsToDevice(int index);
void show();
void showMinimized();
void wheelScrollTimeout();
void slot_screenChanged();
private:
static const android::base::StringView kRemoteDownloadsDir;
static const android::base::StringView kRemoteDownloadsDirApi10;
// When the main window appears, close the "Starting..."
// pop-up, if it was displayed.
void showEvent(QShowEvent* event) {
mStartupTimer.stop();
mStartupDialog.close();
}
void checkAdbVersionAndWarn();
void showAvdArchWarning();
void showGpuWarning();
bool mouseInside();
SkinMouseButtonType getSkinMouseButton(QMouseEvent* event) const;
SkinEvent* createSkinEvent(SkinEventType type);
void forwardKeyEventToEmulator(SkinEventType type, QKeyEvent* event);
void handleKeyEvent(SkinEventType type, QKeyEvent* event);
void screenshotDone(android::emulation::ScreenCapturer::Result result);
void runAdbInstall(const QString& path);
void installDone(android::emulation::ApkInstaller::Result result,
android::base::StringView errorString);
static const int kPushProgressBarMax;
void runAdbPush(const QList<QUrl>& urls);
void adbPushProgress(double progress, bool done);
void adbPushDone(android::base::StringView filePath,
android::emulation::FilePusher::Result result);
void runAdbShellStopAndQuit();
android::base::Looper* mLooper;
QTimer mStartupTimer;
QProgressDialog mStartupDialog;
SkinSurface* mBackingSurface;
QQueue<SkinEvent*> mSkinEventQueue;
android::base::Lock mSkinEventQueueLock;
ToolWindow* mToolWindow;
EmulatorContainer mContainer;
EmulatorOverlay mOverlay;
QRect mDeviceGeometry;
QPointF mFocus;
QPoint mViewportFocus;
double mZoomFactor;
bool mInZoomMode;
bool mNextIsZoom;
bool mForwardShortcutsToDevice;
QPoint mPrevMousePosition;
MainLoopThread* mMainLoopThread;
QMessageBox mAvdWarningBox;
QMessageBox mGpuWarningBox;
QMessageBox mAdbWarningBox;
bool mFirstShowEvent;
EventCapturer mEventCapturer;
std::shared_ptr<UIEventRecorder<android::base::CircularBuffer>>
mEventLogger;
std::shared_ptr<android::qt::UserActionsCounter> mUserActionsCounter;
std::unique_ptr<android::emulation::AdbInterface> mAdbInterface;
android::emulation::AdbCommandPtr mApkInstallCommand;
android::emulation::ApkInstaller mApkInstaller;
android::emulation::FilePusher mFilePusher;
android::emulation::ScreenCapturer mScreenCapturer;
QProgressDialog mInstallDialog;
QProgressDialog mPushDialog;
QTimer mWheelScrollTimer;
QPoint mWheelScrollPos;
bool mStartedAdbStopProcess;
android::metrics::PeriodicReporter::TaskToken mMetricsReportingToken;
};
struct SkinSurface {
int refcount;
int id;
QImage* bitmap;
int w, h, original_w, original_h;
EmulatorQtWindow::Ptr window;
};