diff --git a/src/qt/qt.c b/src/qt/qt.c index f167660cd..7bbc91036 100644 --- a/src/qt/qt.c +++ b/src/qt/qt.c @@ -5,6 +5,7 @@ #if !defined(_WIN32) || !defined(__clang__) #include #endif +#include #include #include diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp index ec0f71f3a..e286cf52f 100644 --- a/src/qt/qt_hardwarerenderer.cpp +++ b/src/qt/qt_hardwarerenderer.cpp @@ -1,10 +1,12 @@ #include "qt_hardwarerenderer.hpp" #include +#include #include extern "C" { #include <86box/86box.h> #include <86box/plat.h> +#include <86box/video.h> } void HardwareRenderer::resizeGL(int w, int h) @@ -12,14 +14,74 @@ void HardwareRenderer::resizeGL(int w, int h) glViewport(0, 0, w, h); } +#define PROGRAM_VERTEX_ATTRIBUTE 0 +#define PROGRAM_TEXCOORD_ATTRIBUTE 1 + void HardwareRenderer::initializeGL() { m_context->makeCurrent(this); initializeOpenGLFunctions(); + m_texture = new QOpenGLTexture(image); + m_blt = new QOpenGLTextureBlitter; + m_blt->setRedBlueSwizzle(true); + m_blt->create(); + QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); + const char *vsrc = + "attribute highp vec4 vertex;\n" + "attribute mediump vec4 texCoord;\n" + "varying mediump vec4 texc;\n" + "uniform mediump mat4 matrix;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + " texc = texCoord;\n" + "}\n"; + vshader->compileSourceCode(vsrc); + + QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); + const char *fsrc = + "uniform sampler2D texture;\n" + "varying mediump vec4 texc;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = texture2D(texture, texc.st).bgra;\n" + "}\n"; + fshader->compileSourceCode(fsrc); + + m_prog = new QOpenGLShaderProgram; + m_prog->addShader(vshader); + m_prog->addShader(fshader); + m_prog->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE); + m_prog->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE); + m_prog->link(); + + m_prog->bind(); + m_prog->setUniformValue("texture", 0); } void HardwareRenderer::paintGL() { - onPaint(this); + m_context->makeCurrent(this); + QVector verts, texcoords; + QMatrix4x4 mat; + mat.setToIdentity(); + mat.ortho(QRect(0, 0, width(), height())); + verts.push_back(QVector2D((float)destination.x(), (float)destination.y())); + verts.push_back(QVector2D((float)destination.x(), (float)destination.y() + destination.height())); + verts.push_back(QVector2D((float)destination.x() + destination.width(), (float)destination.y() + destination.height())); + verts.push_back(QVector2D((float)destination.x() + destination.width(), (float)destination.y())); + texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y()) / 2048.f)); + texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f)); + texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f)); + texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f)); + + m_prog->setUniformValue("matrix", mat); + m_prog->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); + m_prog->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); + m_prog->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, verts.data()); + m_prog->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, texcoords.data()); + m_texture->bind(); + m_texture->setMinMagFilters(video_filter_method ? QOpenGLTexture::Linear : QOpenGLTexture::Nearest, video_filter_method ? QOpenGLTexture::Linear : QOpenGLTexture::Nearest); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } void HardwareRenderer::setRenderType(RenderType type) { @@ -37,7 +99,11 @@ void HardwareRenderer::setRenderType(RenderType type) { } void HardwareRenderer::onBlit(const std::unique_ptr* img, int x, int y, int w, int h, std::atomic_flag* in_use) { - memcpy(image.bits(), img->get(), 2048 * 2048 * 4); + auto tval = this; + void* nuldata = 0; + if (memcmp(&tval, &nuldata, sizeof(void*)) == 0) return; + m_context->makeCurrent(this); + m_texture->setData(QOpenGLTexture::PixelFormat::RGBA, QOpenGLTexture::PixelType::UInt8, (const void*)img->get()); in_use->clear(); source.setRect(x, y, w, h); update(); diff --git a/src/qt/qt_hardwarerenderer.hpp b/src/qt/qt_hardwarerenderer.hpp index d2152def5..f64fa121f 100644 --- a/src/qt/qt_hardwarerenderer.hpp +++ b/src/qt/qt_hardwarerenderer.hpp @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -25,6 +29,9 @@ private: bool wayland = false; QWidget* parentWidget{nullptr}; QOpenGLContext* m_context; + QOpenGLTexture* m_texture; + QOpenGLShaderProgram* m_prog; + QOpenGLTextureBlitter* m_blt; public: void resizeGL(int w, int h) override; void initializeGL() override; @@ -41,7 +48,8 @@ public: } ~HardwareRenderer() { - makeCurrent(); + m_context->makeCurrent(this); + if (m_blt) m_blt->destroy(); } enum class RenderType { diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 1b014fda5..91fee7408 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -94,6 +95,9 @@ main_thread_fn() int main(int argc, char* argv[]) { QApplication app(argc, argv); + QSurfaceFormat fmt = QSurfaceFormat::defaultFormat(); + fmt.setSwapInterval(0); + QSurfaceFormat::setDefaultFormat(fmt); app.setStyle(new StyleOverride()); #ifdef __APPLE__ CocoaEventFilter cocoafilter; @@ -101,9 +105,16 @@ int main(int argc, char* argv[]) { #endif elapsed_timer.start(); - pc_init(argc, argv); + if (!pc_init(argc, argv)) + { + return 0; + } if (! pc_init_modules()) { +#ifdef Q_OS_MACOS + ui_msgbox_header(MBX_FATAL, VC(L"No ROMs found."), VC(L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"~/Library/Application Support/net.86box.86box/roms\" directory.")); +#else ui_msgbox_header(MBX_FATAL, VC(L"No ROMs found."), VC(L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory.")); +#endif return 6; } @@ -134,6 +145,7 @@ int main(int argc, char* argv[]) { /* Set the PAUSE mode depending on the renderer. */ // plat_pause(0); + if (settings_only) dopause = 1; QTimer onesec; QObject::connect(&onesec, &QTimer::timeout, &app, [] { pc_onesec(); diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 6ea9f9c0f..49394a06b 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -138,6 +138,17 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionHiDPI_scaling->setChecked(dpi_scale); ui->actionHide_status_bar->setChecked(hide_status_bar); ui->actionUpdate_status_bar_icons->setChecked(update_icons); + +#if defined Q_OS_WINDOWS || defined Q_OS_MACOS + /* Make the option visible only if ANGLE is loaded. */ + ui->actionHardware_Renderer_OpenGL_ES->setVisible(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES); + if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES && vid_api == 2) vid_api = 1; +#endif + + if (QApplication::platformName().contains("eglfs") && vid_api >= 1) { + fprintf(stderr, "OpenGL renderers are unsupported on EGLFS.\n"); + vid_api = 0; + } QActionGroup* actGroup = nullptr; switch (vid_api) { case 0: @@ -254,11 +265,6 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionChange_contrast_for_monochrome_display->setChecked(true); } -#if defined Q_OS_WINDOWS || defined Q_OS_MACOS - /* Make the option visible only if ANGLE is loaded. */ - ui->actionHardware_Renderer_OpenGL_ES->setVisible(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES); -#endif - setFocusPolicy(Qt::StrongFocus); ui->stackedWidget->setFocusPolicy(Qt::NoFocus); ui->centralwidget->setFocusPolicy(Qt::NoFocus); @@ -270,7 +276,7 @@ MainWindow::MainWindow(QWidget *parent) : } void MainWindow::closeEvent(QCloseEvent *event) { - if (confirm_exit) + if (confirm_exit && cpu_thread_run) { QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", "Are you sure you want to exit 86Box?", QMessageBox::Yes | QMessageBox::No, this); QCheckBox *chkbox = new QCheckBox("Do not ask me again"); @@ -310,7 +316,7 @@ void MainWindow::showEvent(QShowEvent *event) { setGeometry(window_x, window_y, window_w, window_h + menuBar()->height() + (hide_status_bar ? 0 : statusBar()->height())); } if (vid_resize == 2) { - setFixedSize(fixed_size_x, fixed_size_y + this->menuBar()->height() + this->statusBar()->height()); + setFixedSize(fixed_size_x, fixed_size_y + menuBar()->height() + (hide_status_bar ? 0 : statusBar()->height())); scrnsz_x = fixed_size_x; scrnsz_y = fixed_size_y; } @@ -374,6 +380,10 @@ void MainWindow::on_actionSettings_triggered() { break; } plat_pause(currentPause); + if (settings_only) { + cpu_thread_run = 0; + close(); + } } std::array x11_to_xt_base @@ -861,10 +871,11 @@ static std::array& selected_keycode = x11_to_xt_base; uint16_t x11_keycode_to_keysym(uint32_t keycode) { + uint16_t finalkeycode = 0; #if defined(Q_OS_WINDOWS) - return keycode & 0xFFFF; + finalkeycode = (keycode & 0xFFFF); #elif defined(__APPLE__) - return darwin_to_xt[keycode]; + finalkeycode = darwin_to_xt[keycode]; #else static Display* x11display = nullptr; if (QApplication::platformName().contains("wayland")) @@ -874,8 +885,8 @@ uint16_t x11_keycode_to_keysym(uint32_t keycode) else if (QApplication::platformName().contains("eglfs")) { keycode -= 8; - if (keycode <= 88) return keycode; - else return evdev_to_xt[keycode]; + if (keycode <= 88) finalkeycode = keycode; + else finalkeycode = evdev_to_xt[keycode]; } else if (!x11display) { @@ -889,18 +900,27 @@ uint16_t x11_keycode_to_keysym(uint32_t keycode) selected_keycode = x11_to_xt_vnc; } } - return selected_keycode[keycode]; + if (!QApplication::platformName().contains("eglfs")) finalkeycode = selected_keycode[keycode]; #endif + if (rctrl_is_lalt && finalkeycode == 0x11D) + { + finalkeycode = 0x38; + } + return finalkeycode; } void MainWindow::on_actionFullscreen_triggered() { if (video_fullscreen > 0) { showNormal(); ui->menubar->show(); - ui->statusbar->show(); + if (!hide_status_bar) ui->statusbar->show(); video_fullscreen = 0; - setGeometry(geometry()); + if (vid_resize != 1) { + if (vid_resize == 2) setFixedSize(fixed_size_x, fixed_size_y + menuBar()->height() + (!hide_status_bar ? statusBar()->height() : 0)); + emit resizeContents(scrnsz_x, scrnsz_y); + } } else { + setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); ui->menubar->hide(); ui->statusbar->hide(); showFullScreen(); @@ -963,7 +983,7 @@ void MainWindow::showMessage_(const QString &header, const QString &message) { void MainWindow::keyPressEvent(QKeyEvent* event) { - if (send_keyboard_input) + if (send_keyboard_input && !(kbd_req_capture && !mouse_capture && !video_fullscreen)) { #ifdef __APPLE__ keyboard_input(1, x11_keycode_to_keysym(event->nativeVirtualKey())); @@ -1027,7 +1047,8 @@ void MainWindow::on_actionHardware_Renderer_OpenGL_ES_triggered() { void MainWindow::focusInEvent(QFocusEvent* event) { - this->grabKeyboard(); + if (settings_only) ui->actionSettings->trigger(); + else this->grabKeyboard(); } void MainWindow::focusOutEvent(QFocusEvent* event) @@ -1047,6 +1068,7 @@ void MainWindow::on_actionResizable_window_triggered(bool checked) { setWindowFlag(Qt::MSWindowsFixedSizeDialogHint); } show(); + ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api); ui->menuWindow_scale_factor->setEnabled(! checked); emit resizeContents(scrnsz_x, scrnsz_y); diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index d33ba24ac..a41509b9c 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -11,16 +11,20 @@ #include #include #include +#include #include #include +#include "qt_mainwindow.hpp" + #ifdef Q_OS_UNIX #include #endif // static QByteArray buf; extern QElapsedTimer elapsed_timer; +extern MainWindow* main_window; QElapsedTimer elapsed_timer; static std::atomic_int blitmx_contention = 0; @@ -50,6 +54,8 @@ extern "C" { #ifdef Q_OS_WINDOWS #define NOMINMAX #include +#else +#include #endif #include <86box/86box.h> #include <86box/device.h> @@ -75,14 +81,20 @@ uint32_t lang_id = 0x0409, lang_sys = 0x0409; // Multilangual UI variables, for int stricmp(const char* s1, const char* s2) { - return QByteArray(s1).compare(s2, Qt::CaseInsensitive); +#ifdef Q_OS_WINDOWS + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif } int strnicmp(const char *s1, const char *s2, size_t n) { - QByteArray b1(s1, std::min(strlen(s1), n)); - QByteArray b2(s2, std::min(strlen(s2), n)); - return b1.compare(b2, Qt::CaseInsensitive); +#ifdef Q_OS_WINDOWS + return _strnicmp(s1, s2, n); +#else + return strncasecmp(s1, s2, n); +#endif } void @@ -283,14 +295,9 @@ plat_tempfile(char *bufp, char *prefix, char *suffix) name.append(QString("%1-").arg(prefix)); } - name.append("XXXXXX"); - - if (suffix != nullptr) { - name.append(suffix); - } - QTemporaryFile temp(name); - QByteArray buf(bufp); - buf = temp.fileName().toUtf8(); + name.append(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss-zzzz")); + if (suffix) name.append(suffix); + sprintf(&bufp[strlen(bufp)], "%s", name.toUtf8().data()); } void plat_remove(char* path) @@ -359,6 +366,7 @@ plat_power_off(void) cycles -= 99999999; cpu_thread_run = 0; + main_window->close(); } void set_language(uint32_t id) { diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 15ab840d1..8529f0f66 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -203,6 +203,7 @@ void RendererStack::switchRenderer(Renderer renderer) { current->setFocusProxy(this); addWidget(current.get()); + this->setStyleSheet("background-color: black"); for (auto& in_use : buffers_in_use) in_use.clear(); @@ -226,7 +227,7 @@ void RendererStack::blit(int x, int y, int w, int h) if (screenshots) { - video_screenshot((uint32_t *)imagebits, 0, 0, 2048); + video_screenshot((uint32_t *)imagebits, x, y, 2048); } video_blit_complete(); blitToRenderer(&imagebufs[currentBuf], sx, sy, sw, sh, &buffers_in_use[currentBuf]); diff --git a/src/qt/qt_softwarerenderer.cpp b/src/qt/qt_softwarerenderer.cpp index 9a64fe043..d9195d7b6 100644 --- a/src/qt/qt_softwarerenderer.cpp +++ b/src/qt/qt_softwarerenderer.cpp @@ -8,6 +8,9 @@ void SoftwareRenderer::paintEvent(QPaintEvent *event) { } void SoftwareRenderer::onBlit(const std::unique_ptr* img, int x, int y, int w, int h, std::atomic_flag* in_use) { + auto tval = this; + void* nuldata = 0; + if (memcmp(&tval, &nuldata, sizeof(void*)) == 0) return; memcpy(image.bits(), img->get(), 2048 * 2048 * 4); in_use->clear(); source.setRect(x, y, w, h); diff --git a/src/qt/qt_specifydimensions.cpp b/src/qt/qt_specifydimensions.cpp index 85da6212b..ca4aa5706 100644 --- a/src/qt/qt_specifydimensions.cpp +++ b/src/qt/qt_specifydimensions.cpp @@ -45,14 +45,17 @@ void SpecifyDimensions::on_SpecifyDimensions_accepted() window_remember = 0; 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) + main_window->menuBar()->height()); + main_window->setFixedSize(ui->spinBoxWidth->value(), ui->spinBoxHeight->value() + (!hide_status_bar ? main_window->statusBar()->height() : 0) + main_window->menuBar()->height()); emit main_window->updateMenuResizeOptions(); main_window->show(); + main_window->ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api); } else { - if (vid_resize != 1) main_window->ui->actionResizable_window->trigger(); + main_window->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + main_window->ui->actionResizable_window->setChecked(false); vid_resize = 0; + main_window->ui->actionResizable_window->trigger(); window_remember = 1; window_w = ui->spinBoxWidth->value(); window_h = ui->spinBoxHeight->value();