Merge pull request #2432 from Cacodemon345/multimonitor-take2-ui

Multi-monitor support
This commit is contained in:
Jasmine Iwanek
2022-07-10 20:16:35 -04:00
committed by GitHub
67 changed files with 1285 additions and 574 deletions

View File

@@ -8,7 +8,7 @@ extern "C"
#include <86box/video.h>
}
D3D9Renderer::D3D9Renderer(QWidget *parent)
D3D9Renderer::D3D9Renderer(QWidget *parent, int monitor_index)
: QWidget{parent}, RendererCommon()
{
QPalette pal = palette();
@@ -27,6 +27,7 @@ D3D9Renderer::D3D9Renderer(QWidget *parent)
RendererCommon::parentWidget = parent;
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->m_monitor_index = monitor_index;
}
D3D9Renderer::~D3D9Renderer()
@@ -138,8 +139,8 @@ void D3D9Renderer::resizeEvent(QResizeEvent *event)
void D3D9Renderer::blit(int x, int y, int w, int h)
{
if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || surfaceInUse) {
video_blit_complete();
if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (monitors[m_monitor_index].target_buffer == NULL) || surfaceInUse) {
video_blit_complete_monitor(m_monitor_index);
return;
}
surfaceInUse = true;
@@ -152,16 +153,16 @@ void D3D9Renderer::blit(int x, int y, int w, int h)
srcRect.right = source.right();
if (screenshots) {
video_screenshot((uint32_t *) &(buffer32->line[y][x]), 0, 0, 2048);
video_screenshot_monitor((uint32_t *) &(monitors[m_monitor_index].target_buffer->line[y][x]), 0, 0, 2048, m_monitor_index);
}
if (SUCCEEDED(d3d9surface->LockRect(&lockRect, &srcRect, 0))) {
for (int y1 = 0; y1 < h; y1++) {
video_copy(((uint8_t*)lockRect.pBits) + (y1 * lockRect.Pitch), &(buffer32->line[y + y1][x]), w * 4);
video_copy(((uint8_t*)lockRect.pBits) + (y1 * lockRect.Pitch), &(monitors[m_monitor_index].target_buffer->line[y + y1][x]), w * 4);
}
video_blit_complete();
video_blit_complete_monitor(m_monitor_index);
d3d9surface->UnlockRect();
}
else video_blit_complete();
else video_blit_complete_monitor(m_monitor_index);
surfaceInUse = false;
QTimer::singleShot(0, this, [this] { this->update(); });
}

View File

@@ -12,7 +12,7 @@ class D3D9Renderer : public QWidget, public RendererCommon
{
Q_OBJECT
public:
explicit D3D9Renderer(QWidget *parent = nullptr);
explicit D3D9Renderer(QWidget *parent = nullptr, int monitor_index = 0);
~D3D9Renderer();
bool hasBlitFunc() override { return true; }
void blit(int x, int y, int w, int h) override;
@@ -39,6 +39,7 @@ private:
std::atomic<bool> surfaceInUse{false}, finalized{false};
bool alreadyInitialized = false;
int m_monitor_index = 0;
};
#endif // D3D9RENDERER_HPP

View File

@@ -32,6 +32,7 @@ extern "C" {
void HardwareRenderer::resizeGL(int w, int h)
{
m_context->makeCurrent(this);
glViewport(0, 0, qRound(w * devicePixelRatio()), qRound(h * devicePixelRatio()));
}

View File

@@ -122,19 +122,13 @@ main_thread_fn()
/* Just so we dont overload the host OS. */
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
/* If needed, handle a screen resize. */
if (!atomic_flag_test_and_set(&doresize) && !video_fullscreen && !is_quit) {
if (vid_resize & 2)
plat_resize(fixed_size_x, fixed_size_y);
else
plat_resize(scrnsz_x, scrnsz_y);
}
}
is_quit = 1;
}
static std::thread* main_thread;
int main(int argc, char* argv[]) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, false);
@@ -258,7 +252,7 @@ int main(int argc, char* argv[]) {
main_window->installEventFilter(&socket);
socket.connectToServer(qgetenv("86BOX_MANAGER_SOCKET"));
}
pc_reset_hard_init();
//pc_reset_hard_init();
/* Set the PAUSE mode depending on the renderer. */
// plat_pause(0);
@@ -284,13 +278,38 @@ int main(int argc, char* argv[]) {
}
/* Initialize the rendering window, or fullscreen. */
auto main_thread = std::thread([] {
main_thread_fn();
QTimer::singleShot(0, &app, []
{
pc_reset_hard_init();
main_thread = new std::thread(main_thread_fn);
});
QTimer resizeTimer;
resizeTimer.setInterval(0);
resizeTimer.callOnTimeout([]()
{
/* If needed, handle a screen resize. */
for (int i = 0; i < MONITORS_NUM; i++) {
if (!monitors[i].target_buffer) continue;
if (atomic_load(&doresize_monitors[i]) == 1 && !video_fullscreen && !is_quit) {
if (vid_resize & 2)
plat_resize_monitor(fixed_size_x, fixed_size_y, i);
else
plat_resize_monitor(monitors[i].mon_scrnsz_x, monitors[i].mon_scrnsz_y, i);
atomic_store(&doresize_monitors[i], 0);
}
}
if (is_quit) {
QApplication::quit();
}
});
resizeTimer.start();
auto ret = app.exec();
cpu_thread_run = 0;
main_thread.join();
main_thread->join();
pc_close(nullptr);
socket.close();
return ret;

View File

@@ -20,6 +20,8 @@
* Copyright 2021-2022 Teemu Korhonen
* Copyright 2022 dob205
*/
#include <QDebug>
#include "qt_mainwindow.hpp"
#include "ui_qt_mainwindow.h"
@@ -61,6 +63,7 @@ extern "C" {
#include <QPushButton>
#include <QDesktopServices>
#include <QUrl>
#include <QMenuBar>
#include <QCheckBox>
#include <QActionGroup>
#include <QOpenGLContext>
@@ -120,7 +123,7 @@ static BMessageFilter* filter;
#endif
extern void qt_mouse_capture(int);
extern "C" void qt_blit(int x, int y, int w, int h);
extern "C" void qt_blit(int x, int y, int w, int h, int monitor_index);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
@@ -140,7 +143,7 @@ MainWindow::MainWindow(QWidget *parent) :
statusBar()->setVisible(!hide_status_bar);
statusBar()->setStyleSheet("QStatusBar::item {border: None; } QStatusBar QLabel { margin-right: 2px; margin-bottom: 1px; }");
ui->toolBar->setVisible(!hide_tool_bar);
renderers[0].reset(nullptr);
auto toolbar_spacer = new QWidget();
toolbar_spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ui->toolBar->addWidget(toolbar_spacer);
@@ -225,7 +228,7 @@ MainWindow::MainWindow(QWidget *parent) :
});
connect(this, &MainWindow::resizeContents, this, [this](int w, int h) {
if (!QApplication::platformName().contains("eglfs") && vid_resize == 0) {
if (!QApplication::platformName().contains("eglfs") && vid_resize != 1) {
w = (w / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.));
int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.))
@@ -238,6 +241,18 @@ MainWindow::MainWindow(QWidget *parent) :
}
});
connect(this, &MainWindow::resizeContentsMonitor, this, [this](int w, int h, int monitor_index)
{
if (!QApplication::platformName().contains("eglfs") && vid_resize != 1) {
qDebug() << "Resize";
w = (w / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.));
int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.));
renderers[monitor_index]->setFixedSize(w, modifiedHeight);
}
});
connect(ui->menubar, &QMenuBar::triggered, this, [this] {
config_save();
if (QApplication::activeWindow() == this)
@@ -263,6 +278,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->actionHiDPI_scaling->setChecked(dpi_scale);
ui->actionHide_status_bar->setChecked(hide_status_bar);
ui->actionHide_tool_bar->setChecked(hide_tool_bar);
ui->actionShow_non_primary_monitors->setChecked(show_second_monitors);
ui->actionUpdate_status_bar_icons->setChecked(update_icons);
ui->actionEnable_Discord_integration->setChecked(enable_discord);
@@ -305,27 +321,33 @@ MainWindow::MainWindow(QWidget *parent) :
connect(actGroup, &QActionGroup::triggered, [this](QAction* action) {
vid_api = action->property("vid_api").toInt();
RendererStack::Renderer newVidApi = RendererStack::Renderer::Software;
switch (vid_api)
{
case 0:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software);
newVidApi = RendererStack::Renderer::Software;
break;
case 1:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::OpenGL);
newVidApi = (RendererStack::Renderer::OpenGL);
break;
case 2:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::OpenGLES);
newVidApi = (RendererStack::Renderer::OpenGLES);
break;
case 3:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::OpenGL3);
newVidApi = (RendererStack::Renderer::OpenGL3);
break;
case 4:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Vulkan);
newVidApi = (RendererStack::Renderer::Vulkan);
break;
case 5:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Direct3D9);
newVidApi = (RendererStack::Renderer::Direct3D9);
break;
}
ui->stackedWidget->switchRenderer(newVidApi);
if (!show_second_monitors) return;
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i]) renderers[i]->switchRenderer(newVidApi);
}
});
connect(ui->stackedWidget, &RendererStack::rendererChanged, [this]() {
@@ -495,6 +517,11 @@ MainWindow::MainWindow(QWidget *parent) :
#endif
setContextMenuPolicy(Qt::PreventContextMenu);
connect(this, &MainWindow::initRendererMonitor, this, &MainWindow::initRendererMonitorSlot);
connect(this, &MainWindow::initRendererMonitorForNonQtThread, this, &MainWindow::initRendererMonitorSlot, Qt::BlockingQueuedConnection);
connect(this, &MainWindow::destroyRendererMonitor, this, &MainWindow::destroyRendererMonitorSlot);
connect(this, &MainWindow::destroyRendererMonitorForNonQtThread, this, &MainWindow::destroyRendererMonitorSlot, Qt::BlockingQueuedConnection);
}
void MainWindow::closeEvent(QCloseEvent *event) {
@@ -527,18 +554,74 @@ void MainWindow::closeEvent(QCloseEvent *event) {
window_x = this->geometry().x();
window_y = this->geometry().y();
}
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i]) {
monitor_settings[i].mon_window_w = renderers[i]->geometry().width();
monitor_settings[i].mon_window_h = renderers[i]->geometry().height();
if (!QApplication::platformName().contains("wayland")) continue;
monitor_settings[i].mon_window_x = renderers[i]->geometry().x();
monitor_settings[i].mon_window_y = renderers[i]->geometry().y();
}
}
}
qt_nvr_save();
config_save();
if (ui->stackedWidget->mouse_exit_func)
ui->stackedWidget->mouse_exit_func();
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software);
qt_nvr_save();
config_save();
QApplication::processEvents();
cpu_thread_run = 0;
event->accept();
}
void MainWindow::initRendererMonitorSlot(int monitor_index)
{
auto& secondaryRenderer = this->renderers[monitor_index];
secondaryRenderer.reset(new RendererStack(nullptr, monitor_index));
if (secondaryRenderer) {
connect(this, &MainWindow::pollMouse, secondaryRenderer.get(), &RendererStack::mousePoll, Qt::DirectConnection);
connect(secondaryRenderer.get(), &RendererStack::rendererChanged, this, [this, monitor_index]
{
this->renderers[monitor_index]->show();
});
secondaryRenderer->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
secondaryRenderer->setWindowTitle(QObject::tr("86Box Monitor #") + QString::number(monitor_index + 1));
if (vid_resize == 2) {
secondaryRenderer->setFixedSize(fixed_size_x, fixed_size_y);
}
secondaryRenderer->setWindowIcon(this->windowIcon());
if (show_second_monitors) {
secondaryRenderer->show();
if (window_remember) {
secondaryRenderer->setGeometry(monitor_settings[monitor_index].mon_window_x < 120 ? 120 : monitor_settings[monitor_index].mon_window_x,
monitor_settings[monitor_index].mon_window_y < 120 ? 120 : monitor_settings[monitor_index].mon_window_y,
monitor_settings[monitor_index].mon_window_w > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_w,
monitor_settings[monitor_index].mon_window_h > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_h);
}
secondaryRenderer->switchRenderer((RendererStack::Renderer)vid_api);
}
}
}
void MainWindow::destroyRendererMonitorSlot(int monitor_index)
{
if (this->renderers[monitor_index]) {
if (window_remember) {
monitor_settings[monitor_index].mon_window_w = renderers[monitor_index]->geometry().width();
monitor_settings[monitor_index].mon_window_h = renderers[monitor_index]->geometry().height();
monitor_settings[monitor_index].mon_window_x = renderers[monitor_index]->geometry().x();
monitor_settings[monitor_index].mon_window_y = renderers[monitor_index]->geometry().y();
}
config_save();
this->renderers[monitor_index].release()->deleteLater();
}
}
MainWindow::~MainWindow() {
delete ui;
}
@@ -546,6 +629,7 @@ MainWindow::~MainWindow() {
void MainWindow::showEvent(QShowEvent *event) {
if (shownonce) return;
shownonce = true;
if (window_remember) resize(window_w, window_h + menuBar()->height() + (hide_status_bar ? 0 : statusBar()->height()) + (hide_tool_bar ? 0 : ui->toolBar->height()));
if (window_remember && !QApplication::platformName().contains("wayland")) {
setGeometry(window_x, window_y, window_w, window_h + menuBar()->height() + (hide_status_bar ? 0 : statusBar()->height()) + (hide_tool_bar ? 0 : ui->toolBar->height()));
}
@@ -555,15 +639,8 @@ void MainWindow::showEvent(QShowEvent *event) {
+ (hide_status_bar ? 0 : statusBar()->height())
+ (hide_tool_bar ? 0 : ui->toolBar->height()));
scrnsz_x = fixed_size_x;
scrnsz_y = fixed_size_y;
}
else if (window_remember && vid_resize == 1) {
ui->stackedWidget->setFixedSize(window_w, window_h);
adjustSize();
ui->stackedWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
scrnsz_x = window_w;
scrnsz_y = window_h;
monitors[0].mon_scrnsz_x = fixed_size_x;
monitors[0].mon_scrnsz_y = fixed_size_y;
}
}
@@ -1361,7 +1438,7 @@ void MainWindow::on_actionFullscreen_triggered() {
+ (!hide_status_bar ? statusBar()->height() : 0)
+ (!hide_tool_bar ? ui->toolBar->height() : 0));
emit resizeContents(scrnsz_x, scrnsz_y);
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
}
} else {
if (video_fullscreen_first)
@@ -1495,9 +1572,13 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
event->accept();
}
void MainWindow::blitToWidget(int x, int y, int w, int h)
void MainWindow::blitToWidget(int x, int y, int w, int h, int monitor_index)
{
ui->stackedWidget->blit(x, y, w, h);
if (monitor_index >= 1) {
if (renderers[monitor_index]) renderers[monitor_index]->blit(x, y, w, h);
else video_blit_complete_monitor(monitor_index);
}
else ui->stackedWidget->blit(x, y, w, h);
}
void MainWindow::keyReleaseEvent(QKeyEvent* event)
@@ -1536,16 +1617,34 @@ void MainWindow::on_actionResizable_window_triggered(bool checked) {
setWindowFlag(Qt::WindowMaximizeButtonHint);
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false);
setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) {
renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint);
renderers[i]->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
}
} else {
vid_resize = 0;
setWindowFlag(Qt::WindowMaximizeButtonHint, false);
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) {
renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
emit resizeContentsMonitor(monitors[i].mon_scrnsz_x, monitors[i].mon_scrnsz_y, i);
}
}
}
show();
ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api);
ui->menuWindow_scale_factor->setEnabled(! checked);
emit resizeContents(scrnsz_x, scrnsz_y);
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api);
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer && show_second_monitors) {
renderers[i]->show();
renderers[i]->switchRenderer((RendererStack::Renderer)vid_api);
QApplication::processEvents();
}
}
}
static void
@@ -1558,6 +1657,9 @@ video_toggle_option(QAction* action, int *val)
endblit();
config_save();
device_force_redraw();
for (int i = 0; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) video_force_resize_set_monitor(1, i);
}
}
void MainWindow::on_actionInverted_VGA_monitor_triggered() {
@@ -1572,8 +1674,9 @@ static void update_scaled_checkboxes(Ui::MainWindow* ui, QAction* selected) {
reset_screen_size();
device_force_redraw();
video_force_resize_set(1);
atomic_flag_clear(&doresize);
for (int i = 0; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) video_force_resize_set_monitor(1, i);
}
config_save();
}
@@ -1752,7 +1855,6 @@ void MainWindow::on_actionChange_contrast_for_monochrome_display_triggered() {
void MainWindow::on_actionForce_4_3_display_ratio_triggered() {
video_toggle_option(ui->actionForce_4_3_display_ratio, &force_43);
video_force_resize_set(1);
}
void MainWindow::on_actionRemember_size_and_position_triggered()
@@ -1764,6 +1866,14 @@ void MainWindow::on_actionRemember_size_and_position_triggered()
window_x = geometry().x();
window_y = geometry().y();
}
for (int i = 1; i < MONITORS_NUM; i++) {
if (window_remember && renderers[i]) {
monitor_settings[i].mon_window_w = renderers[i]->geometry().width();
monitor_settings[i].mon_window_h = renderers[i]->geometry().height();
monitor_settings[i].mon_window_x = renderers[i]->geometry().x();
monitor_settings[i].mon_window_y = renderers[i]->geometry().y();
}
}
ui->actionRemember_size_and_position->setChecked(window_remember);
}
@@ -1778,7 +1888,10 @@ void MainWindow::on_actionHiDPI_scaling_triggered()
{
dpi_scale ^= 1;
ui->actionHiDPI_scaling->setChecked(dpi_scale);
emit resizeContents(scrnsz_x, scrnsz_y);
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i]) emit resizeContentsMonitor(monitors[i].mon_scrnsz_x, monitors[i].mon_scrnsz_y, i);
}
}
void MainWindow::on_actionHide_status_bar_triggered()
@@ -1794,7 +1907,7 @@ void MainWindow::on_actionHide_status_bar_triggered()
} else {
int vid_resize_orig = vid_resize;
vid_resize = 0;
emit resizeContents(scrnsz_x, scrnsz_y);
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
vid_resize = vid_resize_orig;
}
}
@@ -1812,7 +1925,7 @@ void MainWindow::on_actionHide_tool_bar_triggered()
} else {
int vid_resize_orig = vid_resize;
vid_resize = 0;
emit resizeContents(scrnsz_x, scrnsz_y);
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
vid_resize = vid_resize_orig;
}
}
@@ -1826,7 +1939,8 @@ void MainWindow::on_actionUpdate_status_bar_icons_triggered()
void MainWindow::on_actionTake_screenshot_triggered()
{
startblit();
screenshots++;
for (int i = 0; i < MONITORS_NUM; i++)
monitors[i].mon_screenshots++;
endblit();
device_force_redraw();
}
@@ -1892,8 +2006,13 @@ void MainWindow::on_actionRenderer_options_triggered()
{
auto dlg = ui->stackedWidget->getOptions(this);
if (dlg)
dlg->exec();
if (dlg) {
if (dlg->exec() == QDialog::Accepted) {
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i] && renderers[i]->hasOptions()) renderers[i]->reloadOptions();
}
}
}
}
void MainWindow::on_actionMCA_devices_triggered()
@@ -1904,3 +2023,36 @@ void MainWindow::on_actionMCA_devices_triggered()
dlg->exec();
}
void MainWindow::on_actionShow_non_primary_monitors_triggered()
{
show_second_monitors ^= 1;
if (show_second_monitors) {
for (int monitor_index = 1; monitor_index < MONITORS_NUM; monitor_index++) {
auto& secondaryRenderer = renderers[monitor_index];
if (!renderers[monitor_index]) continue;
secondaryRenderer->show();
if (window_remember) {
secondaryRenderer->setGeometry(monitor_settings[monitor_index].mon_window_x < 120 ? 120 : monitor_settings[monitor_index].mon_window_x,
monitor_settings[monitor_index].mon_window_y < 120 ? 120 : monitor_settings[monitor_index].mon_window_y,
monitor_settings[monitor_index].mon_window_w > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_w,
monitor_settings[monitor_index].mon_window_h > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_h);
}
secondaryRenderer->switchRenderer((RendererStack::Renderer)vid_api);
}
} else {
for (int monitor_index = 1; monitor_index < MONITORS_NUM; monitor_index++) {
auto& secondaryRenderer = renderers[monitor_index];
if (!renderers[monitor_index]) continue;
secondaryRenderer->hide();
if (window_remember && renderers[monitor_index]) {
monitor_settings[monitor_index].mon_window_w = renderers[monitor_index]->geometry().width();
monitor_settings[monitor_index].mon_window_h = renderers[monitor_index]->geometry().height();
monitor_settings[monitor_index].mon_window_x = renderers[monitor_index]->geometry().x();
monitor_settings[monitor_index].mon_window_y = renderers[monitor_index]->geometry().y();
}
}
}
}

View File

@@ -9,6 +9,7 @@
#include <memory>
class MediaMenu;
class RendererStack;
namespace Ui {
class MainWindow;
@@ -26,12 +27,13 @@ public:
void showMessage(int flags, const QString& header, const QString& message);
void getTitle(wchar_t* title);
void blitToWidget(int x, int y, int w, int h);
void blitToWidget(int x, int y, int w, int h, int monitor_index);
QSize getRenderWidgetSize();
void setSendKeyboardInput(bool enabled);
signals:
void paint(const QImage& image);
void resizeContents(int w, int h);
void resizeContentsMonitor(int w, int h, int monitor_index);
void pollMouse();
void statusBarMessage(const QString& msg);
void updateStatusBarPanes();
@@ -40,6 +42,10 @@ signals:
void updateStatusBarTip(int tag);
void updateMenuResizeOptions();
void updateWindowRememberOption();
void initRendererMonitor(int monitor_index);
void destroyRendererMonitor(int monitor_index);
void initRendererMonitorForNonQtThread(int monitor_index);
void destroyRendererMonitorForNonQtThread(int monitor_index);
void setTitle(const QString& title);
void setFullscreen(bool state);
@@ -51,6 +57,8 @@ public slots:
void showSettings();
void hardReset();
void togglePause();
void initRendererMonitorSlot(int monitor_index);
void destroyRendererMonitorSlot(int monitor_index);
private slots:
void on_actionFullscreen_triggered();
void on_actionSettings_triggered();
@@ -115,9 +123,13 @@ protected:
void closeEvent(QCloseEvent* event) override;
void changeEvent(QEvent* event) override;
private slots:
void on_actionShow_non_primary_monitors_triggered();
private:
Ui::MainWindow *ui;
std::unique_ptr<MachineStatus> status;
std::array<std::unique_ptr<RendererStack>, 8> renderers;
std::shared_ptr<MediaMenu> mm;
#ifdef Q_OS_MACOS

View File

@@ -54,7 +54,7 @@
<x>0</x>
<y>0</y>
<width>724</width>
<height>21</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuAction">
@@ -161,6 +161,7 @@
<addaction name="actionHide_tool_bar"/>
<addaction name="actionHide_status_bar"/>
<addaction name="separator"/>
<addaction name="actionShow_non_primary_monitors"/>
<addaction name="actionResizable_window"/>
<addaction name="actionRemember_size_and_position"/>
<addaction name="separator"/>
@@ -757,6 +758,14 @@
<number>5</number>
</property>
</action>
<action name="actionShow_non_primary_monitors">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show non-primary monitors</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -29,6 +29,7 @@
OpenGLRenderer::OpenGLRenderer(QWidget *parent)
: QWindow(parent->windowHandle())
, renderTimer(new QTimer(this))
, options(nullptr)
{
renderTimer->setTimerType(Qt::PreciseTimer);
/* TODO: need's more accuracy, maybe target 1ms earlier and spin yield */
@@ -165,9 +166,7 @@ OpenGLRenderer::initialize()
glTexImage2D(GL_TEXTURE_2D, 0, QOpenGLTexture::RGBA8_UNorm, INIT_WIDTH, INIT_HEIGHT, 0, QOpenGLTexture::BGRA, QOpenGLTexture::UInt32_RGBA8_Rev, NULL);
options = new OpenGLOptions(this, true, glslVersion);
applyOptions();
reloadOptions();
glClearColor(0.f, 0.f, 0.f, 1.f);
@@ -304,6 +303,15 @@ OpenGLRenderer::applyOptions()
currentFilter = options->filter();
}
void
OpenGLRenderer::reloadOptions()
{
if (options) { delete options; options = nullptr; }
options = new OpenGLOptions(this, true, glslVersion);
applyOptions();
}
void
OpenGLRenderer::applyShader(const OpenGLShaderPass &shader)
{

View File

@@ -53,6 +53,7 @@ public:
void finalize() override final;
bool hasOptions() const override { return true; }
QDialog *getOptions(QWidget *parent) override;
void reloadOptions() override;
signals:
void initialized();

View File

@@ -52,7 +52,7 @@ extern MainWindow* main_window;
QElapsedTimer elapsed_timer;
static std::atomic_int blitmx_contention = 0;
static std::mutex blitmx;
static std::recursive_mutex blitmx;
class CharPointer {
public:

View File

@@ -28,6 +28,8 @@ public:
virtual bool hasOptions() const { return false; }
/* Returns options dialog for renderer */
virtual QDialog *getOptions(QWidget *parent) { return nullptr; }
/* Reloads options of renderer */
virtual void reloadOptions() {}
virtual bool hasBlitFunc() { return false; }
virtual void blit(int x, int y, int w, int h) {}

View File

@@ -44,6 +44,7 @@
#endif
extern "C" {
#include <86box/86box.h>
#include <86box/mouse.h>
#include <86box/plat.h>
#include <86box/video.h>
@@ -51,14 +52,21 @@ extern "C" {
double mouse_sensitivity = 1.0;
}
struct mouseinputdata {
int deltax, deltay, deltaz;
int mousebuttons;
};
static mouseinputdata mousedata;
extern "C" void macos_poll_mouse();
extern MainWindow *main_window;
RendererStack::RendererStack(QWidget *parent)
RendererStack::RendererStack(QWidget *parent, int monitor_index)
: QStackedWidget(parent)
, ui(new Ui::RendererStack)
{
ui->setupUi(this);
m_monitor_index = monitor_index;
#if defined __unix__ && !defined __HAIKU__
char *mouse_type = getenv("EMU86BOX_MOUSE"), auto_mouse_type[16];
if (!mouse_type || (mouse_type[0] == '\0') || !stricmp(mouse_type, "auto")) {
@@ -313,7 +321,7 @@ RendererStack::createRenderer(Renderer renderer)
case Renderer::Direct3D9:
{
this->createWinId();
auto hw = new D3D9Renderer(this);
auto hw = new D3D9Renderer(this, m_monitor_index);
rendererWindow = hw;
connect(hw, &D3D9Renderer::error, this, [this](QString str)
{
@@ -398,14 +406,14 @@ RendererStack::createRenderer(Renderer renderer)
void
RendererStack::blitDummy(int x, int y, int w, int h)
{
video_blit_complete();
video_blit_complete_monitor(m_monitor_index);
blitDummied = true;
}
void
RendererStack::blitRenderer(int x, int y, int w, int h)
{
if (blitDummied) { blitDummied = false; video_blit_complete(); return; }
if (blitDummied) { blitDummied = false; video_blit_complete_monitor(m_monitor_index); return; }
directBlitting = true;
rendererWindow->blit(x, y, w, h);
directBlitting = false;
@@ -415,8 +423,8 @@ RendererStack::blitRenderer(int x, int y, int w, int h)
void
RendererStack::blitCommon(int x, int y, int w, int h)
{
if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || imagebufs.empty() || std::get<std::atomic_flag *>(imagebufs[currentBuf])->test_and_set() || blitDummied) {
video_blit_complete();
if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (monitors[m_monitor_index].target_buffer == NULL) || imagebufs.empty() || std::get<std::atomic_flag *>(imagebufs[currentBuf])->test_and_set() || blitDummied) {
video_blit_complete_monitor(m_monitor_index);
return;
}
sx = x;
@@ -426,13 +434,21 @@ RendererStack::blitCommon(int x, int y, int w, int h)
uint8_t *imagebits = std::get<uint8_t *>(imagebufs[currentBuf]);
for (int y1 = y; y1 < (y + h); y1++) {
auto scanline = imagebits + (y1 * rendererWindow->getBytesPerRow()) + (x * 4);
video_copy(scanline, &(buffer32->line[y1][x]), w * 4);
video_copy(scanline, &(monitors[m_monitor_index].target_buffer->line[y1][x]), w * 4);
}
if (screenshots) {
video_screenshot((uint32_t *) imagebits, x, y, 2048);
if (monitors[m_monitor_index].mon_screenshots) {
video_screenshot_monitor((uint32_t *) imagebits, x, y, 2048, m_monitor_index);
}
video_blit_complete();
video_blit_complete_monitor(m_monitor_index);
emit blitToRenderer(currentBuf, sx, sy, sw, sh);
currentBuf = (currentBuf + 1) % imagebufs.size();
}
void RendererStack::closeEvent(QCloseEvent* event)
{
if (cpu_thread_run == 0 || is_quit == 1) { event->accept(); return; }
event->ignore();
main_window->close();
}

View File

@@ -23,7 +23,7 @@ class RendererStack : public QStackedWidget {
Q_OBJECT
public:
explicit RendererStack(QWidget *parent = nullptr);
explicit RendererStack(QWidget *parent = nullptr, int monitor_index = 0);
~RendererStack();
void mousePressEvent(QMouseEvent *event) override;
@@ -31,6 +31,7 @@ public:
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void leaveEvent(QEvent *event) override;
void closeEvent(QCloseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override
{
event->ignore();
@@ -52,6 +53,8 @@ public:
/* Does current renderer implement options dialog */
bool hasOptions() const { return rendererWindow ? rendererWindow->hasOptions() : false; }
/* Reloads options of current renderer */
void reloadOptions() const { return rendererWindow->reloadOptions(); }
/* Returns options dialog for current renderer */
QDialog *getOptions(QWidget *parent) { return rendererWindow ? rendererWindow->getOptions(parent) : nullptr; }
@@ -87,16 +90,11 @@ private:
Ui::RendererStack *ui;
struct mouseinputdata {
int deltax, deltay, deltaz;
int mousebuttons;
};
mouseinputdata mousedata;
int x, y, w, h, sx, sy, sw, sh;
int currentBuf = 0;
int isMouseDown = 0;
int m_monitor_index = 0;
std::vector<std::tuple<uint8_t *, std::atomic_flag *>> imagebufs;

View File

@@ -46,6 +46,7 @@ SettingsDisplay::~SettingsDisplay()
void SettingsDisplay::save() {
gfxcard = ui->comboBoxVideo->currentData().toInt();
gfxcard_2 = ui->comboBoxVideoSecondary->currentData().toInt();
voodoo_enabled = ui->checkBoxVoodoo->isChecked() ? 1 : 0;
ibm8514_enabled = ui->checkBox8514->isChecked() ? 1 : 0;
xga_enabled = ui->checkBoxXga->isChecked() ? 1 : 0;
@@ -87,11 +88,16 @@ void SettingsDisplay::onCurrentMachineChanged(int machineId) {
if (machine_has_flags(machineId, MACHINE_VIDEO_ONLY) > 0) {
ui->comboBoxVideo->setEnabled(false);
ui->comboBoxVideoSecondary->setEnabled(false);
ui->pushButtonConfigureSecondary->setEnabled(false);
selectedRow = 1;
} else {
ui->comboBoxVideo->setEnabled(true);
ui->comboBoxVideoSecondary->setEnabled(true);
ui->pushButtonConfigureSecondary->setEnabled(true);
}
ui->comboBoxVideo->setCurrentIndex(selectedRow);
if (gfxcard_2 == 0) ui->pushButtonConfigureSecondary->setEnabled(false);
}
void SettingsDisplay::on_pushButtonConfigure_clicked() {
@@ -137,6 +143,41 @@ void SettingsDisplay::on_comboBoxVideo_currentIndexChanged(int index) {
ui->checkBoxXga->setChecked(xga_enabled);
ui->pushButtonConfigureXga->setEnabled((hasIsa16 || has_MCA) && ui->checkBoxXga->isChecked());
int c = 2;
ui->comboBoxVideoSecondary->clear();
ui->comboBoxVideoSecondary->addItem(QObject::tr("None"), 0);
ui->comboBoxVideoSecondary->setCurrentIndex(0);
// TODO: Implement support for selecting non-MDA secondary cards properly when MDA cards are the primary ones.
if (video_card_get_flags(videoCard) == VIDEO_FLAG_TYPE_MDA) {
ui->comboBoxVideoSecondary->setCurrentIndex(0);
return;
}
while (true) {
const device_t* video_dev = video_card_getdevice(c);
QString name = DeviceConfig::DeviceName(video_dev, video_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (video_card_available(c) &&
device_is_valid(video_dev, machineId) &&
!(video_card_get_flags(c) == video_card_get_flags(videoCard))) {
ui->comboBoxVideoSecondary->addItem(name, c);
if (c == gfxcard_2)
ui->comboBoxVideoSecondary->setCurrentIndex(ui->comboBoxVideoSecondary->count() - 1);
}
c++;
}
if (gfxcard_2 == 0 || (machine_has_flags(machineId, MACHINE_VIDEO_ONLY) > 0))
{
ui->comboBoxVideoSecondary->setCurrentIndex(0);
ui->pushButtonConfigureSecondary->setEnabled(false);
}
}
void SettingsDisplay::on_checkBoxVoodoo_stateChanged(int state) {
@@ -146,3 +187,21 @@ void SettingsDisplay::on_checkBoxVoodoo_stateChanged(int state) {
void SettingsDisplay::on_checkBoxXga_stateChanged(int state) {
ui->pushButtonConfigureXga->setEnabled(state == Qt::Checked);
}
void SettingsDisplay::on_comboBoxVideoSecondary_currentIndexChanged(int index)
{
if (index < 0) {
ui->pushButtonConfigureSecondary->setEnabled(false);
return;
}
int videoCard = ui->comboBoxVideoSecondary->currentData().toInt();
ui->pushButtonConfigureSecondary->setEnabled(index != 0 && video_card_has_config(videoCard) > 0);
}
void SettingsDisplay::on_pushButtonConfigureSecondary_clicked()
{
auto* device = video_card_getdevice(ui->comboBoxVideoSecondary->currentData().toInt());
DeviceConfig::ConfigureDevice(device, 0, qobject_cast<Settings*>(Settings::settings));
}

View File

@@ -20,6 +20,12 @@ public:
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_pushButtonConfigureSecondary_clicked();
private slots:
void on_comboBoxVideoSecondary_currentIndexChanged(int index);
private slots:
void on_checkBoxVoodoo_stateChanged(int state);
void on_checkBoxXga_stateChanged(int state);

View File

@@ -26,16 +26,29 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxVideo"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox8514">
<property name="text">
<string>Video:</string>
<string>8514/A</string>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxVideo"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonConfigure">
<property name="sizePolicy">
@@ -49,53 +62,57 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonConfigureVoodoo">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxVoodoo">
<property name="text">
<string>Voodoo Graphics</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox8514">
<property name="text">
<string>8514/A</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pushButtonConfigureXga">
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonConfigureVoodoo">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Video:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxXga">
<property name="text">
<string>XGA</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="4" column="2">
<widget class="QPushButton" name="pushButtonConfigureXga">
<property name="text">
<string>Configure</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Video #2:</string>
</property>
</spacer>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxVideoSecondary"/>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonConfigureSecondary">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@@ -20,9 +20,12 @@
#include "qt_mainwindow.hpp"
#include "ui_qt_mainwindow.h"
#include "qt_util.hpp"
#include <QStatusBar>
#include <QMenuBar>
#include <QTimer>
#include <QScreen>
extern "C"
{
@@ -44,6 +47,11 @@ SpecifyDimensions::SpecifyDimensions(QWidget *parent) :
ui->spinBoxWidth->setValue(main_window->getRenderWidgetSize().width());
ui->spinBoxHeight->setRange(16, 2048);
ui->spinBoxHeight->setValue(main_window->getRenderWidgetSize().height());
if (dpi_scale == 0) {
ui->spinBoxWidth->setValue(main_window->getRenderWidgetSize().width() * util::screenOfWidget(main_window)->devicePixelRatio());
ui->spinBoxHeight->setValue(main_window->getRenderWidgetSize().height() * util::screenOfWidget(main_window)->devicePixelRatio());
}
}
SpecifyDimensions::~SpecifyDimensions()
@@ -62,14 +70,21 @@ void SpecifyDimensions::on_SpecifyDimensions_accepted()
fixed_size_x = ui->spinBoxWidth->value();
fixed_size_y = ui->spinBoxHeight->value();
main_window->setFixedSize(ui->spinBoxWidth->value(),
ui->spinBoxHeight->value()
+ (!hide_status_bar ? main_window->statusBar()->height() : 0)
+ (!hide_tool_bar ? main_window->ui->toolBar->height() : 0)
+ main_window->menuBar()->height());
main_window->resizeContents(fixed_size_x, fixed_size_y);
emit main_window->updateMenuResizeOptions();
main_window->show();
for (int i = 1; i < MONITORS_NUM; i++) {
if (main_window->renderers[i]) {
main_window->renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
main_window->renderers[i]->setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
emit main_window->resizeContentsMonitor(fixed_size_x, fixed_size_y, i);
if (show_second_monitors) {
main_window->renderers[i]->show();
main_window->renderers[i]->switchRenderer((RendererStack::Renderer)vid_api);
}
}
}
main_window->ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api);
}
else
@@ -83,6 +98,16 @@ void SpecifyDimensions::on_SpecifyDimensions_accepted()
window_h = ui->spinBoxHeight->value();
main_window->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
emit main_window->resizeContents(ui->spinBoxWidth->value(), ui->spinBoxHeight->value());
for (int i = 1; i < MONITORS_NUM; i++) {
if (main_window->renderers[i]) {
main_window->renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint);
main_window->renderers[i]->setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false);
emit main_window->resizeContentsMonitor(ui->spinBoxWidth->value(), ui->spinBoxHeight->value(), i);
main_window->renderers[i]->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
if (show_second_monitors) { main_window->renderers[i]->show();
main_window->renderers[i]->switchRenderer((RendererStack::Renderer)vid_api); }
}
}
vid_resize = 1;
emit main_window->updateMenuResizeOptions();
}

View File

@@ -55,17 +55,18 @@ wchar_t* ui_window_title(wchar_t* str)
return str;
}
extern "C" void qt_blit(int x, int y, int w, int h)
extern "C" void qt_blit(int x, int y, int w, int h, int monitor_index)
{
main_window->blitToWidget(x, y, w, h);
main_window->blitToWidget(x, y, w, h, monitor_index);
}
void mouse_poll() {
main_window->pollMouse();
}
void plat_resize(int w, int h) {
main_window->resizeContents(w, h);
void plat_resize_monitor(int w, int h, int monitor_index) {
if (monitor_index >= 1) main_window->resizeContentsMonitor(w, h, monitor_index);
else main_window->resizeContents(w, h);
}
void plat_setfullscreen(int on) {
@@ -98,6 +99,20 @@ int ui_msgbox_header(int flags, void *header, void* message) {
return 0;
}
void ui_init_monitor(int monitor_index) {
if (QThread::currentThread() == main_window->thread()) {
emit main_window->initRendererMonitor(monitor_index);
}
else emit main_window->initRendererMonitorForNonQtThread(monitor_index);
}
void ui_deinit_monitor(int monitor_index) {
if (QThread::currentThread() == main_window->thread()) {
emit main_window->destroyRendererMonitor(monitor_index);
}
else emit main_window->destroyRendererMonitorForNonQtThread(monitor_index);
}
int ui_msgbox(int flags, void *message) {
return ui_msgbox_header(flags, nullptr, message);
}

View File

@@ -36,6 +36,7 @@ static zwp_pointer_constraints_v1* conf_pointer_interface = nullptr;
static zwp_locked_pointer_v1* conf_pointer = nullptr;
static int rel_mouse_x = 0, rel_mouse_y = 0;
static bool wl_init_ok = false;
void rel_mouse_event(void* data, zwp_relative_pointer_v1* zwp_relative_pointer_v1, uint32_t tstmp, uint32_t tstmpl, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_real, wl_fixed_t dy_real)
{
@@ -92,15 +93,18 @@ static const struct wl_registry_listener registry_listener = {
void wl_init()
{
wl_display* display = (wl_display*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display");
if (display)
{
auto registry = wl_display_get_registry(display);
if (registry)
if (!wl_init_ok) {
wl_display* display = (wl_display*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display");
if (display)
{
wl_registry_add_listener(registry, &registry_listener, nullptr);
wl_display_roundtrip(display);
auto registry = wl_display_get_registry(display);
if (registry)
{
wl_registry_add_listener(registry, &registry_listener, nullptr);
wl_display_roundtrip(display);
}
}
wl_init_ok = true;
}
}