/* * Qt UI * * Copyright (C) 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Contact: * Jihye Won * SungMin Ha * GiWoong Kim * SeokYeon Hwang * Sangho Park * Stanislav Vorobiov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * Contributors: * - S-Core Co., Ltd * */ #include "mainwindow.h" #include "layout/mainform.h" #include "displayglwidget.h" #include "displayswwidget.h" #include "resource/ui_strings.h" #include "uiutil.h" extern "C" { #include "util/ui_operations.h" void qt5_graphic_hw_update(void); // FIXME: To avoid very complex header inclusion chains void qemu_system_graceful_shutdown_request(unsigned int sec); } MainWindow::MainWindow(UiInformation *uiInfo, bool useGL, QWidget *parent) : QWidget(parent) { /* initialize */ this->uiInfo = uiInfo; this->mainView = NULL; this->popupMenu = NULL; this->display = NULL; this->rotary = NULL; this->screenWidget = NULL; this->captureRequestHandler = NULL; this->captureRequestData = NULL; this->movingWidget = NULL; this->isMovingMode = false; /* windowing */ setWindowTitle(EMULATOR_TITLE); setWindowIcon(QIcon(":/icons/emulator_icon.ico")); setWindowFlags(Qt::FramelessWindowHint | Qt::WindowCloseButtonHint); setMinimumSize(0, 0); setAttribute(Qt::WA_TranslucentBackground, true); /* scene */ mainScene = new QGraphicsScene(this); mainScene->setBackgroundBrush(Qt::transparent); /* view */ mainView = new MainView(mainScene, this); mainView->updateLayout(); /* rotary */ MainForm *form = NULL; bool needRotary = false; for(int i = 0; i < uiInfo->getMainFormList().count(); i++) { form = uiInfo->getMainFormList().at(i); if (form->getRotaryImage() != NULL && form->getRotaryImage()->isNull() == false) { needRotary = true; break; } } if (needRotary == true) { rotary = createRotary(); } /* display */ updateDisplayTransform(); createDisplay(useGL); /* set HW Key shortcut */ keyboardShortcut = new KeyboardShortcut(this); /* popup menu */ popupMenu = new ContextMenu(this); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(slotContextMenu(const QPoint&))); } void MainWindow::createDisplay(bool useGL) { qDebug("create a display"); if (useGL) { /* on-screen rendering */ this->display = new DisplayGLWidget(this, uiInfo->getMainFormDpyType(), uiInfo->getResolution(), getUiState()->getScaleFactor()); } else { /* off-screen rendering */ DisplaySWWidget *widget = new DisplaySWWidget(this, uiInfo->getMainFormDpyType(), uiInfo->getResolution(), getUiState()->getScaleFactor()); this->screenWidget = widget; this->display = widget; } } RotaryView *MainWindow::createRotary() { qDebug("create a rotary"); return new RotaryView(this); } QLabel *MainWindow::getScreenWidget() { return screenWidget; } void MainWindow::slotContextMenu(const QPoint &pos) { qDebug("show context menu"); raise(); popupMenu->popup(mapToGlobal(pos)); } UiInformation *MainWindow::getUiInfo() { return uiInfo; } UIState *MainWindow::getUiState() { return uiInfo->getUiState(); } const QTransform &MainWindow::getDisplayTransform() { return dpyTransform; } MainView *MainWindow::getMainView() { return mainView; } ContextMenu *MainWindow::getPopupMenu() { return popupMenu; } DisplayBase *MainWindow::getDisplay() { return display; } KeyboardShortcut *MainWindow::getKeyboardShortcut() { return keyboardShortcut; } DockingController *MainWindow::getDockingCon() { return getUiState()->getConState()->getDockingCon(); } FloatingController *MainWindow::getFloatingCon() { return getUiState()->getConState()->getFloatingCon(); } void MainWindow::openController(int index, int dockPos) { if (dockPos == (Qt::AlignRight | Qt::AlignCenter)) { qDebug("open right-center docking controller %d", index); } else if (dockPos == (Qt::AlignRight | Qt::AlignTop)) { qDebug("open right-top docking controller %d", index); } else if (dockPos == (Qt::AlignRight | Qt::AlignBottom)) { qDebug("open right-bottom docking controller %d", index); } else if (dockPos == -1) { qDebug("open floating controller %d", index); } else { qDebug("invalid dock position: %d", dockPos); dockPos = -1; } closeController(); if (uiInfo->getConFormList().count() <= index) { qWarning("controller index out of range"); return; } ControllerForm *conForm = uiInfo->getConFormList().at(index); if (conForm == NULL) { qWarning("controller is null"); return; } getUiState()->getConState()->setConFormIndex(index); QAction *action = NULL; if (popupMenu != NULL && popupMenu->getControllerMapper() != NULL) { action = (QAction *)popupMenu->getControllerMapper()->mapping(index); qDebug() << "open controller:" << action->text(); } /* create */ if (dockPos == -1) { getUiState()->getConState()->setFloatingCon( new FloatingController(conForm, action, this)); } else { getUiState()->getConState()->setDockingCon( new DockingController(conForm, action, dockPos, this)); getUiState()->getConState()->setRecentlyDockPos(dockPos); #ifdef CONFIG_WIN32 /* W/A: Sometimes, QGrahpicsItem's update() does not work with QGLWidget * on Windows Aero theme. We need to indirectly repaint the items * through these lines. */ const QSize conViewSize = uiInfo->getConSize(); getUiState()->getConState()-> getDockingCon()->getConView()->resize(conViewSize / 2); getUiState()->getConState()-> getDockingCon()->getConView()->resize(conViewSize); #endif } /* register controller's HW key shortcuts */ keyboardShortcut->registerHwKeyShortcuts(conForm->getKeyList()); /* update layout */ resize(uiInfo->getUiSize()); /* arrange */ if (dockPos == -1) { if (getUiState()->getConState()->getRecentlyFloatPos() == QPoint(-1, -1)) { QPoint globalPos = mapToGlobal(QPoint(0, 0)); getFloatingCon()->move(globalPos.x() + size().width(), globalPos.y()); } else { getFloatingCon()->move(getUiState()->getConState()->getRecentlyFloatPos()); getUiState()->getConState()->setRecentlyFloatPos(QPoint(-1, -1)); } getFloatingCon()->show(); } else { getDockingCon()->show(); /* updateGeometry */ } /* Some part of QGLWidget's surface might be lost on Windows when view changing. * So, we need an additional updating for display. */ display->invalidateDisplay(); #ifdef CONFIG_LINUX popupMenu->slotOnTop(getUiState()->isOnTop()); #endif } void MainWindow::closeController() { if (getDockingCon() != NULL) { qDebug("close docking controller"); /* cancel controller's HW key shortcuts */ keyboardShortcut->cancelHwKeyShortcuts( getDockingCon()->getConForm()->getKeyList()); getDockingCon()->close(); getUiState()->getConState()->setDockingCon(NULL); resize(uiInfo->getUiSize()); } if (getFloatingCon() != NULL) { qDebug("close floating controller"); getUiState()->getConState()->setRecentlyFloatPos(getFloatingCon()->pos()); /* cancel controller's HW key shortcuts */ keyboardShortcut->cancelHwKeyShortcuts( getFloatingCon()->getConForm()->getKeyList()); getFloatingCon()->close(); getUiState()->getConState()->setFloatingCon(NULL); } /* Some part of QGLWidget's surface might be lost on Windows when view changing. * So, we need an additional updating for display. */ display->invalidateDisplay(); } /* override */ void MainWindow::resize(const QSize &size) { if (size.width() <= 0 || size.height() <= 0) { qWarning() << "Invalid scale size. Cannot be less than zero." << size; return; } setFixedSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); QWidget::resize(size); } /* override */ void MainWindow::showEvent(QShowEvent *event) { qInfo() << "show main window"; resize(uiInfo->getUiSize()); setFixedSize(size()); } /* override */ void MainWindow::resizeEvent(QResizeEvent *event) { qDebug() << "resize main window:" << size(); setMask(uiInfo->getUiRegion()); QWidget::resizeEvent(event); setFixedSize(size()); calibratedMove(pos().x(), pos().y()); } /* override */ void MainWindow::setMask(const QRegion ®ion) { clearMask(); if (region.isEmpty() == false) { QWidget::setMask(region); } } void MainWindow::updateDisplayTransform() { dpyTransform.reset(); dpyTransform.scale( getUiState()->getScaleFactor(), getUiState()->getScaleFactor()); dpyTransform.rotate(uiInfo->getMainFormDpyType()->getAngle()); } void MainWindow::switchForm(int index) { qDebug("window switch: %d", index); /* terminate the moving mode */ turnOffMovingMode(); /* cancel HW key shortcuts */ keyboardShortcut->cancelHwKeyShortcuts(uiInfo->getMainForm()->getKeyList()); /* layout switching */ getUiState()->setMainFormIndex(index); /* register new HW key shortcuts */ keyboardShortcut->registerHwKeyShortcuts(uiInfo->getMainForm()->getKeyList()); updateDisplayTransform(); if (getDockingCon() != NULL) { getDockingCon()->updateGeometry(); } resize(uiInfo->getUiSize()); mainView->updateLayout(); if (rotary != NULL) { rotary->updateLayout(); } if (display != NULL) { display->switchForm(uiInfo->getMainFormDpyType()); } setMask(uiInfo->getUiRegion()); } void MainWindow::scaleForm(int scale) { qDebug("window scale: %d", scale); /* terminate the moving mode */ turnOffMovingMode(); /* scale changing */ getUiState()->setScalePct(scale); updateDisplayTransform(); if (getDockingCon() != NULL) { getDockingCon()->updateGeometry(); } resize(uiInfo->getUiSize()); mainView->updateLayout(); if (rotary != NULL) { rotary->updateLayout(); } if (display != NULL) { display->scaleForm(getUiState()->getScaleFactor()); } setMask(uiInfo->getUiRegion()); } void MainWindow::capture(void) { qDebug("window screen capture: handler(%p), data(%p)", captureRequestHandler, captureRequestData); if (captureRequestHandler && captureRequestData) { captureRequestHandler(captureRequestData); } } void MainWindow::setCaptureRequestHandler(void *data, void (*handler)(void *)) { captureRequestHandler = handler; captureRequestData = data; } void MainWindow::unsetCaptureRequestHandler(void *data) { captureRequestHandler = NULL; captureRequestData = NULL; } void MainWindow::processCaptured(bool captured, void *pixels, int width, int height) { qDebug("window process captured: %d pixel: %p [%dx%d]", captured, pixels, width, height); if (captured) { qDebug("save captured image: %p", pixels); // pixels's format is ARGB32 QImage image = QImage((uchar *)pixels, width, height, QImage::Format_ARGB32); // deep copy from image & set all alpha channel value to 0xFF QPixmap pixmap = QPixmap::fromImage(image.convertToFormat(QImage::Format_RGB32)); QMetaObject::invokeMethod(popupMenu, "slotShowScreenshot", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap)); } else { qDebug("save blank image"); QPixmap pixmap(uiInfo->getResolution().width(), uiInfo->getResolution().height()); // fill the uninitialized buffer of the pixmap with 0xFF000000 pixmap.fill(QColor(0, 0, 0, 255)); QMetaObject::invokeMethod(popupMenu, "slotShowScreenshot", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap)); } } void MainWindow::setTopMost(bool on) { popupMenu->slotOnTop(on); } void MainWindow::calibratedMove(int xx, int yy) { QRect hostBounds = UiUtil::getHostScreenBounds(); xx = qMax(xx, hostBounds.x()); yy = qMax(yy, hostBounds.y()); // shift a little bit from screen edge to inside of bounds xx = qMin(xx, hostBounds.x() + hostBounds.width() - 100); yy = qMin(yy, hostBounds.y() + hostBounds.height() - 100); move(xx, yy); qDebug() << "current position:" << pos(); } void MainWindow::turnOnMovingMode() { qDebug("enter the moving mode"); if (isMovingMode) { return; } getDisplay()->turnOnMovingMode(); if (movingWidget == NULL) { movingWidget = new MovingWidget(this); } isMovingMode = true; movingWidget->show(); } void MainWindow::turnOffMovingMode() { qDebug("leave the moving mode"); if (!isMovingMode) { return; } getDisplay()->turnOffMovingMode(); movingWidget->close(); isMovingMode = false; } void MainWindow::updateTexture(void *item) { ((DisplayGLWidget *)getDisplay())->changedTexture((struct dpy_item *)item); } /* override */ void MainWindow::closeEvent(QCloseEvent *event) { int result = QMessageBox::question(this, EMULATOR_TITLE, MSG_FORCE_CLOSE_POPUP, QMessageBox::Yes, QMessageBox::No | QMessageBox::Default); if (result == QMessageBox::Yes) { /* force close */ qDebug() << "exit!"; qemu_system_graceful_shutdown_request(TIMEOUT_FOR_SHUTDOWN); event->accept(); } else { qDebug() << "cancel"; event->ignore(); } } QMessageBox *MainWindow::showMsgBox( QMessageBox::Icon iconType, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { qWarning() << text; QMessageBox *msgBox = new QMessageBox(iconType, EMULATOR_TITLE, text, buttons, this); if (defaultButton != QMessageBox::NoButton) { msgBox->setDefaultButton(defaultButton); } msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->show(); /* non-blocking */ #ifdef CONFIG_LINUX popupMenu->slotOnTop(getUiState()->isOnTop()); #endif return msgBox; } MainWindow::~MainWindow() { qDebug("destroy main window"); }