Merge branch 'main' into gnome-44-max

This commit is contained in:
wheaney 2024-10-10 12:45:42 -07:00
commit c6e4656492
62 changed files with 1861 additions and 387 deletions

1
VERSION Normal file
View File

@ -0,0 +1 @@
1.0.6

View File

@ -1,18 +1,10 @@
diff --git a/gnome-44-max/bin/setup b/gnome-44-max/bin/setup
index 7ee291a..3bd001b 100755
index e34efb5..ee5e694 100755
--- a/gnome-44-max/bin/setup
+++ b/gnome-44-max/bin/setup
@@ -11,6 +11,7 @@ check_command() {
check_command "flatpak"
check_command "gnome-extensions"
+check_command "glib-compile-schemas"
# This script gets packaged with the release and should do the bulk of the setup work. This allows this setup to be tied
# to a specific release of the code, and guarantees it will never run along-side newer or older binaries.
@@ -78,8 +79,10 @@ echo "Copying the manifest file to ${DATA_DIR}"
mkdir -p $DATA_DIR
cp manifest $DATA_DIR
@@ -79,8 +79,10 @@ echo "Copying the manifest file to ${BREEZY_GNOME_DATA_DIR}"
mkdir -p $BREEZY_GNOME_DATA_DIR
cp manifest $BREEZY_GNOME_DATA_DIR
-echo "Installing the breezydesktop@xronlinux.com GNOME extension"
-gnome-extensions install --force breezydesktop@xronlinux.com.shell-extension.zip
@ -21,8 +13,8 @@ index 7ee291a..3bd001b 100755
+gnome-extensions install --force "$EXTENSION_UUID.shell-extension.zip"
+glib-compile-schemas "$GNOME_SHELL_DATA_DIR/extensions/$EXTENSION_UUID/schemas"
echo "Installing the Breezy Desktop UI Flatpak (this may take a couple minutes the first time)"
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
echo "Installing the Breezy Desktop UI application"
cp -r breezy_ui/data/* $XDG_DATA_HOME
diff --git a/gnome-44-max/src/cursor.js b/gnome-44-max/src/cursor.js
index 36ad7ee..41102a0 100644
--- a/gnome-44-max/src/cursor.js
@ -78,7 +70,7 @@ index 44b3f5f..fa65a4a 100644
}
diff --git a/gnome-44-max/src/extension.js b/gnome-44-max/src/extension.js
index cd17162..435154d 100644
index 5f62bfd..d06f91c 100644
--- a/gnome-44-max/src/extension.js
+++ b/gnome-44-max/src/extension.js
@@ -1,19 +1,21 @@
@ -135,7 +127,7 @@ index cd17162..435154d 100644
// Set/destroyed by enable/disable
this._cursor_manager = null;
@@ -593,6 +594,6 @@ export default class BreezyDesktopExtension extends Extension {
@@ -619,6 +620,6 @@ export default class BreezyDesktopExtension extends Extension {
}
}
@ -264,7 +256,7 @@ index 497274e..6c98cdb 100644
}
\ No newline at end of file
diff --git a/gnome-44-max/src/metadata.json b/gnome-44-max/src/metadata.json
index b9b5ebf..c888f94 100644
index 125954e..c888f94 100644
--- a/gnome-44-max/src/metadata.json
+++ b/gnome-44-max/src/metadata.json
@@ -5,7 +5,7 @@
@ -353,7 +345,7 @@ index 7883b9b..5478d2a 100644
}
\ No newline at end of file
diff --git a/gnome-44-max/src/xrEffect.js b/gnome-44-max/src/xrEffect.js
index aab805c..922ec0f 100644
index 6b1421b..7d36c46 100644
--- a/gnome-44-max/src/xrEffect.js
+++ b/gnome-44-max/src/xrEffect.js
@@ -1,13 +1,15 @@
@ -378,7 +370,7 @@ index aab805c..922ec0f 100644
+const Me = ExtensionUtils.getCurrentExtension();
+
+const Globals = Me.imports.globals;
+const {
+const {
dataViewEnd,
dataViewUint8,
dataViewBigUint,
@ -400,7 +392,7 @@ index aab805c..922ec0f 100644
// the driver should be using the same data layout version
const DATA_LAYOUT_VERSION = 3;
@@ -221,7 +223,7 @@ function checkParityByte(dataView) {
@@ -232,7 +234,7 @@ function checkParityByte(dataView) {
return parityByte === parity;
}
@ -409,7 +401,7 @@ index aab805c..922ec0f 100644
Properties: {
'supported-device-detected': GObject.ParamSpec.boolean(
'supported-device-detected',
@@ -361,8 +363,13 @@ export const XREffect = GObject.registerClass({
@@ -372,8 +374,13 @@ export const XREffect = GObject.registerClass({
if (!this._initialized) {
this.set_uniform_float(this.get_uniform_location('screenTexture'), 1, [0]);

View File

@ -14,6 +14,7 @@ const Globals = Me.imports.globals;
const { CursorManager } = Me.imports.cursormanager;
const { Logger } = Me.imports.logger;
const { MonitorManager } = Me.imports.monitormanager;
const { SystemBackground } = Me.imports.systembackground;
const { isValidKeepAlive } = Me.imports.time;
const { IPC_FILE_PATH, XREffect } = Me.imports.xrEffect;
@ -251,24 +252,25 @@ class BreezyDesktopExtension {
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, refreshRate);
this._cursor_manager.enable();
this._overlay = new St.Bin();
this._overlay.opacity = 255;
const overlayContent = new Clutter.Actor({clip_to_allocation: true});
this._overlay = new St.Bin({
child: overlayContent
});
this._overlay.set_position(targetMonitor.x, targetMonitor.y);
this._overlay.set_size(targetMonitor.width, targetMonitor.height);
Globals.logger.log_debug(`BreezyDesktopExtension _effect_enable overlay size: \
${targetMonitor.width}x${targetMonitor.height} at ${targetMonitor.x},${targetMonitor.y}`);
const overlayContent = new Clutter.Actor({clip_to_allocation: true});
global.stage.add_child(this._overlay);
Shell.util_set_hidden_from_pick(this._overlay, true);
this._background = new SystemBackground();
overlayContent.add_child(this._background);
const uiClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup, clip_to_allocation: true });
uiClone.x = -targetMonitor.x;
uiClone.y = -targetMonitor.y;
overlayContent.add_child(uiClone);
this._overlay.set_child(overlayContent);
Shell.util_set_hidden_from_pick(this._overlay, true);
global.stage.add_child(this._overlay);
// In GS 45, use of "actor" was renamed to "child".
const clutterContainer = Clutter.Container !== undefined;
this._actor_added_connection = global.stage.connect(

View File

@ -283,7 +283,6 @@ var MonitorManager = GObject.registerClass({
this._monitorsChangedConnection = null;
this._displayConfigProxy = null;
this._backendManager = null;
this._monitorProperties = null;
this._changeHookFn = null;
this._needsConfigCheck = this.use_optimal_monitor_config;
@ -295,7 +294,6 @@ var MonitorManager = GObject.registerClass({
enable() {
Globals.logger.log_debug('MonitorManager enable');
this._backendManager = global.backend.get_monitor_manager();
newDisplayConfig(this.extension_path, ((proxy, error) => {
if (error) {
return;
@ -313,7 +311,6 @@ var MonitorManager = GObject.registerClass({
this._monitorsChangedConnection = null;
this._displayConfigProxy = null;
this._backendManager = null;
this._monitorProperties = null;
this._changeHookFn = null;
}
@ -405,7 +402,7 @@ var MonitorManager = GObject.registerClass({
const monitorProperties = [];
for (let i = 0; i < result.length; i++) {
const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i];
const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName);
const monitorIndex = global.backend.get_monitor_manager().get_monitor_for_connector(connectorName);
Globals.logger.log_debug(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`);
if (monitorIndex >= 0) {
monitorProperties[monitorIndex] = {

View File

@ -0,0 +1,31 @@
const Cogl = imports.gi.Cogl;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Meta = imports.gi.Meta;
const DEFAULT_BACKGROUND_COLOR = new Cogl.Color({red: 40, green: 40, blue: 40, alpha: 255});
let _systemBackground;
var SystemBackground = GObject.registerClass({
Signals: {'loaded': {}},
}, class SystemBackground extends Meta.BackgroundActor {
_init() {
if (_systemBackground == null) {
_systemBackground = new Meta.Background({meta_display: global.display});
_systemBackground.set_color(DEFAULT_BACKGROUND_COLOR);
}
super._init({
meta_display: global.display,
monitor: 0,
});
this.content.background = _systemBackground;
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.emit('loaded');
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] SystemBackground.loaded');
}
});

View File

@ -326,8 +326,11 @@ var XREffect = GObject.registerClass({
constructor(params = {}) {
super(params);
this._is_display_distance_at_end = false;
this._distance_ease_timeline = null;
this.connect('notify::toggle-display-distance-start', this._handle_display_distance_properties_change.bind(this));
this.connect('notify::toggle-display-distance-end', this._handle_display_distance_properties_change.bind(this));
this.connect('notify::display-distance', this._handle_display_distance_properties_change.bind(this));
this._handle_display_distance_properties_change();
const calibrating = GdkPixbuf.Pixbuf.new_from_file(`${Globals.extension_dir}/textures/calibrating.png`);
this.calibratingImage = new Clutter.Image();
@ -340,6 +343,12 @@ var XREffect = GObject.registerClass({
customBanner.width, customBanner.height, customBanner.rowstride);
}
_handle_display_distance_properties_change() {
const distance_from_end = Math.abs(this.display_distance - this.toggle_display_distance_end);
const distance_from_start = Math.abs(this.display_distance - this.toggle_display_distance_start);
this._is_display_distance_at_end = distance_from_end < distance_from_start;
}
_change_distance() {
if (this._distance_ease_timeline?.is_playing()) this._distance_ease_timeline.stop();
@ -353,7 +362,6 @@ var XREffect = GObject.registerClass({
this._distance_ease_timeline.get_progress() *
(toggle_display_distance_target - this._distance_ease_start);
});
this._is_display_distance_at_end = !this._is_display_distance_at_end;
this._distance_ease_timeline.start();
}

@ -1 +1 @@
Subproject commit 5a7ddc2c18df268476dd123b9af84091e3bf49bb
Subproject commit d270ebfd2e3202133fea75e1513f1571960bdafd

38
ui/bin/dev/use_local_ui.sh Executable file
View File

@ -0,0 +1,38 @@
USER_HOME=$(realpath ~)
ARCH=$(uname -m)
# https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
if [ -z "$XDG_BIN_HOME" ]; then
XDG_BIN_HOME="$USER_HOME/.local/bin"
fi
# create temp directory
tmp_dir=$(mktemp -d -t breezy-gnome-XXXXXXXXXX)
pushd $tmp_dir > /dev/null
echo "Created temp directory: ${tmp_dir}"
echo "Extracting to: ${tmp_dir}/breezy_ui"
tar -xf $SCRIPT_DIR/../../out/breezyUI-${ARCH}.tar.gz
echo "Installing the Breezy Desktop UI application"
cp -r breezy_ui/data/* $XDG_DATA_HOME
cp -r breezy_ui/bin/* $XDG_BIN_HOME
# update copied files to use the local XDG paths
ESCAPED_XDG_DATA_HOME=$(printf '%s\n' "$XDG_DATA_HOME" | sed -e 's/[\/&]/\\&/g')
sed -i -e "s/\/usr\/local\/share/$ESCAPED_XDG_DATA_HOME/g" $XDG_BIN_HOME/breezydesktop
sed -i "/Exec/c\Exec=$XDG_BIN_HOME/breezydesktop" $XDG_DATA_HOME/applications/com.xronlinux.BreezyDesktop.desktop
glib-compile-schemas $XDG_DATA_HOME/glib-2.0/schemas
update-desktop-database $XDG_DATA_HOME/applications
gtk-update-icon-cache
popd > /dev/null
rm -rf $tmp_dir

View File

@ -37,6 +37,7 @@ mkdir -p $PACKAGE_APPS_DIR
mkdir -p $PACKAGE_SCHEMAS_DIR
cp src/*.py $PACKAGE_BREEZY_SRC_DIR
cp -r lib $PACKAGE_BREEZY_SRC_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

View File

@ -0,0 +1,115 @@
pydbus
======
.. image:: https://travis-ci.org/LEW21/pydbus.svg?branch=master
:target: https://travis-ci.org/LEW21/pydbus
.. image:: https://badge.fury.io/py/pydbus.svg
:target: https://badge.fury.io/py/pydbus
Pythonic DBus library.
Changelog: https://github.com/LEW21/pydbus/releases
Requirements
------------
* Python 2.7+ - but works best on 3.4+ (help system is nicer there)
* PyGI_ (not packaged on pypi, you need to install it from your distribution's repository - it's usually called python-gi, python-gobject or pygobject)
* GLib_ 2.46+ and girepository_ 1.46+ (Ubuntu 16.04+) - for object publication support
.. _PyGI: https://wiki.gnome.org/Projects/PyGObject
.. _GLib: https://developer.gnome.org/glib/
.. _girepository: https://wiki.gnome.org/Projects/GObjectIntrospection
Examples
--------
Send a desktop notification
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SessionBus
bus = SessionBus()
notifications = bus.get('.Notifications')
notifications.Notify('test', 0, 'dialog-information', "Hello World!", "pydbus works :)", [], {}, 5000)
List systemd units
~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
bus = SystemBus()
systemd = bus.get(".systemd1")
for unit in systemd.ListUnits():
print(unit)
Start or stop systemd unit
~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
bus = SystemBus()
systemd = bus.get(".systemd1")
job1 = systemd.StopUnit("ssh.service", "fail")
job2 = systemd.StartUnit("ssh.service", "fail")
Watch for new systemd jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
from gi.repository import GLib
bus = SystemBus()
systemd = bus.get(".systemd1")
systemd.JobNew.connect(print)
GLib.MainLoop().run()
# or
systemd.onJobNew = print
GLib.MainLoop().run()
View object's API
~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SessionBus
bus = SessionBus()
notifications = bus.get('.Notifications')
help(notifications)
More examples & documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Tutorial_ contains more examples and docs.
.. _Tutorial: https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst
Copyright Information
---------------------
Copyright (C) 2014, 2015, 2016 Linus Lewandowski <linus@lew21.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,137 @@
Metadata-Version: 2.0
Name: pydbus
Version: 0.6.0
Summary: Pythonic DBus library
Home-page: https://github.com/LEW21/pydbus
Author: Linus Lewandowski
Author-email: linus@lew21.net
License: LGPLv2+
Keywords: dbus
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
pydbus
======
.. image:: https://travis-ci.org/LEW21/pydbus.svg?branch=master
:target: https://travis-ci.org/LEW21/pydbus
.. image:: https://badge.fury.io/py/pydbus.svg
:target: https://badge.fury.io/py/pydbus
Pythonic DBus library.
Changelog: https://github.com/LEW21/pydbus/releases
Requirements
------------
* Python 2.7+ - but works best on 3.4+ (help system is nicer there)
* PyGI_ (not packaged on pypi, you need to install it from your distribution's repository - it's usually called python-gi, python-gobject or pygobject)
* GLib_ 2.46+ and girepository_ 1.46+ (Ubuntu 16.04+) - for object publication support
.. _PyGI: https://wiki.gnome.org/Projects/PyGObject
.. _GLib: https://developer.gnome.org/glib/
.. _girepository: https://wiki.gnome.org/Projects/GObjectIntrospection
Examples
--------
Send a desktop notification
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SessionBus
bus = SessionBus()
notifications = bus.get('.Notifications')
notifications.Notify('test', 0, 'dialog-information', "Hello World!", "pydbus works :)", [], {}, 5000)
List systemd units
~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
bus = SystemBus()
systemd = bus.get(".systemd1")
for unit in systemd.ListUnits():
print(unit)
Start or stop systemd unit
~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
bus = SystemBus()
systemd = bus.get(".systemd1")
job1 = systemd.StopUnit("ssh.service", "fail")
job2 = systemd.StartUnit("ssh.service", "fail")
Watch for new systemd jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SystemBus
from gi.repository import GLib
bus = SystemBus()
systemd = bus.get(".systemd1")
systemd.JobNew.connect(print)
GLib.MainLoop().run()
# or
systemd.onJobNew = print
GLib.MainLoop().run()
View object's API
~~~~~~~~~~~~~~~~~
.. code-block:: python
from pydbus import SessionBus
bus = SessionBus()
notifications = bus.get('.Notifications')
help(notifications)
More examples & documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Tutorial_ contains more examples and docs.
.. _Tutorial: https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst
Copyright Information
---------------------
Copyright (C) 2014, 2015, 2016 Linus Lewandowski <linus@lew21.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

View File

@ -0,0 +1,45 @@
pydbus-0.6.0.dist-info/DESCRIPTION.rst,sha256=pKEYrpPtLiD4ksnqzDs6ZepA9c3uoy5Mjvuxoi1_pEU,3033
pydbus-0.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pydbus-0.6.0.dist-info/METADATA,sha256=JKSoIIJKdCLkuWiXdKtv4bSyMt9rA7BM3_RJbxqk5y4,3838
pydbus-0.6.0.dist-info/RECORD,,
pydbus-0.6.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pydbus-0.6.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
pydbus-0.6.0.dist-info/metadata.json,sha256=fouQjd0oTDJrVRvQO8dwqvMZbNib8Ui4TgArvHoxzmQ,916
pydbus-0.6.0.dist-info/top_level.txt,sha256=X1ybDik1ZA7yilKlt-MUQCPJBNlAW9Jp6bHoAYgAgN0,7
pydbus-0.6.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
pydbus/__init__.py,sha256=5ouUtYT15bJ-LaEFea3ZccXkhlZHyf1ykwKLNnFMlIs,148
pydbus/__pycache__/__init__.cpython-312.pyc,,
pydbus/__pycache__/_inspect3.cpython-312.pyc,,
pydbus/__pycache__/auto_names.cpython-312.pyc,,
pydbus/__pycache__/bus.cpython-312.pyc,,
pydbus/__pycache__/bus_names.cpython-312.pyc,,
pydbus/__pycache__/exitable.cpython-312.pyc,,
pydbus/__pycache__/generic.cpython-312.pyc,,
pydbus/__pycache__/identifier.cpython-312.pyc,,
pydbus/__pycache__/method_call_context.cpython-312.pyc,,
pydbus/__pycache__/proxy.cpython-312.pyc,,
pydbus/__pycache__/proxy_method.cpython-312.pyc,,
pydbus/__pycache__/proxy_property.cpython-312.pyc,,
pydbus/__pycache__/proxy_signal.cpython-312.pyc,,
pydbus/__pycache__/publication.cpython-312.pyc,,
pydbus/__pycache__/registration.cpython-312.pyc,,
pydbus/__pycache__/request_name.cpython-312.pyc,,
pydbus/__pycache__/subscription.cpython-312.pyc,,
pydbus/__pycache__/timeout.cpython-312.pyc,,
pydbus/_inspect3.py,sha256=lDzzSThwvbMQ3PwY-SVpWByu___QPhbSvPY0xwQScc8,708
pydbus/auto_names.py,sha256=elJqIT2dIhioBYUMvIqtH6Pt_tFCf29QSzp0QXbndy8,519
pydbus/bus.py,sha256=dMJAqRq85TJAz8iDZSg8COhjy8sabrofZ2jjSe6Pxds,1565
pydbus/bus_names.py,sha256=vdkfHdQy_9FzMWkJaXJZpDpvktV9N2TFyRzLE8X1cDI,3533
pydbus/exitable.py,sha256=FblBVEUjz6DyqgJpOVlE8-TMeVaC1ZPf9wyi7ejQ-gI,982
pydbus/generic.py,sha256=2K9VNwu-TkMWws818H2BiyLsoX99fZuJgbmoNtVLzjE,2589
pydbus/identifier.py,sha256=iVrw6rDMyNiQouNUAMpvEhGCz1Bog-R0HwzmvsHs0LY,383
pydbus/method_call_context.py,sha256=ilIh0jJmmdoJX26UF1uV9eYvdQXXgYoyxzNHKvIJcJA,1039
pydbus/proxy.py,sha256=Og9VbKJ4sZvr4dI7cBwVKsklWAqfnEmAlSpoaPJIs-g,4203
pydbus/proxy_method.py,sha256=I741zD_vBixhpeKArTVLaUDTQsZGJ98xaX2SGORJkb0,3089
pydbus/proxy_property.py,sha256=WUkOV4V8DuvmN1-yAD54IJklZqgJex2eTV2KZ20PJ1k,1066
pydbus/proxy_signal.py,sha256=D60OxO635erbIknziTzX3QDChhImmjzgUpwjVe8gy6A,2112
pydbus/publication.py,sha256=oJKPraVVv0YPLWN2-y0KjjjW1Lu4NNTg9lNoVneYLlY,1391
pydbus/registration.py,sha256=GWBv2lDDLrkYCr9WsaEFS1hZstuXB7OgHdEnvC0Shc4,5565
pydbus/request_name.py,sha256=jpWNd8H8rlRdJQiNAud-di1jU6gE31JvLOtoXQm_lHE,883
pydbus/subscription.py,sha256=MBXKRIvLvSgYLIT7A0a3Qn6aAvT6Rcx1b6xudTsr7mQ,2166
pydbus/timeout.py,sha256=G4o9dVwN7hb4MaPUHlcGpQKtlUSaTGjBTBC7nuza9mI,303

View File

View File

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.29.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@ -0,0 +1 @@
{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7"], "extensions": {"python.details": {"contacts": [{"email": "linus@lew21.net", "name": "Linus Lewandowski", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/LEW21/pydbus"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["dbus"], "license": "LGPLv2+", "metadata_version": "2.0", "name": "pydbus", "summary": "Pythonic DBus library", "version": "0.6.0"}

View File

@ -0,0 +1 @@
pydbus

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,4 @@
from .bus import SystemBus, SessionBus, connect
from gi.repository.GLib import Variant
__all__ = ["SystemBus", "SessionBus", "connect", "Variant"]

View File

@ -0,0 +1,28 @@
from collections import OrderedDict
from inspect import getargspec
class _empty:
pass
class Signature:
empty = _empty
def __init__(self, parameters=None, return_annotation=_empty):
self.parameters = OrderedDict(((param.name, param) for param in parameters))
self.return_annotation = return_annotation
class Parameter:
empty = _empty
POSITIONAL_ONLY = 0
POSITIONAL_OR_KEYWORD = 1
KEYWORD_ONLY = 999
def __init__(self, name, kind, default=_empty, annotation=_empty):
self.name = name
self.kind = kind
self.annotation = annotation
def signature(f):
parameters = [Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD) for arg in getargspec(f).args]
return Signature(parameters = parameters)

View File

@ -0,0 +1,21 @@
from gi.repository import Gio
def auto_bus_name(bus_name):
if bus_name[0] == ".":
#Default namespace
bus_name = "org.freedesktop" + bus_name
if not Gio.dbus_is_name(bus_name):
raise ValueError("invalid bus name")
return bus_name
def auto_object_path(bus_name, object_path=None):
if object_path is None:
# They always name it like that.
object_path = "/" + bus_name.replace(".", "/")
if object_path[0] != "/":
object_path = "/" + bus_name.replace(".", "/") + "/" + object_path
return object_path

60
ui/lib/pydbus/bus.py Normal file
View File

@ -0,0 +1,60 @@
from gi.repository import Gio
from .proxy import ProxyMixin
from .request_name import RequestNameMixin
from .bus_names import OwnMixin, WatchMixin
from .subscription import SubscriptionMixin
from .registration import RegistrationMixin
from .publication import PublicationMixin
def pydbus_property(self):
try:
return self._pydbus
except AttributeError:
self._pydbus = Bus(self)
return self._pydbus
Gio.DBusConnection.pydbus = property(pydbus_property)
def bus_get(type):
return Gio.bus_get_sync(type, None).pydbus
def connect(address):
c = Gio.DBusConnection.new_for_address_sync(address, Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None)
c.pydbus.autoclose = True
return c.pydbus
class Bus(ProxyMixin, RequestNameMixin, OwnMixin, WatchMixin, SubscriptionMixin, RegistrationMixin, PublicationMixin):
Type = Gio.BusType
def __init__(self, gio_con):
self.con = gio_con
self.autoclose = False
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.autoclose:
self.con.close_sync(None)
@property
def dbus(self):
try:
return self._dbus
except AttributeError:
self._dbus = self.get(".DBus")[""]
return self._dbus
@property
def polkit_authority(self):
try:
return self._polkit_authority
except AttributeError:
self._polkit_authority = self.get(".PolicyKit1", "Authority")[""]
return self._polkit_authority
def SystemBus():
return bus_get(Bus.Type.SYSTEM)
def SessionBus():
return bus_get(Bus.Type.SESSION)

View File

@ -0,0 +1,97 @@
from gi.repository import Gio
from .exitable import ExitableWithAliases
import warnings
class NameOwner(ExitableWithAliases("unown")):
Flags = Gio.BusNameOwnerFlags
__slots__ = ()
def __init__(self, con, name, flags, name_aquired_handler, name_lost_handler):
id = Gio.bus_own_name_on_connection(con, name, flags, name_aquired_handler, name_lost_handler)
self._at_exit(lambda: Gio.bus_unown_name(id))
class NameWatcher(ExitableWithAliases("unwatch")):
Flags = Gio.BusNameWatcherFlags
__slots__ = ()
def __init__(self, con, name, flags, name_appeared_handler, name_vanished_handler):
id = Gio.bus_watch_name_on_connection(con, name, flags, name_appeared_handler, name_vanished_handler)
self._at_exit(lambda: Gio.bus_unwatch_name(id))
class OwnMixin(object):
__slots__ = ()
NameOwnerFlags = NameOwner.Flags
def own_name(self, name, flags=0, name_aquired=None, name_lost=None):
"""[DEPRECATED] Asynchronously aquires a bus name.
Starts acquiring name on the bus specified by bus_type and calls
name_acquired and name_lost when the name is acquired respectively lost.
To receive name_aquired and name_lost callbacks, you need an event loop.
https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst#setting-up-an-event-loop
Parameters
----------
name : string
Bus name to aquire
flags : NameOwnerFlags, optional
name_aquired : callable, optional
Invoked when name is acquired
name_lost : callable, optional
Invoked when name is lost
Returns
-------
NameOwner
An object you can use as a context manager to unown the name later.
See Also
--------
See https://developer.gnome.org/gio/2.44/gio-Owning-Bus-Names.html#g-bus-own-name
for more information.
"""
warnings.warn("own_name() is deprecated, use request_name() instead.", DeprecationWarning)
name_aquired_handler = (lambda con, name: name_aquired()) if name_aquired is not None else None
name_lost_handler = (lambda con, name: name_lost()) if name_lost is not None else None
return NameOwner(self.con, name, flags, name_aquired_handler, name_lost_handler)
class WatchMixin(object):
__slots__ = ()
NameWatcherFlags = NameWatcher.Flags
def watch_name(self, name, flags=0, name_appeared=None, name_vanished=None):
"""Asynchronously watches a bus name.
Starts watching name on the bus specified by bus_type and calls
name_appeared and name_vanished when the name is known to have a owner
respectively known to lose its owner.
To receive name_appeared and name_vanished callbacks, you need an event loop.
https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst#setting-up-an-event-loop
Parameters
----------
name : string
Bus name to watch
flags : NameWatcherFlags, optional
name_appeared : callable, optional
Invoked when name is known to exist
Called as name_appeared(name_owner).
name_vanished : callable, optional
Invoked when name is known to not exist
Returns
-------
NameWatcher
An object you can use as a context manager to unwatch the name later.
See Also
--------
See https://developer.gnome.org/gio/2.44/gio-Watching-Bus-Names.html#g-bus-watch-name
for more information.
"""
name_appeared_handler = (lambda con, name, name_owner: name_appeared(name_owner)) if name_appeared is not None else None
name_vanished_handler = (lambda con, name: name_vanished()) if name_vanished is not None else None
return NameWatcher(self.con, name, flags, name_appeared_handler, name_vanished_handler)

52
ui/lib/pydbus/exitable.py Normal file
View File

@ -0,0 +1,52 @@
import inspect
class Exitable(object):
__slots__ = ("_at_exit_cbs")
def _at_exit(self, cb):
try:
self._at_exit_cbs
except AttributeError:
self._at_exit_cbs = []
self._at_exit_cbs.append(cb)
def __enter__(self):
return self
def __exit__(self, exc_type = None, exc_value = None, traceback = None):
if self._exited:
return
for cb in reversed(self._at_exit_cbs):
call_with_exc = True
try:
inspect.getcallargs(cb, exc_type, exc_value, traceback)
except TypeError:
call_with_exc = False
if call_with_exc:
cb(exc_type, exc_value, traceback)
else:
cb()
self._at_exit_cbs = None
@property
def _exited(self):
try:
return self._at_exit_cbs is None
except AttributeError:
return True
def ExitableWithAliases(*exit_methods):
class CustomExitable(Exitable):
pass
def exit(self):
self.__exit__()
for exit_method_name in exit_methods:
setattr(CustomExitable, exit_method_name, exit)
return CustomExitable

105
ui/lib/pydbus/generic.py Normal file
View File

@ -0,0 +1,105 @@
"""Generic programming utilities.
Utilities implemented in this file are not dependent
on dbus, they can be used everywhere.
"""
class subscription(object):
__slots__ = ("callback_list", "callback")
def __init__(self, callback_list, callback):
self.callback_list = callback_list
self.callback = callback
self.callback_list.append(callback)
def unsubscribe(self):
self.callback_list.remove(self.callback)
self.callback_list = None
self.callback = None
def disconnect(self):
"""An alias for unsubscribe()"""
self.unsubscribe()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if not self.callback is None:
self.unsubscribe()
class bound_signal(object):
__slots__ = ("__signal__", "__self__") # bound method uses ("__func__", "__self__")
def __init__(self, signal, instance):
self.__signal__ = signal
self.__self__ = instance
@property
def callbacks(self):
return self.__signal__.map[self.__self__]
def connect(self, callback):
"""Subscribe to the signal."""
return self.__signal__.connect(self.__self__, callback)
def emit(self, *args):
"""Emit the signal."""
self.__signal__.emit(self.__self__, *args)
def __call__(self, *args):
"""Emit the signal."""
self.emit(*args)
def __repr__(self):
return "<bound signal " + self.__signal__.__qualname__ + " of " + repr(self.__self__) + ">"
class signal(object):
"""Static signal object
You're expected to set it as a class property::
class A:
SomethingHappened = signal()
Declared this way, it can be used on class instances
to connect signal observers::
a = A()
a.SomethingHappened.connect(func)
and emit the signal::
a.SomethingHappened()
You may pass any parameters to the emiting function
- they will be forwarded to all subscribed callbacks.
"""
def __init__(self):
self.map = {}
self.__qualname__ = "<anonymous signal>" # function uses <lambda> ;)
self.__doc__ = "Signal."
def connect(self, object, callback):
"""Subscribe to the signal."""
return subscription(self.map.setdefault(object, []), callback)
def emit(self, object, *args):
"""Emit the signal."""
for cb in self.map.get(object, []):
cb(*args)
def __get__(self, instance, owner):
if instance is None:
return self
return bound_signal(self, instance)
def __set__(self, instance, value):
raise AttributeError("can't set attribute")
def __repr__(self):
return "<signal " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"
bound_method = type(signal().emit) # TODO find a prettier way to get this type

View File

@ -0,0 +1,22 @@
try:
isident = str.isidentifier
except:
import re
isidentre = re.compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
def isident(s):
return isidentre.match(s) is not None
def filter_identifier(name):
name = name.replace("-", "_")
safe_name = ""
for c in name:
if not safe_name:
if isident(c):
safe_name += c
else:
if isident("a" + c):
safe_name += c
return safe_name

View File

@ -0,0 +1,34 @@
from gi.repository import GLib
from collections import namedtuple
AuthorizationResult = namedtuple("AuthorizationResult", "is_authorized is_challenge details")
class MethodCallContext(object):
def __init__(self, gdbus_method_invocation):
self._mi = gdbus_method_invocation
@property
def bus(self):
return self._mi.get_connection().pydbus
@property
def sender(self):
return self._mi.get_sender()
@property
def object_path(self):
return self._mi.get_object_path()
@property
def interface_name(self):
return self._mi.get_interface_name()
@property
def method_name(self):
return self._mi.get_method_name()
def check_authorization(self, action_id, details, interactive=False):
return AuthorizationResult(*self.bus.polkit_authority.CheckAuthorization(('system-bus-name', {'name': GLib.Variant("s", self.sender)}), action_id, details, 1 if interactive else 0, ''))
def is_authorized(self, action_id, details, interactive=False):
return self.check_authorization(action_id, details, interactive).is_authorized

123
ui/lib/pydbus/proxy.py Normal file
View File

@ -0,0 +1,123 @@
from gi.repository import GLib
from xml.etree import ElementTree as ET
from .auto_names import *
from .proxy_method import ProxyMethod
from .proxy_property import ProxyProperty
from .proxy_signal import ProxySignal, OnSignal
from .timeout import timeout_to_glib
class ProxyMixin(object):
__slots__ = ()
def get(self, bus_name, object_path=None, **kwargs):
"""Get a remote object.
Parameters
----------
bus_name : string
Name of the service that exposes this object.
You may start with "." - then org.freedesktop will be automatically prepended.
object_path : string, optional
Path of the object. If not provided, bus_name translated to path format is used.
Returns
-------
ProxyObject implementing all the Interfaces exposed by the remote object.
Note that it inherits from multiple Interfaces, so the method you want to use
may be shadowed by another one, eg. from a newer version of the interface.
Therefore, to interact with only a single interface, use:
>>> bus.get("org.freedesktop.systemd1")["org.freedesktop.systemd1.Manager"]
or simply
>>> bus.get(".systemd1")[".Manager"]
which will give you access to the one specific interface.
"""
# Python 2 sux
for kwarg in kwargs:
if kwarg not in ("timeout",):
raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
timeout = kwargs.get("timeout", None)
bus_name = auto_bus_name(bus_name)
object_path = auto_object_path(bus_name, object_path)
ret = self.con.call_sync(
bus_name, object_path,
'org.freedesktop.DBus.Introspectable', "Introspect", None, GLib.VariantType.new("(s)"),
0, timeout_to_glib(timeout), None)
if not ret:
raise KeyError("no such object; you might need to pass object path as the 2nd argument for get()")
xml, = ret.unpack()
try:
introspection = ET.fromstring(xml)
except:
raise KeyError("object provides invalid introspection XML")
return CompositeInterface(introspection)(self, bus_name, object_path)
class ProxyObject(object):
def __init__(self, bus, bus_name, path, object=None):
self._bus = bus
self._bus_name = bus_name
self._path = path
self._object = object if object else self
def Interface(iface):
class interface(ProxyObject):
@staticmethod
def _Introspect():
print(iface.attrib["name"] + ":")
for member in iface:
print("\t" + member.tag + " " + member.attrib["name"])
print()
interface.__qualname__ = interface.__name__ = iface.attrib["name"]
interface.__module__ = "DBUS"
for member in iface:
member_name = member.attrib["name"]
if member.tag == "method":
setattr(interface, member_name, ProxyMethod(interface.__name__, member))
elif member.tag == "property":
setattr(interface, member_name, ProxyProperty(interface.__name__, member))
elif member.tag == "signal":
signal = ProxySignal(interface.__name__, member)
setattr(interface, member_name, signal)
setattr(interface, "on" + member_name, OnSignal(signal))
return interface
def CompositeInterface(introspection):
class CompositeObject(ProxyObject):
def __getitem__(self, iface):
if iface == "" or iface[0] == ".":
iface = self._path.replace("/", ".")[1:] + iface
matching_bases = [base for base in type(self).__bases__ if base.__name__ == iface]
if len(matching_bases) == 0:
raise KeyError(iface)
assert(len(matching_bases) == 1)
iface_class = matching_bases[0]
return iface_class(self._bus, self._bus_name, self._path, self)
@classmethod
def _Introspect(cls):
for iface in cls.__bases__:
try:
iface._Introspect()
except:
pass
ifaces = sorted([x for x in introspection if x.tag == "interface"], key=lambda x: int(x.attrib["name"].startswith("org.freedesktop.DBus.")))
if not ifaces:
raise KeyError("object does not export any interfaces; you might need to pass object path as the 2nd argument for get()")
CompositeObject.__bases__ = tuple(Interface(iface) for iface in ifaces)
CompositeObject.__name__ = "<CompositeObject>"
CompositeObject.__qualname__ = "<CompositeObject>(" + "+".join(x.__name__ for x in CompositeObject.__bases__) + ")"
CompositeObject.__module__ = "DBUS"
return CompositeObject

View File

@ -0,0 +1,91 @@
from gi.repository import GLib
from .generic import bound_method
from .identifier import filter_identifier
from .timeout import timeout_to_glib
try:
from inspect import Signature, Parameter
put_signature_in_doc = False
except:
from ._inspect3 import Signature, Parameter
put_signature_in_doc = True
class DBUSSignature(Signature):
def __str__(self):
result = []
for param in self.parameters.values():
p = param.name if not param.name.startswith("arg") else ""
if type(param.annotation) == str:
p += ":" + param.annotation
result.append(p)
rendered = '({})'.format(', '.join(result))
if self.return_annotation is not Signature.empty:
rendered += ' -> {}'.format(self.return_annotation)
return rendered
class ProxyMethod(object):
def __init__(self, iface_name, method):
self._iface_name = iface_name
self.__name__ = method.attrib["name"]
self.__qualname__ = self._iface_name + "." + self.__name__
self._inargs = [(arg.attrib.get("name", ""), arg.attrib["type"]) for arg in method if arg.tag == "arg" and arg.attrib["direction"] == "in"]
self._outargs = [arg.attrib["type"] for arg in method if arg.tag == "arg" and arg.attrib["direction"] == "out"]
self._sinargs = "(" + "".join(x[1] for x in self._inargs) + ")"
self._soutargs = "(" + "".join(self._outargs) + ")"
self_param = Parameter("self", Parameter.POSITIONAL_ONLY)
pos_params = []
for i, a in enumerate(self._inargs):
name = filter_identifier(a[0])
if not name:
name = "arg" + str(i)
param = Parameter(name, Parameter.POSITIONAL_ONLY, annotation=a[1])
pos_params.append(param)
ret_type = Signature.empty if len(self._outargs) == 0 else self._outargs[0] if len(self._outargs) == 1 else "(" + ", ".join(self._outargs) + ")"
self.__signature__ = DBUSSignature([self_param] + pos_params, return_annotation=ret_type)
if put_signature_in_doc:
self.__doc__ = self.__name__ + str(self.__signature__)
def __call__(self, instance, *args, **kwargs):
argdiff = len(args) - len(self._inargs)
if argdiff < 0:
raise TypeError(self.__qualname__ + " missing {} required positional argument(s)".format(-argdiff))
elif argdiff > 0:
raise TypeError(self.__qualname__ + " takes {} positional argument(s) but {} was/were given".format(len(self._inargs), len(args)))
# Python 2 sux
for kwarg in kwargs:
if kwarg not in ("timeout",):
raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
timeout = kwargs.get("timeout", None)
ret = instance._bus.con.call_sync(
instance._bus_name, instance._path,
self._iface_name, self.__name__, GLib.Variant(self._sinargs, args), GLib.VariantType.new(self._soutargs),
0, timeout_to_glib(timeout), None).unpack()
if len(self._outargs) == 0:
return None
elif len(self._outargs) == 1:
return ret[0]
else:
return ret
def __get__(self, instance, owner):
if instance is None:
return self
return bound_method(self, instance)
def __repr__(self):
return "<function " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"

View File

@ -0,0 +1,31 @@
from gi.repository import GLib
class ProxyProperty(object):
def __init__(self, iface_name, property):
self._iface_name = iface_name
self.__name__ = property.attrib["name"]
self.__qualname__ = self._iface_name + "." + self.__name__
self._type = property.attrib["type"]
access = property.attrib["access"]
self._readable = access.startswith("read")
self._writeable = access.endswith("write")
self.__doc__ = "(" + self._type + ") " + access
def __get__(self, instance, owner):
if instance is None:
return self
if not self._readable:
raise AttributeError("unreadable attribute")
return instance._object["org.freedesktop.DBus.Properties"].Get(self._iface_name, self.__name__)
def __set__(self, instance, value):
if instance is None or not self._writeable:
raise AttributeError("can't set attribute")
instance._object["org.freedesktop.DBus.Properties"].Set(self._iface_name, self.__name__, GLib.Variant(self._type, value))
def __repr__(self):
return "<property " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"

View File

@ -0,0 +1,66 @@
from .generic import bound_signal
class ProxySignal(object):
def __init__(self, iface_name, signal):
self._iface_name = iface_name
self.__name__ = signal.attrib["name"]
self.__qualname__ = self._iface_name + "." + self.__name__
self._args = [arg.attrib["type"] for arg in signal if arg.tag == "arg"]
self.__doc__ = "Signal. Callback: (" + ", ".join(self._args) + ")"
def connect(self, object, callback):
"""Subscribe to the signal."""
def signal_fired(sender, object, iface, signal, params):
callback(*params)
return object._bus.subscribe(sender=object._bus_name, object=object._path, iface=self._iface_name, signal=self.__name__, signal_fired=signal_fired)
def __get__(self, instance, owner):
if instance is None:
return self
return bound_signal(self, instance)
def __set__(self, instance, value):
raise AttributeError("can't set attribute")
def __repr__(self):
return "<signal " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"
class OnSignal(object):
def __init__(self, signal):
self.signal = signal
self.__name__ = "on" + signal.__name__
self.__qualname__ = signal._iface_name + "." + self.__name__
self.__doc__ = "Assign a callback to subscribe to the signal. Assing None to unsubscribe. Callback: (" + ", ".join(signal._args) + ")"
def __get__(self, instance, owner):
if instance is None:
return self
try:
return getattr(instance, "_on" + self.signal.__name__)
except AttributeError:
return None
def __set__(self, instance, value):
if instance is None:
raise AttributeError("can't set attribute")
try:
old = getattr(instance, "_sub" + self.signal.__name__)
old.unsubscribe()
except AttributeError:
pass
if value is None:
delattr(instance, "_on" + self.signal.__name__)
delattr(instance, "_sub" + self.signal.__name__)
return
sub = self.signal.connect(instance, value)
setattr(instance, "_on" + self.signal.__name__, value)
setattr(instance, "_sub" + self.signal.__name__, sub)
def __repr__(self):
return "<descriptor " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"

View File

@ -0,0 +1,42 @@
from gi.repository import Gio
from .exitable import ExitableWithAliases
from .auto_names import *
class Publication(ExitableWithAliases("unpublish")):
__slots__ = ()
def __init__(self, bus, bus_name, *objects, **kwargs): # allow_replacement=True, replace=False
# Python 2 sux
for kwarg in kwargs:
if kwarg not in ("allow_replacement", "replace",):
raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
allow_replacement = kwargs.get("allow_replacement", True)
replace = kwargs.get("replace", False)
bus_name = auto_bus_name(bus_name)
for object_info in objects:
path, object, node_info = (None, None, None)
if type(object_info) == tuple:
if len(object_info) == 3:
path, object, node_info = object_info
if len(object_info) == 2:
path, object = object_info
if len(object_info) == 1:
object = object_info[0]
else:
object = object_info
path = auto_object_path(bus_name, path)
self._at_exit(bus.register_object(path, object, node_info).__exit__)
# Request name only after registering all the objects.
self._at_exit(bus.request_name(bus_name, allow_replacement=allow_replacement, replace=replace).__exit__)
class PublicationMixin(object):
__slots__ = ()
def publish(self, bus_name, *objects):
"""Expose objects on the bus."""
return Publication(self, bus_name, *objects)

View File

@ -0,0 +1,156 @@
from __future__ import print_function
import sys, traceback
from gi.repository import GLib, Gio
from . import generic
from .exitable import ExitableWithAliases
from functools import partial
from .method_call_context import MethodCallContext
import logging
try:
from inspect import signature, Parameter
except:
from ._inspect3 import signature, Parameter
class ObjectWrapper(ExitableWithAliases("unwrap")):
__slots__ = ["object", "outargs", "readable_properties", "writable_properties"]
def __init__(self, object, interfaces):
self.object = object
self.outargs = {}
for iface in interfaces:
for method in iface.methods:
self.outargs[iface.name + "." + method.name] = [arg.signature for arg in method.out_args]
self.readable_properties = {}
self.writable_properties = {}
for iface in interfaces:
for prop in iface.properties:
if prop.flags & Gio.DBusPropertyInfoFlags.READABLE:
self.readable_properties[iface.name + "." + prop.name] = prop.signature
if prop.flags & Gio.DBusPropertyInfoFlags.WRITABLE:
self.writable_properties[iface.name + "." + prop.name] = prop.signature
for iface in interfaces:
for signal in iface.signals:
s_name = signal.name
def EmitSignal(iface, signal):
return lambda *args: self.SignalEmitted(iface.name, signal.name, GLib.Variant("(" + "".join(s.signature for s in signal.args) + ")", args))
self._at_exit(getattr(object, signal.name).connect(EmitSignal(iface, signal)).__exit__)
if "org.freedesktop.DBus.Properties" not in (iface.name for iface in interfaces):
try:
def onPropertiesChanged(iface, changed, invalidated):
changed = {key: GLib.Variant(self.readable_properties[iface + "." + key], val) for key, val in changed.items()}
args = GLib.Variant("(sa{sv}as)", (iface, changed, invalidated))
self.SignalEmitted("org.freedesktop.DBus.Properties", "PropertiesChanged", args)
self._at_exit(object.PropertiesChanged.connect(onPropertiesChanged).__exit__)
except AttributeError:
pass
SignalEmitted = generic.signal()
def call_method(self, connection, sender, object_path, interface_name, method_name, parameters, invocation):
try:
try:
outargs = self.outargs[interface_name + "." + method_name]
method = getattr(self.object, method_name)
except KeyError:
if interface_name == "org.freedesktop.DBus.Properties":
if method_name == "Get":
method = self.Get
outargs = ["v"]
elif method_name == "GetAll":
method = self.GetAll
outargs = ["a{sv}"]
elif method_name == "Set":
method = self.Set
outargs = []
else:
raise
else:
raise
sig = signature(method)
kwargs = {}
if "dbus_context" in sig.parameters and sig.parameters["dbus_context"].kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
kwargs["dbus_context"] = MethodCallContext(invocation)
result = method(*parameters, **kwargs)
if len(outargs) == 0:
invocation.return_value(None)
elif len(outargs) == 1:
invocation.return_value(GLib.Variant("(" + "".join(outargs) + ")", (result,)))
else:
invocation.return_value(GLib.Variant("(" + "".join(outargs) + ")", result))
except Exception as e:
logger = logging.getLogger(__name__)
logger.exception("Exception while handling %s.%s()", interface_name, method_name)
#TODO Think of a better way to translate Python exception types to DBus error types.
e_type = type(e).__name__
if not "." in e_type:
e_type = "unknown." + e_type
invocation.return_dbus_error(e_type, str(e))
def Get(self, interface_name, property_name):
type = self.readable_properties[interface_name + "." + property_name]
result = getattr(self.object, property_name)
return GLib.Variant(type, result)
def GetAll(self, interface_name):
ret = {}
for name, type in self.readable_properties.items():
ns, local = name.rsplit(".", 1)
if ns == interface_name:
ret[local] = GLib.Variant(type, getattr(self.object, local))
return ret
def Set(self, interface_name, property_name, value):
self.writable_properties[interface_name + "." + property_name]
setattr(self.object, property_name, value)
class ObjectRegistration(ExitableWithAliases("unregister")):
__slots__ = ()
def __init__(self, bus, path, interfaces, wrapper, own_wrapper=False):
if own_wrapper:
self._at_exit(wrapper.__exit__)
def func(interface_name, signal_name, parameters):
bus.con.emit_signal(None, path, interface_name, signal_name, parameters)
self._at_exit(wrapper.SignalEmitted.connect(func).__exit__)
try:
ids = [bus.con.register_object(path, interface, wrapper.call_method, None, None) for interface in interfaces]
except TypeError as e:
if str(e).startswith("argument vtable: Expected Gio.DBusInterfaceVTable"):
raise Exception("GLib 2.46 is required to publish objects; it is impossible in older versions.")
else:
raise
self._at_exit(lambda: [bus.con.unregister_object(id) for id in ids])
class RegistrationMixin:
__slots__ = ()
def register_object(self, path, object, node_info):
if node_info is None:
try:
node_info = type(object).dbus
except AttributeError:
node_info = type(object).__doc__
if type(node_info) != list and type(node_info) != tuple:
node_info = [node_info]
node_info = [Gio.DBusNodeInfo.new_for_xml(ni) for ni in node_info]
interfaces = sum((ni.interfaces for ni in node_info), [])
wrapper = ObjectWrapper(object, interfaces)
return ObjectRegistration(self, path, interfaces, wrapper, own_wrapper=True)

View File

@ -0,0 +1,29 @@
from .exitable import ExitableWithAliases
class NameOwner(ExitableWithAliases("unown")):
__slots__ = ()
def __init__(self, bus, name, allow_replacement, replace):
flags = 4 | (1 if allow_replacement else 0) | (2 if replace else 0)
res = bus.dbus.RequestName(name, flags)
if res == 1:
self._at_exit(lambda: bus.dbus.ReleaseName(name))
return # OK
if res == 3:
raise RuntimeError("name already exists on the bus")
if res == 4:
raise RuntimeError("you're already the owner of this name")
raise RuntimeError("cannot take ownership of the name")
class RequestNameMixin(object):
__slots__ = ()
def request_name(self, name, allow_replacement=True, replace=False):
"""Aquires a bus name.
Returns
-------
NameOwner
An object you can use as a context manager to unown the name later.
"""
return NameOwner(self, name, allow_replacement, replace)

View File

@ -0,0 +1,53 @@
from gi.repository import Gio
from .exitable import ExitableWithAliases
class Subscription(ExitableWithAliases("unsubscribe", "disconnect")):
Flags = Gio.DBusSignalFlags
__slots__ = ()
def __init__(self, con, sender, iface, member, object, arg0, flags, callback):
id = con.signal_subscribe(sender, iface, member, object, arg0, flags, callback)
self._at_exit(lambda: con.signal_unsubscribe(id))
class SubscriptionMixin(object):
__slots__ = ()
SubscriptionFlags = Subscription.Flags
def subscribe(self, sender=None, iface=None, signal=None, object=None, arg0=None, flags=0, signal_fired=None):
"""Subscribes to matching signals.
Subscribes to signals on connection and invokes signal_fired callback
whenever the signal is received.
To receive signal_fired callback, you need an event loop.
https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst#setting-up-an-event-loop
Parameters
----------
sender : string, optional
Sender name to match on (unique or well-known name) or None to listen from all senders.
iface : string, optional
Interface name to match on or None to match on all interfaces.
signal : string, optional
Signal name to match on or None to match on all signals.
object : string, optional
Object path to match on or None to match on all object paths.
arg0 : string, optional
Contents of first string argument to match on or None to match on all kinds of arguments.
flags : SubscriptionFlags, optional
signal_fired : callable, optional
Invoked when there is a signal matching the requested data.
Parameters: sender, object, iface, signal, params
Returns
-------
Subscription
An object you can use as a context manager to unsubscribe from the signal later.
See Also
--------
See https://developer.gnome.org/gio/2.44/GDBusConnection.html#g-dbus-connection-signal-subscribe
for more information.
"""
callback = (lambda con, sender, object, iface, signal, params: signal_fired(sender, object, iface, signal, params.unpack())) if signal_fired is not None else lambda *args: None
return Subscription(self.con, sender, iface, signal, object, arg0, flags, callback)

15
ui/lib/pydbus/timeout.py Normal file
View File

@ -0,0 +1,15 @@
from gi.repository import GLib, GObject
def timeout_to_glib(timeout):
if timeout is None:
try:
return GLib.MAXINT
except AttributeError:
# GLib < 2.46
return GObject.G_MAXINT
else:
try:
timeout = timeout.total_seconds()
except AttributeError:
pass
return int(timeout * 1000)

View File

@ -1,5 +1,5 @@
project('breezydesktop',
version: '1.0.0',
version: run_command('cat', join_paths('..', 'VERSION'), check: true).stdout().strip(),
meson_version: '>= 0.62.0',
default_options: [ 'warning_level=2', 'werror=false', ],
)

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-02 09:15-0700\n"
"POT-Creation-Date: 2024-10-03 21:35-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -27,11 +27,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr ""
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr ""
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr ""
@ -331,22 +331,22 @@ msgid ""
"script. Report this issue if it persists."
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr ""
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr ""
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr ""
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr ""
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr ""
@ -421,22 +421,22 @@ msgstr ""
msgid "Menu"
msgstr ""
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr ""
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr ""
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr ""
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr ""
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 20:54-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
@ -29,11 +29,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr "Diese Funktion wird von Ihrem Gerät derzeit nicht unterstützt."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Bezahlter Tarifstatus"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Funktionsverfügbarkeit"
@ -354,22 +354,22 @@ msgstr ""
"das Setup-Skript bitte erneut aus. Melden Sie dieses Problem, falls es "
"beständig ist."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Lizenzdetails"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Spenden"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Ein Token anfordern"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Token verifizieren"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Lizenzdetails"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Kein Gerät verbunden"
@ -470,22 +470,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Menü"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Einige Funktionen laufen bald ab"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Details anzeigen"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Produktivitätsfunktionen sind deaktiviert"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Erzwingt Zurrücksetzung"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Über BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 20:55-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -28,11 +28,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr "Esta función no es compatible con tu dispositivo en este momento."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Estado del Nivel de Membresía Pagada"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Disponibilidad de Características"
@ -353,22 +353,22 @@ msgstr ""
"Su configuración de Breezy GNOME es inválida o incompleta. Vuelva a ejecutar "
"el script de configuración. Informe sobre este problema si persiste."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Detalles de la Licencia"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Donar"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Solicitar un token"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Verificar token"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Detalles de la Licencia"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "No hay dispositivo conectado"
@ -467,22 +467,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Menú"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Algunas funciones expirarán pronto"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Ver detalles"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Las funciones de productividad están deshabilitadas"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Reinicio forzoso"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Acerca de BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 20:54-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -31,11 +31,11 @@ msgstr ""
"Cette fonctionnalité n'est actuellement pas prise en charge par votre "
"appareil."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Statut de l'abonnement payant"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Disponibilité des fonctionnalités"
@ -337,11 +337,11 @@ msgstr "Par défaut"
#: src/gtk/connected-device.ui:396
msgid "Text Scaling"
msgstr ""
msgstr "Mise à l'échelle du texte"
#: src/gtk/connected-device.ui:397
msgid "Scaling text below 1.0 will simulate a higher resolution display"
msgstr ""
msgstr "Une mise à l'échelle du texte en dessous de 1.0 simulera un affichage de plus haute résolution"
#: src/gtk/failed-verification.ui:13
msgid "Breezy Desktop GNOME invalid setup"
@ -356,22 +356,22 @@ msgstr ""
"exécuter à nouveau le script de configuration. Signalez ce problème s'il "
"persiste."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Détails de la licence"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Faire un don"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Demander un jeton d'authentification"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Vérifier le jeton d'authentification"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Détails de la licence"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Aucun appareil connecté"
@ -470,22 +470,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Menu"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Certaines fonctionnalités expirent bientôt"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Afficher les détails"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Les fonctionnalités de productivité sont désactivées"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Réinitialiser"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "À propos de BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 21:14-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Italian <tp@lists.linux.it>\n"
@ -27,11 +27,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr ""
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr ""
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr ""
@ -331,22 +331,22 @@ msgid ""
"script. Report this issue if it persists."
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr ""
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr ""
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr ""
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr ""
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr ""
@ -421,22 +421,22 @@ msgstr ""
msgid "Menu"
msgstr ""
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr ""
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr ""
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr ""
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr ""
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr ""

View File

@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 20:55-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Japanese <translation-team-ja@lists.sourceforge.net>\n"
@ -31,11 +31,11 @@ msgstr "メガネを3Dモードに切り替え、表示の幅を2倍にします
msgid "This feature is not currently supported for your device."
msgstr "この機能は現在接続されているデバイスではサポートされていません。"
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "有料ティアの状態"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "利用できる機能"
@ -338,7 +338,9 @@ msgstr "テキストスケーリング"
#: src/gtk/connected-device.ui:397
msgid "Scaling text below 1.0 will simulate a higher resolution display"
msgstr "テキストを1.0未満にスケーリングすると、高解像度ディスプレイをシミュレートします。"
msgstr ""
"テキストを1.0未満にスケーリングすると、高解像度ディスプレイをシミュレートしま"
"す。"
#: src/gtk/failed-verification.ui:13
msgid "Breezy Desktop GNOME invalid setup"
@ -352,22 +354,22 @@ msgstr ""
"Breezy GNOMEのセットアップが無効または不完全です。セットアップスクリプトを再"
"実行してください。問題が解決しない場合は、この問題を報告してください。"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "ライセンスの詳細"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "寄付"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "トークンをリクエストする"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "トークンを検証する"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "ライセンスの詳細"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "デバイスが接続されていません"
@ -466,22 +468,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "メニュー"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "一部の機能はもうすぐ期限が切れます"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "詳細を表示"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "プロダクティビティ機能が無効になっています"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "強制リセット"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Breezy Desktopについて"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-16 10:26-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Polish <translation-team-pl@lists.sourceforge.net>\n"
@ -28,11 +28,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr ""
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr ""
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr ""
@ -332,22 +332,22 @@ msgid ""
"script. Report this issue if it persists."
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr ""
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr ""
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr ""
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr ""
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr ""
@ -422,22 +422,22 @@ msgstr ""
msgid "Menu"
msgstr ""
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr ""
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr ""
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr ""
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr ""
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-19 09:39-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Brazilian Portuguese <ldpbr-translation@lists.sourceforge."
@ -30,11 +30,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr "Este recurso não é atualmente suportado para o seu dispositivo."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Status do Nível Pago"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Disponibilidade de Recursos"
@ -339,7 +339,8 @@ msgstr "Redimensionamento de Texto"
#: src/gtk/connected-device.ui:397
msgid "Scaling text below 1.0 will simulate a higher resolution display"
msgstr "Redimensionar o texto abaixo de 1.0 simulará uma tela de resolução mais alta"
msgstr ""
"Redimensionar o texto abaixo de 1.0 simulará uma tela de resolução mais alta"
#: src/gtk/failed-verification.ui:13
msgid "Breezy Desktop GNOME invalid setup"
@ -354,22 +355,22 @@ msgstr ""
"execute novamente o script de configuração. Relate este problema se "
"persistir."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Detalhes da Licença"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Doar"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Solicitar um token"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Verificar token"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Detalhes da Licença"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Nenhum dispositivo conectado"
@ -466,22 +467,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Menu"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Algumas funcionalidades expirarão em breve"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Ver detalhes"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "As funcionalidades de produtividade estão desabilitadas"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Forçar redefinição"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Sobre o BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-17 09:39-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Russian <gnu@d07.ru>\n"
@ -29,11 +29,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr "Эта функция в настоящее время не поддерживается для вашего устройства."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Статус платного уровня"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Статус функций"
@ -355,22 +355,22 @@ msgstr ""
"перезапустите скрипт настройки. Сообщите об этой проблеме, если она "
"сохраняется."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Подробности лицензии"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Донатить"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Запросить токен"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Проверить токен"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Подробности лицензии"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Устройство не подключено"
@ -468,22 +468,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Меню"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Некоторые функции скоро истекут"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Просмотреть детали"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Функции повышения производительности отключены"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Сброс"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "О BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-16 10:31-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
@ -29,11 +29,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr "Din enhet stöder inte den här funktionen för tillfället."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Betalningsstatus"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Funktions tillgänglighet"
@ -349,22 +349,22 @@ msgstr ""
"Din Breezy GNOME inställning är ogiltig eller ofullständig. Var god kör "
"inställning skriptet igen. Rapportera detta problem om det fortsätter."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Licensdetaljer"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Donera"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Begär en token"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Verifiera token"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Licensdetaljer"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Inget enhet ansluten"
@ -462,22 +462,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Meny"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Vissa funktioner upphör snart"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Se detaljer"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Produktivitets funktioner är inaktiverade"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Tvinga Reset"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Om BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-17 10:08-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
@ -28,11 +28,11 @@ msgstr "Переключає окуляри в режим «бок о бок»
msgid "This feature is not currently supported for your device."
msgstr "Ця функція наразі не підтримується на вашому пристрої."
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr "Статус платного рівня"
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr "Статус функцій"
@ -352,22 +352,22 @@ msgstr ""
"Ваша настройка Breezy GNOME є невірною або неповною. Будь ласка, запустіть "
"скрипт настройки повторно. Повідомте про цю проблему, якщо вона не зникає."
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr "Деталі ліцензії"
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr "Донатити"
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr "Запитати токен"
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr "Перевірити токен"
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr "Деталі ліцензії"
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr "Жоден пристрій не підключено"
@ -465,22 +465,22 @@ msgstr "Breezy Desktop"
msgid "Menu"
msgstr "Меню"
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr "Деякі функції закінчуються незабаром"
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr "Переглянути деталі"
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr "Функції продуктивного режиму відключені"
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr "Скинути"
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr "Про BreezyDesktop"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-01 13:21-0700\n"
"POT-Creation-Date: 2024-10-03 21:26-0700\n"
"PO-Revision-Date: 2024-08-02 20:55-0700\n"
"Last-Translator: <wayne@xronlinux.com>\n"
"Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n"
@ -26,11 +26,11 @@ msgstr ""
msgid "This feature is not currently supported for your device."
msgstr ""
#: src/licensedialog.py:57
#: src/licensedialogcontent.py:63
msgid "Paid Tier Status"
msgstr ""
#: src/licensedialog.py:63
#: src/licensedialogcontent.py:71
msgid "Feature Availability"
msgstr ""
@ -330,22 +330,22 @@ msgid ""
"script. Report this issue if it persists."
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:55
msgid "License Details"
msgstr ""
#: src/gtk/license-dialog.ui:27
#: src/gtk/license-dialog-content.ui:15
msgid "Donate"
msgstr ""
#: src/gtk/license-dialog.ui:44
#: src/gtk/license-dialog-content.ui:31
msgid "Request a token"
msgstr ""
#: src/gtk/license-dialog.ui:52
#: src/gtk/license-dialog-content.ui:39
msgid "Verify token"
msgstr ""
#: src/gtk/license-dialog.ui:5 src/gtk/window.ui:91
msgid "License Details"
msgstr ""
#: src/gtk/no-device.ui:13
msgid "No device connected"
msgstr ""
@ -420,22 +420,22 @@ msgstr ""
msgid "Menu"
msgstr ""
#: src/gtk/window.ui:35
#: src/gtk/window.ui:43
msgid "Some features expire soon"
msgstr ""
#: src/gtk/window.ui:36 src/gtk/window.ui:43
#: src/gtk/window.ui:51 src/gtk/window.ui:76
msgid "View details"
msgstr ""
#: src/gtk/window.ui:42
#: src/gtk/window.ui:68
msgid "Productivity features are disabled"
msgstr ""
#: src/gtk/window.ui:59
#: src/gtk/window.ui:95
msgid "Force Reset"
msgstr ""
#: src/gtk/window.ui:63
#: src/gtk/window.ui:99
msgid "About BreezyDesktop"
msgstr ""

View File

@ -4,6 +4,7 @@
<file preprocess="xml-stripblanks">gtk/connected-device.ui</file>
<file preprocess="xml-stripblanks">gtk/failed-verification.ui</file>
<file preprocess="xml-stripblanks">gtk/license-dialog.ui</file>
<file preprocess="xml-stripblanks">gtk/license-dialog-content.ui</file>
<file preprocess="xml-stripblanks">gtk/no-device.ui</file>
<file preprocess="xml-stripblanks">gtk/no-driver.ui</file>
<file preprocess="xml-stripblanks">gtk/no-extension.ui</file>

View File

@ -26,14 +26,20 @@ import locale
import gettext
VERSION = '@VERSION@'
pkgdatadir = '@pkgdatadir@'
localedir = '@localedir@'
xdg_data_home = os.getenv('XDG_DATA_HOME', os.path.join(os.path.expanduser('~'), '.local', 'share'))
appdir = os.getenv('APPDIR', xdg_data_home)
locale_dir = os.path.join(appdir, 'locale')
pkgdatadir = os.path.join(appdir, 'breezydesktop')
sys.path.insert(1, pkgdatadir)
signal.signal(signal.SIGINT, signal.SIG_DFL)
locale.bindtextdomain('breezydesktop', localedir)
locale.textdomain('breezydesktop')
gettext.install('breezydesktop', localedir)
locale.setlocale(locale.LC_ALL, locale.getlocale())
locale.bindtextdomain('breezydesktop', locale_dir)
gettext.install('breezydesktop', locale_dir)
gettext.bindtextdomain('breezydesktop', locale_dir)
gettext.textdomain('breezydesktop')
if __name__ == '__main__':
import gi

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="LicenseDialogContent" parent="GtkBox">
<property name="orientation">vertical</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<child>
<object class="AdwPreferencesGroup">
<property name="margin-top">10</property>
<child>
<object class="AdwActionRow" id="donation_info">
<property name="title" translatable="yes">Donate</property>
<property name="subtitle">ko-fi.com/wheaney</property>
<child type="suffix">
<object class="GtkLinkButton">
<property name="icon-name">go-next-symbolic</property>
<property name="uri">https://ko-fi.com/wheaney</property>
</object>
</child>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwEntryRow" id="request_token">
<property name="visible">0</property>
<property name="title" translatable="yes">Request a token</property>
<property name="input-purpose">6</property>
<property name="show-apply-button">1</property>
</object>
</child>
<child>
<object class="AdwEntryRow" id="verify_token">
<property name="visible">0</property>
<property name="title" translatable="yes">Verify token</property>
<property name="input-hints">16</property>
<property name="show-apply-button">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="features">
</object>
</child>
<child>
<object class="GtkBox" id="tiers">
</object>
</child>
</template>
</interface>

View File

@ -12,59 +12,5 @@
<property name="icon-name">view-refresh-symbolic</property>
</object>
</child>
<child internal-child="content_area">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<child>
<object class="AdwPreferencesGroup">
<property name="margin-top">10</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Donate</property>
<property name="subtitle">ko-fi.com/wheaney</property>
<property name="subtitle-selectable">True</property>
<child type="suffix">
<object class="GtkLinkButton">
<property name="icon-name">go-next-symbolic</property>
<property name="uri">https://ko-fi.com/wheaney</property>
</object>
</child>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwEntryRow" id="request_token">
<property name="visible">0</property>
<property name="title" translatable="yes">Request a token</property>
<property name="input-purpose">6</property>
<property name="show-apply-button">1</property>
</object>
</child>
<child>
<object class="AdwEntryRow" id="verify_token">
<property name="visible">0</property>
<property name="title" translatable="yes">Verify token</property>
<property name="input-hints">16</property>
<property name="show-apply-button">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="features">
</object>
</child>
<child>
<object class="GtkBox" id="tiers">
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -30,17 +30,53 @@
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwBanner" id="license_action_needed_banner">
<object class="GtkInfoBar" id="license_action_needed_banner">
<property name="revealed">0</property>
<property name="title" translatable="yes">Some features expire soon</property>
<property name="button-label" translatable="yes">View details</property>
<property name="show-close-button">False</property>
<property name="message-type">warning</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Some features expire soon</property>
<property name="hexpand">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="license_action_needed_button">
<property name="label" translatable="yes">View details</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwBanner" id="missing_breezy_features_banner">
<object class="GtkInfoBar" id="missing_breezy_features_banner">
<property name="revealed">0</property>
<property name="title" translatable="yes">Productivity features are disabled</property>
<property name="button-label" translatable="yes">View details</property>
<property name="show-close-button">False</property>
<property name="message-type">error</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Productivity features are disabled</property>
<property name="hexpand">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="missing_breezy_features_button">
<property name="label" translatable="yes">View details</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
<child>

View File

@ -1,87 +1,14 @@
from gi.repository import Adw, Gtk, GLib
from .nolicense import NoLicense
from .statemanager import StateManager
from .licensetierrow import LicenseTierRow
from .licensefeaturerow import LicenseFeatureRow
from .xrdriveripc import XRDriverIPC
import gettext
_ = gettext.gettext
from gi.repository import Gtk
from .licensedialogcontent import LicenseDialogContent
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/license-dialog.ui')
class LicenseDialog(Gtk.Dialog):
__gtype_name__ = 'LicenseDialog'
tiers = Gtk.Template.Child()
features = Gtk.Template.Child()
request_token = Gtk.Template.Child()
verify_token = Gtk.Template.Child()
refresh_license_button = Gtk.Template.Child()
def __init__(self):
super(Gtk.Dialog, self).__init__()
self.init_template()
self.ipc = XRDriverIPC.get_instance()
StateManager.get_instance().connect('notify::license-action-needed', self._handle_license)
self._handle_license(StateManager.get_instance())
self.request_token.connect('apply', self._on_request_token)
self.verify_token.connect('apply', self._on_verify_token)
self.refresh_license_button.connect('clicked', self._refresh_license)
self.no_license = NoLicense(hide_refresh_button = True)
def _refresh_license(self, widget):
self.refresh_license_button.set_sensitive(False)
self.ipc.write_control_flags({'refresh_device_license': True})
GLib.timeout_add_seconds(3, self._handle_license)
def _handle_license(self, state_manager = None, val = None):
GLib.idle_add(self._handle_license_idle, state_manager or StateManager.get_instance())
def _handle_license_idle(self, state_manager):
self.refresh_license_button.set_sensitive(False)
license_view = state_manager.state['ui_view'].get('license', {})
self.request_token.set_visible(not state_manager.confirmed_token)
self.verify_token.set_visible(not state_manager.confirmed_token)
for child in self.tiers:
self.tiers.remove(child)
for child in self.features:
self.features.remove(child)
if license_view:
tiers_group = Adw.PreferencesGroup(title=_("Paid Tier Status"), margin_top=20)
self.tiers.append(tiers_group)
for tier_name, tier_details in license_view['tiers'].items():
tiers_group.add(LicenseTierRow(tier_name, tier_details))
features_group = Adw.PreferencesGroup(title=_("Feature Availability"), margin_top=20)
self.features.append(features_group)
for feature_name, feature_details in license_view['features'].items():
features_group.add(LicenseFeatureRow(feature_name, feature_details))
else:
self.tiers.append(self.no_license)
self.refresh_license_button.set_sensitive(True)
def _on_request_token(self, widget):
email_address = self.request_token.get_text()
self.request_token.set_editable(False)
if not self.ipc.request_token(email_address):
self.request_token.set_editable(True)
def _on_verify_token(self, widget):
token = self.verify_token.get_text()
self.request_token.set_editable(False)
self.verify_token.set_editable(False)
if self.ipc.verify_token(token):
self.ipc.write_control_flags({'refresh_device_license': True})
else:
self.request_token.set_editable(True)
self.verify_token.set_editable(True)
self.content = LicenseDialogContent(self.refresh_license_button)
self.get_content_area().append(self.content)

View File

@ -0,0 +1,95 @@
from gi.repository import Adw, Gtk, GLib
from .nolicense import NoLicense
from .statemanager import StateManager
from .licensetierrow import LicenseTierRow
from .licensefeaturerow import LicenseFeatureRow
from .xrdriveripc import XRDriverIPC
import gettext
_ = gettext.gettext
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/license-dialog-content.ui')
class LicenseDialogContent(Gtk.Box):
__gtype_name__ = 'LicenseDialogContent'
tiers = Gtk.Template.Child()
features = Gtk.Template.Child()
request_token = Gtk.Template.Child()
verify_token = Gtk.Template.Child()
donation_info = Gtk.Template.Child()
def __init__(self, refresh_license_button):
super(Gtk.Box, self).__init__()
self.init_template()
# check if it has a set_subtitle_selectable method
if hasattr(self.donation_info, 'set_subtitle_selectable'):
self.donation_info.set_subtitle_selectable(True)
self.refresh_license_button = refresh_license_button
self.refresh_license_button.connect('clicked', self._refresh_license)
self.ipc = XRDriverIPC.get_instance()
StateManager.get_instance().connect('notify::license-action-needed', self._handle_license)
self._handle_license(StateManager.get_instance())
self.request_token.connect('apply', self._on_request_token)
self.verify_token.connect('apply', self._on_verify_token)
self.no_license = NoLicense(hide_refresh_button = True)
def _refresh_license(self, widget):
self.refresh_license_button.set_sensitive(False)
self.ipc.write_control_flags({'refresh_device_license': True})
GLib.timeout_add_seconds(3, self._handle_license)
def _handle_license(self, state_manager = None, val = None):
GLib.idle_add(self._handle_license_idle, state_manager or StateManager.get_instance())
def _handle_license_idle(self, state_manager):
self.refresh_license_button.set_sensitive(False)
license_view = state_manager.state['ui_view'].get('license', {})
self.request_token.set_visible(not state_manager.confirmed_token)
self.verify_token.set_visible(not state_manager.confirmed_token)
for child in self.tiers:
self.tiers.remove(child)
for child in self.features:
self.features.remove(child)
if license_view:
tiers_group = Adw.PreferencesGroup(title=_("Paid Tier Status"), margin_top=20)
self.tiers.append(tiers_group)
for tier_name, tier_details in license_view['tiers'].items():
row = LicenseTierRow(tier_name, tier_details)
if row.get_title() != "":
tiers_group.add(row)
features_group = Adw.PreferencesGroup(title=_("Feature Availability"), margin_top=20)
self.features.append(features_group)
for feature_name, feature_details in license_view['features'].items():
features_group.add(LicenseFeatureRow(feature_name, feature_details))
else:
self.tiers.append(self.no_license)
self.refresh_license_button.set_sensitive(True)
def _on_request_token(self, widget):
email_address = self.request_token.get_text()
self.request_token.set_editable(False)
if not self.ipc.request_token(email_address):
self.request_token.set_editable(True)
def _on_verify_token(self, widget):
token = self.verify_token.get_text()
self.request_token.set_editable(False)
self.verify_token.set_editable(False)
if self.ipc.verify_token(token):
self.ipc.write_control_flags({'refresh_device_license': True})
else:
self.request_token.set_editable(True)
self.verify_token.set_editable(True)

View File

@ -56,7 +56,7 @@ class LicenseTierRow(Adw.ExpanderRow):
'supporter': _('Gaming'),
'subscriber': _('Productivity')
}
return tier_names[tier]
return tier_names.get(tier) or ""
def _period_description(self, period):
period_descriptions = {

View File

@ -17,12 +17,14 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
import gettext
import gi
import locale
import logging
import os
import sys
lib_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')
sys.path.insert(0, lib_dir)
import gi
import logging
import argparse
from logging.handlers import TimedRotatingFileHandler
@ -32,16 +34,6 @@ gi.require_version('Adw', '1')
gi.require_version('Gio', '2.0')
gi.require_version('GLib', '2.0')
user_home = os.path.expanduser('~')
xdg_data_home = os.environ.get('XDG_DATA_HOME') or os.path.join(user_home, '.local', 'share')
locale_dir = os.environ.get('LOCALE_DIR', os.path.join(xdg_data_home, 'locale'))
locale.setlocale(locale.LC_ALL, locale.getdefaultlocale())
locale.bindtextdomain('breezydesktop', locale_dir)
gettext.bindtextdomain('breezydesktop', locale_dir)
gettext.textdomain('breezydesktop')
from gi.repository import Adw, Gtk, Gio
from .licensedialog import LicenseDialog
from .statemanager import StateManager
@ -75,14 +67,16 @@ XRDriverIPC.set_instance(XRDriverIPC(logger, config_dir))
class BreezydesktopApplication(Adw.Application):
"""The main application singleton class."""
def __init__(self, skip_verification):
def __init__(self, version, skip_verification):
super().__init__(application_id='com.xronlinux.BreezyDesktop',
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
self.version = version
self.create_action('quit', self.on_quit_action, ['<primary>q'])
self.create_action('about', self.on_about_action)
self.create_action('license', self.on_license_action)
self.create_action('reset_driver', self.on_reset_driver_action)
self._skip_verification = skip_verification
self._skip_verification = skip_verification or False
# always do this on start-up since the driver sometimes fails to update the license on boot,
# prevent showing a license warning unnecessarily
@ -107,7 +101,7 @@ class BreezydesktopApplication(Adw.Application):
modal=True,
program_name='Breezy Desktop',
logo_icon_name='com.xronlinux.BreezyDesktop',
version='1.0.0',
version=self.version,
authors=['Wayne Heaney'],
copyright='© 2024 Wayne Heaney')
about.present()
@ -151,5 +145,5 @@ def main(version):
parser.add_argument("-sv", "--skip-verification", action="store_true")
args = parser.parse_args()
app = BreezydesktopApplication(args.skip_verification)
app = BreezydesktopApplication(version, args.skip_verification)
return app.run(None)

View File

@ -34,6 +34,7 @@ breezydesktop_sources = [
'failedverification.py',
'license.py',
'licensedialog.py',
'licensedialogcontent.py',
'licensefeaturerow.py',
'licensetierrow.py',
'main.py',

View File

@ -36,10 +36,14 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
main_content = Gtk.Template.Child()
license_action_needed_banner = Gtk.Template.Child()
license_action_needed_button = Gtk.Template.Child()
missing_breezy_features_banner = Gtk.Template.Child()
missing_breezy_features_button = Gtk.Template.Child()
def __init__(self, skip_verification, **kwargs):
super().__init__(**kwargs)
self._skip_verification = skip_verification
self.state_manager = StateManager.get_instance()
self.state_manager.connect('device-update', self._handle_state_update)
@ -54,8 +58,8 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
self.no_extension = NoExtension()
self.no_license = NoLicense()
self.license_action_needed_banner.connect('button-clicked', self._on_license_button_clicked)
self.missing_breezy_features_banner.connect('button-clicked', self._on_license_button_clicked)
self.license_action_needed_button.connect('clicked', self._on_license_button_clicked)
self.missing_breezy_features_button.connect('clicked', self._on_license_button_clicked)
self._handle_state_update(self.state_manager, None)
@ -77,14 +81,14 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
if not self._skip_verification and not verify_installation():
self.main_content.append(self.failed_verification)
elif not ExtensionsManager.get_instance().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:
self.main_content.append(self.no_device)
elif not ExtensionsManager.get_instance().is_installed():
self.main_content.append(self.no_extension)
else:
self.main_content.append(self.connected_device)
self.connected_device.set_device_name(state_manager.connected_device_name)