Citadel changes to gnome-shell
This commit is contained in:
parent
bc533af73f
commit
9cbbbab03a
@ -230,6 +230,14 @@
|
|||||||
<default>["<Super>9"]</default>
|
<default>["<Super>9"]</default>
|
||||||
<summary>Switch to application 9</summary>
|
<summary>Switch to application 9</summary>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="switch-realm" type="as">
|
||||||
|
<default>["<Primary>Tab"]</default>
|
||||||
|
<summary>Open Realm Switcher</summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-realm-backward" type="as">
|
||||||
|
<default>["<Shift><Primary>Tab"]</default>
|
||||||
|
<summary>Open Realm Switcher Backwards</summary>
|
||||||
|
</key>
|
||||||
<key name="show-screenshot-ui" type="as">
|
<key name="show-screenshot-ui" type="as">
|
||||||
<default>["Print"]</default>
|
<default>["Print"]</default>
|
||||||
<summary>Take a screenshot interactively</summary>
|
<summary>Take a screenshot interactively</summary>
|
||||||
|
@ -49,3 +49,5 @@
|
|||||||
// Lock / login screens
|
// Lock / login screens
|
||||||
@import 'widgets/login-dialog';
|
@import 'widgets/login-dialog';
|
||||||
@import 'widgets/screen-shield';
|
@import 'widgets/screen-shield';
|
||||||
|
|
||||||
|
@import 'widgets/realms';
|
||||||
|
17
data/theme/gnome-shell-sass/widgets/_realms.scss
Normal file
17
data/theme/gnome-shell-sass/widgets/_realms.scss
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.realm-switch-label {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: rgba(10, 10, 10, 0.7);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.realm-frame-label {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.realm-config-icon {
|
||||||
|
color: #8e8e80;
|
||||||
|
}
|
@ -29,6 +29,7 @@ theme_sources = files([
|
|||||||
'gnome-shell-sass/widgets/_overview.scss',
|
'gnome-shell-sass/widgets/_overview.scss',
|
||||||
'gnome-shell-sass/widgets/_panel.scss',
|
'gnome-shell-sass/widgets/_panel.scss',
|
||||||
'gnome-shell-sass/widgets/_popovers.scss',
|
'gnome-shell-sass/widgets/_popovers.scss',
|
||||||
|
'gnome-shell-sass/widgets/_realms.scss',
|
||||||
'gnome-shell-sass/widgets/_screen-shield.scss',
|
'gnome-shell-sass/widgets/_screen-shield.scss',
|
||||||
'gnome-shell-sass/widgets/_screenshot.scss',
|
'gnome-shell-sass/widgets/_screenshot.scss',
|
||||||
'gnome-shell-sass/widgets/_scrollbars.scss',
|
'gnome-shell-sass/widgets/_scrollbars.scss',
|
||||||
|
@ -27,7 +27,7 @@ foreach service, dir : dbus_services
|
|||||||
|
|
||||||
serviceconf = configuration_data()
|
serviceconf = configuration_data()
|
||||||
serviceconf.set('service', service)
|
serviceconf.set('service', service)
|
||||||
serviceconf.set('gjs', gjs.full_path())
|
serviceconf.set('gjs', gjs)
|
||||||
serviceconf.set('pkgdatadir', pkgdatadir)
|
serviceconf.set('pkgdatadir', pkgdatadir)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
|
@ -144,5 +144,12 @@
|
|||||||
<file>ui/status/remoteAccess.js</file>
|
<file>ui/status/remoteAccess.js</file>
|
||||||
<file>ui/status/system.js</file>
|
<file>ui/status/system.js</file>
|
||||||
<file>ui/status/thunderbolt.js</file>
|
<file>ui/status/thunderbolt.js</file>
|
||||||
|
|
||||||
|
<file>ui/realms/realmIndicator.js</file>
|
||||||
|
<file>ui/realms/realmManager.js</file>
|
||||||
|
<file>ui/realms/realmSearchProvider.js</file>
|
||||||
|
<file>ui/realms/realmSwitcher.js</file>
|
||||||
|
<file>ui/realms/realmWindowFrame.js</file>
|
||||||
|
<file>ui/realms/realmWindowMenu.js</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
@ -51,6 +51,7 @@ const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
|
|||||||
const ParentalControlsManager = imports.misc.parentalControlsManager;
|
const ParentalControlsManager = imports.misc.parentalControlsManager;
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
const RealmManager = imports.ui.realms.realmManager;
|
||||||
|
|
||||||
const WELCOME_DIALOG_LAST_SHOWN_VERSION = 'welcome-dialog-last-shown-version';
|
const WELCOME_DIALOG_LAST_SHOWN_VERSION = 'welcome-dialog-last-shown-version';
|
||||||
// Make sure to mention the point release, otherwise it will show every time
|
// Make sure to mention the point release, otherwise it will show every time
|
||||||
@ -94,6 +95,7 @@ var kbdA11yDialog = null;
|
|||||||
var inputMethod = null;
|
var inputMethod = null;
|
||||||
var introspectService = null;
|
var introspectService = null;
|
||||||
var locatePointer = null;
|
var locatePointer = null;
|
||||||
|
var realmManager = null;
|
||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
@ -288,6 +290,8 @@ function _initializeUI() {
|
|||||||
extensionManager = new ExtensionSystem.ExtensionManager();
|
extensionManager = new ExtensionSystem.ExtensionManager();
|
||||||
extensionManager.init();
|
extensionManager.init();
|
||||||
|
|
||||||
|
realmManager = new RealmManager.RealmManager();
|
||||||
|
|
||||||
if (sessionMode.isGreeter && screenShield) {
|
if (sessionMode.isGreeter && screenShield) {
|
||||||
layoutManager.connect('startup-prepared', () => {
|
layoutManager.connect('startup-prepared', () => {
|
||||||
screenShield.showDialog();
|
screenShield.showDialog();
|
||||||
|
@ -254,7 +254,7 @@ var Overview = class {
|
|||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
// Remember the workspace we started from
|
// Remember the workspace we started from
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_index();
|
this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragEnd() {
|
_onDragEnd() {
|
||||||
|
@ -341,7 +341,8 @@ class ControlsManager extends St.Widget {
|
|||||||
this._stateAdjustment.connect('notify::value', this._update.bind(this));
|
this._stateAdjustment.connect('notify::value', this._update.bind(this));
|
||||||
|
|
||||||
workspaceManager.connectObject(
|
workspaceManager.connectObject(
|
||||||
'notify::n-workspaces', () => this._updateAdjustment(), this);
|
'notify::n-workspaces', () => this._updateAdjustment(),
|
||||||
|
'context-switched', () => this._updateAdjustment(), this);
|
||||||
|
|
||||||
this._searchController = new SearchController.SearchController(
|
this._searchController = new SearchController.SearchController(
|
||||||
this._searchEntry,
|
this._searchEntry,
|
||||||
|
37
js/ui/realms/realmIndicator.js
Normal file
37
js/ui/realms/realmIndicator.js
Normal file
@ -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('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
55
js/ui/realms/realmManager.js
Normal file
55
js/ui/realms/realmManager.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
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._realmIndicator.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
326
js/ui/realms/realmSearchProvider.js
Normal file
326
js/ui/realms/realmSearchProvider.js
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
325
js/ui/realms/realmSwitcher.js
Normal file
325
js/ui/realms/realmSwitcher.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
308
js/ui/realms/realmWindowFrame.js
Normal file
308
js/ui/realms/realmWindowFrame.js
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
const { Clutter, Cogl, GObject, Meta, Shell, St } = imports.gi;
|
||||||
|
|
||||||
|
var WindowFrameManager = class WindowFrameManager {
|
||||||
|
constructor() {
|
||||||
|
this._realms = Shell.Realms.get_default();
|
||||||
|
let frames = this._realms.window_frames();
|
||||||
|
this._frame_effects = [];
|
||||||
|
|
||||||
|
global.window_manager.connect('map', this._handleWindowMap.bind(this));
|
||||||
|
global.workspace_manager.connect('context-window-moved', this._onContextWindowMoved.bind(this));
|
||||||
|
global.workspace_manager.connect('context-removed', this._onContextRemoved.bind(this));
|
||||||
|
frames.connect('realm-frame-colors-changed', this._onFrameColorsChanged.bind(this));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onContextRemoved(workspaceManager, id) {
|
||||||
|
this.trackWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onFrameColorsChanged(realms) {
|
||||||
|
this.trackWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
trackWindows() {
|
||||||
|
var actors = global.get_window_actors();
|
||||||
|
actors.forEach(a => this.handleWindow(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWindow(actor) {
|
||||||
|
let win = actor.metaWindow;
|
||||||
|
let win_id = win.get_stable_sequence();
|
||||||
|
let effect = this._frame_effects[win_id];
|
||||||
|
|
||||||
|
let frames = this._realms.window_frames();
|
||||||
|
|
||||||
|
if (frames.has_frame(win) && frames.is_frame_enabled(win)) {
|
||||||
|
let color = frames.color_for_window(win);
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
effect.setColor(color);
|
||||||
|
} else {
|
||||||
|
let label = frames.label_for_window(win);
|
||||||
|
effect = new RealmFrameEffect(actor, color, label);
|
||||||
|
this._frame_effects[win_id] = effect;
|
||||||
|
}
|
||||||
|
} else if (effect) {
|
||||||
|
effect.removeEffect(actor);
|
||||||
|
this._frame_effects[win_id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var RealmFrameEffect = GObject.registerClass(
|
||||||
|
class RealmFrameEffect extends Clutter.Effect {
|
||||||
|
_init(actor, color, label_text) {
|
||||||
|
super._init();
|
||||||
|
this._frame_width = 2;
|
||||||
|
this._pipeline = null;
|
||||||
|
this._color = color;
|
||||||
|
this._label_on_top = true;
|
||||||
|
this._label = null;
|
||||||
|
|
||||||
|
if (label_text) {
|
||||||
|
this._label = this._createLabel(actor, label_text);
|
||||||
|
this._updateLabel(actor.metaWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sizeChangedId = actor.metaWindow.connect('size-changed', window => {
|
||||||
|
this._updateLabel(window);
|
||||||
|
});
|
||||||
|
|
||||||
|
actor.add_effect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEffect(actor) {
|
||||||
|
if (this._label) {
|
||||||
|
actor.remove_child(this._label);
|
||||||
|
this._label = null;
|
||||||
|
}
|
||||||
|
if (this._sizeChangedId) {
|
||||||
|
let win = actor.metaWindow;
|
||||||
|
win.disconnect(this._sizeChangedId);
|
||||||
|
this._sizeChangedId = 0;
|
||||||
|
}
|
||||||
|
actor.remove_effect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createLabel(actor, label_text) {
|
||||||
|
let label = new St.Label({
|
||||||
|
style_class: 'realm-frame-label',
|
||||||
|
z_position: 1.0,
|
||||||
|
|
||||||
|
});
|
||||||
|
label.set_text(' '+label_text+' ');
|
||||||
|
actor.add_child(label);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateLabel(window) {
|
||||||
|
if (this._label) {
|
||||||
|
this._updateLabelPosition(window);
|
||||||
|
this._updateLabelColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateLabelPosition(window) {
|
||||||
|
|
||||||
|
if (!this._label_height) {
|
||||||
|
// If we scale the text, the reported size of the label will not be the value we need so
|
||||||
|
// save the initial value.
|
||||||
|
this._label_height = this._label.get_height();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let maximized = window.is_fullscreen() === true || // Fullscreen
|
||||||
|
[Meta.MaximizeFlags.BOTH, Meta.MaximizeFlags.VERTICAL].includes(window.get_maximized()); // Maximized
|
||||||
|
|
||||||
|
this._label_on_top = !maximized;
|
||||||
|
|
||||||
|
let frame_rect = window.get_frame_rect();
|
||||||
|
let buffer_rect = window.get_buffer_rect();
|
||||||
|
|
||||||
|
let offsetX = frame_rect.x - buffer_rect.x;
|
||||||
|
let offsetY = frame_rect.y - buffer_rect.y;
|
||||||
|
|
||||||
|
|
||||||
|
if (window.get_client_type() === Meta.WindowClientType.WAYLAND) {
|
||||||
|
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||||
|
if (scaleFactor !== 1) {
|
||||||
|
offsetX = offsetX / scaleFactor;
|
||||||
|
this._label.set_style(`font-size: ${12 / scaleFactor}pt;`);
|
||||||
|
}
|
||||||
|
offsetX -= 1;
|
||||||
|
offsetY -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If label is on top and there is enough space above title bar move position up by label height
|
||||||
|
if (this._label_on_top && this._label_height <= offsetY) {
|
||||||
|
offsetY -= this._label_height;
|
||||||
|
} else if (maximized) {
|
||||||
|
offsetX = 0;
|
||||||
|
offsetY = 0;
|
||||||
|
}
|
||||||
|
this._label.set_position(offsetX, offsetY);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateLabelColor() {
|
||||||
|
let fg = new Clutter.Color({
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 0,
|
||||||
|
alpha: 96,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bg = this._color.copy();
|
||||||
|
|
||||||
|
if (this._label_on_top) {
|
||||||
|
bg.alpha = 100;
|
||||||
|
} else {
|
||||||
|
bg.alpha = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clutter_text = this._label.get_clutter_text();
|
||||||
|
clutter_text.set_color(fg);
|
||||||
|
clutter_text.set_background_color(bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setColor(color) {
|
||||||
|
if (this._color && this._color.equal(color)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._color = color;
|
||||||
|
this.setPipelineColor();
|
||||||
|
if (this._label) {
|
||||||
|
this._updateLabelColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setPipelineColor() {
|
||||||
|
if (!this._color || !this._pipeline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let s = this._color.to_string();
|
||||||
|
|
||||||
|
let cogl_color = new Cogl.Color();
|
||||||
|
cogl_color.init_from_4ub(this._color.red, this._color.green, this._color.blue, 0xc4);
|
||||||
|
this._pipeline.set_color(cogl_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculate_frame_box(window, allocation) {
|
||||||
|
let frame_rect = window.get_frame_rect();
|
||||||
|
let buffer_rect = window.get_buffer_rect();
|
||||||
|
|
||||||
|
let offsetX = frame_rect.x - buffer_rect.x;
|
||||||
|
let offsetY = frame_rect.y - buffer_rect.y;
|
||||||
|
|
||||||
|
let top = offsetY - 3;
|
||||||
|
let bottom = offsetY - 1;
|
||||||
|
let left = offsetX - 3;
|
||||||
|
let right = offsetX - 1;
|
||||||
|
|
||||||
|
let wayland = window.get_client_type() == Meta.WindowClientType.WAYLAND;
|
||||||
|
|
||||||
|
if (wayland) {
|
||||||
|
bottom += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fw = this._frame_width;
|
||||||
|
|
||||||
|
|
||||||
|
switch (window.get_maximized()) {
|
||||||
|
case Meta.MaximizeFlags.BOTH:
|
||||||
|
top += fw;
|
||||||
|
right += fw;
|
||||||
|
bottom += fw - (wayland ? 5 : 0);
|
||||||
|
left += fw;
|
||||||
|
break;
|
||||||
|
case Meta.MaximizeFlags.HORIZONTAL:
|
||||||
|
right += fw;
|
||||||
|
left += fw;
|
||||||
|
break;
|
||||||
|
case Meta.MaximizeFlags.VERTICAL:
|
||||||
|
top += fw;
|
||||||
|
bottom += fw;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.is_fullscreen()) {
|
||||||
|
top += 3;
|
||||||
|
right += 2;
|
||||||
|
bottom -= (wayland ? 3 : 0);
|
||||||
|
left += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wayland && !window.decorated && !window.is_fullscreen() && (window.get_maximized() !== Meta.MaximizeFlags.BOTH)) {
|
||||||
|
bottom += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = left;
|
||||||
|
let y = top + fw;
|
||||||
|
let w = allocation.get_width() - (right + left);
|
||||||
|
let h = allocation.get_height() - (bottom + top + fw);
|
||||||
|
|
||||||
|
return [x, y, w, h];
|
||||||
|
}
|
||||||
|
|
||||||
|
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, width, width_factor = 1) {
|
||||||
|
this.draw_rect(node, x, y, width, this._frame_width * width_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_vline(node, x, y, height, width_factor = 1) {
|
||||||
|
this.draw_rect(node, x, y, this._frame_width * width_factor, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
this._pipeline = new Cogl.Pipeline(coglContext);
|
||||||
|
this.setPipelineColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pipelineNode = new Clutter.PipelineNode(this._pipeline);
|
||||||
|
pipelineNode.set_name('Realm Frame');
|
||||||
|
node.add_child(pipelineNode);
|
||||||
|
|
||||||
|
let [x, y, width, height] = this._calculate_frame_box(actor.metaWindow, actor.get_allocation_box());
|
||||||
|
|
||||||
|
// Top
|
||||||
|
this.draw_hline(pipelineNode, x, y, width, 2);
|
||||||
|
|
||||||
|
// Right
|
||||||
|
this.draw_vline(pipelineNode, x + width, y, height);
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
this.draw_hline(pipelineNode, x, y + height, width);
|
||||||
|
|
||||||
|
// Left
|
||||||
|
this.draw_vline(pipelineNode, x, y, height);
|
||||||
|
}
|
||||||
|
});
|
133
js/ui/realms/realmWindowMenu.js
Normal file
133
js/ui/realms/realmWindowMenu.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
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 enableFrameItem(window) {
|
||||||
|
const realms = Shell.Realms.get_default();
|
||||||
|
const frames = realms.window_frames();
|
||||||
|
if (!frames.has_frame(window)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let enabled = frames.is_frame_enabled(window);
|
||||||
|
let item = new PopupMenu.PopupMenuItem("Display colored window frame");
|
||||||
|
if (enabled) {
|
||||||
|
item.setOrnament(PopupMenu.Ornament.CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.connect('activate', () => {
|
||||||
|
let realms = Shell.Realms.get_default();
|
||||||
|
const frames = realms.window_frames();
|
||||||
|
frames.set_frame_enabled(window, !enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -78,7 +78,7 @@ const _modes = {
|
|||||||
|
|
||||||
'user': {
|
'user': {
|
||||||
hasOverview: true,
|
hasOverview: true,
|
||||||
showCalendarEvents: true,
|
showCalendarEvents: false,
|
||||||
showWelcomeDialog: true,
|
showWelcomeDialog: true,
|
||||||
allowSettings: true,
|
allowSettings: true,
|
||||||
allowScreencast: true,
|
allowScreencast: true,
|
||||||
|
@ -113,7 +113,7 @@ class Indicator extends PanelMenu.SystemIndicator {
|
|||||||
bindFlags);
|
bindFlags);
|
||||||
|
|
||||||
this._sessionSubMenu = new PopupMenu.PopupSubMenuMenuItem(
|
this._sessionSubMenu = new PopupMenu.PopupSubMenuMenuItem(
|
||||||
_('Power Off / Log Out'), true);
|
_('Power Off'), true);
|
||||||
this._sessionSubMenu.icon.icon_name = 'system-shutdown-symbolic';
|
this._sessionSubMenu.icon.icon_name = 'system-shutdown-symbolic';
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_('Suspend'));
|
item = new PopupMenu.PopupMenuItem(_('Suspend'));
|
||||||
|
@ -199,6 +199,8 @@ var WorkspaceTracker = class {
|
|||||||
workspaceManager.connect('workspaces-reordered', () => {
|
workspaceManager.connect('workspaces-reordered', () => {
|
||||||
this._workspaces.sort((a, b) => a.index() - b.index());
|
this._workspaces.sort((a, b) => a.index() - b.index());
|
||||||
});
|
});
|
||||||
|
workspaceManager.connect('context-switched',
|
||||||
|
this._workspaceContextSwitched.bind(this));
|
||||||
global.window_manager.connect('switch-workspace',
|
global.window_manager.connect('switch-workspace',
|
||||||
this._queueCheckWorkspaces.bind(this));
|
this._queueCheckWorkspaces.bind(this));
|
||||||
|
|
||||||
@ -254,6 +256,8 @@ var WorkspaceTracker = class {
|
|||||||
emptyWorkspaces[index] = false;
|
emptyWorkspaces[index] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_context_id = workspaceManager.active_context_id();
|
||||||
|
|
||||||
let windows = global.get_window_actors();
|
let windows = global.get_window_actors();
|
||||||
for (i = 0; i < windows.length; i++) {
|
for (i = 0; i < windows.length; i++) {
|
||||||
let actor = windows[i];
|
let actor = windows[i];
|
||||||
@ -262,7 +266,12 @@ var WorkspaceTracker = class {
|
|||||||
if (win.is_on_all_workspaces())
|
if (win.is_on_all_workspaces())
|
||||||
continue;
|
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;
|
emptyWorkspaces[workspaceIndex] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,6 +349,28 @@ var WorkspaceTracker = class {
|
|||||||
this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, this._checkWorkspaces.bind(this));
|
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() {
|
_nWorkspacesChanged() {
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
let oldNumWorkspaces = this._workspaces.length;
|
let oldNumWorkspaces = this._workspaces.length;
|
||||||
@ -1629,6 +1660,14 @@ var WindowManager = class {
|
|||||||
|
|
||||||
this._switchInProgress = true;
|
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._workspaceAnimation.animateSwitch(from, to, direction, () => {
|
||||||
this._shellwm.completed_switch_workspace();
|
this._shellwm.completed_switch_workspace();
|
||||||
this._switchInProgress = false;
|
this._switchInProgress = false;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
|
||||||
/* exported WindowMenuManager */
|
/* exported WindowMenuManager */
|
||||||
|
|
||||||
const { GLib, Meta, St } = imports.gi;
|
const { GLib, Meta, Shell, St } = imports.gi;
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const RealmWindowMenu = imports.ui.realms.realmWindowMenu;
|
||||||
const Screenshot = imports.ui.screenshot;
|
const Screenshot = imports.ui.screenshot;
|
||||||
|
|
||||||
var WindowMenu = class extends PopupMenu.PopupMenu {
|
var WindowMenu = class extends PopupMenu.PopupMenu {
|
||||||
@ -25,6 +26,23 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
|
|||||||
|
|
||||||
let item;
|
let item;
|
||||||
|
|
||||||
|
let s = RealmWindowMenu.windowMenuDebugString(window);
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(s));
|
||||||
|
|
||||||
|
item = RealmWindowMenu.enableFrameItem(window);
|
||||||
|
if (item) {
|
||||||
|
this.addMenuItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.is_on_all_workspaces()) {
|
||||||
|
let realmSubmenu = RealmWindowMenu.realmWindowMenu(window);
|
||||||
|
if (realmSubmenu) {
|
||||||
|
this.addMenuItem(realmSubmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
// Translators: entry in the window right click menu.
|
// Translators: entry in the window right click menu.
|
||||||
item = this.addAction(_('Take Screenshot'), async () => {
|
item = this.addAction(_('Take Screenshot'), async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -108,6 +108,7 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
this._updateWorkspaces();
|
this._updateWorkspaces();
|
||||||
workspaceManager.connectObject(
|
workspaceManager.connectObject(
|
||||||
'notify::n-workspaces', this._updateWorkspaces.bind(this),
|
'notify::n-workspaces', this._updateWorkspaces.bind(this),
|
||||||
|
'context-switched', this._refreshWorkspaces.bind(this),
|
||||||
'workspaces-reordered', () => {
|
'workspaces-reordered', () => {
|
||||||
this._workspaces.sort((a, b) => {
|
this._workspaces.sort((a, b) => {
|
||||||
return a.metaWorkspace.index() - b.metaWorkspace.index();
|
return a.metaWorkspace.index() - b.metaWorkspace.index();
|
||||||
@ -434,6 +435,13 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_refreshWorkspaces() {
|
||||||
|
for (let ws = this._workspaces.pop(); ws; ws = this._workspaces.pop()) {
|
||||||
|
ws.destroy();
|
||||||
|
}
|
||||||
|
this._updateWorkspaces();
|
||||||
|
}
|
||||||
|
|
||||||
_updateWorkspaces() {
|
_updateWorkspaces() {
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
let newNumWorkspaces = workspaceManager.n_workspaces;
|
let newNumWorkspaces = workspaceManager.n_workspaces;
|
||||||
|
10
meson.build
10
meson.build
@ -18,8 +18,8 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version
|
|||||||
libmutter_pc = 'libmutter-' + mutter_api_version
|
libmutter_pc = 'libmutter-' + mutter_api_version
|
||||||
libmutter_test_pc = 'libmutter-test-' + mutter_api_version
|
libmutter_test_pc = 'libmutter-test-' + mutter_api_version
|
||||||
|
|
||||||
ecal_req = '>= 3.33.1'
|
#ecal_req = '>= 3.33.1'
|
||||||
eds_req = '>= 3.33.1'
|
#eds_req = '>= 3.33.1'
|
||||||
gcr_req = '>= 3.7.5'
|
gcr_req = '>= 3.7.5'
|
||||||
gio_req = '>= 2.56.0'
|
gio_req = '>= 2.56.0'
|
||||||
gi_req = '>= 1.49.1'
|
gi_req = '>= 1.49.1'
|
||||||
@ -71,8 +71,8 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
atk_bridge_dep = dependency('atk-bridge-2.0')
|
atk_bridge_dep = dependency('atk-bridge-2.0')
|
||||||
ecal_dep = dependency('libecal-2.0', version: ecal_req)
|
#ecal_dep = dependency('libecal-2.0', version: ecal_req)
|
||||||
eds_dep = dependency('libedataserver-1.2', version: eds_req)
|
#eds_dep = dependency('libedataserver-1.2', version: eds_req)
|
||||||
gcr_dep = dependency('gcr-base-3', version: gcr_req)
|
gcr_dep = dependency('gcr-base-3', version: gcr_req)
|
||||||
gdk_x11_dep = dependency('gdk-x11-3.0')
|
gdk_x11_dep = dependency('gdk-x11-3.0')
|
||||||
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0')
|
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0')
|
||||||
@ -138,7 +138,7 @@ endif
|
|||||||
|
|
||||||
mutter_typelibdir = mutter_dep.get_variable('typelibdir')
|
mutter_typelibdir = mutter_dep.get_variable('typelibdir')
|
||||||
python = find_program('python3')
|
python = find_program('python3')
|
||||||
gjs = find_program('gjs')
|
gjs = '/usr/bin/gjs'
|
||||||
|
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!@PYTHON@
|
#!/usr/bin/env python3
|
||||||
# -*- mode: Python; indent-tabs-mode: nil; -*-
|
# -*- mode: Python; indent-tabs-mode: nil; -*-
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!@PYTHON@
|
#!/usr/bin/env python3
|
||||||
# -*- mode: Python; indent-tabs-mode: nil; -*-
|
# -*- mode: Python; indent-tabs-mode: nil; -*-
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "shell-global-private.h"
|
#include "shell-global-private.h"
|
||||||
#include "shell-perf-log.h"
|
#include "shell-perf-log.h"
|
||||||
#include "shell-wm-private.h"
|
#include "shell-wm-private.h"
|
||||||
|
#include "shell-realms.h"
|
||||||
|
|
||||||
#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
|
#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (GnomeShellPlugin, gnome_shell_plugin,
|
G_DECLARE_FINAL_TYPE (GnomeShellPlugin, gnome_shell_plugin,
|
||||||
@ -214,6 +215,20 @@ gnome_shell_plugin_destroy (MetaPlugin *plugin,
|
|||||||
actor);
|
actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_switched_context_current(guint ctx_id) {
|
||||||
|
ShellRealms *realms = shell_realms_get_default();
|
||||||
|
ShellRealmItem *realm = shell_realms_realm_by_context_id(realms, ctx_id);
|
||||||
|
if (!realm) {
|
||||||
|
g_warning ("No realm found for ctx_id = %d in context switch", ctx_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shell_realm_item_is_current(realm)) {
|
||||||
|
shell_realm_item_set_current(realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gnome_shell_plugin_switch_workspace (MetaPlugin *plugin,
|
gnome_shell_plugin_switch_workspace (MetaPlugin *plugin,
|
||||||
gint from,
|
gint from,
|
||||||
@ -221,6 +236,10 @@ gnome_shell_plugin_switch_workspace (MetaPlugin *plugin,
|
|||||||
MetaMotionDirection direction)
|
MetaMotionDirection direction)
|
||||||
{
|
{
|
||||||
_shell_wm_switch_workspace (get_shell_wm(), from, to, direction);
|
_shell_wm_switch_workspace (get_shell_wm(), from, to, direction);
|
||||||
|
|
||||||
|
if (direction == META_MOTION_CONTEXT_SWITCH) {
|
||||||
|
ensure_switched_context_current((guint) (to >> 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
service_data = configuration_data()
|
service_data = configuration_data()
|
||||||
service_data.set('libexecdir', libexecdir)
|
service_data.set('libexecdir', libexecdir)
|
||||||
|
|
||||||
subdir('calendar-server')
|
#subdir('calendar-server')
|
||||||
subdir('hotplug-sniffer')
|
subdir('hotplug-sniffer')
|
||||||
subdir('st')
|
subdir('st')
|
||||||
subdir('tray')
|
subdir('tray')
|
||||||
@ -125,6 +125,7 @@ libshell_private_headers = [
|
|||||||
'shell-app-cache-private.h',
|
'shell-app-cache-private.h',
|
||||||
'shell-app-system-private.h',
|
'shell-app-system-private.h',
|
||||||
'shell-global-private.h',
|
'shell-global-private.h',
|
||||||
|
'shell-realms-private.h',
|
||||||
'shell-window-tracker-private.h',
|
'shell-window-tracker-private.h',
|
||||||
'shell-wm-private.h'
|
'shell-wm-private.h'
|
||||||
]
|
]
|
||||||
@ -147,6 +148,14 @@ libshell_sources = [
|
|||||||
'shell-perf-log.c',
|
'shell-perf-log.c',
|
||||||
'shell-polkit-authentication-agent.c',
|
'shell-polkit-authentication-agent.c',
|
||||||
'shell-polkit-authentication-agent.h',
|
'shell-polkit-authentication-agent.h',
|
||||||
|
'shell-realm-item.c',
|
||||||
|
'shell-realm-item.h',
|
||||||
|
'shell-realms-window-frames.c',
|
||||||
|
'shell-realms-window-frames.h',
|
||||||
|
'shell-realms.c',
|
||||||
|
'shell-realms.h',
|
||||||
|
'shell-realm-tracker.c',
|
||||||
|
'shell-realm-tracker.h',
|
||||||
'shell-screenshot.c',
|
'shell-screenshot.c',
|
||||||
'shell-secure-text-buffer.c',
|
'shell-secure-text-buffer.c',
|
||||||
'shell-secure-text-buffer.h',
|
'shell-secure-text-buffer.h',
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
* Shell is running.
|
* Shell is running.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DEFAULT_TIMEOUT_SECONDS 5
|
#define DEFAULT_TIMEOUT_SECONDS 2
|
||||||
|
|
||||||
struct _ShellAppCache
|
struct _ShellAppCache
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "shell-app-cache-private.h"
|
#include "shell-app-cache-private.h"
|
||||||
#include "shell-app-private.h"
|
#include "shell-app-private.h"
|
||||||
#include "shell-window-tracker-private.h"
|
#include "shell-window-tracker-private.h"
|
||||||
|
#include "shell-realms.h"
|
||||||
#include "shell-app-system-private.h"
|
#include "shell-app-system-private.h"
|
||||||
#include "shell-global.h"
|
#include "shell-global.h"
|
||||||
#include "shell-util.h"
|
#include "shell-util.h"
|
||||||
@ -91,6 +92,110 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
|||||||
G_TYPE_NONE, 0);
|
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
|
static void
|
||||||
scan_startup_wm_class_to_id (ShellAppSystem *self)
|
scan_startup_wm_class_to_id (ShellAppSystem *self)
|
||||||
{
|
{
|
||||||
@ -106,6 +211,7 @@ scan_startup_wm_class_to_id (ShellAppSystem *self)
|
|||||||
{
|
{
|
||||||
GAppInfo *info = l->data;
|
GAppInfo *info = l->data;
|
||||||
const char *startup_wm_class, *id, *old_id;
|
const char *startup_wm_class, *id, *old_id;
|
||||||
|
char *realm_wmclass;
|
||||||
|
|
||||||
id = g_app_info_get_id (info);
|
id = g_app_info_get_id (info);
|
||||||
startup_wm_class = g_desktop_app_info_get_startup_wm_class (G_DESKTOP_APP_INFO (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)
|
if (startup_wm_class == NULL)
|
||||||
continue;
|
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
|
/* In case multiple .desktop files set the same StartupWMClass, prefer
|
||||||
* the one where ID and StartupWMClass match */
|
* the one where ID and StartupWMClass match */
|
||||||
old_id = g_hash_table_lookup (priv->startup_wm_class_to_id, startup_wm_class);
|
old_id = g_hash_table_lookup (priv->startup_wm_class_to_id, realm_wmclass);
|
||||||
if (old_id == NULL || strcmp (id, startup_wm_class) == 0)
|
if (old_id == NULL || strcmp (id, startup_wm_class) == 0) {
|
||||||
g_hash_table_insert (priv->startup_wm_class_to_id,
|
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 *
|
ShellApp *
|
||||||
shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
|
shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
|
||||||
const char *wmclass)
|
const char *wmclass,
|
||||||
|
const char *realm_name)
|
||||||
{
|
{
|
||||||
char *canonicalized;
|
char *canonicalized;
|
||||||
char *desktop_file;
|
char *desktop_file;
|
||||||
|
char *classname;
|
||||||
ShellApp *app;
|
ShellApp *app;
|
||||||
|
|
||||||
if (wmclass == NULL)
|
if (wmclass == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
classname = realm_wm_class (wmclass, realm_name);
|
||||||
|
|
||||||
/* First try without changing the case (this handles
|
/* First try without changing the case (this handles
|
||||||
org.example.Foo.Bar.desktop applications)
|
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
|
the WM_CLASS to Org.example.Foo.Bar, but it also
|
||||||
sets the instance part to org.example.Foo.Bar, so we're ok
|
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);
|
app = shell_app_system_lookup_heuristic_basename (system, desktop_file);
|
||||||
g_free (desktop_file);
|
g_free (desktop_file);
|
||||||
|
|
||||||
if (app)
|
if (app) {
|
||||||
|
g_free (classname);
|
||||||
return app;
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
canonicalized = g_ascii_strdown (wmclass, -1);
|
canonicalized = g_ascii_strdown (classname, -1);
|
||||||
|
|
||||||
/* This handles "Fedora Eclipse", probably others.
|
/* This handles "Fedora Eclipse", probably others.
|
||||||
* Note g_strdelimit is modify-in-place. */
|
* Note g_strdelimit is modify-in-place. */
|
||||||
@ -382,6 +499,7 @@ shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
|
|||||||
|
|
||||||
g_free (canonicalized);
|
g_free (canonicalized);
|
||||||
g_free (desktop_file);
|
g_free (desktop_file);
|
||||||
|
g_free (classname);
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@ -398,14 +516,20 @@ shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
|
|||||||
*/
|
*/
|
||||||
ShellApp *
|
ShellApp *
|
||||||
shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
|
shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
|
||||||
const char *wmclass)
|
const char *wmclass,
|
||||||
|
const char *realm_name)
|
||||||
{
|
{
|
||||||
const char *id;
|
const char *id;
|
||||||
|
char *classname;
|
||||||
|
|
||||||
if (wmclass == NULL)
|
if (wmclass == NULL)
|
||||||
return 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)
|
if (id == NULL)
|
||||||
return 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);
|
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:
|
* shell_app_system_get_running:
|
||||||
* @self: A #ShellAppSystem
|
* @self: A #ShellAppSystem
|
||||||
@ -458,8 +605,10 @@ shell_app_system_get_running (ShellAppSystem *self)
|
|||||||
{
|
{
|
||||||
ShellApp *app = key;
|
ShellApp *app = key;
|
||||||
|
|
||||||
|
if (is_current_realm_context_app (app)) {
|
||||||
ret = g_slist_prepend (ret, app);
|
ret = g_slist_prepend (ret, app);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = g_slist_sort (ret, (GCompareFunc)shell_app_compare);
|
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 ***results = g_desktop_app_info_search (search_string);
|
||||||
char ***groups, **ids;
|
char ***groups, **ids;
|
||||||
|
char *current_realm;
|
||||||
|
|
||||||
|
current_realm = current_realm_name (FALSE);
|
||||||
|
|
||||||
for (groups = results; *groups; groups++)
|
for (groups = results; *groups; groups++)
|
||||||
for (ids = *groups; *ids; ids++)
|
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';
|
**ids = '\0';
|
||||||
|
|
||||||
|
g_free (current_realm);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,5 +657,7 @@ shell_app_system_search (const char *search_string)
|
|||||||
GList *
|
GList *
|
||||||
shell_app_system_get_installed (ShellAppSystem *self)
|
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;
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,11 @@ ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *
|
|||||||
const char *id);
|
const char *id);
|
||||||
|
|
||||||
ShellApp *shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
|
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,
|
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);
|
GSList *shell_app_system_get_running (ShellAppSystem *self);
|
||||||
char ***shell_app_system_search (const char *search_string);
|
char ***shell_app_system_search (const char *search_string);
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "shell-util.h"
|
#include "shell-util.h"
|
||||||
#include "st.h"
|
#include "st.h"
|
||||||
#include "switcheroo-control.h"
|
#include "switcheroo-control.h"
|
||||||
|
#include "shell-realm-tracker.h"
|
||||||
|
|
||||||
static ShellGlobal *the_object = NULL;
|
static ShellGlobal *the_object = NULL;
|
||||||
|
|
||||||
@ -1073,6 +1074,8 @@ _shell_global_set_plugin (ShellGlobal *global,
|
|||||||
global->focus_manager = st_focus_manager_get_for_stage (global->stage);
|
global->focus_manager = st_focus_manager_get_for_stage (global->stage);
|
||||||
|
|
||||||
update_scaling_factor (global, settings);
|
update_scaling_factor (global, settings);
|
||||||
|
|
||||||
|
shell_realm_tracker_start ();
|
||||||
}
|
}
|
||||||
|
|
||||||
GjsContext *
|
GjsContext *
|
||||||
|
396
src/shell-realm-item.c
Normal file
396
src/shell-realm-item.c
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
#include "shell-global.h"
|
||||||
|
#include "shell-realm-item.h"
|
||||||
|
#include "shell-realm-tracker.h"
|
||||||
|
#include <meta/meta-workspace-manager.h>
|
||||||
|
#include <meta/display.h>
|
||||||
|
|
||||||
|
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) {
|
||||||
|
meta_workspace_context_remove (item->context);
|
||||||
|
item->context = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
meta_workspace_context_remove (item->context);
|
||||||
|
item->context = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (item->context) {
|
||||||
|
meta_workspace_context_remove (item->context);
|
||||||
|
item->context = NULL;
|
||||||
|
}
|
||||||
|
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_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));
|
||||||
|
}
|
35
src/shell-realm-item.h
Normal file
35
src/shell-realm-item.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef __SHELL_REALM_ITEM_H__
|
||||||
|
#define __SHELL_REALM_ITEM_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <meta/window.h>
|
||||||
|
|
||||||
|
#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__
|
310
src/shell-realm-tracker.c
Normal file
310
src/shell-realm-tracker.c
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
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
|
||||||
|
set_realm_current_finish (GObject *object, GAsyncResult *result, gpointer 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 'SetCurrent' bus method: %s", error->message);
|
||||||
|
}
|
||||||
|
g_clear_error (&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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), set_realm_current_finish, 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);
|
||||||
|
|
||||||
|
}
|
14
src/shell-realm-tracker.h
Normal file
14
src/shell-realm-tracker.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __SHELL_REALM_TRACKER_H__
|
||||||
|
#define __SHELL_REALM_TRACKER_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#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__ */
|
21
src/shell-realms-private.h
Normal file
21
src/shell-realms-private.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef __SHELL_REALMS_PRIVATE_H__
|
||||||
|
#define __SHELL_REALMS_PRIVATE_H__
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include "shell-realms.h"
|
||||||
|
#include "shell-realms-window-frames.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__
|
386
src/shell-realms-window-frames.c
Normal file
386
src/shell-realms-window-frames.c
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
#include "shell-realms-window-frames.h"
|
||||||
|
#include "shell-realms.h"
|
||||||
|
|
||||||
|
#define CITADEL_SETTINGS_SCHEMA "com.subgraph.citadel"
|
||||||
|
#define FRAME_COLOR_LIST_KEY "frame-color-list"
|
||||||
|
#define REALM_FRAME_COLORS_KEY "realm-frame-colors"
|
||||||
|
|
||||||
|
#define REALM_FRAME_ALPHA 200
|
||||||
|
|
||||||
|
struct _ShellRealmsWindowFrames {
|
||||||
|
GObject parent;
|
||||||
|
GSettings *settings;
|
||||||
|
GHashTable *realm_frame_colors;
|
||||||
|
GList *default_colors;
|
||||||
|
GList *frame_disabled_windows;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (ShellRealmsWindowFrames, shell_realms_window_frames, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
REALM_FRAME_COLORS_CHANGED,
|
||||||
|
LAST_SIGNAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint shell_realms_window_frames_signals [LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_realms_window_frames_process_color (ShellRealmsWindowFrames *frames, const char *entry)
|
||||||
|
{
|
||||||
|
GdkRGBA rgba;
|
||||||
|
|
||||||
|
gchar **split = g_strsplit (entry, ":", -1);
|
||||||
|
|
||||||
|
if (g_strv_length (split) != 2) {
|
||||||
|
g_warning("ShellRealmsWindowFrames: Unable to parse realm-frame-colors entry: %s", entry);
|
||||||
|
g_strfreev (split);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gdk_rgba_parse (&rgba, split[1])) {
|
||||||
|
g_warning("ShellRealmsWindowFrames: Failed to parse RGBA component of realm frame color entry: %s", entry);
|
||||||
|
} else {
|
||||||
|
g_hash_table_insert (frames->realm_frame_colors, g_strdup (split[0]), gdk_rgba_copy (&rgba));
|
||||||
|
}
|
||||||
|
g_strfreev (split);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_realm_frame_colors (ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
guint n_entries, i;
|
||||||
|
char **entries;
|
||||||
|
|
||||||
|
entries = g_settings_get_strv (frames->settings, REALM_FRAME_COLORS_KEY);
|
||||||
|
n_entries = g_strv_length (entries);
|
||||||
|
|
||||||
|
for (i = 0; i < n_entries; i++) {
|
||||||
|
shell_realms_window_frames_process_color (frames, entries[i]);
|
||||||
|
}
|
||||||
|
g_strfreev (entries);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_realm_frame_colors_changed(GSettings *settings, const gchar *key, ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
load_realm_frame_colors (frames);
|
||||||
|
g_signal_emit (frames, shell_realms_window_frames_signals[REALM_FRAME_COLORS_CHANGED], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_default_colors (ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
guint n_entries, i;
|
||||||
|
char **entries;
|
||||||
|
GdkRGBA rgba;
|
||||||
|
|
||||||
|
entries = g_settings_get_strv (frames->settings, FRAME_COLOR_LIST_KEY);
|
||||||
|
n_entries = g_strv_length (entries);
|
||||||
|
|
||||||
|
g_clear_list(&frames->default_colors, (GDestroyNotify) gdk_rgba_free);
|
||||||
|
|
||||||
|
for (i = 0; i < n_entries; i++) {
|
||||||
|
if (gdk_rgba_parse (&rgba, entries[i])) {
|
||||||
|
frames->default_colors = g_list_append (frames->default_colors, gdk_rgba_copy(&rgba));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev (entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_frame_color_list_changed (GSettings *settings, const gchar *key, ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
load_default_colors (frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_realms_window_frames_init (ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
frames->settings = g_settings_new (CITADEL_SETTINGS_SCHEMA);
|
||||||
|
frames->realm_frame_colors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdk_rgba_free);
|
||||||
|
frames->default_colors = NULL;
|
||||||
|
frames->frame_disabled_windows = NULL;
|
||||||
|
|
||||||
|
g_signal_connect(frames->settings,
|
||||||
|
"changed::" FRAME_COLOR_LIST_KEY,
|
||||||
|
G_CALLBACK(on_frame_color_list_changed),
|
||||||
|
frames);
|
||||||
|
|
||||||
|
g_signal_connect(frames->settings,
|
||||||
|
"changed::" REALM_FRAME_COLORS_KEY,
|
||||||
|
G_CALLBACK(on_realm_frame_colors_changed),
|
||||||
|
frames);
|
||||||
|
|
||||||
|
|
||||||
|
load_default_colors (frames);
|
||||||
|
load_realm_frame_colors (frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_realms_window_frames_finalize (GObject *obj)
|
||||||
|
{
|
||||||
|
ShellRealmsWindowFrames *frames = SHELL_REALMS_WINDOW_FRAMES (obj);
|
||||||
|
g_object_unref (frames->settings);
|
||||||
|
g_hash_table_destroy (frames->realm_frame_colors);
|
||||||
|
g_list_free_full (frames->default_colors, (GDestroyNotify) gdk_rgba_free);
|
||||||
|
g_list_free (frames->frame_disabled_windows);
|
||||||
|
G_OBJECT_CLASS (shell_realms_window_frames_parent_class)->finalize (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_realms_window_frames_class_init (ShellRealmsWindowFramesClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
object_class->finalize = shell_realms_window_frames_finalize;
|
||||||
|
|
||||||
|
shell_realms_window_frames_signals[REALM_FRAME_COLORS_CHANGED] =
|
||||||
|
g_signal_new ("realm-frame-colors-changed",
|
||||||
|
G_TYPE_FROM_CLASS(klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
disabled_list_contains (ShellRealmsWindowFrames *frames, guint32 window_id)
|
||||||
|
{
|
||||||
|
return g_list_find (frames->frame_disabled_windows, GUINT_TO_POINTER(window_id)) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
remove_from_disabled_list (ShellRealmsWindowFrames *frames, guint32 window_id)
|
||||||
|
{
|
||||||
|
if (disabled_list_contains (frames, window_id)) {
|
||||||
|
frames->frame_disabled_windows = g_list_remove (frames->frame_disabled_windows, GUINT_TO_POINTER(window_id));
|
||||||
|
g_signal_emit (frames, shell_realms_window_frames_signals[REALM_FRAME_COLORS_CHANGED], 0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
add_to_disabled_list (ShellRealmsWindowFrames *frames, guint32 window_id)
|
||||||
|
{
|
||||||
|
if (!disabled_list_contains (frames, window_id)) {
|
||||||
|
frames->frame_disabled_windows = g_list_append (frames->frame_disabled_windows, GUINT_TO_POINTER(window_id));
|
||||||
|
g_signal_emit (frames, shell_realms_window_frames_signals[REALM_FRAME_COLORS_CHANGED], 0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
color_compare(gconstpointer c1, gconstpointer c2) {
|
||||||
|
return gdk_rgba_equal (c1, c2) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GdkRGBA *
|
||||||
|
allocate_color (ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
guint n_colors = g_list_length (frames->default_colors);
|
||||||
|
|
||||||
|
// 1) No default colors? return a built in color
|
||||||
|
if (n_colors == 0) {
|
||||||
|
GdkRGBA rgba;
|
||||||
|
gdk_rgba_parse (&rgba, "rgb(153, 193, 241)");
|
||||||
|
return gdk_rgba_copy (&rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) No default colors? Find first color on default color list that isn't used already
|
||||||
|
GList *used_colors = g_hash_table_get_values (frames->realm_frame_colors);
|
||||||
|
for (GList *iter = frames->default_colors; iter; iter = iter->next) {
|
||||||
|
GdkRGBA *rgba = iter->data;
|
||||||
|
if (!g_list_find_custom (used_colors, rgba, color_compare)) {
|
||||||
|
return rgba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_list_free (used_colors);
|
||||||
|
|
||||||
|
// 3) Choose a random element of the default list
|
||||||
|
guint index = (guint) g_random_int_range(0, (gint32) n_colors);
|
||||||
|
return g_list_nth_data (frames->default_colors, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_realms_window_frames_store_colors (ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
|
||||||
|
GPtrArray *entries = g_ptr_array_new_with_free_func(g_free);
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, frames->realm_frame_colors);
|
||||||
|
|
||||||
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||||
|
gchar *name = key;
|
||||||
|
GdkRGBA *rgba = value;
|
||||||
|
char *rgba_str = gdk_rgba_to_string (rgba);
|
||||||
|
gchar *entry = g_strconcat (name, ":", rgba_str, NULL);
|
||||||
|
g_ptr_array_add (entries, entry);
|
||||||
|
g_free (rgba_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ptr_array_sort (entries, (GCompareFunc) g_strcmp0);
|
||||||
|
g_ptr_array_add (entries, NULL);
|
||||||
|
g_settings_set_strv (frames->settings, REALM_FRAME_COLORS_KEY, (const gchar * const *) entries->pdata);
|
||||||
|
|
||||||
|
g_ptr_array_unref (entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
shell_realms_window_frames_realm_name_for_window (ShellRealmsWindowFrames *frames, ShellRealms *realms, MetaWindow *window)
|
||||||
|
{
|
||||||
|
|
||||||
|
ShellRealmItem *realm = shell_realms_realm_by_window (realms, window);
|
||||||
|
|
||||||
|
if (realm) {
|
||||||
|
return shell_realm_item_get_realm_name (realm);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_window_unmanaged (MetaWindow *window, ShellRealmsWindowFrames *frames)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_window_unmanaged), frames);
|
||||||
|
guint32 id = meta_window_get_stable_sequence (window);
|
||||||
|
remove_from_disabled_list (frames, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_ignored_window (MetaWindow *window)
|
||||||
|
{
|
||||||
|
switch (meta_window_get_window_type (window)) {
|
||||||
|
case META_WINDOW_MENU:
|
||||||
|
case META_WINDOW_TOOLTIP:
|
||||||
|
case META_WINDOW_POPUP_MENU:
|
||||||
|
case META_WINDOW_DROPDOWN_MENU:
|
||||||
|
return TRUE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames_is_frame_enabled:
|
||||||
|
* @frames: a #ShellRealmsWindowFrames instance
|
||||||
|
* @window: a #MetaWindow
|
||||||
|
*
|
||||||
|
* Return #TRUE if frame has not been disabled for this window.
|
||||||
|
*
|
||||||
|
* Returns: #TRUE if frame has not been disabled for this window.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
shell_realms_window_frames_is_frame_enabled (ShellRealmsWindowFrames *frames, MetaWindow *window)
|
||||||
|
{
|
||||||
|
guint32 id = meta_window_get_stable_sequence (window);
|
||||||
|
return !disabled_list_contains (frames, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames_has_frame:
|
||||||
|
* @frames: a #ShellRealmsWindowFrames instance
|
||||||
|
* @window: a #MetaWindow
|
||||||
|
*
|
||||||
|
* Return #TRUE if this window needs a frame.
|
||||||
|
*
|
||||||
|
* Returns: #TRUE if a frame should be drawn for this window.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
shell_realms_window_frames_has_frame (ShellRealmsWindowFrames *frames, MetaWindow *window)
|
||||||
|
{
|
||||||
|
return !is_ignored_window(window) && meta_window_is_on_foreign_workspace_context (window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames_set_frame_enabled:
|
||||||
|
* @frames: a #ShellRealmsWindowFrames instance
|
||||||
|
* @window: a #MetaWindow
|
||||||
|
* @enabled: Set to #FALSE to disable drawing frame for this window
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
shell_realms_window_frames_set_frame_enabled (ShellRealmsWindowFrames *frames, MetaWindow *window, gboolean enabled)
|
||||||
|
{
|
||||||
|
guint32 id = meta_window_get_stable_sequence (window);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
if (remove_from_disabled_list (frames, id)) {
|
||||||
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_window_unmanaged), frames);
|
||||||
|
}
|
||||||
|
} else if (add_to_disabled_list (frames, id)) {
|
||||||
|
g_signal_connect_object (window, "unmanaged", G_CALLBACK(on_window_unmanaged), frames, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ClutterColor *
|
||||||
|
rgba_to_clutter_color(GdkRGBA *rgba)
|
||||||
|
{
|
||||||
|
guint8 r = (guint8) (0.5 + CLAMP(rgba->red, 0.0, 1.0) * 255.0);
|
||||||
|
guint8 g = (guint8) (0.5 + CLAMP(rgba->green, 0.0, 1.0) * 255.0);
|
||||||
|
guint8 b = (guint8) (0.5 + CLAMP(rgba->blue, 0.0, 1.0) * 255.0);
|
||||||
|
|
||||||
|
return clutter_color_new (r, g, b, REALM_FRAME_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames_color_for_window:
|
||||||
|
* @frames: a #ShellRealmsWindowFrames instance
|
||||||
|
* @window: a #MetaWindow
|
||||||
|
*
|
||||||
|
* Returns a color to use for painting window frame.
|
||||||
|
*
|
||||||
|
* Return value: (transfer full) (nullable): The frame color or %NULL if no frame should be drawn.
|
||||||
|
*/
|
||||||
|
ClutterColor *
|
||||||
|
shell_realms_window_frames_color_for_window (ShellRealmsWindowFrames *frames, MetaWindow *window)
|
||||||
|
{
|
||||||
|
if (!shell_realms_window_frames_has_frame (frames, window)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellRealms *realms = shell_realms_get_default();
|
||||||
|
|
||||||
|
const gchar *name = shell_realms_window_frames_realm_name_for_window (frames, realms, window);
|
||||||
|
|
||||||
|
GdkRGBA *rgba = g_hash_table_lookup (frames->realm_frame_colors, name);
|
||||||
|
|
||||||
|
if (!rgba) {
|
||||||
|
rgba = allocate_color (frames);
|
||||||
|
g_hash_table_insert (frames->realm_frame_colors, g_strdup(name), rgba);
|
||||||
|
shell_realms_window_frames_store_colors (frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgba_to_clutter_color (rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames_label_for_window:
|
||||||
|
* @frames: a #ShellRealmsWindowFrames instance
|
||||||
|
* @window: a #MetaWindow
|
||||||
|
*
|
||||||
|
* Return the label text for window if the window requires a frame.
|
||||||
|
*
|
||||||
|
* Return value: (transfer none) (nullable): The label text or %NULL if no label should be displayed
|
||||||
|
*/
|
||||||
|
const gchar *
|
||||||
|
shell_realms_window_frames_label_for_window (ShellRealmsWindowFrames *frames, MetaWindow *window)
|
||||||
|
{
|
||||||
|
if (!shell_realms_window_frames_has_frame (frames, window)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (meta_window_get_window_type (window) != META_WINDOW_NORMAL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellRealms *realms = shell_realms_get_default();
|
||||||
|
|
||||||
|
if (shell_realms_is_citadel_window (realms, window)) {
|
||||||
|
return "Citadel";
|
||||||
|
} else {
|
||||||
|
return shell_realms_window_frames_realm_name_for_window (frames, realms, window);
|
||||||
|
}
|
||||||
|
}
|
17
src/shell-realms-window-frames.h
Normal file
17
src/shell-realms-window-frames.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __SHELL_REALMS_WINDOW_FRAMES_H__
|
||||||
|
#define __SHELL_REALMS_WINDOW_FRAMES_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
#include <meta/window.h>
|
||||||
|
|
||||||
|
#define SHELL_TYPE_REALMS_WINDOW_FRAMES (shell_realms_window_frames_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (ShellRealmsWindowFrames, shell_realms_window_frames, SHELL, REALMS_WINDOW_FRAMES, GObject)
|
||||||
|
|
||||||
|
gboolean shell_realms_window_frames_has_frame (ShellRealmsWindowFrames *frames, MetaWindow *window);
|
||||||
|
gboolean shell_realms_window_frames_is_frame_enabled (ShellRealmsWindowFrames *frames, MetaWindow *window);
|
||||||
|
void shell_realms_window_frames_set_frame_enabled (ShellRealmsWindowFrames *frames, MetaWindow *window, gboolean enabled);
|
||||||
|
ClutterColor *shell_realms_window_frames_color_for_window (ShellRealmsWindowFrames *frames, MetaWindow *window);
|
||||||
|
const gchar *shell_realms_window_frames_label_for_window (ShellRealmsWindowFrames *frames, MetaWindow *window);
|
||||||
|
|
||||||
|
#endif // __SHELL_REALMS_WINDOW_FRAMES_H__
|
460
src/shell-realms.c
Normal file
460
src/shell-realms.c
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
|
||||||
|
#include <meta/display.h>
|
||||||
|
#include <meta/meta-workspace-manager.h>
|
||||||
|
#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;
|
||||||
|
ShellRealmsWindowFrames *frames;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_realms_window_frames:
|
||||||
|
* @realms: the #ShellRealms instance
|
||||||
|
*
|
||||||
|
* Returns the window frames manager.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a #ShellRealmsWindowFrames instance
|
||||||
|
*/
|
||||||
|
ShellRealmsWindowFrames *
|
||||||
|
shell_realms_window_frames (ShellRealms *realms)
|
||||||
|
{
|
||||||
|
return realms->frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (item && 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;
|
||||||
|
realms->frames = g_object_new (SHELL_TYPE_REALMS_WINDOW_FRAMES, 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_unref (realms->frames);
|
||||||
|
realms->frames = NULL;
|
||||||
|
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));
|
||||||
|
}
|
26
src/shell-realms.h
Normal file
26
src/shell-realms.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef __SHELL_REALMS_H__
|
||||||
|
#define __SHELL_REALMS_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <meta/window.h>
|
||||||
|
#include "shell-realm-item.h"
|
||||||
|
#include "shell-realms-window-frames.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);
|
||||||
|
|
||||||
|
ShellRealmsWindowFrames *shell_realms_window_frames (ShellRealms *realms);
|
||||||
|
|
||||||
|
GList *shell_realms_get_running_realms (ShellRealms *realms);
|
||||||
|
GList *shell_realms_get_all_realms (ShellRealms *realms);
|
||||||
|
|
||||||
|
#endif //__SHELL_REALMS_H__
|
@ -15,6 +15,7 @@
|
|||||||
#include "shell-window-tracker-private.h"
|
#include "shell-window-tracker-private.h"
|
||||||
#include "shell-app-private.h"
|
#include "shell-app-private.h"
|
||||||
#include "shell-global.h"
|
#include "shell-global.h"
|
||||||
|
#include "shell-realms.h"
|
||||||
#include "st.h"
|
#include "st.h"
|
||||||
|
|
||||||
/* This file includes modified code from
|
/* This file includes modified code from
|
||||||
@ -135,6 +136,22 @@ check_app_id_prefix (ShellApp *app,
|
|||||||
return g_str_has_prefix (shell_app_get_id (app), prefix);
|
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:
|
* get_app_from_window_wmclass:
|
||||||
*
|
*
|
||||||
@ -152,6 +169,7 @@ get_app_from_window_wmclass (MetaWindow *window)
|
|||||||
const char *wm_class;
|
const char *wm_class;
|
||||||
const char *wm_instance;
|
const char *wm_instance;
|
||||||
const char *sandbox_id;
|
const char *sandbox_id;
|
||||||
|
const char *realm_name;
|
||||||
g_autofree char *app_prefix = NULL;
|
g_autofree char *app_prefix = NULL;
|
||||||
|
|
||||||
appsys = shell_app_system_get_default ();
|
appsys = shell_app_system_get_default ();
|
||||||
@ -160,6 +178,8 @@ get_app_from_window_wmclass (MetaWindow *window)
|
|||||||
if (sandbox_id)
|
if (sandbox_id)
|
||||||
app_prefix = g_strdup_printf ("%s.", sandbox_id);
|
app_prefix = g_strdup_printf ("%s.", sandbox_id);
|
||||||
|
|
||||||
|
realm_name = get_window_realm_name (window);
|
||||||
|
|
||||||
/* Notes on the heuristics used here:
|
/* Notes on the heuristics used here:
|
||||||
much of the complexity here comes from the desire to support
|
much of the complexity here comes from the desire to support
|
||||||
Chrome apps.
|
Chrome apps.
|
||||||
@ -197,23 +217,23 @@ get_app_from_window_wmclass (MetaWindow *window)
|
|||||||
|
|
||||||
/* first try a match from WM_CLASS (instance part) to StartupWMClass */
|
/* first try a match from WM_CLASS (instance part) to StartupWMClass */
|
||||||
wm_instance = meta_window_get_wm_class_instance (window);
|
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))
|
if (app != NULL && check_app_id_prefix (app, app_prefix))
|
||||||
return g_object_ref (app);
|
return g_object_ref (app);
|
||||||
|
|
||||||
/* then try a match from WM_CLASS to StartupWMClass */
|
/* then try a match from WM_CLASS to StartupWMClass */
|
||||||
wm_class = meta_window_get_wm_class (window);
|
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))
|
if (app != NULL && check_app_id_prefix (app, app_prefix))
|
||||||
return g_object_ref (app);
|
return g_object_ref (app);
|
||||||
|
|
||||||
/* then try a match from WM_CLASS (instance part) to .desktop */
|
/* 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))
|
if (app != NULL && check_app_id_prefix (app, app_prefix))
|
||||||
return g_object_ref (app);
|
return g_object_ref (app);
|
||||||
|
|
||||||
/* finally, try a match from WM_CLASS to .desktop */
|
/* 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))
|
if (app != NULL && check_app_id_prefix (app, app_prefix))
|
||||||
return g_object_ref (app);
|
return g_object_ref (app);
|
||||||
|
|
||||||
@ -242,7 +262,19 @@ get_app_from_id (MetaWindow *window,
|
|||||||
|
|
||||||
appsys = shell_app_system_get_default ();
|
appsys = shell_app_system_get_default ();
|
||||||
|
|
||||||
|
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);
|
desktop_file = g_strconcat (id, ".desktop", NULL);
|
||||||
|
}
|
||||||
app = shell_app_system_lookup_app (appsys, desktop_file);
|
app = shell_app_system_lookup_app (appsys, desktop_file);
|
||||||
if (app)
|
if (app)
|
||||||
return g_object_ref (app);
|
return g_object_ref (app);
|
||||||
|
@ -9,7 +9,7 @@ endif
|
|||||||
launcherconf.set('prefix', prefix)
|
launcherconf.set('prefix', prefix)
|
||||||
launcherconf.set('libdir', libdir)
|
launcherconf.set('libdir', libdir)
|
||||||
launcherconf.set('pkgdatadir', pkgdatadir)
|
launcherconf.set('pkgdatadir', pkgdatadir)
|
||||||
launcherconf.set('gjs', gjs.full_path())
|
launcherconf.set('gjs', gjs)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
input: prgname + '.in',
|
input: prgname + '.in',
|
||||||
|
@ -44,7 +44,7 @@ localedir = join_paths(datadir, 'locale')
|
|||||||
metainfodir = join_paths(datadir, 'metainfo')
|
metainfodir = join_paths(datadir, 'metainfo')
|
||||||
servicedir = join_paths(datadir, 'dbus-1', 'services')
|
servicedir = join_paths(datadir, 'dbus-1', 'services')
|
||||||
|
|
||||||
gjs = find_program('gjs')
|
gjs = '/usr/bin/gjs'
|
||||||
appstream_util = find_program('appstream-util', required: false)
|
appstream_util = find_program('appstream-util', required: false)
|
||||||
desktop_file_validate = find_program('desktop-file-validate', required: false)
|
desktop_file_validate = find_program('desktop-file-validate', required: false)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user