feat: update dependencies.

pull/1/head
DebuggerX 4 years ago
parent 9166c80979
commit 9b64a4101d

@ -1,6 +0,0 @@
# Changelog
## 0.2.0
* Initial release, replacing the existing ffi bindings.

@ -1,8 +0,0 @@
# gsettings.dart Contribution Guide
If you have a problem, please [file an issue](https://github.com/canonical/gsettings.dart/issues/new).
If you have a solution, then we accept contributions via [pull requests](https://github.com/canonical/gsettings.dart/pulls).
All contributions require the author(s) to sign the [contributor license agreement](http://www.ubuntu.com/legal/contributors/).
Thanks for your help!

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

@ -1,19 +0,0 @@
[![Pub Package](https://img.shields.io/pub/v/gsettings.svg)](https://pub.dev/packages/gsettings)
Provides a client to use [GSettings](https://developer.gnome.org/gio/stable/GSettings.html) - a settings database used for storing user preferences on Linux.
```dart
import 'package:dbus/dbus.dart';
import 'package:gsettings/gsettings.dart';
void main() async {
var schema = GSettings('org.gnome.desktop.interface');
var value = await schema.get('font-name');
var font = (value as DBusString).value;
print('Current font set to: $font');
}
```
## Contributing to gsettings.dart
We welcome contributions! See the [contribution guide](CONTRIBUTING.md) for more details.

@ -1 +0,0 @@
include: package:pedantic/analysis_options.yaml

@ -1 +0,0 @@
export 'src/gsettings.dart';

@ -1,287 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:dbus/dbus.dart';
import 'package:xdg_directories/xdg_directories.dart';
import 'getuid.dart';
import 'gvariant_binary_codec.dart';
import 'gvariant_database.dart';
XDGDirectories xdg = XDGDirectories();
/// Message received when DConf notifies changes.
class DConfNotifyEvent {
/// A prefixed applied to each value in [keys].
final String prefix;
/// The paths to each key that has changed, to be prefixed with [prefix].
/// If empty, a single key has changed with the value [prefix].
final List<String> paths;
/// Unique tag for this change, used to detect if this client generated the change.
final String tag;
const DConfNotifyEvent(this.prefix, this.paths, this.tag);
@override
String toString() => "DConfNotifyEvent('$prefix', $paths, '$tag')";
}
/// A client that connects to DConf.
class DConfClient {
final String? profile;
/// Stream of key names that indicate when a value has changed.
Stream<DConfNotifyEvent> get notify => _notifyController.stream;
final _notifyController = StreamController<DConfNotifyEvent>();
/// The D-Bus buses this client is connected to.
final DBusClient _systemBus;
final DBusClient _sessionBus;
final bool _closeSystemBus;
final bool _closeSessionBus;
/// Creates a new DConf client.
DConfClient({this.profile, DBusClient? systemBus, DBusClient? sessionBus})
: _systemBus = systemBus ?? DBusClient.system(),
_sessionBus = sessionBus ?? DBusClient.session(),
_closeSessionBus = sessionBus == null,
_closeSystemBus = systemBus == null {
_notifyController.onListen = () {
_loadSources().then((sources) {
if (sources.isEmpty) {
throw 'No DConf source to write to';
}
_notifyController.addStream(DBusRemoteObjectSignalStream(
object: sources[0].writer,
interface: 'ca.desrt.dconf.Writer',
name: 'Notify',
signature: DBusSignature('sass'))
.map((signal) => DConfNotifyEvent(
(signal.values[0] as DBusString).value,
(signal.values[1] as DBusArray)
.children
.map((child) => (child as DBusString).value)
.toList(),
(signal.values[2] as DBusString).value)));
});
};
}
/// Gets all the keys available underneath the given directory.
Future<List<String>> list(String dir) async {
var sources = await _loadSources();
var keys = <String>{};
for (var source in sources) {
keys.addAll(await source.database.list(dir: dir));
}
return keys.toList();
}
/// Gets the value of a given [key].
Future<DBusValue?> read(String key) async {
var sources = await _loadSources();
for (var source in sources) {
var value = await source.database.lookup(key);
if (value != null) {
return value;
}
}
return null;
}
/// Sets key values in the dconf database.
Future<String> write(Map<String, DBusValue?> values) async {
var sources = await _loadSources();
if (sources.isEmpty) {
throw 'No DConf source to write to';
}
var changeset = DBusDict(
DBusSignature('s'),
DBusSignature('mv'),
values.map((key, value) => MapEntry(
DBusString(key),
DBusMaybe(DBusSignature('v'),
value != null ? DBusVariant(value) : null))));
var codec = GVariantBinaryCodec();
var result = await sources[0].writer.callMethod(
'ca.desrt.dconf.Writer',
'Change',
[DBusArray.byte(codec.encode(changeset, endian: Endian.host))],
replySignature: DBusSignature('s'));
return (result.values[0] as DBusString).value;
}
/// Terminates the connection to the DConf daemon. If a client remains unclosed, the Dart process may not terminate.
Future<void> close() async {
if (_closeSystemBus) {
await _systemBus.close();
}
if (_closeSessionBus) {
await _sessionBus.close();
}
}
// Load the DConf sources in use.
Future<List<DConfEngineSource>> _loadSources() async {
// Generate list of files to look for the profile in.
var paths = <String>[];
var profileName = profile;
if (profileName == null) {
var uid = getuid();
paths.add('/run/dconf/user/$uid');
profileName = Platform.environment['DCONF_PROFILE'];
}
if (profileName != null) {
if (profileName.startsWith('/')) {
paths.add(profileName);
} else {
paths.addAll(_getProfilePaths(profileName));
}
} else {
var rd = xdg.runtimeDir;
if (rd != null) {
paths.add(_buildFilename([rd.path, 'dconf', 'profile']));
}
paths.addAll(_getProfilePaths('user'));
}
// Find the first file that exists.
for (var path in paths) {
var sources = await _loadProfileFile(path);
if (sources != null) {
return sources;
}
}
// Return the default profile.
if (profileName == null) {
return [DConfEngineSourceUser('user', _sessionBus)];
} else {
return [];
}
}
// Get the paths to find a DConf profile with [profileName].
List<String> _getProfilePaths(String profileName) {
var paths = [
_buildFilename(['/etc', 'dconf', 'profile', profileName])
];
for (var dir in xdg.dataDirs) {
paths.add(_buildFilename([dir.path, 'dconf', 'profile', profileName]));
}
return paths;
}
// Load a DConf profile file.
Future<List<DConfEngineSource>?> _loadProfileFile(String path) async {
var file = File(path);
List<String> lines;
try {
lines = await file.readAsLines();
} on FileSystemException {
return null;
}
var sources = <DConfEngineSource>[];
for (var line in lines) {
// Strip off comments.
var commentIndex = line.lastIndexOf('#');
if (commentIndex >= 0) {
line = line.substring(0, commentIndex);
}
line = line.trim();
if (line.isEmpty) {
continue;
}
var index = line.indexOf(':');
if (index < 0) {
throw "Invalid DConf profile line: '$line'";
}
var type = line.substring(0, index);
var value = line.substring(index + 1);
DConfEngineSource source;
switch (type) {
case 'user-db':
source = DConfEngineSourceUser(value, _sessionBus);
break;
case 'system-db':
source = DConfEngineSourceSystem(value, _systemBus);
break;
case 'service-db': // Not implemented
case 'file-db': // Not implemented
default:
throw "Unknown DConf source: 'line'";
}
sources.add(source);
}
return sources;
}
}
class DConfEngineSource {
/// The database containing configuration.
GVariantDatabase get database {
throw ('Not implemented');
}
/// D-Bus object to write to configuration.
DBusRemoteObject get writer {
throw ('Not implemented');
}
}
class DConfEngineSourceUser extends DConfEngineSource {
final String name;
final DBusClient sessionBus;
DConfEngineSourceUser(this.name, this.sessionBus);
@override
GVariantDatabase get database =>
GVariantDatabase(_buildFilename([xdg.configHome.path, 'dconf', name]));
@override
DBusRemoteObject get writer => DBusRemoteObject(sessionBus,
name: 'ca.desrt.dconf',
path: DBusObjectPath('/ca/desrt/dconf/Writer/$name'));
@override
String toString() => "DConfEngineSourceUser('$name')";
}
class DConfEngineSourceSystem extends DConfEngineSource {
final String name;
final DBusClient systemBus;
DConfEngineSourceSystem(this.name, this.systemBus);
@override
GVariantDatabase get database =>
GVariantDatabase(_buildFilename(['/etc', 'dconf', 'db', name]));
@override
DBusRemoteObject get writer => DBusRemoteObject(systemBus,
name: 'ca.desrt.dconf',
path: DBusObjectPath('/ca/desrt/dconf/Writer/$name'));
@override
String toString() => "DConfEngineSourceSystem('$name')";
}
// Build a filename from parts.
String _buildFilename(List<String> parts) {
var path = parts.join('/');
while (true) {
var updatedPath = path.replaceAll('//', '/');
if (updatedPath == path) {
return path;
}
path = updatedPath;
}
}

@ -1,2 +0,0 @@
// Conditionally import the FFI version, so this doesn't break web builds.
export 'getuid_stub.dart' if (dart.library.ffi) 'getuid_linux.dart';

@ -1,11 +0,0 @@
import 'dart:ffi';
typedef _getuidC = Int32 Function();
typedef _getuidDart = int Function();
/// Gets the user ID of the current user.
int getuid() {
final dylib = DynamicLibrary.open('libc.so.6');
final getuidP = dylib.lookupFunction<_getuidC, _getuidDart>('getuid');
return getuidP();
}

@ -1,4 +0,0 @@
/// Gets the user ID of the current user.
int getuid() {
throw 'Unable to determine UID on this system';
}

@ -1,277 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:dbus/dbus.dart';
import 'package:xdg_directories/xdg_directories.dart';
import 'dconf_client.dart';
import 'gvariant_database.dart';
XDGDirectories xdg = XDGDirectories();
/// Get the names of the installed GSettings schemas.
/// These schemas can be accessed using a [GSettings] object.
Future<List<String>> listGSettingsSchemas() async {
var schemaNames = <String>{};
for (var dir in _getSchemaDirs()) {
try {
var database = GVariantDatabase(dir.path + '/gschemas.compiled');
schemaNames.addAll(await database.list(dir: ''));
} on FileSystemException {
continue;
}
}
return schemaNames.toList();
}
/// An object to access settings stored in a GSettings database.
class GSettings {
/// The name of the schema for these settings, e.g. 'org.gnome.desktop.interface'.
final String schemaName;
/// A stream of settings key names as they change.
Stream<List<String>> get keysChanged => _keysChangedController.stream;
final _keysChangedController = StreamController<List<String>>();
// Client for communicating with DConf.
final DConfClient _dconfClient;
/// Creates an object to access settings from the shema with name [schemaName].
GSettings(this.schemaName, {DBusClient? systemBus, DBusClient? sessionBus})
: _dconfClient =
DConfClient(systemBus: systemBus, sessionBus: sessionBus) {
_keysChangedController.onListen = () {
_load().then((table) {
var path = _getPath(table);
_keysChangedController.addStream(_dconfClient.notify
.map((event) => event.paths.isEmpty
? [event.prefix]
: event.paths.map((path) => event.prefix + path))
.where((keys) => keys.any((key) => key.startsWith(path)))
.map((keys) =>
keys.map((key) => key.substring(path.length)).toList()));
});
};
}
/// Gets the names of the settings keys available.
/// If the schema is not installed will throw a [GSettingsSchemaNotInstalledException].
Future<List<String>> list() async {
var table = await _load();
return table.list(dir: '', type: 'v');
}
/// Reads the value of the settings key with [name].
/// Attempting to read an unknown key will throw a [GSettingsUnknownKeyException].
/// If the schema is not installed will throw a [GSettingsSchemaNotInstalledException].
Future<DBusValue> get(String name) async {
var table = await _load();
var schemaEntry = _getSchemaEntry(table, name);
var path = _getPath(table);
// Lookup user value in DConf.
var value = await _dconfClient.read(path + name);
if (value != null) {
return value;
}
return _getDefaultValue(schemaEntry);
}
/// Reads the default value of the settings key with [name].
/// If this key is not set, then this value will be returned by [get].
/// Attempting to read an unknown key will throw a [GSettingsUnknownKeyException].
/// If the schema is not installed will throw a [GSettingsSchemaNotInstalledException].
Future<DBusValue> getDefault(String name) async {
var table = await _load();
var schemaEntry = _getSchemaEntry(table, name);
return _getDefaultValue(schemaEntry);
}
/// Check if the settings key with [name] is set.
Future<bool> isSet(String name) async {
var table = await _load();
var path = _getPath(table);
return await _dconfClient.read(path + name) != null;
}
/// Writes a single settings keys.
/// If you need to set multiple values, use [setAll].
Future<void> set(String name, DBusValue value) async {
var table = await _load();
var path = _getPath(table);
await _dconfClient.write({path + name: value});
}
/// Removes a setting value.
/// The key will now return the default value specified in the GSetting schema.
Future<void> unset(String name) async {
var table = await _load();
var path = _getPath(table);
await _dconfClient.write({path + name: null});
}
/// Writes multiple settings keys in a single transaction.
/// Writing a null value will reset it to its default value.
Future<void> setAll(Map<String, DBusValue?> values) async {
var table = await _load();
var path = _getPath(table);
await _dconfClient
.write(values.map((name, value) => MapEntry(path + name, value)));
}
/// Terminates any open connections. If a settings object remains unclosed, the Dart process may not terminate.
Future<void> close() async {
await _dconfClient.close();
}
// Get the database entry for this schema.
Future<GVariantDatabaseTable> _load() async {
for (var dir in _getSchemaDirs()) {
var database = GVariantDatabase(dir.path + '/gschemas.compiled');
try {
var table = await database.lookupTable(schemaName);
if (table != null) {
return table;
}
} on FileSystemException {
continue;
}
}
throw GSettingsSchemaNotInstalledException(schemaName);
}
_GSettingsSchemaEntry _getSchemaEntry(
GVariantDatabaseTable table, String name) {
var entry = table.lookup(name);
if (entry == null) {
throw GSettingsUnknownKeyException(schemaName, name);
}
entry as DBusStruct;
var defaultValue = entry.children[0];
List<int>? words;
DBusValue? minimumValue;
DBusValue? maximumValue;
Map<String, DBusValue>? desktopOverrides;
for (var item in entry.children.skip(1)) {
item as DBusStruct;
switch ((item.children[0] as DBusByte).value) {
case 108: // 'l' - localization
//var l10n = (item.children[1] as DBusByte).value; // 'm': messages, 't': time.
//var unparsedDefaultValue = (item.children[2] as DBusString).value;
break;
case 102: // 'f' - flags
case 101: // 'e' - enum
case 99: // 'c' - choice
words = (item.children[0] as DBusArray)
.children
.map((value) => (value as DBusUint32).value)
.toList();
break;
case 114: // 'r' - range
minimumValue = item.children[1];
maximumValue = item.children[2];
break;
case 100: // 'd' - desktop overrides
desktopOverrides = (item.children[1] as DBusDict).children.map(
(key, value) => MapEntry(
(key as DBusString).value, (value as DBusVariant).value));
break;
}
}
return _GSettingsSchemaEntry(
defaultValue: defaultValue,
words: words,
minimumValue: minimumValue,
maximumValue: maximumValue,
desktopOverrides: desktopOverrides);
}
DBusValue _getDefaultValue(_GSettingsSchemaEntry entry) {
if (entry.desktopOverrides != null) {
var xdgCurrentDesktop = Platform.environment['XDG_CURRENT_DESKTOP'] ?? '';
for (var desktop in xdgCurrentDesktop.split(':')) {
var defaultValue = entry.desktopOverrides![desktop];
if (defaultValue != null) {
return defaultValue;
}
}
}
return entry.defaultValue;
}
// Get the key path from the database table.
String _getPath(GVariantDatabaseTable table) {
var pathValue = table.lookup('.path');
if (pathValue == null) {
throw ('Unable to determine path for schema $schemaName');
}
return (pathValue as DBusString).value;
}
}
// Get the directories that contain schemas.
List<Directory> _getSchemaDirs() {
var schemaDirs = <Directory>[];
var schemaDir = Platform.environment['GSETTINGS_SCHEMA_DIR'];
if (schemaDir != null) {
schemaDirs.add(Directory(schemaDir));
}
for (var dataDir in xdg.dataDirs) {
var path = dataDir.path;
if (!path.endsWith('/')) {
path += '/';
}
path += 'glib-2.0/schemas';
schemaDirs.add(Directory(path));
}
return schemaDirs;
}
/// Exception thrown when trying to access a GSettings schema that is not installed.
class GSettingsSchemaNotInstalledException implements Exception {
/// The name of the GSettings schema that was being accessed.
final String schemaName;
const GSettingsSchemaNotInstalledException(this.schemaName);
@override
String toString() => 'GSettings schema $schemaName not installed';
}
/// Exception thrown when trying to access a key not in a GSettings schema.
class GSettingsUnknownKeyException implements Exception {
/// The name of the GSettings schema that was being accessed.
final String schemaName;
/// The name of the key being accessed.
final String keyName;
const GSettingsUnknownKeyException(this.schemaName, this.keyName);
@override
String toString() => 'Key $keyName not in GSettings schema $schemaName';
}
class _GSettingsSchemaEntry {
final DBusValue defaultValue;
final List<int>? words;
final DBusValue? minimumValue;
final DBusValue? maximumValue;
final Map<String, DBusValue>? desktopOverrides;
const _GSettingsSchemaEntry(
{required this.defaultValue,
this.words,
this.minimumValue,
this.maximumValue,
this.desktopOverrides});
@override
String toString() =>
'_GSettingsSchemaEntry(defaultValue: $defaultValue, words: $words, minimumValue: $minimumValue, maximumValue: $maximumValue, desktopOverrides: $desktopOverrides)';
}

@ -1,642 +0,0 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:dbus/dbus.dart';
class GVariantBinaryCodec {
GVariantBinaryCodec();
/// Encode a value using GVariant binary format.
Uint8List encode(DBusValue value, {required Endian endian}) {
var builder = BytesBuilder();
_encode(builder, value, endian);
return builder.takeBytes();
}
/// Parse a single GVariant value. [type] is expected to be a valid single type.
DBusValue decode(String type, ByteData data, {required Endian endian}) {
// struct
if (type.startsWith('(')) {
return _parseGVariantStruct(type, data, endian: endian);
}
// array / dict
if (type.startsWith('a')) {
if (type.startsWith('a{')) {
return _parseGVariantDict(type, data, endian: endian);
} else {
var childType = type.substring(1);
return _parseGVariantArray(childType, data, endian: endian);
}
}
// maybe
if (type.startsWith('m')) {
var childType = type.substring(1);
return _parseGVariantMaybe(childType, data, endian: endian);
}
switch (type) {
case 'b': // boolean
if (data.lengthInBytes != 1) {
throw ('Invalid length of ${data.lengthInBytes} for boolean GVariant');
}
return DBusBoolean(data.getUint8(0) != 0);
case 'y': // byte
if (data.lengthInBytes != 1) {
throw ('Invalid length of ${data.lengthInBytes} for byte GVariant');
}
return DBusByte(data.getUint8(0));
case 'n': // int16
if (data.lengthInBytes != 2) {
throw ('Invalid length of ${data.lengthInBytes} for int16 GVariant');
}
return DBusInt16(data.getInt16(0, endian));
case 'q': // uint16
if (data.lengthInBytes != 2) {
throw ('Invalid length of ${data.lengthInBytes} for uint16 GVariant');
}
return DBusUint16(data.getUint16(0, endian));
case 'i': // int32
if (data.lengthInBytes != 4) {
throw ('Invalid length of ${data.lengthInBytes} for int32 GVariant');
}
return DBusInt32(data.getInt32(0, endian));
case 'u': // uint32
if (data.lengthInBytes != 4) {
throw ('Invalid length of ${data.lengthInBytes} for uint32 GVariant');
}
return DBusUint32(data.getUint32(0, endian));
case 'x': // int64
if (data.lengthInBytes != 8) {
throw ('Invalid length of ${data.lengthInBytes} for int64 GVariant');
}
return DBusInt64(data.getInt64(0, endian));
case 't': // uint64
if (data.lengthInBytes != 8) {
throw ('Invalid length of ${data.lengthInBytes} for uint64 GVariant');
}
return DBusUint64(data.getUint64(0, endian));
case 'd': // double
return DBusDouble(data.getFloat64(0, endian));
case 's': // string
if (data.lengthInBytes < 1) {
throw ('Invalid length of ${data.lengthInBytes} for string GVariant');
}
if (data.getUint8(data.lengthInBytes - 1) != 0) {
throw ('Missing trailing nul character for string GVariant');
}
return DBusString(utf8.decode(data.buffer
.asUint8List(data.offsetInBytes, data.lengthInBytes - 1)));
case 'o': // object path
if (data.lengthInBytes < 1) {
throw ('Invalid length of ${data.lengthInBytes} for object path GVariant');
}
if (data.getUint8(data.lengthInBytes - 1) != 0) {
throw ('Missing trailing nul character for object path GVariant');
}
return DBusObjectPath(utf8.decode(data.buffer
.asUint8List(data.offsetInBytes, data.lengthInBytes - 1)));
case 'g': // signature
if (data.lengthInBytes < 1) {
throw ('Invalid length of ${data.lengthInBytes} for signature GVariant');
}
if (data.getUint8(data.lengthInBytes - 1) != 0) {
throw ('Missing trailing nul character for object path GVariant');
}
return DBusSignature(utf8.decode(data.buffer
.asUint8List(data.offsetInBytes, data.lengthInBytes - 1)));
case 'v': // variant
// Type is a suffix on the data
var childType = '';
var offset = data.lengthInBytes - 1;
while (offset >= 0 && data.getUint8(offset) != 0) {
childType = ascii.decode([data.getUint8(offset)]) + childType;
offset--;
}
if (offset < 0) {
throw ('GVariant variant missing child type');
}
var childData = ByteData.sublistView(data, 0, offset);
return DBusVariant(decode(childType, childData, endian: endian));
default:
throw ("Unsupported GVariant type: '$type'");
}
}
void _encode(BytesBuilder builder, DBusValue value, Endian endian) {
/// Align this value.
var offset = _align(builder.length, _getAlignment(value.signature.value));
for (var i = builder.length; i < offset; i++) {
builder.addByte(0);
}
if (value is DBusBoolean) {
builder.addByte(value.value ? 0x01 : 0x00);
} else if (value is DBusByte) {
_writeUint8(builder, value.value);
} else if (value is DBusInt16) {
var buffer = Uint8List(2).buffer;
ByteData.view(buffer).setInt16(0, value.value, endian);
builder.add(buffer.asUint8List());
} else if (value is DBusUint16) {
_writeUint16(builder, value.value, endian);
} else if (value is DBusInt32) {
var buffer = Uint8List(4).buffer;
ByteData.view(buffer).setInt32(0, value.value, endian);
builder.add(buffer.asUint8List());
} else if (value is DBusUint32) {
_writeUint32(builder, value.value, endian);
} else if (value is DBusInt64) {
var buffer = Uint8List(8).buffer;
ByteData.view(buffer).setInt64(0, value.value, endian);
builder.add(buffer.asUint8List());
} else if (value is DBusUint64) {
_writeUint64(builder, value.value, endian);
} else if (value is DBusDouble) {
var buffer = Uint8List(8).buffer;
ByteData.view(buffer).setFloat64(0, value.value, endian);
builder.add(buffer.asUint8List());
} else if (value is DBusString) {
builder.add(utf8.encode(value.value));
builder.addByte(0);
} else if (value is DBusObjectPath) {
builder.add(utf8.encode(value.value));
builder.addByte(0);
} else if (value is DBusSignature) {
builder.add(utf8.encode(value.value));
builder.addByte(0);
} else if (value is DBusVariant) {
_encode(builder, value.value, endian);
builder.addByte(0);
builder.add(utf8.encode(value.value.signature.value));
} else if (value is DBusMaybe) {
if (value.value != null) {
var childSize = _getElementSize(value.valueSignature.value);
_encode(builder, value.value!, endian);
if (childSize == -1) {
builder.addByte(0);
}
}
} else if (value is DBusStruct) {
_encodeStruct(builder, value.children, endian);
} else if (value is DBusArray) {
_encodeArray(builder, value.childSignature.value, value.children, endian);
} else if (value is DBusDict) {
_encodeDict(builder, value.keySignature.value, value.valueSignature.value,
value.children, endian);
} else {
throw ("Unsupported DBus type: '$value'");
}
}
void _encodeStruct(
BytesBuilder builder, List<DBusValue> values, Endian endian) {
var isVariable = false;
var endOffsets = <int>[];
var startOffset = builder.length;
var alignment = 0;
for (var value in values) {
_encode(builder, value, endian);
// Variable sized elements will have end offsets appended.
var valueSize = _getElementSize(value.signature.value);
if (value != values.last && valueSize == -1) {
isVariable = true;
endOffsets.add(builder.length - startOffset);
}
// Struct is alignent to largest element size.
if (valueSize == -1) {
alignment = -1;
} else if (alignment != -1 && valueSize > alignment) {
alignment = valueSize;
}
}
if (isVariable) {
// Calculate smallest size that can be used for offset values.
var dataLength = builder.length - startOffset;
var offsetSize = 1;
while (_getOffsetSize(dataLength + endOffsets.length * offsetSize) !=
offsetSize) {
offsetSize *= 2;
}
// Append en offsets of variable sized elements.
for (var i = endOffsets.length - 1; i >= 0; i--) {
_writeOffset(builder, endOffsets[i], offsetSize, endian);
}
} else {
// Fixed structures must be padded to their alignment.
if (alignment > 0) {
var offset = _align(builder.length, alignment);
for (var i = builder.length; i < offset; i++) {
builder.addByte(0);
}
}
}
}
void _encodeArray(BytesBuilder builder, String childType,
List<DBusValue> values, Endian endian) {
var endOffsets = <int>[];
var startOffset = builder.length;
for (var value in values) {
_encode(builder, value, endian);
endOffsets.add(builder.length - startOffset);
}
// If the elements are of variable length, then each end position needs to recorded.
var isVariable = _getElementSize(childType) == -1;
if (isVariable) {
// Calculate smallest size that can be used for offset values.
var dataLength = builder.length - startOffset;
var offsetSize = 1;
while (_getOffsetSize(dataLength + values.length * offsetSize) !=
offsetSize) {
offsetSize *= 2;
}
// Write the end offsets of each element.
for (var i = 0; i < values.length; i++) {
_writeOffset(builder, endOffsets[i], offsetSize, endian);
}
}
}
void _encodeDict(BytesBuilder builder, String keyType, String valueType,
Map<DBusValue, DBusValue> values, Endian endian) {
var endOffsets = <int>[];
var startOffset = builder.length;
for (var entry in values.entries) {
_encode(builder, DBusStruct([entry.key, entry.value]), endian);
endOffsets.add(builder.length - startOffset);
}
// If the elements are of variable length, then each end position needs to recorded.
var isVariable =
_getElementSize(keyType) == -1 || _getElementSize(valueType) == -1;
if (isVariable) {
// Calculate smallest size that can be used for offset values.
var dataLength = builder.length - startOffset;
var offsetSize = 1;
while (_getOffsetSize(dataLength + values.length * offsetSize) !=
offsetSize) {
offsetSize *= 2;
}
// Write the end offsets of each element.
for (var i = 0; i < values.length; i++) {
_writeOffset(builder, endOffsets[i], offsetSize, endian);
}
}
}
void _writeUint8(BytesBuilder builder, int value) {
builder.addByte(value);
}
void _writeUint16(BytesBuilder builder, int value, Endian endian) {
var buffer = Uint8List(2).buffer;
ByteData.view(buffer).setUint16(0, value, endian);
builder.add(buffer.asUint8List());
}
void _writeUint32(BytesBuilder builder, int value, Endian endian) {
var buffer = Uint8List(4).buffer;
ByteData.view(buffer).setUint32(0, value, endian);
builder.add(buffer.asUint8List());
}
void _writeUint64(BytesBuilder builder, int value, Endian endian) {
var buffer = Uint8List(8).buffer;
ByteData.view(buffer).setUint64(0, value, endian);
builder.add(buffer.asUint8List());
}
void _writeOffset(
BytesBuilder builder, int offset, int offsetSize, Endian endian) {
switch (offsetSize) {
case 1:
_writeUint8(builder, offset);
break;
case 2:
_writeUint16(builder, offset, endian);
break;
case 4:
_writeUint32(builder, offset, endian);
break;
case 8:
_writeUint64(builder, offset, endian);
break;
default:
throw ('Unsupported offset size $offsetSize');
}
}
DBusStruct _parseGVariantStruct(String type, ByteData data,
{required Endian endian}) {
if (!type.startsWith('(') || !type.endsWith(')')) {
throw ('Invalid struct type: $type');
}
var childTypes = DBusSignature(type.substring(1, type.length - 1))
.split()
.map((s) => s.value)
.toList();
var childSizes = childTypes.map((type) => _getElementSize(type)).toList();
// Check if the sizes of the elements can be determined before parsing.
// The last element can be variable, as it takes up the remaining space.
var variableSize = false;
for (var i = 0; i < childSizes.length - 1; i++) {
if (childSizes[i] == -1) {
variableSize = true;
break;
}
}
if (variableSize) {
return _parseGVariantVariableStruct(childTypes, data, endian: endian);
} else {
return _parseGVariantFixedStruct(childTypes, data, endian: endian);
}
}
DBusStruct _parseGVariantFixedStruct(List<String> childTypes, ByteData data,
{required Endian endian}) {
var children = <DBusValue>[];
var offset = 0;
for (var i = 0; i < childTypes.length; i++) {
var start = _align(offset, _getAlignment(childTypes[i]));
var size = _getElementSize(childTypes[i]);
if (size < 0) {
size = data.lengthInBytes - start;
}
children.add(decode(
childTypes[i], ByteData.sublistView(data, start, start + size),
endian: endian));
offset += size;
}
return DBusStruct(children);
}
DBusStruct _parseGVariantVariableStruct(
List<String> childTypes, ByteData data,
{required Endian endian}) {
var offsetSize = _getOffsetSize(data.lengthInBytes);
var dataEnd = data.lengthInBytes;
var children = <DBusValue>[];
var offset = 0;
for (var i = 0; i < childTypes.length; i++) {
var size = _getElementSize(childTypes[i]);
var start = _align(offset, _getAlignment(childTypes[i]));
int end;
if (size > 0) {
// Fixed elements
end = start + size;
} else if (i == childTypes.length - 1) {
// Last variable element ends where the data ends.
end = dataEnd;
} else {
// Read the offset from the end of the data.
end =
_getOffset(data, dataEnd - offsetSize, offsetSize, endian: endian);
if (end > dataEnd) {
throw ('Invalid element end offset in struct');
}
dataEnd -= offsetSize;
}
children.add(decode(childTypes[i], ByteData.sublistView(data, start, end),
endian: endian));
offset = end;
}
return DBusStruct(children);
}
DBusDict _parseGVariantDict(String type, ByteData data,
{required Endian endian}) {
if (!type.startsWith('a{') || !type.endsWith('}')) {
throw ('Invalid dict type: $type');
}
var childTypes = DBusSignature(type.substring(2, type.length - 1))
.split()
.map((s) => s.value)
.toList();
var keyType = childTypes[0];
var valueType = childTypes[1];
// Data is stored as an array, this could be optimised to avoid being unpacked and repacked as a dict.
var array =
_parseGVariantArray('($keyType$valueType)', data, endian: endian);
var values = <DBusValue, DBusValue>{};
for (var child in array.children) {
var keyValue = child as DBusStruct;
values[keyValue.children[0]] = keyValue.children[1];
}
return DBusDict(DBusSignature(keyType), DBusSignature(valueType), values);
}
DBusArray _parseGVariantArray(String childType, ByteData data,
{required Endian endian}) {
var elementSize = _getElementSize(childType);
if (elementSize > 0) {
return _parseGVariantFixedArray(childType, elementSize, data,
endian: endian);
} else {
return _parseGVariantVariableArray(childType, data, endian: endian);
}
}
DBusArray _parseGVariantFixedArray(
String childType, int elementSize, ByteData data,
{required Endian endian}) {
var arrayLength = data.lengthInBytes ~/ elementSize;
var children = <DBusValue>[];
for (var i = 0; i < arrayLength; i++) {
var start = i * elementSize;
var childData = ByteData.sublistView(data, start, start + elementSize);
children.add(decode(childType, childData, endian: endian));
}
return DBusArray(DBusSignature(childType), children);
}
DBusArray _parseGVariantVariableArray(String childType, ByteData data,
{required Endian endian}) {
// Get end of last element.
var offsetSize = _getOffsetSize(data.lengthInBytes);
int arrayLength;
if (data.lengthInBytes > 0) {
var lastOffset = _getOffset(
data, data.lengthInBytes - offsetSize, offsetSize,
endian: endian);
var dataLength = data.lengthInBytes - lastOffset;
if (dataLength < 0 || dataLength % offsetSize != 0) {
throw ('Invalid element end offset in array');
}
// Array size is the number of offsets after the last element.
arrayLength = dataLength ~/ offsetSize;
} else {
arrayLength = 0;
}
var children = <DBusValue>[];
var start = 0;
var offsetStart = data.lengthInBytes - offsetSize * arrayLength;
var childAlignment = _getAlignment(childType);
for (var i = 0; i < arrayLength; i++) {
var end = _getOffset(data, offsetStart + offsetSize * i, offsetSize,
endian: endian);
if (end > offsetStart) {
throw ('Invalid element end offset in array');
}
var childData = ByteData.sublistView(data, start, end);
children.add(decode(childType, childData, endian: endian));
start = _align(end, childAlignment);
}
return DBusArray(DBusSignature(childType), children);
}
DBusMaybe _parseGVariantMaybe(String childType, ByteData data,
{required Endian endian}) {
DBusValue? value;
if (data.lengthInBytes > 0) {
ByteData childData;
var childSize = _getElementSize(childType);
if (childSize == -1) {
if (data.getUint8(data.lengthInBytes - 1) != 0) {
throw ('Invalid padding byte on maybe GVariant');
}
childData = ByteData.sublistView(data, 0, data.lengthInBytes - 1);
} else {
childData = data;
}
value = decode(childType, childData, endian: endian);
}
return DBusMaybe(DBusSignature(childType), value);
}
int _align(int offset, int alignment) {
var x = offset % alignment;
return x == 0 ? offset : offset + (alignment - x);
}
int _getElementSize(String type) {
/// Containers are variable length.
if (type.startsWith('a') || type.startsWith('m')) {
return -1;
}
if (type.startsWith('(') || type.startsWith('{')) {
var childTypes = DBusSignature(type.substring(1, type.length - 1))
.split()
.map((s) => s.value);
var size = 0;
for (var type in childTypes) {
var s = _getElementSize(type);
if (s < 0) {
return -1;
}
size += s;
}
return size;
}
switch (type) {
case 'y': // byte
case 'b': // boolean
return 1;
case 'n': // int16
case 'q': // uint16
return 2;
case 'i': // int32
case 'u': // uint32
return 4;
case 'x': // int64
case 't': // uint64
case 'd': // double
return 8;
case 's': // string
case 'o': // object path
case 'g': // signature
case 'v': // variant
return -1; // variable size
default:
throw ArgumentError.value(type, 'type', 'Unknown type');
}
}
int _getAlignment(String type) {
if (type.startsWith('a') || type.startsWith('m')) {
return _getAlignment(type.substring(1));
}
if (type.startsWith('(') || type.startsWith('{')) {
var childTypes = DBusSignature(type.substring(1, type.length - 1))
.split()
.map((s) => s.value);
var alignment = 1;
for (var type in childTypes) {
var a = _getAlignment(type);
if (a > alignment) {
alignment = a;
}
}
return alignment;
}
switch (type) {
case 'y': // byte
case 'b': // boolean
case 's': // string
case 'o': // object path
case 'g': // signature
return 1;
case 'n': // int16
case 'q': // uint16
return 2;
case 'i': // int32
case 'u': // uint32
return 4;
case 'x': // int64
case 't': // uint64
case 'd': // double
case 'v': // variant
return 8;
default:
throw ArgumentError.value(type, 'type', 'Unknown type');
}
}
int _getOffsetSize(int size) {
if (size > 0xffffffff) {
return 8;
} else if (size > 0xffff) {
return 4;
} else if (size > 0xff) {
return 2;
} else {
return 1;
}
}
int _getOffset(ByteData data, int offset, int offsetSize,
{required Endian endian}) {
switch (offsetSize) {
case 1:
return data.getUint8(offset);
case 2:
return data.getUint16(offset, endian);
case 4:
return data.getUint32(offset, endian);
case 8:
return data.getUint64(offset, endian);
default:
throw ('Unknown offset size $offsetSize');
}
}
}

@ -1,188 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:dbus/dbus.dart';
import 'gvariant_binary_codec.dart';
class GVariantDatabase {
final String path;
GVariantDatabase(this.path);
Future<List<String>> list({String? dir, String? type}) async {
var root = await _loadRootTable();
return root.list(dir: dir, type: type);
}
Future<DBusValue?> lookup(String key) async {
var root = await _loadRootTable();
return root.lookup(key);
}
Future<GVariantDatabaseTable?> lookupTable(String key) async {
var root = await _loadRootTable();
return root.lookupTable(key);
}
Future<GVariantDatabaseTable> _loadRootTable() async {
var rawData = await File(path).readAsBytes();
var data = ByteData.view(rawData.buffer);
// Check for correct signature and detect endianess.
var signature0 = data.getUint32(0, Endian.little);
var signature1 = data.getUint32(4, Endian.little);
var version = data.getUint32(8, Endian.little);
Endian endian;
if (signature0 == 1918981703 && signature1 == 1953390953 && version == 0) {
endian = Endian.little;
/*} else if (signature0 == && signature1 == && version == 0) {
endian = Endian.big;*/
} else {
throw ('Invalid signature');
}
var rootStart = data.getUint32(16, endian);
var rootEnd = data.getUint32(20, endian);
return GVariantDatabaseTable(
ByteData.sublistView(data, rootStart, rootEnd), data, endian);
}
}
class GVariantDatabaseTable {
final ByteData data;
final ByteData fullData;
final Endian endian;
late final int _nBloomWords;
//late final int _bloomOffset; // FIXME
late final int _nBuckets;
late final int _bucketOffset;
late final int _nHashItems;
late final int _hashOffset;
GVariantDatabaseTable(this.data, this.fullData, this.endian) {
var offset = 0;
_nBloomWords = data.getUint32(offset + 0, endian) & 0x3ffffff;
_nBuckets = data.getUint32(offset + 4, endian);
offset += 8;
//_bloomOffset = offset; // FIXME
offset += _nBloomWords * 4;
_bucketOffset = offset;
offset += _nBuckets * 4;
_hashOffset = offset;
_nHashItems = (data.lengthInBytes - _hashOffset) ~/ 24;
}
List<String> list({String? dir, String? type}) {
var dirHash = dir != null ? _hashKey(dir) : 0;
var children = <String>[];
for (var i = 0; i < _nHashItems; i++) {
var parent = _getParent(i);
if (type != null && _getType(i) != type) {
continue;
}
if (dir != null) {
if (parent == 0xffffffff || _getHash(parent) != dirHash) {
continue;
}
} else {
if (parent != 0xffffffff) {
continue;
}
}
children.add(_getKey(i));
}
return children;
}
DBusValue? lookup(String key) {
var value = _lookup(key, type: 'v');
if (value == null) {
return null;
}
var codec = GVariantBinaryCodec();
return (codec.decode('v', value, endian: endian) as DBusVariant).value;
}
GVariantDatabaseTable? lookupTable(String key) {
var value = _lookup(key, type: 'H');
if (value == null) {
return null;
}
return GVariantDatabaseTable(value, fullData, endian);
}
ByteData? _lookup(String key, {required String type}) {
var hash = _hashKey(key);
var bucket = hash % _nBuckets;
var start = data.getUint32(_bucketOffset + bucket * 4, endian);
var end = bucket + 1 < _nBuckets
? data.getUint32(_bucketOffset + (bucket + 1) * 4, endian)
: _nHashItems;
start = 0;
end = _nHashItems;
for (var i = start; i < end; i++) {
if (_getHash(i) == hash &&
_getKey(i, recurse: true) == key &&
_getType(i) == type) {
return _getValue(i);
}
}
return null;
}
/// Gets the hash for a table key.
int _hashKey(String key) {
var hashValue = 5381;
for (var o in utf8.encode(key)) {
// Use bytes as signed 8 bit numbers.
if (o >= 128) {
o -= 256;
}
hashValue = (hashValue * 33 + o) & 0xffffffff;
}
return hashValue;
}
int _getHash(int index) {
return data.getUint32(_getHashItemOffset(index) + 0, endian);
}
int _getParent(int index) {
return data.getUint32(_getHashItemOffset(index) + 4, endian);
}
/// Gets the key name for a hash item.
String _getKey(int index, {bool recurse = false}) {
var parent = recurse ? _getParent(index) : 0xffffffff;
var parentKey =
parent != 0xffffffff ? _getKey(parent, recurse: recurse) : '';
var offset = _getHashItemOffset(index);
var keyStart = data.getUint32(offset + 8, endian);
var keySize = data.getUint16(offset + 12, endian);
return parentKey + utf8.decode(data.buffer.asUint8List(keyStart, keySize));
}
String _getType(int index) {
return ascii.decode([data.getUint8(_getHashItemOffset(index) + 14)]);
}
ByteData _getValue(int index) {
var offset = _getHashItemOffset(index);
var valueStart = data.getUint32(offset + 16, endian);
var valueEnd = data.getUint32(offset + 20, endian);
return ByteData.sublistView(fullData, valueStart, valueEnd);
}
int _getHashItemOffset(int index) {
return _hashOffset + index * 24;
}
}

@ -1,484 +0,0 @@
import 'package:dbus/dbus.dart';
class GVariantTextCodec {
GVariantTextCodec();
/// Encode a value using GVariant text format.
String encode(DBusValue value) {
var buffer = StringBuffer();
_encode(buffer, value);
return buffer.toString();
}
/// Parse a single GVariant value. [type] is expected to be a valid single type.
DBusValue decode(String type, String data) {
var buffer = _DecodeBuffer(data);
var value = _decode(type, buffer);
buffer.consumeWhitespace();
if (!buffer.isEmpty) {
throw "Unexpected data after encoded GVariant: '${buffer.data.substring(buffer.offset)}'";
}
return value;
}
void _encode(StringBuffer buffer, DBusValue value) {
if (value is DBusBoolean) {
buffer.write(value.value ? 'true' : 'false');
} else if (value is DBusByte) {
buffer.write('0x' + value.value.toRadixString(16).padLeft(2, '0'));
} else if (value is DBusInt16) {
buffer.write(value.value.toString());
} else if (value is DBusUint16) {
buffer.write(value.value.toString());
} else if (value is DBusInt32) {
buffer.write(value.value.toString());
} else if (value is DBusUint32) {
buffer.write(value.value.toString());
} else if (value is DBusInt64) {
buffer.write(value.value.toString());
} else if (value is DBusUint64) {
buffer.write(value.value.toString());
} else if (value is DBusDouble) {
buffer.write(value.value.toString());
} else if (value is DBusObjectPath) {
buffer.write('objectpath ');
_writeString(buffer, value.value);
} else if (value is DBusSignature) {
buffer.write('signature ');
_writeString(buffer, value.value);
} else if (value is DBusString) {
_writeString(buffer, value.value);
} else if (value is DBusVariant) {
buffer.write('<');
_encode(buffer, value.value);
buffer.write('>');
} else if (value is DBusMaybe) {
if (value.value != null) {
var childBuffer = StringBuffer();
_encode(childBuffer, value.value!);
var childText = childBuffer.toString();
if (childText.endsWith('nothing')) {
buffer.write('just ');
}
buffer.write(childText);
} else {
buffer.write('nothing');
}
} else if (value is DBusStruct) {
buffer.write('(');
for (var child in value.children) {
if (child != value.children.first) {
buffer.write(', ');
}
_encode(buffer, child);
}
buffer.write(')');
} else if (value is DBusArray) {
buffer.write('[');
for (var child in value.children) {
if (child != value.children.first) {
buffer.write(', ');
}
_encode(buffer, child);
}
buffer.write(']');
} else if (value is DBusDict) {
buffer.write('{');
var first = true;
for (var entry in value.children.entries) {
if (!first) {
buffer.write(', ');
}
first = false;
_encode(buffer, entry.key);
buffer.write(': ');
_encode(buffer, entry.value);
}
buffer.write('}');
} else {
throw ("Unsupported DBus type: '$value'");
}
}
void _writeString(StringBuffer buffer, String value) {
var quote = value.contains("'") ? '"' : "'";
buffer.write(quote);
for (var rune in value.runes) {
switch (rune) {
case 7: // bell
buffer.write(r'\a');
break;
case 8: // backspace
buffer.write(r'\b');
break;
case 9: // tab
buffer.write(r'\t');
break;
case 10: // newline
buffer.write(r'\n');
break;
case 11: // vertical tab
buffer.write(r'\v');
break;
case 12: // form feed
buffer.write(r'\f');
break;
case 13: // carriage return
buffer.write(r'\r');
break;
case 34: // double quote
buffer.write(quote == '"' ? r'\"' : '"');
break;
case 39: // single quote
buffer.write(quote == "'" ? r"\'" : "'");
break;
case 92: // backslash
buffer.write(r'\\');
break;
default:
// There's not a dart method to check if a character is "printable", so we use:
// 00 - 1f - C0 control
// 7f - delete
// 80 - 9f - C1 control
// e000 - f8ff - private use
// fff0 - ffff - specials
// 1ff80 - 1ffff - unassigned
// 2ff80 - 2ffff - unassigned
// 3ff80 - 3ffff - unassigned
// 4ff80 - 4ffff - unassigned
// 5ff80 - 5ffff - unassigned
// 6ff80 - 6ffff - unassigned
// 7ff80 - 7ffff - unassigned
// 8ff80 - 8ffff - unassigned
// 9ff80 - 9ffff - unassigned
// aff80 - affff - unassigned
// bff80 - bffff - unassigned
// cff80 - cffff - unassigned
// dff80 - dffff - unassigned
// eff80 - effff - unassigned
// f0000 - fffff - supplementary private use area A
// 100000 - 10fffd - supplementary private use area B
if (rune <= 0x1f ||
(rune >= 0x7f && rune <= 0x9f) ||
(rune >= 0xe000 && rune <= 0xf8ff) ||
(rune >= 0xfff0 && rune <= 0xffff) ||
(rune >= 0x1ff80 && rune <= 0x1ffff) ||
(rune >= 0x2ff80 && rune <= 0x2ffff) ||
(rune >= 0x3ff80 && rune <= 0x3ffff) ||
(rune >= 0x4ff80 && rune <= 0x4ffff) ||
(rune >= 0x5ff80 && rune <= 0x5ffff) ||
(rune >= 0x6ff80 && rune <= 0x6ffff) ||
(rune >= 0x7ff80 && rune <= 0x7ffff) ||
(rune >= 0x8ff80 && rune <= 0x8ffff) ||
(rune >= 0x9ff80 && rune <= 0x9ffff) ||
(rune >= 0xaff80 && rune <= 0xaffff) ||
(rune >= 0xbff80 && rune <= 0xbffff) ||
(rune >= 0xcff80 && rune <= 0xcffff) ||
(rune >= 0xdff80 && rune <= 0xdffff) ||
(rune >= 0xeff80 && rune <= 0xeffff) ||
(rune >= 0x100000 && rune <= 0x10fffd)) {
int padding;
String prefix;
if (rune <= 0xffff) {
padding = 4;
prefix = 'u';
} else {
padding = 8;
prefix = 'U';
}
var hex = rune.toRadixString(16).padLeft(padding, '0');
buffer.write('\\$prefix$hex');
} else {
buffer.writeCharCode(rune);
}
}
}
buffer.write(quote);
}
DBusValue _decode(String type, _DecodeBuffer buffer) {
buffer.consumeWhitespace();
// struct
if (type.startsWith('(')) {
return _decodeStruct(type, buffer);
}
// array / dict
if (type.startsWith('a')) {
if (type.startsWith('a{')) {
return _decodeDict(type, buffer);
} else {
return _decodeArray(type, buffer);
}
}
// maybe
if (type.startsWith('m')) {
var childType = type.substring(1);
DBusValue? value;
if (!buffer.consume('nothing')) {
value = _decode(childType, buffer);
}
return DBusMaybe(DBusSignature(childType), value);
}
switch (type) {
case 'b': // boolean
bool value;
if (buffer.consume('true')) {
value = true;
} else if (buffer.consume('false')) {
value = false;
} else {
throw 'Invalid boolean encoding';
}
return DBusBoolean(value);
case 'y': // byte
return DBusByte(_decodeInteger(buffer));
case 'n': // int16
return DBusInt16(_decodeInteger(buffer));
case 'q': // uint16
return DBusUint16(_decodeInteger(buffer));
case 'i': // int32
return DBusInt32(_decodeInteger(buffer));
case 'u': // uint32
return DBusUint32(_decodeInteger(buffer));
case 'x': // int64
return DBusInt64(_decodeInteger(buffer));
case 't': // uint64
return DBusUint64(_decodeInteger(buffer));
case 'd': // double
return DBusDouble(_decodeDouble(buffer));
case 's': // string
return DBusString(_decodeString(buffer));
case 'o': // object path
if (!buffer.consume('objectpath ')) {
throw 'Invalid object path encoding';
}
return DBusObjectPath(_decodeString(buffer));
case 'g': // signature
if (!buffer.consume('signature ')) {
throw 'Invalid signature encoding';
}
return DBusSignature(_decodeString(buffer));
default:
throw ("Unsupported GVariant type: '$type'");
}
}
int _decodeInteger(_DecodeBuffer buffer) {
var end = buffer.offset;
if (buffer.data.startsWith('-')) {
end++;
} else if (buffer.data.startsWith('0x')) {
end += 2;
}
while (end < buffer.data.length &&
'0123456789abcdefABCDEF'.contains(buffer.data[end])) {
end++;
}
var value = int.parse(buffer.data.substring(buffer.offset, end));
buffer.offset = end;
return value;
}
double _decodeDouble(_DecodeBuffer buffer) {
var end = buffer.offset;
if (buffer.data.startsWith('-', end)) {
end++;
}
while (
end < buffer.data.length && '0123456789'.contains(buffer.data[end])) {
end++;
}
if (buffer.data.startsWith('.', end)) {
end++;
}
while (
end < buffer.data.length && '0123456789'.contains(buffer.data[end])) {
end++;
}
var value = double.parse(buffer.data.substring(buffer.offset, end));
buffer.offset = end;
return value;
}
String _decodeString(_DecodeBuffer buffer) {
var output = StringBuffer();
if (buffer.isEmpty) {
throw 'No data for string';
}
var quote = buffer.data[buffer.offset];
if (quote != "'" && quote != '"') {
throw 'Missing start quote on string';
}
var end = buffer.offset + 1;
while (end < buffer.data.length) {
var c = buffer.data[end];
end++;
if (c == quote) {
buffer.offset = end;
return output.toString();
} else if (c == r'\') {
if (end == buffer.data.length - 1) {
throw 'Escape character at end of string';
}
var escapeChar = buffer.data[end];
end++;
switch (escapeChar) {
case 'a': // bell
output.writeCharCode(7);
break;
case 'b': // backspace
output.writeCharCode(8);
break;
case 't': // tab
output.writeCharCode(9);
break;
case 'n': // newline
output.writeCharCode(10);
break;
case 'v': // vertical tab
output.writeCharCode(11);
break;
case 'f': // form feed
output.writeCharCode(12);
break;
case 'r': // carriage return
output.writeCharCode(13);
break;
case 'u':
if (end + 4 > buffer.data.length) {
throw ('Not enough space for unicode character');
}
output.writeCharCode(
int.parse(buffer.data.substring(end, end + 4), radix: 16));
end += 4;
break;
case 'U':
if (end + 8 > buffer.data.length) {
throw ('Not enough space for unicode character');
}
output.writeCharCode(
int.parse(buffer.data.substring(end, end + 8), radix: 16));
end += 8;
break;
case '"':
case "'":
case r'\':
default:
output.write(escapeChar);
break;
}
} else {
output.write(c);
}
}
throw 'Missing end quote on string';
}
DBusStruct _decodeStruct(String type, _DecodeBuffer buffer) {
if (!buffer.consume('(')) {
throw 'Missing start of struct';
}
var signature = DBusSignature(type.substring(1, type.length - 1));
var children = <DBusValue>[];
var first = true;
for (var childSignature in signature.split()) {
if (!first && !buffer.consume(',')) {
throw ('Missing comma between struct elements');
}
first = false;
buffer.consumeWhitespace();
children.add(_decode(childSignature.value, buffer));
}
if (!buffer.consume(')')) {
throw 'Missing end of struct';
}
return DBusStruct(children);
}
DBusArray _decodeArray(String type, _DecodeBuffer buffer) {
if (!buffer.consume('[')) {
throw 'Missing start of array';
}
var childType = type.substring(1);
var children = <DBusValue>[];
var first = true;
while (!buffer.isEmpty) {
if (buffer.consume(']')) {
return DBusArray(DBusSignature(childType), children);
}
if (!first && !buffer.consume(',')) {
throw ('Missing comma between array elements');
}
first = false;
buffer.consumeWhitespace();
children.add(_decode(childType, buffer));
}
throw 'Missing end of array';
}
DBusDict _decodeDict(String type, _DecodeBuffer buffer) {
var signatures = DBusSignature(type.substring(2, type.length - 1)).split();
var keyType = signatures[0].value;
var valueType = signatures[1].value;
if (!buffer.consume('{')) {
throw 'Missing start of dict';
}
var children = <DBusValue, DBusValue>{};
var first = true;
while (!buffer.isEmpty) {
if (buffer.consume('}')) {
return DBusDict(
DBusSignature(keyType), DBusSignature(valueType), children);
}
if (!first && !buffer.consume(',')) {
throw ('Missing comma between dict elements');
}
first = false;
buffer.consumeWhitespace();
var key = _decode(keyType, buffer);
buffer.consumeWhitespace();
if (!first && !buffer.consume(':')) {
throw ('Missing colon between dict key and value');
}
buffer.consumeWhitespace();
var value = _decode(valueType, buffer);
children[key] = value;
}
throw 'Missing end of dict';
}
}
class _DecodeBuffer {
String data;
int offset = 0;
_DecodeBuffer(this.data);
bool get isEmpty => offset >= data.length;
void consumeWhitespace() {
while (offset < data.length && data.startsWith(' ', offset)) {
offset++;
}
}
bool consume(String value) {
if (!data.startsWith(value, offset)) {
return false;
}
offset += value.length;
return true;
}
}

@ -1,21 +0,0 @@
name: gsettings
version: 0.2.0
publish_to: none
description:
GSettings client
homepage: https://github.com/canonical/gsettings.dart
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
dbus: ^0.6.0
xdg_directories:
path: ../xdg_directories
dev_dependencies:
pedantic: ^1.9.0
test: ^1.16.8

@ -1,7 +0,0 @@
.DS_Store
.dart_tool/
.packages
.pub/
build/

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: e491544588e8d34fdf31d5f840b4649850ef167a
channel: master
project_type: plugin

@ -1,66 +0,0 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google Inc.
The Chromium Authors
German Saprykin <saprykin.h@gmail.com>
Benjamin Sauer <sauer.benjamin@gmail.com>
larsenthomasj@gmail.com
Ali Bitek <alibitek@protonmail.ch>
Pol Batlló <pol.batllo@gmail.com>
Anatoly Pulyaevskiy
Hayden Flinner <haydenflinner@gmail.com>
Stefano Rodriguez <hlsroddy@gmail.com>
Salvatore Giordano <salvatoregiordanoo@gmail.com>
Brian Armstrong <brian@flutter.institute>
Paul DeMarco <paulmdemarco@gmail.com>
Fabricio Nogueira <feufeu@gmail.com>
Simon Lightfoot <simon@devangels.london>
Ashton Thomas <ashton@acrinta.com>
Thomas Danner <thmsdnnr@gmail.com>
Diego Velásquez <diego.velasquez.lopez@gmail.com>
Hajime Nakamura <nkmrhj@gmail.com>
Tuyển Vũ Xuân <netsoft1985@gmail.com>
Miguel Ruivo <miguel@miguelruivo.com>
Sarthak Verma <sarthak@artiosys.com>
Mike Diarmid <mike@invertase.io>
Invertase <oss@invertase.io>
Elliot Hesp <elliot@invertase.io>
Vince Varga <vince.varga@smaho.com>
Aawaz Gyawali <awazgyawali@gmail.com>
EUI Limited <ian.evans3@admiralgroup.co.uk>
Katarina Sheremet <katarina@sheremet.ch>
Thomas Stockx <thomas@stockxit.com>
Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
Ozkan Eksi <ozeksi@gmail.com>
Rishab Nayak <rishab@bu.edu>
ko2ic <ko2ic.dev@gmail.com>
Jonathan Younger <jonathan@daikini.com>
Jose Sanchez <josesm82@gmail.com>
Debkanchan Samadder <debu.samadder@gmail.com>
Audrius Karosevicius <audrius.karosevicius@gmail.com>
Lukasz Piliszczuk <lukasz@intheloup.io>
SoundReply Solutions GmbH <ch@soundreply.com>
Rafal Wachol <rwachol@gmail.com>
Pau Picas <pau.picas@gmail.com>
Christian Weder <chrstian.weder@yapeal.ch>
Alexandru Tuca <salexandru.tuca@outlook.com>
Christian Weder <chrstian.weder@yapeal.ch>
Rhodes Davis Jr. <rody.davis.jr@gmail.com>
Luigi Agosti <luigi@tengio.com>
Quentin Le Guennec <quentin@tengio.com>
Koushik Ravikumar <koushik@tengio.com>
Nissim Dsilva <nissim@tengio.com>
Giancarlo Rocha <giancarloiff@gmail.com>
Ryo Miyake <ryo@miyake.id>
Théo Champion <contact.theochampion@gmail.com>
Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
Eitan Schwartz <eshvartz@gmail.com>
Chris Rutkowski <chrisrutkowski89@gmail.com>
Juan Alvarez <juan.alvarez@resideo.com>
Aleksandr Yurkovskiy <sanekyy@gmail.com>
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>

@ -1,42 +0,0 @@
## 2.0.2
* Updated installation instructions in README.
## 2.0.1
* Add `implements` to pubspec.yaml.
* Add `registerWith` method to the main Dart class.
## 2.0.0
* Migrate to null safety.
## 0.1.1+3
* Update Flutter SDK constraint.
## 0.1.1+2
* Log errors in the example when calls to the `path_provider` fail.
## 0.1.1+1
* Check in linux/ directory for example/
## 0.1.1 - NOT PUBLISHED
* Reverts changes on 0.1.0, which broke the tree.
## 0.1.0 - NOT PUBLISHED
* This release updates getApplicationSupportPath to use the application ID instead of the executable name.
* No migration is provided, so any older apps that were using this path will now have a different directory.
## 0.0.1+2
* This release updates the example to depend on the endorsed plugin rather than relative path
## 0.0.1+1
* This updates the readme and pubspec and example to reflect the endorsement of this implementation of `path_provider`
## 0.0.1
* The initial implementation of path_provider for Linux
* Implements getApplicationSupportPath, getApplicationDocumentsPath, getDownloadsPath, and getTemporaryPath

@ -1,25 +0,0 @@
Copyright 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,11 +0,0 @@
# path\_provider\_linux
The linux implementation of [`path_provider`].
## Usage
This package is [endorsed][2], which means you can simply use `path_provider`
normally. This package will be automatically included in your app when you do.
[1]: https://pub.dev/packages/path_provider
[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin

@ -1,49 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:xdg_directories/xdg_directories.dart';
XDGDirectories xdg = XDGDirectories();
/// The linux implementation of [PathProviderPlatform]
///
/// This class implements the `package:path_provider` functionality for linux
class PathProviderLinux extends PathProviderPlatform {
/// Registers this class as the default instance of [PathProviderPlatform]
static void registerWith() {
PathProviderPlatform.instance = PathProviderLinux();
}
@override
Future<String?> getTemporaryPath() {
return Future<String?>.value('/tmp');
}
@override
Future<String?> getApplicationSupportPath() async {
final String processName = path.basenameWithoutExtension(
await File('/proc/self/exe').resolveSymbolicLinks());
final Directory directory =
Directory(path.join(xdg.dataHome.path, processName));
// Creating the directory if it doesn't exist, because mobile implementations assume the directory exists
if (!directory.existsSync()) {
await directory.create(recursive: true);
}
return directory.path;
}
@override
Future<String?> getApplicationDocumentsPath() {
return Future<String?>.value(xdg.getUserDirectory('DOCUMENTS')?.path);
}
@override
Future<String?> getDownloadsPath() {
return Future<String?>.value(xdg.getUserDirectory('DOWNLOAD')?.path);
}
}

@ -1,30 +0,0 @@
name: path_provider_linux
description: Linux implementation of the path_provider plugin
repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
version: 2.0.2
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
flutter:
plugin:
implements: path_provider
platforms:
linux:
dartPluginClass: PathProviderLinux
pluginClass: none
dependencies:
flutter:
sdk: flutter
path: ^1.8.0
path_provider_platform_interface: ^2.0.0
xdg_directories:
path: ../xdg_directories
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.10.0

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: d8c0deb1b6c116be79ceeca005f893be34ab5df2
channel: master
project_type: package

@ -1,15 +0,0 @@
## 0.2.0
* Migrated to null safety.
## 0.1.2
* Broaden dependencies to allow nullsafety version of process, meta, and path to be OK.
## 0.1.1
* Remove flutter, flutter_test from pubspec dependencies.
## 0.1.0
* Initial release includes all the features described in the README.md

@ -1,50 +0,0 @@
# `xdg_directories`
A Dart package for reading XDG directory configuration information on Linux.
## Getting Started
On Linux, `xdg` is a system developed by [freedesktop.org](freedesktop.org), a
project to work on interoperability and shared base technology for free software
desktop environments for Linux.
This Dart package can be used to determine the directory configuration
information defined by `xdg`, such as where the Documents or Desktop directories
are. These are called "user directories" and are defined in configuration file
in the user's home directory.
See [this wiki](https://wiki.archlinux.org/index.php/XDG_Base_Directory) for
more details of the XDG Base Directory implementation.
To use this package, the basic XDG values for the following are available via a Dart API:
- `dataHome` - The single base directory relative to which user-specific data
files should be written. (Corresponds to `$XDG_DATA_HOME`).
- `configHome` - The a single base directory relative to which user-specific
configuration files should be written. (Corresponds to `$XDG_CONFIG_HOME`).
- `dataDirs` - The list of preference-ordered base directories relative to
which data files should be searched. (Corresponds to `$XDG_DATA_DIRS`).
- `configDirs` - The list of preference-ordered base directories relative to
which configuration files should be searched. (Corresponds to
`$XDG_CONFIG_DIRS`).
- `cacheHome` - The base directory relative to which user-specific
non-essential (cached) data should be written. (Corresponds to
`$XDG_CACHE_HOME`).
- `runtimeDir` - The base directory relative to which user-specific runtime
files and other file objects should be placed. (Corresponds to
`$XDG_RUNTIME_DIR`).
- `getUserDirectoryNames()` - Returns a set of the names of user directories
defined in the `xdg` configuration files.
- `getUserDirectory(String dirName)` - Gets the value of the user dir with the
given name. Requesting a user dir that doesn't exist returns `null`. The
`dirName` argument is case-insensitive. See [this
wiki](https://wiki.archlinux.org/index.php/XDG_user_directories) for more
details and what values of `dirName` might be available.

@ -1,68 +0,0 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library xdg_directories;
import 'package:universal_io/io.dart';
import 'xdg_directories_web.dart' if (dart.library.io) 'xdg_directories_linux.dart';
typedef EnvironmentAccessor = String? Function(String envVar);
abstract class XDGDirectories {
factory XDGDirectories() => getXDGDirectories();
/// The base directory relative to which user-specific
/// non-essential (cached) data should be written. (Corresponds to
/// `$XDG_CACHE_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get cacheHome;
/// The list of preference-ordered base directories relative to
/// which configuration files should be searched. (Corresponds to
/// `$XDG_CONFIG_DIRS`).
///
/// Throws [StateError] if the HOME environment variable is not set.
List<Directory> get configDirs;
/// The a single base directory relative to which user-specific
/// configuration files should be written. (Corresponds to `$XDG_CONFIG_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get configHome;
/// The list of preference-ordered base directories relative to
/// which data files should be searched. (Corresponds to `$XDG_DATA_DIRS`).
///
/// Throws [StateError] if the HOME environment variable is not set.
List<Directory> get dataDirs;
/// The base directory relative to which user-specific data files should be
/// written. (Corresponds to `$XDG_DATA_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get dataHome;
/// The base directory relative to which user-specific runtime
/// files and other file objects should be placed. (Corresponds to
/// `$XDG_RUNTIME_DIR`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory? get runtimeDir;
/// Gets the xdg user directory named by `dirName`.
///
/// Use [getUserDirectoryNames] to find out the list of available names.
Directory? getUserDirectory(String dirName);
/// Gets the set of user directory names that xdg knows about.
///
/// These are not paths, they are names of xdg values. Call [getUserDirectory]
/// to get the associated directory.
///
/// These are the names of the variables in "[configHome]/user-dirs.dirs", with
/// the `XDG_` prefix removed and the `_DIR` suffix removed.
Set<String> getUserDirectoryNames();
}

@ -1,154 +0,0 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library xdg_directories;
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:process/process.dart';
import 'xdg_directories.dart';
XDGDirectories getXDGDirectories() => XDGDirectoriesLinux();
class XDGDirectoriesLinux implements XDGDirectories {
EnvironmentAccessor _getenv = (String value) => Platform.environment[value];
ProcessManager _processManager = const LocalProcessManager();
List<Directory> _directoryListFromEnvironment(String envVar, List<Directory> fallback) {
ArgumentError.checkNotNull(envVar);
ArgumentError.checkNotNull(fallback);
final String? value = _getenv(envVar);
if (value == null || value.isEmpty) {
return fallback;
}
return value.split(':').where((String value) {
return value.isNotEmpty;
}).map<Directory>((String entry) {
return Directory(entry);
}).toList();
}
Directory? _directoryFromEnvironment(String envVar) {
ArgumentError.checkNotNull(envVar);
final String? value = _getenv(envVar);
if (value == null || value.isEmpty) {
return null;
}
return Directory(value);
}
Directory _directoryFromEnvironmentWithFallback(String envVar, String fallback) {
ArgumentError.checkNotNull(envVar);
final String? value = _getenv(envVar);
if (value == null || value.isEmpty) {
return _getDirectory(fallback);
}
return Directory(value);
}
// Creates a Directory from a fallback path.
Directory _getDirectory(String subdir) {
ArgumentError.checkNotNull(subdir);
assert(subdir.isNotEmpty);
final String? homeDir = _getenv('HOME');
if (homeDir == null || homeDir.isEmpty) {
throw StateError('The "HOME" environment variable is not set. This package (and POSIX) '
'requires that HOME be set.');
}
return Directory(path.joinAll(<String>[homeDir, subdir]));
}
/// The base directory relative to which user-specific
/// non-essential (cached) data should be written. (Corresponds to
/// `$XDG_CACHE_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get cacheHome => _directoryFromEnvironmentWithFallback('XDG_CACHE_HOME', '.cache');
/// The list of preference-ordered base directories relative to
/// which configuration files should be searched. (Corresponds to
/// `$XDG_CONFIG_DIRS`).
///
/// Throws [StateError] if the HOME environment variable is not set.
List<Directory> get configDirs {
return _directoryListFromEnvironment(
'XDG_CONFIG_DIRS',
<Directory>[Directory('/etc/xdg')],
);
}
/// The a single base directory relative to which user-specific
/// configuration files should be written. (Corresponds to `$XDG_CONFIG_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get configHome => _directoryFromEnvironmentWithFallback('XDG_CONFIG_HOME', '.config');
/// The list of preference-ordered base directories relative to
/// which data files should be searched. (Corresponds to `$XDG_DATA_DIRS`).
///
/// Throws [StateError] if the HOME environment variable is not set.
List<Directory> get dataDirs {
return _directoryListFromEnvironment(
'XDG_DATA_DIRS',
<Directory>[Directory('/usr/local/share'), Directory('/usr/share')],
);
}
/// The base directory relative to which user-specific data files should be
/// written. (Corresponds to `$XDG_DATA_HOME`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory get dataHome => _directoryFromEnvironmentWithFallback('XDG_DATA_HOME', '.local/share');
/// The base directory relative to which user-specific runtime
/// files and other file objects should be placed. (Corresponds to
/// `$XDG_RUNTIME_DIR`).
///
/// Throws [StateError] if the HOME environment variable is not set.
Directory? get runtimeDir => _directoryFromEnvironment('XDG_RUNTIME_DIR');
/// Gets the xdg user directory named by `dirName`.
///
/// Use [getUserDirectoryNames] to find out the list of available names.
Directory? getUserDirectory(String dirName) {
final ProcessResult result = _processManager.runSync(
<String>['xdg-user-dir', dirName],
includeParentEnvironment: true,
stdoutEncoding: utf8,
);
final String path = result.stdout.split('\n')[0];
return Directory(path);
}
/// Gets the set of user directory names that xdg knows about.
///
/// These are not paths, they are names of xdg values. Call [getUserDirectory]
/// to get the associated directory.
///
/// These are the names of the variables in "[configHome]/user-dirs.dirs", with
/// the `XDG_` prefix removed and the `_DIR` suffix removed.
Set<String> getUserDirectoryNames() {
final File configFile = File(path.join(configHome.path, 'user-dirs.dirs'));
List<String> contents;
try {
contents = configFile.readAsLinesSync();
} on FileSystemException {
return const <String>{};
}
final Set<String> result = <String>{};
final RegExp dirRegExp = RegExp(r'^\s*XDG_(?<dirname>[^=]*)_DIR\s*=\s*(?<dir>.*)\s*$');
for (String line in contents) {
final RegExpMatch? match = dirRegExp.firstMatch(line);
if (match == null) {
continue;
}
result.add(match.namedGroup('dirname')!);
}
return result;
}
}

@ -1,41 +0,0 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library xdg_directories;
import 'package:universal_io/io.dart';
import 'xdg_directories.dart';
XDGDirectories getXDGDirectories() => XDGDirectoriesWeb();
class XDGDirectoriesWeb implements XDGDirectories {
@override
Directory get cacheHome => throw UnimplementedError();
@override
List<Directory> get configDirs => throw UnimplementedError();
@override
Directory get configHome => throw UnimplementedError();
@override
List<Directory> get dataDirs => throw UnimplementedError();
@override
Directory get dataHome => throw UnimplementedError();
@override
Directory? getUserDirectory(String dirName) {
throw UnimplementedError();
}
@override
Set<String> getUserDirectoryNames() {
throw UnimplementedError();
}
@override
Directory? get runtimeDir => throw UnimplementedError();
}

@ -1,16 +0,0 @@
name: xdg_directories
description: A Dart package for reading XDG directory configuration information on Linux.
version: 0.2.0
homepage: https://github.com/flutter/packages/tree/master/packages/xdg_directories
environment:
sdk: ">=2.12.0-0 <3.0.0"
dependencies:
meta: ^1.3.0
path: ^1.8.0
process: ^4.0.0
universal_io: ^2.0.4
dev_dependencies:
test: ^1.16.0

@ -0,0 +1,40 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library xdg_directories;
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:universal_io/io.dart';
class XDGDirectoriesPlugin {
static void registerWith(Registrar registrar) => null;
}
@override
Directory get cacheHome => throw UnimplementedError();
@override
List<Directory> get configDirs => throw UnimplementedError();
@override
Directory get configHome => throw UnimplementedError();
@override
List<Directory> get dataDirs => throw UnimplementedError();
@override
Directory get dataHome => throw UnimplementedError();
@override
Directory? getUserDirectory(String dirName) {
throw UnimplementedError();
}
@override
Set<String> getUserDirectoryNames() {
throw UnimplementedError();
}
@override
Directory? get runtimeDir => throw UnimplementedError();

@ -0,0 +1,24 @@
name: xdg_directories_web
description: A Dart package for reading XDG directory configuration information on Linux.
version: 0.2.0
flutter:
plugin:
platforms:
web:
fileName: xdg_directories.dart
pluginClass: XDGDirectoriesPlugin
environment:
sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.5.0 <2.0.0"
dependencies:
universal_io: ^2.0.4
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
dev_dependencies:
test: ^1.16.0

@ -2,12 +2,11 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gsettings/gsettings.dart';
import 'package:window_manager/window_manager.dart';
import 'package:xdg_directories/xdg_directories.dart';
import 'package:xdg_directories/xdg_directories.dart' as xdgDir;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb) {
var xdgDir = XDGDirectories();
print(await xdgDir.configHome);
print(await xdgDir.cacheHome);
print(await xdgDir.dataHome);

@ -23,31 +23,21 @@ environment:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
window_manager: ^0.0.3
localstorage: ^4.0.0+1
shared_preferences: ^2.0.7
xdg_directories: 0.2.0
gsettings: 0.2.0
window_size:
path: 3rd_party/window_size
xdg_directories:
path: 3rd_party/xdg_directories
gsettings:
path: 3rd_party/gsettings
xdg_directories_web:
path: 3rd_party/xdg_directories_web
dev_dependencies:
flutter_test:
sdk: flutter
dependency_overrides:
xdg_directories:
path: 3rd_party/xdg_directories
path_provider_linux:
path: 3rd_party/path_provider_linux
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

Loading…
Cancel
Save