blob: 73816b65129ca5a8ce6336ef207887a12fbb6100 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinrtwindow.h"
#include "qwinrtscreen.h"
#include <private/qeventdispatcher_winrt_p.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <qfunctions_winrt.h>
#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/QGuiApplication>
#include <QtGui/QOpenGLContext>
#include <QtGui/QWindow>
#include <QtEglSupport/private/qeglconvenience_p.h>
#include <functional>
#include <wrl.h>
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.controls.h>
#include <windows.ui.viewmanagement.h>
using namespace ABI::Windows::UI::ViewManagement;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::UI;
using namespace ABI::Windows::UI::Xaml;
using namespace ABI::Windows::UI::Xaml::Controls;
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows");
static void setUIElementVisibility(IUIElement *uiElement, bool visibility)
{
Q_ASSERT(uiElement);
QEventDispatcherWinRT::runOnXamlThread([uiElement, visibility]() {
HRESULT hr;
hr = uiElement->put_Visibility(visibility ? Visibility_Visible : Visibility_Collapsed);
Q_ASSERT_SUCCEEDED(hr);
return S_OK;
});
}
class QWinRTWindowPrivate
{
public:
QWinRTScreen *screen;
QSurfaceFormat surfaceFormat;
QString windowTitle;
Qt::WindowStates state;
EGLDisplay display;
EGLSurface surface;
ComPtr<ISwapChainPanel> swapChainPanel;
ComPtr<ICanvasStatics> canvas;
ComPtr<IUIElement> uiElement;
};
QWinRTWindow::QWinRTWindow(QWindow *window)
: QPlatformWindow(window)
, d_ptr(new QWinRTWindowPrivate)
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this;
d->surface = EGL_NO_SURFACE;
d->display = EGL_NO_DISPLAY;
d->screen = static_cast<QWinRTScreen *>(screen());
handleContentOrientationChange(window->contentOrientation());
d->surfaceFormat.setMajorVersion(3);
d->surfaceFormat.setMinorVersion(0);
d->surfaceFormat.setAlphaBufferSize(0);
d->surfaceFormat.setRedBufferSize(8);
d->surfaceFormat.setGreenBufferSize(8);
d->surfaceFormat.setBlueBufferSize(8);
d->surfaceFormat.setDepthBufferSize(24);
d->surfaceFormat.setStencilBufferSize(8);
d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
d->surfaceFormat.setSamples(1);
d->surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
HRESULT hr;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_Canvas).Get(),
IID_PPV_ARGS(&d->canvas));
Q_ASSERT_SUCCEEDED(hr);
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
// Create a new swapchain and place it inside the canvas
HRESULT hr;
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_SwapChainPanel).Get(),
&d->swapChainPanel);
Q_ASSERT_SUCCEEDED(hr);
hr = d->swapChainPanel.As(&d->uiElement);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<Xaml::IFrameworkElement> frameworkElement;
hr = d->swapChainPanel.As(&frameworkElement);
Q_ASSERT_SUCCEEDED(hr);
const QSizeF size = QSizeF(d->screen->geometry().size()) / d->screen->scaleFactor();
hr = frameworkElement->put_Width(size.width());
Q_ASSERT_SUCCEEDED(hr);
hr = frameworkElement->put_Height(size.height());
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IDependencyObject> canvas = d->screen->canvas();
ComPtr<IPanel> panel;
hr = canvas.As(&panel);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IVector<UIElement *>> children;
hr = panel->get_Children(&children);
Q_ASSERT_SUCCEEDED(hr);
hr = children->Append(d->uiElement.Get());
Q_ASSERT_SUCCEEDED(hr);
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
setWindowFlags(window->flags());
setWindowState(window->windowStates());
setWindowTitle(window->title());
setGeometry(window->geometry());
}
QWinRTWindow::~QWinRTWindow()
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this;
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
HRESULT hr;
ComPtr<IDependencyObject> canvas = d->screen->canvas();
ComPtr<IPanel> panel;
hr = canvas.As(&panel);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IVector<UIElement *>> children;
hr = panel->get_Children(&children);
Q_ASSERT_SUCCEEDED(hr);
quint32 index;
boolean found;
hr = children->IndexOf(d->uiElement.Get(), &index, &found);
Q_ASSERT_SUCCEEDED(hr);
if (found) {
hr = children->RemoveAt(index);
Q_ASSERT_SUCCEEDED(hr);
}
return S_OK;
});
RETURN_VOID_IF_FAILED("Failed to completely destroy window resources, likely because the application is shutting down");
if (d->screen->mouseGrabWindow() == this)
d->screen->setMouseGrabWindow(this, false);
if (d->screen->keyboardGrabWindow() == this)
d->screen->setKeyboardGrabWindow(this, false);
d->screen->removeWindow(window());
if (!d->surface)
return;
qCDebug(lcQpaWindows) << __FUNCTION__ << ": Destroying surface";
EGLBoolean value = eglDestroySurface(d->display, d->surface);
d->surface = EGL_NO_SURFACE;
if (Q_UNLIKELY(value == EGL_FALSE))
qCritical("Failed to destroy EGL window surface: 0x%x", eglGetError());
}
QSurfaceFormat QWinRTWindow::format() const
{
Q_D(const QWinRTWindow);
return d->surfaceFormat;
}
bool QWinRTWindow::isActive() const
{
Q_D(const QWinRTWindow);
return d->screen->topWindow() == window();
}
bool QWinRTWindow::isExposed() const
{
Q_D(const QWinRTWindow);
const bool exposed = isActive() && !d->screen->resizePending();
return exposed;
}
void QWinRTWindow::setGeometry(const QRect &rect)
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this << rect;
const Qt::WindowFlags windowFlags = window()->flags();
const Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask;
if (window()->isTopLevel() && (windowType == Qt::Window || windowType == Qt::Dialog)) {
const QRect screenRect = windowFlags & Qt::MaximizeUsingFullscreenGeometryHint
? d->screen->geometry() : d->screen->availableGeometry();
qCDebug(lcQpaWindows) << __FUNCTION__ << "top-level, overwrite" << screenRect;
QPlatformWindow::setGeometry(screenRect);
QWindowSystemInterface::handleGeometryChange(window(), geometry());
} else {
QPlatformWindow::setGeometry(rect);
QWindowSystemInterface::handleGeometryChange(window(), rect);
}
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
HRESULT hr;
const QRect windowGeometry = geometry();
const QPointF topLeft= QPointF(windowGeometry.topLeft()) / d->screen->scaleFactor();
hr = d->canvas->SetTop(d->uiElement.Get(), topLeft.y());
Q_ASSERT_SUCCEEDED(hr);
hr = d->canvas->SetLeft(d->uiElement.Get(), topLeft.x());
Q_ASSERT_SUCCEEDED(hr);
ComPtr<Xaml::IFrameworkElement> frameworkElement;
hr = d->swapChainPanel.As(&frameworkElement);
Q_ASSERT_SUCCEEDED(hr);
const QSizeF size = QSizeF(windowGeometry.size()) / d->screen->scaleFactor();
hr = frameworkElement->put_Width(size.width());
Q_ASSERT_SUCCEEDED(hr);
hr = frameworkElement->put_Height(size.height());
Q_ASSERT_SUCCEEDED(hr);
qCDebug(lcQpaWindows) << __FUNCTION__ << "(setGeometry Xaml)" << this
<< topLeft << size;
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
}
void QWinRTWindow::setVisible(bool visible)
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this << visible;
if (!window()->isTopLevel())
return;
if (visible) {
d->screen->addWindow(window());
setUIElementVisibility(d->uiElement.Get(), d->state != Qt::WindowMinimized);
} else {
d->screen->removeWindow(window());
setUIElementVisibility(d->uiElement.Get(), false);
}
}
void QWinRTWindow::setWindowTitle(const QString &title)
{
Q_D(QWinRTWindow);
d->windowTitle = title;
if (d->screen->topWindow() == window())
d->screen->updateWindowTitle(title);
}
void QWinRTWindow::raise()
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this;
if (!window()->isTopLevel())
return;
d->screen->raise(window());
}
void QWinRTWindow::lower()
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this;
if (!window()->isTopLevel())
return;
d->screen->lower(window());
}
WId QWinRTWindow::winId() const
{
Q_D(const QWinRTWindow);
return WId(d->swapChainPanel.Get());
}
qreal QWinRTWindow::devicePixelRatio() const
{
return screen()->devicePixelRatio();
}
void QWinRTWindow::setWindowState(Qt::WindowStates state)
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this << state;
if (d->state == state)
return;
if (state & Qt::WindowMinimized) {
setUIElementVisibility(d->uiElement.Get(), false);
d->state = state;
return;
}
if (state & Qt::WindowFullScreen) {
HRESULT hr;
boolean success;
hr = QEventDispatcherWinRT::runOnXamlThread([&hr, &success]() {
ComPtr<IApplicationViewStatics2> applicationViewStatics;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(),
IID_PPV_ARGS(&applicationViewStatics));
RETURN_HR_IF_FAILED("Could not access application view statics.");
ComPtr<IApplicationView> view;
hr = applicationViewStatics->GetForCurrentView(&view);
RETURN_HR_IF_FAILED("Could not access application view.");
ComPtr<IApplicationView3> view3;
hr = view.As(&view3);
Q_ASSERT_SUCCEEDED(hr);
hr = view3->TryEnterFullScreenMode(&success);
return hr;
});
if (FAILED(hr) || !success) {
qCDebug(lcQpaWindows) << "Failed to enter full screen mode.";
return;
}
d->screen->setResizePending();
d->state = state;
return;
}
if (d->state & Qt::WindowFullScreen) {
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([&hr]() {
ComPtr<IApplicationViewStatics2> applicationViewStatics;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(),
IID_PPV_ARGS(&applicationViewStatics));
RETURN_HR_IF_FAILED("Could not access application view statics.");
ComPtr<IApplicationView> view;
hr = applicationViewStatics->GetForCurrentView(&view);
RETURN_HR_IF_FAILED("Could not access application view.");
ComPtr<IApplicationView3> view3;
hr = view.As(&view3);
Q_ASSERT_SUCCEEDED(hr);
hr = view3->ExitFullScreenMode();
return hr;
});
if (FAILED(hr)) {
qCDebug(lcQpaWindows) << "Failed to exit full screen mode.";
return;
}
d->screen->setResizePending();
}
if (d->state & Qt::WindowMinimized || state == Qt::WindowNoState || state == Qt::WindowActive)
setUIElementVisibility(d->uiElement.Get(), true);
d->state = state;
}
bool QWinRTWindow::setMouseGrabEnabled(bool grab)
{
Q_D(QWinRTWindow);
if (!isActive() && grab) {
qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
__FUNCTION__, window()->metaObject()->className(),
qPrintable(window()->objectName()));
return false;
}
return d->screen->setMouseGrabWindow(this, grab);
}
bool QWinRTWindow::setKeyboardGrabEnabled(bool grab)
{
Q_D(QWinRTWindow);
return d->screen->setKeyboardGrabWindow(this, grab);
}
EGLSurface QWinRTWindow::eglSurface() const
{
Q_D(const QWinRTWindow);
return d->surface;
}
void QWinRTWindow::createEglSurface(EGLDisplay display, EGLConfig config)
{
Q_D(QWinRTWindow);
if (d->surface == EGL_NO_SURFACE) {
d->display = display;
QEventDispatcherWinRT::runOnXamlThread([this, d, display, config]() {
d->surface = eglCreateWindowSurface(display, config,
reinterpret_cast<EGLNativeWindowType>(winId()),
nullptr);
if (Q_UNLIKELY(d->surface == EGL_NO_SURFACE))
qCritical("Failed to create EGL window surface: 0x%x", eglGetError());
return S_OK;
});
}
}
QT_END_NAMESPACE