Initial Version

This commit is contained in:
Bashy 2026-04-25 13:09:44 +03:00
commit bdf4330738
5 changed files with 383 additions and 0 deletions

171
hudfx.py Normal file
View file

@ -0,0 +1,171 @@
#!/usr/bin/env python3
import sys
import json
import signal
import os
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QSystemTrayIcon, QMenu, QAction,
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QComboBox, QPushButton, QMessageBox
)
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings
from PyQt5.QtGui import QColor, QIcon
CONFIG_PATH = os.path.expanduser("~/.config/hudfx-lnx/config.json")
def load_config():
if os.path.exists(CONFIG_PATH):
with open(CONFIG_PATH) as f:
return json.load(f)
return {"url": "", "screen_index": 0}
def save_config(config):
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
with open(CONFIG_PATH, "w") as f:
json.dump(config, f, indent=2)
class Overlay(QMainWindow):
def __init__(self, url, screen):
super().__init__()
self.setWindowFlags(
Qt.FramelessWindowHint |
Qt.WindowStaysOnTopHint |
Qt.Tool |
Qt.WindowTransparentForInput
)
self.setAttribute(Qt.WA_TranslucentBackground)
view = QWebEngineView()
profile = QWebEngineProfile(view)
page = QWebEnginePage(profile, view)
view.setPage(page)
view.page().setBackgroundColor(QColor(0, 0, 0, 0))
view.page().setAudioMuted(False)
settings = view.page().settings()
settings.setAttribute(QWebEngineSettings.PlaybackRequiresUserGesture, False)
settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
view.load(QUrl(url))
self.setCentralWidget(view)
self.setGeometry(screen.geometry())
self.show()
class SettingsDialog(QDialog):
def __init__(self, config, screens, parent=None):
super().__init__(parent)
self.setWindowTitle("HudFX Settings")
self.setMinimumWidth(420)
self.config = config
layout = QVBoxLayout(self)
layout.addWidget(QLabel("Overlay URL:"))
self.url_input = QLineEdit(config.get("url", ""))
self.url_input.setPlaceholderText("https://...")
layout.addWidget(self.url_input)
layout.addWidget(QLabel("Display monitor:"))
self.screen_combo = QComboBox()
for i, screen in enumerate(screens):
s = screen.size()
name = f"Monitor {i + 1}{s.width()}x{s.height()} ({screen.name()})"
self.screen_combo.addItem(name)
self.screen_combo.setCurrentIndex(config.get("screen_index", 0))
layout.addWidget(self.screen_combo)
buttons = QHBoxLayout()
save_btn = QPushButton("Save")
cancel_btn = QPushButton("Cancel")
save_btn.clicked.connect(self.accept)
cancel_btn.clicked.connect(self.reject)
buttons.addWidget(save_btn)
buttons.addWidget(cancel_btn)
layout.addLayout(buttons)
def get_values(self):
return {
"url": self.url_input.text().strip(),
"screen_index": self.screen_combo.currentIndex(),
}
class TrayApp:
def __init__(self, app):
self.app = app
self.config = load_config()
self.overlay = None
self.tray = QSystemTrayIcon()
self.tray.setToolTip("HudFX Overlay")
icon = QIcon.fromTheme("video-display")
if icon.isNull():
icon = QIcon.fromTheme("preferences-desktop-display")
self.tray.setIcon(icon)
self.menu = QMenu()
self.toggle_action = QAction("Start overlay")
self.toggle_action.triggered.connect(self.toggle_overlay)
self.menu.addAction(self.toggle_action)
self.settings_action = QAction("Settings...")
self.settings_action.triggered.connect(self.open_settings)
self.menu.addAction(self.settings_action)
self.menu.addSeparator()
self.quit_action = QAction("Quit")
self.quit_action.triggered.connect(self.quit)
self.menu.addAction(self.quit_action)
self.tray.setContextMenu(self.menu)
self.tray.show()
self.toggle_overlay()
def toggle_overlay(self):
if self.overlay:
self.overlay.close()
self.overlay = None
self.toggle_action.setText("Start overlay")
else:
url = self.config.get("url", "").strip()
if not url:
QMessageBox.warning(None, "HudFX", "No URL set. Open Settings first.")
return
idx = self.config.get("screen_index", 0)
screens = self.app.screens()
screen = screens[idx] if idx < len(screens) else self.app.primaryScreen()
self.overlay = Overlay(url, screen)
self.toggle_action.setText("Stop overlay")
def open_settings(self):
dialog = SettingsDialog(self.config, self.app.screens())
if dialog.exec_() == QDialog.Accepted:
self.config = dialog.get_values()
save_config(self.config)
def quit(self):
if self.overlay:
self.overlay.close()
self.app.quit()
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
signal.signal(signal.SIGINT, signal.SIG_DFL)
timer = QTimer()
timer.start(200)
timer.timeout.connect(lambda: None)
tray = TrayApp(app)
sys.exit(app.exec_())