diff --git a/README.md b/README.md
index 519c40e..2ae61a8 100644
--- a/README.md
+++ b/README.md
@@ -26,15 +26,14 @@ Place the directory wherever you like (`%localappdata%\Programs` is a good one)
## Usage
-
-If you have a two monitors setup (Main monitor + TV), i do recommend you uncheck `Do not use displayswitch`.
-displayswitch.exe will automatically disable your primary monitor and enable your external monitor.
-
-If you have more than two monitors, you'll need to check it, and select your preferred monitor in Steam Big Picture settings.
-
Specify your audio outputs.
You can use a short name. BigPictureTV will try to find the correct audio output from keywords. Less is more.
+For monitor switching, BigPictureTV relies on Windows built in `displayswitch.exe`.
+Documentation for this is pretty weak, from my personal testing when using external screen with multiple monitors displayswitch seems to select the highest resolution monitor. For most case it will work just fine but not always.
+
+If it does not work for you, you should check `Clone screen instead of switching`.
+
**You're all set!** You can now close the settings window.
## Build
diff --git a/assets/screenshot.png b/assets/screenshot.png
index e313e73..d64f582 100644
Binary files a/assets/screenshot.png and b/assets/screenshot.png differ
diff --git a/src/BigPictureTV.py b/src/BigPictureTV.py
index 97e2537..4fd56cb 100644
--- a/src/BigPictureTV.py
+++ b/src/BigPictureTV.py
@@ -1,112 +1,19 @@
import sys
import os
import json
-import subprocess
-import time
-import re
-import winshell
-import pygetwindow as gw
-from enum import Enum
import darkdetect
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QMainWindow
from PyQt6.QtGui import QIcon, QAction
from PyQt6.QtCore import QTimer, QSharedMemory
from design import Ui_MainWindow
-from steam_language_reader import get_big_picture_window_title
+from monitor_manager import enable_clone_mode, enable_external_mode, enable_internal_mode
+from audio_manager import switch_audio, is_audio_device_cmdlets_installed
+from mode_manager import Mode, read_current_mode, write_current_mode
+from shortcut_manager import check_startup_shortcut, handle_startup_checkbox_state_changed
+from window_monitor import is_bigpicture_running
SETTINGS_FILE = os.path.join(os.environ["APPDATA"], "BigPictureTV", "settings.json")
ICONS_FOLDER = "icons" if getattr(sys, "frozen", False) else os.path.join(os.path.dirname(__file__), "icons")
-MULTIMONITORTOOL_PATH = "dependencies/multimonitortool-x64/MultiMonitorTool.exe"
-
-
-class Mode(Enum):
- DESKTOP = 1
- GAMEMODE = 2
-
-
-def get_mode_file_path():
- app_data_folder = os.path.join(os.environ["APPDATA"], "BigPictureTV")
- if not os.path.exists(app_data_folder):
- os.makedirs(app_data_folder)
- return os.path.join(app_data_folder, "current_mode.txt")
-
-
-def read_current_mode():
- return Mode.GAMEMODE if os.path.exists(get_mode_file_path()) else Mode.DESKTOP
-
-
-def get_audio_devices():
- cmd = "powershell Get-AudioDevice -list"
- output = subprocess.check_output(cmd, shell=True, text=True)
- devices = re.findall(r"Index\s+:\s+(\d+)\s+.*?Name\s+:\s+(.*?)\s+ID\s+:\s+{(.*?)}", output, re.DOTALL)
- return devices
-
-
-def set_audio_device(device_name, devices):
- device_words = device_name.lower().split()
- for index, name, _ in devices:
- if all(word in name.lower() for word in device_words):
- cmd = f"powershell set-audiodevice -index {index}"
- result = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
- return result.returncode == 0
- return False
-
-
-def switch_audio(audio_output):
- devices = get_audio_devices()
- success = set_audio_device(audio_output, devices)
- retries = 0
- while not success and retries < 10:
- print("Failed to switch audio, retrying...")
- time.sleep(1)
- success = set_audio_device(audio_output, devices)
- retries += 1
- if not success:
- print("Failed to switch audio after 10 attempts.")
-
-
-def is_bigpicture_running():
- big_picture_title = get_big_picture_window_title().lower()
- big_picture_words = big_picture_title.split()
- current_window_titles = [title.lower() for title in gw.getAllTitles()]
-
- for window_title in current_window_titles:
- if all(word in window_title for word in big_picture_words):
- return True
-
- return False
-
-
-def write_current_mode(current_mode):
- file_path = get_mode_file_path()
- if current_mode == Mode.GAMEMODE:
- open(file_path, "w").close()
- elif current_mode == Mode.DESKTOP and os.path.exists(file_path):
- os.remove(file_path)
-
-
-def manage_startup_shortcut(state):
- target_path = os.path.join(os.getcwd(), "BigPictureTV.exe")
- startup_folder = winshell.startup()
- shortcut_path = os.path.join(startup_folder, "BigPictureTV.lnk")
- if state:
- winshell.CreateShortcut(
- Path=shortcut_path,
- Target=target_path,
- Icon=(target_path, 0),
- Description="Launch BigPictureTV",
- StartIn=os.path.dirname(target_path),
- )
- elif os.path.exists(shortcut_path):
- os.remove(shortcut_path)
-
-
-def check_startup_shortcut():
- return os.path.exists(os.path.join(winshell.startup(), "BigPictureTV.lnk"))
-
-
-def handle_startup_checkbox_state_changed(state):
- manage_startup_shortcut(state)
class BigPictureTV(QMainWindow):
@@ -122,13 +29,10 @@ def __init__(self):
self.settings = {}
self.first_run = False
self.paused = False
- self.use_displayswitch = False
self.timer = QTimer()
- self.gamemode_screen = "/external"
- self.desktop_screen = "/internal"
self.load_settings()
self.initialize_ui()
- self.is_audio_device_cmdlets_installed()
+ self.get_audio_capabilities()
self.current_mode = read_current_mode()
self.switch_mode(self.current_mode or Mode.DESKTOP)
self.tray_icon = self.create_tray_icon()
@@ -144,8 +48,8 @@ def initialize_ui(self):
self.ui.gamemodeEntry.textChanged.connect(self.save_settings)
self.ui.desktopEntry.textChanged.connect(self.save_settings)
self.ui.checkRateSpinBox.valueChanged.connect(self.save_settings)
- self.ui.disable_displayswitch_box.stateChanged.connect(self.save_settings)
self.ui.startupCheckBox.setChecked(check_startup_shortcut())
+ self.ui.clone_checkbox.stateChanged.connect(self.save_settings)
self.apply_settings()
@@ -186,8 +90,8 @@ def apply_settings(self):
self.ui.desktopEntry.setText(self.settings.get("DESKTOP_AUDIO", ""))
self.ui.disableAudioCheckbox.setChecked(self.settings.get("DisableAudioSwitch", False))
self.ui.checkRateSpinBox.setValue(self.settings.get("CheckRate", 1000))
- self.ui.disable_displayswitch_box.setChecked(self.settings.get("DisableDisplayswitch", False))
self.toggle_audio_settings(not self.ui.disableAudioCheckbox.isChecked())
+ self.ui.clone_checkbox.setChecked(self.settings.get("use_clone", False))
def save_settings(self):
self.settings = {
@@ -195,7 +99,7 @@ def save_settings(self):
"DESKTOP_AUDIO": self.ui.desktopEntry.text(),
"DisableAudioSwitch": self.ui.disableAudioCheckbox.isChecked(),
"CheckRate": self.ui.checkRateSpinBox.value(),
- "DisableDisplayswitch": self.ui.disable_displayswitch_box.isChecked(),
+ "use_clone": self.ui.clone_checkbox.isChecked(),
}
os.makedirs(os.path.dirname(SETTINGS_FILE), exist_ok=True)
with open(SETTINGS_FILE, "w") as f:
@@ -204,34 +108,35 @@ def save_settings(self):
def switch_mode(self, mode):
if mode == self.current_mode:
return
+
self.current_mode = mode
- self.switch_screen(self.gamemode_screen if mode == Mode.GAMEMODE else self.desktop_screen)
+ gamemode_audio = self.settings.get("GAMEMODE_AUDIO")
+ desktop_audio = self.settings.get("DESKTOP_AUDIO")
+
+ self.switch_screen("gamemode" if mode == Mode.GAMEMODE else "desktop")
+
if not self.ui.disableAudioCheckbox.isChecked():
- switch_audio(
- self.settings.get("GAMEMODE_AUDIO") if mode == Mode.GAMEMODE else self.settings.get("DESKTOP_AUDIO")
- )
+ switch_audio(gamemode_audio if mode == Mode.GAMEMODE else desktop_audio)
write_current_mode(mode)
+
if self.tray_icon:
self.update_tray_icon_menu()
self.update_tray_icon()
- def switch_screen(self, mode):
- if not self.ui.disable_displayswitch_box.isChecked():
- if mode == self.gamemode_screen:
- mode = "/external"
- else:
- mode = "/internal"
- subprocess.run(["displayswitch.exe", mode], check=True)
-
- def is_audio_device_cmdlets_installed(self):
- cmd = 'powershell "Get-Module -ListAvailable -Name AudioDeviceCmdlets"'
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
- if "AudioDeviceCmdlets" in result.stdout:
- return True
- self.ui.disableAudioCheckbox.setChecked(True)
- self.ui.disableAudioCheckbox.setEnabled(False)
- self.toggle_audio_settings(False)
- return False
+ def switch_screen(self, screen):
+ if screen == "gamemode" and self.ui.clone_checkbox.isChecked():
+ enable_clone_mode()
+ elif screen == "gamemode" and not self.ui.clone_checkbox.isChecked():
+ enable_external_mode()
+ elif screen == "desktop":
+ enable_internal_mode()
+
+ def get_audio_capabilities(self):
+ if is_audio_device_cmdlets_installed() is False:
+ self.ui.disableAudioCheckbox.setChecked(True)
+ self.ui.disableAudioCheckbox.setEnabled(False)
+ self.toggle_audio_settings(False)
+ return
def update_mode(self):
if is_bigpicture_running() and self.current_mode != Mode.GAMEMODE:
diff --git a/src/audio_manager.py b/src/audio_manager.py
new file mode 100644
index 0000000..c3c467c
--- /dev/null
+++ b/src/audio_manager.py
@@ -0,0 +1,42 @@
+import subprocess
+import re
+import time
+
+
+def get_audio_devices():
+ cmd = "powershell Get-AudioDevice -list"
+ output = subprocess.check_output(cmd, shell=True, text=True)
+ devices = re.findall(r"Index\s+:\s+(\d+)\s+.*?Name\s+:\s+(.*?)\s+ID\s+:\s+{(.*?)}", output, re.DOTALL)
+ return devices
+
+
+def set_audio_device(device_name, devices):
+ device_words = device_name.lower().split()
+ for index, name, _ in devices:
+ if all(word in name.lower() for word in device_words):
+ cmd = f"powershell set-audiodevice -index {index}"
+ result = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ return result.returncode == 0
+ return False
+
+
+def switch_audio(audio_output):
+ devices = get_audio_devices()
+ success = set_audio_device(audio_output, devices)
+ retries = 0
+ while not success and retries < 10:
+ print("Failed to switch audio, retrying...")
+ time.sleep(1)
+ success = set_audio_device(audio_output, devices)
+ retries += 1
+ if not success:
+ print("Failed to switch audio after 10 attempts.")
+
+
+def is_audio_device_cmdlets_installed():
+ cmd = 'powershell "Get-Module -ListAvailable -Name AudioDeviceCmdlets"'
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
+ if "AudioDeviceCmdlets" in result.stdout:
+ return True
+ else:
+ return False
diff --git a/src/design.py b/src/design.py
index ddbfabe..e7a33ae 100644
--- a/src/design.py
+++ b/src/design.py
@@ -12,7 +12,7 @@
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
- MainWindow.resize(469, 324)
+ MainWindow.resize(372, 324)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@@ -29,41 +29,6 @@ def setupUi(self, MainWindow):
self.audioOutputLabel.setFont(font)
self.audioOutputLabel.setObjectName("audioOutputLabel")
self.gridLayout_2.addWidget(self.audioOutputLabel, 2, 0, 1, 1)
- self.audioFrame = QtWidgets.QFrame(parent=self.centralwidget)
- self.audioFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
- self.audioFrame.setObjectName("audioFrame")
- self.gridLayout = QtWidgets.QGridLayout(self.audioFrame)
- self.gridLayout.setContentsMargins(9, 9, 9, 9)
- self.gridLayout.setSpacing(12)
- self.gridLayout.setObjectName("gridLayout")
- self.gamemodeLabel = QtWidgets.QLabel(parent=self.audioFrame)
- self.gamemodeLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.gamemodeLabel.setObjectName("gamemodeLabel")
- self.gridLayout.addWidget(self.gamemodeLabel, 0, 0, 1, 1)
- self.desktopLabel = QtWidgets.QLabel(parent=self.audioFrame)
- self.desktopLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.desktopLabel.setObjectName("desktopLabel")
- self.gridLayout.addWidget(self.desktopLabel, 1, 0, 1, 1)
- self.desktopEntry = QtWidgets.QLineEdit(parent=self.audioFrame)
- self.desktopEntry.setMinimumSize(QtCore.QSize(0, 25))
- self.desktopEntry.setAutoFillBackground(False)
- self.desktopEntry.setFrame(True)
- self.desktopEntry.setObjectName("desktopEntry")
- self.gridLayout.addWidget(self.desktopEntry, 1, 1, 1, 1)
- self.gamemodeEntry = QtWidgets.QLineEdit(parent=self.audioFrame)
- self.gamemodeEntry.setMinimumSize(QtCore.QSize(0, 25))
- self.gamemodeEntry.setAutoFillBackground(False)
- self.gamemodeEntry.setFrame(True)
- self.gamemodeEntry.setObjectName("gamemodeEntry")
- self.gridLayout.addWidget(self.gamemodeEntry, 0, 1, 1, 1)
- self.gridLayout_2.addWidget(self.audioFrame, 3, 0, 1, 1)
- self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
- self.label_2.setMinimumSize(QtCore.QSize(0, 25))
- font = QtGui.QFont()
- font.setBold(True)
- self.label_2.setFont(font)
- self.label_2.setObjectName("label_2")
- self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.settingsFrame = QtWidgets.QFrame(parent=self.centralwidget)
self.settingsFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.settingsFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
@@ -72,32 +37,26 @@ def setupUi(self, MainWindow):
self.gridLayout_5.setContentsMargins(9, 9, 9, 9)
self.gridLayout_5.setSpacing(12)
self.gridLayout_5.setObjectName("gridLayout_5")
- self.startupCheckBox = QtWidgets.QCheckBox(parent=self.settingsFrame)
- self.startupCheckBox.setMinimumSize(QtCore.QSize(0, 25))
- self.startupCheckBox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
- self.startupCheckBox.setText("")
- self.startupCheckBox.setObjectName("startupCheckBox")
- self.gridLayout_5.addWidget(self.startupCheckBox, 0, 1, 1, 2)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.gridLayout_5.addItem(spacerItem, 3, 1, 1, 1)
+ self.label_4 = QtWidgets.QLabel(parent=self.settingsFrame)
+ self.label_4.setMinimumSize(QtCore.QSize(0, 25))
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_5.addWidget(self.label_4, 3, 0, 1, 1)
self.disableAudioCheckbox = QtWidgets.QCheckBox(parent=self.settingsFrame)
self.disableAudioCheckbox.setMinimumSize(QtCore.QSize(0, 25))
self.disableAudioCheckbox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
self.disableAudioCheckbox.setText("")
self.disableAudioCheckbox.setObjectName("disableAudioCheckbox")
self.gridLayout_5.addWidget(self.disableAudioCheckbox, 1, 1, 1, 2)
- self.label_6 = QtWidgets.QLabel(parent=self.settingsFrame)
- self.label_6.setMinimumSize(QtCore.QSize(0, 25))
- self.label_6.setObjectName("label_6")
- self.gridLayout_5.addWidget(self.label_6, 1, 0, 1, 1)
- self.label_4 = QtWidgets.QLabel(parent=self.settingsFrame)
- self.label_4.setMinimumSize(QtCore.QSize(0, 25))
- self.label_4.setObjectName("label_4")
- self.gridLayout_5.addWidget(self.label_4, 3, 0, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.settingsFrame)
self.label_5.setMinimumSize(QtCore.QSize(0, 25))
self.label_5.setObjectName("label_5")
self.gridLayout_5.addWidget(self.label_5, 0, 0, 1, 1)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
- self.gridLayout_5.addItem(spacerItem, 3, 1, 1, 1)
+ self.label_6 = QtWidgets.QLabel(parent=self.settingsFrame)
+ self.label_6.setMinimumSize(QtCore.QSize(0, 25))
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_5.addWidget(self.label_6, 1, 0, 1, 1)
self.checkRateSpinBox = QtWidgets.QSpinBox(parent=self.settingsFrame)
self.checkRateSpinBox.setMinimumSize(QtCore.QSize(90, 25))
self.checkRateSpinBox.setFrame(False)
@@ -106,18 +65,58 @@ def setupUi(self, MainWindow):
self.checkRateSpinBox.setProperty("value", 1000)
self.checkRateSpinBox.setObjectName("checkRateSpinBox")
self.gridLayout_5.addWidget(self.checkRateSpinBox, 3, 2, 1, 1)
+ self.startupCheckBox = QtWidgets.QCheckBox(parent=self.settingsFrame)
+ self.startupCheckBox.setMinimumSize(QtCore.QSize(0, 25))
+ self.startupCheckBox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
+ self.startupCheckBox.setText("")
+ self.startupCheckBox.setObjectName("startupCheckBox")
+ self.gridLayout_5.addWidget(self.startupCheckBox, 0, 1, 1, 2)
self.label = QtWidgets.QLabel(parent=self.settingsFrame)
self.label.setMinimumSize(QtCore.QSize(0, 25))
self.label.setObjectName("label")
self.gridLayout_5.addWidget(self.label, 2, 0, 1, 1)
- self.disable_displayswitch_box = QtWidgets.QCheckBox(parent=self.settingsFrame)
- self.disable_displayswitch_box.setMinimumSize(QtCore.QSize(0, 25))
- self.disable_displayswitch_box.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
- self.disable_displayswitch_box.setText("")
- self.disable_displayswitch_box.setObjectName("disable_displayswitch_box")
- self.gridLayout_5.addWidget(self.disable_displayswitch_box, 2, 2, 1, 1)
+ self.clone_checkbox = QtWidgets.QCheckBox(parent=self.settingsFrame)
+ self.clone_checkbox.setMinimumSize(QtCore.QSize(0, 25))
+ self.clone_checkbox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
+ self.clone_checkbox.setText("")
+ self.clone_checkbox.setObjectName("clone_checkbox")
+ self.gridLayout_5.addWidget(self.clone_checkbox, 2, 2, 1, 1)
self.gridLayout_2.addWidget(self.settingsFrame, 1, 0, 1, 1)
- self.gridLayout_2.setColumnStretch(0, 1)
+ self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
+ self.label_2.setMinimumSize(QtCore.QSize(0, 25))
+ font = QtGui.QFont()
+ font.setBold(True)
+ self.label_2.setFont(font)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
+ self.audioFrame = QtWidgets.QFrame(parent=self.centralwidget)
+ self.audioFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
+ self.audioFrame.setObjectName("audioFrame")
+ self.gridLayout = QtWidgets.QGridLayout(self.audioFrame)
+ self.gridLayout.setContentsMargins(9, 9, 9, 9)
+ self.gridLayout.setSpacing(12)
+ self.gridLayout.setObjectName("gridLayout")
+ self.gamemodeLabel = QtWidgets.QLabel(parent=self.audioFrame)
+ self.gamemodeLabel.setMinimumSize(QtCore.QSize(0, 25))
+ self.gamemodeLabel.setObjectName("gamemodeLabel")
+ self.gridLayout.addWidget(self.gamemodeLabel, 0, 0, 1, 1)
+ self.desktopLabel = QtWidgets.QLabel(parent=self.audioFrame)
+ self.desktopLabel.setMinimumSize(QtCore.QSize(0, 25))
+ self.desktopLabel.setObjectName("desktopLabel")
+ self.gridLayout.addWidget(self.desktopLabel, 1, 0, 1, 1)
+ self.desktopEntry = QtWidgets.QLineEdit(parent=self.audioFrame)
+ self.desktopEntry.setMinimumSize(QtCore.QSize(0, 25))
+ self.desktopEntry.setAutoFillBackground(False)
+ self.desktopEntry.setFrame(True)
+ self.desktopEntry.setObjectName("desktopEntry")
+ self.gridLayout.addWidget(self.desktopEntry, 1, 1, 1, 1)
+ self.gamemodeEntry = QtWidgets.QLineEdit(parent=self.audioFrame)
+ self.gamemodeEntry.setMinimumSize(QtCore.QSize(0, 25))
+ self.gamemodeEntry.setAutoFillBackground(False)
+ self.gamemodeEntry.setFrame(True)
+ self.gamemodeEntry.setObjectName("gamemodeEntry")
+ self.gridLayout.addWidget(self.gamemodeEntry, 0, 1, 1, 1)
+ self.gridLayout_2.addWidget(self.audioFrame, 3, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
@@ -130,11 +129,11 @@ def setupUi(self, MainWindow):
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
- self.audioOutputLabel.setText(_translate("MainWindow", "Audio output"))
- self.gamemodeLabel.setText(_translate("MainWindow", "Gamemode"))
- self.desktopLabel.setText(_translate("MainWindow", "Desktop"))
- self.label_2.setText(_translate("MainWindow", "Settings"))
- self.label_6.setText(_translate("MainWindow", "Disable audio switching"))
+ self.audioOutputLabel.setText(_translate("MainWindow", "Audio output configuration"))
self.label_4.setText(_translate("MainWindow", "Window check rate"))
self.label_5.setText(_translate("MainWindow", "Run at startup"))
- self.label.setText(_translate("MainWindow", "Do not use displayswitch"))
+ self.label_6.setText(_translate("MainWindow", "Disable audio switching"))
+ self.label.setText(_translate("MainWindow", "Clone screen instead of switching"))
+ self.label_2.setText(_translate("MainWindow", "Settings"))
+ self.gamemodeLabel.setText(_translate("MainWindow", "Gamemode"))
+ self.desktopLabel.setText(_translate("MainWindow", "Desktop"))
diff --git a/src/mode_manager.py b/src/mode_manager.py
new file mode 100644
index 0000000..0466bad
--- /dev/null
+++ b/src/mode_manager.py
@@ -0,0 +1,26 @@
+import os
+from enum import Enum
+
+
+class Mode(Enum):
+ DESKTOP = 1
+ GAMEMODE = 2
+
+
+def get_mode_file_path():
+ app_data_folder = os.path.join(os.environ["APPDATA"], "BigPictureTV")
+ if not os.path.exists(app_data_folder):
+ os.makedirs(app_data_folder)
+ return os.path.join(app_data_folder, "current_mode.txt")
+
+
+def read_current_mode():
+ return Mode.GAMEMODE if os.path.exists(get_mode_file_path()) else Mode.DESKTOP
+
+
+def write_current_mode(current_mode):
+ file_path = get_mode_file_path()
+ if current_mode == Mode.GAMEMODE:
+ open(file_path, "w").close()
+ elif current_mode == Mode.DESKTOP and os.path.exists(file_path):
+ os.remove(file_path)
diff --git a/src/monitor_manager.py b/src/monitor_manager.py
new file mode 100644
index 0000000..788ad39
--- /dev/null
+++ b/src/monitor_manager.py
@@ -0,0 +1,18 @@
+import subprocess
+
+DISPLAYSWITCH = "DisplaySwitch.exe"
+CLONE = "/clone"
+INTERNAL = "/internal"
+EXTERNAL = "/external"
+
+
+def enable_clone_mode():
+ subprocess.run([DISPLAYSWITCH, CLONE])
+
+
+def enable_external_mode():
+ subprocess.run([DISPLAYSWITCH, EXTERNAL])
+
+
+def enable_internal_mode():
+ subprocess.run([DISPLAYSWITCH, INTERNAL])
diff --git a/src/setup.py b/src/setup.py
index 4b3bffc..987e353 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -3,7 +3,6 @@
src_dir = os.path.dirname(os.path.abspath(__file__))
build_dir = "build/BigPictureTV"
-install_dir = os.path.join(os.getenv("LOCALAPPDATA"), "programs", "BigPictureTV")
include_files = [
os.path.join(src_dir, "icons"),
diff --git a/src/shortcut_manager.py b/src/shortcut_manager.py
new file mode 100644
index 0000000..c6fa529
--- /dev/null
+++ b/src/shortcut_manager.py
@@ -0,0 +1,26 @@
+import os
+import winshell
+
+
+def manage_startup_shortcut(state):
+ target_path = os.path.join(os.getcwd(), "BigPictureTV.exe")
+ startup_folder = winshell.startup()
+ shortcut_path = os.path.join(startup_folder, "BigPictureTV.lnk")
+ if state:
+ winshell.CreateShortcut(
+ Path=shortcut_path,
+ Target=target_path,
+ Icon=(target_path, 0),
+ Description="Launch BigPictureTV",
+ StartIn=os.path.dirname(target_path),
+ )
+ elif os.path.exists(shortcut_path):
+ os.remove(shortcut_path)
+
+
+def check_startup_shortcut():
+ return os.path.exists(os.path.join(winshell.startup(), "BigPictureTV.lnk"))
+
+
+def handle_startup_checkbox_state_changed(state):
+ manage_startup_shortcut(state)
diff --git a/src/steam_language_reader.py b/src/steam_language_reader.py
index fd9f585..757d807 100644
--- a/src/steam_language_reader.py
+++ b/src/steam_language_reader.py
@@ -30,14 +30,14 @@
"swedish": "Steams Big Picture-läge",
"turkish": "Steam Geniş Ekran Modu",
"vietnamese": "Chế độ Big Picture trên Steam",
- "ukrainian": "Steam у режимі Big Picture"
+ "ukrainian": "Steam у режимі Big Picture",
}
def get_steam_language():
try:
key_path = r"Software\Valve\Steam\steamglobal"
-
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path) as key:
language, reg_type = winreg.QueryValueEx(key, "Language")
return language
@@ -54,4 +54,3 @@ def get_big_picture_window_title():
return BIG_PICTURE_WINDOW_TITLES.get(language, BIG_PICTURE_WINDOW_TITLES["english"])
else:
return BIG_PICTURE_WINDOW_TITLES["english"]
-
diff --git a/src/ui/design.ui b/src/ui/design.ui
index 3a096a2..651e48d 100644
--- a/src/ui/design.ui
+++ b/src/ui/design.ui
@@ -6,7 +6,7 @@
0
0
- 469
+ 372
324
@@ -20,7 +20,7 @@
MainWindow
-
+
-
@@ -35,16 +35,19 @@
- Audio output
+ Audio output configuration
- -
-
+
-
+
QFrame::Shape::StyledPanel
-
+
+ QFrame::Shadow::Sunken
+
+
9
@@ -60,8 +63,50 @@
12
+
-
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 25
+
+
+
+ Window check rate
+
+
+
+ -
+
+
+
+ 0
+ 25
+
+
+
+ Qt::LayoutDirection::RightToLeft
+
+
+
+
+
+
-
-
+
0
@@ -69,12 +114,12 @@
- Gamemode
+ Run at startup
-
-
+
0
@@ -82,39 +127,74 @@
- Desktop
+ Disable audio switching
- -
-
+
-
+
- 0
+ 90
25
-
+
false
-
- true
+
+ 1
+
+
+ 1000
+
+
+ 1000
- -
-
+
-
+
0
25
-
- false
+
+ Qt::LayoutDirection::RightToLeft
-
- true
+
+
+
+
+
+ -
+
+
+
+ 0
+ 25
+
+
+
+ Clone screen instead of switching
+
+
+
+ -
+
+
+
+ 0
+ 25
+
+
+
+ Qt::LayoutDirection::RightToLeft
+
+
+
@@ -139,15 +219,12 @@
- -
-
+
-
+
QFrame::Shape::StyledPanel
-
- QFrame::Shadow::Sunken
-
-
+
9
@@ -163,53 +240,21 @@
12
-
-
-
-
-
- 0
- 25
-
-
-
- Qt::LayoutDirection::RightToLeft
-
-
-
-
-
-
- -
-
+
-
+
0
25
-
- Qt::LayoutDirection::RightToLeft
-
-
+ Gamemode
-
-
-
-
- 0
- 25
-
-
-
- Disable audio switching
-
-
-
- -
-
+
0
@@ -217,84 +262,39 @@
- Window check rate
+ Desktop
- -
-
+
-
+
0
25
-
- Run at startup
-
-
-
- -
-
-
- Qt::Orientation::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 90
- 25
-
-
-
+
false
-
- 1
-
-
- 1000
-
-
- 1000
-
-
-
- -
-
-
-
- 0
- 25
-
-
-
- Do not use displayswitch
+
+ true
- -
-
+
-
+
0
25
-
- Qt::LayoutDirection::RightToLeft
+
+ false
-
-
+
+ true
diff --git a/src/window_monitor.py b/src/window_monitor.py
new file mode 100644
index 0000000..4b7a5e0
--- /dev/null
+++ b/src/window_monitor.py
@@ -0,0 +1,14 @@
+import pygetwindow as gw
+from steam_language_reader import get_big_picture_window_title
+
+
+def is_bigpicture_running():
+ big_picture_title = get_big_picture_window_title().lower()
+ big_picture_words = big_picture_title.split()
+ current_window_titles = [title.lower() for title in gw.getAllTitles()]
+
+ for window_title in current_window_titles:
+ if all(word in window_title for word in big_picture_words):
+ return True
+
+ return False