Browse Source

Removed whither package, partially.

sisyphus
JezerM 3 years ago
parent
commit
7c154980ac
No known key found for this signature in database
GPG Key ID: 66BBC5D01388C6B5
  1. 1
      build/utils.sh
  2. 42
      web-greeter/__main__.py
  3. 30
      web-greeter/bridge/Config.py
  4. 147
      web-greeter/bridge/Greeter.py
  5. 19
      web-greeter/bridge/ThemeUtils.py
  6. 46
      web-greeter/bridge/devtools.py
  7. 0
      web-greeter/browser/__init__.py
  8. 46
      web-greeter/browser/bridge.py
  9. 276
      web-greeter/browser/browser.py
  10. 140
      web-greeter/browser/error_prompt.py
  11. 55
      web-greeter/browser/interceptor.py
  12. 74
      web-greeter/browser/url_scheme.py
  13. 83
      web-greeter/config.py
  14. 223
      web-greeter/globals.py
  15. 50
      web-greeter/logger.py
  16. 176
      web-greeter/main.py
  17. 0
      web-greeter/requirements.txt
  18. 62
      web-greeter/whither.yml

1
build/utils.sh

@ -28,7 +28,6 @@ do_build() {
# Create "Zip Application" # Create "Zip Application"
(cd "${PKGNAME}" \ (cd "${PKGNAME}" \
&& mv main.py __main__.py \
&& zip -rq ../"${PKGNAME}.zip" . -x '**__pycache__**' 'resources/*' \ && zip -rq ../"${PKGNAME}.zip" . -x '**__pycache__**' 'resources/*' \
&& cd - >/dev/null \ && cd - >/dev/null \
&& mkdir -p "${INSTALL_ROOT}${PREFIX}"/{bin,share} \ && mkdir -p "${INSTALL_ROOT}${PREFIX}"/{bin,share} \

42
web-greeter/__main__.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
# __main__.py
#
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard lib
import sys
import ruamel.yaml as yaml
import os
# 3rd-Party Libs
from browser.browser import Browser
from logger import logger
import globals
import config
if __name__ == '__main__':
globals.greeter = Browser()
greeter = globals.greeter
greeter.run()

30
web-greeter/bridge/Config.py

@ -3,6 +3,7 @@
# Config.py # Config.py
# #
# Copyright © 2017 Antergos # Copyright © 2017 Antergos
# Copyright © 2021 JezerM
# #
# This file is part of Web Greeter. # This file is part of Web Greeter.
# #
@ -26,17 +27,15 @@
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>. # along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# 3rd-Party Libs # 3rd-Party Libs
from whither.bridge import ( from browser.bridge import Bridge, BridgeObject
BridgeObject, from PyQt5.QtCore import QVariant
bridge,
Variant,
)
import gi import gi
gi.require_version('LightDM', '1') gi.require_version('LightDM', '1')
from gi.repository import LightDM from gi.repository import LightDM
from typing import List from typing import List
from config import web_greeter_config
from . import ( from . import (
layout_to_dict layout_to_dict
@ -56,28 +55,29 @@ def get_layouts(config_layouts: List[str]):
class Config(BridgeObject): class Config(BridgeObject):
noop_signal = bridge.signal() noop_signal = Bridge.signal()
def __init__(self, config, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(name='Config', *args, **kwargs) super().__init__(name='Config', *args, **kwargs)
self._branding = config.branding.as_dict() config = web_greeter_config["config"]
self._greeter = config.greeter.as_dict() self._branding = config["branding"]
self._features = config.features.as_dict() self._greeter = config["greeter"]
self._layouts = get_layouts(config.layouts) self._features = config["features"]
self._layouts = get_layouts(config["layouts"])
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def branding(self): def branding(self):
return self._branding return self._branding
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def greeter(self): def greeter(self):
return self._greeter return self._greeter
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def features(self): def features(self):
return self._features return self._features
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def layouts(self): def layouts(self):
return self._layouts return self._layouts

147
web-greeter/bridge/Greeter.py

@ -3,6 +3,7 @@
# Greeter.py # Greeter.py
# #
# Copyright © 2017 Antergos # Copyright © 2017 Antergos
# Copyright © 2021 JezerM
# #
# This file is part of Web Greeter. # This file is part of Web Greeter.
# #
@ -27,19 +28,17 @@
# Standard Lib # Standard Lib
import subprocess import subprocess
import re
import threading import threading
# 3rd-Party Libs # 3rd-Party Libs
import gi import gi
gi.require_version('LightDM', '1') gi.require_version('LightDM', '1')
from gi.repository import LightDM from gi.repository import LightDM
from whither.bridge import (
BridgeObject, from browser.bridge import Bridge, BridgeObject
bridge, from PyQt5.QtCore import QVariant, QTimer
Variant,
) from config import web_greeter_config
from PyQt5.QtCore import QTimer
# This Application # This Application
from . import ( from . import (
@ -51,17 +50,17 @@ from . import (
logger logger
) )
import utils.battery as battery # import utils.battery as battery
LightDMGreeter = LightDM.Greeter() LightDMGreeter = LightDM.Greeter()
LightDMUsers = LightDM.UserList() LightDMUsers = LightDM.UserList()
def changeBrightness(self, method: str, quantity: int): def changeBrightness(self, method: str, quantity: int):
if self._config.features.backlight["enabled"] != True: if self._config["features"]["backlight"]["enabled"] != True:
return return
try: try:
steps = self._config.features.backlight["steps"] steps = self._config["features"]["backlight"]["steps"]
child = subprocess.run(["xbacklight", method, str(quantity), "-steps", str(steps)]) child = subprocess.run(["xbacklight", method, str(quantity), "-steps", str(steps)])
if child.returncode == 1: if child.returncode == 1:
raise ChildProcessError("xbacklight returned 1") raise ChildProcessError("xbacklight returned 1")
@ -72,7 +71,7 @@ def changeBrightness(self, method: str, quantity: int):
def getBrightness(self): def getBrightness(self):
if self._config.features.backlight["enabled"] != True: if self._config["features"]["backlight"]["enabled"] != True:
return -1 return -1
try: try:
level = subprocess.run(["xbacklight", "-get"], stdout=subprocess.PIPE, level = subprocess.run(["xbacklight", "-get"], stdout=subprocess.PIPE,
@ -85,30 +84,30 @@ def getBrightness(self):
class Greeter(BridgeObject): class Greeter(BridgeObject):
# LightDM.Greeter Signals # LightDM.Greeter Signals
authentication_complete = bridge.signal() authentication_complete = Bridge.signal()
autologin_timer_expired = bridge.signal() autologin_timer_expired = Bridge.signal()
idle = bridge.signal() idle = Bridge.signal()
reset = bridge.signal() reset = Bridge.signal()
show_message = bridge.signal(str, LightDM.MessageType, arguments=('text', 'type')) show_message = Bridge.signal(str, LightDM.MessageType, arguments=('text', 'type'))
show_prompt = bridge.signal(str, LightDM.PromptType, arguments=('text', 'type')) show_prompt = Bridge.signal(str, LightDM.PromptType, arguments=('text', 'type'))
brightness_update = bridge.signal() brightness_update = Bridge.signal()
battery_update = bridge.signal() battery_update = Bridge.signal()
noop_signal = bridge.signal() noop_signal = Bridge.signal()
property_changed = bridge.signal() property_changed = Bridge.signal()
_battery = None _battery = None
def __init__(self, config, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(name='LightDMGreeter', *args, **kwargs) super().__init__(name='LightDMGreeter', *args, **kwargs)
self._config = config self._config = web_greeter_config["config"]
self._shared_data_directory = '' self._shared_data_directory = ''
self._themes_directory = config.themes_dir self._themes_directory = web_greeter_config["app"]["theme_dir"]
if self._config.features.battery == True: # if self._config.features.battery == True:
self._battery = battery.Battery() # self._battery = battery.Battery()
LightDMGreeter.connect_to_daemon_sync() LightDMGreeter.connect_to_daemon_sync()
@ -149,27 +148,27 @@ class Greeter(BridgeObject):
self.property_changed.emit() self.property_changed.emit()
QTimer().singleShot(300, lambda: _signal.emit(*args)) QTimer().singleShot(300, lambda: _signal.emit(*args))
@bridge.prop(str, notify=property_changed) @Bridge.prop(str, notify=property_changed)
def authentication_user(self): def authentication_user(self):
return LightDMGreeter.get_authentication_user() or '' return LightDMGreeter.get_authentication_user() or ''
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def autologin_guest(self): def autologin_guest(self):
return LightDMGreeter.get_autologin_guest_hint() return LightDMGreeter.get_autologin_guest_hint()
@bridge.prop(int, notify=noop_signal) @Bridge.prop(int, notify=noop_signal)
def autologin_timeout(self): def autologin_timeout(self):
return LightDMGreeter.get_autologin_timeout_hint() return LightDMGreeter.get_autologin_timeout_hint()
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def autologin_user(self): def autologin_user(self):
return LightDMGreeter.get_autologin_user_hint() return LightDMGreeter.get_autologin_user_hint()
@bridge.prop(Variant, notify=battery_update) @Bridge.prop(QVariant, notify=battery_update)
def batteryData(self): def batteryData(self):
return battery_to_dict(self._battery) return battery_to_dict(self._battery)
@bridge.prop(int, notify=brightness_update) @Bridge.prop(int, notify=brightness_update)
def brightness(self): def brightness(self):
return getBrightness(self) return getBrightness(self)
@ -179,63 +178,63 @@ class Greeter(BridgeObject):
args=(self, "-set", quantity)) args=(self, "-set", quantity))
thread.start() thread.start()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_hibernate(self): def can_hibernate(self):
return LightDM.get_can_hibernate() return LightDM.get_can_hibernate()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_restart(self): def can_restart(self):
return LightDM.get_can_restart() return LightDM.get_can_restart()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_shutdown(self): def can_shutdown(self):
return LightDM.get_can_shutdown() return LightDM.get_can_shutdown()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_suspend(self): def can_suspend(self):
return LightDM.get_can_suspend() return LightDM.get_can_suspend()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_access_brightness(self): def can_access_brightness(self):
return self._config.features.backlight["enabled"] return self._config["features"]["backlight"]["enabled"]
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def can_access_battery(self): def can_access_battery(self):
return self._config.features.battery return self._config["features"]["battery"]
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def default_session(self): def default_session(self):
return LightDMGreeter.get_default_session_hint() return LightDMGreeter.get_default_session_hint()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def has_guest_account(self): def has_guest_account(self):
return LightDMGreeter.get_has_guest_account_hint() return LightDMGreeter.get_has_guest_account_hint()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def hide_users_hint(self): def hide_users_hint(self):
return LightDMGreeter.get_hide_users_hint() return LightDMGreeter.get_hide_users_hint()
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def hostname(self): def hostname(self):
return LightDM.get_hostname() return LightDM.get_hostname()
@bridge.prop(bool, notify=property_changed) @Bridge.prop(bool, notify=property_changed)
def in_authentication(self): def in_authentication(self):
return LightDMGreeter.get_in_authentication() return LightDMGreeter.get_in_authentication()
@bridge.prop(bool, notify=property_changed) @Bridge.prop(bool, notify=property_changed)
def is_authenticated(self): def is_authenticated(self):
return LightDMGreeter.get_is_authenticated() return LightDMGreeter.get_is_authenticated()
@bridge.prop(Variant, notify=property_changed) @Bridge.prop(QVariant, notify=property_changed)
def language(self): def language(self):
return language_to_dict(LightDM.get_language()) return language_to_dict(LightDM.get_language())
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def languages(self): def languages(self):
return [language_to_dict(lang) for lang in LightDM.get_languages()] return [language_to_dict(lang) for lang in LightDM.get_languages()]
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def layout(self): def layout(self):
return layout_to_dict(LightDM.get_layout()) return layout_to_dict(LightDM.get_layout())
@ -250,117 +249,117 @@ class Greeter(BridgeObject):
) )
return LightDM.set_layout(LightDM.Layout(**lay)) return LightDM.set_layout(LightDM.Layout(**lay))
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def layouts(self): def layouts(self):
return [layout_to_dict(layout) for layout in LightDM.get_layouts()] return [layout_to_dict(layout) for layout in LightDM.get_layouts()]
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def lock_hint(self): def lock_hint(self):
return LightDMGreeter.get_lock_hint() return LightDMGreeter.get_lock_hint()
@bridge.prop(Variant, notify=property_changed) @Bridge.prop(QVariant, notify=property_changed)
def remote_sessions(self): def remote_sessions(self):
return [session_to_dict(session) for session in LightDM.get_remote_sessions()] return [session_to_dict(session) for session in LightDM.get_remote_sessions()]
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def select_guest_hint(self): def select_guest_hint(self):
return LightDMGreeter.get_select_guest_hint() return LightDMGreeter.get_select_guest_hint()
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def select_user_hint(self): def select_user_hint(self):
return LightDMGreeter.get_select_user_hint() or '' return LightDMGreeter.get_select_user_hint() or ''
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def sessions(self): def sessions(self):
return [session_to_dict(session) for session in LightDM.get_sessions()] return [session_to_dict(session) for session in LightDM.get_sessions()]
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def shared_data_directory(self): def shared_data_directory(self):
return self._shared_data_directory return self._shared_data_directory
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def show_manual_login_hint(self): def show_manual_login_hint(self):
return LightDMGreeter.get_show_manual_login_hint() return LightDMGreeter.get_show_manual_login_hint()
@bridge.prop(bool, notify=noop_signal) @Bridge.prop(bool, notify=noop_signal)
def show_remote_login_hint(self): def show_remote_login_hint(self):
return LightDMGreeter.get_show_remote_login_hint() return LightDMGreeter.get_show_remote_login_hint()
@bridge.prop(str, notify=noop_signal) @Bridge.prop(str, notify=noop_signal)
def themes_directory(self): def themes_directory(self):
return self._themes_directory return self._themes_directory
@bridge.prop(Variant, notify=noop_signal) @Bridge.prop(QVariant, notify=noop_signal)
def users(self): def users(self):
return [user_to_dict(user) for user in LightDMUsers.get_users()] return [user_to_dict(user) for user in LightDMUsers.get_users()]
@bridge.method(str) @Bridge.method(str)
def authenticate(self, username): def authenticate(self, username):
LightDMGreeter.authenticate(username) LightDMGreeter.authenticate(username)
self.property_changed.emit() self.property_changed.emit()
@bridge.method() @Bridge.method()
def authenticate_as_guest(self): def authenticate_as_guest(self):
LightDMGreeter.authenticate_as_guest() LightDMGreeter.authenticate_as_guest()
self.property_changed.emit() self.property_changed.emit()
@bridge.method(int) @Bridge.method(int)
def brightnessSet(self, quantity): def brightnessSet(self, quantity):
thread = threading.Thread(target=changeBrightness, thread = threading.Thread(target=changeBrightness,
args=(self, "-set", quantity)) args=(self, "-set", quantity))
thread.start() thread.start()
@bridge.method(int) @Bridge.method(int)
def brightnessIncrease(self, quantity): def brightnessIncrease(self, quantity):
thread = threading.Thread(target=changeBrightness, thread = threading.Thread(target=changeBrightness,
args=(self, "-inc", quantity)) args=(self, "-inc", quantity))
thread.start() thread.start()
@bridge.method(int) @Bridge.method(int)
def brightnessDecrease(self, quantity): def brightnessDecrease(self, quantity):
thread = threading.Thread(target=changeBrightness, thread = threading.Thread(target=changeBrightness,
args=(self, "-dec", quantity)) args=(self, "-dec", quantity))
thread.start() thread.start()
@bridge.method() @Bridge.method()
def cancel_authentication(self): def cancel_authentication(self):
LightDMGreeter.cancel_authentication() LightDMGreeter.cancel_authentication()
self.property_changed.emit() self.property_changed.emit()
@bridge.method() @Bridge.method()
def cancel_autologin(self): def cancel_autologin(self):
LightDMGreeter.cancel_autologin() LightDMGreeter.cancel_autologin()
self.property_changed.emit() self.property_changed.emit()
@bridge.method(result=bool) @Bridge.method(result=bool)
def hibernate(self): def hibernate(self):
return LightDM.hibernate() return LightDM.hibernate()
@bridge.method(str) @Bridge.method(str)
def respond(self, response): def respond(self, response):
LightDMGreeter.respond(response) LightDMGreeter.respond(response)
self.property_changed.emit() self.property_changed.emit()
@bridge.method(result=bool) @Bridge.method(result=bool)
def restart(self): def restart(self):
return LightDM.restart() return LightDM.restart()
@bridge.method(str) @Bridge.method(str)
def set_language(self, lang): def set_language(self, lang):
if self.is_authenticated: if self.is_authenticated:
LightDMGreeter.set_language(lang) LightDMGreeter.set_language(lang)
self.property_changed.emit() self.property_changed.emit()
@bridge.method(result=bool) @Bridge.method(result=bool)
def shutdown(self): def shutdown(self):
return LightDM.shutdown() return LightDM.shutdown()
@bridge.method(str, result=bool) @Bridge.method(str, result=bool)
def start_session(self, session): def start_session(self, session):
if not session.strip(): if not session.strip():
return return
return LightDMGreeter.start_session_sync(session) return LightDMGreeter.start_session_sync(session)
@bridge.method(result=bool) @Bridge.method(result=bool)
def suspend(self): def suspend(self):
return LightDM.suspend() return LightDM.suspend()

19
web-greeter/bridge/ThemeUtils.py

@ -3,6 +3,7 @@
# ThemeUtils.py # ThemeUtils.py
# #
# Copyright © 2017 Antergos # Copyright © 2017 Antergos
# Copyright © 2021 JezerM
# #
# This file is part of Web Greeter. # This file is part of Web Greeter.
# #
@ -31,29 +32,27 @@ from glob import glob
import tempfile import tempfile
# 3rd-Party Libs # 3rd-Party Libs
from whither.bridge import ( from browser.bridge import Bridge, BridgeObject
BridgeObject, from PyQt5.QtCore import QVariant
bridge,
Variant,
)
from config import web_greeter_config
class ThemeUtils(BridgeObject): class ThemeUtils(BridgeObject):
def __init__(self, greeter, config, *args, **kwargs): def __init__(self, greeter, *args, **kwargs):
super().__init__(name='ThemeUtils', *args, **kwargs) super().__init__(name='ThemeUtils', *args, **kwargs)
self._config = config self._config = web_greeter_config
self._greeter = greeter self._greeter = greeter
self._allowed_dirs = ( self._allowed_dirs = (
self._config.themes_dir, self._config["app"]["theme_dir"],
self._config.branding.background_images_dir, self._config["config"]["branding"]["background_images_dir"],
self._greeter.shared_data_directory, self._greeter.shared_data_directory,
tempfile.gettempdir(), tempfile.gettempdir(),
) )
@bridge.method(str, bool, result=Variant) @Bridge.method(str, bool, result=QVariant)
def dirlist(self, dir_path, only_images=True): def dirlist(self, dir_path, only_images=True):
if not dir_path or not isinstance(dir_path, str) or '/' == dir_path: if not dir_path or not isinstance(dir_path, str) or '/' == dir_path:
return [] return []

46
web-greeter/bridge/devtools.py

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
#
# devtools.py
#
# Copyright © 2016-2017 Antergos
#
# This file is part of whither.
#
# whither is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# whither is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with whither; If not, see <http://www.gnu.org/licenses/>.
# 3rd-Party Libs
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import (
QWebEngineView,
QWebEnginePage,
)
class DevTools:
def __init__(self):
super().__init__()
self.view = QWebEngineView()
self.page = self.view.page() # type: QWebEnginePage
self.view.load(QUrl('http://127.0.0.1:12345'))
self.view.show()

0
web-greeter/browser/__init__.py

46
web-greeter/browser/bridge.py

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
#
# bridge.py
#
# Copyright © 2016-2017 Antergos
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
class Bridge:
@staticmethod
def method(*args, **kwargs):
return pyqtSlot(*args, **kwargs)
@staticmethod
def prop(*args, **kwargs):
return pyqtProperty(*args, **kwargs)
@staticmethod
def signal(*args, **kwargs):
return pyqtSignal(*args, **kwargs)
class BridgeObject(QObject):
def __init__(self, name: str):
super().__init__(parent=None)
self._name = name

276
web-greeter/browser/browser.py

@ -0,0 +1,276 @@
# -*- coding: utf-8 -*-
#
# browser.py
#
# Copyright © 2017 Antergos
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard lib
from bridge.devtools import DevTools
import os
from typing import (
Dict,
Tuple,
TypeVar,
)
# 3rd-Party Libs
from PyQt5.QtCore import QUrl, Qt, QCoreApplication, QFile
from PyQt5.QtWidgets import QApplication, QDesktopWidget, QMainWindow
from PyQt5.QtWebEngineCore import QWebEngineUrlScheme
from PyQt5.QtWebEngineWidgets import QWebEngineScript, QWebEngineProfile, QWebEngineSettings, QWebEngineView
from PyQt5.QtGui import QColor
from PyQt5.QtWebChannel import QWebChannel
from browser.error_prompt import WebPage, errorPrompt
from browser.url_scheme import QtUrlSchemeHandler
from browser.interceptor import QtUrlRequestInterceptor
from logger import logger
from config import web_greeter_config
from bridge import Greeter, Config, ThemeUtils
import resources
# Typing Helpers
BridgeObjects = Tuple['BridgeObject']
Url = TypeVar('Url', str, QUrl)
os.environ["QT_DEVICE_PIXEL_RATIO"] = "0"
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
os.environ["QT_SCREEN_SCALE_FACTORS"] = "1"
os.environ["QT_SCALE_FACTOR"] = "1"
WINDOW_STATES = {
'NORMAL': Qt.WindowNoState,
'MINIMIZED': Qt.WindowMinimized,
'MAXIMIZED': Qt.WindowMaximized,
'FULLSCREEN': Qt.WindowFullScreen,
} # type: Dict[str, Qt.WindowState]
DISABLED_SETTINGS = [
'PluginsEnabled', # Qt 5.6+
]
ENABLED_SETTINGS = [
'FocusOnNavigationEnabled', # Qt 5.8+
'FullScreenSupportEnabled', # Qt 5.6+
'LocalContentCanAccessFileUrls',
'ScreenCaptureEnabled', # Qt 5.7+
'ScrollAnimatorEnabled',
'FocusOnNavigationEnabled', # Qt 5.11+
]
class Application:
app: QApplication
desktop: QDesktopWidget
window: QMainWindow
states = WINDOW_STATES
def __init__(self):
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
self.app = QApplication([])
self.window = QMainWindow()
self.desktop = self.app.desktop()
self.window.setAttribute(Qt.WA_DeleteOnClose)
self.window.setWindowTitle("Web Greeter")
self.window.setWindowFlags(self.window.windowFlags() | Qt.FramelessWindowHint)
self.window.setWindowFlags(
self.window.windowFlags() | Qt.MaximizeUsingFullscreenGeometryHint
)
state = self.states['NORMAL']
try:
self.window.windowHandle().setWindowState(state)
except Exception:
self.window.setWindowState(state)
self.window.setCursor(Qt.ArrowCursor)
self.app.aboutToQuit.connect(self._before_exit)
def _before_exit(self):
pass
def show(self):
self.window.show()
logger.debug("Window is ready")
def run(self) -> int:
logger.debug("Web Greeter started")
return self.app.exec_()
class Browser(Application):
url_scheme: QWebEngineUrlScheme
def __init__(self):
super().__init__()
self.init()
self.load()
def init(self):
logger.debug("Initializing Browser Window")
web_greeter_config["config"]["greeter"]["debug_mode"] = True
if web_greeter_config["config"]["greeter"]["debug_mode"]:
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '12345'
url_scheme = "web-greeter"
self.url_scheme = QWebEngineUrlScheme(url_scheme.encode())
self.url_scheme.setDefaultPort(QWebEngineUrlScheme.SpecialPort.PortUnspecified)
self.url_scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme or
QWebEngineUrlScheme.Flag.LocalScheme or
QWebEngineUrlScheme.Flag.LocalAccessAllowed)
QWebEngineUrlScheme.registerScheme(self.url_scheme)
self.profile = QWebEngineProfile.defaultProfile()
self.interceptor = QtUrlRequestInterceptor(url_scheme)
self.url_scheme_handler = QtUrlSchemeHandler()
self.view = QWebEngineView(parent=self.window)
self.page = WebPage()
self.view.setPage(self.page)
self.channel = QWebChannel(self.page)
self.bridge_initialized = False
self.profile.installUrlSchemeHandler(url_scheme.encode(), self.url_scheme_handler)
self._initialize_page()
if web_greeter_config["config"]["greeter"]["debug_mode"]:
self.devtools = DevTools()
if web_greeter_config["config"]["greeter"]["secure_mode"]:
self.profile.setUrlRequestInterceptor(self.interceptor)
self.page.setBackgroundColor(QColor(0, 0, 0))
self.view.show()
self.window.setCentralWidget(self.view)
logger.debug("Browser Window created")
self.show()
def load(self):
self.greeter = Greeter()
self.greeter_config = Config()
self.theme_utils = ThemeUtils(self.greeter)
self.bridge_objects = (self.greeter, self.greeter_config, self.theme_utils)
self.initialize_bridge_objects()
self.load_script(':/_greeter/js/bundle.js', 'Web Greeter Bundle')
self.load_theme()
def _initialize_page(self):
page_settings = self.page.settings().globalSettings()
if not web_greeter_config["config"]["greeter"]["secure_mode"]:
ENABLED_SETTINGS.append('LocalContentCanAccessRemoteUrls')
else:
DISABLED_SETTINGS.append('LocalContentCanAccessRemoteUrls')
for setting in DISABLED_SETTINGS:
try:
page_settings.setAttribute(getattr(QWebEngineSettings, setting), False)
except AttributeError:
pass
for setting in ENABLED_SETTINGS:
try:
page_settings.setAttribute(getattr(QWebEngineSettings, setting), True)
except AttributeError:
pass
self.page.setView(self.view)
def load_theme(self):
theme = web_greeter_config["config"]["greeter"]["theme"]
dir = "/usr/share/web-greeter/themes/"
path_to_theme = os.path.join(dir, theme, "index.html")
def_theme = "gruvbox"
if (theme.startswith("/")): path_to_theme = theme
elif (theme.__contains__(".") or theme.__contains__("/")):
path_to_theme = os.path.join(os.getcwd(), theme)
if (not path_to_theme.endswith(".html")):
path_to_theme = os.path.join(path_to_theme, "index.html")
if (not os.path.exists(path_to_theme)):
print("Path does not exists")
path_to_theme = os.path.join(dir, def_theme, "index.html")
url = QUrl("web-greeter://app/{0}".format(path_to_theme))
self.page.load(url)
logger.debug("Theme loaded")
@staticmethod
def _create_webengine_script(path: Url, name: str) -> QWebEngineScript:
script = QWebEngineScript()
script_file = QFile(path)
# print(script_file, path)
if script_file.open(QFile.ReadOnly):
script_string = str(script_file.readAll(), 'utf-8')
script.setInjectionPoint(QWebEngineScript.DocumentCreation)
script.setName(name)
script.setWorldId(QWebEngineScript.MainWorld)
script.setSourceCode(script_string)
# print(script_string)
return script
def _get_channel_api_script(self) -> QWebEngineScript:
return self._create_webengine_script(':/qtwebchannel/qwebchannel.js', 'QWebChannel API')
def _init_bridge_channel(self) -> None:
self.page.setWebChannel(self.channel)
self.page.scripts().insert(self._get_channel_api_script())
self.bridge_initialized = True
def initialize_bridge_objects(self) -> None:
if not self.bridge_initialized:
self._init_bridge_channel()
registered_objects = self.channel.registeredObjects()
for obj in self.bridge_objects:
if obj not in registered_objects:
self.channel.registerObject(obj._name, obj)
# print("Registered", obj._name)
def load_script(self, path: Url, name: str):
script = self._create_webengine_script(path, name)
self.page.scripts().insert(script)

140
web-greeter/browser/error_prompt.py

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
#
# error_prompt.py
#
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard lib
# 3rd-Party Libs
from PyQt5.QtWebEngineWidgets import QWebEnginePage
from PyQt5.QtWidgets import QDialogButtonBox, QDialog, QVBoxLayout, QLabel, QPushButton
from config import web_greeter_config
import globals
from logging import (
getLogger,
DEBUG,
Formatter,
StreamHandler,
)
log_format = ''.join([
'%(asctime)s [ %(levelname)s ] %(filename)s %(',
'lineno)d: %(message)s'
])
formatter = Formatter(fmt=log_format, datefmt="%Y-%m-%d %H:%M:%S")
logger = getLogger("javascript")
logger.propagate = False
stream_handler = StreamHandler()
stream_handler.setLevel(DEBUG)
stream_handler.setFormatter(formatter)
logger.setLevel(DEBUG)
logger.addHandler(stream_handler)
class WebPage(QWebEnginePage):
def javaScriptConsoleMessage(self, level: QWebEnginePage.JavaScriptConsoleMessageLevel, message: str, lineNumber: int, sourceID: str):
if sourceID == "":
sourceID = "console"
logLevel = 0
if level == WebPage.JavaScriptConsoleMessageLevel.ErrorMessageLevel:
logLevel = 40
elif level == WebPage.JavaScriptConsoleMessageLevel.WarningMessageLevel:
logLevel = 30
elif level == WebPage.JavaScriptConsoleMessageLevel.InfoMessageLevel:
return
else:
return
record = logger.makeRecord(
name="javascript",
level=logLevel,
fn="",
lno=lineNumber,
msg=message,
args=(),
exc_info=None
)
record.filename = sourceID
logger.handle(record)
if logLevel == 40:
errorMessage = "{source} {line}: {msg}".format(
source=sourceID, line=lineNumber, msg=message)
errorPrompt(errorMessage)
class ErrorDialog(QDialog):
def __init__(self, parent=None, err=""):
super().__init__(parent)
self.setWindowTitle("Error")
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)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
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(self.buttonBox)
self.setLayout(self.layout)
def handle_reload(self, value: bool):
self.done(2)
def errorPrompt(err):
if not web_greeter_config["config"]["greeter"]["detect_theme_errors"]:
return
dia = ErrorDialog(globals.greeter.window, err)
dia.exec()
result = dia.result()
if result == 0: # Cancel
return
elif result == 1: # Default theme
web_greeter_config["config"]["greeter"]["theme"] = "gruvbox"
globals.greeter.load_theme()
return
elif result == 2: # Reload
globals.greeter.load_theme()
return
return

55
web-greeter/browser/interceptor.py

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#
# interceptor.py
#
# Copyright © 2016-2017 Antergos
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# 3rd-Party Libs
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
class QtUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, url_scheme: str):
super().__init__()
self._url_scheme = url_scheme
def intercept_request(self, info: QWebEngineUrlRequestInfo) -> None:
url = info.requestUrl().toString()
not_webg_uri = self._url_scheme != info.requestUrl().scheme()
not_data_uri = 'data' != info.requestUrl().scheme()
not_local_file = not info.requestUrl().isLocalFile()
# print(url)
not_devtools = (
not url.startswith('http://127.0.0.1') and not url.startswith('ws://127.0.0.1')
)
block_request = not_devtools and not_data_uri and not_webg_uri and not_local_file
info.block(block_request) # Block everything that is not allowed
def interceptRequest(self, info: QWebEngineUrlRequestInfo) -> None:
self.intercept_request(info)

74
web-greeter/browser/url_scheme.py

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
#
# url_scheme.py
#
# Copyright © 2016-2018 Antergos
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
""" Custom Url Scheme Handler """
# Standard Lib
import os
import mimetypes
# 3rd-Party Libs
from PyQt5.QtCore import QBuffer, QIODevice
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, QWebEngineUrlRequestJob
class QtUrlSchemeHandler(QWebEngineUrlSchemeHandler):
def requestStarted(self, job: QWebEngineUrlRequestJob) -> None:
path = job.requestUrl().path()
path = os.path.realpath(path)
# print("PATH", job.requestUrl().path())
if not path:
# print("JOB FAIL", path)
job.fail(QWebEngineUrlRequestJob.UrlInvalid)
return
if not os.path.exists(path):
# print("NOT FOUND", path)
job.fail(QWebEngineUrlRequestJob.UrlNotFound)
return
try:
with open(path, 'rb') as file:
content_type = mimetypes.guess_type(path)
if not content_type[0]:
content_type = ["text/plain", None]
buffer = QBuffer(parent=self)
buffer.open(QIODevice.WriteOnly)
buffer.write(file.read())
buffer.seek(0)
buffer.close()
job.reply(content_type[0].encode(), buffer)
except Exception as err:
raise err

83
web-greeter/config.py

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
#
# config.py
#
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard lib
import sys
import os
import ruamel.yaml as yaml
import globals
from logger import logger
path_to_config = "/etc/lightdm/web-greeter.yml"
global web_greeter_config
web_greeter_config = {
"config": {
"branding": {
"background_images_dir": "/usr/share/backgrounds",
"logo_image": "",
"user_image": "",
},
"greeter": {
"debug_mode": False,
"detect_theme_errors": True,
"screensaver_timeout": 300,
"secure_mode": True,
"theme": "gruvbox",
"icon_theme": None,
"time_language": None,
},
"layouts": ["us", "latam"],
"features": {
"battery": False,
"backlight": {
"enabled": False,
"value": 10,
"steps": 0,
}
}
},
"app": {
"fullscreen": True,
"frame": False,
"debug_mode": False,
"theme_dir": "/usr/share/web-greeter/themes/"
}
}
def load_config():
try:
if (not os.path.exists(path_to_config)):
raise Exception("Config file not found")
file = open(path_to_config, "r")
web_greeter_config["config"] = yaml.safe_load(file)
except Exception as err:
logger.error("Config was not loaded:\n\t{0}".format(err))
pass
load_config()

223
web-greeter/globals.py

@ -1,222 +1 @@
# -*- coding: utf-8 -*- global greeter # type: Browser
#
# globals.py
#
# Copyright © 2017 Antergos
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
import sys
import pkg_resources
import os
from typing import (
ClassVar,
Type,
List,
Tuple,
)
# 3rd-Party Libs
from whither.app import App
from whither.base.config_loader import ConfigLoader
from whither.bridge import BridgeObject
# This Application
import resources
from bridge import (
Config,
Greeter,
ThemeUtils,
)
from logging import (
getLogger,
DEBUG,
ERROR,
Formatter,
StreamHandler,
)
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtGui import QColor
from PyQt5.QtCore import QResource
import subprocess
from utils import theme
# Typing Helpers
BridgeObj = Type[BridgeObject]
log_format = ''.join([
'%(asctime)s [ %(levelname)s ] %(module)s - %(filename)s:%(',
'lineno)d : %(funcName)s | %(message)s'
])
formatter = Formatter(fmt=log_format, datefmt="%Y-%m-%d %H:%M:%S")
stream_handler = StreamHandler()
logger = getLogger("debug")
stream_handler.setLevel(DEBUG)
stream_handler.setFormatter(formatter)
logger.propagate = False
logger.setLevel(DEBUG)
logger.addHandler(stream_handler)
initial_timeout = 0
def setScreenSaver(timeout: int):
global initial_timeout
timeout = timeout if timeout >= 0 else 300
try:
child = subprocess.Popen(["xset", "q"], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True)
awk = subprocess.run(
["awk", "/^ timeout: / {print $2}"], stdout=subprocess.PIPE, stdin=child.stdout, text=True)
initial_timeout = int(awk.stdout.replace("\n", ""))
subprocess.run(["xset", "s", str(timeout)], check=True)
except Exception as err:
logger.error("Screensaver timeout couldn't be set")
else:
logger.debug("Screensaver timeout set")
def resetScreenSaver():
try:
subprocess.run(["xset", "s", str(initial_timeout)])
except Exception as err:
logger.error("Screensaver reset failed")
else:
logger.debug("Screensaver reset")
def getDefaultCursor():
cursor_theme = ""
try:
child = subprocess.Popen(["cat", "/usr/share/icons/default/index.theme"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
awk = subprocess.run(
["awk", "-F=", "/Inherits/ {print $2}"], stdout=subprocess.PIPE, stdin=child.stdout, text=True)
cursor_theme = awk.stdout.replace("\n", "")
except Exception as err:
logger.error("Default cursor couldn't be get")
return cursor_theme
def loadStyle(path):
file = QResource(path)
file = file.uncompressedData()
file = str(file.data(), encoding='utf-8')
return file
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
CONFIG_FILE = os.path.join(BASE_DIR, 'whither.yml')
class WebGreeter(App):
greeter = None # type: ClassVar[BridgeObj] | None
greeter_config = None # type: ClassVar[BridgeObj] | None
theme_utils = None # type: ClassVar[BridgeObj] | None
def __init__(self, *args, **kwargs) -> None:
super().__init__('WebGreeter', *args, **kwargs)
self.logger.debug('Web Greeter started.')
self.greeter = Greeter(self.config)
self.greeter_config = Config(self.config)
self.theme_utils = ThemeUtils(self.greeter, self.config)
self._web_container.bridge_objects = (self.greeter, self.greeter_config, self.theme_utils)
style = loadStyle(":/_greeter/css/style.css")
self._main_window.widget.setStyleSheet(style)
page = self._main_window.widget.centralWidget().page()
page.setBackgroundColor(QColor(0, 0, 0))
setScreenSaver(self.config.greeter["screensaver_timeout"])
self._web_container.initialize_bridge_objects()
self._web_container.load_script(':/_greeter/js/bundle.js', 'Web Greeter Bundle')
self.load_theme()
@classmethod
def __pre_init__(cls):
ConfigLoader.add_filter(cls.validate_greeter_config_data)
def _before_main_window_init(self):
self.get_and_apply_user_config()
def _before_exit(self):
resetScreenSaver()
@classmethod
def validate_greeter_config_data(cls, key: str, data: str) -> str:
if "'@" not in data:
return data
if 'WebGreeter' == key:
path = '../build/web-greeter/whither.yml'
else:
path = '../build/dist/web-greeter.yml'
return open(path, 'r').read()
def get_and_apply_user_config(self):
self.logger.debug("Aplying config")
config_file = os.path.join(self.config.config_dir, 'web-greeter.yml')
branding_config = ConfigLoader('branding', config_file).config
greeter_config = ConfigLoader('greeter', config_file).config
features_config = ConfigLoader('features', config_file).config
layouts_config = ConfigLoader('layouts', config_file).config
greeter_config.update(custom_config["app"]["greeter"])
self.config.branding.update(branding_config)
self.config.greeter.update(greeter_config)
self.config.features.update(features_config)
self.config.layouts = layouts_config
cursor_theme = greeter_config["icon_theme"]
os.environ["XCURSOR_THEME"] = cursor_theme if cursor_theme != None else getDefaultCursor()
self._config.debug_mode = greeter_config['debug_mode']
self._config.allow_remote_urls = not greeter_config['secure_mode']
self._config.context_menu.enabled = greeter_config['debug_mode']
self._config.window.update(custom_config["whither"]["window"])
def load_theme(self):
self.logger.debug('Loading theme...')
theme_url = theme.checkTheme(self)
self._web_container.load(theme_url)
global custom_config
global greeter
custom_config = {
"whither": {
"window": {}
},
"app": {
"greeter": {}
}
}

50
web-greeter/logger.py

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
#
# logger.py
#
# Copyright © 2017 Antergos
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
from logging import (
getLogger,
DEBUG,
Formatter,
StreamHandler
)
log_format = ''.join([
'%(asctime)s [ %(levelname)s ] %(module)s - %(filename)s:%(',
'lineno)d : %(funcName)s | %(message)s'
])
formatter = Formatter(fmt=log_format, datefmt="%Y-%m-%d %H:%M:%S")
stream_handler = StreamHandler()
global logger
logger = getLogger("debug")
stream_handler.setLevel(DEBUG)
stream_handler.setFormatter(formatter)
logger.propagate = False
logger.setLevel(DEBUG)
logger.addHandler(stream_handler)

176
web-greeter/main.py

@ -1,176 +0,0 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# main.py
#
# Copyright © 2021 JezerM
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard Lib
import sys
import ruamel.yaml as yaml
import pkg_resources
import os
from typing import ( List )
# 3rd-Party Libs
# This Application
from utils import errorPrompt, keyboard
import globals
from globals import WebGreeter, logger
def loadWhitherConf():
global whither_yaml
global webGreeter_conf
global file_test
try:
file_test = pkg_resources.resource_string("__main__", 'whither.yml').decode('utf-8')
except Exception:
file_test = pkg_resources.resource_string(__file__, 'whither.yml').decode('utf-8')
whither_yaml = yaml.safe_load(file_test)
webGreeter_conf = whither_yaml["WebGreeter"]
def show_help():
version = webGreeter_conf["app"]["version"]["full"]
help_text = """Usage:
web-greeter [OPTION...] - LightDM Web Greeter
--debug Runs the greeter in debug mode
--normal Runs in non-debug mode
--list Lists available themes
--theme Sets the theme to use
-h, --help Show this help list
-v, --version Print program version""".format(
version = version
)
print(help_text)
def show_version():
version = webGreeter_conf["app"]["version"]["full"]
print("{version}".format(version = version))
def changeConfig(option: str, value):
custom_config[option] = value
return
def debugMode(value: bool):
window = dict(custom_config["whither"]["window"])
greeter = dict(custom_config["app"]["greeter"])
if value:
greeter["debug_mode"] = True
window["decorated"] = True
window["stays_on_top"] = False
window["initial_state"] = "normal"
else:
greeter["debug_mode"] = False
window["decorated"] = False
window["stays_on_top"] = True
custom_config["whither"]["window"] = window
custom_config["app"]["greeter"] = greeter
def changeTheme(theme: str):
custom_config["app"]["greeter"]["theme"] = theme
def listThemes(quiet = False):
themes_dir = webGreeter_conf["app"]["themes_dir"]
themes_dir = themes_dir if os.path.exists(themes_dir) else "/usr/share/web-greeter/themes"
filenames = os.listdir(themes_dir)
dirlist = []
for file in filenames:
if os.path.isdir(os.path.join(themes_dir, file)):
dirlist.append(file)
if not quiet:
print("Themes are located in {themes_dir}\n".format(themes_dir = themes_dir))
for theme in dirlist:
print("-", theme)
return dirlist
args_lenght = sys.argv.__len__()
def yargs(args: List[str]):
loadWhitherConf()
used = 0
if args[0] == "--help" or args[0] == "-h":
show_help()
used += 1
exit()
elif args[0] == "--version" or args[0] == "-v":
show_version()
used += 1
exit()
elif args[0] == "--debug":
debugMode(True)
used += 1
elif args[0] == "--normal":
debugMode(False)
used += 1
elif args[0] == "--theme":
if args.__len__() > 1:
changeTheme(args[1])
used += 2
else:
print("No theme provided")
used += 1
exit(1)
elif args[0] == "--list":
listThemes()
used += 1
exit()
else:
show_help()
used += 1
exit(1)
for x in range(used):
args.pop(0)
if args.__len__() != 0:
yargs(args)
if __name__ == '__main__':
custom_config = globals.custom_config
if args_lenght > 1:
args = sys.argv
args.pop(0)
yargs(args)
globals.greeter = WebGreeter()
globals.greeter.run()

0
web-greeter/requirements.txt

62
web-greeter/whither.yml

@ -1,62 +0,0 @@
# Whither (Universal Linux Apps) Configuration
# App Name
WebGreeter:
# Whither's Config
whither:
allow_remote_urls: False
at_spi_service:
enabled: '@at_spi_service@'
command: /usr/lib/at-spi2-core/at-spi-bus-launcher
arg: --launch-immediately
app_id: web-greeter
url_scheme: web-greeter
context_menu:
enabled: False
debug_mode: '@debug_mode@'
entry_point:
autoload: False
toolbar:
enabled: False
toolkit: auto
window:
decorated: '@decorated@'
initial_state: maximized
stays_on_top: '@stays_on_top@'
title: Web Greeter for LightDM
# App's Config
app:
branding:
background_images_dir: '@background_images_dir@'
logo_image: '@logo_image@'
user_image: '@user_image@'
config_dir: '@config_dir@'
greeter:
debug_mode: '@debug_mode@'
detect_theme_errors: True
screensaver_timeout: 300
secure_mode: True
theme: gruvbox
icon_theme:
time_language:
layouts:
- us
- latam
features:
battery: False
backlight:
enabled: False
value: 10
steps: 0
greeters_dir: '@greeters_dir@'
locale_dir: '@locale_dir@'
themes_dir: '@themes_dir@'
version:
full: '3.0.0'
major: 3
minor: 0
micro: 0
alpha: False
beta: False
rc: False
Loading…
Cancel
Save