mirror of https://github.com/PCSX2/pcsx2.git
335 lines
8.4 KiB
C++
335 lines
8.4 KiB
C++
|
/* PCSX2 - PS2 Emulator for PCs
|
||
|
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||
|
*
|
||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* PCSX2 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 PCSX2.
|
||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "PrecompiledHeader.h"
|
||
|
|
||
|
#include "DisplayWidget.h"
|
||
|
#include "EmuThread.h"
|
||
|
#include "QtHost.h"
|
||
|
|
||
|
#include "pcsx2/GS/GSIntrin.h" // _BitScanForward
|
||
|
|
||
|
#include <QtCore/QDebug>
|
||
|
#include <QtGui/QGuiApplication>
|
||
|
#include <QtGui/QKeyEvent>
|
||
|
#include <QtGui/QScreen>
|
||
|
#include <QtGui/QWindow>
|
||
|
#include <QtGui/QWindowStateChangeEvent>
|
||
|
#include <cmath>
|
||
|
|
||
|
#if !defined(_WIN32) && !defined(APPLE)
|
||
|
#include <qpa/qplatformnativeinterface.h>
|
||
|
#endif
|
||
|
|
||
|
DisplayWidget::DisplayWidget(QWidget* parent)
|
||
|
: QWidget(parent)
|
||
|
{
|
||
|
// We want a native window for both D3D and OpenGL.
|
||
|
setAutoFillBackground(false);
|
||
|
setAttribute(Qt::WA_NativeWindow, true);
|
||
|
setAttribute(Qt::WA_NoSystemBackground, true);
|
||
|
setAttribute(Qt::WA_PaintOnScreen, true);
|
||
|
setFocusPolicy(Qt::StrongFocus);
|
||
|
setMouseTracking(true);
|
||
|
}
|
||
|
|
||
|
DisplayWidget::~DisplayWidget() = default;
|
||
|
|
||
|
qreal DisplayWidget::devicePixelRatioFromScreen() const
|
||
|
{
|
||
|
QScreen* screen_for_ratio;
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||
|
screen_for_ratio = windowHandle()->screen();
|
||
|
#else
|
||
|
screen_for_ratio = screen();
|
||
|
#endif
|
||
|
if (!screen_for_ratio)
|
||
|
screen_for_ratio = QGuiApplication::primaryScreen();
|
||
|
|
||
|
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
||
|
}
|
||
|
|
||
|
int DisplayWidget::scaledWindowWidth() const
|
||
|
{
|
||
|
return static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioFromScreen()));
|
||
|
}
|
||
|
|
||
|
int DisplayWidget::scaledWindowHeight() const
|
||
|
{
|
||
|
return static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen()));
|
||
|
}
|
||
|
|
||
|
std::optional<WindowInfo> DisplayWidget::getWindowInfo() const
|
||
|
{
|
||
|
WindowInfo wi;
|
||
|
|
||
|
// Windows and Apple are easy here since there's no display connection.
|
||
|
#if defined(_WIN32)
|
||
|
wi.type = WindowInfo::Type::Win32;
|
||
|
wi.window_handle = reinterpret_cast<void*>(winId());
|
||
|
#elif defined(__APPLE__)
|
||
|
wi.type = WindowInfo::Type::MacOS;
|
||
|
wi.window_handle = reinterpret_cast<void*>(winId());
|
||
|
#else
|
||
|
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||
|
const QString platform_name = QGuiApplication::platformName();
|
||
|
if (platform_name == QStringLiteral("xcb"))
|
||
|
{
|
||
|
wi.type = WindowInfo::Type::X11;
|
||
|
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||
|
wi.window_handle = reinterpret_cast<void*>(winId());
|
||
|
}
|
||
|
else if (platform_name == QStringLiteral("wayland"))
|
||
|
{
|
||
|
wi.type = WindowInfo::Type::Wayland;
|
||
|
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||
|
wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qCritical() << "Unknown PNI platform " << platform_name;
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
wi.surface_width = scaledWindowWidth();
|
||
|
wi.surface_height = scaledWindowHeight();
|
||
|
wi.surface_scale = devicePixelRatioFromScreen();
|
||
|
return wi;
|
||
|
}
|
||
|
|
||
|
void DisplayWidget::setRelativeMode(bool enabled)
|
||
|
{
|
||
|
if (m_relative_mouse_enabled == enabled)
|
||
|
return;
|
||
|
|
||
|
if (enabled)
|
||
|
{
|
||
|
m_relative_mouse_start_position = QCursor::pos();
|
||
|
|
||
|
const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2));
|
||
|
QCursor::setPos(center_pos);
|
||
|
m_relative_mouse_last_position = center_pos;
|
||
|
grabMouse();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QCursor::setPos(m_relative_mouse_start_position);
|
||
|
releaseMouse();
|
||
|
}
|
||
|
|
||
|
m_relative_mouse_enabled = enabled;
|
||
|
}
|
||
|
|
||
|
QPaintEngine* DisplayWidget::paintEngine() const
|
||
|
{
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
bool DisplayWidget::event(QEvent* event)
|
||
|
{
|
||
|
switch (event->type())
|
||
|
{
|
||
|
case QEvent::KeyPress:
|
||
|
case QEvent::KeyRelease:
|
||
|
{
|
||
|
const QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
|
||
|
if (!key_event->isAutoRepeat())
|
||
|
{
|
||
|
emit windowKeyEvent(key_event->key(), static_cast<int>(key_event->modifiers()),
|
||
|
event->type() == QEvent::KeyPress);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::MouseMove:
|
||
|
{
|
||
|
const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||
|
|
||
|
if (!m_relative_mouse_enabled)
|
||
|
{
|
||
|
const qreal dpr = devicePixelRatioFromScreen();
|
||
|
const QPoint mouse_pos = mouse_event->pos();
|
||
|
const int scaled_x = static_cast<int>(static_cast<qreal>(mouse_pos.x()) * dpr);
|
||
|
const int scaled_y = static_cast<int>(static_cast<qreal>(mouse_pos.y()) * dpr);
|
||
|
|
||
|
windowMouseMoveEvent(scaled_x, scaled_y);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
|
||
|
const QPoint mouse_pos = mapToGlobal(mouse_event->pos());
|
||
|
|
||
|
const int dx = mouse_pos.x() - center_pos.x();
|
||
|
const int dy = mouse_pos.y() - center_pos.y();
|
||
|
m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx);
|
||
|
m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy);
|
||
|
windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y());
|
||
|
QCursor::setPos(center_pos);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::MouseButtonPress:
|
||
|
case QEvent::MouseButtonDblClick:
|
||
|
case QEvent::MouseButtonRelease:
|
||
|
{
|
||
|
unsigned long button_index;
|
||
|
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
|
||
|
emit windowMouseButtonEvent(static_cast<int>(button_index + 1u), event->type() != QEvent::MouseButtonRelease);
|
||
|
|
||
|
if (event->type() == QEvent::MouseButtonDblClick &&
|
||
|
static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton &&
|
||
|
Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true))
|
||
|
{
|
||
|
g_emu_thread->toggleFullscreen();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::Wheel:
|
||
|
{
|
||
|
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
|
||
|
emit windowMouseWheelEvent(wheel_event->angleDelta());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::Resize:
|
||
|
{
|
||
|
QWidget::event(event);
|
||
|
|
||
|
emit windowResizedEvent(scaledWindowWidth(), scaledWindowHeight(), devicePixelRatioFromScreen());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::Close:
|
||
|
{
|
||
|
emit windowClosedEvent();
|
||
|
QWidget::event(event);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::WindowStateChange:
|
||
|
{
|
||
|
QWidget::event(event);
|
||
|
|
||
|
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||
|
emit windowRestoredEvent();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::FocusIn:
|
||
|
{
|
||
|
QWidget::event(event);
|
||
|
emit windowFocusEvent();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
case QEvent::ActivationChange:
|
||
|
{
|
||
|
QWidget::event(event);
|
||
|
if (isActiveWindow())
|
||
|
emit windowFocusEvent();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return QWidget::event(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DisplayContainer::DisplayContainer()
|
||
|
: QStackedWidget(nullptr)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
DisplayContainer::~DisplayContainer() = default;
|
||
|
|
||
|
bool DisplayContainer::IsNeeded(bool fullscreen, bool render_to_main)
|
||
|
{
|
||
|
#if defined(_WIN32) || defined(__APPLE__)
|
||
|
return false;
|
||
|
#else
|
||
|
if (fullscreen || render_to_main)
|
||
|
return false;
|
||
|
|
||
|
// We only need this on Wayland because of client-side decorations...
|
||
|
const QString platform_name = QGuiApplication::platformName();
|
||
|
return (platform_name == QStringLiteral("wayland"));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void DisplayContainer::setDisplayWidget(DisplayWidget* widget)
|
||
|
{
|
||
|
pxAssert(!m_display_widget);
|
||
|
m_display_widget = widget;
|
||
|
addWidget(widget);
|
||
|
}
|
||
|
|
||
|
DisplayWidget* DisplayContainer::removeDisplayWidget()
|
||
|
{
|
||
|
DisplayWidget* widget = m_display_widget;
|
||
|
pxAssert(widget);
|
||
|
m_display_widget = nullptr;
|
||
|
removeWidget(widget);
|
||
|
return widget;
|
||
|
}
|
||
|
|
||
|
bool DisplayContainer::event(QEvent* event)
|
||
|
{
|
||
|
const bool res = QStackedWidget::event(event);
|
||
|
if (!m_display_widget)
|
||
|
return res;
|
||
|
|
||
|
switch (event->type())
|
||
|
{
|
||
|
case QEvent::Close:
|
||
|
{
|
||
|
emit m_display_widget->windowClosedEvent();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case QEvent::WindowStateChange:
|
||
|
{
|
||
|
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||
|
emit m_display_widget->windowRestoredEvent();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case QEvent::FocusIn:
|
||
|
{
|
||
|
emit m_display_widget->windowFocusEvent();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case QEvent::ActivationChange:
|
||
|
{
|
||
|
if (isActiveWindow())
|
||
|
emit m_display_widget->windowFocusEvent();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|