diff --git a/web-greeter/__main__.py b/web-greeter/__main__.py index fa6d7af..a46e6be 100644 --- a/web-greeter/__main__.py +++ b/web-greeter/__main__.py @@ -61,7 +61,7 @@ def set_debug(value: bool): conf = config.web_greeter_config["config"] app = config.web_greeter_config["app"] conf["greeter"]["debug_mode"] = value - app["decorated"] = value + app["frame"] = value app["fullscreen"] = not value def parse(argv): diff --git a/web-greeter/bridge/Greeter.py b/web-greeter/bridge/Greeter.py index 19deb2c..ff84c10 100644 --- a/web-greeter/bridge/Greeter.py +++ b/web-greeter/bridge/Greeter.py @@ -27,6 +27,7 @@ # along with Web Greeter; If not, see . # Standard Lib +from browser.error_prompt import Dialog import subprocess import threading @@ -36,9 +37,11 @@ gi.require_version('LightDM', '1') from gi.repository import LightDM from browser.bridge import Bridge, BridgeObject -from PyQt5.QtCore import QVariant, QTimer +from PyQt5.QtCore import QFileSystemWatcher, QVariant, QTimer from config import web_greeter_config +from utils.battery import Battery +import globals # This Application from . import ( @@ -56,19 +59,52 @@ LightDMGreeter = LightDM.Greeter() LightDMUsers = LightDM.UserList() -def changeBrightness(self, method: str, quantity: int): - if self._config["features"]["backlight"]["enabled"] != True: +def changeBrightness(method: str, quantity: int = None): + backlight = web_greeter_config["config"]["features"]["backlight"] + if not backlight["enabled"]: return + if not quantity: + quantity = backlight["value"] try: - steps = self._config["features"]["backlight"]["steps"] + steps = backlight["steps"] child = subprocess.run(["xbacklight", method, str(quantity), "-steps", str(steps)]) if child.returncode == 1: raise ChildProcessError("xbacklight returned 1") except Exception as err: logger.error("Brightness: {}".format(err)) else: - self.brightness_update.emit() + if globals.greeter: + globals.greeter.greeter.brightness_update.emit() +def increaseBrightness(quantity: int = None): + backlight = web_greeter_config["config"]["features"]["backlight"] + if not backlight["enabled"]: + return + if not quantity: + quantity = backlight["value"] + thread = threading.Thread(target=changeBrightness, + args=("-inc", quantity)) + thread.start() + +def decreaseBrightness(quantity: int = None): + backlight = web_greeter_config["config"]["features"]["backlight"] + if not backlight["enabled"]: + return + if not quantity: + quantity = backlight["value"] + thread = threading.Thread(target=changeBrightness, + args=("-dec", quantity)) + thread.start() + +def setBrightness(quantity: int = None): + backlight = web_greeter_config["config"]["features"]["backlight"] + if not backlight["enabled"]: + return + if not quantity: + quantity = backlight["value"] + thread = threading.Thread(target=changeBrightness, + args=("-set", quantity)) + thread.start() def getBrightness(self): if self._config["features"]["backlight"]["enabled"] != True: @@ -106,13 +142,23 @@ class Greeter(BridgeObject): self._shared_data_directory = '' self._themes_directory = web_greeter_config["app"]["theme_dir"] - # if self._config.features.battery == True: - # self._battery = battery.Battery() + if self._config["features"]["battery"]: + self._battery = Battery() - LightDMGreeter.connect_to_daemon_sync() + try: + LightDMGreeter.connect_to_daemon_sync() + except Exception as err: + logger.error(err) + dia = Dialog(title="An error ocurred", + message="Detected a problem that could interfere with the system login process", + detail="LightDM: {0}\nYou can continue without major problems, but you won't be able to log in".format(err), + buttons=["Okay"]) + dia.exec() + pass self._connect_signals() self._determine_shared_data_directory_path() + logger.debug("LightDM API connected") def _determine_shared_data_directory_path(self): user = LightDMUsers.get_users()[0] @@ -174,9 +220,7 @@ class Greeter(BridgeObject): @brightness.setter def brightness(self, quantity): - thread = threading.Thread(target=changeBrightness, - args=(self, "-set", quantity)) - thread.start() + setBrightness(quantity) @Bridge.prop(bool, notify=noop_signal) def can_hibernate(self): @@ -305,21 +349,15 @@ class Greeter(BridgeObject): @Bridge.method(int) def brightnessSet(self, quantity): - thread = threading.Thread(target=changeBrightness, - args=(self, "-set", quantity)) - thread.start() + setBrightness(quantity) @Bridge.method(int) def brightnessIncrease(self, quantity): - thread = threading.Thread(target=changeBrightness, - args=(self, "-inc", quantity)) - thread.start() + increaseBrightness(quantity) @Bridge.method(int) def brightnessDecrease(self, quantity): - thread = threading.Thread(target=changeBrightness, - args=(self, "-dec", quantity)) - thread.start() + decreaseBrightness(quantity) @Bridge.method() def cancel_authentication(self): diff --git a/web-greeter/bridge/__init__.py b/web-greeter/bridge/__init__.py index 6e164be..fb8d8ab 100644 --- a/web-greeter/bridge/__init__.py +++ b/web-greeter/bridge/__init__.py @@ -119,6 +119,7 @@ def battery_to_dict(battery): name = battery.get_name(), level = battery.get_level(), state = battery.get_state(), + ac_status = battery.get_ac_status(), capacity = battery.get_capacity(), time = battery.get_time(), watt = battery.get_watt() diff --git a/web-greeter/browser/browser.py b/web-greeter/browser/browser.py index fa94e74..86271bf 100644 --- a/web-greeter/browser/browser.py +++ b/web-greeter/browser/browser.py @@ -29,7 +29,6 @@ # Standard lib from browser.window import MainWindow -from browser.devtools import DevTools import os from typing import ( Dict, @@ -39,10 +38,10 @@ from typing import ( # 3rd-Party Libs from PyQt5.QtCore import QUrl, Qt, QCoreApplication, QFile -from PyQt5.QtWidgets import QApplication, QDesktopWidget, QDockWidget, QMainWindow +from PyQt5.QtWidgets import QAction, QApplication, QDesktopWidget, QDockWidget, QMainWindow, qApp from PyQt5.QtWebEngineCore import QWebEngineUrlScheme from PyQt5.QtWebEngineWidgets import QWebEngineScript, QWebEngineProfile, QWebEngineSettings, QWebEngineView, QWebEnginePage -from PyQt5.QtGui import QColor +from PyQt5.QtGui import QColor, QIcon from PyQt5.QtWebChannel import QWebChannel from browser.error_prompt import WebPage @@ -106,7 +105,13 @@ class Application: self.window.windowFlags() | Qt.WindowType.MaximizeUsingFullscreenGeometryHint ) + if web_greeter_config["app"]["frame"]: + self._init_menu_bar() + state = self.states['NORMAL'] + if web_greeter_config["app"]["fullscreen"]: + state = self.states["FULLSCREEN"] + try: self.window.windowHandle().setWindowState(state) except Exception: @@ -127,6 +132,27 @@ class Application: logger.debug("Web Greeter started") return self.app.exec_() + def _init_menu_bar(self): + exit_action = QAction(QIcon('exit.png'), '&Exit', self.window) + exit_action.setShortcut('Ctrl+Q') + exit_action.setStatusTip('Exit application') + exit_action.triggered.connect(qApp.quit) + + menu_bar = self.window.menuBar() + + file_menu = menu_bar.addMenu('&File') + file_menu.addAction(exit_action) + + edit_menu = menu_bar.addMenu('&Edit') + edit_menu.addAction(exit_action) + + view_menu = menu_bar.addMenu('&View') + view_menu.addAction(exit_action) + + about_menu = menu_bar.addMenu('&About') + about_menu.addAction(exit_action) + + class Browser(Application): url_scheme: QWebEngineUrlScheme @@ -211,8 +237,10 @@ class Browser(Application): return if self.qdock.isVisible(): self.qdock.hide() + self.view.setFocus() else: self.qdock.show() + self.dev_view.setFocus() def _initialize_page(self): page_settings = self.page.settings().globalSettings() diff --git a/web-greeter/browser/error_prompt.py b/web-greeter/browser/error_prompt.py index 1a38cf4..c035629 100644 --- a/web-greeter/browser/error_prompt.py +++ b/web-greeter/browser/error_prompt.py @@ -28,8 +28,9 @@ # Standard lib # 3rd-Party Libs +from typing import List from PyQt5.QtWebEngineWidgets import QWebEnginePage -from PyQt5.QtWidgets import QDialogButtonBox, QDialog, QVBoxLayout, QLabel, QPushButton +from PyQt5.QtWidgets import QAbstractButton, QDialogButtonBox, QDialog, QVBoxLayout, QLabel, QPushButton from config import web_greeter_config import globals @@ -86,54 +87,49 @@ class WebPage(QWebEnginePage): source=sourceID, line=lineNumber, msg=message) errorPrompt(errorMessage) -class ErrorDialog(QDialog): - def __init__(self, parent=None, err=""): +class Dialog(QDialog): + def __init__(self, parent=None, title:str = "Dialog", message:str = "Message", detail:str = "", buttons: List[str] = []): super().__init__(parent) - - self.setWindowTitle("Error") + self.setWindowTitle(title) self.buttonBox = QDialogButtonBox() - cancelBtn = QPushButton("Cancel") - defaultBtn = QPushButton("Set default theme") - reloadBtn = QPushButton("Reload theme") - - reloadBtn.clicked.connect(self.handle_reload) - - self.buttonBox.addButton(defaultBtn, QDialogButtonBox.ButtonRole.AcceptRole) - self.buttonBox.addButton(reloadBtn, QDialogButtonBox.ButtonRole.ResetRole) - self.buttonBox.addButton(cancelBtn, QDialogButtonBox.ButtonRole.RejectRole) + for i in range(0, len(buttons)): + button = QPushButton(buttons[i]) + button.role = i + self.buttonBox.addButton(button, QDialogButtonBox.ButtonRole.NoRole) - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.rejected.connect(self.reject) + self.buttonBox.clicked.connect(self.handle_click) self.layout = QVBoxLayout() - message = QLabel("An error ocurred. Do you want to change to default theme?") - err = QLabel(err) - self.layout.addWidget(message) - self.layout.addWidget(err) + self.layout.addWidget(QLabel(message)) + self.layout.addWidget(QLabel(detail)) self.layout.addWidget(self.buttonBox) - self.setLayout(self.layout) - def handle_reload(self, value: bool): - self.done(2) + self.setLayout(self.layout) + def handle_click(self, button: QAbstractButton): + self.done(button.role) def errorPrompt(err): if not web_greeter_config["config"]["greeter"]["detect_theme_errors"]: return - dia = ErrorDialog(globals.greeter.window, err) + dia = Dialog(parent=globals.greeter.window, title="Error", + message="An error ocurred. Do you want to change to default theme?", + detail=err, + buttons=["Reload theme", "Set default theme", "Cancel"], + ) dia.exec() result = dia.result() - if result == 0: # Cancel + if result == 2: # Cancel return elif result == 1: # Default theme web_greeter_config["config"]["greeter"]["theme"] = "gruvbox" globals.greeter.load_theme() return - elif result == 2: # Reload + elif result == 0: # Reload globals.greeter.load_theme() return diff --git a/web-greeter/browser/window.py b/web-greeter/browser/window.py index 0ef46dd..5f8ad95 100644 --- a/web-greeter/browser/window.py +++ b/web-greeter/browser/window.py @@ -25,22 +25,49 @@ # You should have received a copy of the GNU General Public License # along with Web Greeter; If not, see . -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QMainWindow +from bridge.Greeter import changeBrightness, decreaseBrightness, increaseBrightness +from PyQt5.QtCore import QFileSystemWatcher, Qt +from PyQt5.QtWidgets import QAction, QMainWindow from PyQt5.QtGui import QKeyEvent +from config import web_greeter_config import globals class MainWindow(QMainWindow): - def keyPressEvent(self, keyEvent: QKeyEvent) -> None: - super().keyPressEvent(keyEvent) - key = keyEvent.key() - mod = keyEvent.modifiers() # type: Qt.KeyboardModifiers - if (key == Qt.Key.Key_MonBrightnessUp): - pass - elif (key == Qt.Key.Key_MonBrightnessDown): - pass - elif (key == Qt.Key.Key_I - and mod & Qt.KeyboardModifier.ControlModifier - and mod & Qt.KeyboardModifier.ShiftModifier): - globals.greeter.toggle_devtools() + def __init__(self): + super().__init__() + self.init_actions() + # self.watchBrightness() + + def init_actions(self): + devAct = QAction(text="&Toggle Devtools", parent=self) + devAct.setShortcut("Shift+Ctrl+I") + devAct.triggered.connect(self.toggle_devtools) + + monBUp = QAction(text="&Increase brightness", parent=self) + monBDo = QAction(text="&Decrease brightness", parent=self) + monBUp.setShortcut(Qt.Key.Key_MonBrightnessUp) + monBDo.setShortcut(Qt.Key.Key_MonBrightnessDown) + monBUp.triggered.connect(self.inc_brightness) + monBDo.triggered.connect(self.dec_brightness) + + self.addAction(devAct) + self.addAction(monBUp) + self.addAction(monBDo) + + def toggle_devtools(self): + globals.greeter.toggle_devtools() + + def inc_brightness(self): + increaseBrightness() + def dec_brightness(self): + decreaseBrightness() + + def watchBrightness(self): + self.watcher = QFileSystemWatcher(parent=self) + self.watcher.addPath("/sys/class/backlight/intel_backlight/brightness") + self.watcher.fileChanged.connect(self.updateBrightness) + + def updateBrightness(self): + if globals.greeter: + globals.greeter.greeter.brightness_update.emit()