From 01ea0f3c74e10af1613e9e272c469a03f6701419 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Fri, 2 Apr 2021 06:46:32 -0400
Subject: [PATCH] configure_graphics: Add Borderless Windowed fullscreen mode

The borderless windowed fullscreen mode solves several issues with the presentation of the overlay dialogs and on-screen keyboard in exclusive fullscreen mode, and also has other benefits such as smoother gameplay, lower latency and a significant reduction in screen tearing.

Co-authored-by: Its-Rei <kupfel@gmail.com>
---
 src/core/settings.h                           |  1 +
 src/yuzu/configuration/config.cpp             |  2 +
 src/yuzu/configuration/configure_graphics.cpp | 19 ++++++-
 src/yuzu/configuration/configure_graphics.ui  | 41 +++++++++++++-
 src/yuzu/main.cpp                             | 54 ++++++++++++++++---
 5 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/src/core/settings.h b/src/core/settings.h
index d849dded38..0501bf0db4 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -139,6 +139,7 @@ struct Values {
     Setting<int> vulkan_device;
 
     Setting<u16> resolution_factor{1};
+    Setting<int> fullscreen_mode;
     Setting<int> aspect_ratio;
     Setting<int> max_anisotropy;
     Setting<bool> use_frame_limit;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1bac57bb2d..712a699445 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -770,6 +770,7 @@ void Config::ReadRendererValues() {
     ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
     ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
     ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
+    ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 0);
     ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
     ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
     ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
@@ -1333,6 +1334,7 @@ void Config::SaveRendererValues() {
                        Settings::values.renderer_backend.UsingGlobal(), 0);
     WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
     WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
+    WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 0);
     WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
     WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
     WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 49acc48b22..8a2008b2a9 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -77,18 +77,25 @@ void ConfigureGraphics::SetConfiguration() {
 
     if (Settings::IsConfiguringGlobal()) {
         ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
+        ui->fullscreen_mode_combobox->setCurrentIndex(Settings::values.fullscreen_mode.GetValue());
         ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
     } else {
         ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
         ConfigurationShared::SetHighlight(ui->api_layout,
                                           !Settings::values.renderer_backend.UsingGlobal());
+
+        ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox,
+                                               &Settings::values.fullscreen_mode);
+        ConfigurationShared::SetHighlight(ui->fullscreen_mode_label,
+                                          !Settings::values.fullscreen_mode.UsingGlobal());
+
         ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
                                                &Settings::values.aspect_ratio);
+        ConfigurationShared::SetHighlight(ui->ar_label,
+                                          !Settings::values.aspect_ratio.UsingGlobal());
 
         ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
         ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
-        ConfigurationShared::SetHighlight(ui->ar_label,
-                                          !Settings::values.aspect_ratio.UsingGlobal());
         ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
     }
 
@@ -107,6 +114,9 @@ void ConfigureGraphics::ApplyConfiguration() {
         if (Settings::values.vulkan_device.UsingGlobal()) {
             Settings::values.vulkan_device.SetValue(vulkan_device);
         }
+        if (Settings::values.fullscreen_mode.UsingGlobal()) {
+            Settings::values.fullscreen_mode.SetValue(ui->fullscreen_mode_combobox->currentIndex());
+        }
         if (Settings::values.aspect_ratio.UsingGlobal()) {
             Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
         }
@@ -140,6 +150,8 @@ void ConfigureGraphics::ApplyConfiguration() {
             }
         }
 
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode,
+                                                 ui->fullscreen_mode_combobox);
         ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
                                                  ui->aspect_ratio_combobox);
 
@@ -253,6 +265,7 @@ void ConfigureGraphics::SetupPerGameUI() {
     if (Settings::IsConfiguringGlobal()) {
         ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
         ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
+        ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal());
         ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
         ui->use_asynchronous_gpu_emulation->setEnabled(
             Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
@@ -278,6 +291,8 @@ void ConfigureGraphics::SetupPerGameUI() {
 
     ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
                                             Settings::values.aspect_ratio.GetValue(true));
+    ConfigurationShared::SetColoredComboBox(ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
+                                            Settings::values.fullscreen_mode.GetValue(true));
     ConfigurationShared::InsertGlobalItem(
         ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
 }
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 58486eb1e4..ab0bd4d773 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -104,9 +104,48 @@
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QWidget" name="fullscreen_mode_layout" native="true">
+          <layout class="QHBoxLayout" name="horizontalLayout_1">
+           <property name="leftMargin">
+            <number>0</number>
+           </property>
+           <property name="topMargin">
+            <number>0</number>
+           </property>
+           <property name="rightMargin">
+            <number>0</number>
+           </property>
+           <property name="bottomMargin">
+            <number>0</number>
+           </property>
+           <item>
+            <widget class="QLabel" name="fullscreen_mode_label">
+             <property name="text">
+              <string>Fullscreen Mode:</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QComboBox" name="fullscreen_mode_combobox">
+             <item>
+              <property name="text">
+               <string>Borderless Windowed</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Exclusive Fullscreen</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
         <item>
          <widget class="QWidget" name="aspect_ratio_layout" native="true">
-          <layout class="QHBoxLayout" name="horizontalLayout_6">
+          <layout class="QHBoxLayout" name="horizontalLayout_2">
            <property name="leftMargin">
             <number>0</number>
            </property>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 06445b993d..23ea4983db 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2295,24 +2295,66 @@ void GMainWindow::ToggleFullscreen() {
 void GMainWindow::ShowFullscreen() {
     if (ui.action_Single_Window_Mode->isChecked()) {
         UISettings::values.geometry = saveGeometry();
+
         ui.menubar->hide();
         statusBar()->hide();
-        showFullScreen();
+
+        if (Settings::values.fullscreen_mode.GetValue() == 1) {
+            showFullScreen();
+            return;
+        }
+
+        hide();
+        setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
+        const auto screen_geometry = QApplication::desktop()->screenGeometry(this);
+        setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
+                    screen_geometry.height() + 1);
+        raise();
+        showNormal();
     } else {
         UISettings::values.renderwindow_geometry = render_window->saveGeometry();
-        render_window->showFullScreen();
+
+        if (Settings::values.fullscreen_mode.GetValue() == 1) {
+            render_window->showFullScreen();
+            return;
+        }
+
+        render_window->hide();
+        render_window->setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
+        const auto screen_geometry = QApplication::desktop()->screenGeometry(this);
+        render_window->setGeometry(screen_geometry.x(), screen_geometry.y(),
+                                   screen_geometry.width(), screen_geometry.height() + 1);
+        render_window->raise();
+        render_window->showNormal();
     }
 }
 
 void GMainWindow::HideFullscreen() {
     if (ui.action_Single_Window_Mode->isChecked()) {
+        if (Settings::values.fullscreen_mode.GetValue() == 1) {
+            showNormal();
+            restoreGeometry(UISettings::values.geometry);
+        } else {
+            hide();
+            setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
+            restoreGeometry(UISettings::values.geometry);
+            raise();
+            show();
+        }
+
         statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
         ui.menubar->show();
-        showNormal();
-        restoreGeometry(UISettings::values.geometry);
     } else {
-        render_window->showNormal();
-        render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
+        if (Settings::values.fullscreen_mode.GetValue() == 1) {
+            render_window->showNormal();
+            render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
+        } else {
+            render_window->hide();
+            render_window->setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
+            render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
+            render_window->raise();
+            render_window->show();
+        }
     }
 }