After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 689304 - Add support for external session modes
Add support for external session modes
Status: RESOLVED FIXED
Product: gnome-shell
Classification: Core
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: gnome-shell-maint
gnome-shell-maint
Depends on:
Blocks: 685744
 
 
Reported: 2012-11-29 18:59 UTC by Florian Müllner
Modified: 2012-12-05 21:02 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
sessionMode: Add support for external mode definitions (4.17 KB, patch)
2012-11-29 18:59 UTC, Florian Müllner
reviewed Details | Review
overview: Handle sessionMode.hasOverview changes (1.78 KB, patch)
2012-11-29 18:59 UTC, Florian Müllner
committed Details | Review
main: Stop setting custom keybinding handlers conditionally (3.05 KB, patch)
2012-11-29 18:59 UTC, Florian Müllner
reviewed Details | Review
sessionMode: Make loading of external modes asynchronous (6.34 KB, patch)
2012-11-29 19:00 UTC, Florian Müllner
accepted-commit_now Details | Review
sessionMode: Add support for external mode definitions (4.10 KB, patch)
2012-11-29 19:52 UTC, Florian Müllner
none Details | Review
sessionMode: Make loading of external modes asynchronous (6.15 KB, patch)
2012-11-29 19:53 UTC, Florian Müllner
none Details | Review
sessionMode: Add support for external mode definitions (3.92 KB, patch)
2012-11-29 21:01 UTC, Florian Müllner
committed Details | Review
remoteSearch: Factor out collectFromDatadirsAsync() utility function (8.24 KB, patch)
2012-11-30 18:42 UTC, Florian Müllner
needs-work Details | Review
extensionUtils: Load extensions asynchronously (4.22 KB, patch)
2012-11-30 18:43 UTC, Florian Müllner
needs-work Details | Review
sessionMode: Make loading of external modes asynchronous (4.70 KB, patch)
2012-11-30 18:43 UTC, Florian Müllner
none Details | Review
remoteSearch: Factor out collectFromDatadirsAsync() utility function (8.63 KB, patch)
2012-11-30 21:51 UTC, Florian Müllner
committed Details | Review
extensionUtils: Load extensions asynchronously (4.17 KB, patch)
2012-11-30 21:51 UTC, Florian Müllner
committed Details | Review
sessionMode: Make loading of external modes asynchronous (4.75 KB, patch)
2012-11-30 21:51 UTC, Florian Müllner
committed Details | Review

Description Florian Müllner 2012-11-29 18:59:47 UTC
Administrators may want to define customized session modes, e.g. for kiosk modes. To allow this without requiring to patch the source, add the ability to load external mode definitions from $(pkgdatadir)/modes. The same mechanism could also be used by modules that require a special shell mode, like initial-setup and the 3.8 fallback replacement.
Comment 1 Florian Müllner 2012-11-29 18:59:50 UTC
Created attachment 230226 [details] [review]
sessionMode: Add support for external mode definitions

Currently adding a new session mode requires patching the sources.
As defining custom modes can be desirable in some circumstances
(for instance for administrators of kiosk setups), load additional
modes from JSON files.
Comment 2 Florian Müllner 2012-11-29 18:59:54 UTC
Created attachment 230227 [details] [review]
overview: Handle sessionMode.hasOverview changes

Currently we assume that either the initial sessionMode will have
the overview or none of the pushed modes - starting without the
overview and pushing a mode that adds it fails spectacularly.
However this is exactly what we are going to do when loading external
modes asynchronously - we'll initially use the default mode while
the modes are loading, and switch to the mode passed on the command
line when finished. So make sure that the overview UI gets initialized
properly in that case.
Comment 3 Florian Müllner 2012-11-29 18:59:57 UTC
Created attachment 230228 [details] [review]
main: Stop setting custom keybinding handlers conditionally

Currently we call wm.setCustomKeybindingHandler() on session mode
changes for the panel-menu and run-dialog keybindings, passing
either the proper callback or null depending on the corresponding
session mode properties.
However as meta_keybindings_set_custom_handler() only allows to
replace existing handlers, setting it to NULL will disable the
binding permanently. To fix, set the custom handler unconditionally
and move the check for the session mode property into the handler.
Comment 4 Florian Müllner 2012-11-29 19:00:00 UTC
Created attachment 230229 [details] [review]
sessionMode: Make loading of external modes asynchronous

Blocking IO on startup is bad m'kay ...
Comment 5 Jasper St. Pierre (not reading bugmail) 2012-11-29 19:45:43 UTC
Review of attachment 230226 [details] [review]:

I thought about doing this, but there's things (like UnlockDialog or createSession) which aren't JSON values.

::: js/ui/sessionMode.js
@@ +159,3 @@
+                let evalProps = ['unlockDialog'];
+                if (evalProps.indexOf(prop) > -1)
+                    modes[modeName][prop] = eval(newMode[prop]);

No. I'd prefer a dispatch table of unlock dialogs, or not allowing to specify this in the mode.
Comment 6 Jasper St. Pierre (not reading bugmail) 2012-11-29 19:46:26 UTC
Review of attachment 230227 [details] [review]:

OK.
Comment 7 Jasper St. Pierre (not reading bugmail) 2012-11-29 19:48:39 UTC
Review of attachment 230228 [details] [review]:

I assume "disable the binding permanently" means "disable the binding until the next time set_custom_handler is called", in which case, wouldn't the session updated do the right thing currently? If that's not the case, that's a mutter bug.
Comment 8 Jasper St. Pierre (not reading bugmail) 2012-11-29 19:51:11 UTC
Review of attachment 230229 [details] [review]:

::: js/ui/sessionMode.js
@@ +111,3 @@
+    let loadState = { loadedModes: _modes,
+                      numLoading: 0,
+                      modesLoadedCallback: modesLoadedCallback };

Hello, another case for Deferred/gatherResults.

@@ +154,1 @@
+            loadState.loadedModes[modeName] = {};

Just want to make sure you're aware of the race condition here where the values can be random if we load two files with the same mode name from two separate dirs.
Comment 9 Florian Müllner 2012-11-29 19:52:38 UTC
Created attachment 230232 [details] [review]
sessionMode: Add support for external mode definitions

Minor cleanup
Comment 10 Florian Müllner 2012-11-29 19:53:14 UTC
Created attachment 230233 [details] [review]
sessionMode: Make loading of external modes asynchronous

Minor fix.
Comment 11 Florian Müllner 2012-11-29 19:57:15 UTC
(In reply to comment #7)
> Review of attachment 230228 [details] [review]:
> 
> I assume "disable the binding permanently" means "disable the binding until the
> next time set_custom_handler is called"

No. As explained in the commit message, meta_keybindings_set_custom_handler() only allows replacing *existing* handlers. Which translates to a check for NULL in C, so setting the handler to NULL removes it completely.
Comment 12 Jasper St. Pierre (not reading bugmail) 2012-11-29 20:01:18 UTC
(In reply to comment #11)
> No. As explained in the commit message, meta_keybindings_set_custom_handler()
> only allows replacing *existing* handlers. Which translates to a check for NULL
> in C, so setting the handler to NULL removes it completely.

Yeah, I think this is a case of a bad API that we should fix in mutter. I can imagine this tripping up people with extensions, too.
Comment 13 Florian Müllner 2012-11-29 20:30:39 UTC
Comment on attachment 230228 [details] [review]
main: Stop setting custom keybinding handlers conditionally

OK, found the real bug, pushed as 5ecc204d570140 ...
Comment 14 Florian Müllner 2012-11-29 20:31:42 UTC
Comment on attachment 230227 [details] [review]
overview: Handle sessionMode.hasOverview changes

Attachment 230227 [details] pushed as e00eb06 - overview: Handle sessionMode.hasOverview changes
Comment 15 Florian Müllner 2012-11-29 21:01:39 UTC
Created attachment 230237 [details] [review]
sessionMode: Add support for external mode definitions

(In reply to comment #5)
> No. I'd prefer [...] not allowing to specify this in the mode.

Yeah, I had considered that before, but that was before I wrote the parentMode patch, so I needed some way to support that property for a mode similar to the normal user one.
Now that we can just inherit from the user mode, let's go with the simple backlist approach ...
Comment 16 Jasper St. Pierre (not reading bugmail) 2012-11-29 21:03:53 UTC
Review of attachment 230237 [details] [review]:

Sure.
Comment 17 Florian Müllner 2012-11-30 18:42:51 UTC
Created attachment 230325 [details] [review]
remoteSearch: Factor out collectFromDatadirsAsync() utility function

Processing files from a subdirectory of various share/gnome-shell
directories asynchronously is a common enough pattern to share
the common code.
Comment 18 Florian Müllner 2012-11-30 18:43:00 UTC
Created attachment 230326 [details] [review]
extensionUtils: Load extensions asynchronously

We shouldn't use blocking I/O on session start, so make extension
loading asynchronous using the new collectFromDatadirsAsync()
function.
Comment 19 Florian Müllner 2012-11-30 18:43:06 UTC
Created attachment 230327 [details] [review]
sessionMode: Make loading of external modes asynchronous

Blocking IO on startup is bad m'kay ...
Comment 20 Jasper St. Pierre (not reading bugmail) 2012-11-30 19:25:03 UTC
Review of attachment 230325 [details] [review]:

::: js/misc/fileUtils.js
@@ +49,3 @@
+}
+
+function collectFromDatadirsAsync(subdir, processFile, params) {

Seems weird to pass one callback as a parameter, and the other in params. Perhaps we should just use params entirely?

::: js/ui/remoteSearch.js
@@ +36,3 @@
+        loadRemoteSearchProvider,
+        { loadedCallback: remoteProvidersLoaded,
+          data: { loadedProviders: [],

let data = { loadedProviders: [],

...

{ loadedCallback: remoteProvidersLoaded,
  data: data }

@@ -45,3 @@
-        dir.query_info_async('standard:type', Gio.FileQueryInfoFlags.NONE,
-            GLib.PRIORITY_DEFAULT, null,
-                function(object, res) {

Indentation is a bit wacky. Wrap the function on the last line.
Comment 21 Jasper St. Pierre (not reading bugmail) 2012-11-30 19:26:36 UTC
Review of attachment 230326 [details] [review]:

::: js/misc/extensionUtils.js
@@ +180,3 @@
+                                           Lang.bind(this, this._loadExtension),
+                                           { includeUserdir: true,
+                                             data: extensions });

data isn't used. Remove.
Comment 22 Jasper St. Pierre (not reading bugmail) 2012-11-30 19:27:42 UTC
Review of attachment 230325 [details] [review]:

::: js/misc/fileUtils.js
@@ +29,3 @@
+    dir.query_info_async('standard:type', Gio.FileQueryInfoFlags.NONE,
+        GLib.PRIORITY_DEFAULT, null,
+            function(object, res) {

And I meant to put the indentation comment here, even though it's existing.

@@ +32,3 @@
+                try {
+                    object.query_info_finish(res);
+                } catch (e) {

Oh, we should probably test that the error is actually something we care about rather than drop all errors on the floor.
Comment 23 Florian Müllner 2012-11-30 21:01:58 UTC
(In reply to comment #20)
> Seems weird to pass one callback as a parameter, and the other in params.
> Perhaps we should just use params entirely?

The idea is to use params for optional parameters - not running a callback after the entire operation has finished seems perfectly valid, iterating over all files in a couple of directories to not do anything does not.
Comment 24 Florian Müllner 2012-11-30 21:08:13 UTC
(In reply to comment #22)
> Oh, we should probably test that the error is actually something we care about
> rather than drop all errors on the floor.

I don't think aborting on any error is "dropping all errors on the floor". Any particular errors you suggest to ignore?
Comment 25 Jasper St. Pierre (not reading bugmail) 2012-11-30 21:15:12 UTC
(In reply to comment #24)
> I don't think aborting on any error is "dropping all errors on the floor". Any
> particular errors you suggest to ignore?

Logging about Permissions Denied or other errors may help administrators investigate why their modes aren't being used.

(In reply to comment #23)
> The idea is to use params for optional parameters - not running a callback
> after the entire operation has finished seems perfectly valid, iterating over
> all files in a couple of directories to not do anything does not.

We use Params for required params elsewhere, so I don't see why this should be anything different.
Comment 26 Florian Müllner 2012-11-30 21:20:06 UTC
(In reply to comment #25)
> We use Params for required params elsewhere, so I don't see why this should be
> anything different.

I'd consider that a bug if we don't (or can't) provide a meaningful default value at the same time.
Comment 27 Florian Müllner 2012-11-30 21:51:14 UTC
Created attachment 230346 [details] [review]
remoteSearch: Factor out collectFromDatadirsAsync() utility function

I still think processFile doesn't make sense as optional parameter, but whatever ...
Comment 28 Florian Müllner 2012-11-30 21:51:25 UTC
Created attachment 230347 [details] [review]
extensionUtils: Load extensions asynchronously
Comment 29 Florian Müllner 2012-11-30 21:51:33 UTC
Created attachment 230348 [details] [review]
sessionMode: Make loading of external modes asynchronous

Blocking IO on startup is bad m'kay ...
Comment 30 Bastien Nocera 2012-12-01 13:51:59 UTC
Can the GDM mode be implemented in the same way?
Comment 31 Florian Müllner 2012-12-02 18:02:59 UTC
(In reply to comment #30)
> Can the GDM mode be implemented in the same way?

It'd require a bit of code separation, but generally it shouldn't be too hard to use an external mode + extension and move it to the gdm module. I don't mind having support for a part of the core experience like the login screen in gnome-shell core, but maybe you are right and the move makes sense anyway ...
Comment 32 Jasper St. Pierre (not reading bugmail) 2012-12-02 19:14:06 UTC
Extension? No.

I'd say that if anything, we should have a separate binary for "gdm" with a shared "base compositor" library, which contains most of the js/ui/ stuff. This is why I've been wanting to reorg the js/ directories so bad.
Comment 33 Florian Müllner 2012-12-02 19:19:36 UTC
(In reply to comment #32)
> I'd say that if anything, we should have a separate binary for "gdm" with a
> shared "base compositor" library, which contains most of the js/ui/ stuff.

But gdm and normal shell differ only in js - so how does that make any sense?
Comment 34 Jasper St. Pierre (not reading bugmail) 2012-12-02 19:45:32 UTC
(In reply to comment #33)
> But gdm and normal shell differ only in js - so how does that make any sense?

Stuff like ShellEntry and ModalDialog should be shared, but AppDisplay is clearly shell-only. I'm imagining that those shared components go into a "Widgets" library that can be used by gdm.
Comment 35 Jasper St. Pierre (not reading bugmail) 2012-12-02 20:05:27 UTC
Review of attachment 230346 [details] [review]:

::: js/misc/fileUtils.js
@@ +76,3 @@
+    };
+
+    if (params.includeUserdir) {

This seems like it could introduce capitalization typos: 'includeUserDir'. 'includeUser' or 'includeHome' might be better.

There's also the question of whether the scan for homedir should go before or after system dirs. I don't think we need this flexibility quite yet.

@@ +78,3 @@
+    if (params.includeUserdir) {
+        let path = GLib.build_filenamev([global.userdatadir, subdir]);
+        let dir = Gio.file_new_for_path(path);

Gio.File.new_for_path; functions on interfaces are deprecated and may be removed in the future.

@@ +81,3 @@
+
+        loadState.loadedCallback = collectFromSystemDirs;
+        _collectFromDirectoryAsync(dir, loadState);

eek. I'd prefer a "collectFromDirs", and we do:

    let dirs = GLib.get_system_data_dirs();
    if (params.includeWhatever) {
        dirs.unshift(dir);
    }

    collectFromDirsAsync(dirs);

That would remove the ugly munging of the loadState callback, too, and we can just set it directly.
Comment 36 Jasper St. Pierre (not reading bugmail) 2012-12-02 20:13:22 UTC
Review of attachment 230347 [details] [review]:

This won't help that much, considering that loading the extension script itself will still use blocking IO, and that the extensions will execute synchronously as well.

It's a nice commit, but with an extremely misleading commit message. I don't think we can have real asynchronous extension loading until we get better JS APIs, which will only happen when (if???) js187 comes out.

::: js/misc/extensionUtils.js
@@ +164,3 @@
 
+        let extension;
+        let perUserDir = Gio.File.new_for_path(global.userdatadir);

I wonder if we can save this file on the state to prevent recreating it. Oh well, microoptimization.
Comment 37 Florian Müllner 2012-12-02 20:16:54 UTC
(In reply to comment #35)
> eek. I'd prefer a "collectFromDirs", and we do:
> 
>     let dirs = GLib.get_system_data_dirs();
>     if (params.includeWhatever) {
>         dirs.unshift(dir);
>     }
> 
>     collectFromDirsAsync(dirs);
> 
> That would remove the ugly munging of the loadState callback, too, and we can
> just set it directly.

That's more or less what I had first, but I rewrote it because I assumed that the extension system guarantees that extensions in home take precedence over extensions in system directories. If you are OK with possible randomness there, I'm fine with changing the code back.
Comment 38 Jasper St. Pierre (not reading bugmail) 2012-12-02 20:17:31 UTC
Review of attachment 230348 [details] [review]:

Fine by me.
Comment 39 Florian Müllner 2012-12-02 20:34:17 UTC
(In reply to comment #34)
> Stuff like ShellEntry and ModalDialog should be shared, but AppDisplay is
> clearly shell-only. I'm imagining that those shared components go into a
> "Widgets" library that can be used by gdm.

I'm just not sure any advantage of doing that outweighs the necessary duplication (at least large parts of main.c and main.js) and justifies moving the shell towards the bag-of-bits-build-your-own-on-top platform.
Comment 40 Florian Müllner 2012-12-04 19:00:11 UTC
Any comments on comment #37?
Comment 41 Jasper St. Pierre (not reading bugmail) 2012-12-04 19:22:04 UTC
(In reply to comment #37)
> That's more or less what I had first, but I rewrote it because I assumed that
> the extension system guarantees that extensions in home take precedence over
> extensions in system directories. If you are OK with possible randomness there,
> I'm fine with changing the code back.

I'm fine with it. It's not ideal, but when we fix it we should fix it for all users, not just home vs. system.
Comment 42 Florian Müllner 2012-12-05 21:02:35 UTC
Attachment 230237 [details] pushed as 8a17f51 - sessionMode: Add support for external mode definitions
Attachment 230346 [details] pushed as c528401 - remoteSearch: Factor out collectFromDatadirsAsync() utility function
Attachment 230347 [details] pushed as 6b40c39 - extensionUtils: Load extensions asynchronously
Attachment 230348 [details] pushed as 92083ea - sessionMode: Make loading of external modes asynchronous

(In reply to comment #36)
> It's a nice commit, but with an extremely misleading commit message.

OK, changed to something less promising.


> ::: js/misc/extensionUtils.js
> I wonder if we can save this file on the state to prevent recreating it. Oh
> well, microoptimization.

Yeah, did that as well


(In reply to comment #41)
> I'm fine with it. It's not ideal, but when we fix it we should fix it for all
> users, not just home vs. system.

OK, I'll add this to the nice-to-fix-later list then :-)