diff --git a/meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.0.bb b/meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.1.bb similarity index 96% rename from meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.0.bb rename to meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.1.bb index 4010f29..8a2290d 100644 --- a/meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.0.bb +++ b/meta-gnome/recipes-gnome/glib-2.0/glib-2.0_2.70.1.bb @@ -19,7 +19,7 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \ " SRC_URI_append_class-native = " file://relocate-modules.patch" -SRC_URI[sha256sum] = "200d7df811c5ba634afbf109f14bb40ba7fde670e89389885da14e27c0840742" +SRC_URI[sha256sum] = "f9b7bce7f51753a1f43853bbcaca8bf09e15e994268e29cfd7a76f65636263c0" # Find any meson cross files in FILESPATH that are relevant for the current # build (using siteinfo) and add them to EXTRA_OEMESON. diff --git a/meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.3.1.bb b/meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.4.1.bb similarity index 61% rename from meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.3.1.bb rename to meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.4.1.bb index 5e833a9..85e605e 100644 --- a/meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.3.1.bb +++ b/meta-gnome/recipes-gnome/gnome-autoar/gnome-autoar_0.4.1.bb @@ -7,10 +7,12 @@ DEPENDS = " \ libarchive \ " +GNOMEBASEBUILDCLASS = "meson" +GIR_MESON_ENABLE_FLAG = "enabled" +GTKDOC_MESON_OPTION = "gtk_doc" inherit gnomebase gobject-introspection gtk-doc vala -SRC_URI[archive.md5sum] = "3149496d0189623a8e1289bbab4d8385" -SRC_URI[archive.sha256sum] = "0e78713e6f0de339fdee16bab82753ae290fe80fa7b4ba6e3db8c1465b81d0f8" +SRC_URI[archive.sha256sum] = "646bd50ebad92d91c1be89097a15364156157442cac1471ded7ecb27d9a9150e" do_compile_prepend() { export GIR_EXTRA_LIBS_PATH="${B}/gnome-autoar/.libs" diff --git a/meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.0.bb b/meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.1.bb similarity index 95% rename from meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.0.bb rename to meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.1.bb index 3c6594e..b73ac08 100644 --- a/meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.0.bb +++ b/meta-gnome/recipes-gnome/gnome-control-center/gnome-control-center_41.1.bb @@ -37,7 +37,7 @@ def gnome_verdir(v): SRC_URI = "${GNOME_MIRROR}/${GNOMEBN}/${@gnome_verdir("${PV}")}/${GNOMEBN}-${PV}.tar.${GNOME_COMPRESS_TYPE};name=archive" -SRC_URI[archive.sha256sum] = "a5696222ad353fcf9a07c406e287ecc9138e250645cb2a5941a88ff0685658c1" +SRC_URI[archive.sha256sum] = "ea0c71484c65ce2cc11376f9b01e6211fa4a7ffd334f4307fc52c93f0fddd4c7" # diff --git a/meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.0.bb b/meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.1.bb similarity index 92% rename from meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.0.bb rename to meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.1.bb index fff8f51..92ba8b8 100644 --- a/meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.0.bb +++ b/meta-gnome/recipes-gnome/gnome-desktop/gnome-desktop_41.1.bb @@ -15,7 +15,7 @@ def gnome_verdir(v): SRC_URI = "${GNOME_MIRROR}/${GNOMEBN}/${@gnome_verdir("${PV}")}/${GNOMEBN}-${PV}.tar.${GNOME_COMPRESS_TYPE};name=archive" SRC_URI += "file://0001-needs-stdint-include.patch" -SRC_URI[archive.sha256sum] = "69cb1d3d9a10700eb66348ef1c0e66a855fc5a97ae62902df97a499da11562d2" +SRC_URI[archive.sha256sum] = "be8aafa64d7ba2fd31079eed639d39fda1ea77ef77d35a678f019c4d91d473c2" DEPENDS += "itstool-native gsettings-desktop-schemas gconf virtual/libx11 gtk+3 glib-2.0 startup-notification xkeyboard-config iso-codes udev libseccomp" diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Citadel-Gnome-Shell-changes.patch.old b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Citadel-Gnome-Shell-changes.patch.old deleted file mode 100644 index de12280..0000000 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Citadel-Gnome-Shell-changes.patch.old +++ /dev/null @@ -1,3701 +0,0 @@ -From 8cdf7d5e05a56788db1030cf74698561d4facbf8 Mon Sep 17 00:00:00 2001 -From: Bruce Leidl -Date: Mon, 4 Oct 2021 07:47:02 -0400 -Subject: [PATCH] Citadel Gnome Shell changes - ---- - data/org.gnome.shell.gschema.xml.in | 8 + - data/theme/gnome-shell-high-contrast.css | 57 ++- - data/theme/gnome-shell-sass/_widgets.scss | 2 + - .../gnome-shell-sass/widgets/_realms.scss | 17 + - data/theme/gnome-shell.css | 57 ++- - data/theme/meson.build | 1 + - js/js-resources.gresource.xml | 7 + - js/ui/main.js | 4 + - js/ui/overview.js | 2 +- - js/ui/overviewControls.js | 3 + - js/ui/realms/realmIndicator.js | 37 ++ - js/ui/realms/realmManager.js | 54 +++ - js/ui/realms/realmSearchProvider.js | 326 +++++++++++++ - js/ui/realms/realmSwitcher.js | 325 +++++++++++++ - js/ui/realms/realmWindowFrame.js | 371 +++++++++++++++ - js/ui/realms/realmWindowMenu.js | 113 +++++ - js/ui/windowManager.js | 41 +- - js/ui/windowMenu.js | 15 +- - js/ui/workspacesView.js | 12 + - src/meson.build | 7 + - src/shell-app-cache.c | 2 +- - src/shell-app-system.c | 179 ++++++- - src/shell-app-system.h | 6 +- - src/shell-global.c | 3 + - src/shell-realm-item.c | 392 ++++++++++++++++ - src/shell-realm-item.h | 35 ++ - src/shell-realm-tracker.c | 297 ++++++++++++ - src/shell-realm-tracker.h | 14 + - src/shell-realms-private.h | 21 + - src/shell-realms.c | 442 ++++++++++++++++++ - src/shell-realms.h | 23 + - src/shell-window-tracker.c | 43 +- - 32 files changed, 2853 insertions(+), 63 deletions(-) - create mode 100644 data/theme/gnome-shell-sass/widgets/_realms.scss - create mode 100644 js/ui/realms/realmIndicator.js - create mode 100644 js/ui/realms/realmManager.js - create mode 100644 js/ui/realms/realmSearchProvider.js - create mode 100644 js/ui/realms/realmSwitcher.js - create mode 100644 js/ui/realms/realmWindowFrame.js - create mode 100644 js/ui/realms/realmWindowMenu.js - create mode 100644 src/shell-realm-item.c - create mode 100644 src/shell-realm-item.h - create mode 100644 src/shell-realm-tracker.c - create mode 100644 src/shell-realm-tracker.h - create mode 100644 src/shell-realms-private.h - create mode 100644 src/shell-realms.c - create mode 100644 src/shell-realms.h - -diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in -index cd6a235..a4c9e74 100644 ---- a/data/org.gnome.shell.gschema.xml.in -+++ b/data/org.gnome.shell.gschema.xml.in -@@ -238,6 +238,14 @@ - ["<Super>9"] - Switch to application 9 - -+ -+ ["<Primary>Tab"] -+ Open Realm Switcher -+ -+ -+ ["<Shift><Primary>Tab"] -+ Open Realm Switcher Backwards -+ - - - ui/status/remoteAccess.js - ui/status/system.js - ui/status/thunderbolt.js -+ -+ ui/realms/realmIndicator.js -+ ui/realms/realmManager.js -+ ui/realms/realmSearchProvider.js -+ ui/realms/realmSwitcher.js -+ ui/realms/realmWindowFrame.js -+ ui/realms/realmWindowMenu.js - - -diff --git a/js/ui/main.js b/js/ui/main.js -index bf7b6ac..963ec8d 100644 ---- a/js/ui/main.js -+++ b/js/ui/main.js -@@ -49,6 +49,7 @@ const PointerA11yTimeout = imports.ui.pointerA11yTimeout; - const ParentalControlsManager = imports.misc.parentalControlsManager; - const Config = imports.misc.config; - const Util = imports.misc.util; -+const RealmManager = imports.ui.realms.realmManager; - - const WELCOME_DIALOG_LAST_SHOWN_VERSION = 'welcome-dialog-last-shown-version'; - // Make sure to mention the point release, otherwise it will show every time -@@ -91,6 +92,7 @@ var kbdA11yDialog = null; - var inputMethod = null; - var introspectService = null; - var locatePointer = null; -+var realmManager = null; - let _startDate; - let _defaultCssStylesheet = null; - let _cssStylesheet = null; -@@ -268,6 +270,8 @@ function _initializeUI() { - extensionManager = new ExtensionSystem.ExtensionManager(); - extensionManager.init(); - -+ realmManager = new RealmManager.RealmManager(); -+ - if (sessionMode.isGreeter && screenShield) { - layoutManager.connect('startup-prepared', () => { - screenShield.showDialog(); -diff --git a/js/ui/overview.js b/js/ui/overview.js -index 75fe6e1..a716cad 100644 ---- a/js/ui/overview.js -+++ b/js/ui/overview.js -@@ -258,7 +258,7 @@ var Overview = class { - DND.addDragMonitor(this._dragMonitor); - // Remember the workspace we started from - let workspaceManager = global.workspace_manager; -- this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_index(); -+ this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_id(); - } - - _onDragEnd(time) { -diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js -index a897403..9eb8ca8 100644 ---- a/js/ui/overviewControls.js -+++ b/js/ui/overviewControls.js -@@ -336,6 +336,8 @@ class ControlsManager extends St.Widget { - workspaceManager.connect('notify::n-workspaces', - this._updateAdjustment.bind(this)); - -+ this._contextSwitchedId = workspaceManager.connect('context-switched', this._updateAdjustment.bind(this)); -+ - this._searchController = new SearchController.SearchController( - this._searchEntry, - this.dash.showAppsButton); -@@ -629,6 +631,7 @@ class ControlsManager extends St.Widget { - - _onDestroy() { - global.workspace_manager.disconnect(this._nWorkspacesNotifyId); -+ global.workspace_manager.disconnect(this._contextSwitchedId); - } - - _updateAdjustment() { -diff --git a/js/ui/realms/realmIndicator.js b/js/ui/realms/realmIndicator.js -new file mode 100644 -index 0000000..d71e346 ---- /dev/null -+++ b/js/ui/realms/realmIndicator.js -@@ -0,0 +1,37 @@ -+const { Clutter, GObject, Shell, St } = imports.gi; -+const PanelMenu = imports.ui.panelMenu; -+ -+var RealmPanelIndicator = GObject.registerClass( -+class RealmPanelIndicator extends PanelMenu.Button { -+ _init() { -+ super._init(0.5, "Current Realm"); -+ -+ this._label = new St.Label({ -+ style_class: 'current-realm-label', -+ y_align: Clutter.ActorAlign.CENTER -+ }); -+ this.add_child(this._label); -+ -+ this.update(); -+ } -+ -+ clear() { -+ this._label.set_text(''); -+ } -+ -+ update() { -+ let realm_name = ''; -+ const realms = Shell.Realms.get_default(); -+ let current = realms.current_realm; -+ if (current) { -+ realm_name = current.realm_name; -+ this._label.set_text(`realm-${realm_name}`); -+ } -+ -+ if (realm_name.length > 0) { -+ this._label.set_text(`realm-${realm_name}`); -+ } else { -+ this._label.set_text(''); -+ } -+ } -+ }); -diff --git a/js/ui/realms/realmManager.js b/js/ui/realms/realmManager.js -new file mode 100644 -index 0000000..0432190 ---- /dev/null -+++ b/js/ui/realms/realmManager.js -@@ -0,0 +1,54 @@ -+const { Clutter, Gio, Meta, Shell, St } = imports.gi; -+ -+const Main = imports.ui.main; -+const RealmIndicator = imports.ui.realms.realmIndicator; -+const RealmSwitcher = imports.ui.realms.realmSwitcher; -+const Lightbox = imports.ui.lightbox; -+const RealmSearchProvider = imports.ui.realms.realmSearchProvider; -+const RealmWindowFrame = imports.ui.realms.realmWindowFrame; -+ -+var RealmManager = class { -+ constructor() { -+ -+ this._realmIndicator = new RealmIndicator.RealmPanelIndicator(); -+ Main.panel.addToStatusArea('RealmIndicator', this._realmIndicator); -+ -+ this._switchAction = Main.wm.addKeybinding('switch-realm', -+ new Gio.Settings({ schema_id: "org.gnome.shell.keybindings"}), -+ Meta.KeyBindingFlags.NONE, -+ Shell.ActionMode.NORMAL, -+ this._switchRealms.bind(this)); -+ -+ this._switchActionBackward = Main.wm.addKeybinding('switch-realm-backward', -+ new Gio.Settings({ schema_id: "org.gnome.shell.keybindings"}), -+ Meta.KeyBindingFlags.IS_REVERSED, -+ Shell.ActionMode.NORMAL, -+ this._switchRealms.bind(this)); -+ -+ const realms = Shell.Realms.get_default(); -+ realms.connect('realm-context-switched', () => { -+ Main.overview.dash._queueRedisplay(); -+ }); -+ -+ this._switchAnimation = new RealmSwitcher.ContextSwitchAnimationController(this._realmIndicator); -+ -+ this._searchResults = Main.overview._overview.controls._searchController._searchResults; -+ this._searchProvider = new RealmSearchProvider.RealmSearchProvider(); -+ this._searchProvider.createResultDisplay(this._searchResults); -+ this._searchResults._registerProvider(this._searchProvider); -+ -+ this._frameManager = new RealmWindowFrame.WindowFrameManager(); -+ } -+ -+ animateSwitch(from, to, onComplete) { -+ this._switchAnimation.animateSwitch(from, to, onComplete); -+ } -+ -+ _switchRealms(display, window, binding) { -+ let popup = new RealmSwitcher.SwitchRealmPopup(this._switchAction, this._switchActionBackward); -+ if (!popup.show(binding.is_reversed(), binding.get_name(), binding.get_mask())) -+ popup.fadeAndDestroy(); -+ } -+ -+}; -+ -diff --git a/js/ui/realms/realmSearchProvider.js b/js/ui/realms/realmSearchProvider.js -new file mode 100644 -index 0000000..00ad04d ---- /dev/null -+++ b/js/ui/realms/realmSearchProvider.js -@@ -0,0 +1,326 @@ -+const { Clutter, GObject, Pango, Shell, St } = imports.gi; -+ -+const Search = imports.ui.search; -+const Main = imports.ui.main; -+const Util = imports.misc.util; -+ -+// Based on ProviderInfo in search.js -+var RealmProviderInfo = GObject.registerClass( -+class RealmProviderInfo extends St.Button { -+ _init() { -+ super._init({ -+ style_class: 'search-provider-icon', -+ reactive: false, -+ can_focus: false, -+ accessible_name: "Realms", -+ track_hover: false, -+ y_align: Clutter.ActorAlign.START, -+ }); -+ -+ this._content = new St.BoxLayout({ vertical: false, -+ style_class: 'list-search-provider-content' }); -+ this.set_child(this._content); -+ -+ let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE, -+ icon_name: 'computer' }); -+ -+ let detailsBox = new St.BoxLayout({ style_class: 'list-search-provider-details', -+ vertical: true, -+ x_expand: true }); -+ -+ let nameLabel = new St.Label({ -+ text: "Realms", -+ x_align: Clutter.ActorAlign.START -+ }); -+ -+ this._moreLabel = new St.Label({ x_align: Clutter.ActorAlign.START }); -+ -+ detailsBox.add_actor(nameLabel); -+ detailsBox.add_actor(this._moreLabel); -+ -+ -+ this._content.add_actor(icon); -+ this._content.add_actor(detailsBox); -+ } -+ -+ get PROVIDER_ICON_SIZE() { -+ return 48; -+ } -+ -+ setMoreCount(count) { -+ this._moreLabel.text = ngettext("%d more", "%d more", count).format(count); -+ this._moreLabel.visible = count > 0; -+ } -+}); -+ -+var MAX_LIST_SEARCH_RESULTS_ROWS = 10; -+ -+// Based on ListSearchResult in search.js -+var RealmSearchResult = GObject.registerClass( -+class ListSearchResult extends Search.SearchResult { -+ _init(provider, metaInfo, resultsView) { -+ super._init(provider, metaInfo, resultsView); -+ -+ this.style_class = 'list-search-result'; -+ -+ let content = new St.BoxLayout({ -+ style_class: 'list-search-result-content', -+ vertical: false, -+ x_align: Clutter.ActorAlign.FILL, -+ x_expand: true, -+ y_expand: true, -+ }); -+ this.set_child(content); -+ -+ this._termsChangedId = 0; -+ -+ let titleBox = new St.BoxLayout({ -+ style_class: 'list-search-result-title', -+ y_align: Clutter.ActorAlign.CENTER, -+ }); -+ -+ content.add_child(titleBox); -+ -+ // An icon for, or thumbnail of, content -+ let icon = this.metaInfo['createIcon'](this.ICON_SIZE); -+ if (icon) -+ titleBox.add(icon); -+ -+ let title = new St.Label({ -+ text: this.metaInfo['name'], -+ y_align: Clutter.ActorAlign.CENTER, -+ }); -+ titleBox.add_child(title); -+ -+ this.label_actor = title; -+ -+ if (this.metaInfo['description']) { -+ this._descriptionLabel = new St.Label({ -+ style_class: 'list-search-result-description', -+ x_expand: true, -+ x_align: Clutter.ActorAlign.START, -+ y_align: Clutter.ActorAlign.CENTER, -+ }); -+ this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; -+ content.add_child(this._descriptionLabel); -+ -+ this._termsChangedId = -+ this._resultsView.connect('terms-changed', -+ this._highlightTerms.bind(this)); -+ -+ this._highlightTerms(); -+ } -+ -+ let id = this.metaInfo['id']; -+ -+ if (id != ':new:') { -+ this.configButton = new St.Button({ -+ style_class: 'button', -+ track_hover: true, -+ can_focus: true, -+ child: new St.Icon({ -+ style_class: 'realm-config-icon', -+ icon_name: 'emblem-system-symbolic', -+ icon_size: 24, -+ }), -+ }); -+ -+ this.configButton.connect('clicked', () => { -+ Main.overview.toggle(); -+ Util.spawn(['/usr/libexec/realm-config-ui', id]); -+ }); -+ content.add_child(this.configButton); -+ } -+ -+ this.connect('destroy', this._onDestroy.bind(this)); -+ } -+ -+ get ICON_SIZE() { -+ return 24; -+ } -+ -+ _highlightTerms() { -+ let markup = this._resultsView.highlightTerms(this.metaInfo['description'].split('\n')[0]); -+ this._descriptionLabel.clutter_text.set_markup(markup); -+ } -+ -+ _onDestroy() { -+ if (this._termsChangedId) -+ this._resultsView.disconnect(this._termsChangedId); -+ this._termsChangedId = 0; -+ } -+}); -+ -+// Based on ListSearchResults in search.js -+var RealmSearchResults = GObject.registerClass( -+class RealmSearchResults extends Search.SearchResultsBase { -+ _init(provider, resultsView) { -+ super._init(provider, resultsView); -+ -+ this._container = new St.BoxLayout({ style_class: 'search-section-content' }); -+ this.providerInfo = new RealmProviderInfo(); -+ this.providerInfo.connect('key-focus-in', this._keyFocusIn.bind(this)); -+ this.providerInfo.connect('clicked', () => { -+ Main.overview.toggle(); -+ }); -+ -+ this._container.add_child(this.providerInfo); -+ -+ this._content = new St.BoxLayout({ -+ style_class: 'list-search-results', -+ vertical: true, -+ x_expand: true, -+ }); -+ this._container.add_child(this._content); -+ -+ this._resultDisplayBin.set_child(this._container); -+ } -+ -+ _setMoreCount(count) { -+ this.providerInfo.setMoreCount(count); -+ } -+ -+ _getMaxDisplayedResults() { -+ return MAX_LIST_SEARCH_RESULTS_ROWS; -+ } -+ -+ _clearResultDisplay() { -+ this._content.remove_all_children(); -+ } -+ -+ _createResultDisplay(meta) { -+ return super._createResultDisplay(meta) || -+ new RealmSearchResult(this.provider, meta, this._resultsView); -+ } -+ -+ _addItem(display) { -+ this._content.add_actor(display); -+ } -+ -+ getFirstResult() { -+ if (this._content.get_n_children() > 0) -+ return this._content.get_child_at_index(0); -+ else -+ return null; -+ } -+ -+}); -+ -+var RealmSearchProvider = class RealmSearchProvider { -+ constructor() { -+ this._shellRealms = Shell.Realms.get_default(); -+ this.id = 'realms'; -+ this.isRemoteProvider = false; -+ this.canLaunchSearch = false; -+ this.display = null; -+ } -+ -+ createResultDisplay(resultsView) { -+ this.display = new RealmSearchResults(this, resultsView); -+ this.display.connect('notify::focus-child', resultsView._focusChildChanged.bind(resultsView)); -+ this.display.hide(); -+ resultsView._content.add(this.display); -+ } -+ -+ createIcon(size, realm) { -+ if (realm.is_running()) { -+ return new St.Icon({ icon_name: 'emblem-synchronizing', icon_size: size }); -+ } else { -+ return new St.Icon({ icon_name: 'exaile', icon_size: size }); -+ } -+ } -+ -+ createRealmMeta(realm) { -+ let id = realm.get_realm_name(); -+ let description = ''; -+ if (realm.is_running()) { -+ description = `Set realm-${id} as current realm`; -+ } else { -+ description = `Start realm-${id}`; -+ } -+ -+ return { -+ id: id, -+ name: `realm-${id}`, -+ description: description, -+ createIcon: size => { -+ return this.createIcon(size, realm); -+ }, -+ }; -+ } -+ -+ createNewRealmMeta() { -+ return { -+ id: ':new:', -+ name: 'New Realm', -+ description: 'Create a new realm', -+ createIcon: size => { -+ return new St.Icon({ -+ icon_name: 'computer', -+ icon_size: size, -+ }); -+ } -+ } -+ } -+ -+ getResultMetas (ids, callback) { -+ let metas = []; -+ -+ for (let id of ids) { -+ if (id == ":new:") { -+ metas.push(this.createNewRealmMeta()); -+ } else { -+ let realm = this._shellRealms.realm_by_name(id); -+ if (realm && !realm.is_current()) { -+ metas.push(this.createRealmMeta(realm)); -+ } else { -+ log(`No realm found for ${id}`); -+ } -+ } -+ } -+ callback(metas); -+ } -+ -+ activateResult (resultId, _terms) { -+ -+ if (resultId == ':new:') { -+ Main.overview.toggle(); -+ Util.spawn(['/usr/libexec/realm-config-ui', '--new']); -+ return; -+ } -+ -+ let realm = this._shellRealms.realm_by_name(resultId); -+ if (realm) { -+ realm.set_current(); -+ } else { -+ log(`No realm found for ${resultId}`); -+ } -+ } -+ -+ filterResults(results, maxNumber) { -+ return results.slice(0, maxNumber) -+ } -+ -+ getInitialResultSet(terms, callback, _cancellable) { -+ let realms = this._shellRealms.get_all_realms(); -+ let matches = []; -+ -+ if (terms.length == 1 && "new".startsWith(terms[0])) { -+ matches.push(":new:"); -+ } -+ -+ for (let realm of realms) { -+ if (!realm.is_current()) { -+ let name = realm.get_realm_name(); -+ if (terms.every(t => name.indexOf(t) != -1)) { -+ matches.push(name); -+ } -+ } -+ } -+ callback(matches); -+ } -+ -+ getSubsearchResultSet(previousResults, terms, callback, cancellable) { -+ this.getInitialResultSet(terms, callback, cancellable) -+ } -+} -diff --git a/js/ui/realms/realmSwitcher.js b/js/ui/realms/realmSwitcher.js -new file mode 100644 -index 0000000..507df00 ---- /dev/null -+++ b/js/ui/realms/realmSwitcher.js -@@ -0,0 +1,325 @@ -+ -+const { Clutter, GObject, Meta, Shell, St } = imports.gi; -+ -+const Background = imports.ui.background; -+const SwitcherPopup = imports.ui.switcherPopup; -+const Layout = imports.ui.layout; -+const Main = imports.ui.main; -+ -+const WINDOW_ANIMATION_TIME = 2000; -+var APP_ICON_SIZE = 96; -+ -+var RealmItem = GObject.registerClass( -+class RealmItem extends St.BoxLayout { -+ _init(realm, workspace_group) { -+ super._init({ vertical: true }); -+ this.realm = realm; -+ -+ this.add_child(workspace_group); -+ -+ this.label = new St.Label({ -+ text: `realm-${this.realm.realm_name}`, -+ x_align: Clutter.ActorAlign.CENTER, -+ }); -+ -+ this.add_child(this.label); -+ } -+ -+ activate() { -+ this.realm.set_current(); -+ } -+ -+}); -+ -+function getRealmItems() { -+ const monitor = Main.layoutManager.primaryMonitor; -+ const realms = Shell.Realms.get_default(); -+ let realm_list = realms.get_running_realms(); -+ let items = []; -+ realm_list.forEach(realm => { -+ let ws = realm.get_active_workspace(); -+ if (ws) { -+ let size = 256; // default thumbnail size -+ let scale = Math.min(1.0, size / monitor.width, size / monitor.height); -+ let wsgroup = new WorkspaceGroup(ws, monitor, scale); -+ items.push(new RealmItem(realm, wsgroup)); -+ } -+ }); -+ return items; -+} -+ -+var SwitchRealmList = GObject.registerClass( -+class SwitchRealmList extends SwitcherPopup.SwitcherList { -+ _init(items) { -+ super._init(false); -+ -+ items.forEach(item => { -+ this.addItem(item, item.label); -+ }); -+ } -+}); -+ -+var SwitchRealmPopup = GObject.registerClass( -+class SwitchRealmPopup extends SwitcherPopup.SwitcherPopup { -+ _init(action, actionBackward) { -+ super._init(); -+ this._action = action; -+ this._actionBackward = actionBackward; -+ this._items = getRealmItems(); -+ this._switcherList = new SwitchRealmList(this._items); -+ } -+ -+ _keyPressHandler(keysym, action) { -+ if (action == this._action) -+ this._select(this._next()); -+ else if (action == this._actionBackward) -+ this._select(this._previous()); -+ else if (keysym == Clutter.KEY_Left) -+ this._select(this._previous()); -+ else if (keysym == Clutter.KEY_Right) -+ this._select(this._next()); -+ else -+ return Clutter.EVENT_PROPAGATE; -+ -+ return Clutter.EVENT_STOP; -+ } -+ -+ _finish() { -+ super._finish(); -+ this._items[this._selectedIndex].activate(); -+ } -+ -+}); -+ -+const WorkspaceGroup = GObject.registerClass( -+class WorkspaceGroup extends Clutter.Actor { -+ _init(workspace, monitor, scale = 1.0) { -+ super._init(); -+ this._workspace = workspace; -+ this._monitor = monitor; -+ this._scale = scale; -+ this._windowRecords = []; -+ this.width = monitor.width * scale; -+ this.height = monitor.height * scale; -+ this._background = new Meta.BackgroundGroup({ -+ width: this.width * this._scale, -+ height: this.height * this._scale, -+ }); -+ this.add_actor(this._background); -+ this._bgManager = new Background.BackgroundManager({ -+ container: this._background, -+ monitorIndex: this._monitor.index, -+ controlPosition: false, -+ }); -+ this.clip_to_allocation = true; -+ -+ this._createWindows(); -+ this.connect('destroy', this._onDestroy.bind(this)); -+ this._restackedId = global.display.connect('restacked', this._syncStacking.bind(this)); -+ } -+ -+ get workspace() { -+ return this._workspace; -+ } -+ -+ _shouldShowWindow(window) { -+ if (!window.showing_on_its_workspace()) -+ return false; -+ -+ const geometry = global.display.get_monitor_geometry(this._monitor.index); -+ const [intersects] = window.get_frame_rect().intersect(geometry); -+ if (!intersects) -+ return false; -+ -+ const isSticky = window.is_on_all_workspaces(); -+ -+ return !isSticky && window.located_on_workspace(this._workspace); -+ } -+ -+ _syncStacking() { -+ const windowActors = global.get_window_actors().filter(w => -+ this._shouldShowWindow(w.meta_window)); -+ -+ let lastRecord; -+ -+ for (const windowActor of windowActors) { -+ const record = this._windowRecords.find(r => r.windowActor === windowActor); -+ this.set_child_above_sibling(record.clone, lastRecord ? lastRecord.clone : this._background); -+ lastRecord = record; -+ } -+ } -+ -+ _createWindows() { -+ const windowActors = global.get_window_actors().filter(w => -+ this._shouldShowWindow(w.meta_window)); -+ for (const windowActor of windowActors) { -+ let [width,height] = windowActor.get_size(); -+ const clone = new Clutter.Clone({ -+ source: windowActor, -+ width: width * this._scale, -+ height: height * this._scale, -+ x: (windowActor.x - this._monitor.x) * this._scale, -+ y: (windowActor.y - this._monitor.y) * this._scale, -+ }); -+ this.add_child(clone); -+ const record = {windowActor, clone }; -+ record.windowDestroyId = windowActor.connect('destroy', () => { -+ clone.destroy(); -+ this._windowRecords.splice(this._windowRecords.indexOf(record), 1); -+ }); -+ this._windowRecords.push(record); -+ } -+ } -+ -+ _removeWindows() { -+ for (const record of this._windowRecords) { -+ record.windowActor.disconnect(record.windowDestroyId); -+ record.clone.destroy(); -+ } -+ this._windowRecords = []; -+ } -+ -+ _onDestroy() { -+ global.display.disconnect(this._restackedId); -+ this._removeWindows(); -+ this._bgManager.destroy(); -+ } -+ -+}); -+ -+const MonitorGroup = GObject.registerClass({ -+ Properties: { -+ 'progress': GObject.ParamSpec.double( -+ 'progress', 'progress', 'progress', -+ GObject.ParamFlags.READWRITE, -+ -Infinity, Infinity, 0), -+ }, -+}, class MonitorGroup extends St.Widget { -+ _init(monitor, fromIndex, toIndex) { -+ super._init({ -+ clip_to_allocation: true, -+ style_class: 'workspace-animation', -+ }); -+ this._monitor = monitor; -+ const constraint = new Layout.MonitorConstraint({ index: monitor.index }); -+ this.add_constraint(constraint); -+ -+ this._container = new Clutter.Actor(); -+ this.add_child(this._container); -+ -+ this._progress = 0; -+ this._fadeOut = true; -+ -+ this._workspaceGroups = []; -+ this._blackBackground = new Clutter.Actor(); -+ this._blackBackground.width = monitor.width; -+ this._blackBackground.height = monitor.height; -+ let [_res, color] = Clutter.Color.from_string("#000000ff"); -+ this._blackBackground.background_color = color; -+ -+ -+ -+ this.addWorkspaceByIndex(toIndex, monitor); -+ // add opaque black actor -+ this._container.add_child(this._blackBackground); -+ this.addWorkspaceByIndex(fromIndex, monitor); -+ -+ // tween 'from' WorkspaceGroup opacity from 255 to 0 fading workspace to black background -+ // tween 'block' actor opacity from 255 to 0 revealing 'to' WorkspaceGroup -+ } -+ -+ addWorkspaceByIndex(idx, monitor) { -+ const workspaceManager = global.workspace_manager; -+ const ws = workspaceManager.get_workspace_by_index(idx); -+ if (ws) { -+ const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen()); -+ const group = new WorkspaceGroup(ws, monitor); -+ this._workspaceGroups.push(group); -+ this._container.add_child(group); -+ } -+ } -+ -+ get progress() { -+ return this._progress; -+ } -+ -+ // Interpolate opacity from 0 (full opaque) to 50 (full transparent) -+ calculateOpacity(progress) { -+ return 255 - (255 * (progress / 50.0)); -+ } -+ -+ set progress(p) { -+ const fromGroup = this._workspaceGroups[this._workspaceGroups.length - 1]; -+ this._progress = p; -+ // 0 - 50 -+ if (p < 50) { -+ this._blackBackground.opacity = 255; -+ fromGroup.opacity = this.calculateOpacity(p); -+ } else if (p < 100) { -+ if (this._fadeOut) { -+ this._fadeOut = false; -+ } -+ fromGroup.opacity = 0; -+ this._blackBackground.opacity = this.calculateOpacity(p - 50); -+ } else { -+ fromGroup.opacity = 0; -+ this._blackBackground.opacity = 0; -+ } -+ } -+}); -+ -+var ContextSwitchAnimationController = class { -+ constructor(indicator) { -+ this._switchData = null; -+ this._indicator = indicator; -+ } -+ -+ _prepareContextSwitch(fromIdx, toIdx) { -+ if (this._switchData) { -+ this._switchData.monitors[0].remove_all_transitions(); -+ this._finishContextSwitch(this._switchData); -+ } -+ -+ const switchData = {}; -+ this._switchData = switchData; -+ switchData.monitors = []; -+ switchData.inProgress = false; -+ -+ const monitor = Main.layoutManager.primaryMonitor; -+ -+ const group = new MonitorGroup(monitor, fromIdx, toIdx); -+ -+ Main.uiGroup.insert_child_above(group, global.window_group); -+ -+ switchData.monitors.push(group); -+ -+ Meta.disable_unredirect_for_display(global.display); -+ } -+ -+ _finishContextSwitch(switchData) { -+ Meta.enable_unredirect_for_display(global.display); -+ this._indicator.update(); -+ this._switchData = null; -+ switchData.monitors.forEach(m => m.destroy()); -+ if (switchData.onComplete) { -+ switchData.onComplete(); -+ } -+ } -+ -+ animateSwitch(fromIdx, toIdx, onComplete) { -+ -+ this._prepareContextSwitch(fromIdx, toIdx); -+ this._switchData.inProgress = true; -+ this._switchData.onComplete = onComplete; -+ -+ const params = { -+ duration: WINDOW_ANIMATION_TIME, -+ mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, -+ }; -+ params.onComplete = () => { -+ this._finishContextSwitch(this._switchData); -+ }; -+ this._indicator.clear(); -+ this._switchData.monitors[0].ease_property('progress', 100, params); -+ } -+} -diff --git a/js/ui/realms/realmWindowFrame.js b/js/ui/realms/realmWindowFrame.js -new file mode 100644 -index 0000000..0a36179 ---- /dev/null -+++ b/js/ui/realms/realmWindowFrame.js -@@ -0,0 +1,371 @@ -+const { Clutter, Cogl, GObject, Meta, Shell, St } = imports.gi; -+ -+const Main = imports.ui.main; -+ -+var pastelColors = {}; -+ -+function pastelColorsFromName(name) { -+ var hash = 0; -+ -+ if (name in pastelColors) { -+ return pastelColors[name]; -+ } -+ if (name === undefined || name === null || name === "") { -+ hash = Math.random() * 0xffffff; -+ } else { -+ for (let i = 0, ii = name.length; i < ii; i++) { -+ hash = name.charCodeAt(i) + ((hash << 5) - hash); -+ } -+ } -+ -+ let baseline = 127; -+ let multiplier = 64 + ((hash>>24)&0xff); -+ let ok = false; -+ let i = 0; -+ while (!ok && i++ < 10) { -+ var r = ( Math.round( (hash&0xff) * multiplier ) + baseline ) & 0xff; -+ var g = ( Math.round( ((hash>>8)&0xff) * multiplier ) + baseline ) & 0xff; -+ var b = ( Math.round( ((hash>>16)&0xff) * multiplier ) + baseline ) & 0xff; -+ multiplier += 10; -+ let d = Math.sqrt( Math.pow((r-0xff), 2) + Math.pow(g, 2) + Math.pow(b, 2) ); -+ let p = Math.round( ( d / Math.sqrt( Math.pow(255, 2) * 3 ) ) * 100 ); -+ if (p <= 35) { -+ continue; -+ } -+ ok = true; -+ } -+ -+ pastelColors[name] = [r,g,b]; -+ return pastelColors[name]; -+} -+ -+let IgnoredWindowTypes = [ -+ Meta.WindowType.TOOLTIP -+ , Meta.WindowType.MENU -+ , Meta.WindowType.DROPDOWN_MENU -+ , Meta.WindowType.POPUP_MENU -+]; -+ -+var WindowFrameManager = class WindowFrameManager { -+ constructor() { -+ this._windowMapId = -+ global.window_manager.connect('map', this._handleWindowMap.bind(this)); -+ this._contextWindowMovedId = -+ global.workspace_manager.connect('context-window-moved', this._onContextWindowMoved.bind(this)); -+ -+ this._realms = Shell.Realms.get_default(); -+ this._labelOnTop = true; -+ this._workspaces = []; -+ this.trackWindows(); -+ } -+ -+ _onContextWindowMoved(workspaceManager, window) { -+ let actor = window.get_compositor_private(); -+ if (actor) { -+ this.handleWindow(actor); -+ } -+ return Clutter.EVENT_PROPAGATE; -+ } -+ -+ _handleWindowMap(shellwm, actor) { -+ this.handleWindow(actor); -+ return Clutter.EVENT_PROPAGATE; -+ } -+ -+ windowRealmName(win) { -+ if (this._realms.is_citadel_window(win)) { -+ return "Citadel"; -+ } else { -+ let realm = this._realms.realm_by_window(win); -+ if (realm) { -+ return realm.realm_name; -+ } -+ } -+ return ""; -+ } -+ -+ handleWindow(actor) { -+ let win = actor.metaWindow; -+ -+ if (win.is_on_foreign_workspace_context()) { -+ let realm_name = this.windowRealmName(win); -+ this._applyBorder(actor, win, realm_name); -+ } else { -+ this._removeBorder(actor); -+ } -+ } -+ -+ trackWindows() { -+ var actors = global.get_window_actors(); -+ actors.forEach(a => this.handleWindow(a)); -+ } -+ -+ _removeLabel(actor) { -+ var children = actor.get_children(); -+ for (const child of children) { -+ if (child.get_name() == 'realm-label') { -+ actor.remove_child(child); -+ } -+ } -+ } -+ -+ _removeBorder(actor) { -+ actor.remove_effect_by_name('foreign'); -+ this._removeLabel(actor); -+ } -+ -+ _applyLabel(actor, win, realm_name, r, g, b) { -+ let _lot = this._labelOnTop; -+ //let geom = actor.get_allocation_geometry(); -+ let box = actor.get_allocation_box(); -+ if ((box.y <= 30) || // Top bar XXX: Calculate/obtain from somewhere -+ win.is_fullscreen() === true || // Fullscreen -+ [Meta.MaximizeFlags.BOTH, Meta.MaximizeFlags.VERTICAL].includes(win.get_maximized())) { // Maximized -+ _lot = false; -+ } -+ this._removeLabel(actor); -+ -+ let label = new St.Label({ -+ style_class: 'realm-frame-label', -+ z_position: 1.0, -+ }) -+ let clutter_text = label.get_clutter_text(); -+ let alpha = 64; -+ if (_lot === true) { -+ alpha *= 2; -+ } -+ let color_text = new Clutter.Color({ -+ red: 0, -+ green: 0, -+ blue: 0, -+ alpha: alpha * 1.5 -+ }); -+ let color_background = new Clutter.Color({ -+ red: r, -+ green: g, -+ blue: b, -+ alpha: alpha -+ }); -+ clutter_text.set_color(color_text); -+ clutter_text.set_background_color(color_background); -+ clutter_text.set_name('realm-label'); -+ label.set_text(' '+realm_name+' '); -+ -+ let orect = win.get_frame_rect(); -+ let irect = win.get_buffer_rect(); -+ let borderX = orect.x - irect.x; -+ let borderY = orect.y - irect.y; -+ let margins = { -+ top: borderY + 1 -+ , left: borderX + 2 -+ }; -+ margins.top -= 3; -+ margins.left -= 3; -+ -+ if (_lot === true) { -+ margins.top -= label.get_height(); -+ margins.left -= 2; -+ } -+ if (win.is_fullscreen() === true || win.get_maximized() === Meta.MaximizeFlags.BOTH) { -+ margins.top = 0; -+ margins.left = 0; -+ } -+ label.x = margins.left; -+ label.y = margins.top; -+ actor.add_child(label); -+ } -+ -+ _applyBorder(actor, win, realm_name) { -+ if (IgnoredWindowTypes.includes(win.get_window_type())) { -+ return; -+ } -+ let e = new RealmFrameEffect(); -+ e.setRGB(realm_name, win); -+ -+ if (win.get_window_type() === Meta.WindowType.NORMAL) { -+ this._applyLabel(actor, win, realm_name, e.r, e.g, e.b); -+ } -+ -+ if (!actor.get_effect('foreign')) { -+ actor.add_effect_with_name('foreign', e); -+ } -+ } -+ -+} -+ -+var RealmFrameEffect = GObject.registerClass( -+ class RealmFrameEffect extends Clutter.Effect { -+ _init() { -+ super._init(); -+ this._pipeline = null; -+ } -+ setRGB(name, win) { -+ this._win = win; -+ var r = 0, g = 0, b = 0; -+ if (name == 'Citadel') { -+ r = 0xff; -+ } else { -+ [r,g,b] = pastelColorsFromName(name); -+ } -+ this.r = r; -+ this.g = g; -+ this.b = b; -+ } -+ -+ adjust_actor_box(box, win) { -+ let frame_rect = win.get_frame_rect(); -+ let buffer_rect = win.get_buffer_rect(); -+ let borderX = frame_rect.x - buffer_rect.x; -+ let borderY = frame_rect.y - buffer_rect.y; -+ -+ let margins = { -+ top: borderY - 3, -+ bottom: borderY - 1, -+ left: borderX - 3, -+ right: borderX - 3, -+ }; -+ -+ let wayland = win.get_client_type() == Meta.WindowClientType.WAYLAND; -+ -+ if (wayland) { -+ margins.bottom += 4; -+ } -+ -+ let width = 2; -+ -+ switch (win.get_maximized()) { -+ case Meta.MaximizeFlags.BOTH: -+ margins.top += width; -+ margins.right += width; -+ margins.bottom += width - (wayland ? 5 : 0); -+ margins.left += width; -+ break; -+ case Meta.MaximizeFlags.HORIZONTAL: -+ margins.right += width; -+ margins.left += width; -+ break; -+ case Meta.MaximizeFlags.VERTICAL: -+ margins.top += width; -+ margins.bottom += width; -+ break; -+ } -+ -+ if (win.is_fullscreen()) { -+ margins.top += 3; -+ margins.right += 2; -+ margins.bottom -= (wayland ? 3 : 0); -+ margins.left += 3; -+ } -+ -+ if (!wayland && !win.decorated && !win.is_fullscreen() && (win.get_maximized() !== Meta.MaximizeFlags.BOTH)) { -+ margins.bottom += 4; -+ } -+ -+ } -+ -+ draw_rect(node, x, y, width, height) { -+ const box = new Clutter.ActorBox(); -+ box.set_origin(x, y); -+ box.set_size(width, height); -+ node.add_rectangle(box); -+ } -+ -+ draw_hline(node, x, y, size, line_width = 2) { -+ this.draw_rect(node, x, y, size, line_width); -+ } -+ -+ draw_vline(node, x, y, size, line_width = 2) { -+ this.draw_rect(node, x, y, line_width, size); -+ } -+ -+ vfunc_paint_node(node, ctx) { -+ let actor = this.get_actor(); -+ -+ const actorNode = new Clutter.ActorNode(actor, -1); -+ node.add_child(actorNode); -+ -+ if (!this._pipeline) { -+ let framebuffer = ctx.get_framebuffer(); -+ let coglContext = framebuffer.get_context(); -+ let color = new Cogl.Color(); -+ color.init_from_4ub(this.r, this.g, this.b, 0xc4); -+ this._pipeline = new Cogl.Pipeline(coglContext); -+ this._pipeline.set_color(color); -+ } -+ -+ let win = actor.get_meta_window(); -+ let frame_rect = win.get_frame_rect(); -+ let buffer_rect = win.get_buffer_rect(); -+ let borderX = frame_rect.x - buffer_rect.x; -+ let borderY = frame_rect.y - buffer_rect.y; -+ -+ let margins = { -+ top: borderY - 3, -+ bottom: borderY - 1, -+ left: borderX - 3, -+ right: borderX - 3, -+ }; -+ -+ let wayland = win.get_client_type() == Meta.WindowClientType.WAYLAND; -+ -+ if (wayland) { -+ margins.bottom += 4; -+ } -+ -+ let width = 2; -+ -+ switch (win.get_maximized()) { -+ case Meta.MaximizeFlags.BOTH: -+ margins.top += width; -+ margins.right += width; -+ margins.bottom += width - (wayland ? 5 : 0); -+ margins.left += width; -+ break; -+ case Meta.MaximizeFlags.HORIZONTAL: -+ margins.right += width; -+ margins.left += width; -+ break; -+ case Meta.MaximizeFlags.VERTICAL: -+ margins.top += width; -+ margins.bottom += width; -+ break; -+ } -+ -+ if (win.is_fullscreen()) { -+ margins.top += 3; -+ margins.right += 2; -+ margins.bottom -= (wayland ? 3 : 0); -+ margins.left += 3; -+ } -+ -+ if (!wayland && !win.decorated && !win.is_fullscreen() && (win.get_maximized() !== Meta.MaximizeFlags.BOTH)) { -+ margins.bottom += 4; -+ } -+ -+ const pipelineNode = new Clutter.PipelineNode(this._pipeline); -+ pipelineNode.set_name('Realm Frame'); -+ node.add_child(pipelineNode); -+ -+ const render_box = new Clutter.ActorBox(); -+ -+ let alloc = actor.get_allocation_box(); -+ -+ let w = alloc.get_width() - (margins.right + margins.left); -+ let h = alloc.get_height() - (margins.bottom + margins.top + width); -+ -+ // Clockwise starting at top -+ -+ // Top -+ -+ this.draw_hline(pipelineNode, margins.left, margins.top + width, w, width * 2); -+ -+ // Right -+ this.draw_vline(pipelineNode, alloc.get_width() - width - margins.right, width + margins.top, h); -+ -+ // Bottom -+ this.draw_hline(pipelineNode, margins.left, alloc.get_height() - width - margins.bottom, w); -+ -+ // Left -+ this.draw_vline(pipelineNode, margins.left, margins.top + width, h); -+ } -+ }); -diff --git a/js/ui/realms/realmWindowMenu.js b/js/ui/realms/realmWindowMenu.js -new file mode 100644 -index 0000000..1aef4d0 ---- /dev/null -+++ b/js/ui/realms/realmWindowMenu.js -@@ -0,0 +1,113 @@ -+ -+const { Shell, GObject } = imports.gi; -+ -+const PopupMenu = imports.ui.popupMenu; -+ -+function _windowAppId(window) { -+ const tracker = Shell.WindowTracker.get_default(); -+ const app = tracker.get_window_app(window); -+ if (app) { -+ return app.get_id(); -+ } else { -+ log(`No app found for window ${window.get_description()}`) -+ return null; -+ } -+} -+ -+function windowMenuDebugString(window) { -+ const id = _windowAppId(window); -+ const realm_name = windowRealmName(window); -+ -+ if (!realm_name) { -+ return id; -+ } else if (window.is_on_foreign_workspace_context()) { -+ return `${id} [${realm_name}]`; -+ } else { -+ return `${id} (${realm_name})`; -+ } -+} -+ -+function _createMoveWindowItem(label, realm_name, window) { -+ let item = new PopupMenu.PopupMenuItem(label); -+ item.connect('activate', () => { -+ let realms = Shell.Realms.get_default(); -+ let realm = realms.realm_by_name(realm_name); -+ -+ if (realm) { -+ realm.move_window_to_context(window); -+ } -+ }); -+ return item; -+} -+ -+// Return name of the realm the application this window belongs to is running in. -+function windowRealmName(window) { -+ const realms = Shell.Realms.get_default(); -+ -+ if (realms.is_citadel_window(window)) { -+ return "Citadel" -+ } -+ -+ let realm = realms.realm_by_window(window); -+ -+ if (realm) { -+ return realm.realm_name; -+ } else { -+ return null; -+ } -+} -+ -+// Return name of realm the context this window is currently located on belongs to -+function windowContextRealmName(window) { -+ if (window.on_all_workspaces) { -+ return windowRealmName(window); -+ } -+ -+ let ws = window.get_workspace(); -+ -+ if (!ws) { -+ return null; -+ } -+ const realms = Shell.Realms.get_default(); -+ let realm = realms.realm_by_context_id(ws.get_context_id()); -+ -+ if (realm) { -+ return realm.realm_name; -+ } else { -+ return null; -+ } -+} -+ -+function realmWindowMenu(window) { -+ -+ const realm_name = windowContextRealmName(window); -+ -+ if (!realm_name) { -+ return null; -+ } -+ -+ const realms = Shell.Realms.get_default(); -+ let other_realms = []; -+ -+ let running_realms = realms.get_running_realms(); -+ running_realms.forEach(realm => { -+ if (realm.realm_name != realm_name) { -+ other_realms.push(realm.realm_name); -+ } -+ }); -+ -+ if (other_realms.length == 0) { -+ return null; -+ } else if (other_realms.length == 1) { -+ let name = other_realms[0]; -+ return _createMoveWindowItem(`Move to realm-${name}`, name, window); -+ } -+ -+ let subMenu = new PopupMenu.PopupSubMenuMenuItem('Move to Realm...', true); -+ -+ other_realms.forEach(name => { -+ let item = _createMoveWindowItem(`realm-${name}`, name, window); -+ subMenu.menu.addMenuItem(item); -+ }); -+ return subMenu; -+} -\ No newline at end of file -diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js -index 34c54b6..66774dc 100644 ---- a/js/ui/windowManager.js -+++ b/js/ui/windowManager.js -@@ -198,6 +198,8 @@ var WorkspaceTracker = class { - workspaceManager.connect('workspaces-reordered', () => { - this._workspaces.sort((a, b) => a.index() - b.index()); - }); -+ workspaceManager.connect('context-switched', -+ this._workspaceContextSwitched.bind(this)); - global.window_manager.connect('switch-workspace', - this._queueCheckWorkspaces.bind(this)); - -@@ -253,6 +255,8 @@ var WorkspaceTracker = class { - emptyWorkspaces[index] = false; - } - -+ let current_context_id = workspaceManager.active_context_id(); -+ - let windows = global.get_window_actors(); - for (i = 0; i < windows.length; i++) { - let actor = windows[i]; -@@ -261,7 +265,12 @@ var WorkspaceTracker = class { - if (win.is_on_all_workspaces()) - continue; - -- let workspaceIndex = win.get_workspace().index(); -+ let workspace = win.get_workspace(); -+ -+ if (workspace.get_context_id() != current_context_id) -+ continue; -+ -+ let workspaceIndex = workspace.index(); - emptyWorkspaces[workspaceIndex] = false; - } - -@@ -339,6 +348,28 @@ var WorkspaceTracker = class { - this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, this._checkWorkspaces.bind(this)); - } - -+ -+ _workspaceContextSwitched() { -+ let workspaceManager = global.workspace_manager; -+ let numWorkspaces = workspaceManager.n_workspaces; -+ -+ this._workspaces.forEach(workspace => { -+ workspace.disconnect(workspace._windowAddedId); -+ workspace.disconnect(workspace._windowRemovedId); -+ }); -+ -+ this._workspaces = []; -+ -+ for (let w = 0; w < numWorkspaces; w++) { -+ let workspace = workspaceManager.get_workspace_by_index(w); -+ workspace._windowAddedId = workspace.connect('window-added', this._queueCheckWorkspaces.bind(this)); -+ workspace._windowRemovedId = workspace.connect('window-removed', this._windowRemoved.bind(this)); -+ this._workspaces[w] = workspace; -+ } -+ this._queueCheckWorkspaces(); -+ return false; -+ } -+ - _nWorkspacesChanged() { - let workspaceManager = global.workspace_manager; - let oldNumWorkspaces = this._workspaces.length; -@@ -1630,6 +1661,14 @@ var WindowManager = class { - - this._switchInProgress = true; - -+ if (direction == Meta.MotionDirection.CONTEXT_SWITCH) { -+ Main.realmManager.animateSwitch(from, to, () => { -+ this._shellwm.completed_switch_workspace(); -+ this._switchInProgress = false; -+ }); -+ return; -+ } -+ - this._workspaceAnimation.animateSwitch(from, to, direction, () => { - this._shellwm.completed_switch_workspace(); - this._switchInProgress = false; -diff --git a/js/ui/windowMenu.js b/js/ui/windowMenu.js -index bb6a8df..fbb7e23 100644 ---- a/js/ui/windowMenu.js -+++ b/js/ui/windowMenu.js -@@ -1,11 +1,12 @@ - // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -* - /* exported WindowMenuManager */ - --const { GLib, Meta, St } = imports.gi; -+const { GLib, Meta, Shell, St } = imports.gi; - - const BoxPointer = imports.ui.boxpointer; - const Main = imports.ui.main; - const PopupMenu = imports.ui.popupMenu; -+const RealmWindowMenu = imports.ui.realms.realmWindowMenu; - - var WindowMenu = class extends PopupMenu.PopupMenu { - constructor(window, sourceActor) { -@@ -24,6 +25,18 @@ var WindowMenu = class extends PopupMenu.PopupMenu { - - let item; - -+ let s = RealmWindowMenu.windowMenuDebugString(window); -+ -+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(s)); -+ -+ if (!window.is_on_all_workspaces()) { -+ let realmSubmenu = RealmWindowMenu.realmWindowMenu(window); -+ if (realmSubmenu) { -+ this.addMenuItem(realmSubmenu); -+ } -+ } -+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); -+ - item = this.addAction(_("Minimize"), () => { - window.minimize(); - }); -diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js -index 1af45d8..c7f5eb6 100644 ---- a/js/ui/workspacesView.js -+++ b/js/ui/workspacesView.js -@@ -123,6 +123,10 @@ class WorkspacesView extends WorkspacesViewBase { - this._updateWorkspacesId = - workspaceManager.connect('notify::n-workspaces', - this._updateWorkspaces.bind(this)); -+ this._contextSwitchedId = -+ workspaceManager.connect('context-switched', -+ this._refreshWorkspaces.bind(this)); -+ - this._reorderWorkspacesId = - workspaceManager.connect('workspaces-reordered', () => { - this._workspaces.sort((a, b) => { -@@ -451,6 +455,13 @@ class WorkspacesView extends WorkspacesViewBase { - } - } - -+ _refreshWorkspaces() { -+ for (let ws = this._workspaces.pop(); ws; ws = this._workspaces.pop()) { -+ ws.destroy(); -+ } -+ this._updateWorkspaces(); -+ } -+ - _updateWorkspaces() { - let workspaceManager = global.workspace_manager; - let newNumWorkspaces = workspaceManager.n_workspaces; -@@ -500,6 +511,7 @@ class WorkspacesView extends WorkspacesViewBase { - global.window_manager.disconnect(this._switchWorkspaceNotifyId); - let workspaceManager = global.workspace_manager; - workspaceManager.disconnect(this._updateWorkspacesId); -+ workspaceManager.disconnect(this._contextSwitchedId); - workspaceManager.disconnect(this._reorderWorkspacesId); - } - -diff --git a/src/meson.build b/src/meson.build -index 3ca8f9c..ab54287 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -125,6 +125,7 @@ libshell_private_headers = [ - 'shell-app-cache-private.h', - 'shell-app-system-private.h', - 'shell-global-private.h', -+ 'shell-realms-private.h', - 'shell-window-tracker-private.h', - 'shell-wm-private.h' - ] -@@ -147,6 +148,12 @@ libshell_sources = [ - 'shell-perf-log.c', - 'shell-polkit-authentication-agent.c', - 'shell-polkit-authentication-agent.h', -+ 'shell-realm-item.c', -+ 'shell-realm-item.h', -+ 'shell-realms.c', -+ 'shell-realms.h', -+ 'shell-realm-tracker.c', -+ 'shell-realm-tracker.h', - 'shell-screenshot.c', - 'shell-secure-text-buffer.c', - 'shell-secure-text-buffer.h', -diff --git a/src/shell-app-cache.c b/src/shell-app-cache.c -index 44fc8b0..092e83f 100644 ---- a/src/shell-app-cache.c -+++ b/src/shell-app-cache.c -@@ -23,7 +23,7 @@ - * Shell is running. - */ - --#define DEFAULT_TIMEOUT_SECONDS 5 -+#define DEFAULT_TIMEOUT_SECONDS 2 - - struct _ShellAppCache - { -diff --git a/src/shell-app-system.c b/src/shell-app-system.c -index 828fa72..033e731 100644 ---- a/src/shell-app-system.c -+++ b/src/shell-app-system.c -@@ -12,6 +12,7 @@ - #include "shell-app-cache-private.h" - #include "shell-app-private.h" - #include "shell-window-tracker-private.h" -+#include "shell-realms.h" - #include "shell-app-system-private.h" - #include "shell-global.h" - #include "shell-util.h" -@@ -91,6 +92,110 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass) - G_TYPE_NONE, 0); - } - -+/* -+ * Applications belonging to realms have a prefix added to the filenames -+ * of their desktop files indicating the name of the realm. For example -+ * in a realm called 'main' the desktop file 'org.gnome.Terminal.desktop' -+ * is renamed to 'realm-main.org.gnome.Terminal.desktop'. -+ * -+ * This has the effect of creating a separate application ID for instances -+ * of the same application in multiple realms. -+ */ -+static const char * -+realm_name_from_application_id (const char *id) -+{ -+ gchar **split = NULL; -+ const char *result = NULL; -+ -+ if (!g_str_has_prefix (id, "realm-")) { -+ return NULL; -+ } -+ -+ split = g_strsplit(id, ".", 2); -+ -+ if (split[0]) { -+ g_assert_true (g_str_has_prefix (split[0], "realm-")); -+ result = g_strdup(split[0] + 6); -+ } -+ -+ g_strfreev (split); -+ return result; -+} -+ -+ -+char * -+current_realm_name (gboolean name_only) -+{ -+ gchar *link = NULL; -+ gchar *p = NULL; -+ gchar *realm = NULL; -+ -+ link = g_file_read_link ("/run/citadel/realms/current/current.realm", NULL); -+ -+ if (link) { -+ p = g_strrstr(link, "/realm-"); -+ if (p) { -+ /* skip slash character */ -+ p++; -+ if (name_only) { -+ /* skip 'realm-' */ -+ p += 6; -+ } -+ realm = g_strdup(p); -+ } -+ g_free (link); -+ } -+ -+ return realm; -+} -+ -+static gboolean -+is_current_realm_app (const char *id, const char *current_realm) -+{ -+ -+ return !g_str_has_prefix (id, "realm-") || g_str_has_prefix (id, current_realm); -+} -+ -+static void -+refresh_installed_apps (ShellAppSystem *self) -+{ -+ const GList *l; -+ GAppInfo *app; -+ const char *app_id; -+ char *current_realm; -+ ShellAppSystemPrivate *priv = self->priv; -+ -+ g_list_free_full (g_steal_pointer (&priv->installed_apps), g_object_unref); -+ -+ l = shell_app_cache_get_all (shell_app_cache_get_default ()); -+ -+ current_realm = current_realm_name (FALSE); -+ -+ while (l) { -+ app = l->data; -+ app_id = g_app_info_get_id (app); -+ -+ if (is_current_realm_app (app_id, current_realm)) { -+ priv->installed_apps = g_list_prepend (priv->installed_apps, g_object_ref (app)); -+ } -+ l = l->next; -+ } -+ priv->installed_apps = g_list_reverse (priv->installed_apps); -+ -+ g_free (current_realm); -+} -+ -+ -+static char * -+realm_wm_class (const char *wmclass, const char *realm_name) -+{ -+ if (realm_name) { -+ return g_strdup_printf ("realm-%s.%s", realm_name, wmclass); -+ } else { -+ return g_strdup (wmclass); -+ } -+} -+ - static void - scan_startup_wm_class_to_id (ShellAppSystem *self) - { -@@ -106,6 +211,7 @@ scan_startup_wm_class_to_id (ShellAppSystem *self) - { - GAppInfo *info = l->data; - const char *startup_wm_class, *id, *old_id; -+ char *realm_wmclass; - - id = g_app_info_get_id (info); - startup_wm_class = g_desktop_app_info_get_startup_wm_class (G_DESKTOP_APP_INFO (info)); -@@ -113,12 +219,17 @@ scan_startup_wm_class_to_id (ShellAppSystem *self) - if (startup_wm_class == NULL) - continue; - -+ realm_wmclass = realm_wm_class (startup_wm_class, realm_name_from_application_id (id)); -+ - /* In case multiple .desktop files set the same StartupWMClass, prefer - * the one where ID and StartupWMClass match */ -- old_id = g_hash_table_lookup (priv->startup_wm_class_to_id, startup_wm_class); -- if (old_id == NULL || strcmp (id, startup_wm_class) == 0) -+ old_id = g_hash_table_lookup (priv->startup_wm_class_to_id, realm_wmclass); -+ if (old_id == NULL || strcmp (id, startup_wm_class) == 0) { - g_hash_table_insert (priv->startup_wm_class_to_id, -- g_strdup (startup_wm_class), g_strdup (id)); -+ g_strdup (realm_wmclass), g_strdup (id)); -+ } -+ -+ g_free (realm_wmclass); - } - } - -@@ -347,15 +458,19 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, - */ - ShellApp * - shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system, -- const char *wmclass) -+ const char *wmclass, -+ const char *realm_name) - { - char *canonicalized; - char *desktop_file; -+ char *classname; - ShellApp *app; - - if (wmclass == NULL) - return NULL; - -+ classname = realm_wm_class (wmclass, realm_name); -+ - /* First try without changing the case (this handles - org.example.Foo.Bar.desktop applications) - -@@ -363,14 +478,16 @@ shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system, - the WM_CLASS to Org.example.Foo.Bar, but it also - sets the instance part to org.example.Foo.Bar, so we're ok - */ -- desktop_file = g_strconcat (wmclass, ".desktop", NULL); -+ desktop_file = g_strconcat (classname, ".desktop", NULL); - app = shell_app_system_lookup_heuristic_basename (system, desktop_file); - g_free (desktop_file); - -- if (app) -+ if (app) { -+ g_free (classname); - return app; -+ } - -- canonicalized = g_ascii_strdown (wmclass, -1); -+ canonicalized = g_ascii_strdown (classname, -1); - - /* This handles "Fedora Eclipse", probably others. - * Note g_strdelimit is modify-in-place. */ -@@ -382,6 +499,7 @@ shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system, - - g_free (canonicalized); - g_free (desktop_file); -+ g_free (classname); - - return app; - } -@@ -398,14 +516,20 @@ shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system, - */ - ShellApp * - shell_app_system_lookup_startup_wmclass (ShellAppSystem *system, -- const char *wmclass) -+ const char *wmclass, -+ const char *realm_name) - { - const char *id; -+ char *classname; - - if (wmclass == NULL) - return NULL; - -- id = g_hash_table_lookup (system->priv->startup_wm_class_to_id, wmclass); -+ classname = realm_wm_class (wmclass, realm_name); -+ -+ id = g_hash_table_lookup (system->priv->startup_wm_class_to_id, classname); -+ g_free (classname); -+ - if (id == NULL) - return NULL; - -@@ -435,6 +559,29 @@ _shell_app_system_notify_app_state_changed (ShellAppSystem *self, - g_signal_emit (self, signals[APP_STATE_CHANGED], 0, app); - } - -+static gboolean -+is_current_realm_context_app(ShellApp *app) -+{ -+ ShellRealms *realms = shell_realms_get_default(); -+ ShellRealmItem *item = shell_realms_current_realm (realms); -+ guint id = (item) ? shell_realm_item_get_context_id (item) : 0; -+ -+ GSList *iter = shell_app_get_windows (app); -+ -+ while (iter) { -+ MetaWindow *window = iter->data; -+ if (meta_window_is_on_all_workspaces (window)) { -+ return true; -+ } -+ MetaWorkspace *workspace = meta_window_get_workspace (window); -+ if (meta_workspace_get_context_id (workspace) == id) { -+ return true; -+ } -+ iter = iter->next; -+ } -+ return false; -+} -+ - /** - * shell_app_system_get_running: - * @self: A #ShellAppSystem -@@ -458,7 +605,9 @@ shell_app_system_get_running (ShellAppSystem *self) - { - ShellApp *app = key; - -- ret = g_slist_prepend (ret, app); -+ if (is_current_realm_context_app (app)) { -+ ret = g_slist_prepend (ret, app); -+ } - } - - ret = g_slist_sort (ret, (GCompareFunc)shell_app_compare); -@@ -482,12 +631,16 @@ shell_app_system_search (const char *search_string) - { - char ***results = g_desktop_app_info_search (search_string); - char ***groups, **ids; -+ char *current_realm; -+ -+ current_realm = current_realm_name (FALSE); - - for (groups = results; *groups; groups++) - for (ids = *groups; *ids; ids++) -- if (!g_utf8_validate (*ids, -1, NULL)) -+ if (!g_utf8_validate (*ids, -1, NULL) || !is_current_realm_app (*ids, current_realm)) - **ids = '\0'; - -+ g_free (current_realm); - return results; - } - -@@ -504,5 +657,7 @@ shell_app_system_search (const char *search_string) - GList * - shell_app_system_get_installed (ShellAppSystem *self) - { -- return shell_app_cache_get_all (shell_app_cache_get_default ()); -+ ShellAppSystemPrivate *priv = self->priv; -+ refresh_installed_apps (self); -+ return priv->installed_apps; - } -diff --git a/src/shell-app-system.h b/src/shell-app-system.h -index 8719dbc..6a0203e 100644 ---- a/src/shell-app-system.h -+++ b/src/shell-app-system.h -@@ -20,9 +20,11 @@ ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem * - const char *id); - - ShellApp *shell_app_system_lookup_startup_wmclass (ShellAppSystem *system, -- const char *wmclass); -+ const char *wmclass, -+ const char *realm_name); - ShellApp *shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system, -- const char *wmclass); -+ const char *wmclass, -+ const char *realm_name); - - GSList *shell_app_system_get_running (ShellAppSystem *self); - char ***shell_app_system_search (const char *search_string); -diff --git a/src/shell-global.c b/src/shell-global.c -index 027c9d6..a48d5ef 100644 ---- a/src/shell-global.c -+++ b/src/shell-global.c -@@ -49,6 +49,7 @@ - #include "shell-util.h" - #include "st.h" - #include "switcheroo-control.h" -+#include "shell-realm-tracker.h" - - static ShellGlobal *the_object = NULL; - -@@ -1060,6 +1061,8 @@ _shell_global_set_plugin (ShellGlobal *global, - global->focus_manager = st_focus_manager_get_for_stage (global->stage); - - update_scaling_factor (global, settings); -+ -+ shell_realm_tracker_start (); - } - - GjsContext * -diff --git a/src/shell-realm-item.c b/src/shell-realm-item.c -new file mode 100644 -index 0000000..6ea84bd ---- /dev/null -+++ b/src/shell-realm-item.c -@@ -0,0 +1,392 @@ -+#include "shell-global.h" -+#include "shell-realm-item.h" -+#include "shell-realm-tracker.h" -+#include -+#include -+ -+struct _ShellRealmItem { -+ GObject parent; -+ char *realm_name; -+ char *description; -+ char *namespace; -+ MetaWorkspaceContext *context; -+ guint8 status; -+ gboolean tagged; -+ gboolean disposed; -+}; -+ -+G_DEFINE_TYPE (ShellRealmItem, shell_realm_item, G_TYPE_OBJECT); -+ -+ -+enum { -+ PROP_0, -+ PROP_ITEM_REALM_NAME, -+ PROP_ITEM_DESCRIPTION, -+ PROP_ITEM_NAMESPACE -+}; -+ -+#define REALM_STATUS_RUNNING 1 -+#define REALM_STATUS_CURRENT 2 -+#define REALM_STATUS_SYSTEM 4 -+ -+static void -+shell_realm_item_init(ShellRealmItem *item) -+{ -+} -+ -+ShellRealmItem * -+shell_realm_item_new (const char *realm_name, const char *description, const char *namespace, guint8 status) -+{ -+ ShellRealmItem *item = g_object_new (SHELL_TYPE_REALM_ITEM, NULL); -+ item->realm_name = g_strdup (realm_name); -+ item->description = g_strdup (description); -+ item->namespace = g_strdup (namespace); -+ item->status = status; -+ item->context = NULL; -+ item->tagged = FALSE; -+ item->disposed = FALSE; -+ -+ return item; -+} -+ -+void -+shell_realm_item_acquire_context (ShellRealmItem *item) -+{ -+ if (item->context || item->disposed || shell_realm_item_is_system (item)) { -+ return; -+ } -+ -+ if (!item->namespace || !shell_realm_item_is_running (item)) { -+ g_warning ("ShellRealmItem: Cannot acquire workspace context for realm '%s' because not running or no namespace", item->realm_name); -+ return; -+ } -+ -+ MetaDisplay *display = shell_global_get_display (shell_global_get()); -+ MetaWorkspaceManager *workspace_manager = meta_display_get_workspace_manager (display); -+ item->context = meta_workspace_manager_context_for_namespace (workspace_manager, item->namespace); -+} -+ -+/** -+ * shell_realm_item_get_realm_name: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: The name of the realm for this #ShellRealmItem -+ */ -+const char * -+shell_realm_item_get_realm_name (ShellRealmItem *item) -+{ -+ return item->realm_name; -+} -+ -+/** -+ * shell_realm_item_get_description: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: The description field for this realm or an empty string if no description is set -+ */ -+const char * -+shell_realm_item_get_description (ShellRealmItem *item) -+{ -+ if (item->description) -+ return item->description; -+ else -+ return ""; -+} -+ -+ -+/** -+ * shell_realm_item_get_namespace: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: The namespace field for this realm or an empty string if no namespace is set -+ */ -+const char * -+shell_realm_item_get_namespace (ShellRealmItem *item) -+{ -+ if (item->namespace) -+ return item->namespace; -+ else -+ return ""; -+} -+ -+/** -+ * shell_realm_item_get_context_id: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: The context id for the #MetaWorkspaceContext of this realm or 0 if -+ * no context exists. -+ */ -+guint -+shell_realm_item_get_context_id (ShellRealmItem *item) -+{ -+ if (shell_realm_item_is_running (item) && !item->context) { -+ shell_realm_item_acquire_context (item); -+ } -+ -+ if (item->context) { -+ return meta_workspace_context_id (item->context); -+ } else { -+ return 0; -+ } -+} -+ -+/** -+ * shell_realm_item_get_active_workspace: -+ * @item: A #ShellRealmItem -+ * -+ * Returns: (transfer none): The current workspace for the context -+ * belonging to this item. -+ */ -+MetaWorkspace * -+shell_realm_item_get_active_workspace (ShellRealmItem *item) -+{ -+ if (shell_realm_item_is_running (item) && !item->context) { -+ shell_realm_item_acquire_context (item); -+ } -+ -+ if (item->context) { -+ return meta_workspace_context_get_active_workspace (item->context); -+ } else { -+ return NULL; -+ } -+} -+ -+/** -+ * shell_realm_item_activate_context: -+ * @item: A #ShellRealmItem instance for a running realm -+ * -+ * If a #MetaWorkspaceContext is associated with this realm -+ * set it as the active workspace context. -+ */ -+void -+shell_realm_item_activate_context (ShellRealmItem *item) -+{ -+ shell_realm_item_acquire_context (item); -+ -+ if (item->context) { -+ meta_workspace_context_activate (item->context); -+ } -+} -+ -+/** -+ * shell_realm_item_set_current: -+ * @item: A #ShellRealmItem instance for a running realm -+ * -+ * Sends a DBUS request to change the current realm to this realm. This does not immediately -+ * influence any local state in GNOME shell. Once the realms daemon has changed the current realm -+ * it will emit a signal and the processing of that signal will update the local state. -+ */ -+void -+shell_realm_item_set_current (ShellRealmItem *item) { -+ ShellRealmTracker *tracker = shell_realm_tracker_get_default(); -+ if (item && item->realm_name) { -+ shell_realm_tracker_call_set_current (tracker, item->realm_name); -+ } -+} -+ -+/** -+ * shell_realm_item_move_window_to_context: -+ * @item: A #ShellRealmItem instance for a running realm -+ * @window: A #MetaWindow for some window -+ * -+ * Move window to the currently active workspace in the #MetaWorkspaceContext for -+ * this realm. -+ */ -+void -+shell_realm_item_move_window_to_context (ShellRealmItem *item, MetaWindow *window) -+{ -+ shell_realm_item_acquire_context (item); -+ -+ if (item->context) { -+ meta_workspace_context_move_window_to_context (item->context, window); -+ } else { -+ g_warning ("ShellRealmItem: Attempted to move window to realm '%s' which has no workspace context", item->realm_name); -+ } -+} -+ -+static gboolean -+is_flag_set(guint8 status, guchar flag) -+{ -+ return ((status & flag) != 0) ? TRUE : FALSE; -+} -+ -+static gboolean -+has_status_flag (ShellRealmItem *item, guchar flag) -+{ -+ return is_flag_set (item->status, flag); -+} -+ -+static void -+set_status_flag (ShellRealmItem *item, guint8 flag, gboolean value) -+{ -+ if (value) { -+ item->status |= flag; -+ } else { -+ item->status &= ~flag; -+ } -+} -+ -+/** -+ * shell_realm_item_is_current: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: %TRUE if this #ShellRealmItem is the current realm -+ */ -+gboolean -+shell_realm_item_is_current (ShellRealmItem *item) -+{ -+ return has_status_flag (item, REALM_STATUS_CURRENT); -+} -+ -+/** -+ * shell_realm_item_is_running: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: %TRUE if this #ShellRealmItem is running -+ */ -+gboolean -+shell_realm_item_is_running (ShellRealmItem *item) -+{ -+ return has_status_flag (item, REALM_STATUS_RUNNING); -+} -+ -+/** -+ * shell_realm_item_is_system: -+ * @item: A #ShellRealmItem instance -+ * -+ * Returns: %TRUE if this #ShellRealmItem is a system realm -+ */ -+gboolean -+shell_realm_item_is_system (ShellRealmItem *item) -+{ -+ return has_status_flag (item, REALM_STATUS_SYSTEM); -+} -+ -+ -+void shell_realm_item_set_current_flag (ShellRealmItem *item, gboolean value) -+{ -+ set_status_flag (item, REALM_STATUS_CURRENT, value); -+} -+ -+void shell_realm_item_set_running_flag (ShellRealmItem *item, gboolean value) -+{ -+ set_status_flag (item, REALM_STATUS_RUNNING, value); -+ -+ if (!value && item->context) { -+ g_clear_object(&item->context); -+ } -+} -+ -+ -+void shell_realm_item_update (ShellRealmItem *item, const char *realm_name, const char *namespace, guint8 status) -+{ -+ if (g_strcmp0 (item->realm_name, realm_name)) { -+ g_message ("ShellRealmItem: Realm name changed from %s to %s", item->realm_name, realm_name); -+ g_free (item->realm_name); -+ item->realm_name = g_strdup (realm_name); -+ } -+ -+ if (g_strcmp0 (item->namespace, namespace)) { -+ g_free(item->namespace); -+ item->namespace = g_strdup (namespace); -+ } -+ -+ if (item->status != status) { -+ gboolean was_running = has_status_flag (item, REALM_STATUS_RUNNING); -+ gboolean is_running = is_flag_set (status, REALM_STATUS_RUNNING); -+ gboolean stopped = was_running && !is_running; -+ -+ item->status = status; -+ -+ if (stopped) { -+ g_clear_object(&item->context); -+ } -+ } -+} -+ -+void -+shell_realm_item_set_tagged (ShellRealmItem *item, gboolean is_tagged) -+{ -+ item->tagged = is_tagged; -+} -+ -+gboolean -+shell_realm_item_is_tagged (ShellRealmItem *item) -+{ -+ return item->tagged; -+} -+ -+static void shell_realm_item_get_property (GObject *gobject, -+ guint prop_id, -+ GValue *value, -+ GParamSpec *pspec) -+{ -+ ShellRealmItem *item = SHELL_REALM_ITEM (gobject); -+ switch (prop_id) { -+ case PROP_ITEM_REALM_NAME: -+ g_value_set_string (value, shell_realm_item_get_realm_name (item)); -+ break; -+ case PROP_ITEM_DESCRIPTION: -+ g_value_set_string (value, shell_realm_item_get_description (item)); -+ break; -+ case PROP_ITEM_NAMESPACE: -+ g_value_set_string (value, shell_realm_item_get_namespace (item)); -+ break; -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); -+ break; -+ } -+} -+ -+static void shell_realm_item_dispose (GObject *object) -+{ -+ ShellRealmItem *item = SHELL_REALM_ITEM (object); -+ g_clear_object(&item->context); -+ item->disposed = TRUE; -+ G_OBJECT_CLASS(shell_realm_item_parent_class)->dispose (object); -+} -+ -+static void -+shell_realm_item_finalize (GObject *object) -+{ -+ ShellRealmItem *item = SHELL_REALM_ITEM (object); -+ g_message("ShellRealmItem: finalize (%s)", item->realm_name); -+ g_free (item->realm_name); -+ g_free (item->description); -+ g_free (item->namespace); -+ -+ G_OBJECT_CLASS(shell_realm_item_parent_class)->finalize (object); -+} -+ -+static void -+shell_realm_item_class_init (ShellRealmItemClass *klass) -+{ -+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); -+ -+ gobject_class->get_property = shell_realm_item_get_property; -+ gobject_class->dispose = shell_realm_item_dispose; -+ gobject_class->finalize = shell_realm_item_finalize; -+ -+ g_object_class_install_property (gobject_class, -+ PROP_ITEM_NAMESPACE, -+ g_param_spec_string ("namespace", -+ "Context Namespace", -+ "PID namespace of context", -+ NULL, -+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -+ g_object_class_install_property (gobject_class, -+ PROP_ITEM_REALM_NAME, -+ g_param_spec_string ("realm-name", -+ "Realm Name", -+ "Name of realm associated with this context", -+ NULL, -+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, -+ PROP_ITEM_DESCRIPTION, -+ g_param_spec_string ("description", -+ "Realm Description", -+ "Optional description of realm", -+ NULL, -+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -+} -\ No newline at end of file -diff --git a/src/shell-realm-item.h b/src/shell-realm-item.h -new file mode 100644 -index 0000000..8e9d8ec ---- /dev/null -+++ b/src/shell-realm-item.h -@@ -0,0 +1,35 @@ -+#ifndef __SHELL_REALM_ITEM_H__ -+#define __SHELL_REALM_ITEM_H__ -+ -+#include -+#include -+ -+#define SHELL_TYPE_REALM_ITEM (shell_realm_item_get_type()) -+G_DECLARE_FINAL_TYPE (ShellRealmItem, shell_realm_item, SHELL, REALM_ITEM, GObject) -+ -+ShellRealmItem *shell_realm_item_new (const char *realm_name, const char *description, const char *namespace, guint8 status); -+ -+const char *shell_realm_item_get_realm_name (ShellRealmItem *item); -+const char *shell_realm_item_get_description (ShellRealmItem *item); -+const char *shell_realm_item_get_namespace (ShellRealmItem *item); -+guint shell_realm_item_get_context_id (ShellRealmItem *item); -+MetaWorkspace *shell_realm_item_get_active_workspace (ShellRealmItem *item); -+ -+void shell_realm_item_update (ShellRealmItem *item, const char *realm_name, const char *namespace, guint8 status); -+ -+ -+void shell_realm_item_set_current_flag (ShellRealmItem *item, gboolean value); -+void shell_realm_item_set_running_flag (ShellRealmItem *item, gboolean value); -+ -+void shell_realm_item_activate_context (ShellRealmItem *item); -+void shell_realm_item_set_current (ShellRealmItem *item); -+void shell_realm_item_move_window_to_context (ShellRealmItem *item, MetaWindow *window); -+gboolean shell_realm_item_is_current(ShellRealmItem *item); -+gboolean shell_realm_item_is_running(ShellRealmItem *item); -+gboolean shell_realm_item_is_system(ShellRealmItem *item); -+ -+void shell_realm_item_set_tagged (ShellRealmItem *item, gboolean is_tagged); -+gboolean shell_realm_item_is_tagged (ShellRealmItem *item); -+ -+void shell_realm_item_acquire_context (ShellRealmItem *item); -+#endif //__SHELL_REALM_ITEM_H__ -diff --git a/src/shell-realm-tracker.c b/src/shell-realm-tracker.c -new file mode 100644 -index 0000000..97cb6ed ---- /dev/null -+++ b/src/shell-realm-tracker.c -@@ -0,0 +1,297 @@ -+#include "shell-realm-tracker.h" -+#include "shell-realms-private.h" -+ -+#define NUM_BUS_SIGNAL_IDS 5 -+ -+#define REALMS_BUS_NAME "com.subgraph.realms" -+#define REALMS_OBJECT_PATH "/com/subgraph/realms" -+#define REALMS_MANAGER_INTERFACE "com.subgraph.realms.Manager" -+ -+struct _ShellRealmTracker { -+ GObject parent; -+ GDBusConnection *dbus; -+ guint realms_watch_id; -+ guint bus_signal_ids[NUM_BUS_SIGNAL_IDS]; -+ gboolean destroy_in_progress; -+}; -+ -+G_DEFINE_TYPE (ShellRealmTracker, shell_realm_tracker, G_TYPE_OBJECT); -+ -+static void -+shell_realm_tracker_init (ShellRealmTracker *tracker) -+{ -+ tracker->dbus = NULL; -+ tracker->realms_watch_id = 0; -+ tracker->destroy_in_progress = FALSE; -+} -+ -+static void -+shell_realm_tracker_class_init (ShellRealmTrackerClass *klass) -+{ -+} -+ -+static void -+on_realm_bus_signal(GDBusConnection *connection, -+ const gchar *sender_name, -+ const gchar *object_path, -+ const gchar *interface_name, -+ const gchar *signal_name, -+ GVariant *parameters, -+ gpointer user_data) -+{ -+ ShellRealms *realms = shell_realms_get_default(); -+ -+ const gchar *realm_name = NULL; -+ const gchar *description = NULL; -+ const gchar *namespace = NULL; -+ guint8 status = 0; -+ -+ g_message ("ShellRealmTracker: on_realm_bus_signal(%s)", signal_name); -+ -+ if (g_str_equal (signal_name, "RealmStarted")) { -+ -+ g_variant_get (parameters, "(&s&sy)", &realm_name, &namespace, &status); -+ shell_realms_on_realm_started (realms, realm_name, namespace, status); -+ -+ } else if (g_str_equal (signal_name, "RealmStopped")) { -+ -+ g_variant_get (parameters, "(&sy)", &realm_name, &status); -+ shell_realms_on_realm_stopped (realms, realm_name); -+ -+ } else if (g_str_equal (signal_name, "RealmRemoved")) { -+ -+ g_variant_get (parameters, "(&s)", &realm_name); -+ shell_realms_on_realm_removed (realms, realm_name); -+ -+ } else if (g_str_equal (signal_name, "RealmCurrent")) { -+ -+ g_variant_get (parameters, "(&sy)", &realm_name, &status); -+ shell_realms_on_realm_current (realms, realm_name); -+ -+ } else if (g_str_equal (signal_name, "RealmNew")) { -+ -+ g_variant_get (parameters, "(&s&sy)", &realm_name, &description, status); -+ shell_realms_on_realm_new (realms, realm_name, description, status); -+ -+ } else { -+ g_warning("Unexpected signal name '%s' received from realms manager DBUS", signal_name); -+ } -+} -+ -+static void -+realm_state_process_elements (ShellRealmTracker *self, GVariant *response) -+{ -+ -+ GVariantIter *iter = NULL; -+ const gchar *name = NULL; -+ const gchar *description = NULL; -+ const gchar *namespace = NULL; -+ guchar status = 0; -+ -+ ShellRealms *realms = shell_realms_get_default(); -+ shell_realms_untag_all (realms); -+ -+ g_variant_get(response, "(a(ssssy))", &iter); -+ -+ // (name, desc, realmfs, namespace, status) -+ while (g_variant_iter_next(iter, "(&s&ss&sy)", &name, &description, NULL, &namespace, &status)) { -+ shell_realms_update_realm (realms, name, description, namespace, status); -+ } -+ -+ shell_realms_remove_untagged (realms); -+ g_variant_iter_free(iter); -+} -+ -+static void -+request_realm_state_finish(GObject *object, GAsyncResult *result, gpointer data) -+{ -+ ShellRealmTracker *self = data; -+ -+ GError *error = NULL; -+ -+ GVariant *response = g_dbus_connection_call_finish (G_DBUS_CONNECTION(object), result, &error); -+ -+ if (!response) { -+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { -+ g_warning("MetaRealmDbus: Error calling 'List' bus method: %s", error->message); -+ } -+ g_clear_error (&error); -+ return; -+ } -+ -+ if (self->destroy_in_progress) { -+ g_variant_unref (response); -+ return; -+ } -+ -+ realm_state_process_elements(self, response); -+ g_variant_unref(response); -+} -+ -+static void -+call_dbus_method (ShellRealmTracker *self, const gchar *method, GVariant *parameters, GAsyncReadyCallback callback, gpointer user_data) -+{ -+ if (!self->dbus) { -+ g_warning("ShellRealmTracker: call_dbus_method(%s) called when no bus connection present", method); -+ return; -+ } -+ -+ g_dbus_connection_call(self->dbus, -+ REALMS_BUS_NAME, -+ REALMS_OBJECT_PATH, -+ REALMS_MANAGER_INTERFACE, -+ method, -+ parameters, -+ NULL, -+ G_DBUS_CALL_FLAGS_NO_AUTO_START, -+ -1, -+ NULL, -+ callback, -+ user_data); -+} -+ -+static void -+request_realm_state(ShellRealmTracker *self) -+{ -+ call_dbus_method (self, "List", NULL, request_realm_state_finish, self); -+} -+ -+void -+shell_realm_tracker_call_set_current (ShellRealmTracker *self, const char *realm_name) -+{ -+ call_dbus_method (self, "SetCurrent", g_variant_new("(s)", realm_name), NULL, NULL); -+} -+ -+ -+static void -+unsubscribe_signals (ShellRealmTracker *self) -+{ -+ for (int i = 0; i < NUM_BUS_SIGNAL_IDS; i++) { -+ if (self->bus_signal_ids[i]) { -+ if (self->dbus) { -+ g_dbus_connection_signal_unsubscribe(self->dbus, self->bus_signal_ids[i]); -+ } -+ self->bus_signal_ids[i] = 0; -+ } -+ } -+} -+ -+static guint -+bus_signal_subscribe (ShellRealmTracker *self, const gchar *signal_name) -+{ -+ g_assert(self->dbus); -+ -+ return g_dbus_connection_signal_subscribe(self->dbus, -+ REALMS_BUS_NAME, -+ REALMS_MANAGER_INTERFACE, -+ signal_name, -+ REALMS_OBJECT_PATH, -+ NULL, -+ G_DBUS_SIGNAL_FLAGS_NONE, -+ on_realm_bus_signal, -+ self, -+ NULL); -+} -+ -+static void -+subscribe_bus_signals (ShellRealmTracker *self) -+{ -+ if (self->dbus) { -+ int idx = 0; -+ self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmStarted"); -+ self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmStopped"); -+ self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmCurrent"); -+ self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmNew"); -+ self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmRemoved"); -+ g_assert(idx == NUM_BUS_SIGNAL_IDS); -+ } -+} -+ -+static void -+on_realm_manager_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) -+{ -+ ShellRealmTracker *self = user_data; -+ -+ // Avoid processing spurious events while destroying 'self' -+ if (self->destroy_in_progress) { -+ return; -+ } -+ -+ if (!self->dbus) { -+ self->dbus = g_object_ref(connection); -+ subscribe_bus_signals (self); -+ } else { -+ g_warning("Realm tracker already has a connection in on_realm_manager_appeared()"); -+ } -+ -+ request_realm_state (self); -+} -+ -+ -+static void -+on_realm_manager_vanished (GDBusConnection *connection, const gchar *name, gpointer user_data) -+{ -+ ShellRealmTracker *self = user_data; -+ -+ // Avoid processing spurious events while destroying 'self' -+ if (self->destroy_in_progress) { -+ return; -+ } -+ -+ if (!connection) { -+ g_clear_object (&self->dbus); -+ } -+ -+ unsubscribe_signals(self); -+} -+ -+/** -+ * shell_realm_tracker_get_default: -+ * -+ * Return Value: (transfer none): The global #ShellRealmTracker singleton -+ */ -+ShellRealmTracker * -+shell_realm_tracker_get_default(void) -+{ -+ static ShellRealmTracker *instance; -+ if (instance == NULL) { -+ instance = g_object_new (SHELL_TYPE_REALM_TRACKER, NULL); -+ } -+ return instance; -+} -+ -+void shell_realm_tracker_start () -+{ -+ ShellRealmTracker *tracker = shell_realm_tracker_get_default(); -+ -+ if (tracker->realms_watch_id) { -+ g_warning ("ShellRealmTracker: shell_realm_tracker_start() called when already started"); -+ return; -+ } -+ -+ tracker->realms_watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, -+ REALMS_BUS_NAME, -+ G_BUS_NAME_WATCHER_FLAGS_NONE, -+ on_realm_manager_appeared, -+ on_realm_manager_vanished, -+ tracker, -+ g_free); -+ -+} -+ -+void -+shell_realm_tracker_destroy(ShellRealmTracker *self) -+{ -+ if (self->dbus) { -+ unsubscribe_signals (self); -+ g_clear_object (&self->dbus); -+ } -+ -+ // event handlers check this and will bail early in case there are -+ // any in queue. see docs for g_bus_unwatch_name() -+ self->destroy_in_progress = TRUE; -+ -+ // frees 'self' in destroy notifier -+ g_bus_unwatch_name(self->realms_watch_id); -+ -+} -diff --git a/src/shell-realm-tracker.h b/src/shell-realm-tracker.h -new file mode 100644 -index 0000000..a979f95 ---- /dev/null -+++ b/src/shell-realm-tracker.h -@@ -0,0 +1,14 @@ -+#ifndef __SHELL_REALM_TRACKER_H__ -+#define __SHELL_REALM_TRACKER_H__ -+ -+#include -+ -+#define SHELL_TYPE_REALM_TRACKER (shell_realm_tracker_get_type ()) -+G_DECLARE_FINAL_TYPE (ShellRealmTracker, shell_realm_tracker, -+ SHELL, REALM_TRACKER, GObject) -+ -+ShellRealmTracker *shell_realm_tracker_get_default(void); -+void shell_realm_tracker_call_set_current (ShellRealmTracker *self, const char *realm_name); -+void shell_realm_tracker_start (); -+ -+#endif /* __SHELL_REALM_TRACKER_H__ */ -diff --git a/src/shell-realms-private.h b/src/shell-realms-private.h -new file mode 100644 -index 0000000..16a81b2 ---- /dev/null -+++ b/src/shell-realms-private.h -@@ -0,0 +1,21 @@ -+#ifndef __SHELL_REALMS_PRIVATE_H__ -+#define __SHELL_REALMS_PRIVATE_H__ -+#include -+#include "shell-realms.h" -+ -+ -+void shell_realms_untag_all (ShellRealms *realms); -+void shell_realms_remove_untagged (ShellRealms *realms); -+void shell_realms_update_realm (ShellRealms *realms, -+ const char *realm_name, -+ const char *description, -+ const char *namespace, -+ guint8 status); -+ -+void shell_realms_on_realm_started (ShellRealms *realms, const gchar *realm_name, const gchar *namespace, guint8 status); -+void shell_realms_on_realm_current (ShellRealms *realms, const gchar *realm_name); -+void shell_realms_on_realm_stopped (ShellRealms *realms, const gchar *realm_name); -+void shell_realms_on_realm_removed (ShellRealms *realms, const gchar *realm_name); -+void shell_realms_on_realm_new (ShellRealms *realms, const gchar *realm_name, const gchar *description, guint8 status); -+ -+#endif //__SHELL_REALMS_PRIVATE_H__ -diff --git a/src/shell-realms.c b/src/shell-realms.c -new file mode 100644 -index 0000000..44ee2b7 ---- /dev/null -+++ b/src/shell-realms.c -@@ -0,0 +1,442 @@ -+ -+#include -+#include -+#include "shell-realm-item.h" -+#include "shell-realm-tracker.h" -+#include "shell-realms-private.h" -+#include "shell-global.h" -+ -+struct _ShellRealms { -+ GObject parent; -+ GHashTable *realms; -+ GList *running_realms; -+ ShellRealmItem *current_realm; -+}; -+ -+G_DEFINE_TYPE (ShellRealms, shell_realms, G_TYPE_OBJECT); -+ -+enum { -+ REALM_CONTEXT_SWITCHED, -+ LAST_SIGNAL, -+}; -+ -+enum { -+ PROP_0, -+ PROP_CURRENT_REALM, -+}; -+ -+static guint shell_realms_signals [LAST_SIGNAL] = { 0 }; -+ -+/** -+ * shell_realms_current_realm: -+ * @realms: A #ShellRealms instance -+ * -+ * Returns: (transfer none) (nullable): The current realm as a #ShellRealmItem -+ * or %NULL if no realm is current. -+ */ -+ShellRealmItem * -+shell_realms_current_realm (ShellRealms *realms) -+{ -+ return realms->current_realm; -+} -+ -+/** -+ * shell_realms_realm_by_name: -+ * @realms: a #ShellRealms instance -+ * @realm_name: The name of a realm to look up -+ * -+ * Returns: (transfer none) (nullable): A realm #ShellRealmItem or %NULL -+ * if name not found -+ */ -+ShellRealmItem * -+shell_realms_realm_by_name(ShellRealms *realms, const gchar *realm_name) -+{ -+ ShellRealmItem *item = g_hash_table_lookup (realms->realms, realm_name); -+ if (!item) { -+ g_warning("ShellRealms: No realm found for name '%s'", realm_name); -+ } -+ return item; -+} -+ -+/** -+ * shell_realms_realm_by_context_id: -+ * @realms: a #ShellRealms instance -+ * @context_id: A context id to search for. -+ * -+ * Returns: (transfer none) (nullable): The realm #ShellRealmItem for the realm -+ * with a workspace context id matching the specified value or %NULL if no such realm is found -+ */ -+ShellRealmItem * -+shell_realms_realm_by_context_id (ShellRealms *realms, guint context_id) -+{ -+ if (context_id == 0) { -+ return NULL; -+ } -+ -+ for (GList *iter = realms->running_realms; iter; iter = iter->next) { -+ ShellRealmItem *item = iter->data; -+ if (shell_realm_item_get_context_id (item) == context_id) { -+ return item; -+ } -+ } -+ return NULL; -+} -+ -+/** -+ * shell_realms_realm_by_window: -+ * @realms: a #ShellRealms instance -+ * @window: A window to find the corresponding realm for. -+ * -+ * Returns: (transfer none) (nullable): The realm #ShellRealmItem for the realm -+ * the application the window belongs to is running in or %NULL if no realm is found -+ */ -+ShellRealmItem * -+shell_realms_realm_by_window (ShellRealms *realms, MetaWindow *window) -+{ -+ const char *window_ns = meta_window_namespace (window); -+ -+ if (!window_ns) { -+ return NULL; -+ } -+ -+ for (GList *iter = realms->running_realms; iter; iter = iter->next) { -+ ShellRealmItem *item = iter->data; -+ if (g_strcmp0 (window_ns, shell_realm_item_get_namespace (item)) == 0) { -+ return item; -+ } -+ } -+ return NULL; -+} -+ -+/** -+ * shell_realms_is_citadel_window: -+ * @realms: A #ShellRealms instance -+ * @window: A #MetaWindow -+ * -+ * Return #TRUE if the window belongs to an application running inside of Citadel -+ * rather than running in a realm. -+ * -+ * Returns: If window belongs to an application running in Citadel return #True -+ */ -+gboolean -+shell_realms_is_citadel_window (ShellRealms *realms, MetaWindow *window) -+{ -+ MetaDisplay *display = shell_global_get_display (shell_global_get()); -+ MetaWorkspaceManager *workspace_manager = meta_display_get_workspace_manager (display); -+ -+ const char *mutter_ns = meta_workspace_manager_mutter_namespace (workspace_manager); -+ const char *window_ns = meta_window_namespace (window); -+ -+ return g_strcmp0 (mutter_ns, window_ns) == 0; -+} -+ -+/** -+ * shell_realms_get_running_realms: -+ * @realms: the #ShellRealms instance -+ * -+ * Returns all running realms as a list of #ShellRealmItem -+ * -+ * Returns: (transfer none) (element-type ShellRealmItem): a list of -+ * #ShellRealmItem for all running realms. -+ * -+ */ -+GList * -+shell_realms_get_running_realms (ShellRealms *realms) -+{ -+ return realms->running_realms; -+} -+ -+/** -+ * shell_realms_get_all_realms: -+ * @realms: the #ShellRealms instance -+ * -+ * Returns all realms as a list of #ShellRealmItem -+ * -+ * Returns: (transfer container) (element-type ShellRealmItem): all realms as -+ * a list of #ShellRealmItem -+ */ -+GList * -+shell_realms_get_all_realms (ShellRealms *realms) -+{ -+ return g_hash_table_get_values (realms->realms); -+} -+ -+static gboolean -+shell_realms_is_on_running_list (ShellRealms *realms, ShellRealmItem *item) -+{ -+ return (g_list_index (realms->running_realms, item) >= 0); -+} -+ -+static void -+shell_realms_remove_running_realm (ShellRealms *realms, ShellRealmItem *item) -+{ -+ -+ if (!shell_realms_is_on_running_list (realms, item)) { -+ return; -+ } -+ -+ realms->running_realms = g_list_remove(realms->running_realms, item); -+ g_object_unref (item); -+} -+ -+static void -+shell_realms_update_running_list_for_item (ShellRealms *realms, ShellRealmItem *item) -+{ -+ gboolean running = shell_realm_item_is_running (item); -+ -+ if (running) { -+ if (!shell_realms_is_on_running_list (realms, item)) { -+ realms->running_realms = g_list_append (realms->running_realms, g_object_ref (item)); -+ } -+ -+ // If realm is current realm, make sure it's at the front of the list -+ if (shell_realm_item_is_current (item) && g_list_index (realms->running_realms, item) > 0) { -+ realms->running_realms = g_list_remove (realms->running_realms, item); -+ realms->running_realms = g_list_prepend (realms->running_realms, item); -+ } -+ -+ } else { -+ shell_realms_remove_running_realm (realms, item); -+ } -+} -+ -+static void -+shell_realms_set_current_item (ShellRealms *realms, ShellRealmItem *item) -+{ -+ if (realms->current_realm == item) { -+ return; -+ } else if (realms->current_realm) { -+ shell_realm_item_set_current_flag (realms->current_realm, FALSE); -+ } -+ -+ shell_realm_item_set_current_flag (item, TRUE); -+ realms->current_realm = item; -+} -+ -+/* -+ * If a realm already exists for 'realm_name' update it with provided information, otherwise -+ * create a new ShellRealmItem and add it to hash table unless it is a system realm. Returns -+ * the existing or newly created ShellRealmItem or returns NULL if the realm is a system realm. -+ */ -+static ShellRealmItem * -+shell_realms_add_realm_item (ShellRealms *realms, -+ const char *realm_name, -+ const char *description, -+ const char *namespace, -+ guint8 status) -+{ -+ ShellRealmItem *item = g_hash_table_lookup (realms->realms, realm_name); -+ if (item) { -+ shell_realm_item_update (item, realm_name, namespace, status); -+ return item; -+ } -+ -+ item = shell_realm_item_new (realm_name, description, namespace, status); -+ -+ if (shell_realm_item_is_system (item)) { -+ g_clear_object(&item); -+ return NULL; -+ } -+ g_hash_table_insert (realms->realms, g_strdup (realm_name), item); -+ return item; -+} -+ -+void -+shell_realms_update_realm (ShellRealms *realms, -+ const char *realm_name, -+ const char *description, -+ const char *namespace, -+ guint8 status) -+{ -+ ShellRealmItem *item = shell_realms_add_realm_item (realms, realm_name, description, namespace, status); -+ -+ // Ignore system realms -+ if (!item) { -+ return; -+ } -+ -+ shell_realms_update_running_list_for_item (realms, item); -+ shell_realm_item_set_tagged (item, TRUE); -+ -+ // If realm is current, make sure it has a context. This also ensures that the -+ // first context requested is for the current realm. -+ if (shell_realm_item_is_current (item)) { -+ shell_realms_set_current_item (realms, item); -+ shell_realm_item_acquire_context (item); -+ } -+} -+ -+// When processing list of realms returned from "List" dbus method, -+// -+// 1) first all the existing realms are "untagged" -+// 2) As each realm on list is processed it is marked as "tagged" -+// 3) After processing list, realm items that are not tagged were not in list -+// returned from server so remove them. -+// -+void -+shell_realms_untag_all (ShellRealms *realms) -+{ -+ GList *item_list = g_hash_table_get_values (realms->realms); -+ -+ for (GList *iter = item_list; iter; iter = iter->next) { -+ ShellRealmItem *item = iter->data; -+ shell_realm_item_set_tagged (item, FALSE); -+ iter = iter->next; -+ } -+ g_list_free (item_list); -+} -+ -+void -+shell_realms_remove_untagged (ShellRealms *realms) -+{ -+ GHashTableIter iter; -+ gpointer value; -+ -+ g_hash_table_iter_init (&iter, realms->realms); -+ while (g_hash_table_iter_next (&iter, NULL, &value)) { -+ ShellRealmItem *item = value; -+ if (!shell_realm_item_is_tagged (item)) { -+ shell_realms_remove_running_realm (realms, item); -+ g_hash_table_iter_remove (&iter); -+ } -+ } -+} -+ -+// Signal handlers -+ -+void -+shell_realms_on_realm_started (ShellRealms *realms, const gchar *realm_name, const gchar *namespace, guint8 status) -+{ -+ ShellRealmItem *item = shell_realms_realm_by_name (realms, realm_name); -+ if (item) { -+ shell_realm_item_update (item, realm_name, namespace, status); -+ if (!shell_realm_item_is_system (item)) { -+ shell_realms_update_running_list_for_item (realms, item); -+ shell_realm_item_acquire_context (item); -+ } -+ } -+} -+ -+void -+shell_realms_on_realm_current (ShellRealms *realms, const gchar *realm_name) -+{ -+ ShellRealmItem *item = shell_realms_realm_by_name (realms, realm_name); -+ -+ if (realms->current_realm != item) { -+ shell_realms_set_current_item (realms, item); -+ shell_realms_update_running_list_for_item (realms, item); -+ shell_realm_item_activate_context (item); -+ g_signal_emit (realms, shell_realms_signals[REALM_CONTEXT_SWITCHED], 0); -+ } -+} -+ -+void -+shell_realms_on_realm_stopped (ShellRealms *realms, const gchar *realm_name) -+{ -+ ShellRealmItem *item = shell_realms_realm_by_name (realms, realm_name); -+ if (item) { -+ shell_realm_item_set_running_flag (item, FALSE); -+ shell_realms_remove_running_realm (realms, item); -+ } -+} -+ -+void -+shell_realms_on_realm_removed (ShellRealms *realms, const gchar *realm_name) -+{ -+ ShellRealmItem *item = shell_realms_realm_by_name (realms, realm_name); -+ if (item) { -+ shell_realms_remove_running_realm (realms, item); -+ g_hash_table_remove (realms->realms, realm_name); -+ } -+} -+ -+void -+shell_realms_on_realm_new (ShellRealms *realms, const gchar *realm_name, const gchar *description, guint8 status) -+{ -+ if (!g_hash_table_contains (realms->realms, realm_name)) { -+ (void) shell_realms_add_realm_item (realms, realm_name, description, NULL, status); -+ } else { -+ g_warning("ShellRealms: RealmNew signal received for realm '%s' but it already exists", realm_name); -+ } -+} -+ -+/** -+ * shell_realms_get_default: -+ * -+ * Return Value: (transfer none): The global #ShellRealms singleton -+ */ -+ShellRealms * -+shell_realms_get_default(void) -+{ -+ static ShellRealms *instance; -+ -+ if (instance == NULL) { -+ instance = g_object_new (SHELL_TYPE_REALMS, NULL); -+ } -+ return instance; -+} -+ -+static void -+shell_realms_init(ShellRealms *realms) -+{ -+ realms->realms = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -+ realms->running_realms = NULL; -+ realms->current_realm = NULL; -+} -+ -+static void -+shell_realms_finalize (GObject *obj) -+{ -+ ShellRealms *realms = SHELL_REALMS (obj); -+ realms->current_realm = NULL; -+ g_list_free_full (realms->running_realms, g_object_unref); -+ g_hash_table_destroy (realms->realms); -+ G_OBJECT_CLASS (shell_realms_parent_class)->finalize (obj); -+} -+ -+static void -+shell_realms_get_property (GObject *gobject, -+ guint prop_id, -+ GValue *value, -+ GParamSpec *pspec) -+{ -+ ShellRealms *realms = SHELL_REALMS (gobject); -+ -+ switch (prop_id) { -+ case PROP_CURRENT_REALM: -+ if (realms->current_realm) { -+ g_value_set_object (value, realms->current_realm); -+ } -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+shell_realms_class_init (ShellRealmsClass *klass) -+{ -+ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->get_property = shell_realms_get_property; -+ object_class->finalize = shell_realms_finalize; -+ -+ shell_realms_signals[REALM_CONTEXT_SWITCHED] = -+ g_signal_new ("realm-context-switched", -+ G_TYPE_FROM_CLASS(klass), -+ G_SIGNAL_RUN_LAST, -+ 0, -+ NULL, NULL, NULL, -+ G_TYPE_NONE, 0); -+ -+ g_object_class_install_property (object_class, -+ PROP_CURRENT_REALM, -+ g_param_spec_object ("current-realm", -+ "Current Realm", -+ "The currently active realm", -+ SHELL_TYPE_REALM_ITEM, -+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -+} -diff --git a/src/shell-realms.h b/src/shell-realms.h -new file mode 100644 -index 0000000..a1a3353 ---- /dev/null -+++ b/src/shell-realms.h -@@ -0,0 +1,23 @@ -+#ifndef __SHELL_REALMS_H__ -+#define __SHELL_REALMS_H__ -+ -+#include -+#include -+#include "shell-realm-item.h" -+ -+#define SHELL_TYPE_REALMS (shell_realms_get_type()) -+G_DECLARE_FINAL_TYPE(ShellRealms, shell_realms, SHELL, REALMS, GObject) -+ -+ShellRealms *shell_realms_get_default(void); -+ -+ -+ShellRealmItem *shell_realms_current_realm (ShellRealms *realms); -+ShellRealmItem *shell_realms_realm_by_name(ShellRealms *realms, const gchar *realm_name); -+ShellRealmItem *shell_realms_realm_by_context_id (ShellRealms *realms, guint context_id); -+ShellRealmItem *shell_realms_realm_by_window (ShellRealms *realms, MetaWindow *window); -+gboolean shell_realms_is_citadel_window (ShellRealms *realms, MetaWindow *window); -+ -+GList *shell_realms_get_running_realms (ShellRealms *realms); -+GList *shell_realms_get_all_realms (ShellRealms *realms); -+ -+#endif //__SHELL_REALMS_H__ -diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c -index 5c9d2ec..0ca7004 100644 ---- a/src/shell-window-tracker.c -+++ b/src/shell-window-tracker.c -@@ -15,6 +15,7 @@ - #include "shell-window-tracker-private.h" - #include "shell-app-private.h" - #include "shell-global.h" -+#include "shell-realms.h" - #include "st.h" - - /* This file includes modified code from -@@ -129,6 +130,22 @@ check_app_id_prefix (ShellApp *app, - return g_str_has_prefix (shell_app_get_id (app), prefix); - } - -+static const char * -+get_window_realm_name (MetaWindow *window) { -+ -+ ShellRealms *realms = shell_realms_get_default(); -+ if (shell_realms_is_citadel_window (realms, window)) { -+ return NULL; -+ } -+ ShellRealmItem *item = shell_realms_realm_by_window(realms, window); -+ -+ if (item) { -+ return shell_realm_item_get_realm_name (item); -+ } else { -+ return NULL; -+ } -+} -+ - /* - * get_app_from_window_wmclass: - * -@@ -146,6 +163,7 @@ get_app_from_window_wmclass (MetaWindow *window) - const char *wm_class; - const char *wm_instance; - const char *sandbox_id; -+ const char *realm_name; - g_autofree char *app_prefix = NULL; - - appsys = shell_app_system_get_default (); -@@ -154,6 +172,9 @@ get_app_from_window_wmclass (MetaWindow *window) - if (sandbox_id) - app_prefix = g_strdup_printf ("%s.", sandbox_id); - -+ realm_name = get_window_realm_name (window); -+ -+ g_warning ("ShellWindowTracker: get_app_from_window_wmclass() realm_name=%s", realm_name ? realm_name : "None"); - /* Notes on the heuristics used here: - much of the complexity here comes from the desire to support - Chrome apps. -@@ -191,23 +212,23 @@ get_app_from_window_wmclass (MetaWindow *window) - - /* first try a match from WM_CLASS (instance part) to StartupWMClass */ - wm_instance = meta_window_get_wm_class_instance (window); -- app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance); -+ app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance, realm_name); - if (app != NULL && check_app_id_prefix (app, app_prefix)) - return g_object_ref (app); - - /* then try a match from WM_CLASS to StartupWMClass */ - wm_class = meta_window_get_wm_class (window); -- app = shell_app_system_lookup_startup_wmclass (appsys, wm_class); -+ app = shell_app_system_lookup_startup_wmclass (appsys, wm_class, realm_name); - if (app != NULL && check_app_id_prefix (app, app_prefix)) - return g_object_ref (app); - - /* then try a match from WM_CLASS (instance part) to .desktop */ -- app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance); -+ app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance, realm_name); - if (app != NULL && check_app_id_prefix (app, app_prefix)) - return g_object_ref (app); - - /* finally, try a match from WM_CLASS to .desktop */ -- app = shell_app_system_lookup_desktop_wmclass (appsys, wm_class); -+ app = shell_app_system_lookup_desktop_wmclass (appsys, wm_class, realm_name); - if (app != NULL && check_app_id_prefix (app, app_prefix)) - return g_object_ref (app); - -@@ -236,7 +257,19 @@ get_app_from_id (MetaWindow *window, - - appsys = shell_app_system_get_default (); - -- desktop_file = g_strconcat (id, ".desktop", NULL); -+ const char *realm_name = NULL; -+ ShellRealms *realms = shell_realms_get_default(); -+ ShellRealmItem *item = shell_realms_realm_by_window (realms, window); -+ -+ if (item) { -+ realm_name = shell_realm_item_get_realm_name (item); -+ } -+ -+ if (realm_name) { -+ desktop_file = g_strconcat ("realm-", realm_name, ".", id, ".desktop", NULL); -+ } else { -+ desktop_file = g_strconcat (id, ".desktop", NULL); -+ } - app = shell_app_system_lookup_app (appsys, desktop_file); - if (app) - return g_object_ref (app); diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Disabled-calendar-events-from-user-session.patch b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Disabled-calendar-events-from-user-session.patch index fc422c4..03d2660 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Disabled-calendar-events-from-user-session.patch +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Disabled-calendar-events-from-user-session.patch @@ -1,4 +1,4 @@ -From 724ea2e98f186456ea984fe9250f8650ed025a9c Mon Sep 17 00:00:00 2001 +From ac70fbe1edeef149c55d5779008429aec9945b76 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Tue, 5 Oct 2021 11:17:47 +0000 Subject: [PATCH] Disabled calendar events from user session diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-calendar-server-fix-build.patch b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-calendar-server-fix-build.patch index 5c36d18..f9b246f 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-calendar-server-fix-build.patch +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-calendar-server-fix-build.patch @@ -1,4 +1,4 @@ -From 12fd2083761c1d432a2b505a210d1289b2b1624f Mon Sep 17 00:00:00 2001 +From 9d4edfc419cdb2868a6a69bf1ed4565478789a35 Mon Sep 17 00:00:00 2001 From: David McKinney Date: Wed, 8 Jul 2020 14:07:06 +0000 Subject: [PATCH] Remove calendar server, fix build @@ -12,10 +12,10 @@ Subject: [PATCH] Remove calendar server, fix build 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/js/dbusServices/meson.build b/js/dbusServices/meson.build -index 2f047bb..6484cdc 100644 +index 68e8bd1..16efaeb 100644 --- a/js/dbusServices/meson.build +++ b/js/dbusServices/meson.build -@@ -26,7 +26,7 @@ foreach service, dir : dbus_services +@@ -27,7 +27,7 @@ foreach service, dir : dbus_services serviceconf = configuration_data() serviceconf.set('service', service) @@ -25,12 +25,12 @@ index 2f047bb..6484cdc 100644 configure_file( diff --git a/meson.build b/meson.build -index d86879a..c23ab0b 100644 +index 58f66cd..1dc4729 100644 --- a/meson.build +++ b/meson.build -@@ -18,8 +18,8 @@ cogl_pc = 'mutter-cogl-' + mutter_api_version - cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version +@@ -19,8 +19,8 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version libmutter_pc = 'libmutter-' + mutter_api_version + libmutter_test_pc = 'libmutter-test-' + mutter_api_version -ecal_req = '>= 3.33.1' -eds_req = '>= 3.33.1' @@ -39,7 +39,7 @@ index d86879a..c23ab0b 100644 gcr_req = '>= 3.7.5' gio_req = '>= 2.56.0' gi_req = '>= 1.49.1' -@@ -71,8 +71,8 @@ else +@@ -72,8 +72,8 @@ else endif atk_bridge_dep = dependency('atk-bridge-2.0') @@ -50,7 +50,7 @@ index d86879a..c23ab0b 100644 gcr_dep = dependency('gcr-base-3', version: gcr_req) gdk_x11_dep = dependency('gdk-x11-3.0') gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0') -@@ -134,7 +134,7 @@ endif +@@ -139,7 +139,7 @@ endif mutter_typelibdir = mutter_dep.get_pkgconfig_variable('typelibdir') python = find_program('python3') @@ -60,7 +60,7 @@ index d86879a..c23ab0b 100644 cc = meson.get_compiler('c') diff --git a/src/meson.build b/src/meson.build -index a7c56bb..3118e67 100644 +index 53b8b52..5fa9fdf 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,7 +1,7 @@ @@ -73,10 +73,10 @@ index a7c56bb..3118e67 100644 subdir('st') subdir('tray') diff --git a/subprojects/extensions-app/js/meson.build b/subprojects/extensions-app/js/meson.build -index a378a7e..f2a37ba 100644 +index f311db6..8f7bba4 100644 --- a/subprojects/extensions-app/js/meson.build +++ b/subprojects/extensions-app/js/meson.build -@@ -5,7 +5,7 @@ launcherconf.set('PACKAGE_VERSION', meson.project_version()) +@@ -9,7 +9,7 @@ endif launcherconf.set('prefix', prefix) launcherconf.set('libdir', libdir) launcherconf.set('pkgdatadir', pkgdatadir) @@ -86,10 +86,10 @@ index a378a7e..f2a37ba 100644 configure_file( input: prgname + '.in', diff --git a/subprojects/extensions-app/meson.build b/subprojects/extensions-app/meson.build -index 4acd94c..c6bdd5f 100644 +index 6143eef..a619f63 100644 --- a/subprojects/extensions-app/meson.build +++ b/subprojects/extensions-app/meson.build -@@ -35,7 +35,7 @@ localedir = join_paths(datadir, 'locale') +@@ -44,7 +44,7 @@ localedir = join_paths(datadir, 'locale') metainfodir = join_paths(datadir, 'metainfo') servicedir = join_paths(datadir, 'dbus-1', 'services') diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-log-out-label-from-power-off-in-status-UI.patch b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-log-out-label-from-power-off-in-status-UI.patch index 0853221..09aac8b 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-log-out-label-from-power-off-in-status-UI.patch +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-Remove-log-out-label-from-power-off-in-status-UI.patch @@ -1,4 +1,4 @@ -From 38a41a143b97b7dcf0512d6509e61d11582e681d Mon Sep 17 00:00:00 2001 +From 5e5da06bd97175bed4a2719e5e67a5b2b51e4efc Mon Sep 17 00:00:00 2001 From: David McKinney Date: Fri, 12 Feb 2021 13:58:36 +0000 Subject: [PATCH] Remove log out label from power off in status UI diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-do-not-use-python-path-from-build-environment.patch b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-do-not-use-python-path-from-build-environment.patch index 35fa8cd..a720bbd 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-do-not-use-python-path-from-build-environment.patch +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0001-do-not-use-python-path-from-build-environment.patch @@ -1,4 +1,4 @@ -From 66351cb08cf4e71d5b30dfcd4a47fd69eb49c116 Mon Sep 17 00:00:00 2001 +From 4398d05df199e3c63d1af8de0774582d430368d5 Mon Sep 17 00:00:00 2001 From: brl Date: Sun, 24 Dec 2017 17:44:02 -0500 Subject: [PATCH] do not use python path from build environment diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0002-Citadel-Gnome-Shell-changes.patch b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0002-Citadel-Gnome-Shell-changes.patch index 79e96d5..4325534 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0002-Citadel-Gnome-Shell-changes.patch +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell/0002-Citadel-Gnome-Shell-changes.patch @@ -1,4 +1,4 @@ -From 681362d7640885a9cc9a97b50292e7810e4385a7 Mon Sep 17 00:00:00 2001 +From fa02a178a28487474df5c550116f6379b872c63e Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Tue, 5 Oct 2021 11:34:09 +0000 Subject: [PATCH] Citadel Gnome Shell changes diff --git a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.0.bb b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.1.bb similarity index 95% rename from meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.0.bb rename to meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.1.bb index 25ebcc5..badef96 100644 --- a/meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.0.bb +++ b/meta-gnome/recipes-gnome/gnome-shell/gnome-shell_41.1.bb @@ -20,7 +20,7 @@ SRC_URI = "${GNOME_MIRROR}/${GNOMEBN}/${@gnome_verdir("${PV}")}/${GNOMEBN}-${PV} file://0002-Citadel-Gnome-Shell-changes.patch \ " -SRC_URI[archive.sha256sum] = "52f971e85140e5de74b9369ef6656e49ce95af1f232fc1e0df1f046129ab4f65" +SRC_URI[archive.sha256sum] = "5f742456dfe00605c0f090a3728ca62797bc49e50af852bbd7285da1a0517ff6" DEPENDS = " \ diff --git a/meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.41.0.bb b/meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.42.1.bb similarity index 89% rename from meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.41.0.bb rename to meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.42.1.bb index 63c65f3..77ceb46 100644 --- a/meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.41.0.bb +++ b/meta-gnome/recipes-gnome/gnome-terminal/gnome-terminal_3.42.1.bb @@ -19,7 +19,7 @@ DEPENDS = " \ GNOMEBASEBUILDCLASS = "meson" inherit gnomebase gsettings gnome-help gettext itstool upstream-version-is-even -SRC_URI[archive.sha256sum] = "b016d89efc3fbc2a85a8ea664077a06158a462c03c222d82478ce01531d5390c" +SRC_URI[archive.sha256sum] = "c319b1405501b8c7693e616f48eced41695d2e786148ca5f9e27bc7d98f4aeb1" EXTRA_OEMESON = "-Ddocs=false -Dnautilus_extension=false -Dsearch_provider=false" diff --git a/meta-gnome/recipes-gnome/gtk4/gtk4_4.4.0.bb b/meta-gnome/recipes-gnome/gtk4/gtk4_4.4.1.bb similarity index 85% rename from meta-gnome/recipes-gnome/gtk4/gtk4_4.4.0.bb rename to meta-gnome/recipes-gnome/gtk4/gtk4_4.4.1.bb index 3343c7a..b076b24 100644 --- a/meta-gnome/recipes-gnome/gtk4/gtk4_4.4.0.bb +++ b/meta-gnome/recipes-gnome/gtk4/gtk4_4.4.1.bb @@ -5,7 +5,7 @@ MAJ_VER = "${@oe.utils.trim_version("${PV}", 2)}" SRC_URI = "http://ftp.gnome.org/pub/gnome/sources/gtk/${MAJ_VER}/gtk-${PV}.tar.xz \ " -SRC_URI[sha256sum] = "e0a1508f441686c3a20dfec48af533b19a4b2e017c18eaee31dccdb7d292505b" +SRC_URI[sha256sum] = "0faada983dc6b0bc409cb34c1713c1f3267e67c093f86b1e3b17db6100a3ddf4" S = "${WORKDIR}/gtk-${PV}" diff --git a/meta-gnome/recipes-gnome/mutter/mutter/0001-Citadel-changes-to-Mutter.patch.old b/meta-gnome/recipes-gnome/mutter/mutter/0001-Citadel-changes-to-Mutter.patch.old deleted file mode 100644 index e6d09d5..0000000 --- a/meta-gnome/recipes-gnome/mutter/mutter/0001-Citadel-changes-to-Mutter.patch.old +++ /dev/null @@ -1,1300 +0,0 @@ -From c203de9ad5f88eb48f749cda1b5a6c8896a2af15 Mon Sep 17 00:00:00 2001 -From: Bruce Leidl -Date: Mon, 4 Oct 2021 07:19:16 -0400 -Subject: [PATCH] Citadel changes to Mutter - ---- - src/compositor/compositor.c | 9 +- - src/core/meta-workspace-manager-private.h | 69 ++++ - src/core/meta-workspace-manager.c | 465 +++++++++++++++++++++- - src/core/util-private.h | 2 + - src/core/util.c | 48 +++ - src/core/window-private.h | 3 + - src/core/window.c | 41 +- - src/core/workspace-private.h | 3 + - src/core/workspace.c | 170 ++++---- - src/meta/common.h | 4 +- - src/meta/meta-workspace-manager.h | 44 ++ - src/meta/types.h | 1 + - src/meta/window.h | 6 + - src/meta/workspace.h | 6 + - 14 files changed, 786 insertions(+), 85 deletions(-) - -diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c -index 1770550..d1eb7f8 100644 ---- a/src/compositor/compositor.c -+++ b/src/compositor/compositor.c -@@ -744,8 +744,13 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, - meta_compositor_get_instance_private (compositor); - gint to_indx, from_indx; - -- to_indx = meta_workspace_index (to); -- from_indx = meta_workspace_index (from); -+ if (direction == META_MOTION_CONTEXT_SWITCH) { -+ to_indx = meta_workspace_get_id (to); -+ from_indx = meta_workspace_get_id (from); -+ } else { -+ to_indx = meta_workspace_index (to); -+ from_indx = meta_workspace_index (from); -+ } - - priv->switch_workspace_in_progress++; - -diff --git a/src/core/meta-workspace-manager-private.h b/src/core/meta-workspace-manager-private.h -index 261c4d4..4b5d4f4 100644 ---- a/src/core/meta-workspace-manager-private.h -+++ b/src/core/meta-workspace-manager-private.h -@@ -30,6 +30,36 @@ - #include "meta/types.h" - #include "meta/meta-workspace-manager.h" - -+// -+// Stores list of MetaWorkspaces and pointer to an active MetaWorkspace. When this context becomes -+// the current context, the workspace fields are swapped into the corresponding fields in -+// MetaWorkspaceManager. -+// -+// -+struct _MetaWorkspaceContext -+{ -+ -+ GObject parent; -+ // Link back to MetaWorkspaceManager -+ MetaWorkspaceManager *manager; -+ -+ // MetaWorkspace list belonging to this context. Copied to field with same name in -+ // MetaWorkspaceManager when this context is active. Any code which changes this list -+ // must make sure this context is not currently active in which case the list in -+ // MetaWorkspaceManager must be changed instead. -+ GList *workspaces; -+ -+ // Active MetaWorkspace for this context. Also copied to workspace manager upon activation. -+ // The rule above about not writing if context is currently active also applies to this field. -+ MetaWorkspace *active_workspace; -+ -+ // PID namespace -+ gchar *namespace; -+ -+ // A unique ID value for this context. -+ guint id; -+}; -+ - struct _MetaWorkspaceManager - { - GObject parent; -@@ -37,8 +67,21 @@ struct _MetaWorkspaceManager - MetaDisplay *display; - MetaWorkspace *active_workspace; - -+ GList *all_workspaces; - GList *workspaces; - -+ // List of WorkspaceContext -+ GList *context_list; -+ -+ gchar *mutter_namespace; -+ -+ // Current active WorkspaceContext. MetaWorkspaceManager state (workspaces, active_workspace) -+ // will be saved here when a new WorkspaceContext is made active. -+ MetaWorkspaceContext *active_context; -+ -+ // The next id value to allocate when creating a new WorkspaceContext -+ guint next_context_id; -+ - int rows_of_workspaces; - int columns_of_workspaces; - MetaDisplayCorner starting_corner; -@@ -93,4 +136,30 @@ void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspa - guint32 timestamp, - int new_num); - -+MetaWorkspaceContext *meta_workspace_context_new (MetaWorkspaceManager *manager, const char *namespace); -+ -+void meta_workspace_context_make_active (MetaWorkspaceContext *context); -+ -+MetaWorkspaceContext *meta_workspace_manager_lookup_context (MetaWorkspaceManager *workspace_manager, -+ guint context_id); -+ -+ -+void meta_workspace_manager_append_context_workspace (MetaWorkspaceManager *manager, -+ MetaWorkspace *workspace); -+ -+void meta_workspace_manager_remove_context_workspace (MetaWorkspaceManager *manager, -+ MetaWorkspace *workspace); -+ -+int meta_workspace_manager_context_workspace_index (MetaWorkspaceManager *workspace_manager, -+ MetaWorkspace *workspace); -+ -+int meta_workspace_manager_get_workspace_id (MetaWorkspaceManager *workspace_manager, -+ MetaWorkspace *workspace); -+MetaWorkspace * -+meta_workspace_manager_lookup_workspace_by_id (MetaWorkspaceManager *workspace_manager, int workspace_id); -+ -+gboolean -+meta_workspace_manager_is_window_on_foreign_context (MetaWorkspaceManager *workspace_manager, MetaWindow *window); -+ -+ - #endif /* META_WORKSPACE_MANAGER_PRIVATE_H */ -diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c -index 61fbc00..d7696a7 100644 ---- a/src/core/meta-workspace-manager.c -+++ b/src/core/meta-workspace-manager.c -@@ -44,6 +44,8 @@ enum - WORKSPACES_REORDERED, - ACTIVE_WORKSPACE_CHANGED, - SHOWING_DESKTOP_CHANGED, -+ CONTEXT_SWITCHED, -+ CONTEXT_WINDOW_MOVED, - LAST_SIGNAL - }; - -@@ -178,6 +180,21 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - -+ workspace_manager_signals[CONTEXT_SWITCHED] = -+ g_signal_new ("context-switched", -+ G_TYPE_FROM_CLASS (klass), -+ G_SIGNAL_RUN_LAST, -+ 0, NULL, NULL, NULL, -+ G_TYPE_NONE, 0); -+ -+ workspace_manager_signals[CONTEXT_WINDOW_MOVED] = -+ g_signal_new("context-window-moved", -+ G_TYPE_FROM_CLASS(klass), -+ G_SIGNAL_RUN_LAST, -+ 0, NULL, NULL, NULL, -+ G_TYPE_NONE, 1, -+ META_TYPE_WINDOW); -+ - g_object_class_install_property (object_class, - PROP_LAYOUT_COLUMNS, - g_param_spec_int ("layout-columns", -@@ -201,6 +218,7 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) - "Number of workspaces", - 1, G_MAXINT, 1, - G_PARAM_READABLE)); -+ - } - - static void -@@ -213,7 +231,7 @@ meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manage - { - GList *l; - -- for (l = workspace_manager->workspaces; l; l = l->next) -+ for (l = workspace_manager->all_workspaces; l; l = l->next) - { - MetaWorkspace *workspace = l->data; - -@@ -230,12 +248,24 @@ meta_workspace_manager_new (MetaDisplay *display) - - workspace_manager->display = display; - workspace_manager->active_workspace = NULL; -+ workspace_manager->all_workspaces = NULL; - workspace_manager->workspaces = NULL; - workspace_manager->rows_of_workspaces = 1; - workspace_manager->columns_of_workspaces = -1; - workspace_manager->vertical_workspaces = FALSE; - workspace_manager->starting_corner = META_DISPLAY_TOPLEFT; - -+ workspace_manager->context_list = NULL; -+ workspace_manager->active_context = NULL; -+ workspace_manager->next_context_id = 1; -+ workspace_manager->mutter_namespace = meta_read_pid_namespace (getpid()); -+ -+ MetaWorkspaceContext *context = meta_workspace_context_new (workspace_manager, NULL); -+ -+ workspace_manager->workspaces = g_steal_pointer (&context->workspaces); -+ workspace_manager->active_workspace = g_steal_pointer (&context->active_workspace); -+ workspace_manager->active_context = context; -+ - /* This is the default layout extracted from default - * variable values in update_num_workspaces () - * This can be overridden using _NET_DESKTOP_LAYOUT in -@@ -246,11 +276,6 @@ meta_workspace_manager_new (MetaDisplay *display) - 1, - -1); - -- /* There must be at least one workspace at all times, -- * so create that required workspace. -- */ -- meta_workspace_new (workspace_manager); -- - meta_workspace_manager_init_workspaces (workspace_manager); - - meta_prefs_add_listener (prefs_changed_callback, workspace_manager); -@@ -301,6 +326,9 @@ MetaWorkspace * - meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager, - int idx) - { -+ if ((idx >> 16) & 0xFFFF) { -+ return meta_workspace_manager_lookup_workspace_by_id (workspace_manager, idx); -+ } - return g_list_nth_data (workspace_manager->workspaces, idx); - } - -@@ -1001,7 +1029,7 @@ meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager) - GList * - meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager) - { -- return workspace_manager->workspaces; -+ return workspace_manager->all_workspaces; - } - - int -@@ -1015,6 +1043,17 @@ meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspa - return meta_workspace_index (active); - } - -+int -+meta_workspace_manager_get_active_workspace_id (MetaWorkspaceManager *workspace_manager) -+{ -+ MetaWorkspace *active = workspace_manager->active_workspace; -+ -+ if (!active) -+ return -1; -+ -+ return meta_workspace_get_id (active); -+} -+ - /** - * meta_workspace_manager_get_active_workspace: - * @workspace_manager: A #MetaWorkspaceManager -@@ -1033,9 +1072,15 @@ meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manag - int to, - MetaMotionDirection direction) - { -- g_signal_emit (workspace_manager, -- workspace_manager_signals[WORKSPACE_SWITCHED], 0, -- from, to, direction); -+ if (direction == META_MOTION_CONTEXT_SWITCH) { -+ g_signal_emit (workspace_manager, -+ workspace_manager_signals[CONTEXT_SWITCHED], -+ 0, NULL); -+ } else { -+ g_signal_emit (workspace_manager, -+ workspace_manager_signals[WORKSPACE_SWITCHED], 0, -+ from, to, direction); -+ } - } - - static void -@@ -1058,3 +1103,403 @@ prefs_changed_callback (MetaPreference pref, - timestamp, new_num); - } - } -+ -+ -+/** -+ * meta_workspace_manager_set_builtin_struts_all: -+ * @workspace_manager: a #MetaWorkspaceManager -+ * @struts: (element-type Meta.Strut) (transfer none): list of #MetaStrut -+ * -+ * Sets a list of struts on every workspace that will be used in addition to the struts -+ * of the windows in the workspace when computing the work area of -+ * the workspace. -+ */ -+void meta_workspace_manager_set_builtin_struts_all(MetaWorkspaceManager *workspace_manager, -+ GSList *struts) -+{ -+ GList *context_iter = workspace_manager->context_list; -+ while (context_iter) { -+ MetaWorkspaceContext *context = context_iter->data; -+ for (GList *ws_iter = context->workspaces; ws_iter; ws_iter = ws_iter->next) { -+ MetaWorkspace *workspace = ws_iter->data; -+ meta_workspace_set_builtin_struts(workspace, struts); -+ } -+ context_iter = context_iter->next; -+ } -+} -+ -+ -+ -+/** -+ * meta_workspace_manager_lookup_context: -+ * @workspace_manager: a #MetaWorkspaceManager -+ * @context_id: id value of context to look up -+ * -+ * Look up WorkspaceContext by id and return it. If no context is found with -+ * the specified id the return value is %NULL. -+ * -+ * Return value: (transfer none) (nullable): the workspace context with the specified id -+ * or %NULL if no context with matching id exists. -+ */ -+MetaWorkspaceContext * -+meta_workspace_manager_lookup_context (MetaWorkspaceManager *workspace_manager, guint context_id) -+{ -+ for (GList *iter = workspace_manager->context_list; iter; iter = iter->next) { -+ MetaWorkspaceContext *context = iter->data; -+ if (context_id == context->id) { -+ return context; -+ } -+ } -+ return NULL; -+} -+ -+const char * -+meta_workspace_manager_mutter_namespace (MetaWorkspaceManager *workspace_manager) -+{ -+ if (!workspace_manager->mutter_namespace) { -+ workspace_manager->mutter_namespace = meta_read_pid_namespace (getpid()); -+ } -+ return workspace_manager->mutter_namespace; -+} -+ -+/* -+ * Return pointer to the live workspace list depending on whether or not the context is currently active. -+ * While a workspace context is active, the list must be accessed through the pointer workspace_manager->workspaces -+ */ -+GList ** -+meta_workspace_manager_workspace_list_for_context (MetaWorkspaceManager *workspace_manager, guint context_id) -+{ -+ if (workspace_manager->active_context && workspace_manager->active_context->id == context_id) { -+ return &workspace_manager->workspaces; -+ } -+ -+ MetaWorkspaceContext *context = meta_workspace_manager_lookup_context (workspace_manager, context_id); -+ -+ if (context) { -+ return &context->workspaces; -+ } else { -+ g_warning ("MetaWorkspaceManager: Failed to find context workspace list (context_id = %d)", context_id); -+ return NULL; -+ } -+} -+ -+void -+meta_workspace_manager_append_context_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) -+{ -+ GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); -+ if (p_workspaces) { -+ *p_workspaces = g_list_append (*p_workspaces, workspace); -+ } -+} -+ -+void -+meta_workspace_manager_remove_context_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) -+{ -+ GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); -+ if (p_workspaces) { -+ *p_workspaces = g_list_remove (*p_workspaces, workspace); -+ } -+ workspace_manager->all_workspaces = g_list_remove (workspace_manager->all_workspaces, workspace); -+} -+ -+static int -+workspace_index_in_context (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) -+{ -+ GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); -+ if (p_workspaces) { -+ return g_list_index (*p_workspaces, workspace); -+ } else { -+ g_warning ("MetaWorkspaceManager: Could not find context for id=%d in workspace_index_in_context()", workspace->context_id); -+ return -1; -+ } -+} -+ -+int -+meta_workspace_manager_get_workspace_id (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) -+{ -+ int idx = workspace_index_in_context (workspace_manager, workspace); -+ -+ if (idx >= 0) { -+ idx |= (int)(workspace->context_id << 16); -+ } else { -+ g_warning ("MetaWorkspaceManager: Workspace with context_id = %d not found in meta_workspace_manager_get_workspace_id()", workspace->context_id); -+ } -+ return idx; -+} -+ -+int -+meta_workspace_manager_context_workspace_index (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) -+{ -+ -+ if (!(workspace_manager->active_context && workspace_manager->active_context->id == workspace->context_id)) { -+ guint active = (workspace_manager->active_context) ? (workspace_manager->active_context->id) : 0; -+ g_warning ("MetaWorkspaceManager: context_workspace_index() called on workspace not in active context. (ws->context_id = %d, active->context_id=%d)", -+ workspace->context_id, active); -+ } -+ -+ int idx = workspace_index_in_context (workspace_manager, workspace); -+ if (idx < 0) { -+ g_warning ("MetaWorkspaceManager: Failed to find workspace with context_id=%d in context_workspace_index()", -+ workspace->context_id); -+ } -+ return idx; -+} -+ -+MetaWorkspace * -+meta_workspace_manager_lookup_workspace_by_id (MetaWorkspaceManager *workspace_manager, int workspace_id) -+{ -+ uint context_id = (workspace_id >> 16) & 0xFFFF; -+ if (context_id) { -+ GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, context_id); -+ int idx = workspace_id & 0xFFFF; -+ if (p_workspaces) { -+ return g_list_nth_data (*p_workspaces, idx); -+ } -+ } -+ return NULL; -+} -+ -+/** -+ * meta_workspace_manager_context_for_namespace: -+ * @workspace_manager: a #MetaWorkspaceManager -+ * @namespace: namespace of context -+ * -+ * Find an existing WorkspaceContext for the specified namespace. If no WorkspaceContext -+ * currently exists for namespace create a new one. -+ * -+ * Return value: (transfer none): the workspace context for the specified namespace -+ */ -+MetaWorkspaceContext * -+meta_workspace_manager_context_for_namespace (MetaWorkspaceManager *workspace_manager, const gchar *namespace) -+{ -+ MetaWorkspaceContext *anonymous_context = NULL; -+ -+ for (GList *iter = workspace_manager->context_list; iter; iter = iter->next) { -+ MetaWorkspaceContext *context = iter->data; -+ if (context->namespace == NULL && anonymous_context == NULL) { -+ anonymous_context = context; -+ } else if (!g_strcmp0 (context->namespace, namespace )) { -+ return context; -+ } -+ } -+ -+ if (anonymous_context) { -+ g_message ("MetaWorkspaceManager: Assigning anonymous workspace context to namespace %s", namespace); -+ anonymous_context->namespace = g_strdup (namespace); -+ return anonymous_context; -+ } -+ -+ return meta_workspace_context_new (workspace_manager, namespace); -+} -+ -+guint -+meta_workspace_manager_active_context_id (MetaWorkspaceManager *workspace_manager) -+{ -+ if (workspace_manager->active_context) { -+ return workspace_manager->active_context->id; -+ } else { -+ return 0; -+ } -+} -+ -+G_DEFINE_TYPE (MetaWorkspaceContext, meta_workspace_context, G_TYPE_OBJECT) -+ -+guint -+meta_workspace_context_id (MetaWorkspaceContext *context) -+{ -+ return context->id; -+} -+ -+void -+meta_workspace_context_remove (MetaWorkspaceContext *context) -+{ -+ if (meta_workspace_context_is_current (context)) { -+ g_warning ("MetaWorkspaceManager: attempt to remove active context ignored"); -+ return; -+ } -+ -+ context->manager->context_list = g_list_remove (context->manager->context_list, context); -+ g_object_unref (context); -+} -+ -+static void -+meta_workspace_context_destroy (MetaWorkspaceContext *context) -+{ -+ MetaWorkspaceManager *manager= context->manager; -+ -+ g_return_if_fail (manager->active_context != context); -+ -+ manager->context_list = g_list_remove(manager->context_list, context); -+ -+ for (GList *iter = context->workspaces; iter; iter = iter->next) { -+ MetaWorkspace *workspace = iter->data; -+ meta_workspace_relocate_windows (workspace, manager->active_workspace); -+ } -+ context->active_workspace = NULL; -+ g_free (context->namespace); -+ g_list_free_full (context->workspaces, (GDestroyNotify) meta_workspace_remove); -+ g_free(context); -+} -+ -+static void -+meta_workspace_context_finalize (GObject *object) -+{ -+ MetaWorkspaceContext *context = META_WORKSPACE_CONTEXT (object); -+ meta_workspace_context_destroy(context); -+ G_OBJECT_CLASS(meta_workspace_context_parent_class)->finalize(object); -+} -+ -+static void -+meta_workspace_context_class_init (MetaWorkspaceContextClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ object_class->finalize = meta_workspace_context_finalize; -+} -+ -+static void -+meta_workspace_context_init (MetaWorkspaceContext *context) -+{ -+} -+ -+MetaWorkspaceContext * -+meta_workspace_context_new (MetaWorkspaceManager *manager, const char *namespace) -+{ -+ guint context_id = manager->next_context_id++; -+ -+ MetaWorkspaceContext *context = g_object_new (META_TYPE_WORKSPACE_CONTEXT, NULL); -+ -+ context->manager = manager; -+ context->workspaces = NULL; -+ context->active_workspace = NULL; -+ context->namespace = NULL; -+ context->id = context_id; -+ -+ if (namespace) { -+ g_message ("MetaWorkspaceManager: Creating new workspace context for namespace %s (id: %d)", namespace, context_id); -+ context->namespace = g_strdup (namespace); -+ } else { -+ g_message ("MetaWorkspaceManager: Creating new anonymous workspace context (id: %d)", context_id); -+ } -+ -+ manager->context_list = g_list_append (manager->context_list, context); -+ -+ context->active_workspace = meta_workspace_new_with_context_id (manager, context_id); -+ g_object_notify (G_OBJECT (manager), "n-workspaces"); -+ -+ return context; -+} -+ -+ -+/* -+ * Synchronize the state stored in the MetaWorkspaceManager to 'context' -+ * by copying the relevant fields to the WorkspaceContext structure. -+ * -+ * The state which is saved is the list of workspaces and the active -+ * workspace pointer: -+ * -+ * workspace_manager->active_workspace -+ * workspace_manager->workspaces -+ */ -+static void -+workspace_context_sync_manager_state (MetaWorkspaceContext *context) -+{ -+ context->active_workspace = context->manager->active_workspace; -+ context->workspaces = context->manager->workspaces; -+} -+ -+gboolean -+meta_workspace_context_is_current (MetaWorkspaceContext *context) -+{ -+ return context && context->manager->active_context == context; -+} -+ -+/** -+ * meta_workspace_context_get_active_workspace: -+ * @context: A #MetaWorkspaceContext -+ * -+ * Returns: (transfer none): The current workspace for this context -+ */ -+MetaWorkspace * -+meta_workspace_context_get_active_workspace (MetaWorkspaceContext *context) -+{ -+ if (meta_workspace_context_is_current (context)) { -+ return context->manager->active_workspace; -+ } else { -+ return context->active_workspace; -+ } -+} -+ -+void -+meta_workspace_context_move_window_to_context (MetaWorkspaceContext *context, MetaWindow *window) -+{ -+ if (!context->active_workspace) { -+ g_warning ("MetaWorkspaceContext: Cannot move window to context (%d) because no workspace is active", context->id); -+ return; -+ } -+ -+ if (window->workspace->context_id != context->id) { -+ meta_window_change_workspace (window, context->active_workspace); -+ g_signal_emit (context->manager, workspace_manager_signals[CONTEXT_WINDOW_MOVED], 0, window); -+ } -+} -+ -+ -+void -+meta_workspace_context_make_active (MetaWorkspaceContext *context) { -+ -+ g_message ("MetaWorkspaceManager: Activate workspace context (id: %d, ns: %s)", context->id, context->namespace); -+ -+ MetaWorkspaceManager *manager = context->manager; -+ -+ workspace_context_sync_manager_state (manager->active_context); -+ -+ manager->active_context = context; -+ -+ /* -+ * Do not update manager->active_workspace because meta_workspace_activate() expects -+ * old value -+ */ -+ -+ /* steal pointer to avoid writing code elsewhere that changes this list while context is active */ -+ manager->workspaces = g_steal_pointer (&context->workspaces); -+} -+ -+/* -+ * Activate a WorkspaceContext instance by copying the context state fields -+ * into the MetaWorkspaceManager structure. -+ * -+ * If 'context' is already the current WorkspaceContext do nothing. -+ * -+ * If some other context is active, save the context fields by calling -+ * -+ * workspace_context_sync_manager_state() -+ */ -+void -+meta_workspace_context_activate (MetaWorkspaceContext *context) -+{ -+ if (meta_workspace_context_is_current (context)) { -+ return; -+ } -+ -+ meta_workspace_context_make_active (context); -+ -+ guint32 timestamp; -+ -+ timestamp = meta_display_get_current_time_roundtrip (context->manager->display); -+ -+ /* Will set manager->active_workspace from context->active_workspace */ -+ meta_workspace_activate ( g_steal_pointer (&context->active_workspace), timestamp); -+} -+ -+gboolean -+meta_workspace_manager_is_window_on_foreign_context (MetaWorkspaceManager *workspace_manager, MetaWindow *window) -+{ -+ const char *ns = meta_window_namespace (window); -+ -+ if (!ns || !g_strcmp0 (ns, workspace_manager->mutter_namespace) || !window->workspace) { -+ return FALSE; -+ } -+ -+ MetaWorkspaceContext *context = meta_workspace_manager_context_for_namespace (workspace_manager, ns); -+ return context->id != window->workspace->context_id; -+} -diff --git a/src/core/util-private.h b/src/core/util-private.h -index 0d68a72..834b3e2 100644 ---- a/src/core/util-private.h -+++ b/src/core/util-private.h -@@ -43,4 +43,6 @@ void meta_set_is_wayland_compositor (gboolean setting); - char * meta_generate_random_id (GRand *rand, - int length); - -+char * meta_read_pid_namespace (pid_t pid); -+ - #endif -diff --git a/src/core/util.c b/src/core/util.c -index bbb5a24..57645e0 100644 ---- a/src/core/util.c -+++ b/src/core/util.c -@@ -741,3 +741,51 @@ meta_get_debug_paint_flags (void) - { - return debug_paint_flags; - } -+ -+static GFileInfo * -+pid_namespace_file_info (pid_t pid) -+{ -+ char *filename = g_strdup_printf("/proc/%u/ns/pid", pid); -+ GFile *file = g_file_new_for_path(filename); -+ g_free(filename); -+ -+ GError *error = NULL; -+ -+ GFileInfo *info = g_file_query_info(file, -+ G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, -+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, -+ NULL, -+ &error); -+ -+ g_object_unref(file); -+ -+ if (info == NULL) { -+ g_warning("MetaWindow: Error attempting to read /proc/%u/ns/pid symlink: %s", pid, error->message); -+ g_error_free (error); -+ return NULL; -+ } -+ -+ if (!g_file_info_get_is_symlink (info)) { -+ g_warning("MetaWindow: /proc/%u/ns/pid exists but is not a symlink?", pid); -+ g_object_unref (info); -+ return NULL; -+ } -+ -+ return info; -+ -+} -+ -+char * -+meta_read_pid_namespace (pid_t pid) -+{ -+ char *value = NULL; -+ GFileInfo *info = pid_namespace_file_info (pid); -+ const char *target = g_file_info_get_symlink_target (info); -+ -+ if (target) { -+ value = g_strdup (target); -+ } -+ g_object_unref (info); -+ -+ return value; -+} -diff --git a/src/core/window-private.h b/src/core/window-private.h -index d1730c9..6a55630 100644 ---- a/src/core/window-private.h -+++ b/src/core/window-private.h -@@ -562,6 +562,9 @@ struct _MetaWindow - guint unmanage_idle_id; - - pid_t client_pid; -+ -+ guint namespace_set: 1; -+ gchar *namespace; - }; - - struct _MetaWindowClass -diff --git a/src/core/window.c b/src/core/window.c -index c843c62..c0b9e3f 100644 ---- a/src/core/window.c -+++ b/src/core/window.c -@@ -345,6 +345,7 @@ meta_window_finalize (GObject *object) - g_free (window->gtk_app_menu_object_path); - g_free (window->gtk_menubar_object_path); - g_free (window->placement.rule); -+ g_free (window->namespace); - - G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); - } -@@ -1564,7 +1565,7 @@ meta_window_unmanage (MetaWindow *window, - g_assert (window->workspace == NULL); - - #ifndef G_DISABLE_CHECKS -- tmp = workspace_manager->workspaces; -+ tmp = workspace_manager->all_workspaces; - while (tmp != NULL) - { - MetaWorkspace *workspace = tmp->data; -@@ -4889,7 +4890,7 @@ set_workspace_state (MetaWindow *window, - else if (window->on_all_workspaces) - { - GList *l; -- for (l = workspace_manager->workspaces; l != NULL; l = l->next) -+ for (l = workspace_manager->all_workspaces; l != NULL; l = l->next) - { - MetaWorkspace *ws = l->data; - meta_workspace_remove_window (ws, window); -@@ -4904,7 +4905,7 @@ set_workspace_state (MetaWindow *window, - else if (window->on_all_workspaces) - { - GList *l; -- for (l = workspace_manager->workspaces; l != NULL; l = l->next) -+ for (l = workspace_manager->all_workspaces; l != NULL; l = l->next) - { - MetaWorkspace *ws = l->data; - meta_workspace_add_window (ws, window); -@@ -5203,8 +5204,10 @@ meta_window_change_workspace_by_index (MetaWindow *window, - workspace = - meta_workspace_manager_get_workspace_by_index (workspace_manager, space_index); - -- if (!workspace && append) -+ if (!workspace && append) { - workspace = meta_workspace_manager_append_new_workspace (workspace_manager, FALSE, META_CURRENT_TIME); -+ } -+ - - if (workspace) - meta_window_change_workspace (window, workspace); -@@ -5528,7 +5531,7 @@ meta_window_get_workspaces (MetaWindow *window) - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - if (window->on_all_workspaces) -- return workspace_manager->workspaces; -+ return workspace_manager->all_workspaces; - else if (window->workspace != NULL) - return window->workspace->list_containing_self; - else if (window->constructing) -@@ -8675,3 +8678,31 @@ meta_window_get_client_type (MetaWindow *window) - { - return window->client_type; - } -+ -+/** -+ * meta_window_namespace: -+ * @window: a #MetaWindow -+ * -+ * Returns string identifying PID namespace this window belongs to -+ * if known -+ * -+ * Return value: (transfer none): the pid namespace string, or %NULL -+ */ -+const char * -+meta_window_namespace (MetaWindow *window) -+{ -+ if (!window->namespace_set) { -+ window->namespace_set = TRUE; -+ pid_t pid = meta_window_get_pid (window); -+ if (pid) { -+ window->namespace = meta_read_pid_namespace (pid); -+ } -+ } -+ return window->namespace; -+} -+ -+gboolean -+meta_window_is_on_foreign_workspace_context (MetaWindow *window) -+{ -+ return meta_workspace_manager_is_window_on_foreign_context (window->display->workspace_manager, window); -+} -\ No newline at end of file -diff --git a/src/core/workspace-private.h b/src/core/workspace-private.h -index a58b234..2e364a1 100644 ---- a/src/core/workspace-private.h -+++ b/src/core/workspace-private.h -@@ -39,6 +39,7 @@ struct _MetaWorkspace - GObject parent_instance; - MetaDisplay *display; - MetaWorkspaceManager *manager; -+ guint context_id; - - GList *windows; - -@@ -74,6 +75,8 @@ struct _MetaWorkspaceClass - }; - - MetaWorkspace* meta_workspace_new (MetaWorkspaceManager *workspace_manager); -+MetaWorkspace* meta_workspace_new_with_context_id (MetaWorkspaceManager *workspace_manager, -+ guint context_id); - void meta_workspace_remove (MetaWorkspace *workspace); - void meta_workspace_add_window (MetaWorkspace *workspace, - MetaWindow *window); -diff --git a/src/core/workspace.c b/src/core/workspace.c -index 321d3ef..633184d 100644 ---- a/src/core/workspace.c -+++ b/src/core/workspace.c -@@ -237,7 +237,7 @@ meta_workspace_init (MetaWorkspace *workspace) - } - - MetaWorkspace * --meta_workspace_new (MetaWorkspaceManager *workspace_manager) -+meta_workspace_new_with_context_id (MetaWorkspaceManager *workspace_manager, guint context_id) - { - MetaDisplay *display = workspace_manager->display; - MetaWorkspace *workspace; -@@ -247,9 +247,11 @@ meta_workspace_new (MetaWorkspaceManager *workspace_manager) - - workspace->display = display; - workspace->manager = workspace_manager; -+ workspace->context_id = context_id; -+ -+ workspace_manager->all_workspaces = -+ g_list_append (workspace_manager->all_workspaces, workspace); - -- workspace_manager->workspaces = -- g_list_append (workspace_manager->workspaces, workspace); - workspace->windows = NULL; - workspace->mru_list = NULL; - -@@ -276,9 +278,17 @@ meta_workspace_new (MetaWorkspaceManager *workspace_manager) - meta_workspace_add_window (workspace, l->data); - g_slist_free (windows); - -+ meta_workspace_manager_append_context_workspace (workspace_manager, workspace); -+ - return workspace; - } - -+MetaWorkspace * -+meta_workspace_new (MetaWorkspaceManager *workspace_manager) -+{ -+ return meta_workspace_new_with_context_id (workspace_manager, workspace_manager->active_context->id); -+} -+ - /** - * workspace_free_all_struts: - * @workspace: The workspace. -@@ -333,8 +343,7 @@ meta_workspace_remove (MetaWorkspace *workspace) - - assert_workspace_empty (workspace); - -- manager->workspaces = -- g_list_remove (manager->workspaces, workspace); -+ meta_workspace_manager_remove_context_workspace (manager, workspace); - - meta_workspace_clear_logical_monitor_data (workspace); - -@@ -381,8 +390,8 @@ meta_workspace_add_window (MetaWorkspace *workspace, - if (window->struts) - { - meta_topic (META_DEBUG_WORKAREA, -- "Invalidating work area of workspace %d since we're adding window %s to it", -- meta_workspace_index (workspace), window->desc); -+ "Invalidating work area of workspace %08x since we're adding window %s to it", -+ meta_workspace_get_id (workspace), window->desc); - meta_workspace_invalidate_work_area (workspace); - } - -@@ -405,8 +414,8 @@ meta_workspace_remove_window (MetaWorkspace *workspace, - if (window->struts) - { - meta_topic (META_DEBUG_WORKAREA, -- "Invalidating work area of workspace %d since we're removing window %s from it", -- meta_workspace_index (workspace), window->desc); -+ "Invalidating work area of workspace %08x since we're removing window %s from it", -+ meta_workspace_get_id (workspace), window->desc); - meta_workspace_invalidate_work_area (workspace); - } - -@@ -456,6 +465,13 @@ workspace_switch_sound(MetaWorkspace *from, - int i, nw, x, y, fi, ti; - const char *e; - -+ g_message ("MetaWorkspace: workspace_switch_sound(from: %08x, to: %08x)", from->context_id, to->context_id); -+ if (from->context_id != to->context_id) { -+ /* XXX: There is no sound for context switches, but there should be (?) */ -+ return; -+ } -+ g_message ("MetaWorkspace: workspace_switch_sound(), calling meta_workspace_index() on from and to"); -+ - nw = meta_workspace_manager_get_n_workspaces (from->manager); - fi = meta_workspace_index(from); - ti = meta_workspace_index(to); -@@ -536,9 +552,15 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace, - MetaWorkspaceLayout layout1, layout2; - gint num_workspaces, current_space, new_space; - MetaMotionDirection direction; -+ MetaWorkspaceContext *context; -+ -+ meta_verbose ("Activating workspace %08x", -+ meta_workspace_get_id (workspace)); - -- meta_verbose ("Activating workspace %d", -- meta_workspace_index (workspace)); -+ context = meta_workspace_manager_lookup_context (workspace->manager, workspace->context_id); -+ if(context && !meta_workspace_context_is_current (context)) { -+ meta_workspace_context_make_active (context); -+ } - - if (workspace->manager->active_workspace == workspace) - { -@@ -601,52 +623,59 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace, - comp = meta_display_get_compositor (workspace->display); - direction = 0; - -- current_space = meta_workspace_index (old); -- new_space = meta_workspace_index (workspace); -- num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); -- meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, -- current_space, &layout1); - -- meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, -+ if (workspace->context_id != old->context_id) { -+ direction = META_MOTION_CONTEXT_SWITCH; -+ current_space = meta_workspace_get_id (old); -+ new_space = meta_workspace_get_id (workspace); -+ } else { -+ current_space = meta_workspace_index (old); -+ new_space = meta_workspace_index (workspace); -+ num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); -+ meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, -+ current_space, &layout1); -+ -+ meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, - new_space, &layout2); - -- if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) -- { -- if (layout1.current_col > layout2.current_col) -- direction = META_MOTION_RIGHT; -- else if (layout1.current_col < layout2.current_col) -- direction = META_MOTION_LEFT; -- } -- else -- { -- if (layout1.current_col < layout2.current_col) -- direction = META_MOTION_RIGHT; -- else if (layout1.current_col > layout2.current_col) -- direction = META_MOTION_LEFT; -- } -+ if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) -+ { -+ if (layout1.current_col > layout2.current_col) -+ direction = META_MOTION_RIGHT; -+ else if (layout1.current_col < layout2.current_col) -+ direction = META_MOTION_LEFT; -+ } -+ else -+ { -+ if (layout1.current_col < layout2.current_col) -+ direction = META_MOTION_RIGHT; -+ else if (layout1.current_col > layout2.current_col) -+ direction = META_MOTION_LEFT; -+ } - -- if (layout1.current_row < layout2.current_row) -- { -- if (!direction) -- direction = META_MOTION_DOWN; -- else if (direction == META_MOTION_RIGHT) -- direction = META_MOTION_DOWN_RIGHT; -- else -- direction = META_MOTION_DOWN_LEFT; -- } -- -- if (layout1.current_row > layout2.current_row) -- { -- if (!direction) -- direction = META_MOTION_UP; -- else if (direction == META_MOTION_RIGHT) -- direction = META_MOTION_UP_RIGHT; -- else -- direction = META_MOTION_UP_LEFT; -- } -- -- meta_workspace_manager_free_workspace_layout (&layout1); -- meta_workspace_manager_free_workspace_layout (&layout2); -+ if (layout1.current_row < layout2.current_row) -+ { -+ if (!direction) -+ direction = META_MOTION_DOWN; -+ else if (direction == META_MOTION_RIGHT) -+ direction = META_MOTION_DOWN_RIGHT; -+ else -+ direction = META_MOTION_DOWN_LEFT; -+ } -+ -+ if (layout1.current_row > layout2.current_row) -+ { -+ if (!direction) -+ direction = META_MOTION_UP; -+ else if (direction == META_MOTION_RIGHT) -+ direction = META_MOTION_UP_RIGHT; -+ else -+ direction = META_MOTION_UP_LEFT; -+ } -+ -+ meta_workspace_manager_free_workspace_layout (&layout1); -+ meta_workspace_manager_free_workspace_layout (&layout2); -+ } - - meta_compositor_switch_workspace (comp, old, workspace, direction); - -@@ -683,14 +712,19 @@ meta_workspace_activate (MetaWorkspace *workspace, - int - meta_workspace_index (MetaWorkspace *workspace) - { -- int ret; -- -- ret = g_list_index (workspace->manager->workspaces, workspace); -+ return meta_workspace_manager_context_workspace_index (workspace->manager, workspace); -+} - -- if (ret < 0) -- meta_bug ("Workspace does not exist to index!"); -+int -+meta_workspace_get_id (MetaWorkspace *workspace) -+{ -+ return meta_workspace_manager_get_workspace_id (workspace->manager, workspace); -+} - -- return ret; -+guint -+meta_workspace_get_context_id (MetaWorkspace *workspace) -+{ -+ return workspace->context_id; - } - - void -@@ -747,14 +781,14 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace) - if (workspace->work_areas_invalid) - { - meta_topic (META_DEBUG_WORKAREA, -- "Work area for workspace %d is already invalid", -- meta_workspace_index (workspace)); -+ "Work area for workspace %08x is already invalid", -+ meta_workspace_get_id (workspace)); - return; - } - - meta_topic (META_DEBUG_WORKAREA, -- "Invalidating work area for workspace %d", -- meta_workspace_index (workspace)); -+ "Invalidating work area for workspace %08x", -+ meta_workspace_get_id (workspace)); - - /* If we are in the middle of a resize or move operation, we - * might have cached pointers to the workspace's edges */ -@@ -925,8 +959,8 @@ ensure_work_areas_validated (MetaWorkspace *workspace) - } - workspace->work_area_screen = work_area; - meta_topic (META_DEBUG_WORKAREA, -- "Computed work area for workspace %d: %d,%d %d x %d", -- meta_workspace_index (workspace), -+ "Computed work area for workspace %08x: %d,%d %d x %d", -+ meta_workspace_get_id (workspace), - workspace->work_area_screen.x, - workspace->work_area_screen.y, - workspace->work_area_screen.width, -@@ -956,8 +990,8 @@ ensure_work_areas_validated (MetaWorkspace *workspace) - - meta_topic (META_DEBUG_WORKAREA, - "Computed work area for workspace %d " -- "monitor %d: %d,%d %d x %d", -- meta_workspace_index (workspace), -+ "monitor %08x: %d,%d %d x %d", -+ meta_workspace_get_id (workspace), - logical_monitor->number, - data->logical_monitor_work_area.x, - data->logical_monitor_work_area.y, -@@ -1199,6 +1233,8 @@ meta_motion_direction_to_string (MetaMotionDirection direction) - return "Up-Left"; - case META_MOTION_DOWN_LEFT: - return "Down-Left"; -+ case META_MOTION_CONTEXT_SWITCH: -+ return "Switch-Context"; - } - - return "Unknown"; -diff --git a/src/meta/common.h b/src/meta/common.h -index ab93080..32b6912 100644 ---- a/src/meta/common.h -+++ b/src/meta/common.h -@@ -328,6 +328,7 @@ typedef enum - * @META_MOTION_UP_RIGHT: Motion up and to the right - * @META_MOTION_DOWN_LEFT: Motion down and to the left - * @META_MOTION_DOWN_RIGHT: Motion down and to the right -+ * @META_MOTION_REALM_SWITCH: Workspace switch is a change of current realm - */ - - /* Negative to avoid conflicting with real workspace -@@ -343,7 +344,8 @@ typedef enum - META_MOTION_UP_LEFT = -5, - META_MOTION_UP_RIGHT = -6, - META_MOTION_DOWN_LEFT = -7, -- META_MOTION_DOWN_RIGHT = -8 -+ META_MOTION_DOWN_RIGHT = -8, -+ META_MOTION_CONTEXT_SWITCH = -9 - } MetaMotionDirection; - - /** -diff --git a/src/meta/meta-workspace-manager.h b/src/meta/meta-workspace-manager.h -index 92cd681..d00948d 100644 ---- a/src/meta/meta-workspace-manager.h -+++ b/src/meta/meta-workspace-manager.h -@@ -30,6 +30,14 @@ - #include - #include - -+#define META_TYPE_WORKSPACE_CONTEXT (meta_workspace_context_get_type ()) -+ -+META_EXPORT -+G_DECLARE_FINAL_TYPE (MetaWorkspaceContext, -+ meta_workspace_context, -+ META, WORKSPACE_CONTEXT, -+ GObject) -+ - #define META_TYPE_WORKSPACE_MANAGER (meta_workspace_manager_get_type ()) - - META_EXPORT -@@ -66,6 +74,9 @@ void meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_m - META_EXPORT - int meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager); - -+META_EXPORT -+int meta_workspace_manager_get_active_workspace_id (MetaWorkspaceManager *workspace_manager); -+ - META_EXPORT - MetaWorkspace *meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager); - -@@ -75,4 +86,37 @@ void meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *wor - gboolean vertical_layout, - int n_rows, - int n_columns); -+META_EXPORT -+guint meta_workspace_manager_active_context_id (MetaWorkspaceManager *workspace_manager); -+ -+META_EXPORT -+void meta_workspace_manager_set_builtin_struts_all(MetaWorkspaceManager *workspace_manager, -+ GSList *struts); -+ -+META_EXPORT -+MetaWorkspaceContext *meta_workspace_manager_context_for_namespace (MetaWorkspaceManager *workspace_manager, -+ const gchar *namespace); -+ -+META_EXPORT -+const char *meta_workspace_manager_mutter_namespace (MetaWorkspaceManager *workspace_manager); -+ -+META_EXPORT -+void meta_workspace_context_activate (MetaWorkspaceContext *context); -+ -+META_EXPORT -+void meta_workspace_context_remove (MetaWorkspaceContext *context); -+ -+META_EXPORT -+gboolean meta_workspace_context_is_current (MetaWorkspaceContext *context); -+ -+META_EXPORT -+MetaWorkspace * -+meta_workspace_context_get_active_workspace (MetaWorkspaceContext *context); -+ -+META_EXPORT -+guint meta_workspace_context_id (MetaWorkspaceContext *context); -+ -+META_EXPORT -+void meta_workspace_context_move_window_to_context (MetaWorkspaceContext *context, MetaWindow *window); -+ - #endif /* META_WORKSPACE_MANAGER_H */ -diff --git a/src/meta/types.h b/src/meta/types.h -index 49fb568..d479c19 100644 ---- a/src/meta/types.h -+++ b/src/meta/types.h -@@ -42,6 +42,7 @@ typedef struct _MetaCursorTracker MetaCursorTracker; - typedef struct _MetaDnd MetaDnd; - typedef struct _MetaSettings MetaSettings; - -+typedef struct _MetaWorkspaceContext MetaWorkspaceContext; - typedef struct _MetaWorkspaceManager MetaWorkspaceManager; - typedef struct _MetaSelection MetaSelection; - -diff --git a/src/meta/window.h b/src/meta/window.h -index 3157d44..a5901ee 100644 ---- a/src/meta/window.h -+++ b/src/meta/window.h -@@ -446,4 +446,10 @@ uint64_t meta_window_get_id (MetaWindow *window); - META_EXPORT - MetaWindowClientType meta_window_get_client_type (MetaWindow *window); - -+META_EXPORT -+const char *meta_window_namespace (MetaWindow *window); -+ -+META_EXPORT -+gboolean meta_window_is_on_foreign_workspace_context (MetaWindow *window); -+ - #endif -diff --git a/src/meta/workspace.h b/src/meta/workspace.h -index 99aebee..4b94cff 100644 ---- a/src/meta/workspace.h -+++ b/src/meta/workspace.h -@@ -39,6 +39,12 @@ GType meta_workspace_get_type (void); - META_EXPORT - int meta_workspace_index (MetaWorkspace *workspace); - -+META_EXPORT -+int meta_workspace_get_id (MetaWorkspace *workspace); -+ -+META_EXPORT -+guint meta_workspace_get_context_id (MetaWorkspace *workspace); -+ - META_EXPORT - MetaDisplay *meta_workspace_get_display (MetaWorkspace *workspace); - diff --git a/meta-gnome/recipes-gnome/mutter/mutter_41.0.bb b/meta-gnome/recipes-gnome/mutter/mutter_41.1.bb similarity index 96% rename from meta-gnome/recipes-gnome/mutter/mutter_41.0.bb rename to meta-gnome/recipes-gnome/mutter/mutter_41.1.bb index 6730eee..94b36ab 100644 --- a/meta-gnome/recipes-gnome/mutter/mutter_41.0.bb +++ b/meta-gnome/recipes-gnome/mutter/mutter_41.1.bb @@ -49,7 +49,7 @@ SRC_URI = "${GNOME_MIRROR}/${GNOMEBN}/${@gnome_verdir("${PV}")}/${GNOMEBN}-${PV} file://0001-Citadel-changes-to-Mutter.patch \ " -SRC_URI[archive.sha256sum] = "fa80a1a744044d88ebfd677ff03203d67705ed2cd624ea06cbb8b58948cdf89e" +SRC_URI[archive.sha256sum] = "58e63fd0bc43f35134f21313aff4aebf92c829d6df4dc99a044a1e376691e3f3" # x11 is still mandatory - see meson.build REQUIRED_DISTRO_FEATURES = "x11 systemd" diff --git a/meta-gnome/recipes-support/vte/vte_0.64.2.bb b/meta-gnome/recipes-support/vte/vte_0.66.0.bb similarity index 96% rename from meta-gnome/recipes-support/vte/vte_0.64.2.bb rename to meta-gnome/recipes-support/vte/vte_0.66.0.bb index 8f55189..125befa 100644 --- a/meta-gnome/recipes-support/vte/vte_0.64.2.bb +++ b/meta-gnome/recipes-support/vte/vte_0.66.0.bb @@ -18,8 +18,8 @@ GIR_MESON_OPTION = 'gir' inherit gnomebase gtk-doc features_check upstream-version-is-even gobject-introspection -SRCREV = "a913a6de14047c1939b84617f2dd483d37e89194" -SRC_URI = "git://gitlab.gnome.org/GNOME/vte.git;protocol=https;branch=vte-0-64" +SRCREV = "956d3bbd265279c296c56d9fb5ada1fee94768ee" +SRC_URI = "git://gitlab.gnome.org/GNOME/vte.git;protocol=https;branch=vte-0-66" # vapigen.m4 is required when vala is not present (but the one from vala should be used normally) SRC_URI += "file://0001-Add-W_EXITCODE-macro-for-non-glibc-systems.patch"