commit 4045acccc4747620e171869ef7ec249cd6ca84c9 Author: MSVSphere Packaging Team Date: Tue Nov 26 16:09:48 2024 +0300 import gnome-shell-extensions-47~alpha-3.el10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f06dea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gnome-shell-extensions-47.alpha.tar.xz diff --git a/.gnome-shell-extensions.metadata b/.gnome-shell-extensions.metadata new file mode 100644 index 0000000..584f622 --- /dev/null +++ b/.gnome-shell-extensions.metadata @@ -0,0 +1 @@ +c3c6c96b0db36cada61b74e35f86e9eede07a647 SOURCES/gnome-shell-extensions-47.alpha.tar.xz diff --git a/SOURCES/0001-Include-top-icons-in-classic-session.patch b/SOURCES/0001-Include-top-icons-in-classic-session.patch new file mode 100644 index 0000000..7f56213 --- /dev/null +++ b/SOURCES/0001-Include-top-icons-in-classic-session.patch @@ -0,0 +1,32 @@ +From 8a2191519e2431a946aa1be36474bfe323a454a8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 23 Feb 2018 16:56:46 +0100 +Subject: [PATCH] Include top-icons in classic session + +--- + meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index dce1731c..b915b68c 100644 +--- a/meson.build ++++ b/meson.build +@@ -34,6 +34,7 @@ classic_extensions = [ + 'apps-menu', + 'places-menu', + 'launch-new-instance', ++ 'top-icons', + 'window-list' + ] + +@@ -44,7 +45,6 @@ default_extensions += [ + 'light-style', + 'screenshot-window-sizer', + 'system-monitor', +- 'top-icons', + 'windowsNavigator', + 'workspace-indicator' + ] +-- +2.45.2 + diff --git a/SOURCES/0001-workspace-indicator-Re-fittsify-workspace-previews.patch b/SOURCES/0001-workspace-indicator-Re-fittsify-workspace-previews.patch new file mode 100644 index 0000000..fae56d0 --- /dev/null +++ b/SOURCES/0001-workspace-indicator-Re-fittsify-workspace-previews.patch @@ -0,0 +1,51 @@ +From 97d71d4a7ef4b1d4c9c2eab55db62173311f5366 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 2 Jul 2024 19:04:10 +0200 +Subject: [PATCH] workspace-indicator: Re-fittsify workspace previews + +For the window-list extension, it is important that the workspace +previews extend to the bottom edge for easier click targets. + +That broke while merging the code with the workspace-indicator, +fix it again by moving the padding from the parent box into the +thumbnail children. +--- + .../workspace-indicator/stylesheet-dark.css | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index b4a716b8..3c57c3e6 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -18,7 +18,6 @@ + } + + .workspace-indicator .workspaces-box { +- padding: 5px; + spacing: 3px; + } + +@@ -27,6 +26,20 @@ + spacing: 6px; + } + ++.workspace-indicator .workspace-box { ++ padding-top: 5px; ++ padding-bottom: 5px; ++} ++ ++.workspace-indicator StButton:first-child:ltr > .workspace-box, ++.workspace-indicator StButton:last-child:rtl > .workspace-box { ++ padding-left: 5px; ++} ++.workspace-indicator StButton:last-child:ltr > .workspace-box, ++.workspace-indicator StButton:first-child:rtl > .workspace-box { ++ padding-right: 5px; ++} ++ + .workspace-indicator-menu .workspace-box { + spacing: 6px; + } +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0001-Add-top-icons-extension.patch b/SOURCES/extra-extensions-0001-Add-top-icons-extension.patch new file mode 100644 index 0000000..e40226e --- /dev/null +++ b/SOURCES/extra-extensions-0001-Add-top-icons-extension.patch @@ -0,0 +1,158 @@ +From 778e3f5ec9b8897af89af1919381a14e2e3494f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 20 May 2015 17:44:50 +0200 +Subject: [PATCH 1/5] Add top-icons extension + +--- + extensions/top-icons/extension.js | 91 +++++++++++++++++++++++++++ + extensions/top-icons/meson.build | 9 +++ + extensions/top-icons/metadata.json.in | 10 +++ + meson.build | 1 + + 4 files changed, 111 insertions(+) + create mode 100644 extensions/top-icons/extension.js + create mode 100644 extensions/top-icons/meson.build + create mode 100644 extensions/top-icons/metadata.json.in + +diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js +new file mode 100644 +index 00000000..c28f1386 +--- /dev/null ++++ b/extensions/top-icons/extension.js +@@ -0,0 +1,91 @@ ++// SPDX-FileCopyrightText: 2018 Adel Gadllah ++// SPDX-FileCopyrightText: 2018 Florian Müllner ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import Clutter from 'gi://Clutter'; ++import Shell from 'gi://Shell'; ++import St from 'gi://St'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++import {Button as PanelButton} from 'resource:///org/gnome/shell/ui/panelMenu.js'; ++ ++const PANEL_ICON_SIZE = 16; ++ ++const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [ ++ 'bluetooth-applet', ++ 'gnome-sound-applet', ++ 'nm-applet', ++ 'gnome-power-manager', ++ 'keyboard', ++ 'a11y-keyboard', ++ 'kbd-scrolllock', ++ 'kbd-numlock', ++ 'kbd-capslock', ++ 'ibus-ui-gtk', ++]; ++ ++export default class SysTray { ++ constructor() { ++ this._icons = new Map(); ++ this._tray = null; ++ } ++ ++ _onTrayIconAdded(o, icon) { ++ let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : ''; ++ if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass)) ++ return; ++ ++ let button = new PanelButton(0.5, null, true); ++ ++ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; ++ let iconSize = PANEL_ICON_SIZE * scaleFactor; ++ ++ icon.set({ ++ width: iconSize, ++ height: iconSize, ++ x_align: Clutter.ActorAlign.CENTER, ++ y_align: Clutter.ActorAlign.CENTER, ++ }); ++ ++ let iconBin = new St.Widget({ ++ layout_manager: new Clutter.BinLayout(), ++ style_class: 'system-status-icon', ++ }); ++ iconBin.add_child(icon); ++ button.add_child(iconBin); ++ ++ this._icons.set(icon, button); ++ ++ button.connect('button-release-event', ++ (actor, event) => icon.click(event)); ++ button.connect('key-press-event', ++ (actor, event) => icon.click(event)); ++ ++ const role = `${icon}`; ++ Main.panel.addToStatusArea(role, button); ++ } ++ ++ _onTrayIconRemoved(o, icon) { ++ const button = this._icons.get(icon); ++ button?.destroy(); ++ this._icons.delete(icon); ++ } ++ ++ enable() { ++ this._tray = new Shell.TrayManager(); ++ this._tray.connect('tray-icon-added', ++ this._onTrayIconAdded.bind(this)); ++ this._tray.connect('tray-icon-removed', ++ this._onTrayIconRemoved.bind(this)); ++ this._tray.manage_screen(Main.panel); ++ } ++ ++ disable() { ++ this._icons.forEach(button => button.destroy()); ++ this._icons.clear(); ++ ++ this._tray.unmanage_screen(); ++ this._tray = null; ++ } ++} +diff --git a/extensions/top-icons/meson.build b/extensions/top-icons/meson.build +new file mode 100644 +index 00000000..b30272ad +--- /dev/null ++++ b/extensions/top-icons/meson.build +@@ -0,0 +1,9 @@ ++# SPDX-FileCopyrightText: 2018 Florian Müllner ++# ++# SPDX-License-Identifier: GPL-2.0-or-later ++ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) +diff --git a/extensions/top-icons/metadata.json.in b/extensions/top-icons/metadata.json.in +new file mode 100644 +index 00000000..1d2e0bc2 +--- /dev/null ++++ b/extensions/top-icons/metadata.json.in +@@ -0,0 +1,10 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"settings-schema": "@gschemaname@", ++"gettext-domain": "@gettext_domain@", ++"name": "Top Icons", ++"description": "Show legacy tray icons on top", ++"shell-version": [ "@shell_current@" ], ++"url": "@url@" ++} +diff --git a/meson.build b/meson.build +index 536efe49..a7294c18 100644 +--- a/meson.build ++++ b/meson.build +@@ -43,6 +43,7 @@ default_extensions += [ + 'light-style', + 'screenshot-window-sizer', + 'system-monitor', ++ 'top-icons', + 'windowsNavigator', + 'workspace-indicator' + ] +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0002-Add-gesture-inhibitor-extension.patch b/SOURCES/extra-extensions-0002-Add-gesture-inhibitor-extension.patch new file mode 100644 index 0000000..0d19d46 --- /dev/null +++ b/SOURCES/extra-extensions-0002-Add-gesture-inhibitor-extension.patch @@ -0,0 +1,181 @@ +From ff5063cb006a3723f422017d44787cfd908bb147 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Thu, 28 Jan 2021 00:06:12 +0100 +Subject: [PATCH 2/5] Add gesture-inhibitor extension + +This extension may disable default GNOME Shell gestures. +--- + extensions/gesture-inhibitor/extension.js | 79 +++++++++++++++++++ + extensions/gesture-inhibitor/meson.build | 8 ++ + extensions/gesture-inhibitor/metadata.json.in | 12 +++ + ...l.extensions.gesture-inhibitor.gschema.xml | 25 ++++++ + meson.build | 1 + + 5 files changed, 125 insertions(+) + create mode 100644 extensions/gesture-inhibitor/extension.js + create mode 100644 extensions/gesture-inhibitor/meson.build + create mode 100644 extensions/gesture-inhibitor/metadata.json.in + create mode 100644 extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml + +diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js +new file mode 100644 +index 00000000..872020ba +--- /dev/null ++++ b/extensions/gesture-inhibitor/extension.js +@@ -0,0 +1,79 @@ ++// SPDX-FileCopyrightText: 2021 Carlos Garnacho ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++// ++ ++import Clutter from 'gi://Clutter'; ++import Gio from 'gi://Gio'; ++import St from 'gi://St'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++ ++import {AppSwitchAction} from 'resource:///org/gnome/shell/ui/windowManager.js'; ++import {EdgeDragAction} from 'resource:///org/gnome/shell/ui/edgeDragAction.js'; ++ ++import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; ++ ++export default class GestureInhibitorExtension extends Extension { ++ constructor(metadata) { ++ super(metadata); ++ ++ let actions = global.stage.get_actions(); ++ ++ actions.forEach(a => { ++ if (a instanceof AppSwitchAction) ++ this._appSwitch = a; ++ else if (a instanceof EdgeDragAction && ++ a._side === St.Side.BOTTOM) ++ this._showOsk = a; ++ else if (a instanceof EdgeDragAction && ++ a._side === St.Side.TOP) ++ this._unfullscreen = a; ++ }); ++ ++ this._map = [ ++ {setting: 'overview', action: Main.overview._swipeTracker}, ++ {setting: 'app-switch', action: this._appSwitch}, ++ {setting: 'show-osk', action: this._showOsk}, ++ {setting: 'unfullscreen', action: this._unfullscreen}, ++ {setting: 'workspace-switch', action: Main.wm._workspaceAnimation._swipeTracker}, ++ ]; ++ ++ this._enabledDesc = Object.getOwnPropertyDescriptor( ++ Clutter.ActorMeta.prototype, 'enabled'); ++ } ++ ++ _overrideEnabledSetter(obj, set) { ++ if (!(obj instanceof Clutter.ActorMeta)) ++ return; ++ ++ const desc = set ++ ? {...this._enabledDesc, set} ++ : {...this._enabledDesc}; ++ Object.defineProperty(obj, 'enabled', desc); ++ } ++ ++ enable() { ++ const settings = this.getSettings(); ++ ++ this._map.forEach(m => { ++ settings.bind(m.setting, m.action, 'enabled', ++ Gio.SettingsBindFlags.DEFAULT); ++ ++ this._overrideEnabledSetter(m.action, function (value) { ++ if (settings.get_boolean(m.setting)) { ++ // eslint-disable-next-line no-invalid-this ++ this.set_enabled(value); ++ } ++ }); ++ }); ++ } ++ ++ disable() { ++ this._map.forEach(m => { ++ Gio.Settings.unbind(m.action, 'enabled'); ++ this._overrideEnabledSetter(m.action); ++ m.action.enabled = true; ++ }); ++ } ++} +diff --git a/extensions/gesture-inhibitor/meson.build b/extensions/gesture-inhibitor/meson.build +new file mode 100644 +index 00000000..fdad5cc8 +--- /dev/null ++++ b/extensions/gesture-inhibitor/meson.build +@@ -0,0 +1,8 @@ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) ++ ++# extension_sources += files('prefs.js') ++extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') +diff --git a/extensions/gesture-inhibitor/metadata.json.in b/extensions/gesture-inhibitor/metadata.json.in +new file mode 100644 +index 00000000..37d6a117 +--- /dev/null ++++ b/extensions/gesture-inhibitor/metadata.json.in +@@ -0,0 +1,12 @@ ++{ ++ "uuid": "@uuid@", ++ "extension-id": "@extension_id@", ++ "settings-schema": "@gschemaname@", ++ "gettext-domain": "@gettext_domain@", ++ "name": "Gesture Inhibitor", ++ "description": "Makes touchscreen gestures optional.", ++ "shell-version": [ "@shell_current@" ], ++ "original-authors": [ "cgarnach@redhat.com" ], ++ "url": "@url@" ++} ++ +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 +new file mode 100644 +index 00000000..b06d027a +--- /dev/null ++++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml +@@ -0,0 +1,25 @@ ++ ++ ++ ++ true ++ Show OSK gesture ++ ++ ++ true ++ Show Overview gesture ++ ++ ++ true ++ Application switch gesture ++ ++ ++ true ++ Workspace switch gesture ++ ++ ++ true ++ Unfullscreen gesture ++ ++ ++ ++ +diff --git a/meson.build b/meson.build +index a7294c18..e36d948d 100644 +--- a/meson.build ++++ b/meson.build +@@ -51,6 +51,7 @@ default_extensions += [ + all_extensions = default_extensions + all_extensions += [ + 'auto-move-windows', ++ 'gesture-inhibitor', + 'native-window-placement', + 'user-theme' + ] +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0003-Add-classification-banner.patch b/SOURCES/extra-extensions-0003-Add-classification-banner.patch new file mode 100644 index 0000000..f3f6bad --- /dev/null +++ b/SOURCES/extra-extensions-0003-Add-classification-banner.patch @@ -0,0 +1,479 @@ +From 950f0fded26d8664bce5410db4c280674147cdd8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 2 Dec 2021 19:39:50 +0100 +Subject: [PATCH] Add classification-banner + +--- + extensions/classification-banner/extension.js | 163 +++++++++++++++ + extensions/classification-banner/meson.build | 9 + + .../classification-banner/metadata.json.in | 11 + + ...tensions.classification-banner.gschema.xml | 29 +++ + extensions/classification-banner/prefs.js | 192 ++++++++++++++++++ + .../classification-banner/stylesheet.css | 3 + + meson.build | 1 + + 7 files changed, 408 insertions(+) + create mode 100644 extensions/classification-banner/extension.js + create mode 100644 extensions/classification-banner/meson.build + create mode 100644 extensions/classification-banner/metadata.json.in + create mode 100644 extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml + create mode 100644 extensions/classification-banner/prefs.js + create mode 100644 extensions/classification-banner/stylesheet.css + +diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js +new file mode 100644 +index 00000000..32c7d794 +--- /dev/null ++++ b/extensions/classification-banner/extension.js +@@ -0,0 +1,163 @@ ++// SPDX-FileCopyrightText: 2021 Florian Müllner ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import Clutter from 'gi://Clutter'; ++import Cogl from 'gi://Cogl'; ++import Gio from 'gi://Gio'; ++import GLib from 'gi://GLib'; ++import GObject from 'gi://GObject'; ++import Shell from 'gi://Shell'; ++import St from 'gi://St'; ++ ++import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js'; ++ ++class ClassificationBanner extends Clutter.Actor { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ #topBanner; ++ #bottomBanner; ++ #monitorConstraint; ++ #settings; ++ ++ constructor(index, settings) { ++ const constraint = new MonitorConstraint({index}); ++ super({ ++ layout_manager: new Clutter.BinLayout(), ++ constraints: constraint, ++ }); ++ this.#monitorConstraint = constraint; ++ ++ Shell.util_set_hidden_from_pick(this, true); ++ ++ this.#settings = settings; ++ ++ this.#topBanner = new St.BoxLayout({ ++ style_class: 'classification-banner', ++ x_expand: true, ++ y_expand: true, ++ y_align: Clutter.ActorAlign.START, ++ }); ++ this.add_child(this.#topBanner); ++ this.#settings.bind('top-banner', ++ this.#topBanner, 'visible', ++ Gio.SettingsBindFlags.GET); ++ ++ this.#bottomBanner = new St.BoxLayout({ ++ style_class: 'classification-banner', ++ x_expand: true, ++ y_expand: true, ++ y_align: Clutter.ActorAlign.END, ++ }); ++ this.add_child(this.#bottomBanner); ++ this.#settings.bind('bottom-banner', ++ this.#bottomBanner, 'visible', ++ Gio.SettingsBindFlags.GET); ++ ++ for (const banner of [this.#topBanner, this.#bottomBanner]) { ++ const label = new St.Label({ ++ style_class: 'classification-message', ++ x_align: Clutter.ActorAlign.CENTER, ++ x_expand: true, ++ }); ++ banner.add_child(label); ++ ++ this.#settings.bind('message', ++ label, 'text', ++ Gio.SettingsBindFlags.GET); ++ } ++ ++ const hostLabel = new St.Label({ ++ style_class: 'classification-system-info', ++ text: GLib.get_host_name(), ++ }); ++ this.#topBanner.insert_child_at_index(hostLabel, 0); ++ this.#settings.bind('system-info', ++ hostLabel, 'visible', ++ Gio.SettingsBindFlags.GET); ++ ++ const userLabel = new St.Label({ ++ style_class: 'classification-system-info', ++ text: GLib.get_user_name(), ++ }); ++ this.#topBanner.add_child(userLabel); ++ this.#settings.bind('system-info', ++ userLabel, 'visible', ++ Gio.SettingsBindFlags.GET); ++ ++ global.display.connectObject('in-fullscreen-changed', ++ () => this.#updateMonitorConstraint(), this); ++ this.#updateMonitorConstraint(); ++ ++ this.#settings.connectObject( ++ 'changed::color', () => this.#updateStyles(), ++ 'changed::background-color', () => this.#updateStyles(), ++ this); ++ this.#updateStyles(); ++ } ++ ++ #getColorSetting(key) { ++ const str = this.#settings.get_string(key); ++ const [valid, color] = Cogl.Color.from_string(str); ++ if (!valid) ++ return ''; ++ const {red, green, blue, alpha} = color; ++ 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'); ++ const style = `${bgStyle}${fgStyle}`; ++ this.#topBanner.set({style}); ++ this.#bottomBanner.set({style}); ++ } ++} ++ ++export default class ClassificationBannerExtension extends Extension { ++ #banners = []; ++ ++ #updateMonitors() { ++ const {monitors, panelBox, primaryIndex} = Main.layoutManager; ++ if (monitors.length !== this.#banners.length) { ++ this.#clearBanners(); ++ ++ const settings = this.getSettings(); ++ for (let i = 0; i < monitors.length; i++) { ++ const banner = new ClassificationBanner(i, settings); ++ Main.uiGroup.add_child(banner); ++ this.#banners.push(banner); ++ } ++ } ++ ++ const primaryBanner = this.#banners[primaryIndex]; ++ if (primaryBanner) ++ Main.uiGroup.set_child_below_sibling(primaryBanner, panelBox); ++ } ++ ++ #clearBanners() { ++ this.#banners.forEach(b => b.destroy()); ++ this.#banners = []; ++ } ++ ++ enable() { ++ Main.layoutManager.connectObject('monitors-changed', ++ () => this.#updateMonitors(), this); ++ this.#updateMonitors(); ++ } ++ ++ disable() { ++ Main.layoutManager.disconnectObject(this); ++ this.#clearBanners(); ++ } ++} +diff --git a/extensions/classification-banner/meson.build b/extensions/classification-banner/meson.build +new file mode 100644 +index 00000000..aa943741 +--- /dev/null ++++ b/extensions/classification-banner/meson.build +@@ -0,0 +1,9 @@ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) ++extension_data += files('stylesheet.css') ++ ++extension_sources += files('prefs.js') ++extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') +diff --git a/extensions/classification-banner/metadata.json.in b/extensions/classification-banner/metadata.json.in +new file mode 100644 +index 00000000..f93b1a2d +--- /dev/null ++++ b/extensions/classification-banner/metadata.json.in +@@ -0,0 +1,11 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"settings-schema": "@gschemaname@", ++"gettext-domain": "@gettext_domain@", ++"name": "Classification Banner", ++"description": "Display classification level banner", ++"shell-version": [ "@shell_current@" ], ++"session-modes": [ "gdm", "unlock-dialog", "user" ], ++"url": "@url@" ++} +diff --git a/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml +new file mode 100644 +index 00000000..0314ef60 +--- /dev/null ++++ b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml +@@ -0,0 +1,29 @@ ++ ++ ++ ++ true ++ Show a banner at the top ++ ++ ++ true ++ Show a banner at the bottom ++ ++ ++ "UNCLASSIFIED" ++ classification message ++ ++ ++ "#fff" ++ text color ++ ++ ++ "rgba(0,122,51,0.75)" ++ background color ++ ++ ++ false ++ Include system info in top banner ++ ++ ++ +diff --git a/extensions/classification-banner/prefs.js b/extensions/classification-banner/prefs.js +new file mode 100644 +index 00000000..dc73ddae +--- /dev/null ++++ b/extensions/classification-banner/prefs.js +@@ -0,0 +1,192 @@ ++import Adw from 'gi://Adw'; ++import Gdk from 'gi://Gdk'; ++import Gio from 'gi://Gio'; ++import GObject from 'gi://GObject'; ++import Gtk from 'gi://Gtk'; ++ ++import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; ++ ++class GenericPrefs extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ #actionGroup = new Gio.SimpleActionGroup(); ++ #settings; ++ ++ constructor(settings) { ++ super(); ++ ++ this.#settings = settings; ++ this.insert_action_group('options', this.#actionGroup); ++ ++ this.#actionGroup.add_action(settings.create_action('top-banner')); ++ this.#actionGroup.add_action(settings.create_action('bottom-banner')); ++ this.#actionGroup.add_action(settings.create_action('system-info')); ++ ++ this.add(new Adw.SwitchRow({ ++ title: _('Top Banner'), ++ action_name: 'options.top-banner', ++ })); ++ ++ this.add(new Adw.SwitchRow({ ++ title: _('Bottom Banner'), ++ action_name: 'options.bottom-banner', ++ })); ++ ++ this.add(new Adw.SwitchRow({ ++ title: _('System Info'), ++ action_name: 'options.system-info', ++ })); ++ } ++} ++ ++class BannerPreset extends GObject.Object { ++ static [GObject.properties] = { ++ 'message': GObject.ParamSpec.string( ++ 'message', 'message', 'message', ++ GObject.ParamFlags.READWRITE, ++ null), ++ 'color': GObject.ParamSpec.string( ++ 'color', 'color', 'color', ++ GObject.ParamFlags.READWRITE, ++ null), ++ 'background-color': GObject.ParamSpec.string( ++ 'background-color', 'background-color', 'background-color', ++ GObject.ParamFlags.READWRITE, ++ null), ++ }; ++ ++ static { ++ GObject.registerClass(this); ++ } ++} ++ ++class AppearancePrefs extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ #settings; ++ ++ constructor(settings) { ++ super(); ++ ++ this.#settings = settings; ++ ++ const model = new Gio.ListStore({item_type: BannerPreset.$gtype}); ++ model.append(new BannerPreset({ ++ message: 'UNCLASSIFIED', ++ color: '#fff', ++ background_color: 'rgba(0, 122, 51, 0.75)', ++ })); ++ model.append(new BannerPreset({ ++ message: 'CONFIDENTIAL', ++ color: '#fff', ++ background_color: 'rgba(0, 51, 160, 0.75)', ++ })); ++ model.append(new BannerPreset({ ++ message: 'SECRET', ++ color: '#fff', ++ background_color: 'rgba(200, 16, 46, 0.75)', ++ })); ++ model.append(new BannerPreset({ ++ message: 'TOP SECRET', ++ color: '#fff', ++ background_color: 'rgba(255, 103, 31, 0.75)', ++ })); ++ model.append(new BannerPreset({ ++ message: 'TOP SECRET//SCI', ++ color: '#000', ++ background_color: 'rgba(247, 234, 72, 0.75)', ++ })); ++ ++ let row, activatableWidget; ++ row = this.#createPresetsRow(model); ++ row.connect('notify::selected-item', comboRow => { ++ const {message, color, backgroundColor} = comboRow.selected_item; ++ this.#settings.set_string('message', message); ++ this.#settings.set_string('color', color); ++ this.#settings.set_string('background-color', backgroundColor); ++ }); ++ this.add(row); ++ ++ activatableWidget = new Gtk.Entry({ ++ valign: Gtk.Align.CENTER, ++ }); ++ this.#settings.bind('message', ++ activatableWidget, 'text', ++ Gio.SettingsBindFlags.DEFAULT); ++ row = new Adw.ActionRow({title: _('Message'), activatableWidget}); ++ row.add_suffix(activatableWidget); ++ this.add(row); ++ ++ activatableWidget = this.#createColorButton('background-color', { ++ use_alpha: true, ++ }); ++ row = new Adw.ActionRow({title: _('Background color'), activatableWidget}); ++ row.add_suffix(activatableWidget); ++ this.add(row); ++ ++ activatableWidget = this.#createColorButton('color'); ++ row = new Adw.ActionRow({title: _('Text color'), activatableWidget}); ++ row.add_suffix(activatableWidget); ++ this.add(row); ++ } ++ ++ #createPresetsRow(model) { ++ const listFactory = new Gtk.SignalListItemFactory(); ++ listFactory.connect('setup', ++ (f, item) => item.set_child(new Gtk.Label())); ++ listFactory.connect('bind', (f, listItem) => { ++ const {child, item} = listItem; ++ ++ const provider = new Gtk.CssProvider(); ++ provider.load_from_data(`* { ++ border-radius: 99px; ++ padding: 6px; ++ color: ${item.color}; ++ background-color: ${item.background_color}; ++ }`, -1); ++ child.get_style_context().add_provider(provider, ++ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ child.label = item.message; ++ }); ++ ++ return new Adw.ComboRow({ ++ title: _('Presets'), ++ model, ++ listFactory, ++ expression: Gtk.ConstantExpression.new_for_value(''), ++ }); ++ } ++ ++ #createColorButton(key, params = {}) { ++ const rgba = new Gdk.RGBA(); ++ rgba.parse(this.#settings.get_string(key)); ++ ++ const button = new Gtk.ColorButton({ ++ ...params, ++ rgba, ++ valign: Gtk.Align.CENTER, ++ }); ++ this.#settings.connect(`changed::${key}`, () => { ++ const newRgba = new Gdk.RGBA(); ++ newRgba.parse(this.#settings.get_string(key)); ++ if (!newRgba.equal(button.rgba)) ++ button.set({rgba: newRgba}); ++ }); ++ button.connect('notify::rgba', ++ () => this.#settings.set_string(key, button.rgba.to_string())); ++ return button; ++ } ++} ++ ++export default class ClassificationPrefs extends ExtensionPreferences { ++ getPreferencesWidget() { ++ const page = new Adw.PreferencesPage(); ++ page.add(new AppearancePrefs(this.getSettings())); ++ page.add(new GenericPrefs(this.getSettings())); ++ return page; ++ } ++} +diff --git a/extensions/classification-banner/stylesheet.css b/extensions/classification-banner/stylesheet.css +new file mode 100644 +index 00000000..fb6a697e +--- /dev/null ++++ b/extensions/classification-banner/stylesheet.css +@@ -0,0 +1,3 @@ ++.classification-system-info { padding: 0 24px; } ++.classification-message { font-weight: bold; } ++.classification-banner { font-size: 0.9em; } +diff --git a/meson.build b/meson.build +index e36d948d..63bd9ee0 100644 +--- a/meson.build ++++ b/meson.build +@@ -51,6 +51,7 @@ default_extensions += [ + all_extensions = default_extensions + all_extensions += [ + 'auto-move-windows', ++ 'classification-banner', + 'gesture-inhibitor', + 'native-window-placement', + 'user-theme' +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0004-Add-heads-up-display.patch b/SOURCES/extra-extensions-0004-Add-heads-up-display.patch new file mode 100644 index 0000000..84cc770 --- /dev/null +++ b/SOURCES/extra-extensions-0004-Add-heads-up-display.patch @@ -0,0 +1,878 @@ +From 271cee0eab89dcbf7b6c307c55cfbbbf843e7a0e Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 24 Aug 2021 15:03:57 -0400 +Subject: [PATCH 4/5] Add heads-up-display + +--- + extensions/heads-up-display/extension.js | 404 ++++++++++++++++++ + extensions/heads-up-display/headsUpMessage.js | 166 +++++++ + extensions/heads-up-display/meson.build | 13 + + extensions/heads-up-display/metadata.json.in | 12 + + ...ll.extensions.heads-up-display.gschema.xml | 60 +++ + extensions/heads-up-display/prefs.js | 92 ++++ + extensions/heads-up-display/stylesheet.css | 38 ++ + meson.build | 1 + + po/POTFILES.in | 1 + + 9 files changed, 787 insertions(+) + create mode 100644 extensions/heads-up-display/extension.js + create mode 100644 extensions/heads-up-display/headsUpMessage.js + create mode 100644 extensions/heads-up-display/meson.build + create mode 100644 extensions/heads-up-display/metadata.json.in + create mode 100644 extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml + create mode 100644 extensions/heads-up-display/prefs.js + create mode 100644 extensions/heads-up-display/stylesheet.css + +diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js +new file mode 100644 +index 00000000..a71b5925 +--- /dev/null ++++ b/extensions/heads-up-display/extension.js +@@ -0,0 +1,404 @@ ++// SPDX-FileCopyrightText: 2021 Ray Strode ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import GObject from 'gi://GObject'; ++import Meta from 'gi://Meta'; ++import Mtk from 'gi://Mtk'; ++ ++import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js'; ++ ++import {HeadsUpMessage} from './headsUpMessage.js'; ++ ++var HeadsUpConstraint = GObject.registerClass({ ++ Properties: { ++ 'offset': GObject.ParamSpec.int( ++ 'offset', 'Offset', 'offset', ++ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, ++ -1, 0, -1), ++ 'active': GObject.ParamSpec.boolean( ++ 'active', 'Active', 'active', ++ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, ++ true), ++ }, ++}, class HeadsUpConstraint extends MonitorConstraint { ++ constructor(props) { ++ super(props); ++ this._offset = 0; ++ this._active = true; ++ } ++ ++ get offset() { ++ return this._offset; ++ } ++ ++ set offset(o) { ++ this._offset = o; ++ } ++ ++ get active() { ++ return this._active; ++ } ++ ++ set active(a) { ++ this._active = a; ++ } ++ ++ vfunc_update_allocation(actor, actorBox) { ++ if (!Main.layoutManager.primaryMonitor) ++ return; ++ ++ if (!this.active) ++ return; ++ ++ if (actor.has_allocation()) ++ return; ++ ++ const workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); ++ actorBox.init_rect(workArea.x, workArea.y + this.offset, workArea.width, workArea.height - this.offset); ++ } ++}); ++ ++export default class HeadsUpDisplayExtension extends Extension { ++ enable() { ++ this._settings = this.getSettings('org.gnome.shell.extensions.heads-up-display'); ++ this._settings.connectObject('changed', ++ () => this._updateMessage(), this); ++ ++ this._idleMonitor = global.backend.get_core_idle_monitor(); ++ this._messageInhibitedUntilIdle = false; ++ global.window_manager.connectObject('map', ++ this._onWindowMap.bind(this), this); ++ ++ if (Main.layoutManager._startingUp) ++ Main.layoutManager.connectObject('startup-complete', () => this._onStartupComplete(), this); ++ else ++ this._onStartupComplete(); ++ } ++ ++ disable() { ++ this._dismissMessage(); ++ ++ this._stopWatchingForIdle(); ++ ++ Main.sessionMode.disconnectObject(this); ++ Main.overview.disconnectObject(this); ++ Main.layoutManager.panelBox.disconnectObject(this); ++ Main.layoutManager.disconnectObject(this); ++ global.window_manager.disconnectObject(this); ++ ++ if (this._screenShieldVisibleId) { ++ Main.screenShield._dialog._clock.disconnect(this._screenShieldVisibleId); ++ this._screenShieldVisibleId = 0; ++ } ++ ++ this._settings.disconnectObject(this); ++ delete this._settings; ++ } ++ ++ _onWindowMap(shellwm, actor) { ++ const windowObject = actor.meta_window; ++ const windowType = windowObject.get_window_type(); ++ ++ if (windowType !== Meta.WindowType.NORMAL) ++ return; ++ ++ if (!this._message || !this._message.visible) ++ return; ++ ++ const messageRect = new Mtk.Rectangle({ ++ x: this._message.x, ++ y: this._message.y, ++ width: this._message.width, ++ height: this._message.height, ++ }); ++ const windowRect = windowObject.get_frame_rect(); ++ ++ if (windowRect.intersect(messageRect)) ++ windowObject.move_frame(false, windowRect.x, this._message.y + this._message.height); ++ } ++ ++ _onStartupComplete() { ++ Main.overview.connectObject( ++ 'showing', () => this._updateMessage(), ++ 'hidden', () => this._updateMessage(), ++ this); ++ Main.layoutManager.panelBox.connectObject('notify::visible', ++ () => this._updateMessage(), this); ++ Main.sessionMode.connectObject('updated', ++ () => this._onSessionModeUpdated(), this); ++ ++ this._updateMessage(); ++ } ++ ++ _onSessionModeUpdated() { ++ if (!Main.sessionMode.hasWindows) ++ this._messageInhibitedUntilIdle = false; ++ ++ const dialog = Main.screenShield._dialog; ++ if (!Main.sessionMode.isGreeter && dialog && !this._screenShieldVisibleId) { ++ this._screenShieldVisibleId = dialog._clock.connect('notify::visible', this._updateMessage.bind(this)); ++ this._screenShieldDestroyId = dialog._clock.connect('destroy', () => { ++ this._screenShieldVisibleId = 0; ++ this._screenShieldDestroyId = 0; ++ }); ++ } ++ this._updateMessage(); ++ } ++ ++ _stopWatchingForIdle() { ++ if (this._idleWatchId) { ++ this._idleMonitor.remove_watch(this._idleWatchId); ++ this._idleWatchId = 0; ++ } ++ ++ if (this._idleTimeoutChangedId) { ++ this._settings.disconnect(this._idleTimeoutChangedId); ++ this._idleTimeoutChangedId = 0; ++ } ++ } ++ ++ _onIdleTimeoutChanged() { ++ this._stopWatchingForIdle(); ++ this._messageInhibitedUntilIdle = false; ++ } ++ ++ _onUserIdle() { ++ this._messageInhibitedUntilIdle = false; ++ this._updateMessage(); ++ } ++ ++ _watchForIdle() { ++ this._stopWatchingForIdle(); ++ ++ const idleTimeout = this._settings.get_uint('idle-timeout'); ++ ++ this._idleTimeoutChangedId = ++ this._settings.connect('changed::idle-timeout', ++ this._onIdleTimeoutChanged.bind(this)); ++ this._idleWatchId = this._idleMonitor.add_idle_watch(idleTimeout * 1000, ++ this._onUserIdle.bind(this)); ++ } ++ ++ _updateMessage() { ++ if (this._messageInhibitedUntilIdle) { ++ if (this._message) ++ this._dismissMessage(); ++ return; ++ } ++ ++ this._stopWatchingForIdle(); ++ ++ if (Main.sessionMode.hasOverview && Main.overview.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (!Main.layoutManager.panelBox.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ let supportedModes = []; ++ ++ if (this._settings.get_boolean('show-when-unlocked')) ++ supportedModes.push('user'); ++ ++ if (this._settings.get_boolean('show-when-unlocking') || ++ this._settings.get_boolean('show-when-locked')) ++ supportedModes.push('unlock-dialog'); ++ ++ if (this._settings.get_boolean('show-on-login-screen')) ++ supportedModes.push('gdm'); ++ ++ if (!supportedModes.includes(Main.sessionMode.currentMode) && ++ !supportedModes.includes(Main.sessionMode.parentMode)) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (Main.sessionMode.currentMode === 'unlock-dialog') { ++ const dialog = Main.screenShield._dialog; ++ if (!this._settings.get_boolean('show-when-locked')) { ++ if (dialog._clock.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ } ++ ++ if (!this._settings.get_boolean('show-when-unlocking')) { ++ if (!dialog._clock.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ } ++ } ++ ++ const heading = this._settings.get_string('message-heading'); ++ const body = this._settings.get_string('message-body'); ++ ++ if (!heading && !body) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (!this._message) { ++ this._message = new HeadsUpMessage(heading, body); ++ ++ this._message.connect('notify::allocation', this._adaptSessionForMessage.bind(this)); ++ this._message.connect('clicked', this._onMessageClicked.bind(this)); ++ } ++ ++ this._message.reactive = true; ++ this._message.track_hover = true; ++ ++ this._message.setHeading(heading); ++ this._message.setBody(body); ++ ++ if (!Main.sessionMode.hasWindows) { ++ this._message.track_hover = false; ++ this._message.reactive = false; ++ } ++ } ++ ++ _onMessageClicked() { ++ if (!Main.sessionMode.hasWindows) ++ return; ++ ++ this._watchForIdle(); ++ this._messageInhibitedUntilIdle = true; ++ this._updateMessage(); ++ } ++ ++ _dismissMessage() { ++ if (!this._message) ++ return; ++ ++ this._message.visible = false; ++ this._message.destroy(); ++ this._message = null; ++ this._resetMessageTray(); ++ this._resetLoginDialog(); ++ } ++ ++ _resetMessageTray() { ++ if (!Main.messageTray) ++ return; ++ ++ if (this._updateMessageTrayId) { ++ global.stage.disconnect(this._updateMessageTrayId); ++ this._updateMessageTrayId = 0; ++ } ++ ++ if (this._messageTrayConstraint) { ++ Main.messageTray.remove_constraint(this._messageTrayConstraint); ++ this._messageTrayConstraint = null; ++ } ++ } ++ ++ _alignMessageTray() { ++ if (!Main.messageTray) ++ return; ++ ++ if (!this._message || !this._message.visible) { ++ this._resetMessageTray(); ++ return; ++ } ++ ++ if (this._updateMessageTrayId) ++ return; ++ ++ this._updateMessageTrayId = global.stage.connect('before-update', () => { ++ if (!this._messageTrayConstraint) { ++ this._messageTrayConstraint = new HeadsUpConstraint({primary: true}); ++ ++ Main.layoutManager.panelBox.bind_property('visible', ++ this._messageTrayConstraint, 'active', ++ GObject.BindingFlags.SYNC_CREATE); ++ ++ Main.messageTray.add_constraint(this._messageTrayConstraint); ++ } ++ ++ const panelBottom = Main.layoutManager.panelBox.y + Main.layoutManager.panelBox.height; ++ const messageBottom = this._message.y + this._message.height; ++ ++ this._messageTrayConstraint.offset = messageBottom - panelBottom; ++ global.stage.disconnect(this._updateMessageTrayId); ++ this._updateMessageTrayId = 0; ++ }); ++ } ++ ++ _resetLoginDialog() { ++ if (!Main.sessionMode.isGreeter) ++ return; ++ ++ if (!Main.screenShield || !Main.screenShield._dialog) ++ return; ++ ++ const dialog = Main.screenShield._dialog; ++ ++ if (this._authPromptAllocatedId) { ++ dialog.disconnect(this._authPromptAllocatedId); ++ this._authPromptAllocatedId = 0; ++ } ++ ++ if (this._updateLoginDialogId) { ++ global.stage.disconnect(this._updateLoginDialogId); ++ this._updateLoginDialogId = 0; ++ } ++ ++ if (this._loginDialogConstraint) { ++ dialog.remove_constraint(this._loginDialogConstraint); ++ this._loginDialogConstraint = null; ++ } ++ } ++ ++ _adaptLoginDialogForMessage() { ++ if (!Main.sessionMode.isGreeter) ++ return; ++ ++ if (!Main.screenShield || !Main.screenShield._dialog) ++ return; ++ ++ if (!this._message || !this._message.visible) { ++ this._resetLoginDialog(); ++ return; ++ } ++ ++ const dialog = Main.screenShield._dialog; ++ ++ if (this._updateLoginDialogId) ++ return; ++ ++ this._updateLoginDialogId = global.stage.connect('before-update', () => { ++ let messageHeight = this._message.y + this._message.height; ++ if (dialog._logoBin.visible) ++ messageHeight -= dialog._logoBin.height; ++ ++ if (!this._logindDialogConstraint) { ++ this._loginDialogConstraint = new HeadsUpConstraint({primary: true}); ++ dialog.add_constraint(this._loginDialogConstraint); ++ } ++ ++ this._loginDialogConstraint.offset = messageHeight; ++ ++ global.stage.disconnect(this._updateLoginDialogId); ++ this._updateLoginDialogId = 0; ++ }); ++ } ++ ++ _adaptSessionForMessage() { ++ this._alignMessageTray(); ++ ++ if (Main.sessionMode.isGreeter) { ++ this._adaptLoginDialogForMessage(); ++ if (!this._authPromptAllocatedId) { ++ const dialog = Main.screenShield._dialog; ++ this._authPromptAllocatedId = dialog._authPrompt.connect('notify::allocation', this._adaptLoginDialogForMessage.bind(this)); ++ } ++ } ++ } ++} +diff --git a/extensions/heads-up-display/headsUpMessage.js b/extensions/heads-up-display/headsUpMessage.js +new file mode 100644 +index 00000000..30298847 +--- /dev/null ++++ b/extensions/heads-up-display/headsUpMessage.js +@@ -0,0 +1,166 @@ ++// SPDX-FileCopyrightText: 2021 Ray Strode ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import Atk from 'gi://Atk'; ++import Clutter from 'gi://Clutter'; ++import GObject from 'gi://GObject'; ++import St from 'gi://St'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++ ++const HeadsUpMessageBodyLabel = GObject.registerClass({ ++}, class HeadsUpMessageBodyLabel extends St.Label { ++ constructor(params) { ++ super(params); ++ ++ this._widthCoverage = 0.75; ++ this._heightCoverage = 0.25; ++ ++ global.display.connectObject('workareas-changed', ++ () => this._getWorkAreaAndMeasureLineHeight()); ++ } ++ ++ _getWorkAreaAndMeasureLineHeight() { ++ if (!this.get_parent()) ++ return; ++ ++ this._workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); ++ ++ this.clutter_text.single_line_mode = true; ++ this.clutter_text.line_wrap = false; ++ ++ this._lineHeight = super.vfunc_get_preferred_height(-1)[0]; ++ ++ this.clutter_text.single_line_mode = false; ++ this.clutter_text.line_wrap = true; ++ } ++ ++ vfunc_parent_set() { ++ this._getWorkAreaAndMeasureLineHeight(); ++ } ++ ++ vfunc_get_preferred_width(forHeight) { ++ const maxWidth = this._widthCoverage * this._workArea.width; ++ ++ let [labelMinimumWidth, labelNaturalWidth] = super.vfunc_get_preferred_width(forHeight); ++ ++ labelMinimumWidth = Math.min(labelMinimumWidth, maxWidth); ++ labelNaturalWidth = Math.min(labelNaturalWidth, maxWidth); ++ ++ return [labelMinimumWidth, labelNaturalWidth]; ++ } ++ ++ vfunc_get_preferred_height(forWidth) { ++ const labelHeightUpperBound = this._heightCoverage * this._workArea.height; ++ const numberOfLines = Math.floor(labelHeightUpperBound / this._lineHeight); ++ this._numberOfLines = Math.max(numberOfLines, 1); ++ ++ const maxHeight = this._lineHeight * this._numberOfLines; ++ ++ let [labelMinimumHeight, labelNaturalHeight] = super.vfunc_get_preferred_height(forWidth); ++ ++ labelMinimumHeight = Math.min(labelMinimumHeight, maxHeight); ++ labelNaturalHeight = Math.min(labelNaturalHeight, maxHeight); ++ ++ return [labelMinimumHeight, labelNaturalHeight]; ++ } ++}); ++ ++export const HeadsUpMessage = GObject.registerClass({ ++}, class HeadsUpMessage extends St.Button { ++ constructor(heading, body) { ++ super({ ++ style_class: 'message', ++ accessible_role: Atk.Role.NOTIFICATION, ++ can_focus: false, ++ opacity: 0, ++ }); ++ ++ Main.layoutManager.addChrome(this, {affectsInputRegion: true}); ++ ++ this.add_style_class_name('heads-up-display-message'); ++ ++ this.connect('destroy', () => this._onDestroy()); ++ ++ Main.layoutManager.panelBox.connectObject('notify::allocation', ++ () => this._alignWithPanel()); ++ this.connect('notify::allocation', ++ () => this._alignWithPanel()); ++ ++ const contentsBox = new St.BoxLayout({ ++ style_class: 'heads-up-message-content', ++ vertical: true, ++ x_align: Clutter.ActorAlign.CENTER, ++ }); ++ this.add_child(contentsBox); ++ ++ this._headingLabel = new St.Label({ ++ style_class: 'heads-up-message-heading', ++ x_expand: true, ++ x_align: Clutter.ActorAlign.CENTER, ++ }); ++ ++ this.setHeading(heading); ++ contentsBox.add_child(this._headingLabel); ++ ++ this._bodyLabel = new HeadsUpMessageBodyLabel({ ++ style_class: 'heads-up-message-body', ++ x_expand: true, ++ y_expand: true, ++ }); ++ contentsBox.add_child(this._bodyLabel); ++ ++ this.setBody(body); ++ } ++ ++ vfunc_parent_set() { ++ this._alignWithPanel(); ++ } ++ ++ _alignWithPanel() { ++ if (this._beforeUpdateId) ++ return; ++ ++ this._beforeUpdateId = global.stage.connect('before-update', () => { ++ let x = Main.panel.x; ++ let y = Main.panel.y + Main.panel.height; ++ ++ x += Main.panel.width / 2; ++ x -= this.width / 2; ++ x = Math.floor(x); ++ this.set_position(x, y); ++ this.opacity = 255; ++ ++ global.stage.disconnect(this._beforeUpdateId); ++ this._beforeUpdateId = 0; ++ }); ++ } ++ ++ setHeading(text) { ++ if (text) { ++ const heading = text ? text.replace(/\n/g, ' ') : ''; ++ this._headingLabel.text = heading; ++ this._headingLabel.visible = true; ++ } else { ++ this._headingLabel.text = text; ++ this._headingLabel.visible = false; ++ } ++ } ++ ++ setBody(text) { ++ this._bodyLabel.text = text; ++ ++ if (text) ++ this._bodyLabel.visible = true; ++ else ++ this._bodyLabel.visible = false; ++ } ++ ++ _onDestroy() { ++ if (this._beforeUpdateId) { ++ global.stage.disconnect(this._beforeUpdateId); ++ this._beforeUpdateId = 0; ++ } ++ } ++}); +diff --git a/extensions/heads-up-display/meson.build b/extensions/heads-up-display/meson.build +new file mode 100644 +index 00000000..42ce222c +--- /dev/null ++++ b/extensions/heads-up-display/meson.build +@@ -0,0 +1,13 @@ ++# SPDX-FileCopyrightText: 2021 Ray Strode ++# ++# SPDX-License-Identifier: GPL-2.0-or-later ++ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) ++ ++extension_data += files('stylesheet.css') ++extension_sources += files('headsUpMessage.js', 'prefs.js') ++extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') +diff --git a/extensions/heads-up-display/metadata.json.in b/extensions/heads-up-display/metadata.json.in +new file mode 100644 +index 00000000..01bcd4df +--- /dev/null ++++ b/extensions/heads-up-display/metadata.json.in +@@ -0,0 +1,12 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"settings-schema": "@gschemaname@", ++"gettext-domain": "@gettext_domain@", ++"name": "Heads-up Display Message", ++"description": "Add a message to be displayed on screen always above all windows and chrome.", ++"original-authors": [ "rstrode@redhat.com" ], ++"shell-version": [ "@shell_current@" ], ++"url": "@url@", ++"session-modes": [ "gdm", "lock-screen", "unlock-dialog", "user" ] ++} +diff --git a/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml +new file mode 100644 +index 00000000..1e2119c8 +--- /dev/null ++++ b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml +@@ -0,0 +1,60 @@ ++ ++ ++ ++ ++ ++ 30 ++ Idle Timeout ++ ++ Number of seconds until message is reshown after user goes idle. ++ ++ ++ ++ "" ++ Message to show at top of display ++ ++ The top line of the heads up display message. ++ ++ ++ ++ "" ++ Banner message ++ ++ A message to always show at the top of the screen. ++ ++ ++ ++ true ++ Show on login screen ++ ++ Whether or not the message should display on the login screen ++ ++ ++ ++ false ++ Show on screen shield ++ ++ Whether or not the message should display when the screen is locked ++ ++ ++ ++ false ++ Show on unlock screen ++ ++ Whether or not the message should display on the unlock screen. ++ ++ ++ ++ false ++ Show in user session ++ ++ Whether or not the message should display when the screen is unlocked. ++ ++ ++ ++ +diff --git a/extensions/heads-up-display/prefs.js b/extensions/heads-up-display/prefs.js +new file mode 100644 +index 00000000..304c8813 +--- /dev/null ++++ b/extensions/heads-up-display/prefs.js +@@ -0,0 +1,92 @@ ++// SPDX-FileCopyrightText: 2021 Ray Strode ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import Adw from 'gi://Adw'; ++import Gio from 'gi://Gio'; ++import GObject from 'gi://GObject'; ++import Gtk from 'gi://Gtk'; ++ ++import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; ++ ++class GeneralGroup extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor(settings) { ++ super(); ++ ++ const actionGroup = new Gio.SimpleActionGroup(); ++ this.insert_action_group('options', actionGroup); ++ ++ actionGroup.add_action(settings.create_action('show-when-locked')); ++ actionGroup.add_action(settings.create_action('show-when-unlocking')); ++ actionGroup.add_action(settings.create_action('show-when-unlocked')); ++ ++ this.add(new Adw.SwitchRow({ ++ title: _('Show message when screen is locked'), ++ action_name: 'options.show-when-locked', ++ })); ++ this.add(new Adw.SwitchRow({ ++ title: _('Show message on unlock screen'), ++ action_name: 'options.show-when-unlocking', ++ })); ++ this.add(new Adw.SwitchRow({ ++ title: _('Show message when screen is unlocked'), ++ action_name: 'options.show-when-unlocked', ++ })); ++ ++ const spinRow = new Adw.SpinRow({ ++ title: _('Seconds after user goes idle before reshowing message'), ++ adjustment: new Gtk.Adjustment({ ++ lower: 0, ++ upper: 2147483647, ++ step_increment: 1, ++ page_increment: 60, ++ page_size: 60, ++ }), ++ }); ++ settings.bind('idle-timeout', ++ spinRow, 'value', ++ Gio.SettingsBindFlags.DEFAULT); ++ this.add(spinRow); ++ } ++} ++ ++class MessageGroup extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor(settings) { ++ super({ ++ title: _('Message'), ++ }); ++ ++ const textView = new Gtk.TextView({ ++ accepts_tab: false, ++ wrap_mode: Gtk.WrapMode.WORD, ++ top_margin: 6, ++ bottom_margin: 6, ++ left_margin: 6, ++ right_margin: 6, ++ vexpand: true, ++ }); ++ textView.add_css_class('card'); ++ ++ settings.bind('message-body', ++ textView.get_buffer(), 'text', ++ Gio.SettingsBindFlags.DEFAULT); ++ this.add(textView); ++ } ++} ++ ++export default class HeadsUpDisplayPrefs extends ExtensionPreferences { ++ getPreferencesWidget() { ++ const page = new Adw.PreferencesPage(); ++ page.add(new GeneralGroup(this.getSettings())); ++ page.add(new MessageGroup(this.getSettings())); ++ return page; ++ } ++} +diff --git a/extensions/heads-up-display/stylesheet.css b/extensions/heads-up-display/stylesheet.css +new file mode 100644 +index 00000000..a1a34e3f +--- /dev/null ++++ b/extensions/heads-up-display/stylesheet.css +@@ -0,0 +1,38 @@ ++/* ++ * SPDX-FileCopyrightText: 2021 Ray Strode ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++.heads-up-display-message { ++ background-color: rgba(0.24, 0.24, 0.24, 0.80); ++ border: 1px solid black; ++ border-radius: 6px; ++ color: #eeeeec; ++ font-size: 11pt; ++ margin-top: 0.5em; ++ margin-bottom: 0.5em; ++ padding: 0.9em; ++} ++ ++.heads-up-display-message:insensitive { ++ background-color: rgba(0.24, 0.24, 0.24, 0.33); ++} ++ ++.heads-up-display-message:hover { ++ background-color: rgba(0.24, 0.24, 0.24, 0.2); ++ border: 1px solid rgba(0.0, 0.0, 0.0, 0.5); ++ color: #4d4d4d; ++ transition-duration: 250ms; ++} ++ ++.heads-up-message-heading { ++ height: 1.75em; ++ font-size: 1.25em; ++ font-weight: bold; ++ text-align: center; ++} ++ ++.heads-up-message-body { ++ text-align: center; ++} +diff --git a/meson.build b/meson.build +index 63bd9ee0..82269ff5 100644 +--- a/meson.build ++++ b/meson.build +@@ -40,6 +40,7 @@ classic_extensions = [ + default_extensions = classic_extensions + default_extensions += [ + 'drive-menu', ++ 'heads-up-display', + 'light-style', + 'screenshot-window-sizer', + 'system-monitor', +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 447465a1..b7cb8a7c 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -6,6 +6,7 @@ extensions/auto-move-windows/extension.js + extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml + extensions/auto-move-windows/prefs.js + extensions/drive-menu/extension.js ++extensions/heads-up-display/prefs.js + extensions/native-window-placement/extension.js + extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml + extensions/places-menu/extension.js +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0005-Add-custom-menu-extension.patch b/SOURCES/extra-extensions-0005-Add-custom-menu-extension.patch new file mode 100644 index 0000000..27d5772 --- /dev/null +++ b/SOURCES/extra-extensions-0005-Add-custom-menu-extension.patch @@ -0,0 +1,719 @@ +From b146a94c18e9e9ddbc7f29e8d885768d19fd7d49 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 12 Jan 2023 19:43:52 +0100 +Subject: [PATCH 5/5] Add custom-menu extension + +--- + extensions/custom-menu/config.js | 445 ++++++++++++++++++++++++ + extensions/custom-menu/extension.js | 201 +++++++++++ + extensions/custom-menu/meson.build | 7 + + extensions/custom-menu/metadata.json.in | 10 + + meson.build | 1 + + 5 files changed, 664 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 + +diff --git a/extensions/custom-menu/config.js b/extensions/custom-menu/config.js +new file mode 100644 +index 00000000..d08e3201 +--- /dev/null ++++ b/extensions/custom-menu/config.js +@@ -0,0 +1,445 @@ ++import Gio from 'gi://Gio'; ++import GLib from 'gi://GLib'; ++import Json from 'gi://Json'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; ++ ++import {getLogger} from './extension.js'; ++ ++class Entry { ++ constructor(prop) { ++ this.type = prop.type; ++ this.title = prop.title || ""; ++ this.__vars = prop.__vars || []; ++ this.updateEnv(prop); ++ } ++ ++ setTitle(text) { ++ this.item.label.get_clutter_text().set_text(text); ++ } ++ ++ updateEnv(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() { ++ } ++ ++ _try_destroy() { ++ try { ++ if (this.item && this.item.destroy) { ++ this.item.destroy(); ++ } ++ } catch(e) { /* Ignore all errors during destory*/ } ++ } ++} ++ ++class DerivedEntry { ++ constructor(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(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; ++ } ++} ++ ++let __pipeOpenQueue = []; ++ ++/* callback: function (stdout, stderr, exit_status) { } */ ++function pipeOpen(cmdline, env, callback) { ++ if (cmdline === undefined || callback === undefined) { ++ return false; ++ } ++ realPipeOpen(cmdline, env, callback); ++ 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) { ++ const decoder = new TextDecoder(); ++ stdout_content = decoder.decode(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 = decoder.decode(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); ++ 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); ++ } ++ }); ++} ++ ++// Detect menu toggle on startup ++function _toggleDetect(command, env, object) { ++ pipeOpen(command, env, function(stdout, stderr, exit_status) { ++ if (exit_status == 0) { ++ object.item.setToggleState(true); ++ } ++ }); ++} /**/ ++ ++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 = { }; ++ ++class TogglerEntry extends Entry { ++ constructor(prop) { ++ super(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() { ++ 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', this._onManuallyToggled.bind(this)); ++ this._loadState(); ++ _toggleDetect(this.detector, this.__env, this); ++ return this.item; ++ } ++ ++ _onManuallyToggled(_, state) { ++ // when switched on again, this flag will get cleared. ++ this._manually_switched_off = !state; ++ this._storeState(state); ++ this._onToggled(state); ++ } ++ ++ _onToggled(state) { ++ if (state) { ++ _generalSpawn(this.command_on, this.__env, this.title); ++ } else { ++ _generalSpawn(this.command_off, this.__env, this.title); ++ } ++ } ++ ++ _detect(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*$/))); ++ }); ++ } ++ ++ // compare the new state with cached state notify when state is different ++ compareState(new_state) { ++ 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(state) { ++ let hash = JSON.stringify({ env: this.__env, detector: this.detector }); ++ _toggler_state_cache[hash] = state; ++ } ++ ++ _loadState() { ++ 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() { ++ this._detect(state => { ++ this.compareState(state); ++ this._storeState(state); ++ this._loadState(); ++ 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() { ++ this.item.toggle(); ++ } ++} ++ ++class LauncherEntry extends Entry { ++ constructor(prop) { ++ super(prop); ++ this.command = prop.command || ""; ++ } ++ ++ createItem() { ++ this._try_destroy(); ++ this.item = new PopupMenu.PopupMenuItem(this.title); ++ this.item.label.get_clutter_text().set_use_markup(true); ++ this.item.connect('activate', this._onClicked.bind(this)); ++ return this.item; ++ } ++ ++ _onClicked(_) { ++ _generalSpawn(this.command, this.__env, this.title); ++ } ++ ++ perform() { ++ this.item.emit('activate'); ++ } ++} ++ ++class SubMenuEntry extends Entry { ++ constructor(prop) { ++ super(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() { ++ 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() { ++ for (let i in this.entries) { ++ let entry = this.entries[i]; ++ entry.pulse(); ++ } ++ } ++} ++ ++class SeparatorEntry extends Entry { ++ createItem() { ++ 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); ++} ++ ++export class Loader { ++ constructor(filename) { ++ if (filename) { ++ this.loadConfig(filename); ++ } ++ } ++ ++ loadConfig(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(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..9edbc548 +--- /dev/null ++++ b/extensions/custom-menu/extension.js +@@ -0,0 +1,201 @@ ++/* 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 . ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++/** ++ * @author Ben ++ * @see https://github.com/andreabenini/gnome-plugin.custom-menu-panel ++ */ ++ ++const CONFIGURATION_FILE = '/.entries.json'; ++ ++import Gio from 'gi://Gio'; ++import GLib from 'gi://GLib'; ++import GObject from 'gi://GObject'; ++import St from 'gi://St'; ++ ++import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; ++ ++import * as Main from 'resource:///org/gnome/shell/ui/main.js'; ++import * as BackgroundMenu from 'resource:///org/gnome/shell/ui/backgroundMenu.js'; ++import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; ++import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; ++ ++import * as Config from './config.js'; ++ ++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)); ++ } ++ } ++); /**/ ++ ++ ++class Logger { ++ constructor(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() { ++ this.log = function(_) { }; ++ } ++ ++ _initGnomeLog() { ++ this.log = function(s) { ++ global.log("custom-menu-panel> " + s); ++ }; ++ } ++ ++ _initFileLog() { ++ 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(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; ++export function getLogger() { ++ if (logger === null) { ++ logger = new Logger("gnome-shell"); ++ } ++ return logger; ++} /**/ ++ ++export default class CustomMenuExtension extends Extension { ++ enable() { ++ BackgroundMenu.BackgroundMenu = CustomBackgroundMenu; ++ Main.layoutManager._updateBackgrounds(); ++ } ++ ++ disable() { ++ BackgroundMenu.BackgroundMenu = OriginalBackgroundMenu; ++ Main.layoutManager._updateBackgrounds(); ++ } ++} /**/ +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/meson.build b/meson.build +index 82269ff5..dce1731c 100644 +--- a/meson.build ++++ b/meson.build +@@ -53,6 +53,7 @@ all_extensions = default_extensions + all_extensions += [ + 'auto-move-windows', + 'classification-banner', ++ 'custom-menu', + 'gesture-inhibitor', + 'native-window-placement', + 'user-theme' +-- +2.45.2 + diff --git a/SOURCES/extra-extensions-0006-Add-desktop-icons-extension.patch b/SOURCES/extra-extensions-0006-Add-desktop-icons-extension.patch new file mode 100644 index 0000000..60639f8 --- /dev/null +++ b/SOURCES/extra-extensions-0006-Add-desktop-icons-extension.patch @@ -0,0 +1,72 @@ +From d0f2273765ab61e55c5cf10e7283a545fcafa947 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 Aug 2024 13:24:20 +0200 +Subject: [PATCH] Add stub desktop-icons extension + +--- + extensions/desktop-icons/extension.js | 5 +++++ + extensions/desktop-icons/meson.build | 9 +++++++++ + extensions/desktop-icons/metadata.json.in | 10 ++++++++++ + meson.build | 1 + + 4 files changed, 25 insertions(+) + create mode 100644 extensions/desktop-icons/extension.js + create mode 100644 extensions/desktop-icons/meson.build + create mode 100644 extensions/desktop-icons/metadata.json.in + +diff --git a/extensions/desktop-icons/extension.js b/extensions/desktop-icons/extension.js +new file mode 100644 +index 00000000..bbc96ef2 +--- /dev/null ++++ b/extensions/desktop-icons/extension.js +@@ -0,0 +1,5 @@ ++// SPDX-FileCopyrightText: 2024 Florian Müllner ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++export {Extension as default} from 'resource:///org/gnome/shell/extensions/extension.js'; +diff --git a/extensions/desktop-icons/meson.build b/extensions/desktop-icons/meson.build +new file mode 100644 +index 00000000..7b28a2ef +--- /dev/null ++++ b/extensions/desktop-icons/meson.build +@@ -0,0 +1,9 @@ ++# SPDX-FileCopyrightText: 2017 Florian Müllner ++# ++# SPDX-License-Identifier: GPL-2.0-or-later ++ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) +diff --git a/extensions/desktop-icons/metadata.json.in b/extensions/desktop-icons/metadata.json.in +new file mode 100644 +index 00000000..78a55abb +--- /dev/null ++++ b/extensions/desktop-icons/metadata.json.in +@@ -0,0 +1,10 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"settings-schema": "@gschemaname@", ++"gettext-domain": "@gettext_domain@", ++"name": "Desktop Icons", ++"description": "Show icons on the desktop", ++"shell-version": [ "@shell_current@" ], ++"url": "@url@" ++} +diff --git a/meson.build b/meson.build +index b915b68c..63a7432e 100644 +--- a/meson.build ++++ b/meson.build +@@ -40,6 +40,7 @@ classic_extensions = [ + + default_extensions = classic_extensions + default_extensions += [ ++ 'desktop-icons', + 'drive-menu', + 'heads-up-display', + 'light-style', +-- +2.45.2 + diff --git a/SPECS/gnome-shell-extensions.spec b/SPECS/gnome-shell-extensions.spec new file mode 100644 index 0000000..1606cda --- /dev/null +++ b/SPECS/gnome-shell-extensions.spec @@ -0,0 +1,1202 @@ +## START: Set by rpmautospec +## (rpmautospec version 0.6.5) +## RPMAUTOSPEC: autorelease, autochangelog +%define autorelease(e:s:pb:n) %{?-p:0.}%{lua: + release_number = 3; + base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); + print(release_number + base_release_number - 1); +}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} +## END: Set by rpmautospec + +# Minimum GNOME Shell version supported +%global min_gs_version %%(cut -d "." -f 1 <<<%{version}) + +%global pkg_prefix gnome-shell-extension +%global tarball_version %%(echo %{version} | tr '~' '.') +%global major_version %%(cut -d "." -f 1 <<<%{tarball_version}) + +%if 0%{?rhel} +%global xsession 0 +%else +%global xsession 1 +%endif + +Name: gnome-shell-extensions +Version: 47~alpha +Release: %autorelease +Summary: Modify and extend GNOME Shell functionality and behavior + +License: GPL-2.0-or-later +URL: http://wiki.gnome.org/Projects/GnomeShell/Extensions +Source0: http://ftp.gnome.org/pub/GNOME/sources/%{name}/%{major_version}/%{name}-%{tarball_version}.tar.xz + +BuildRequires: meson +BuildRequires: git +BuildRequires: gettext >= 0.19.6 +BuildRequires: glib2%{?_isa} +Requires: gnome-shell >= %{min_gs_version} +BuildArch: noarch + + +Patch: extra-extensions-0001-Add-top-icons-extension.patch +Patch: extra-extensions-0002-Add-gesture-inhibitor-extension.patch +Patch: extra-extensions-0003-Add-classification-banner.patch +Patch: extra-extensions-0004-Add-heads-up-display.patch +Patch: extra-extensions-0005-Add-custom-menu-extension.patch +Patch: extra-extensions-0006-Add-desktop-icons-extension.patch + +Patch: 0001-Include-top-icons-in-classic-session.patch + +Patch: 0001-workspace-indicator-Re-fittsify-workspace-previews.patch + +%description +GNOME Shell Extensions is a collection of extensions providing additional and +optional functionality to GNOME Shell. + +Enabled extensions: + * apps-menu + * auto-move-windows + * classification-banner + * custom-menu + * desktop-icons + * drive-menu + * gesture-inhibitor + * heads-up-display + * launch-new-instance + * light-style + * native-window-placement + * places-menu + * screenshot-window-sizer + * system-monitor + * top-icons + * user-theme + * window-list + * windowsNavigator + * workspace-indicator + + +%package -n %{pkg_prefix}-common +Summary: Files common to GNOME Shell Extensions +License: GPL-2.0-or-later +Requires: gnome-shell >= %{min_gs_version} +Obsoletes: %{pkg_prefix}-horizontal-workspaces < 40.0~alpha.1-3 + +%description -n %{pkg_prefix}-common +GNOME Shell Extensions is a collection of extensions providing additional and +optional functionality to GNOME Shell. + +This package provides common data files shared by various extensions. + + +%package -n gnome-classic-session +Summary: GNOME "classic" mode session +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-apps-menu = %{version}-%{release} +Requires: %{pkg_prefix}-launch-new-instance = %{version}-%{release} +Requires: %{pkg_prefix}-places-menu = %{version}-%{release} +Requires: %{pkg_prefix}-window-list = %{version}-%{release} +Requires: nautilus + +%description -n gnome-classic-session +This package contains the required components for the GNOME Shell "classic" +mode, which aims to provide a GNOME 2-like user interface. + + +%if %{xsession} +%package -n gnome-classic-session-xsession +Summary: GNOME "classic" mode session on X11 +License: GPL-2.0-or-later +Requires: gnome-classic-session = %{version}-%{release} +# The X11 session is deprecated and eventually will be removed +Provides: deprecated() + +%description -n gnome-classic-session-xsession +This package contains the required components for the GNOME Shell "classic" +mode on X11, which aims to provide a GNOME 2-like user interface. +%endif + + +%package -n %{pkg_prefix}-apps-menu +Summary: Application menu for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} +Requires: gnome-menus + +%description -n %{pkg_prefix}-apps-menu +This GNOME Shell extension adds a GNOME 2.x style menu for applications. + + +%package -n %{pkg_prefix}-auto-move-windows +Summary: Assign specific workspaces to applications in GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-auto-move-windows +This GNOME Shell extension enables easy workspace management. A specific +workspace can be assigned to each application as soon as it creates a window, in +a manner configurable with a GSettings key. + + +%package -n %{pkg_prefix}-classification-banner +Summary: Display classification level banner in GNOME Shell +Group: User Interface/Desktops +License: GPLv2+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-classification-banner +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}-desktop-icons +Summary: Desktop icons support for GNOME Shell (stub) +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-desktop-icons +This GNOME Shell extension provides support for icons on the desktop. + + +%package -n %{pkg_prefix}-drive-menu +Summary: Drive status menu for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-drive-menu +This GNOME Shell extension provides a panel status menu for accessing and +unmounting removable devices. + + +%package -n %{pkg_prefix}-gesture-inhibitor +Summary: Gesture inhibitor +Group: User Interface/Desktops +License: GPLv2+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-gesture-inhibitor +This GNOME Shell extension allows disabling the default desktop gestures. + + +%package -n %{pkg_prefix}-heads-up-display +Summary: Display persistent on-screen message +Group: User Interface/Desktops +License: GPLv3+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-heads-up-display +This GNOME Shell extension displays a persistent message in the top middle of the screen. +This message can appear on the login screen, lock screen, or regular user session. + + +%package -n %{pkg_prefix}-launch-new-instance +Summary: Always launch a new application instance for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-launch-new-instance +This GNOME Shell extension modifies the behavior of clicking in the dash and app +launcher to always launch a new application instance. + + +%package -n %{pkg_prefix}-light-style +Summary: Use light style in GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-light-style +This GNOME Shell extension changes the default style to light. + + +%package -n %{pkg_prefix}-native-window-placement +Summary: Native window placement for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-native-window-placement +This GNOME Shell extension provides additional configurability for the window +layout in the overview, including a mechanism similar to KDE4. + + +%package -n %{pkg_prefix}-places-menu +Summary: Places status menu for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-places-menu +This GNOME Shell extension add a system status menu for quickly navigating +places in the system. + + +%package -n %{pkg_prefix}-screenshot-window-sizer +Summary: Screenshot window sizer for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-screenshot-window-sizer +This GNOME Shell extension allows to easily resize windows for GNOME Software +screenshots. + + +%package -n %{pkg_prefix}-system-monitor +Summary: System monitor for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-system-monitor +This GNOME Shell extension displays system usage information in the top bar. + + +%package -n %{pkg_prefix}-top-icons +Summary: Show legacy icons on top +License: GPLv2+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-top-icons +This GNOME Shell extension moves legacy tray icons into the top bar + + +%package -n %{pkg_prefix}-user-theme +Summary: Support for custom themes in GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-user-theme +This GNOME Shell extension enables loading a GNOME Shell theme from +~/.themes//gnome-shell/. + + +%package -n %{pkg_prefix}-window-list +Summary: Display a window list at the bottom of the screen in GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-window-list +This GNOME Shell extension displays a window list at the bottom of the screen. + + +%package -n %{pkg_prefix}-windowsNavigator +Summary: Support for keyboard selection of windows and workspaces in GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-windowsNavigator +This GNOME Shell extension enables keyboard selection of windows and workspaces +in overlay mode, by pressing the Alt and Ctrl key respectively. + + +%package -n %{pkg_prefix}-workspace-indicator +Summary: Workspace indicator for GNOME Shell +License: GPL-2.0-or-later +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-workspace-indicator +This GNOME Shell extension add a system status menu for quickly changing +workspaces. + + +%prep +%autosetup -S git -n %{name}-%{tarball_version} + + +%build +%meson -Dextension_set="all" -Dclassic_mode=true +%meson_build + + +%install +%meson_install + +%find_lang %{name} + +%if !%{xsession} +rm -rf %{buildroot}/%{_datadir}/xsessions +%endif + + +%files -n %{pkg_prefix}-common -f %{name}.lang +%doc NEWS README.md +%license COPYING + + +%files -n gnome-classic-session +%{_datadir}/gnome-shell/modes/classic.json +%{_datadir}/wayland-sessions/gnome-classic.desktop +%{_datadir}/wayland-sessions/gnome-classic-wayland.desktop +%{_datadir}/glib-2.0/schemas/00_org.gnome.shell.extensions.classic.gschema.override + + +%if %{xsession} +%files -n gnome-classic-session-xsession +%{_datadir}/xsessions/gnome-classic.desktop +%{_datadir}/xsessions/gnome-classic-xorg.desktop +%endif + + +%files -n %{pkg_prefix}-apps-menu +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.apps-menu.gschema.xml +%{_datadir}/gnome-shell/extensions/apps-menu*/ + + +%files -n %{pkg_prefix}-auto-move-windows +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.auto-move-windows.gschema.xml +%{_datadir}/gnome-shell/extensions/auto-move-windows*/ + +%files -n %{pkg_prefix}-classification-banner +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.classification-banner.gschema.xml +%{_datadir}/gnome-shell/extensions/classification-banner*/ + + +%files -n %{pkg_prefix}-custom-menu +%{_datadir}/gnome-shell/extensions/custom-menu*/ + + +%files -n %{pkg_prefix}-desktop-icons +%{_datadir}/gnome-shell/extensions/desktop-icons*/ + + +%files -n %{pkg_prefix}-drive-menu +%{_datadir}/gnome-shell/extensions/drive-menu*/ + + +%files -n %{pkg_prefix}-gesture-inhibitor +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml +%{_datadir}/gnome-shell/extensions/gesture-inhibitor*/ + + +%files -n %{pkg_prefix}-heads-up-display +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.heads-up-display.gschema.xml +%{_datadir}/gnome-shell/extensions/heads-up-display*/ + + +%files -n %{pkg_prefix}-launch-new-instance +%{_datadir}/gnome-shell/extensions/launch-new-instance*/ + + +%files -n %{pkg_prefix}-light-style +%{_datadir}/gnome-shell/extensions/light-style*/ + + +%files -n %{pkg_prefix}-native-window-placement +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.native-window-placement.gschema.xml +%{_datadir}/gnome-shell/extensions/native-window-placement*/ + + +%files -n %{pkg_prefix}-places-menu +%{_datadir}/gnome-shell/extensions/places-menu*/ + + +%files -n %{pkg_prefix}-screenshot-window-sizer +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml +%{_datadir}/gnome-shell/extensions/screenshot-window-sizer*/ + + +%files -n %{pkg_prefix}-system-monitor +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.system-monitor.gschema.xml +%{_datadir}/gnome-shell/extensions/system-monitor*/ + + +%files -n %{pkg_prefix}-top-icons +%{_datadir}/gnome-shell/extensions/top-icons*/ + + +%files -n %{pkg_prefix}-user-theme +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.user-theme.gschema.xml +%{_datadir}/gnome-shell/extensions/user-theme*/ + + +%files -n %{pkg_prefix}-window-list +%{_datadir}/gnome-shell/extensions/window-list*/ +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.window-list.gschema.xml + + +%files -n %{pkg_prefix}-windowsNavigator +%{_datadir}/gnome-shell/extensions/windowsNavigator*/ + + +%files -n %{pkg_prefix}-workspace-indicator +%{_datadir}/gnome-shell/extensions/workspace-indicator*/ +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml + + +%changelog +* Tue Nov 26 2024 MSVSphere Packaging Team - 47~alpha-3 +- Rebuilt for MSVSphere 10 + +## START: Generated by rpmautospec +* Thu Aug 22 2024 Florian Müllner - 47~alpha-3 +- Add desktop-icons stub + +* Tue Jul 30 2024 Florian Müllner - 47~alpha-2 +- Adjust classification banner for GNOME 47 changes + +* Tue Jul 23 2024 Florian Müllner - 47~alpha-1 +- Update to 47.alpha + +* Tue Jul 02 2024 Florian Müllner - 46.2-4 +- Extend workspace buttons to screen edge + +* Mon Jul 01 2024 Florian Müllner - 46.2-3 +- Conditionalize Xorg session + +* Mon Jun 24 2024 Troy Dawson - 46.2-2 +- Bump release for June 2024 mass rebuild + +* Mon May 27 2024 Florian Müllner - 46.2-1 +- Update to 46.2 + +* Wed May 15 2024 Florian Müllner - 46.1-3 +- Re-apply downstream patches + +* Thu May 02 2024 Tomas Pelka - 46.1-2 +- Add gating.yaml via API + +* Mon Apr 22 2024 Florian Müllner - 46.1-1 +- Pick up changes from F40 + +* Wed Apr 10 2024 Florian Müllner - 46.0-1 +- Backport F40 changes + +* Wed Jan 24 2024 Fedora Release Engineering - 46~alpha-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 19 2024 Fedora Release Engineering - 46~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sun Jan 07 2024 Florian Müllner - 46~alpha-1 +- Update to 46.alpha + +* Sat Dec 02 2023 Florian Müllner - 45.2-1 +- Update to 45.2 + +* Tue Oct 31 2023 Florian Müllner - 45.1-1 +- Update to 45.1 + +* Sat Sep 16 2023 Florian Müllner - 45.0-1 +- Update to 45.0 + +* Wed Sep 06 2023 Adam Williamson - 45~rc-2 +- Rebuild on a side tag to create combined update + +* Wed Sep 06 2023 Florian Müllner - 45~rc-1 +- Update to 45.rc + +* Mon Aug 07 2023 Florian Müllner - 45~beta-1 +- Update to 45.beta + +* Wed Jul 19 2023 Fedora Release Engineering - 45~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Thu Jul 06 2023 Florian Müllner - 45~alpha-1 +- Update to 45.alpha + +* Sun Mar 19 2023 Florian Müllner - 44.0-1 +- Update to 44.0 + +* Mon Mar 06 2023 Florian Müllner - 44~rc-1 +- Update to 44.rc + +* Tue Feb 14 2023 Florian Müllner - 44~beta-1 +- Update to 44.beta + +* Thu Jan 19 2023 Fedora Release Engineering - 43.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Fri Oct 28 2022 Florian Müllner - 43.1-2 +- Adjust gnome-shell dependency + +* Wed Oct 26 2022 Florian Müllner - 43.1-1 +- Update to 43.1 + +* Sat Sep 17 2022 Florian Müllner - 43.0-1 +- Update to 43.0 + +* Sun Sep 04 2022 Florian Müllner - 43~rc-1 +- Update to 43.rc + +* Wed Aug 10 2022 Florian Müllner - 43~beta-1 +- Update to 43.beta + +* Thu Jul 21 2022 Fedora Release Engineering - 43~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Sun Jul 10 2022 Florian Müllner - 43~alpha-1 +- Update to 43.alpha + +* Sat May 28 2022 Florian Müllner - 42.2-1 +- Update to 42.2 + +* Fri May 06 2022 Florian Müllner - 42.1-1 +- Update to 42.1 + +* Sun Mar 13 2022 Florian Müllner - 42.0-1 +- Update to 42.0 + +* Mon Mar 07 2022 Florian Müllner - 42~rc-1 +- Update to 42.rc + +* Tue Feb 15 2022 Florian Müllner - 42~beta-1 +- Update to 42.beta + +* Thu Jan 20 2022 Fedora Release Engineering - 42~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Tue Jan 11 2022 Florian Müllner - 42~alpha-1 +- Update to 42.alpha + +* Fri Oct 29 2021 Neal Gompa - 41.0-2 +- Backport GNOME Classic session for Wayland (#2015741) + +* Sun Sep 19 2021 Florian Müllner - 41.0-1 +- Update to 41.0 + +* Mon Sep 06 2021 Florian Müllner - 41~rc.1-1 +- Update to 41.rc.1 + +* Sun Sep 05 2021 Florian Müllner - 41~rc-1 +- Update to 41.rc + +* Wed Aug 18 2021 Florian Müllner - 41~beta-1 +- Update to 41.beta + +* Thu Jul 22 2021 Fedora Release Engineering - 40.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Mon Jul 12 2021 Florian Müllner - 40.3-1 +- Update to 40.3 + +* Thu Jun 10 2021 Florian Müllner - 40.2-1 +- Update to 40.2 + +* Thu May 13 2021 Florian Müllner - 40.1-1 +- Update to 40.1 + +* Sat Mar 20 2021 Florian Müllner - 40.0-1 +- Update to 40.0 + +* Mon Mar 15 2021 Florian Müllner - 40.rc-1 +- Update to 40.rc + +* Wed Feb 24 2021 Florian Müllner - 40.beta-1 +- Update to 40.beta + +* Thu Feb 18 2021 Kalev Lember - 40.0~alpha.1-6.20210212git9fa522c +- Fix typo in horizontal-workspaces obsoletes package name + +* Thu Feb 18 2021 Kalev Lember - 40.0~alpha.1-5.20210212git9fa522c +- Fix obsoletes version + +* Mon Feb 15 2021 Mohamed El Morabity - 40.0~alpha.1-4.20210212git9fa522c +- Add Obsoletes for horizontal-workspaces extension to fix upgrades to Fedora 34 + (RHBZ #1928415) + +* Fri Feb 12 2021 Florian Müllner - 40.0~alpha.1-3.20210212git9fa522c +- Build snapshot of current upstream +- Drop horizontal-workspaces subpackage + (removed upstream, because horizontal workspaces are the default now) + +* Tue Jan 26 2021 Fedora Release Engineering - 40.0~alpha.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Jan 15 2021 Florian Müllner - 40.0~alpha.1-1 +- Update to 40.alpha.1 + +* Wed Dec 02 2020 Florian Müllner - 40.0~alpha-1 +- Update to 40.alpha + +* Mon Oct 05 2020 Florian Müllner - 3.38.1-1 +- Update to 3.38.1 + +* Mon Sep 14 2020 Florian Müllner - 3.38.0-1 +- Update to 3.38.0 + +* Sun Sep 06 2020 Florian Müllner - 3.37.92-1 +- Update to 3.37.92 + +* Mon Aug 24 2020 Florian Müllner - 3.37.91-1 +- Update to 3.37.91 + +* Wed Aug 19 2020 Kalev Lember - 3.37.90-2 +- Rebuild + +* Tue Aug 11 2020 Florian Müllner - 3.37.90-1 +- Update to 3.37.90 + +* Mon Jul 27 2020 Fedora Release Engineering - 3.37.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jul 07 2020 Florian Müllner - 3.37.3-1 +- Update to 3.37.3 + +* Wed Jun 03 2020 Florian Müllner - 3.37.2-1 +- Update to 3.37.2 + +* Thu Apr 30 2020 Florian Müllner - 3.37.1-1 +- Update to 3.37.1 + +* Tue Mar 31 2020 Florian Müllner - 3.36.1-1 +- Update to 3.36.1 + +* Sat Mar 07 2020 Florian Müllner - 3.36.0-1 +- Update to 3.36.0 + +* Sun Mar 01 2020 Florian Müllner - 3.35.92-1 +- Update to 3.35.92 + +* Tue Feb 18 2020 Florian Müllner - 3.35.91-1 +- Update to 3.35.91 + +* Thu Feb 06 2020 Florian Müllner - 3.35.90-1 +- Update to 3.35.90 + +* Tue Jan 28 2020 Fedora Release Engineering - 3.35.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Sun Jan 05 2020 Florian Müllner - 3.35.3-1 +- Update to 3.35.3 + +* Wed Dec 11 2019 Florian Müllner - 3.35.2-1 +- Update to 3.35.2 + +* Wed Oct 09 2019 Florian Müllner - 3.34.1-1 +- Update to 3.34.1 + +* Wed Sep 25 2019 Debarshi Ray - 3.34.0-2 +- Unbreak the 'classic' GNOME session + +* Mon Sep 09 2019 Florian Müllner - 3.34.0-1 +- Update to 3.34.0 + +* Wed Sep 04 2019 Florian Müllner - 3.33.92-1 +- Update to 3.33.92 + +* Wed Aug 21 2019 Florian Müllner - 3.33.91-1 +- Update to 3.33.91 + +* Sat Aug 10 2019 Florian Müllner - 3.33.90-1 +- Update to 3.33.90 + +* Thu Jul 25 2019 Fedora Release Engineering - 3.33.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sat Jul 20 2019 Florian Müllner - 3.33.4-1 +- Update to 3.33.4 + +* Mon Jun 24 2019 Florian Müllner - 3.33.3-1 +- Update to 3.33.3 + +* Wed May 22 2019 Florian Müllner - 3.33.2-1 +- Update to 3.33.2 + +* Tue May 14 2019 Florian Müllner - 3.33.1-1 +- Update to 3.33.1 + +* Wed Apr 17 2019 Florian Müllner - 3.32.1-1 +- Update to 3.32.1 + +* Tue Mar 12 2019 Florian Müllner - 3.32.0-1 +- Update to 3.32.0 + +* Tue Mar 05 2019 Florian Müllner - 3.31.92-1 +- Update to 3.31.92 + +* Thu Feb 21 2019 Florian Müllner - 3.31.91-1 +- Update to 3.31.91 + +* Thu Feb 07 2019 Florian Müllner - 3.31.90-1 +- Update to 3.31.90 + +* Thu Jan 31 2019 Fedora Release Engineering - 3.31.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Nov 21 2018 Mohamed El Morabity - 3.31.2-2 +- Fix alternate-tab extension Obsoletes tag (RHBZ #1650519) + +* Wed Nov 14 2018 Florian Müllner - 3.31.2-1 +- Update to 3.31.2 + +* Mon Oct 08 2018 Florian Müllner - 3.30.1-1 +- Update to 3.30.1 + +* Tue Sep 04 2018 Florian Müllner - 3.30.0-1 +- Update to 3.30.0 + +* Mon Aug 20 2018 Florian Müllner - 3.29.91-1 +- Update to 3.29.91 + +* Wed Aug 01 2018 Florian Müllner - 3.29.90-1 +- Update to 3.29.90 + +* Fri Jul 13 2018 Fedora Release Engineering - 3.29.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu May 24 2018 Florian Müllner - 3.29.2-1 +- Update to 3.29.2 + +* Fri Apr 13 2018 Florian Müllner - 3.28.1-1 +- Update to 3.28.1 + +* Mon Mar 12 2018 Florian Müllner - 3.28.0-1 +- Update to 3.28.0 + +* Mon Mar 05 2018 Florian Müllner - 3.27.92-1 +- Update to 3.27.92 + +* Thu Feb 22 2018 Florian Müllner - 3.27.91-1 +- Update to 3.27.91 + +* Wed Feb 07 2018 Fedora Release Engineering - 3.27.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Jan 06 2018 Igor Gnatenko - 3.27.1-2 +- Remove obsolete scriptlets + +* Tue Oct 17 2017 Florian Müllner - 3.27.1-1 +- Update to 3.27.1 + +* Wed Oct 04 2017 Florian Müllner - 3.26.1-1 +- Update to 3.26.1 + +* Tue Sep 12 2017 Florian Müllner - 3.26.0-1 +- Update to 3.26.0 + +* Tue Aug 22 2017 Florian Müllner - 3.25.91-1 +- Update to 3.25.91 + +* Fri Aug 11 2017 Kevin Fenzi - 3.25.90-2 +- Rebuild with older working rpm + +* Thu Aug 10 2017 Florian Müllner - 3.25.90-1 +- Update to 3.25.90 + +* Wed Jul 26 2017 Fedora Release Engineering - 3.25.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu Jul 20 2017 Florian Müllner - 3.25.4-1 +- Update to 3.25.4 + +* Wed Jun 21 2017 Florian Müllner - 3.25.3-1 +- Update to 3.25.3 + +* Thu May 25 2017 Florian Müllner - 3.25.2-1 +- Update to 3.25.2 + +* Thu Apr 27 2017 Florian Müllner - 3.25.1-1 +- Update to 3.25.1 + +* Tue Apr 11 2017 Florian Müllner - 3.24.1-1 +- Update to 3.24.1 + +* Mon Mar 20 2017 Florian Müllner - 3.24.0-1 +- Update to 3.24.0 + +* Tue Mar 14 2017 Florian Müllner - 3.23.92-1 +- Update to 3.23.92 + +* Wed Mar 01 2017 Florian Müllner - 3.23.91-1 +- Update to 3.23.91 + +* Thu Feb 16 2017 Florian Müllner - 3.23.90-1 +- Update to 3.23.90 + +* Fri Feb 10 2017 Fedora Release Engineering - 3.23.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Nov 23 2016 Florian Müllner - 3.23.2-1 +- Update to 3.23.2 + +* Tue Oct 11 2016 Florian Müllner - 3.22.1-1 +- Update to 3.22.1 + +* Mon Sep 19 2016 Florian Müllner - 3.22.0-1 +- Update to 3.22.0 + +* Tue Sep 13 2016 Florian Müllner - 3.21.92-1 +- Update to 3.21.92 + +* Tue Aug 30 2016 Florian Müllner - 3.21.91-1 +- Update to 3.21.91 + +* Fri Aug 19 2016 Florian Müllner - 3.21.90-1 +- Update to 3.21.90 + +* Wed Jul 20 2016 Florian Müllner - 3.21.4-1 +- Update to 3.21.4 + +* Tue Jun 21 2016 Florian Müllner - 3.21.3-1 +- Update to 3.21.3 + +* Fri May 27 2016 Florian Müllner - 3.21.2-1 +- Update to 3.21.2 + +* Tue May 10 2016 Florian Müllner - 3.20.1-1 +- Update to 3.20.1 + +* Tue Mar 22 2016 Florian Müllner - 3.20.0-1 +- Update to 3.20.0 + +* Wed Mar 16 2016 Florian Müllner - 3.19.92-1 +- Update to 3.19.92 + +* Thu Mar 03 2016 Florian Müllner - 3.19.91-1 +- Update to 3.19.91 + +* Fri Feb 19 2016 Florian Müllner - 3.19.90-1 +- Update to 3.19.90 + +* Wed Feb 03 2016 Fedora Release Engineering - 3.19.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Jan 21 2016 Florian Müllner - 3.19.4-1 +- Update to 3.19.4 + +* Thu Dec 17 2015 Florian Müllner - 3.19.3-1 +- Update to 3.19.3 + +* Wed Nov 25 2015 Florian Müllner - 3.19.2-1 +- Update to 3.19.2 + +* Thu Oct 29 2015 Florian Müllner - 3.19.1-1 +- Update to 3.19.1 + +* Thu Oct 15 2015 Florian Müllner - 3.18.1-1 +- Update to 3.18.1 + +* Mon Sep 21 2015 Florian Müllner - 3.18.0-1 +- Update to 3.18.0 + +* Wed Sep 16 2015 Florian Müllner - 3.17.92-1 +- Update to 3.17.92 + +* Thu Sep 03 2015 Florian Müllner - 3.17.91-1 +- Update to 3.17.91 + +* Thu Aug 20 2015 Florian Müllner - 3.17.90-1 +- Update to 3.17.90 + +* Wed Aug 19 2015 Kalev Lember - 3.17.4-2 +- Don't own /usr/share/gnome-shell/extensions directory: now part of + gnome-shell package + +* Thu Jul 23 2015 Florian Müllner - 3.17.4-1 +- Update to 3.17.4 + +* Thu Jul 02 2015 Florian Müllner - 3.17.3-1 +- Update to 3.17.3 + +* Wed Jun 17 2015 Fedora Release Engineering - 3.17.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed May 27 2015 Florian Müllner - 3.17.2-1 +- Update to 3.17.2 + +* Fri May 01 2015 Kalev Lember - 3.17.1-2 +- Add glib-compile-schemas rpm scripts for screenshot-window-sizer + +* Thu Apr 30 2015 Florian Müllner - 3.17.1-1 +- Update to 3.17.1 + +* Tue Apr 14 2015 Florian Müllner - 3.16.1-1 +- Update to 3.16.1 + +* Mon Mar 23 2015 Florian Müllner - 3.16.0-1 +- Update to 3.16.0 + +* Tue Mar 17 2015 Florian Müllner - 3.15.92-1 +- Update to 3.15.92 + +* Thu Mar 05 2015 Kalev Lember - 3.15.91-2 +- Obsolete the systemMonitor extension that was dropped in 3.15.91 + +* Thu Mar 05 2015 Florian Müllner - 3.15.91-1 +- Update to 3.15.91 + +* Fri Feb 20 2015 Florian Müllner - 3.15.90-1 +- Update to 3.15.90 + +* Wed Jan 21 2015 Florian Müllner - 3.15.4-1 +- Update to 3.15.4 + +* Fri Dec 19 2014 Florian Müllner - 3.15.3.1-1 +- Update to 3.15.3.1 + +* Fri Dec 19 2014 Florian Müllner - 3.15.3-1 +- Update to 3.15.3 + +* Thu Nov 27 2014 Florian Müllner - 3.15.2-1 +- Update to 3.15.2 + +* Thu Oct 30 2014 Florian Müllner - 3.15.1-1 +- Update to 3.15.1 + +* Tue Oct 14 2014 Florian Müllner - 3.14.1-1 +- Update to 3.14.1 + +* Mon Sep 22 2014 Florian Müllner - 3.14.0-1 +- Update to 3.14.0 + +* Wed Sep 17 2014 Florian Müllner - 3.13.92-1 +- Update to 3.13.92 + +* Wed Sep 03 2014 Florian Müllner - 3.13.91-1 +- Update to 3.13.91 + +* Wed Aug 20 2014 Mohamed El Morabity - 3.13.90-1 +- Update to 3.13.90 + +* Thu Jul 24 2014 Kalev Lember - 3.13.4-1 +- Update to 3.13.4 + +* Thu Jun 26 2014 Richard Hughes - 3.13.3-1 +- Update to 3.13.3 + +* Sat Jun 07 2014 Fedora Release Engineering - 3.13.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed May 28 2014 Mohamed El Morabity - 3.13.2-1 +- Update to 3.13.2 + +* Fri May 02 2014 Kalev Lember - 3.13.1-1 +- Update to 3.13.1 + +* Tue Mar 25 2014 Richard Hughes - 3.12.0-1 +- Update to 3.12.0 + +* Thu Mar 20 2014 Mohamed El Morabity - 3.11.92-1 +- Update to 3.11.92 + +* Thu Mar 06 2014 Mohamed El Morabity - 3.11.91-1 +- Update to 3.11.91 + +* Thu Feb 20 2014 Mohamed El Morabity - 3.11.90-1 +- Update to 3.11.90 + +* Wed Feb 05 2014 Mohamed El Morabity - 3.11.5-1 +- Update to 3.11.5 + +* Mon Feb 03 2014 Mohamed El Morabity - 3.11.4-1 +- Update to 3.11.4 + +* Sun Dec 22 2013 Mohamed El Morabity - 3.11.3-1 +- Update to 3.11.3 + +* Wed Nov 13 2013 Mohamed El Morabity - 3.11.2-1 +- Update to 3.11.2 + +* Wed Oct 16 2013 Mohamed El Morabity - 3.10.1-1 +- Update to 3.10.1 + +* Tue Sep 24 2013 Mohamed El Morabity - 3.10.0-1 +- Update to 3.10.0 + +* Tue Sep 17 2013 Mohamed El Morabity - 3.9.92-1 +- Update to 3.9.92 + +* Tue Sep 03 2013 Mohamed El Morabity - 3.9.91-1 +- Update to 3.9.91 + +* Thu Aug 22 2013 Mohamed El Morabity - 3.9.90-1 +- Update to 3.9.90 +- Drop xrand-indicator subpackage, no longer provided upstream + +* Mon Aug 12 2013 Mohamed El Morabity - 3.9.5-3 +- Fix alternative-status-menu subpackage obsoleting + +* Mon Aug 12 2013 Nils Philippsen - 3.9.5-2 +- obsolete alternative-status-menu subpackage to allow smooth upgrades + +* Sun Aug 04 2013 Mohamed El Morabity - 3.9.5-1 +- Update to 3.9.5 +- Drop alternative-status-menu subpackage, no longer provided upstream + +* Sat Aug 03 2013 Fedora Release Engineering - 3.9.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Thu Jun 20 2013 Rahul Sundaram - 3.9.3-1 +- Update to 3.9.3 +- Obsolete default-min-max and static workspaces extensions +- Use make_install macro +- Fix bogus dates in spec changelog + +* Tue May 28 2013 Mohamed El Morabity - 3.9.2-1 +- Update to 3.9.2 + +* Fri May 10 2013 Mohamed El Morabity - 3.9.1-1 +- Update to 3.9.1 + +* Fri May 10 2013 Kalev Lember - 3.8.1-3 +- Obsolete gnome-applet-sensors + +* Wed May 01 2013 Kalev Lember - 3.8.1-2 +- Obsolete a few more fallback mode packages +- Remove gnome-panel provides + +* Tue Apr 16 2013 Matthias Clasen - 3.8.1-1 +- Update to 3.8.1 + +* Tue Mar 26 2013 Mohamed El Morabity - 3.8.0-1 +- Update to 3.8.0 + +* Tue Mar 19 2013 Ray Strode 3.7.92-1 +- Update to 3.7.92 + +* Tue Mar 05 2013 Mohamed El Morabity - 3.7.91-1 +- Update to 3.7.91 + +* Sat Mar 02 2013 Adel Gadllah - 3.7.90-2 +- Obsolete gnome-panel + +* Fri Feb 22 2013 Kalev Lember - 3.7.90-1 +- Update to 3.7.90 + +* Thu Feb 07 2013 Kalev Lember - 3.7.5.1-2 +- Depend on gnome-shell 3.7.5, there's no 3.7.5.1 + +* Thu Feb 07 2013 Mohamed El Morabity - 3.7.5.1-1 +- Update to 3.7.5 +- Enable new launch-new-instance and window-list extensions, and add them in the + classic-mode extension set +- Re-add places-menu in the classic-mode extension set + +* Wed Jan 16 2013 Mohamed El Morabity - 3.7.4-1 +- Update to 3.7.4 +- places-menu extension no longer part of the classic-mode extension set + +* Tue Jan 01 2013 Mohamed El Morabity - 3.7.3-1 +- Update to 3.7.3 +- Enable new default-min-max and static-workspaces extensions +- Provide new subpackage gnome-classic-session +- Revamp summaries and descriptions + +* Tue Oct 30 2012 Mohamed El Morabity - 3.7.1-1 +- Update to 3.7.1 +- Drop dock and gajim extensions, no longer provided + +* Tue Oct 30 2012 Mohamed El Morabity - 3.6.1-1 +- Update to 3.6.1 + +* Tue Oct 02 2012 Mohamed El Morabity - 3.6.0-1 +- Update to 3.6.0 + +* Thu Sep 06 2012 Mohamed El Morabity - 3.5.91-1 +- Update to 3.5.91 + +* Wed Aug 29 2012 Mohamed El Morabity - 3.5.90-1 +- Update to 3.5.90 + +* Sat Aug 11 2012 Mohamed El Morabity - 3.5.5-1 +- Update to 3.5.5 + +* Sun Jul 22 2012 Mohamed El Morabity - 3.5.4-1 +- Update to 3.5.4 + +* Wed Jul 18 2012 Mohamed El Morabity - 3.5.2-1 +- Update to 3.5.2 +- Drop useless Provides/Obsoletes + +* Sat Mar 24 2012 Mohamed El Morabity - 3.4.0-1 +- Update to 3.4.0 +- Minor spec fixes + +* Sat Mar 24 2012 Mohamed El Morabity - 3.3.92-1 +- Update to 3.3.92 + +* Tue Feb 28 2012 Mohamed El Morabity - 3.3.90-1 +- Update to 3.3.90 + +* Thu Feb 16 2012 Mohamed El Morabity - 3.3.5-1 +- Update to 3.3.5 +- Spec cleanup + +* Fri Jan 13 2012 Fedora Release Engineering - 3.3.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Nov 30 2011 Mohamed El Morabity - 3.3.2-1 +- Update to 3.3.2 + +* Wed Nov 30 2011 Mohamed El Morabity - 3.2.1-1 +- Update to 3.2.1 +- Fix alternative-status-menu extension crash when login + +* Wed Nov 09 2011 Mohamed El Morabity - 3.2.0-2 +- Fix dock and alternate-tab extensions +- Fix GNOME Shell version to work with GS 3.2.1 + +* Mon Oct 03 2011 Mohamed El Morabity - 3.2.0-1 +- Update to 3.2.0 + +* Mon Sep 26 2011 Mohamed El Morabity - 3.1.91-3.20111001gite102c0c6 +- Update to a newer git snapshot +- Fix GNOME Shell version to work with GS 3.2.0 +- Add Requires on GS 3.2.0 or above to gnome-shell-common + +* Wed Sep 14 2011 Mohamed El Morabity - 3.1.91-2 +- Enable xrandr-indicator and workspace-indicator extensions + +* Mon Sep 12 2011 Michel Salim - 3.1.91-1 +- Update to 3.1.91 +- add more documentation + +* Thu Sep 1 2011 Michel Salim - 3.1.4-3.20110830git6b5e3a3e +- Update to git snapshot, for gnome-shell 3.1.90 + +* Sun Aug 21 2011 Michel Salim - 3.1.4-2 +- Enable apps-menu extension +- Spec cleanup + +* Sun Aug 21 2011 Michel Salim - 3.1.4-1 +- Update to 3.1.4 +- Enable systemMonitor extension +- Prepare xrandr-indicator, commenting out since it does not seem to work yet +- Rename subpackages in line with new guidelines (# 715367) +- Sort subpackages in alphabetical order + +* Sat May 28 2011 Timur Kristóf - 3.0.2-1.g63dd27cgit +- Update to a newer git snapshot +- Fix RHBZ bug #708230 +- Enabled systemMonitor extension, but commented out since the requirements are not available + +* Fri May 13 2011 Mohamed El Morabity - 3.0.1-3.03660fgit +- Update to a newer git snapshot +- Enable native-window-placement extension + +* Fri May 06 2011 Rahul Sundaram - 3.0.1-2b20cbagit +- Fix description + +* Thu May 5 2011 Elad Alfassa - 3.0.1-1.b20cbagit +- Update to a newer git snapshot +- Enabled the places-menu extension + +* Tue Apr 26 2011 Mohamed El Morabity - 3.0.1-1.f016b9git +- Update to a newer git snapshot (post-3.0.1 release) +- Enable drive-menu extension + +* Mon Apr 11 2011 Mohamed El Morabity - 3.0.0-5.6d56cfgit +- Enable auto-move-windows extension + +* Mon Apr 11 2011 Rahul Sundaram - 3.0.0-4.6d56cfgit +- Add glib2-devel as build requires + +* Mon Apr 11 2011 Rahul Sundaram - 3.0.0-3.6d56cfgit +- Tweak description +- Fix typo in configure + +* Mon Apr 11 2011 Rahul Sundaram - 3.0.0-2.6d56cfgit +- Added the user-theme extension +- Patch from Timur Kristóf + +* Fri Apr 08 2011 Rahul Sundaram - 3.0.0-1.6d56cfgit +- Make sure configure doesn't get called twice + +* Fri Apr 08 2011 Rahul Sundaram - 3.0.0-0.6d56cfgit +- Initial build + +## END: Generated by rpmautospec