import gnome-shell-extensions-3.32.1-33.el8

This commit is contained in:
CentOS Sources 2023-05-16 06:08:58 +00:00 committed by Stepan Oksanichenko
parent 27629185ec
commit ae47908807
7 changed files with 1218 additions and 21 deletions

View File

@ -0,0 +1,68 @@
From 3d32ab1848011a3a7af97255307b3541a7553b09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 14 Dec 2022 16:55:51 +0100
Subject: [PATCH] classification-banner: Handle fullscreen monitors
When a monitor is in fullscreen, we don't want its classification
banner to be offset by an imaginary panel, but at the top of the
screen.
---
extensions/classification-banner/extension.js | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js
index 6c2fe007..1cf03b3f 100644
--- a/extensions/classification-banner/extension.js
+++ b/extensions/classification-banner/extension.js
@@ -27,16 +27,19 @@ const Main = imports.ui.main;
const ClassificationBanner = GObject.registerClass(
class ClassificationBanner extends Clutter.Actor {
_init(index) {
+ const constraint = new Layout.MonitorConstraint({index});
super._init({
layout_manager: new Clutter.BinLayout(),
- constraints: new Layout.MonitorConstraint({
- work_area: true,
- index,
- }),
+ constraints: constraint,
});
+ this._monitorConstraint = constraint;
this._settings = ExtensionUtils.getSettings();
this.connect('destroy', () => {
+ if (this._fullscreenChangedId)
+ global.display.disconnect(this._fullscreenChangedId);
+ delete this._fullscreenChangedId;
+
if (this._settings)
this._settings.run_dispose();
this._settings = null;
@@ -95,6 +98,11 @@ class ClassificationBanner extends Clutter.Actor {
userLabel, 'visible',
Gio.SettingsBindFlags.GET);
+ this._fullscreenChangedId =
+ global.display.connect('in-fullscreen-changed',
+ () => this._updateMonitorConstraint());
+ this._updateMonitorConstraint();
+
this._settings.connect('changed::color',
() => this._updateStyles());
this._settings.connect('changed::background-color',
@@ -111,6 +119,12 @@ class ClassificationBanner extends Clutter.Actor {
return `${key}: rgba(${red},${green},${blue},${alpha / 255});`;
}
+ _updateMonitorConstraint() {
+ const {index} = this._monitorConstraint;
+ this._monitorConstraint.work_area =
+ !global.display.get_monitor_in_fullscreen(index);
+ }
+
_updateStyles() {
const bgStyle = this._getColorSetting('background-color');
const fgStyle = this._getColorSetting('color');
--
2.38.1

View File

@ -0,0 +1,76 @@
From 93e3e938b322433aff862bbc46f80c60ab7dc2ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 17 Jan 2023 20:31:21 +0100
Subject: [PATCH] desktop-icons: Don't use blocking IO
---
extensions/desktop-icons/desktopManager.js | 35 +++++++++++++++++-----
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
index 399aee03..2ce6eefb 100644
--- a/extensions/desktop-icons/desktopManager.js
+++ b/extensions/desktop-icons/desktopManager.js
@@ -53,6 +53,21 @@ function findMonitorIndexForPos(x, y) {
return getDpy().get_monitor_index_for_rect(new Meta.Rectangle({x, y}));
}
+async function queryInfo(file, attributes = DesktopIconsUtil.DEFAULT_ATTRIBUTES, cancellable = null) {
+ const flags = Gio.FileQueryInfoFlags.NONE;
+ const priority = GLib.PRIORITY_DEFAULT;
+ return new Promise((resolve, reject) => {
+ file.query_info_async(attributes, flags, priority, cancellable, (o, res) => {
+ try {
+ const info = file.query_info_finish(res);
+ resolve(info);
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+}
+
var DesktopManager = GObject.registerClass({
Properties: {
@@ -221,9 +236,7 @@ var DesktopManager = GObject.registerClass({
if (!this._unixMode) {
let desktopDir = DesktopIconsUtil.getDesktopDir();
- let fileInfo = desktopDir.query_info(Gio.FILE_ATTRIBUTE_UNIX_MODE,
- Gio.FileQueryInfoFlags.NONE,
- null);
+ let fileInfo = await queryInfo(desktopDir, Gio.FILE_ATTRIBUTE_UNIX_MODE);
this._unixMode = fileInfo.get_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE);
this._setWritableByOthers((this._unixMode & S_IWOTH) != 0);
}
@@ -268,14 +281,22 @@ var DesktopManager = GObject.registerClass({
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT,
this._desktopEnumerateCancellable,
- (source, result) => {
+ async (source, result) => {
try {
let fileEnum = source.enumerate_children_finish(result);
+ let extraFolders = await Promise.all(DesktopIconsUtil.getExtraFolders()
+ .map(async ([folder, extras]) => {
+ const info = await queryInfo(folder,
+ DesktopIconsUtil.DEFAULT_ATTRIBUTES,
+ this._desktopEnumerateCancellable);
+ return [folder, info, extras];
+ }));
+
let resultGenerator = function *() {
+ for (let [newFolder, info, extras] of extraFolders)
+ yield [newFolder, info, extras];
+
let info;
- for (let [newFolder, extras] of DesktopIconsUtil.getExtraFolders()) {
- yield [newFolder, newFolder.query_info(DesktopIconsUtil.DEFAULT_ATTRIBUTES, Gio.FileQueryInfoFlags.NONE, this._desktopEnumerateCancellable), extras];
- }
while ((info = fileEnum.next_file(null)))
yield [fileEnum.get_child(info), info, Prefs.FileType.NONE];
}.bind(this);
--
2.38.1

View File

@ -0,0 +1,31 @@
From 506c6d69eaa5e056d9580a28e9c200586b0e1fb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 2 Dec 2022 15:20:40 +0100
Subject: [PATCH] fileItem: Just destroy menus
The menu manager is smart enough to remove the menu automatically,
and the actor will be destroyed alongside the menu. Not doing those
actions explicitly allows the automatic handling to proceed without
confusing the grab state.
---
extensions/desktop-icons/fileItem.js | 4 ----
1 file changed, 4 deletions(-)
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
index 44a93352..f2f03440 100644
--- a/extensions/desktop-icons/fileItem.js
+++ b/extensions/desktop-icons/fileItem.js
@@ -575,10 +575,6 @@ var FileItem = class {
_removeMenu() {
if (this._menu != null) {
- if (this._menuManager != null)
- this._menuManager.removeMenu(this._menu);
-
- Main.layoutManager.uiGroup.remove_child(this._menu.actor);
this._menu.destroy();
this._menu = null;
}
--
2.38.1

View File

@ -0,0 +1,147 @@
From be4ab59a3f2bb9829dde390db3dd8868a08840eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 2 Dec 2022 19:28:54 +0100
Subject: [PATCH] fileItem: Support .desktop files of type Link
Gio only has direct support for .desktop files of type Application.
However in the context of desktop icons (and file managers), shortcuts
of URLs are useful as well, so add explicit support for .desktop files
of type Link.
---
extensions/desktop-icons/fileItem.js | 71 +++++++++++++++++++++++-----
1 file changed, 60 insertions(+), 11 deletions(-)
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
index f2f03440..1c9a1e55 100644
--- a/extensions/desktop-icons/fileItem.js
+++ b/extensions/desktop-icons/fileItem.js
@@ -239,12 +239,32 @@ var FileItem = class {
log(`desktop-icons: File ${this._displayName} is writable by others - will not allow launching`);
if (this._isDesktopFile) {
- this._desktopFile = Gio.DesktopAppInfo.new_from_filename(this._file.get_path());
- if (!this._desktopFile) {
- log(`Couldnt parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
+ try {
+ const keyFile = new GLib.KeyFile();
+ keyFile.load_from_file(this._file.get_path(), GLib.KeyFileFlags.NONE);
+
+ const type = keyFile.get_string(
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_TYPE);
+ switch (type) {
+ case GLib.KEY_FILE_DESKTOP_TYPE_APPLICATION:
+ this._desktopFile = Gio.DesktopAppInfo.new_from_keyfile(keyFile);
+ if (!this._desktopFile) {
+ log(`Couldnt parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
+ this._isValidDesktopFile = false;
+ } else {
+ this._isValidDesktopFile = true;
+ }
+ break;
+ case GLib.KEY_FILE_DESKTOP_TYPE_LINK:
+ const url = keyFile.get_string(
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_URL);
+ if (url)
+ this._linkFile = keyFile;
+ default: // fall-through
+ this._isValidDesktopFile = false;
+ }
+ } catch (e) {
this._isValidDesktopFile = false;
- } else {
- this._isValidDesktopFile = true;
}
} else {
this._isValidDesktopFile = false;
@@ -356,8 +376,17 @@ var FileItem = class {
if (this._isBrokenSymlink) {
this._icon.child = this._createEmblemedStIcon(null, 'text-x-generic');
} else {
- if (this.trustedDesktopFile && this._desktopFile.has_key('Icon'))
- this._icon.child = this._createEmblemedStIcon(null, this._desktopFile.get_string('Icon'));
+ let iconName = null;
+
+ try {
+ if (this.trustedDesktopFile)
+ iconName = this._desktopFile.get_string('Icon');
+ else if (this._linkFile)
+ iconName = this._linkFile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_ICON);
+ } catch (e) {}
+
+ if (iconName)
+ this._icon.child = this._createEmblemedStIcon(null, iconName);
else
this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
}
@@ -411,7 +440,7 @@ var FileItem = class {
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-unreadable')));
else
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
- } else if (this.trustedDesktopFile) {
+ } else if (this.trustedDesktopFile || this._linkFile) {
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
}
@@ -440,6 +469,12 @@ var FileItem = class {
return;
}
+ if (this._linkFile) {
+ this._openUri(this._linkFile.get_string(
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_URL));
+ return;
+ }
+
if (this._attributeCanExecute &&
!this._isDirectory &&
!this._isValidDesktopFile &&
@@ -449,13 +484,17 @@ var FileItem = class {
return;
}
- Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
+ this._openUri(this.file.get_uri());
+ }
+
+ _openUri(uri) {
+ Gio.AppInfo.launch_default_for_uri_async(uri,
null, null,
(source, result) => {
try {
Gio.AppInfo.launch_default_for_uri_finish(result);
} catch (e) {
- log('Error opening file ' + this.file.get_uri() + ': ' + e.message);
+ log('Error opening file ' + uri + ': ' + e.message);
}
}
);
@@ -555,7 +594,9 @@ var FileItem = class {
}
canRename() {
- return !this.trustedDesktopFile && this._fileExtra == Prefs.FileType.NONE;
+ return !this.trustedDesktopFile &&
+ !this._linkFile &&
+ this._fileExtra == Prefs.FileType.NONE;
}
_doOpenWith() {
@@ -819,6 +860,14 @@ var FileItem = class {
if (this.trustedDesktopFile)
return this._desktopFile.get_name();
+ if (this._linkFile) {
+ try {
+ const name = this._linkFile.get_string(
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_NAME);
+ return name;
+ } catch (e) {}
+ }
+
return this._displayName || null;
}
--
2.38.1

View File

@ -0,0 +1,51 @@
From ce75829479b1e7bf99e74bf835174e91c8da2276 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 9 Dec 2022 15:31:08 +0100
Subject: [PATCH] gesture-inhibitor: Allow inhibiting workspace switch gesture
---
extensions/gesture-inhibitor/extension.js | 5 ++++-
.../org.gnome.shell.extensions.gesture-inhibitor.gschema.xml | 4 ++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
index e74ede2f..bf02d075 100644
--- a/extensions/gesture-inhibitor/extension.js
+++ b/extensions/gesture-inhibitor/extension.js
@@ -37,6 +37,8 @@ class Extension {
this._showOverview = a;
else if (a instanceof WindowManager.AppSwitchAction)
this._appSwitch = a;
+ else if (a instanceof WindowManager.WorkspaceSwitchAction)
+ this._workspaceSwitch = a;
else if (a instanceof EdgeDragAction.EdgeDragAction &&
a._side == St.Side.BOTTOM)
this._showOsk = a;
@@ -52,7 +54,8 @@ class Extension {
{ setting: 'app-switch', action: this._appSwitch },
{ setting: 'show-osk', action: this._showOsk },
{ setting: 'unfullscreen', action: this._unfullscreen },
- { setting: 'show-app-grid', action: this._showAppGrid }
+ { setting: 'show-app-grid', action: this._showAppGrid },
+ { setting: 'workspace-switch', action: this._workspaceSwitch },
];
}
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
index 1d67dcc0..a5e97a3d 100644
--- a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
@@ -16,6 +16,10 @@
<default>true</default>
<summary>Application switch gesture</summary>
</key>
+ <key name="workspace-switch" type="b">
+ <default>true</default>
+ <summary>Workspace switch gesture</summary>
+ </key>
<key name="unfullscreen" type="b">
<default>true</default>
<summary>Unfullscreen gesture</summary>
--
2.38.1

View File

@ -1,7 +1,7 @@
From ed28c7abd7c324dc6071ff96309854b1f5d48761 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 17:44:50 +0200
Subject: [PATCH 01/10] Add top-icons extension
Subject: [PATCH 01/11] Add top-icons extension
---
extensions/top-icons/extension.js | 96 +++++++++++++++++++++++++++
@ -164,13 +164,13 @@ index b987f2d4..6050c32f 100644
]
--
2.33.1
2.38.1
From b99f1a2ead84c4fe494a387a032715f2973fbfa7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 18:05:41 +0200
Subject: [PATCH 02/10] Add dash-to-dock extension
Subject: [PATCH 02/11] Add dash-to-dock extension
---
extensions/dash-to-dock/Settings.ui | 3335 +++++++++++++++++
@ -13413,13 +13413,13 @@ index 6050c32f..2909135a 100644
'top-icons',
'user-theme'
--
2.33.1
2.38.1
From 9ffe67c4d25f34fa6c3af5ee4ddbd0be3018ef14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 18:55:47 +0200
Subject: [PATCH 03/10] Add panel-favorites extension
Subject: [PATCH 03/11] Add panel-favorites extension
---
extensions/panel-favorites/extension.js | 267 ++++++++++++++++++++
@ -13766,13 +13766,13 @@ index 2909135a..e8e00dce 100644
'user-theme'
]
--
2.33.1
2.38.1
From 4bd1716e559af83795eec5b02025798b02c09fa4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 4 Mar 2016 17:07:21 +0100
Subject: [PATCH 04/10] Add updates-dialog extension
Subject: [PATCH 04/11] Add updates-dialog extension
---
extensions/updates-dialog/extension.js | 503 ++++++++++++++++++
@ -14396,13 +14396,13 @@ index 9c1438ac..55f0e9aa 100644
extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml
extensions/window-list/extension.js
--
2.33.1
2.38.1
From 0ba4b86fa5f73bccd3ab1984d9deef0d39f656c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 1 Jun 2017 23:57:14 +0200
Subject: [PATCH 05/10] Add no-hot-corner extension
Subject: [PATCH 05/11] Add no-hot-corner extension
---
extensions/no-hot-corner/extension.js | 31 +++++++++++++++++++++++
@ -14499,13 +14499,13 @@ index d129e6cd..6f27f460 100644
'top-icons',
'updates-dialog',
--
2.33.1
2.38.1
From f56b4374904cdfd8e1790dc3cf5080b60f30ebea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 26 Mar 2019 19:44:43 +0100
Subject: [PATCH 06/10] Add window-grouper extension
Subject: [PATCH 06/11] Add window-grouper extension
---
extensions/window-grouper/extension.js | 109 ++++++++++
@ -14903,13 +14903,13 @@ index 6f27f460..4b9d138c 100644
enabled_extensions = get_option('enable_extensions')
--
2.33.1
2.38.1
From 25c4999ff6adf19a32bab2a4d6cccae42520563b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 26 Mar 2019 21:32:09 +0100
Subject: [PATCH 07/10] Add disable-screenshield extension
Subject: [PATCH 07/11] Add disable-screenshield extension
---
extensions/disable-screenshield/extension.js | 27 +++++++++++++++++++
@ -15002,13 +15002,13 @@ index 4b9d138c..cf855a01 100644
'no-hot-corner',
'panel-favorites',
--
2.33.1
2.38.1
From 59604979f6ba48b7ff8d1616ab9df739dcf46a20 Mon Sep 17 00:00:00 2001
From: Carlos Soriano <csoriano@gnome.org>
Date: Mon, 13 Aug 2018 17:28:41 +0200
Subject: [PATCH 08/10] Add desktop icons extension
Subject: [PATCH 08/11] Add desktop icons extension
---
.../desktop-icons/createFolderDialog.js | 164 ++++
@ -26738,13 +26738,13 @@ index 74a95f8a..fa5ba9b8 100644
#~ msgstr "Dock 的位置"
--
2.33.1
2.38.1
From dc47faaf827011e5dd7a53f9007ea618c6e88203 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 8 Oct 2021 19:36:18 +0200
Subject: [PATCH 09/10] Add dash-to-panel
Subject: [PATCH 09/11] Add dash-to-panel
---
extensions/dash-to-panel/COPYING | 341 +
@ -70770,13 +70770,13 @@ index fa5ba9b8..015d85a4 100644
#~ msgstr "退出應用程式"
--
2.33.1
2.38.1
From e8facafa1d16fc62a01c02bcee9e34a388f81572 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 2 Dec 2021 19:39:50 +0100
Subject: [PATCH 10/10] Add classification-banner
Subject: [PATCH 10/11] Add classification-banner
---
extensions/classification-banner/extension.js | 170 ++++++++
@ -71427,5 +71427,789 @@ index d4791263..75a2beaa 100644
'dash-to-panel',
'disable-screenshield',
--
2.33.1
2.38.1
From 214b5c2959ab3c0ab64d32b79fbff011302d1af8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 12 Jan 2023 19:43:52 +0100
Subject: [PATCH 11/11] Add custom-menu extension
---
extensions/custom-menu/config.js | 484 ++++++++++++++++++++++++
extensions/custom-menu/extension.js | 217 +++++++++++
extensions/custom-menu/meson.build | 7 +
extensions/custom-menu/metadata.json.in | 10 +
extensions/custom-menu/stylesheet.css | 1 +
meson.build | 1 +
6 files changed, 720 insertions(+)
create mode 100644 extensions/custom-menu/config.js
create mode 100644 extensions/custom-menu/extension.js
create mode 100644 extensions/custom-menu/meson.build
create mode 100644 extensions/custom-menu/metadata.json.in
create mode 100644 extensions/custom-menu/stylesheet.css
diff --git a/extensions/custom-menu/config.js b/extensions/custom-menu/config.js
new file mode 100644
index 00000000..652c0223
--- /dev/null
+++ b/extensions/custom-menu/config.js
@@ -0,0 +1,484 @@
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Json = imports.gi.Json;
+const Lang = imports.lang;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const ByteArray = imports.byteArray;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const getLogger = Me.imports.extension.getLogger;
+
+const Entry = new Lang.Class({
+ Name: 'Entry',
+ Abstract: true,
+
+ _init: function(prop) {
+ this.type = prop.type;
+ this.title = prop.title || "";
+
+ this.__vars = prop.__vars || [];
+ this.updateEnv(prop);
+ },
+
+ setTitle: function(text) {
+ this.item.label.get_clutter_text().set_text(text);
+ },
+
+ updateEnv: function(prop) {
+ this.__env = {}
+ if(!this.__vars) return;
+
+ for(let i in this.__vars) {
+ let v = this.__vars[i];
+ this.__env[v] = prop[v] ? String(prop[v]) : "";
+ }
+ },
+
+ // the pulse function should be read as "a pulse arrives"
+ pulse: function() { },
+
+ _try_destroy: function() {
+ try {
+ if(this.item && this.item.destroy)
+ this.item.destroy();
+ } catch(e) { /* Ignore all errors during destory*/ }
+ },
+});
+
+const DerivedEntry = new Lang.Class({
+ Name: 'DerivedEntry',
+
+ _init: function(prop) {
+ if(!prop.base)
+ throw new Error("Base entry not specified in type definition.");
+
+ this.base = prop.base;
+ this.vars = prop.vars || [];
+
+ delete prop.base;
+ delete prop.vars;
+
+ this.prop = prop;
+ },
+
+ createInstance: function(addit_prop) {
+ let cls = type_map[this.base];
+ if(!cls) throw new Error("Bad base class.");
+ if(cls.createInstance) throw new Error("Not allowed to derive from dervied types");
+
+ for(let rp in this.prop)
+ addit_prop[rp] = this.prop[rp];
+ addit_prop.__vars = this.vars;
+
+ let instance = new cls(addit_prop);
+
+ return instance;
+ },
+});
+
+/*
+ * callback: function (stdout, stderr, exit_status) { }
+ */
+let __pipeOpenQueue = [];
+let __pipeExecTimer = null;
+
+function pipeOpen(cmdline, env, callback) {
+ let param = [cmdline, env, callback]
+ __pipeOpenQueue.push(param);
+ if(__pipeExecTimer === null) {
+ __pipeExecTimer = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50,
+ function() {
+ let param = __pipeOpenQueue.shift();
+ if(param === undefined) {
+ __pipeExecTimer = null;
+ return false;
+ }
+ if(realPipeOpen) realPipeOpen(param[0], param[1], param[2]);
+ return true;
+ });
+
+ }
+}
+
+function realPipeOpen(cmdline, env, callback) {
+ let user_cb = callback;
+ let proc;
+
+ function wait_cb(_, _res) {
+ let stdout_pipe = proc.get_stdout_pipe();
+ let stderr_pipe = proc.get_stderr_pipe();
+
+ let stdout_content;
+ let stderr_content;
+
+ // Only the first GLib.MAXINT16 characters are fetched for optimization.
+ stdout_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(osrc, ores) {
+ stdout_content = ByteArray.toString(stdout_pipe.read_bytes_finish(ores).get_data());
+ stdout_pipe.close(null);
+
+ stderr_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(esrc, eres) {
+ stderr_content = ByteArray.toString(stderr_pipe.read_bytes_finish(eres).get_data());
+ stderr_pipe.close(null);
+
+ user_cb(stdout_content, stderr_content, proc.get_exit_status());
+ });
+ });
+ }
+
+ if(user_cb) {
+ let _pipedLauncher = new Gio.SubprocessLauncher({
+ flags:
+ Gio.SubprocessFlags.STDERR_PIPE |
+ Gio.SubprocessFlags.STDOUT_PIPE
+ });
+ for(let key in env) {
+ _pipedLauncher.setenv(key, env[key], true);
+ }
+ proc = _pipedLauncher.spawnv(['bash', '-c', cmdline]);
+ proc.wait_async(null, wait_cb);
+ } else {
+ // Detached launcher is used to spawn commands that we are not concerned
+ // about its result.
+ let _detacLauncher = new Gio.SubprocessLauncher();
+ for(let key in env) {
+ _detacLauncher.setenv(key, env[key], true);
+ }
+ proc = _detacLauncher.spawnv(['bash', '-c', cmdline]);
+ }
+
+ getLogger().info("Spawned " + cmdline);
+
+ // log(`Spawning ${cmdline}`); TODO: BEN
+ return proc.get_identifier();
+}
+
+function _generalSpawn(command, env, title) {
+ title = title || "Process";
+ pipeOpen(command, env, function(stdout, stderr, exit_status) {
+ if(exit_status != 0) {
+ log
+ getLogger().warning(stderr);
+ getLogger().notify("proc", title +
+ " exited with status " + exit_status, stderr);
+ }
+ });
+}
+
+function quoteShellArg(arg) {
+ arg = arg.replace(/'/g, "'\"'\"'");
+ return "'" + arg + "'";
+}
+
+// This cache is used to reduce detector cost. Each time creating an item, it
+// check if the result of this detector is cached, which prevent the togglers
+// from running detector on each creation. This is useful especially in search
+// mode.
+let _toggler_state_cache = { };
+
+const TogglerEntry = new Lang.Class({
+ Name: 'TogglerEntry',
+ Extends: Entry,
+
+ _init: function(prop) {
+ this.parent(prop);
+
+ this.command_on = prop.command_on || "";
+ this.command_off = prop.command_off || "";
+ this.detector = prop.detector || "";
+ this.auto_on = prop.auto_on || false;
+ this.notify_when = prop.notify_when || [];
+ // if the switch is manually turned off, auto_on is disabled.
+ this._manually_switched_off = false;
+ this.pulse(); // load initial state
+ },
+
+ createItem: function() {
+ this._try_destroy();
+ this.item = new PopupMenu.PopupSwitchMenuItem(this.title, false);
+ this.item.label.get_clutter_text().set_use_markup(true);
+ this.item.connect('toggled', Lang.bind(this, this._onManuallyToggled));
+ this._loadState();
+ return this.item;
+ },
+
+ _onManuallyToggled: function(_, state) {
+ // when switched on again, this flag will get cleared.
+ this._manually_switched_off = !state;
+ this._storeState(state);
+ this._onToggled(state);
+ },
+
+ _onToggled: function(state) {
+ if(state)
+ _generalSpawn(this.command_on, this.__env, this.title);
+ else
+ _generalSpawn(this.command_off, this.__env, this.title);
+ },
+
+ _detect: function(callback) {
+ // abort detecting if detector is an empty string
+ if(!this.detector)
+ return;
+
+ pipeOpen(this.detector, this.__env, function(out) {
+ out = String(out);
+ callback(!Boolean(out.match(/^\s*$/)));
+ });
+ },
+
+ compareState: function(new_state) {
+ // compare the new state with cached state
+ // notify when state is different
+ let old_state = _toggler_state_cache[this.detector];
+ if(old_state === undefined) return;
+ if(old_state == new_state) return;
+
+ if(this.notify_when.indexOf(new_state ? "on" : "off") >= 0) {
+ let not_str = this.title + (new_state ? " started." : " stopped.");
+ if(!new_state && this.auto_on)
+ not_str += " Attempt to restart it now.";
+ getLogger().notify("state", not_str);
+ }
+ },
+
+ _storeState: function(state) {
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
+ _toggler_state_cache[hash] = state;
+ },
+
+ _loadState: function() {
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
+ let state = _toggler_state_cache[hash];
+ if(state !== undefined)
+ this.item.setToggleState(state); // doesn't emit 'toggled'
+ },
+
+ pulse: function() {
+ this._detect(Lang.bind(this, function(state) {
+ this.compareState(state);
+
+ this._storeState(state);
+ this._loadState();
+ //global.log(this.title + ': ' + this._manually_switched_off);
+
+ if(!state && !this._manually_switched_off && this.auto_on)
+ // do not call setToggleState here, because command_on may fail
+ this._onToggled(this.item, true);
+ }));
+ },
+
+ perform: function() {
+ this.item.toggle();
+ },
+});
+
+const LauncherEntry = new Lang.Class({
+ Name: 'LauncherEntry',
+ Extends: Entry,
+
+ _init: function(prop) {
+ this.parent(prop);
+
+ this.command = prop.command || "";
+ },
+
+ createItem: function() {
+ this._try_destroy();
+
+ this.item = new PopupMenu.PopupMenuItem(this.title);
+ this.item.label.get_clutter_text().set_use_markup(true);
+ this.item.connect('activate', Lang.bind(this, this._onClicked));
+
+ return this.item;
+ },
+
+ _onClicked: function(_) {
+ _generalSpawn(this.command, this.__env, this.title);
+ },
+
+ perform: function() {
+ this.item.emit('activate');
+ },
+});
+
+const SubMenuEntry = new Lang.Class({
+ Name: 'SubMenuEntry',
+ Extends: Entry,
+
+ _init: function(prop) {
+ this.parent(prop)
+
+ if(prop.entries == undefined)
+ throw new Error("Expected entries provided in submenu entry.");
+
+ this.entries = [];
+
+ for(let i in prop.entries) {
+ let entry_prop = prop.entries[i];
+ let entry = createEntry(entry_prop);
+ this.entries.push(entry);
+ }
+ },
+
+ createItem: function() {
+ this._try_destroy();
+
+ this.item = new PopupMenu.PopupSubMenuMenuItem(this.title);
+ this.item.label.get_clutter_text().set_use_markup(true);
+ for(let i in this.entries) {
+ let entry = this.entries[i];
+ this.item.menu.addMenuItem(entry.createItem());
+ }
+
+ return this.item;
+ },
+
+ pulse: function() {
+ for(let i in this.entries) {
+ let entry = this.entries[i];
+ entry.pulse();
+ }
+ }
+});
+
+const SeparatorEntry = new Lang.Class({
+ Name: 'SeparatorEntry',
+ Extends: Entry,
+
+ _init: function(prop) { },
+
+ createItem: function() {
+ this._try_destroy();
+
+ this.item = new PopupMenu.PopupSeparatorMenuItem(this.title);
+ this.item.label.get_clutter_text().set_use_markup(true);
+
+ return this.item;
+ },
+});
+
+let type_map = {};
+
+////////////////////////////////////////////////////////////////////////////////
+// Config Loader loads config from JSON file.
+
+// convert Json Nodes (GLib based) to native javascript value.
+function convertJson(node) {
+ if(node.get_node_type() == Json.NodeType.VALUE)
+ return node.get_value();
+ if(node.get_node_type() == Json.NodeType.OBJECT) {
+ let obj = {}
+ node.get_object().foreach_member(function(_, k, v_n) {
+ obj[k] = convertJson(v_n);
+ });
+ return obj;
+ }
+ if(node.get_node_type() == Json.NodeType.ARRAY) {
+ let arr = []
+ node.get_array().foreach_element(function(_, i, elem) {
+ arr.push(convertJson(elem));
+ });
+ return arr;
+ }
+ return null;
+}
+
+//
+function createEntry(entry_prop) {
+ if(!entry_prop.type)
+ throw new Error("No type specified in entry.");
+
+ let cls = type_map[entry_prop.type];
+ if(!cls)
+ throw new Error("Incorrect type '" + entry_prop.type + "'");
+ else if(cls.createInstance)
+ return cls.createInstance(entry_prop);
+
+ return new cls(entry_prop);
+}
+
+var Loader = new Lang.Class({
+ Name: 'ConfigLoader',
+
+ _init: function(filename) {
+ if(filename)
+ this.loadConfig(filename);
+ },
+
+ loadConfig: function(filename) {
+ // reset type_map everytime load the config
+ type_map = {
+ launcher: LauncherEntry,
+ toggler: TogglerEntry,
+ submenu: SubMenuEntry,
+ separator: SeparatorEntry
+ };
+
+ type_map.systemd = new DerivedEntry({
+ base: 'toggler',
+ vars: ['unit'],
+ command_on: "pkexec systemctl start ${unit}",
+ command_off: "pkexec systemctl stop ${unit}",
+ detector: "systemctl status ${unit} | grep Active:\\\\s\\*activ[ei]",
+ });
+
+ type_map.tmux = new DerivedEntry({
+ base: 'toggler',
+ vars: ['command', 'session'],
+ command_on: 'tmux new -d -s ${session} bash -c "${command}"',
+ command_off: 'tmux kill-session -t ${session}',
+ detector: 'tmux has -t "${session}" 2>/dev/null && echo yes',
+ });
+
+ /*
+ * Refer to README file for detailed config file format.
+ */
+ this.entries = []; // CAUTION: remove all entries.
+
+ let config_parser = new Json.Parser();
+ config_parser.load_from_file(filename);
+
+ let conf = convertJson(config_parser.get_root());
+ if (conf.entries == undefined)
+ throw new Error("Key 'entries' not found.");
+ if (conf.deftype) {
+ for (let tname in conf.deftype) {
+ if (type_map[tname])
+ throw new Error("Type \""+tname+"\" duplicated.");
+ type_map[tname] = new DerivedEntry(conf.deftype[tname]);
+ }
+ }
+
+ for (let conf_i in conf.entries) {
+ let entry_prop = conf.entries[conf_i];
+ this.entries.push(createEntry(entry_prop));
+ }
+ },
+
+
+ saveDefaultConfig: function(filename) {
+ // Write default config
+ const PERMISSIONS_MODE = 0o640;
+ const jsonString = JSON.stringify({
+ "_homepage_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel",
+ "_examples_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel/tree/main/examples",
+ "entries": [ {
+ "type": "launcher",
+ "title": "Edit menu",
+ "command": "gedit $HOME/.entries.json"
+ } ]
+ }, null, 4);
+ let fileConfig = Gio.File.new_for_path(filename);
+ if (GLib.mkdir_with_parents(fileConfig.get_parent().get_path(), PERMISSIONS_MODE) === 0) {
+ fileConfig.replace_contents(jsonString, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
+ }
+ // Try to load newly saved file
+ try {
+ this.loadConfig(filename);
+ } catch(e) {
+ Main.notify(_('Cannot create and load file: '+filename));
+ }
+ }, /**/
+
+});
diff --git a/extensions/custom-menu/extension.js b/extensions/custom-menu/extension.js
new file mode 100644
index 00000000..9f3e3ce8
--- /dev/null
+++ b/extensions/custom-menu/extension.js
@@ -0,0 +1,217 @@
+/* extension.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+/**
+ * @author Ben
+ * @see https://github.com/andreabenini/gnome-plugin.custom-menu-panel
+ */
+
+/* exported init */
+
+const GETTEXT_DOMAIN = 'custom-menu-panel';
+const CONFIGURATION_FILE = '/.entries.json';
+
+const { GObject, St } = imports.gi;
+
+const Gettext = imports.gettext.domain(GETTEXT_DOMAIN);
+const _ = Gettext.gettext;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Lang = imports.lang;
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const BackgroundMenu = imports.ui.backgroundMenu;
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Config = Me.imports.config
+
+const LOGGER_INFO = 0;
+const LOGGER_WARNING = 1;
+const LOGGER_ERROR = 2;
+
+const { BackgroundMenu: OriginalBackgroundMenu } = BackgroundMenu;
+
+
+class CustomMenu extends PopupMenu.PopupMenu {
+ constructor(sourceActor) {
+ super(sourceActor, 0.0, St.Side.TOP, 0);
+
+ this._loadSetup();
+ }
+
+ /**
+ * LOAD Program settings from .entries.json file
+ */
+ _loadSetup() {
+ this.removeAll();
+ // Loading configuration from file
+ this.configLoader = new Config.Loader();
+ try {
+ this.configLoader.loadConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // $HOME/.entries.json
+ } catch(e) {
+ this.configLoader.saveDefaultConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // create default entries
+ }
+ // Build the menu
+ let i = 0;
+ for (let i in this.configLoader.entries) {
+ let item = this.configLoader.entries[i].createItem();
+ this.addMenuItem(item);
+ }
+ } /**/
+}
+
+class CustomBackgroundMenu extends CustomMenu {
+ constructor(layoutManager) {
+ super(layoutManager.dummyCursor);
+
+ this.actor.add_style_class_name('background-menu');
+
+ layoutManager.uiGroup.add_actor(this.actor);
+ this.actor.hide();
+
+ this.connect('open-state-changed', (menu, open) => {
+ if (open)
+ this._updateMaxHeight();
+ });
+ }
+
+ _updateMaxHeight() {
+ const monitor = Main.layoutManager.findMonitorForActor(this.actor);
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const vMargins = this.actor.margin_top + this.actor.margin_bottom;
+ const {y: offsetY} = this.sourceActor;
+
+ const maxHeight = Math.round((monitor.height - offsetY - vMargins) / scaleFactor);
+ this.actor.style = `max-height: ${maxHeight}px;`;
+ }
+}
+
+const Indicator = GObject.registerClass(
+ class Indicator extends PanelMenu.Button {
+ _init() {
+ super._init(0.0, _('Custom Menu Panel Indicator'), true);
+ this.add_child(new St.Icon({
+ icon_name: 'view-list-bullet-symbolic',
+ style_class: 'system-status-icon',
+ }));
+
+ this.setMenu(new CustomMenu(this));
+ }
+ }
+);
+
+
+const Logger = new Lang.Class({
+ Name: 'Logger',
+
+ _init: function(log_file) {
+ this._log_file = log_file;
+ // initailize log_backend
+ if(!log_file)
+ this._initEmptyLog();
+ else if(log_file == "gnome-shell")
+ this._initGnomeLog();
+ else
+ this._initFileLog();
+
+ this.level = LOGGER_WARNING;
+
+ this.info = function(t) {
+ if(this.level <= LOGGER_INFO) this.log(t)
+ };
+ this.warning = function(t) {
+ if(this.level <= LOGGER_WARNING) this.log(t)
+ };
+ this.error = function(t) {
+ if(this.level <= LOGGER_ERROR) this.log(t);
+ };
+ },
+
+ _initEmptyLog: function() {
+ this.log = function(_) { };
+ },
+
+ _initGnomeLog: function() {
+ this.log = function(s) {
+ global.log("custom-menu-panel> " + s);
+ };
+ },
+
+ _initFileLog: function() {
+ this.log = function(s) {
+ // all operations are synchronous: any needs to optimize?
+ if(!this._output_file || !this._output_file.query_exists(null) ||
+ !this._fstream || this._fstream.is_closed()) {
+
+ this._output_file = Gio.File.new_for_path(this._log_file);
+ this._fstream = this._output_file.append_to(
+ Gio.FileCreateFlags.NONE, null);
+
+ if(!this._fstream instanceof Gio.FileIOStream) {
+ this._initGnomeLog();
+ this.log("IOError: Failed to append to " + this._log_file +
+ " [Gio.IOErrorEnum:" + this._fstream + "]");
+ return;
+ }
+ }
+
+ this._fstream.write(String(new Date())+" "+s+"\n", null);
+ this._fstream.flush(null);
+ }
+ },
+
+ notify: function(t, str, details) {
+ this.ncond = this.ncond || ['proc', 'ext', 'state'];
+ if(this.ncond.indexOf(t) < 0) return;
+ Main.notify(str, details || "");
+ },
+});
+
+// lazy-evaluation
+let logger = null;
+function getLogger() {
+ if(logger === null)
+ logger = new Logger("gnome-shell");
+ return logger;
+}
+
+class Extension {
+ constructor(uuid) {
+ this._uuid = uuid;
+
+ ExtensionUtils.initTranslations(GETTEXT_DOMAIN);
+ }
+
+ enable() {
+ BackgroundMenu.BackgroundMenu = CustomBackgroundMenu;
+ Main.layoutManager._updateBackgrounds();
+ }
+
+ disable() {
+ BackgroundMenu.BackgroundMenu = OriginalBackgroundMenu;
+ Main.layoutManager._updateBackgrounds();
+ }
+}
+
+function init(meta) {
+ return new Extension(meta.uuid);
+}
diff --git a/extensions/custom-menu/meson.build b/extensions/custom-menu/meson.build
new file mode 100644
index 00000000..92450963
--- /dev/null
+++ b/extensions/custom-menu/meson.build
@@ -0,0 +1,7 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
+
+extension_sources += files('config.js')
diff --git a/extensions/custom-menu/metadata.json.in b/extensions/custom-menu/metadata.json.in
new file mode 100644
index 00000000..054f639b
--- /dev/null
+++ b/extensions/custom-menu/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Custom menu",
+"description": "Quick custom menu for launching your favorite applications",
+"shell-version": [ "@shell_current@" ],
+"url": "@url@"
+}
diff --git a/extensions/custom-menu/stylesheet.css b/extensions/custom-menu/stylesheet.css
new file mode 100644
index 00000000..25134b65
--- /dev/null
+++ b/extensions/custom-menu/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index 75a2beaa..b0c94fe4 100644
--- a/meson.build
+++ b/meson.build
@@ -51,6 +51,7 @@ all_extensions = default_extensions
all_extensions += [
'auto-move-windows',
'classification-banner',
+ 'custom-menu',
'dash-to-dock',
'dash-to-panel',
'disable-screenshield',
--
2.38.1

View File

@ -6,7 +6,7 @@
Name: gnome-shell-extensions
Version: 3.32.1
Release: 29%{?dist}
Release: 33%{?dist}
Summary: Modify and extend GNOME Shell functionality and behavior
Group: User Interface/Desktops
@ -51,6 +51,11 @@ Patch0022: 0001-gesture-inhibitor-Put-a-foot-down-with-self-enabling.pat
Patch0023: 0001-desktop-icons-Use-a-single-unique-name-to-access-nau.patch
Patch0024: window-list-touch.patch
Patch0025: 0001-auto-move-windows-Don-t-move-windows-already-on-all-.patch
Patch0026: 0001-fileItem-Just-destroy-menus.patch
Patch0027: 0001-fileItem-Support-.desktop-files-of-type-Link.patch
Patch0028: 0001-classification-banner-Handle-fullscreen-monitors.patch
Patch0029: 0001-gesture-inhibitor-Allow-inhibiting-workspace-switch-.patch
Patch0030: 0001-desktop-icons-Don-t-use-blocking-IO.patch
%description
GNOME Shell Extensions is a collection of extensions providing additional and
@ -60,6 +65,7 @@ Enabled extensions:
* apps-menu
* auto-move-windows
* classification-banner
* custom-menu
* dash-to-dock
* dash-to-panel
* disable-screenshield
@ -165,6 +171,16 @@ Requires: %{pkg_prefix}-common = %{version}-%{release}
This GNOME Shell extension adds a banner that displays the classification level.
%package -n %{pkg_prefix}-custom-menu
Summary: Add a custom menu to the desktop
Group: User Interface/Desktops
License: GPLv2+
Requires: %{pkg_prefix}-common = %{version}-%{release}
%description -n %{pkg_prefix}-custom-menu
This GNOME Shell extension adds a custom menu to the desktop background.
%package -n %{pkg_prefix}-dash-to-dock
Summary: Show the dash outside the activities overview
Group: User Interface/Desktops
@ -449,6 +465,10 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions
%{_datadir}/gnome-shell/extensions/classification-banner*/
%files -n %{pkg_prefix}-custom-menu
%{_datadir}/gnome-shell/extensions/custom-menu*/
%files -n %{pkg_prefix}-dash-to-dock
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
%{_datadir}/gnome-shell/extensions/dash-to-dock*/
@ -549,6 +569,26 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions
%changelog
* Tue Jan 17 2023 Florian Müllner <fmuellner@redhat.com> - 3.32.1-33
- Avoid blocking IO in desktop-icons
Resolves: #2162017
* Thu Jan 12 2023 Florian Müllner <fmuellner@redhat.com> - 3.32.1-32
- Add custom-menu extension
Resolves: #2033572
* Wed Dec 14 2022 Florian Müllner <fmuellner@redhat.com> - 3.32.1-31
- Adjust classification banner position in fullscreen
Resolves: #2150107
- Allow inhibiting workspace switch gesture
Resolves: #2138109
* Fri Dec 09 2022 Florian Müllner <fmuellner@redhat.com> - 3.32.1-30
- Fix stuck grab if disabled with open context menu
Resolves: #2149670
- Support .desktop files of type Link
Resolves: #2143825
* Mon Aug 29 2022 Jonas Ådahl <jadahl@redhat.com> - 3.32.1-29
- Avoid invalid window management in auto-move-windows
Resolves: #2089311