Pull gnome-specific portions of UI out so UI package can be reused for breezy-box
This commit is contained in:
parent
254fff37ff
commit
ece35f9af0
|
|
@ -97,7 +97,7 @@ popd
|
|||
UI_BUILD_ARTIFACT=$UI_DIR/out/breezyUI-$ARCH.tar.gz
|
||||
if [ ! -e "$UI_BUILD_ARTIFACT" ] || [ "$1" == "--rebuild-ui" ] || [ "$1" == "--rebuild-all" ]; then
|
||||
pushd $UI_DIR
|
||||
bin/package $ARCH
|
||||
RUNTIME_DIR=$GNOME_DIR/ui bin/package $ARCH
|
||||
popd
|
||||
fi
|
||||
tar -xf $UI_BUILD_ARTIFACT -C $PACKAGE_DIR
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
"""GNOME Shell runtime environment for Breezy Desktop.
|
||||
|
||||
This is the reference RuntimeEnvironment implementation. It is packaged into the
|
||||
UI's ``runtimes`` subpackage by the GNOME package script (see bin/package_gnome
|
||||
-> ui/bin/package), so its imports are relative to the installed
|
||||
``breezydesktop`` package.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pydbus
|
||||
|
||||
from ..runtimeenvironment import RuntimeEnvironment
|
||||
|
||||
logger = logging.getLogger('breezy_ui')
|
||||
|
||||
BREEZY_DESKTOP_UUID = "breezydesktop@xronlinux.com"
|
||||
EXTENSION_STATE_ENABLED = 1
|
||||
|
||||
|
||||
class BreezyGNOMERuntimeEnvironment(RuntimeEnvironment):
|
||||
"""Runs Breezy Desktop as a GNOME Shell extension.
|
||||
|
||||
Enablement is backed by the GNOME Shell extension state, verification runs
|
||||
the breezy_gnome_verify binary, updates are checked against GitHub, and
|
||||
virtual displays are created via the Mutter ScreenCast portal.
|
||||
"""
|
||||
|
||||
APP_NAMESPACE = 'breezy_gnome'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.bus = pydbus.SessionBus()
|
||||
self.gnome_shell_extensions = self.bus.get("org.gnome.Shell.Extensions")
|
||||
self.gnome_shell_extensions.ExtensionStateChanged.connect(self._handle_extension_state_change)
|
||||
|
||||
self._breezy_enabled = self.is_enabled()
|
||||
|
||||
def _handle_extension_state_change(self, extension_uuid, state):
|
||||
if extension_uuid == BREEZY_DESKTOP_UUID:
|
||||
enabled = state.get('state') == EXTENSION_STATE_ENABLED
|
||||
# update internal state first so do_set_property doesn't re-trigger
|
||||
# an extension enable/disable; this just emits the notify
|
||||
self._breezy_enabled = enabled
|
||||
self.set_property('breezy-enabled', enabled)
|
||||
|
||||
# --- effect enablement ------------------------------------------------
|
||||
|
||||
def is_installed(self):
|
||||
extensions_result = self.gnome_shell_extensions.ListExtensions()
|
||||
for extension in extensions_result:
|
||||
if extension == BREEZY_DESKTOP_UUID:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_enabled(self):
|
||||
return self.gnome_shell_extensions.GetExtensionInfo(BREEZY_DESKTOP_UUID).get('state') == EXTENSION_STATE_ENABLED
|
||||
|
||||
def enable(self):
|
||||
if not self.gnome_shell_extensions.UserExtensionsEnabled:
|
||||
self.gnome_shell_extensions.UserExtensionsEnabled = True
|
||||
self.gnome_shell_extensions.EnableExtension(BREEZY_DESKTOP_UUID)
|
||||
self._breezy_enabled = True
|
||||
|
||||
def disable(self):
|
||||
self.gnome_shell_extensions.DisableExtension(BREEZY_DESKTOP_UUID)
|
||||
self._breezy_enabled = False
|
||||
|
||||
# --- verification / updates -------------------------------------------
|
||||
|
||||
def verify(self):
|
||||
from .verify import verify_installation
|
||||
return verify_installation()
|
||||
|
||||
def check_for_update(self, current_version, callback):
|
||||
from .updatechecker import check_for_update
|
||||
return check_for_update(current_version, callback)
|
||||
|
||||
# --- optional views ---------------------------------------------------
|
||||
|
||||
@property
|
||||
def shows_no_device_view(self):
|
||||
return True
|
||||
|
||||
# --- virtual displays -------------------------------------------------
|
||||
|
||||
def is_virtual_display_supported(self):
|
||||
# wayland + the Mutter ScreenCast portal are required to create displays
|
||||
from .virtualdisplay import is_screencast_available
|
||||
return is_screencast_available() and "WAYLAND_DISPLAY" in os.environ
|
||||
|
||||
def _create_virtual_display_manager(self):
|
||||
from .virtualdisplaymanager import VirtualDisplayManager
|
||||
return VirtualDisplayManager.get_instance()
|
||||
|
||||
# --- GObject property plumbing ----------------------------------------
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
if prop.name == 'breezy-enabled' and value != self._breezy_enabled:
|
||||
self.enable() if value else self.disable()
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'breezy-enabled':
|
||||
return self._breezy_enabled
|
||||
|
|
@ -2,7 +2,7 @@ import logging
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
from .files import get_bin_home
|
||||
from ..files import get_bin_home
|
||||
|
||||
logger = logging.getLogger('breezy_ui')
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ logger = logging.getLogger('breezy_ui')
|
|||
gi.require_version('GLib', '2.0')
|
||||
from gi.repository import GLib, GObject
|
||||
|
||||
from .files import get_bin_home
|
||||
from ..files import get_bin_home
|
||||
|
||||
class VirtualDisplayManager(GObject.GObject):
|
||||
__gproperties__ = {
|
||||
|
|
@ -6,6 +6,18 @@ set -e
|
|||
ARCH=${ARCH:-$(uname -m)}
|
||||
echo "Building Breezy UI for $ARCH"
|
||||
|
||||
# Directory containing the RuntimeEnvironment implementation(s) to bundle. Every
|
||||
# *.py in here is copied into the UI's runtimes/ package; the first
|
||||
# RuntimeEnvironment subclass found at startup becomes the active environment.
|
||||
# Defaults to the reference GNOME implementation so a standalone UI build (e.g.
|
||||
# local dev) still has a runtime.
|
||||
RUNTIME_DIR=${RUNTIME_DIR:-$(realpath "$(dirname "$0")/../../gnome/ui")}
|
||||
echo "Bundling runtime environment from $RUNTIME_DIR"
|
||||
if [ ! -d "$RUNTIME_DIR" ]; then
|
||||
echo "Runtime directory $RUNTIME_DIR does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_PATH=build
|
||||
UI_BUILD_PATH=$BUILD_PATH/ui
|
||||
PACKAGE_DIR=$BUILD_PATH/breezy_ui
|
||||
|
|
@ -38,6 +50,12 @@ mkdir -p $PACKAGE_SCHEMAS_DIR
|
|||
|
||||
cp src/*.py $PACKAGE_BREEZY_SRC_DIR
|
||||
cp -r lib $PACKAGE_BREEZY_SRC_DIR
|
||||
|
||||
# bundle the runtimes subpackage and the selected runtime implementation(s)
|
||||
PACKAGE_RUNTIMES_DIR=$PACKAGE_BREEZY_SRC_DIR/runtimes
|
||||
mkdir -p $PACKAGE_RUNTIMES_DIR
|
||||
cp src/runtimes/__init__.py $PACKAGE_RUNTIMES_DIR
|
||||
cp $RUNTIME_DIR/*.py $PACKAGE_RUNTIMES_DIR
|
||||
cp -L modules/PyXRLinuxDriverIPC/xrdriveripc.py $PACKAGE_BREEZY_SRC_DIR
|
||||
cp $UI_BUILD_PATH/src/breezydesktop.gresource $PACKAGE_BREEZY_DIR
|
||||
cp -r po/mo/* $PACKAGE_LOCALE_DIR
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@ from gi.repository import Gio, GLib, Gtk, GObject
|
|||
from .configmanager import ConfigManager
|
||||
from .customresolutiondialog import CustomResolutionDialog
|
||||
from .displaydistancedialog import DisplayDistanceDialog
|
||||
from .extensionsmanager import ExtensionsManager
|
||||
from .files import get_state_dir
|
||||
from .license import BREEZY_GNOME_FEATURES
|
||||
from .runtimeenvironment import RuntimeEnvironment
|
||||
from .settingsmanager import SettingsManager
|
||||
from .shortcutdialog import bind_shortcut_settings
|
||||
from .statemanager import StateManager
|
||||
from .virtualdisplaymanager import VirtualDisplayManager
|
||||
from .virtualdisplay import is_screencast_available
|
||||
from .virtualdisplayrow import VirtualDisplayRow
|
||||
from .xrdriveripc import XRDriverIPC
|
||||
|
||||
|
|
@ -115,8 +113,8 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.settings = SettingsManager.get_instance().settings
|
||||
self.desktop_settings = SettingsManager.get_instance().desktop_settings
|
||||
self.ipc = XRDriverIPC.get_instance()
|
||||
self.virtual_display_manager = VirtualDisplayManager.get_instance()
|
||||
self.extensions_manager = ExtensionsManager.get_instance()
|
||||
self.runtime = RuntimeEnvironment.get_instance()
|
||||
self.virtual_display_manager = self.runtime.virtual_display_manager
|
||||
|
||||
self.settings.bind('disable-physical-displays', self.disable_physical_displays_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.connect('changed::display-distance', self._handle_display_distance)
|
||||
|
|
@ -207,7 +205,7 @@ class ConnectedDevice(Gtk.Box):
|
|||
self._handle_device_supports_sbs(self.state_manager, None)
|
||||
self._handle_enabled_config(None, None)
|
||||
self._refresh_use_optimal_monitor_config(self.use_optimal_monitor_config_switch, None)
|
||||
self.extensions_manager.connect('notify::breezy-enabled', self._handle_enabled_config)
|
||||
self.runtime.connect('notify::breezy-enabled', self._handle_enabled_config)
|
||||
|
||||
self._settings_displays_app_info = None
|
||||
|
||||
|
|
@ -229,9 +227,6 @@ class ConnectedDevice(Gtk.Box):
|
|||
for id in self._custom_resolution_options:
|
||||
self.add_virtual_display_menu.insert(self._default_resolution_options_count, id, id)
|
||||
|
||||
# wayland is required to create virtual displays
|
||||
self.is_wayland = "WAYLAND_DISPLAY" in os.environ
|
||||
|
||||
def _bind_scale_to_config(self, scale, config_key):
|
||||
self.config_manager.bind_property(config_key, scale, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
scale.set_value(self.config_manager.get_property(config_key))
|
||||
|
|
@ -285,7 +280,7 @@ class ConnectedDevice(Gtk.Box):
|
|||
# self.widescreen_mode_row.set_subtitle(subtitle)
|
||||
|
||||
def _handle_enabled_config(self, object, val):
|
||||
enabled = self.config_manager.get_property('breezy-desktop-enabled') and self.extensions_manager.get_property('breezy-enabled')
|
||||
enabled = self.config_manager.get_property('breezy-desktop-enabled') and self.runtime.get_property('breezy-enabled')
|
||||
if enabled != self.effect_enable_switch.get_active():
|
||||
self.effect_enable_switch.set_active(enabled)
|
||||
|
||||
|
|
@ -297,14 +292,14 @@ class ConnectedDevice(Gtk.Box):
|
|||
|
||||
# never turn off the extension, disabling the effect is done via configs only
|
||||
if requesting_enabled:
|
||||
self.extensions_manager.set_property('breezy-enabled', True)
|
||||
self.runtime.set_property('breezy-enabled', True)
|
||||
|
||||
self.config_manager.set_property('breezy-desktop-enabled', requesting_enabled)
|
||||
|
||||
for widget in self.all_enabled_state_inputs:
|
||||
widget.set_sensitive(requesting_enabled)
|
||||
|
||||
if not is_screencast_available() or not self.is_wayland:
|
||||
if not self.runtime.is_virtual_display_supported():
|
||||
self.virtual_displays_row.set_subtitle(
|
||||
_("Unable to add virtual displays on this machine. Wayland, xdg-desktop-portal, and the pipewire GStreamer plugin are required."))
|
||||
self.add_virtual_display_button.set_sensitive(False)
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import pydbus
|
||||
from gi.repository import GObject
|
||||
|
||||
BREEZY_DESKTOP_UUID = "breezydesktop@xronlinux.com"
|
||||
EXTENSION_STATE_ENABLED = 1
|
||||
|
||||
class ExtensionsManager(GObject.GObject):
|
||||
__gproperties__ = {
|
||||
'breezy-enabled': (bool, 'Breezy Enabled', 'Whether the Breezy Desktop GNOME extension is enabled', False, GObject.ParamFlags.READWRITE)
|
||||
}
|
||||
|
||||
_instance = None
|
||||
|
||||
@staticmethod
|
||||
def get_instance():
|
||||
if ExtensionsManager._instance is None:
|
||||
ExtensionsManager._instance = ExtensionsManager()
|
||||
return ExtensionsManager._instance
|
||||
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
self.bus = pydbus.SessionBus()
|
||||
self.gnome_shell_extensions = self.bus.get("org.gnome.Shell.Extensions")
|
||||
self.gnome_shell_extensions.ExtensionStateChanged.connect(self._handle_extension_state_change)
|
||||
|
||||
self.remote_extension_state = self.is_enabled()
|
||||
|
||||
def _handle_extension_state_change(self, extension_uuid, state):
|
||||
if extension_uuid == BREEZY_DESKTOP_UUID:
|
||||
self.remote_extension_state = state.get('state') == EXTENSION_STATE_ENABLED
|
||||
self.set_property('breezy-enabled', self.remote_extension_state)
|
||||
|
||||
def is_installed(self):
|
||||
return self._is_installed(BREEZY_DESKTOP_UUID)
|
||||
|
||||
def enable(self):
|
||||
self._enable_extension(BREEZY_DESKTOP_UUID)
|
||||
|
||||
def disable(self):
|
||||
self._disable_extension(BREEZY_DESKTOP_UUID)
|
||||
|
||||
def is_enabled(self):
|
||||
return self._is_enabled(BREEZY_DESKTOP_UUID)
|
||||
|
||||
def _is_installed(self, extension_uuid):
|
||||
extensions_result = self.gnome_shell_extensions.ListExtensions()
|
||||
for extension in extensions_result:
|
||||
if extension == extension_uuid:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _enable_extension(self, extension_uuid):
|
||||
if not self.gnome_shell_extensions.UserExtensionsEnabled:
|
||||
self.gnome_shell_extensions.UserExtensionsEnabled = True
|
||||
|
||||
self.gnome_shell_extensions.EnableExtension(extension_uuid)
|
||||
|
||||
def _disable_extension(self, extension_uuid):
|
||||
self.gnome_shell_extensions.DisableExtension(extension_uuid)
|
||||
|
||||
def _is_enabled(self, extension_uuid):
|
||||
return self.gnome_shell_extensions.GetExtensionInfo(extension_uuid).get('state') == EXTENSION_STATE_ENABLED
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
if prop.name == 'breezy-enabled' and value != self.remote_extension_state:
|
||||
self.enable() if value == True else self.disable()
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'breezy-enabled':
|
||||
return self.remote_extension_state
|
||||
|
|
@ -10,8 +10,11 @@ def get_config_dir():
|
|||
return os.path.expanduser(config_home)
|
||||
|
||||
def get_state_dir():
|
||||
# imported lazily to avoid an import cycle (runtime discovery imports
|
||||
# modules that import this one)
|
||||
from .runtime import runtime_namespace
|
||||
state_home = os.environ.get('XDG_STATE_HOME', '~/.local/state')
|
||||
return os.path.join(os.path.expanduser(state_home), 'breezy_gnome')
|
||||
return os.path.join(os.path.expanduser(state_home), runtime_namespace())
|
||||
|
||||
def get_data_home():
|
||||
data_home = os.environ.get('XDG_DATA_HOME', '~/.local/share')
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ breezydesktop_sources = [
|
|||
'connecteddevice.py',
|
||||
'customresolutiondialog.py',
|
||||
'customresolutiondialogcontent.py',
|
||||
'extensionsmanager.py',
|
||||
'displaydistancedialog.py',
|
||||
'displaydistancedialogcontent.py',
|
||||
'failedverification.py',
|
||||
|
|
@ -57,16 +56,18 @@ breezydesktop_sources = [
|
|||
'nodriver.py',
|
||||
'noextension.py',
|
||||
'nolicense.py',
|
||||
'runtime.py',
|
||||
'runtimeenvironment.py',
|
||||
'settingsmanager.py',
|
||||
'shortcutdialog.py',
|
||||
'statemanager.py',
|
||||
'time.py',
|
||||
'updatechecker.py',
|
||||
'virtualdisplay.py',
|
||||
'virtualdisplaymanager.py',
|
||||
'verify.py',
|
||||
'window.py'
|
||||
]
|
||||
|
||||
install_data(breezydesktop_sources, install_dir: moduledir)
|
||||
install_subdir('../lib', install_dir: moduledir)
|
||||
install_subdir('../lib', install_dir: moduledir)
|
||||
|
||||
# the runtimes subpackage marker; a concrete RuntimeEnvironment implementation
|
||||
# is copied in per-build by the package script (see ui/bin/package).
|
||||
install_data('runtimes/__init__.py', install_dir: moduledir / 'runtimes')
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from gi.repository import Gio, Gtk
|
||||
from .configmanager import ConfigManager
|
||||
from .extensionsmanager import ExtensionsManager
|
||||
from .runtimeenvironment import RuntimeEnvironment
|
||||
from .settingsmanager import SettingsManager
|
||||
from .statemanager import StateManager
|
||||
from .xrdriveripc import XRDriverIPC
|
||||
|
|
@ -18,7 +18,7 @@ class NoDevice(Gtk.Box):
|
|||
self.init_template()
|
||||
|
||||
self.ipc = XRDriverIPC.get_instance()
|
||||
self.extensions_manager = ExtensionsManager.get_instance()
|
||||
self.runtime = RuntimeEnvironment.get_instance()
|
||||
self.settings = SettingsManager.get_instance().settings
|
||||
self.config_manager = ConfigManager.get_instance()
|
||||
self.config_manager.connect('notify::breezy-desktop-enabled', self._handle_enabled_config)
|
||||
|
|
@ -30,16 +30,16 @@ class NoDevice(Gtk.Box):
|
|||
self._handle_enabled_config(self.config_manager, None)
|
||||
|
||||
def _handle_enabled_config(self, config_manager, val):
|
||||
enabled = config_manager.get_property('breezy-desktop-enabled') and self.extensions_manager.get_property('breezy-enabled')
|
||||
enabled = config_manager.get_property('breezy-desktop-enabled') and self.runtime.get_property('breezy-enabled')
|
||||
if enabled != self.effect_enable_switch.get_active():
|
||||
self.effect_enable_switch.set_active(enabled)
|
||||
|
||||
|
||||
def _handle_switch_enabled_state(self, switch, param):
|
||||
requesting_enabled = switch.get_active()
|
||||
|
||||
# never turn off the extension, disabling the effect is done via configs only
|
||||
if requesting_enabled:
|
||||
self.extensions_manager.set_property('breezy-enabled', True)
|
||||
self.runtime.set_property('breezy-enabled', True)
|
||||
|
||||
self.config_manager.set_property('breezy-desktop-enabled', requesting_enabled)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
"""Runtime environment discovery.
|
||||
|
||||
Exactly one concrete :class:`~breezydesktop.runtimeenvironment.RuntimeEnvironment`
|
||||
implementation is bundled into the ``runtimes`` subpackage at package time. This
|
||||
module finds it (the first one it sees) and exposes both the class and a cheap
|
||||
way to read its namespace without constructing the (potentially side-effectful)
|
||||
instance.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
import pkgutil
|
||||
|
||||
from .runtimeenvironment import RuntimeEnvironment, DEFAULT_APP_NAMESPACE
|
||||
|
||||
logger = logging.getLogger('breezy_ui')
|
||||
|
||||
_runtime_class = None
|
||||
|
||||
|
||||
def get_runtime_class():
|
||||
"""Return the active RuntimeEnvironment subclass.
|
||||
|
||||
Scans the bundled ``runtimes`` subpackage and returns the first concrete
|
||||
RuntimeEnvironment subclass found. The result is cached. Raises
|
||||
RuntimeError if no implementation is bundled.
|
||||
"""
|
||||
global _runtime_class
|
||||
if _runtime_class is not None:
|
||||
return _runtime_class
|
||||
|
||||
from . import runtimes
|
||||
|
||||
for module_info in pkgutil.iter_modules(runtimes.__path__, runtimes.__name__ + '.'):
|
||||
try:
|
||||
module = importlib.import_module(module_info.name)
|
||||
except Exception as e:
|
||||
logger.error("Failed to import runtime module %s: %s", module_info.name, e)
|
||||
continue
|
||||
|
||||
for _, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if issubclass(obj, RuntimeEnvironment) and obj is not RuntimeEnvironment \
|
||||
and obj.__module__ == module_info.name:
|
||||
logger.info("Using runtime environment %s", obj.__name__)
|
||||
_runtime_class = obj
|
||||
return _runtime_class
|
||||
|
||||
raise RuntimeError(
|
||||
"No RuntimeEnvironment implementation was found in the 'runtimes' package. "
|
||||
"A runtime implementation must be bundled at package time.")
|
||||
|
||||
|
||||
def runtime_namespace():
|
||||
"""Return the active runtime's application namespace.
|
||||
|
||||
Falls back to the default namespace if no runtime is bundled, so early
|
||||
bootstrap paths (e.g. log directory setup) never fail.
|
||||
"""
|
||||
try:
|
||||
return get_runtime_class().app_namespace()
|
||||
except RuntimeError:
|
||||
return DEFAULT_APP_NAMESPACE
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
import gettext
|
||||
|
||||
from gi.repository import GObject
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
# Default namespace used for XDG directories, application identity, etc. A
|
||||
# concrete RuntimeEnvironment should override APP_NAMESPACE.
|
||||
DEFAULT_APP_NAMESPACE = 'breezy_gnome'
|
||||
|
||||
|
||||
class NullVirtualDisplayManager(GObject.GObject):
|
||||
"""A no-op virtual display manager.
|
||||
|
||||
Provides the same interface (the 'displays' property + change
|
||||
notifications, plus create/destroy methods) that the UI binds to, but
|
||||
never creates anything. Runtime environments that don't support virtual
|
||||
displays can use this so the UI degrades gracefully.
|
||||
"""
|
||||
__gproperties__ = {
|
||||
'displays': (object, 'Displays', 'A list of the displays', GObject.ParamFlags.READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
self._displays = []
|
||||
|
||||
def create_virtual_display(self, width, height, framerate):
|
||||
return None
|
||||
|
||||
def destroy_virtual_display(self, pid):
|
||||
return False
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
if prop.name == 'displays':
|
||||
self._displays = value
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'displays':
|
||||
return self._displays
|
||||
|
||||
|
||||
class RuntimeEnvironment(GObject.GObject):
|
||||
"""Abstraction over the host environment the UI is running in.
|
||||
|
||||
A RuntimeEnvironment encapsulates everything that differs between the
|
||||
environments Breezy can run in (e.g. GNOME Shell vs. a headless Breezy Box):
|
||||
how the effect is enabled, how the installation is verified, whether/how
|
||||
updates are checked, how virtual displays are managed, and which optional
|
||||
views and fields the UI should present.
|
||||
|
||||
The first concrete subclass discovered in the bundled ``runtimes`` package
|
||||
(see :mod:`breezydesktop.runtime`) is instantiated as the active
|
||||
environment, so a build can swap behavior simply by packaging a different
|
||||
implementation.
|
||||
|
||||
Subclasses inherit the ``breezy-enabled`` GObject property; override
|
||||
:meth:`enable`/:meth:`disable`/:meth:`is_enabled` (and, if needed,
|
||||
``do_set_property``/``do_get_property``) to back it with real state.
|
||||
"""
|
||||
|
||||
# The application/namespace identifier, e.g. 'breezy_gnome' or 'breezy_box'.
|
||||
# Used for namespacing XDG directories and similar. Subclasses must set
|
||||
# this.
|
||||
APP_NAMESPACE = None
|
||||
|
||||
__gproperties__ = {
|
||||
'breezy-enabled': (bool, 'Breezy Enabled', 'Whether the Breezy Desktop effect is enabled', False, GObject.ParamFlags.READWRITE)
|
||||
}
|
||||
|
||||
_instance = None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
if RuntimeEnvironment._instance is None:
|
||||
from .runtime import get_runtime_class
|
||||
RuntimeEnvironment._instance = get_runtime_class()()
|
||||
return RuntimeEnvironment._instance
|
||||
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
self._breezy_enabled = False
|
||||
self._virtual_display_manager = None
|
||||
|
||||
# --- identity ---------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
def app_namespace(cls):
|
||||
return cls.APP_NAMESPACE or DEFAULT_APP_NAMESPACE
|
||||
|
||||
# --- effect enablement (backs the 'breezy-enabled' property) ----------
|
||||
|
||||
def is_installed(self):
|
||||
"""Whether the supporting components for this environment are installed.
|
||||
|
||||
Environments with no separate component to install (e.g. a headless
|
||||
box where the runtime is always present) should return True.
|
||||
"""
|
||||
return True
|
||||
|
||||
def is_enabled(self):
|
||||
return self._breezy_enabled
|
||||
|
||||
def enable(self):
|
||||
self._breezy_enabled = True
|
||||
|
||||
def disable(self):
|
||||
self._breezy_enabled = False
|
||||
|
||||
# --- verification -----------------------------------------------------
|
||||
|
||||
def verify(self):
|
||||
"""Verify the installation. Return True when verification passes (or
|
||||
when the environment has no verification step)."""
|
||||
return True
|
||||
|
||||
# --- update checking --------------------------------------------------
|
||||
|
||||
def check_for_update(self, current_version, callback):
|
||||
"""Asynchronously check for a newer version.
|
||||
|
||||
Implementations that support updates should invoke
|
||||
``callback(latest_version_str)`` when a newer version is available.
|
||||
The default is a no-op (no update prompt).
|
||||
"""
|
||||
return None
|
||||
|
||||
# --- optional views / fields ------------------------------------------
|
||||
|
||||
@property
|
||||
def shows_no_device_view(self):
|
||||
"""Whether a dedicated "no device connected" view should be shown.
|
||||
|
||||
When False, the connected-device view is always shown and
|
||||
:meth:`no_device_label` supplies the label used when no device is
|
||||
actually connected.
|
||||
"""
|
||||
return False
|
||||
|
||||
def no_device_label(self):
|
||||
"""Label shown in place of a device name when no device is connected
|
||||
(only relevant when :attr:`shows_no_device_view` is False)."""
|
||||
return _("No supported glasses connected")
|
||||
|
||||
# --- virtual displays -------------------------------------------------
|
||||
|
||||
def is_virtual_display_supported(self):
|
||||
return False
|
||||
|
||||
def _create_virtual_display_manager(self):
|
||||
"""Build the virtual display manager for this environment. Override to
|
||||
provide a real implementation."""
|
||||
return NullVirtualDisplayManager()
|
||||
|
||||
@property
|
||||
def virtual_display_manager(self):
|
||||
if self._virtual_display_manager is None:
|
||||
self._virtual_display_manager = self._create_virtual_display_manager()
|
||||
return self._virtual_display_manager
|
||||
|
||||
# --- GObject property plumbing ----------------------------------------
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
if prop.name == 'breezy-enabled' and value != self.is_enabled():
|
||||
self.enable() if value else self.disable()
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'breezy-enabled':
|
||||
return self.is_enabled()
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
"""Bundled runtime environment implementations.
|
||||
|
||||
Exactly one concrete RuntimeEnvironment implementation module is copied into
|
||||
this package at package time (see ui/bin/package). The implementation is
|
||||
selected per-build from a runtime source directory (e.g. gnome/ui), so behavior
|
||||
can be swapped without touching the core UI.
|
||||
"""
|
||||
|
|
@ -85,8 +85,8 @@ def create_display(width, height, framerate):
|
|||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from breezydesktop import virtualdisplay
|
||||
from breezydesktop.virtualdisplay import VirtualDisplay
|
||||
from breezydesktop.runtimes import virtualdisplay
|
||||
from breezydesktop.runtimes.virtualdisplay import VirtualDisplay
|
||||
|
||||
global virtual_display_instance
|
||||
global loop
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from gi.repository import Adw, Gtk
|
||||
from .virtualdisplaymanager import VirtualDisplayManager
|
||||
from .runtimeenvironment import RuntimeEnvironment
|
||||
|
||||
import gettext
|
||||
|
||||
|
|
@ -28,4 +28,4 @@ class VirtualDisplayRow(Adw.ActionRow):
|
|||
self.remove_virtual_display_button.connect('clicked', self._remove_virtual_display)
|
||||
|
||||
def _remove_virtual_display(self, widget):
|
||||
VirtualDisplayManager.get_instance().destroy_virtual_display(self.pid)
|
||||
RuntimeEnvironment.get_instance().virtual_display_manager.destroy_virtual_display(self.pid)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from gi.repository import Gtk, GLib
|
||||
from .extensionsmanager import ExtensionsManager
|
||||
from .license import BREEZY_GNOME_FEATURES
|
||||
from .licensedialog import LicenseDialog
|
||||
from .runtimeenvironment import RuntimeEnvironment
|
||||
from .statemanager import StateManager
|
||||
from .settingsmanager import SettingsManager
|
||||
from .connecteddevice import ConnectedDevice
|
||||
|
|
@ -10,8 +10,6 @@ from .nodevice import NoDevice
|
|||
from .nodriver import NoDriver
|
||||
from .noextension import NoExtension
|
||||
from .nolicense import NoLicense
|
||||
from .updatechecker import check_for_update
|
||||
from .verify import verify_installation
|
||||
|
||||
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/window.ui')
|
||||
class BreezydesktopWindow(Gtk.ApplicationWindow):
|
||||
|
|
@ -29,6 +27,7 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
def __init__(self, version, skip_verification, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.runtime = RuntimeEnvironment.get_instance()
|
||||
self.connected_device = ConnectedDevice()
|
||||
self.failed_verification = FailedVerification()
|
||||
self.no_device = NoDevice()
|
||||
|
|
@ -57,7 +56,7 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
|
||||
self.connect("destroy", self._on_window_destroy)
|
||||
|
||||
check_for_update(version, self._on_update_check_result)
|
||||
self.runtime.check_for_update(version, self._on_update_check_result)
|
||||
|
||||
def _handle_settings_update(self, settings_manager, key):
|
||||
self._handle_state_update(self.state_manager, None)
|
||||
|
|
@ -82,19 +81,19 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
if self.settings.get_boolean('debug-no-device'):
|
||||
self.main_content.append(self.connected_device)
|
||||
self.connected_device.set_device_name('Fake device')
|
||||
elif not self._skip_verification and not verify_installation():
|
||||
elif not self._skip_verification and not self.runtime.verify():
|
||||
self.main_content.append(self.failed_verification)
|
||||
elif not ExtensionsManager.get_instance().is_installed():
|
||||
elif not self.runtime.is_installed():
|
||||
self.main_content.append(self.no_extension)
|
||||
elif not self.state_manager.driver_running:
|
||||
self.main_content.append(self.no_driver)
|
||||
elif not self.state_manager.license_present:
|
||||
self.main_content.append(self.no_license)
|
||||
elif not state_manager.connected_device_name:
|
||||
elif not state_manager.connected_device_name and self.runtime.shows_no_device_view:
|
||||
self.main_content.append(self.no_device)
|
||||
else:
|
||||
self.main_content.append(self.connected_device)
|
||||
self.connected_device.set_device_name(state_manager.connected_device_name)
|
||||
self.connected_device.set_device_name(state_manager.connected_device_name or self.runtime.no_device_label())
|
||||
|
||||
self.set_resizable(True)
|
||||
self.set_default_size(1, 1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue