Compare commits

...

282 Commits

Author SHA1 Message Date
Milan Crha
c00d79bae2 calendar-server: Improve performance by properly using ECalClientView
The previous code always restarted whole ECalClientView when it received
any changes in it, which could sometimes lead to constant repeated restarts
of the view.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-04-27 16:14:57 +00:00
Florian Müllner
30d902f898 calendar-server: Drop separate private struct
CalendarSources is a final type, so the regular instance struct is
already non-public. No need for a separate private struct and priv
pointer ...

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-04-27 16:14:57 +00:00
Florian Müllner
8f9da6f801 calendar-server: Add missing spaces
... according to coding style.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-04-27 16:14:57 +00:00
Florian Müllner
20648e9207 calendar-server: Replace tabs with spaces
... according to the coding style.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-04-27 16:14:57 +00:00
Daniel Mustieles
614fe202e0 Updated Spanish translation 2020-04-27 18:14:01 +02:00
Jonas Ådahl
c3646a7642 js/main: Inhibit remote access when depending on session mode
The session mode determines whether the screen casting should work or
not, but until now only dealt with the built in screen cast, not the
ones using PipeWire. Add the newly added API for inhibiting remote
access when the session mode says screencasts are not allowed.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1210
2020-04-27 15:18:18 +00:00
Jonas Ådahl
772df91762 shell/global: Add 'backend' property
Replace all Meta.get_backend() with global.backend. Maybe sooner or
later we can further decrease the amount of singletons handled by
libmutter.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1210
2020-04-27 15:18:18 +00:00
qarmin
c90910731f croco: Remove duplicated conditions
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1219
2020-04-27 16:23:46 +02:00
Florian Müllner
ba69cd99d1 ci: Simplify flatpak job
The template has been updated to rewrite the manifest to use the
checked out tree, so it's no longer necessary to pass additional
build arguments (which are now ignored) or generate translations
before the build (it's already in the manifest).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1217
2020-04-27 13:16:55 +00:00
Florian Müllner
fb6e341efd Revert "ci: Revert to a previous version of the flatpak template"
This reverts commit 1ff638a51f.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1217
2020-04-27 13:16:55 +00:00
Andy Holmes
3dc4f01113 JS: migrate from the global window to globalThis
As of mozjs68 (gjs-1.64) `globalThis` is recommended over `window` and
it makes more sense in this context anyways. Migrate the few instances
of `window` we use and adjust the eslint configuration.

`window` will continue to resolve to `globalThis`, so this won't affect
extensions or other downstream users.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2322

closes #2322
2020-04-26 19:07:02 -07:00
Florian Müllner
d94d0f60c8 calendar: Do less work in hasEvents()
getEvents() filters all events for the given range and sorts the result.

That's more than we need when checking whether there are any events,
where we only care that there's at least one event in the range.

Address this by splitting out the event filtering into a generator
function, so hasEvents() can return after at most one iteration.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1192
2020-04-26 16:20:49 +00:00
Florian Müllner
8d79f6f4c8 calendar: Update events on changes
We track messages so that we can account for just added and removed
events instead of having to rebuild the entire list, however it's
also possible that the time or summary of an existing event changed.

Account for that by updating existing messages in-place.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1192
2020-04-26 16:20:49 +00:00
Asier Sarasua Garmendia
c5e5bb0be1 Update Basque translation 2020-04-25 17:21:35 +00:00
Jonas Dreßler
8d139bbd95 authPrompt: Grab key focus when making entry sensitive
We currently let the entry of the autoPrompt grab the key focus inside
setQuestion(), which is called from _onAskQuestion(), which is the
callback of the "ask-question" signal.

It seems that the "ask-question" signal isn't emitted again right after
the password-check failed, but a few seconds after that. Since we get
the "verification-failed" signal earlier than "ask-question" (right
after we know the check failed) and we also get a hint whether the entry
should be usable again with the canRetry argument, we can also grab key
focus to in the same step.

So do that by grabbing key focus when making the entry sensitive.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2655
2020-04-25 14:54:07 +00:00
Jonas Dreßler
fb1bb291eb unlockDialog: Call AuthPrompt.addCharacter() directly
The additional function UnlockDialog.addCharacter() is only used at one
place, so we can simply remove it and call AuthPrompt.addCharacter()
directly. The AuthPrompt is shown right before that anyway.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1209
2020-04-25 14:54:07 +00:00
Will Thompson
3199620a83 systemActions: include ASCII alternatives in search index
With this change, "eteindre" matches "éteindre" (the French translation
for "power off"), consistent with search for applications where "e"
matches "é".

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2688
2020-04-25 13:55:20 +01:00
Yuri Chornoivan
c88bb66369 Update Ukrainian translation 2020-04-25 08:08:40 +00:00
Matthew Leeds
a60d57ea1f screenShield: Don't inhibit suspend during initial setup
Normally, we inhibit suspend while locking the screen. But in the
session mode used for gnome-initial-setup locking is not supported, so
in that case this inhibit call is pointless and should be avoided.
Without this patch you get the following error when you suspend and
resume during initial setup:

JS ERROR: Error getting systemd inhibitor: Gio.IOErrorEnum:
GDBus.Error:org.freedesktop.login1.OperationInProgress: The operation
inhibition has been requested for is already running
_promisify/proto[asyncFunc]/</<@resource:///org/gnome/gjs/modules/core/overrides/Gio.js:435:45

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1213
2020-04-24 23:47:31 +00:00
Matthew Leeds
527ce66cd4 screenShield: Fix use of null this._dialog
On Fedora 32 if you close the laptop lid during gnome-initial-setup,
gnome-shell hits this error:

JS ERROR: Exception in callback for signal: prepare-for-sleep: TypeError: this._dialog is undefined
_resetLockScreen@resource:///org/gnome/shell/ui/screenShield.js:434:9
activate@resource:///org/gnome/shell/ui/screenShield.js:571:14
lock@resource:///org/gnome/shell/ui/screenShield.js:617:14
_prepareForSleep@resource:///org/gnome/shell/ui/screenShield.js:219:22
_emit@resource:///org/gnome/gjs/modules/core/_signals.js:133:47
_prepareForSleep@resource:///org/gnome/shell/misc/loginManager.js:198:14
_emit@resource:///org/gnome/gjs/modules/core/_signals.js:133:47
_convertToNativeSignal@resource:///org/gnome/gjs/modules/core/overrides/Gio.js:169:19

This is because _ensureUnlockDialog() hit its first early return. So
return early from activate() in that case, so this._dialog doesn't get
used while it's null.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1213
2020-04-24 23:47:31 +00:00
Danial Behzadi
40415a6849 Update Persian translation 2020-04-24 20:06:24 +00:00
Jordan Petridis
1ff638a51f ci: Revert to a previous version of the flatpak template
A new version seems to be causing some issues, so
use the previous revision until it's resolved

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1214
2020-04-24 20:34:32 +02:00
Georges Basile Stavracas Neto
2a9ccf2e2c appDisplay: Return the parent class' result in overrides
StButton returns CLUTTER_EVENT_STOP in various circumstances, but
AppIcon throws that away and returns CLUTTER_EVENT_PROPAGATE even
when it should stop.

Return the parent class' result instead of CLUTTER_EVENT_PROPAGATE.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1211
2020-04-23 19:14:43 -03:00
Georges Basile Stavracas Neto
2909d91c13 appDisplay: Use const instead of let in vfunc_leave_event
The return value of the chain up is not changed, let's use the
proper descriptor.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1211
2020-04-23 19:14:40 -03:00
Jonas Ådahl
0dba12193d st: Update to new API for creating paint context
It now takes a redraw clip and flags.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1207
2020-04-23 14:46:03 +00:00
Petr Kovář
02f40b3b63 Update Czech translation 2020-04-22 13:01:15 +00:00
Rafael Fontenelle
1fd51efc7f Update Brazilian Portuguese translation
(cherry picked from commit 60ee6ab4b7)
2020-04-22 12:33:18 +00:00
Will Thompson
6f881f232e test-theme: Check return value of chdir()
I noticed the following warning while building the .deb package for
gnome-shell on Endless OS:

      ../src/st/test-theme.c: In function ‘main’:
      ../src/st/test-theme.c:549:3: warning: ignoring return value of ‘chdir’, declared with attribute warn_unused_result [-Wunused-result]
         chdir (cwd);
         ^~~~~~~~~~~

(Of course this is very unlikely to fail in practice.)

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1208
2020-04-21 15:00:23 +02:00
Xiaoguang Wang
be12c71534 loginDialog: Get resource scale by get_resource_scale
To shut up 'Getting invalid resource scale property' warnings in the log.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1206
2020-04-21 13:48:56 +02:00
Philip Chimento
6aa1b817c9 messageTray: Make NotificationPolicy properties read-only
These properties are never written; in the base class they are always
their default values, and in the subclasses the getters are overridden.

This will be necessary because GJS is adding checks to make sure that
readable properties always have a getter, writable properties always
have a setter, and that the variations of camelCase/snake_case are
handled correctly. It's supposedly backwards compatible, but that
assumes that code is not doing things like forgetting a setter on a
writable property. (If the missing setter had ever been called, it might
have led to a crash, which is why we've made this change.)

This is the minimally invasive patch which should work with both older
and newer versions of GJS. If you decide to require GJS 1.65.2, then
you'll also be able to remove the getters from NotificationPolicy as
well.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1205
2020-04-20 17:27:48 -07:00
Dingzhong Chen
300961e19e Update zh_CN translations 2020-04-18 09:10:21 +00:00
Florian Müllner
b191e9ef91 authPrompt: Fix spinner alignment
We want the spinner to be centered with regard to the entry, but
constraining the height breaks that:

 1. clutter_actor_allocate() is called with the available size
 2. clutter_actor_update_constraints() then adjusts that according
    to the constraints
 3. clutter_actor_adjust_allocation() applies the margin/expand/align
    properties.

The issue there is that 2. reduces the allocation to the desired size,
so there is no more extra space to distribute in 3.

We can fix this by either constraining everything (and rely on the
cancel button's alignment) or limit the constraint to the width. The
latter seems more appropriate, given that the constraint is only used
to center the entry horizontally.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2628
2020-04-17 22:35:31 +00:00
Florian Müllner
5ec5978d4a windowManager: Fix ngettext() call
We don't pass the number that allows gettext to decide on the
correct string, whoops.

Spotted by Alexandre Franke.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2649
2020-04-17 22:25:09 +00:00
Marco Trevisan (Treviño)
f4d90bc127 polkitAgent: Reset the session request timeout when removing it
When handing the resetDialog request we're leaving a source ID alive,
leading this error:

(gnome-shell:22464): GLib-CRITICAL **: 17:46:11.065: Source ID 12934 was not
found when attempting to remove it:

== Stack trace for context 0x55c9246916c0 ==
#0 55c9249151b8 i   js/ui/components/polkitAgent.js:391 (11f71fd544c0 @ 100)
#1 7ffc55140aa0 b   self-hosted:1009 (3062ba49af88 @ 423)
#2 55c924915120 i   js/ui/modalDialog.js:167 (1c9e50ae9880 @ 62)
#3 55c924915098 i   js/ui/modalDialog.js:186 (1c9e50ae9970 @ 12)
#4 55c924915008 i   js/ui/environment.js:75 (1c9e50a8d5b0 @ 98)
#5 55c924914f78 i   js/ui/environment.js:149 (1c9e50a8d9e8 @ 14)

So, reset the source handle to avoid trying to remove it.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1203
2020-04-17 16:17:51 +00:00
Marco Trevisan (Treviño)
bfa34914db polkitAgent: Return GLib.SOURCE_REMOVE on timeout callback
resetDialog is used (also) as GLib timeout function, so while just returning
an undefined value will stop the timeout anyways, it's just cleaner to
use the GSource expected return values

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1203
2020-04-17 16:17:51 +00:00
Jonas Ådahl
8d1e4659d1 js/ui: Always use namespace for animation modes
Clutter.Animation doesn't contain any animation modes, they live in
Clutter.AnimationMode. The places we did `Clutter.Animation.WHATEVER`
just evaluated to `undefined`. Thus, use the correct namespace for the
animation mode enums.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1199
2020-04-15 13:59:38 +00:00
Jonas Ådahl
b3b91f1699 js: Drop tweener.js
We're using clutter's animation framework now, so lets drop the old
tweener support layer.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1200
2020-04-15 13:25:14 +00:00
Jan Tojnar
ff4c5270d3 build: Obtain systemduserunitdir from pkg-config
Previously, this prevented local installation because systemd did not
make the variable's prefix overridable

https://github.com/systemd/systemd/issues/9595

but since it has been fixed in systemd 242, we can finally use that.

---

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1194
2020-04-13 15:27:09 +00:00
Jan Tojnar
e240f7ea59 build: Install keybindings relative to our datadír
It is a good practice to install files relative to our own variables

https://www.bassi.io/articles/2018/03/15/pkg-config-and-paths/

and it is required on systems like NixOS.

---

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1194
2020-04-13 15:27:09 +00:00
Jan Tojnar
9f1ad5d86d extension-tool/build: Install completions relative to our datadír
It is a good practice to install files relative to our own variables

https://www.bassi.io/articles/2018/03/15/pkg-config-and-paths/

and it is required on systems like NixOS.

Thanks to Iñigo, bash-completion support that since 2.10:

https://github.com/scop/bash-completion/pull/344

---

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1194
2020-04-13 15:27:09 +00:00
Jiri Grönroos
a1ab32af0f Update Finnish translation
(cherry picked from commit cc0fac895c)
2020-04-13 15:03:35 +00:00
Florian Müllner
3fac0632a8 appDisplay: Look up directory- instead of category translations
Translations are provided by .directory files, so trying to look
up a category name without the suffix will always fail.

Commit 343b3351f1 tried to fix this previously by changing the
saved keys, but that broke existing translatable folders.

Appending the .directory suffix for the lookup instead fixes the
issue without regressing non-custom folders.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2623
2020-04-13 14:56:34 +00:00
Florian Müllner
c2b518929d Revert "app-cache: Fix cache for folder translations"
Existing folders as created by gnome-software (including the
default ones) all have the .directory suffix, so dropping
the suffix from the keys broke those folders.

This reverts commit 343b3351f1.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2623
2020-04-13 14:56:34 +00:00
Carmen Bianca BAKKER
c422d82752 Update Esperanto translation 2020-04-11 18:21:21 +00:00
Mariana Picolo
da0c7fc2b6 dateMenu: Update empty weather label
Remove subtitle for the empty weather state
to match world clocks button.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2179
2020-04-09 10:52:44 -03:00
Georges Basile Stavracas Neto
167bc080d9 appDisplay: Reorganize AppDisplay actor hierarchy
Right now, the actor hierarchy is such that the scroll view
does not contain the actual grid. It looks as follows:

              StScrollView
                    ↓
               StBoxLayout
                    ↓
                ShellStack
                ↓        ↓
   PaginatedIconGrid     StWidget

This hierarchy can be slightly reorganized by changing it to be as
follows:

            ShellStack
             ↓     ↓
   StScrollView   StWidget
        ↓
   StBoxLayout
        ↓
 PaginatedIconGrid

This will simplify future work where the PaginatedIconGrid will be
an implementation of StScrollable, in which case we'll be able to
simply remove the StBoxLayout from there.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1191
2020-04-08 21:12:06 -03:00
Florian Müllner
7d7a15f978 st/entry: Allow hint actor to shrink
We currently always ignore the minimum width of the hint actor and
request/allocate the preferred width. This can be problematic with
labels with long text, where we should rather ellipsize the text
than allow the entry to grow indefinively.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2607
2020-04-08 22:41:19 +00:00
Florian Müllner
1b5cf0b8a8 build: Bump version requirement
We are currently optionally using a 0.53 feature, bump the requirement
to make it non-optional.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1186
2020-04-07 23:13:27 +02:00
Florian Müllner
306b005943 extensions-tool: Add small indicator template
Adding an additional icon to the top bar is one of the more common
patterns used by extensions. Make this easier by providing a template
for it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
d229abf07d extensions-tool: Prompt for template when appropriate
When creating an extension interactively, we currently always use
the default template unless the --template option is used.

Instead, display the list of available templates to the user and
prompt them to pick one if it wasn't specified.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
6f6251c0bc extensions-tool: Add --template create option
We now have everything in place to allow users to pick a template
other than the default one (although at the moment it's the only
one we have), so add a corresponding option.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
fd034e3551 extensions-tool: Add hidden --list-templates option
This isn't a very useful option on its own, but will be used to
provide completions for a user-visible --template option.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
f9bee05d49 extensions-tool: Add template metadata
When we allow users to choose between different templates, we should
provide some context for each template to facilitate that choice.

Add that metadata in the form of a .desktop file, which allows us to
specify name and description, and is well supported by our translation
infrastructure.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
37c6fbc6b2 extensions-tool: Prepare for alternative templates
The template used when creating a new extension is intentionally
minimal, as the sample code in the old extensions-tool proved to
be annoying more often than not.

However as we support per-command options, we don't have to limit
ourselves to a single template, and can offer alternatives for
common use cases.

To prepare for that, namespace the existing template by moving it
into a subfolder.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
9719604b79 extensions-tool: Separate create metadata when prompting
When using the create command's --interactive option, we prompt for
any metadata that wasn't passed on the command line. As every prompt
is preceded by a short multi-line description, it is hard to follow
when everything is lumped together.

Improve legibility by separating all prompts by newlines.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
b6262f0666 extensions-tool: Handle NULL input when prompting for metadata
g_data_input_stream_read_line_utf8() may return NULL, for example
when interrupting the prompt with ^D. Handle that case and keep
prompting until we got a line.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Florian Müllner
6c0bd207e9 ci: Handle POTFILES.skip in potfiles check
Allow marking files as ignored when searching for translatable
strings, similar to "good" ol' intltool.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/812
2020-04-07 20:27:00 +00:00
Philip Withnall
91b13effc8 appFavorites: Hide favourites which are blacklisted by parental controls
If a favourite is set for an app which is blocked by parental controls,
that favourite should be hidden.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/465
2020-04-07 17:47:33 +02:00
Philip Withnall
143ab6ac7f search: Hide search providers which are blacklisted by parental controls
If a search provider is installed by an app which is blacklisted for the
current user by their parental controls, don’t show it or results for
it.

Currently, this only filters ‘remote’ (not built-in to the shell) search
providers. This seems fine for now; in future it could be expanded to
also filter built-in search providers, if any of them end up needing to
be filtered.

No corresponding changes need to be made `remoteSearch.js`, because the
results of `loadRemoteSearchProviders()` are filtered in `search.js`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/465
2020-04-07 17:47:33 +02:00
Philip Withnall
3e5b90dbba js: Add support for parental controls filtering to the desktop
Filter the apps shown on the desktop and in search results according to
whether they are blacklisted by the user’s parental controls.

This supports dynamically updating the filter during the user’s session.

This adds an optional dependency on libmalcontent. If that’s unavailable, no
parental controls filtering will occur.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/465
2020-04-07 17:47:29 +02:00
Philip Withnall
b82039e324 util: Add a wrapper around getuid()
So we can use it from JS in an upcoming commit.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/465
2020-04-07 17:46:39 +02:00
Florian Müllner
83862d04a0 build: Bump gjs requirement
We require gjs!410 for commit 764527. There has been a gjs release
with that change now, so bump the requirement accordingly.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2559
2020-04-07 14:06:45 +00:00
Florian Müllner
52d07f6d9b ci: Update to F32-based image
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2559
2020-04-07 14:06:45 +00:00
Florian Müllner
21de88c3ba bluetooth: Do not update hadSetupDevices on adapter changes
While we now deal more gracefully with adapter removals, we can
still mess up the hadSetupDevices tracking:

As adapters become available before any devices, we'll always
reset the setting to false when Bluetooth is turned on. And if
no set up device happens to be in range, it will still be false
when Bluetooth is turned off again.

To address that, only update the setting if we have an adapter
(like we do now) and we had one before (so it wasn't the adapter
itself that changed).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1174
2020-04-07 15:44:50 +02:00
Florian Müllner
456ca3d3e0 bluetooth: Do not sync() immediately on model changes
Our intended behavior when bluetooth is turned off is to keep
the menu visible if devices had been set up previously.

However since gnome-bluetooth@c437c729, devices are removed
first before removing the default adapter, so we now end up
always setting the property to false before checking for it.

Fix this by deferring all model changes to an idle, so that
we can process them as a unit. Do the same for proxy property
changes, as those may trigger a row-removal.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1174
2020-04-07 15:44:50 +02:00
Florian Müllner
f4ba3e4ab8 bluetooth: Fix showing menu when devices were set up
Since commit 26c2cb9f65, nDevices is always the actual number of
paired/trusted devices. So when bluetooth is turned off, it is
now 0 rather than forced to 1 if devices were set up previously.

Fix this by checking the property that tracks set up devices instead.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1174
2020-04-07 15:44:50 +02:00
Jonas Dreßler
d4db5a59c1 shell-app: Ignore invalid window PIDs
When building the list of window PIDs, it's possible Mutter doesn't know
about the PID the client has and meta_window_get_pid() will return 0. We
should handle this case by not adding the PID to the list of PIDs
instead of adding an invalid one to it.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1184
2020-04-06 21:53:37 +00:00
Jonas Dreßler
4aee87a31b shell-window-tracker: Detect invalid PID values of 0
MetaWindows get_pid() API changed to use the client PID, which also
works for Wayland clients instead of only X11 clients now. This API
returns 0 instead of -1 for invalid PIDs, so update our check according
to that.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1184
2020-04-06 21:53:37 +00:00
Jakub Steiner
e8f5a842b1 theme: tone down weekend with events
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2588
2020-04-06 20:54:37 +00:00
Yuri Chornoivan
5c681a76b6 Update Ukrainian translation 2020-04-06 16:19:08 +00:00
Georges Basile Stavracas Neto
53a24e6ddd animation: Set size through CSS
Pretty much the same case of the previous commit: we want this size
to be scale-dependant, and using the width and height properties of
ClutterActor doesn't automatically update.

Use CSS to set the width and height.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
2020-04-06 11:24:30 -03:00
Georges Basile Stavracas Neto
b27c89f836 appDisplay: Set the folder icon geometry through CSS
The CSS engine is scale-aware, whereas simply setting the
width and height properties directly isn't.

Use CSS to set the folder icon.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
2020-04-06 11:24:30 -03:00
Georges Basile Stavracas Neto
9f870aa1c7 st/theme-node: Use the node's scale factor
Each node stores the scale factor in place when it was created.
Creating nodes with the same style, but with different scale
factors, yields different nodes.

Use the node's scale factor instead of retrieving the context's
one.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
2020-04-06 11:24:30 -03:00
Georges Basile Stavracas Neto
c9cfeb8318 st/theme-node: Consider scale factor when comparing
The CSS engine of St is scale-aware, which means every length
and size it produces is multiplied by the current scale factor.

However, the individual nodes aren't aware of the scale factor
when they compare to each other.

Store and compare the scale factors in the nodes themselves.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1635

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
2020-04-06 11:24:30 -03:00
Georges Basile Stavracas Neto
497e66ce6a st/theme-context: Add a getter for the scale-factor property
Will be used by the next commit to avoid going through the GObject
machinery when retrieving the scale factor.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
2020-04-06 11:24:30 -03:00
Jonas Dreßler
bc973b80d7 st/theme-context: Also invalidate root node on stylesheet changes
Since commit 6a42d77261 we invalidate the
cached properties for each theme node on stylesheet changes by iterating
over the hashtable of the theme context instead of listening to the
signal in each individual theme node.

That commit forgot one particular node though that's not stored in the
hashtable, but using the `priv->root_node` property instead: The theme
node that belongs to the stage.

So make sure we also invalidate the cached properties of the stage theme
node on stylesheet changes. This fixes various crashes that happened
with extensions providing custom stylesheets (emitting the
"custom-stylesheets-changed" signal on every extension enable/disable),
trying to access an already freed CSS property of the stage.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2584
2020-04-06 10:45:55 +00:00
Jonas Dreßler
85846d88f0 shell-app: Use container widget for fallback X11 app icons
Just like StIcon does, we should use a container widget for the fallback
app icon that we get using the cairo surface property. It's needed
because the widget returned by shell_app_create_icon_texture() can be
resized freely, while we want the aspect ratio of the actual texture to
remain the same.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2578
2020-04-06 08:28:26 +00:00
Florian Müllner
6b7c85b079 data: Add extension-portal desktop file
Now that the extension preference dialog is opened by a separate
D-Bus service rather than the Extensions app, it can be opened
without a parent window that provides name and icon.

Fix this by adding back a hidden .desktop file.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2562
2020-04-05 13:36:35 +00:00
Florian Müllner
d80ef67d1d extension-tool: Use enable/disable D-Bus API if possible
Commit 4589da957 added D-Bus API for enabling/disabling extensions,
use that if possible to provide better feedback and not clutter the
settings with non-existent UUIDs.

The old code path is preserved as fallback to keep the commands
working from outside a running shell session.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2391
2020-04-05 13:42:50 +02:00
Florian Müllner
62f3457a95 extensions-tool/uninstall: Error out for system extensions
UninstallExtensions() only returns whether the operation was successful,
not why it failed. However we know that only user extensions can be
uninstalled, so check that first to provide a more meaningful error.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2391
2020-04-05 13:42:50 +02:00
Florian Müllner
636ab4b0e9 extensions-tool: Split out get_extension_property() helper
For the prefs command, we first fetch the extension info to check
whether the extension exists and actually has preferences. This
pattern can be useful for other commands and properties, so split
out a generic helper function.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2391
2020-04-05 13:42:50 +02:00
Florian Müllner
23e382dd33 extensions-tool: Log existing errors
In many cases we currently only indicate failure in the return value,
which is easily missed. Print some meaningful errors instead.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2391
2020-04-05 13:42:50 +02:00
Florian Müllner
f5a170ce46 extensions-tool: Add common option to silence errors
Error reporting is useful when used interactively, but often undesirable
when used in scripts. Account for that with a common --quiet option,
which is more convenient than redirection stderr to /dev/null.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2391
2020-04-05 13:42:50 +02:00
Florian Müllner
fbd6beea2c extensions-app: Validate .desktop file during meson test
... if desktop-file-validate is available.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1160
2020-04-05 11:02:00 +00:00
Florian Müllner
360f5b1642 extensions-app: Add category in .desktop file
Predefined categories aren't a great way for organizing installed
applications, but they have their use in "stores" like Software
or flathub.

Not listing any category means we fall through the cracks, so
pick the (hopefully) least inappropriate one ...

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1160
2020-04-05 11:02:00 +00:00
Florian Müllner
f0a785df9d extensions-app: Add developer name in metainfo
Unlike Software, flathub doesn't fall back to the "project group"
when no developer is specified, so add one.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1160
2020-04-05 11:02:00 +00:00
Florian Müllner
e2c1bfbedd extensions-app: Add flathub badge to README
The app is now on flathub, so brag about that.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1160
2020-04-05 11:02:00 +00:00
Rūdolfs Mazurs
7e5a1cfd90 Update Latvian translation
(cherry picked from commit 6d38a4a7b3)
2020-04-04 16:14:55 +00:00
Jonas Dreßler
07deda593a st/icon: Always show empty texture if both gicons are NULL
Commit 7ff7fb5d3b forgot to clear the
`priv->icon_texture` actor when returning from st_icon_update(), which
means we don't always switch to an empty icon if both gicon properties
are set to NULL.

Fix this and destroy the actor before returning early from
st_icon_update().

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1178
2020-04-04 16:20:35 +02:00
Jonas Dreßler
1ca39e8586 st/icon: Use a static GIcon for the missing-image icon
Don't create a new GIcon for the "missing-image" texture but simply
create it once statically instead and always use that.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1179
2020-04-04 13:21:54 +02:00
Georges Basile Stavracas Neto
402fd8ec29 iconGrid: Downscale icon size when comparing to defaults
The return value of st_theme_node_lookup_length() is scaled according
to the scale factor. IconGrid.ICON_SIZE is not. However, when BaseIcon
tries to fetch the CSS value for "icon-size" (which returns a scaled
value), it uses it as-is, mixing the two coordinate systems.

Use a single coordinate system (unscaled sizes) in IconGrid.BaseIcon.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1175
2020-04-03 16:04:40 -03:00
Florian Müllner
fbe2e30f38 screenShield: Wake up on deactivate()
Usually the screen is woken up before the shield is deactivated, but
it is also possible to unlock the session programmatically via the
org.gnome.ScreenSaver D-Bus API.

The intention is very likely not to unlock a turned off screen in
that case. Nor does it seem like a good idea to change the lock
state without any indication.

Waking up the screen is more likely to meet expectations and is
more reasonable too, so do that.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1158
2020-04-03 14:44:50 +00:00
Florian Müllner
fb6ead2881 screenShield: Switch lightboxes off before unlock transition
There is no point in animating a transition with fullscreen black
rectangles stacked on top, so switch them off before rather than
after the transition.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1158
2020-04-03 14:44:50 +00:00
Andre Moreira Magalhaes
7ff7fb5d3b st/icon: Only load default fallback icon if an icon was set and failed to load
Commit c89d6a633 introduced a default fallback icon that would be displayed in
case the main gicon or the fallback gicon wasn't set or failed to load.

This broke the use case where a StIcon is created but no main icon or
fallback icon are set on purpose, for example the appindicator extension
which always creates a StIcon to represent icons in menu items but the
actual icons are only set if the application provides one, leaving the
menu showing the default fallback ("image-missing") icon for all menu
entries that don't actually have an icon provided by the application.

Fix that by only using the default fallback icon if the provided one
failed to load.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1173
2020-04-03 14:28:24 +00:00
Florian Müllner
8030d9ad32 extensionUtils: Add openPrefs() convenience method
Opening their own preferences is a reasonable desire for extensions,
so make up for breaking it by adding a convenience method for that
action.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1163
2020-04-03 15:27:37 +02:00
Florian Müllner
45bc850715 extensionSystem: Add method for opening extension prefs
Extension that want to expose their own preferences (for example as menu
items) do that by passing their UUID to gnome-shell-extension-prefs.

But since 3.36.1 the app is optional and no longer accepts arguments on
the command line. To adjust, extensions now need to make a D-Bus call
the extensions portal, just like the app and gnome-shell.

We will add a convenience method for that purpose, so it makes
sense to share the existing code. As it's extension-related, the
extension manager looks like the right place ...

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1163
2020-04-03 15:23:26 +02:00
Florian Müllner
51a913730e workspace: Fix chaining up
Gah, accidentally dropped the 'vfunc' prefix :-(

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1172
2020-04-03 12:57:01 +00:00
Florian Müllner
0a4974ac8c extensions-app: Do not expand headerbar switch
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2563
2020-04-03 02:29:55 +00:00
Florian Müllner
1666fa195d js: Account for promisified call() method
A promisified method expects the callback parameter to be either
a function (in which case the original method is called normally)
or omitted altogether (in which case a Promise is returned).

The call to open application details in Software does neither and
passes null instead, which will result in a warning (because no
function argument means a promise will be used, but not omitting
the parameter means we end up with too many arguments).

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2551
2020-04-03 02:02:40 +00:00
Jonas Ådahl
a9df4e7516 appDisplay: Don't clear signal handler id before emitting
Otherwise we won't clear the 'view-loaded' handler after it was emitted.

Also move field initialization to the correct place, i.e. the init
function of the base class.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1169
2020-04-02 20:53:20 +00:00
Andre Moreira Magalhaes
343b3351f1 app-cache: Fix cache for folder translations
The app-cache code currently stores the folder translations in a hash
that can be accessed via shell_util_get_translated_folder_name().
This hash uses the filename (inc. extension) for the "desktop-directory"
as key which causes an issue when trying to find the translation
on AppDisplay._findBestFolderName() which gets categories (folder names)
from the app info which doesn't contain the ".directory" extension.

Fix that by storing the filename without extension as the hash key for
the cached folder translations.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1168
2020-04-02 20:37:20 +00:00
Марко Костић
407b12c3cb Update Serbian translation
(cherry picked from commit 428d38179d)
2020-04-02 19:43:17 +00:00
Ibai Oihanguren Sala
455a8f3076 Update Basque translation
(cherry picked from commit fe9708ebd8)
2020-04-02 16:15:48 +00:00
Florian Müllner
5067bda61a bluetooth: Fix infinite loop
Bailing out early of the loop means the iter is never increased,
resulting in an infinite loop.

Fixes 26c2cb9f6.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1165
2020-04-02 12:08:00 +00:00
Jiri Grönroos
e138b6e3af Update Finnish translation
(cherry picked from commit 8398769321)
2020-04-02 09:44:52 +00:00
Mariana Picolo
9bc9d5165f bluetooth: Update bluetooth submenu title
- "Bluetooth" to "on/off" labels
  to match with other menus

- Display single connected device name
  to show more relevant information

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2340
2020-04-02 00:15:37 +00:00
Mariana Picolo
26c2cb9f65 bluetooth: Add getDeviceInfos function
In case where only one device is connected, we want to display its name
in the menu. For that we will need more than the number of known/connected
devices, so change the function to return an array of device infos instead.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2340
2020-04-02 00:15:37 +00:00
Danial Behzadi
da44649e6f Update Persian translation
(cherry picked from commit 69426cbfda)
2020-04-01 01:43:14 +00:00
Marco Trevisan (Treviño)
a0def23940 main: Don't override DesktopAppInfo desktop if already GNOME
During the shell initialization we call the (deprecated) function to
override the Desktop environment in Gio DesktopAppInfo to make sure that
applications are correctly shown (as per commit b2fbf5a2), however this
might break the cases in which $XDG_CURRENT_DESKTOP is already set and
contains GNOME (given that is now a list).

In Ubuntu this is in fact set to: ubuntu:GNOME.
Now, if an application contains NotShowIn=ubuntu, the key will be ignored by
the shell, and the application is still listed everywhere.

So, override the DesktopAppInfo desktop environment only in the case that
the current desktop is not already GNOME.

At the current date I think we could just safely get rid of this override at
all, but there could be still cases where it still might be useful, like when
running as nested in some other environment, so keeping it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1156
2020-03-31 20:27:12 +02:00
Jonas Ådahl
f49b58cf97 appDisplay: Clear animateLater callbacks when unmapping
In some situations we could end up not with lingering 'view-loaded'
handler. This could result in delayed spring animate-in being initiated,
e.g. after a minute after the activities overview was already closed.

Fix this by removing any lingering signal or later handlers when
unmapping.

Fixes: 5c33fe4a0a

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1155
2020-03-31 19:05:15 +02:00
Cheng-Chia Tseng
cadd9a99c0 Update Chinese (Taiwan) translation
(cherry picked from commit 1ab5e6973a)
2020-03-31 16:07:40 +00:00
Fabio Tomat
7061889a29 Update Friulian translation
(cherry picked from commit 1dea3341ec)
2020-03-31 11:14:43 +00:00
Florian Müllner
764527c8c9 js: Promisify async operations
Promises make asynchronous operations easier to manage, in particular
when used through the async/await syntax that allows for asynchronous
code to closely resemble synchronous one.

gjs has included a Gio._promisify() helper for a while now, which
monkey-patches methods that follow GIO's async pattern to return a
Promise when called without a callback argument.

Use that to get rid of all those GAsyncReadyCallbacks!

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1126
2020-03-31 05:43:40 +00:00
Florian Müllner
18742fcc32 build: Post-release version bump
With 3.36.1 out, let's start the next development cycle ...
2020-03-31 00:36:48 +02:00
Jonas Ådahl
5c33fe4a0a appDisplay: Don't start animation from the 'paint' signal
Starting the animation from the actor 'paint' signal has various
unwanted consequences, such as sometimes trigger a
clutter_actor_queue_relayout() during the paint phase. One unwanted
consequence was that an offscreen actor effect was disabled during
painting, meaning the effect would begin being active, but later during
the post-paint processing being disabled. The caused said effect to push
an offscreen framebuffer to the paint context, but then just destroy it
instead of popping it. When this happened, we'd end up trying to operate
on a framebuffer that may had been finalized, or not, depending on the
garbage collector. Sometimes, for some users, this caused a segmentation
fault when trying to pop a matrix from the framebuffer matrix stack.

Deal with this more properly, by using the 'view-loaded' signal to wait
with animation until the view is loaded, as well as using MetaLater to
schedule the start of the animation.

For when a view was signalled to be ready, we're in a state where we can
start animation before the next frame as the layout is ready, but when
not, we have to add back the "hack" where we must wait for one frame for
the target icon positions to be up to date. Do this by adding a
MetaLater IDLE callback that starts the animation *after* the next
frame. This also needs the old 'opacity = 0' work around to not show an
incorrect first frame.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2418

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1154
2020-03-30 16:24:31 +00:00
Florian Müllner
4f427f4e0d js: Do not set entries' ClutterText:editable property
The property influences the text's (and thus entry's) minimum width[0],
which is generally not what we want. And as we now prevent text from
being entered in non-reactive entries by other means, we can simply
drop it.

[0] https://gitlab.gnome.org/GNOME/mutter/-/blob/master/clutter/clutter/clutter-text.c#L2940

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2423
2020-03-29 22:12:57 +02:00
Florian Müllner
6d3c740b37 st/entry: Unset key focus when made unreactive
It seems reasonable that an entry shouldn't allow entering text when not
reactive. The same could be achieved by changing the text's :editable
property, however that will disable scrolling if the text doesn't fit,
which may result in an unwanted size change.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2423
2020-03-29 22:12:57 +02:00
Florian Müllner
34c4627db9 st/entry: Bind ClutterText reactivity to entry
The text is part of the entry, so it is surprising that it can
still be edited when the entry itself isn't reactive. Address
this by setting up a binding instead of expecting all consumers
to handle the case themselves.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2423
2020-03-29 22:12:57 +02:00
Florian Müllner
b08b125df6 st/entry: Remove unused macro
https://gitlab.gnome.org/GNOME/gnome-shell/issues/2423
2020-03-29 22:12:57 +02:00
Aurimas Černius
50301bcfd4 Updated Lithuanian translation 2020-03-29 22:28:04 +03:00
Florian Müllner
c2cacc63ee extensions-app: Point homepage in metainfo to subproject
This is where the app code is located now, so it makes for a less
surprising landing page than the toplevel gnome-shell directory.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1150
2020-03-29 17:49:08 +00:00
Florian Müllner
c226081a23 extensions-app: Update README
Add the icon and use the app name instead of the subproject name.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1150
2020-03-29 17:49:08 +00:00
Luke Yelavich
16f4e4dc4c checkbox: Improve accessibility of check boxes for Orca users
Set the label actor, so Orca presents the label text when the check box
has focus. Also change the role to ATK_ROLE_CHECKBOX.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2517
2020-03-29 12:56:33 +00:00
Yosef Or Boczko
308b58175f Update Hebrew translation 2020-03-29 07:45:59 +00:00
Luke Yelavich
454e3fd39a calendar: Improve the accessibility of the "Do Not Disturb" switch
Set the do not disturb label as the label actor for the do not disturb switch,
so that Orca speaks the do not disturb label when the user moves
keyboard focus to the do not disturb switch.

Also enable toggle mode for the "Do Not Disturb" button and bind it's checked
state to the state property of the switch. This makes sure that Orca presents
thecorrect state of the do not disturb switch to the user.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2508
2020-03-29 11:07:32 +11:00
Yosef Or Boczko
e4d72fb2b0 Update Hebrew translation 2020-03-28 23:10:11 +00:00
Florian Müllner
f3fcc4adb7 shellDBus: Return error from ReloadExtension
The method has been deprecated because it generally doesn't (and
can't) work. Clarify that by returning an error instead of
apparently doing nothing.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2510
2020-03-28 22:14:44 +00:00
Jonas Dreßler
867587ef4c keyboard: Hide keyboardBox after destroying the keyboard
It seems there is a weird race condition between Clutter trying to
destroy the keyboard actor and Clutter trying to hide the keyboardBox
container actor: If the keyboardBox is hidden before destroying the
keyboard actor, Clutter doesn't repaint anything and the keyboard
remains visible until something else draws over it.

To fix this issue until we find the underlying Clutter bug, simply
destroy the keyboard actor before hiding the keyboardBox. The order in
which we call these doesn't matter anyway since hideKeyboard(true) hides
the keyboard immediately without an animation.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1736

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1142
2020-03-28 20:45:32 +00:00
Jonas Dreßler
b68fb35783 layout: Use translation_y of 0 to hide keyboard
Since we show the keyboard using a translation_y of -keyboardHeight, the
keyboard will be moved down far enough to be out of sight by setting
translation_y to 0.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1142
2020-03-28 20:45:32 +00:00
Jonas Dreßler
8dfed7e762 keyboard: Don't include keyboard devices when updating lastDevice
We're dealing with attached keyboards now using the touch_mode property
of ClutterSeat: If a device has a keyboard attached, the touch-mode is
FALSE and we won't automatically show the OSK on touches, also the
touch-mode gets set to FALSE when an external keyboard is being plugged
in, so that also hides the OSK automatically.

With that, we can now ignore keyboard devices when updating the last
used device and no longer have to special-case our own virtual devices.

Because there was no special-case for the virtual device we use on
Wayland now, this fixes a bug where the keyboard disappeared after
touching keys like Enter or Backspace.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2287

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1142
2020-03-28 20:45:32 +00:00
Florian Müllner
97fe4f761a volume: Cancel before checking state
Since commit 2894085c45 we omit sound feedback on volume changes
if something is already outputting sound. Unfortunately that
"something" may be our own feedback (from a previous volume
change).

In that case we do not want to omit the new feedback, so instead
cancel the previous one.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1147
2020-03-28 19:03:12 +01:00
Jonas Dreßler
ba8210ea98 keyboard: Run dispose manually on virtual input device when destroying
We want to make sure any buttons that are still pressed on the virtual
input device used by the OSK are released immediately when destroying
the OSK. Do this by calling run_dispose() on the destroy() function of
the KeyboardController, which makes sure we don't have to wait for the
garbage collection to dispose the object and a still pressed key remains
being pressed until the GC kicks in.

Related: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2287

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/956

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1141
2020-03-28 17:31:04 +00:00
Carlos Garnacho
656168543f st: Honor alpha range specified by pango_attr_foreground_alpha_new()
This is documented as a value between 1 and 65536. However we were passing
a 0 value for 100% transparent colors, which is interpreted as "system
inherited" in pango_renderer_get_alpha() docs.

Ensure we respect this range by specifying the minimum allowed alpha (1)
if the color is fully transparent. If someone notices this 1/65535th change
I'll ask him how many pleiades can he count.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2504

(yes, again).
2020-03-28 16:58:30 +00:00
Carlos Garnacho
8378c9c9e0 inputMethod: Protect for running with older mutter versions
The offset argument is changing from uint to int. Which means we
might would pass a negative offset and trigger an "out of bounds"
error. Make it work more or less alright with older mutters, by
clamping the offset to 0.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1146
2020-03-28 16:38:10 +00:00
Florian Müllner
de16fe8dff dateMenu: Only use nearest city when appropriate
Since commit 784c0b7e4 we use the name of the nearest city rather
than the weather station, as the latter tend to have unwieldy
and weird names.

However the nearest city may not be that near after all, in which
case the result is again surprising.

Address this by not using the nearest city name unconditionally, but
only if it appears in the station name.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2468
2020-03-28 16:24:03 +00:00
Jwtiyar Nariman
793f053309 Add Kurdish Sorani translation
(cherry picked from commit aba60dcac8)
2020-03-28 15:40:30 +00:00
Georges Basile Stavracas Neto
bea34da289 unlockDialog: Only cancel AuthPrompt if it exists
AuthPrompt is created on demand, and this._authPrompt is
expected to be null except on very strictly controlled
occasions. The idle monitor callback isn't one of them.

Check if AuthPrompt exists before cancelling it.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2371
2020-03-27 23:40:08 +00:00
Florian Müllner
aafec16f49 ci: Build Extensions flatpak bundle
GNOME apps use (or are encouraged to use) flatpak in their CI setup[0],
so do that for the Extensions app as well and get:

 - test building the flatpak
 - produce a bundle for download and testing
 - publish the build in gnome-nightly

[0] https://gitlab.gnome.org/GNOME/Initiatives/wikis/DevOps-with-Flatpak

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1133
2020-03-27 23:33:30 +00:00
Florian Müllner
73df61f36d extensions-app: Add flatpak manifest
We finally have everything in place for distributing the Extensions app
as flatpak without jumping through too many hoops. Add a manifest that
can produce a nightly build like other GNOME modules, and can serve as
a template for a stable manifest on flathub.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1133
2020-03-27 23:33:30 +00:00
Florian Müllner
39e6375aff extensions-app: Move Extensions app to new subproject
The Extensions app code is now independent enough from the rest of the code
base to move it to its own subprojects, like we did for the extensions-tool.

This allows for stand-alone builds of the app, which we are about to use
for distributing it as flatpak.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1133
2020-03-27 23:33:30 +00:00
Florian Müllner
ed21a4e5c1 extensionPrefs: Fake Config module
We include config.js because it is a dependency of ExtensionUtils,
but it's not actually used in the code paths we exercise.

As we want to allow stand-alone builds of the app, it is much easier to
fake the module than to either include a generated file from elsewhere
in the tree or generate it ourselves.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1133
2020-03-27 23:33:30 +00:00
Carlos Garnacho
73472ba6a7 st: Forward CSS foreground alpha as a PangoAttribute to text
Let the PangoRenderer handle this.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2504
2020-03-27 22:47:48 +00:00
Jonas Dreßler
702417ce83 appDisplay: Only use dragMonitor for one icon at a time
Instead of adding a dragMonitor for every icon in the grid as soon as
one icon is getting dragged, only add a dragMonitor for the icon that is
currently being dragged over (ie. the current drag-target). With a large
number of icons in the iconGrid, this should significantly reduce lags
while dragging.

We can do this by detecting the DnD-entering of an icon or folder using
the `handleDragOver()` callback of drag-targets, adding the dragMonitor
because we know an icon is hovering above the drag-target and then
detecting the DnD-leaving of the drag-target by using the `dragMotion()`
handler, where we remove the dragMonitor again as soon as the
targetActor is no longer our actor (ie. the drag-target).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/849
2020-03-27 22:38:41 +00:00
Jonas Dreßler
766e9034e2 appDisplay: Remove dragMonitor when FolderIcon is destroyed
While it should be impossible to destroy a FolderIcon while a DnD action
is still going on, there might still be rare cases where this happens
(ie. when a folder is removed because an app got deleted during DnD).

So make sure we're on the safe side here and don't potentially leave
dragMonitors around after the icon is destroyed by removing the
dragMonitor inside the onDestroy handler of the FolderIcon, just like we
do for AppIcons.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/849
2020-03-27 22:38:41 +00:00
Jonas Dreßler
91748aedb7 blur-effect: Handle failure of background blitting gracefully
`paint_background` already provides a return value in case the blitting
of the framebuffer fails, handle that and fall back to only drawing the
actor in case something goes wrong.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1000
2020-03-27 22:31:57 +00:00
Jonas Dreßler
4783d767d6 blur-effect: Properly clear background framebuffer
We want to completely clear the background framebuffer when switching
back to ACTOR mode to make sure the `background_fb.framebuffer` check
will fail in `update_background_fbo` when switching to BACKGROUND mode
again. Otherwise the checks in `update_background_fbo` will return TRUE
and we will keep using the background framebuffer that was created
before switchig to ACTOR mode.

While at it, also clear the background framebuffer completely when
changing the actor to avoid the same issue here.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1000
2020-03-27 22:31:57 +00:00
Jonas Dreßler
dedbf0cb09 blur-effect: Fix framebuffer sizes when stage-view scaling is used
When blurring only the actor (ACTOR mode), we don't want to apply any
scale, it looks fine without using the resource scale and it also seems
like `clutter_actor_continue_paint` in `paint_actor_offscreen` only
draws an unscaled texture anyway (ie. if the resource scale is 2, only a
quarter of the framebuffer is being drawn to).

In BACKGROUND mode though, we need to scale the framebuffer using the
scale factor of the stage view (ie. the final scale factor for the
monitor) because the content of the framebuffer we blit is scaled using
that factor. Also, since the framebuffer we blit belongs to a stage view
and only includes the contents of this view, we need to adjust the
stage-coordinates of the actor to be relative to the stage-view.

To make sure we don't have to get the transformed actor size or position
multiple times during one paint-run and don't have to carefully floor()
or ceil() widths and positions, store the size of the actor (which is
also the size of the framebuffer) and its position relative to the stage
view inside a ClutterActorBox.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1000
2020-03-27 22:31:56 +00:00
Jonas Dreßler
e5b7462b94 blur-effect: Don't check whether effect is enabled when painting
A ClutterEffect is being painted as part of the paint cycle of
ClutterActor, where _clutter_effect_paint() is called before painting
the actual actor. With that, it's impossible that an effect gets painted
while it's disabled, so remove the check whether the ClutterActorMeta is
enabled before painting.

Also if everything works fine in Clutter, the ClutterActorMeta should
have an actor set and we've been notified about that actor in
shell_blur_effect_set_actor(), so assert that our cached actor is set
when painting the effect.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1000
2020-03-27 22:31:56 +00:00
Jonas Dreßler
48b0a91385 blur-effect: Fix alignment of function arguments
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1000
2020-03-27 22:31:56 +00:00
Takao Fujiwara
6170bea283 inputMethod: Fix delete-surrounding-text signal
Forward the arguments at the 'delete-surrounding-text' signal
from IBusInputContext to clutter_input_method_delete_surrounding()
so that ibus-typing-booster use the deleting surrounding text function.

Input method engines can delete the output text in applications
with this function.

This change will require a change of mutter of mutter!517
because the first arguemnt of the 'delete-surrounding-text' is INT
to express the offset of the current cursor position but
the first one of clutter_input_method_delete_surrounding() is UINT
since the Wayland spec accepts UINT in delete_surrounding()

mutter will change the type of the first one to INT in
clutter_input_method_delete_surrounding() to work with this change.

https://gitlab.gnome.org/GNOME/mutter/issues/539
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/477
2020-03-27 19:42:07 +00:00
Bjørn Lie
dcceb615bf data: Update Norwegian OSK layout
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1073
2020-03-27 17:07:55 +00:00
Florian Müllner
da673639ca cldr2json: Do not overwrite existing files
We had various requests to improve existing OSK layouts, but
haven't accepted them so far as any changes would be overridden
when regenerating the layouts.

However as the upstream layouts at http://www.unicode.org are
extremely slow to update(*), we shouldn't block all improvements.

So instead of letting the update script override all existing
layouts, just make it import new layouts.

(*) not their fault, as the android layouts are a downstream to Google

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1136
2020-03-27 16:44:27 +00:00
Florian Müllner
dd7727e315 cldr2json: Don't use deprecated method
Replace the deprecated 'warn()' with 'warning()' to shut up
a runtime warning.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1136
2020-03-27 16:44:27 +00:00
Florian Müllner
f3ba1e65ba data: Don't clone cldr2json when updating OSK layouts
Meh, we imported the module so we don't have to go through another
upstream to make changes to the script, but then ended up not using
the fork at all.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1136
2020-03-27 16:44:27 +00:00
Florian Müllner
f8b4696211 data: Move cldr2json fork into subdirectory
The module was imported into the toplevel in !424, but that's at
least a bit weird:
 - it's a helper script for one particular aspect (OSK layouts)
 - it adds a README.mdwn to our own README.md, and a test/ directory
   to our tests/

Move the whole thing to a subdirectory under data/, which is more
appropriate.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1136
2020-03-27 16:44:27 +00:00
Jonas Dreßler
d71d85e90c theme/switcher-popup: Increase contrast of switcher items
Use a brighter color and increase the contrast of the selected/active
items in the switcher popup.

Since a bright color doesn't go well with a box-shadow, remove that
shadow, an effect like should only be used for elements clicked with a
mouse anyway.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1135
2020-03-27 13:58:47 +00:00
Florian Müllner
660a2b6e62 extensionSystem: Require Extensions app for updates
While we don't technically need the app to download and apply
updates, we do require it for notifying the user about available
updates and listing extensions with pending updates.

So instead of intransparently applying updates in the background
without the user noticing, disable updates altogether if the
Extensions app is not installed.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2346
2020-03-27 13:28:41 +00:00
Florian Müllner
cc347bf6d8 extensionDownloader: Stop handling 'blacklist' operation
Blacklist support was added all the way back in commit 1e286e43, but
the code had been defunctional until recently. While uninstalling an
extension that has been blacklisted makes sense off-hand, unfortunately
we don't know if an extension was *actually* blacklisted:

The website returns that operation for any extensions for which it
doesn't find any versions that match the shell version. That is, the
most likely reason is that the user updated to a new GNOME release
which the extension doesn't support yet.

It doesn't look like the website is going to change that behavior any
time soon[0], so drop the 'blacklist' handling for the time being.

[0] https://gitlab.gnome.org/Infrastructure/extensions-web/-/issues/95

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1132
2020-03-27 12:41:57 +00:00
Florian Müllner
26ffeaae47 js: Add missing chain-ups in vfuncs
Commit 55b57421d changed signal handlers to the corresponding vfuncs,
but didn't always chain up as necessary. In most places this doesn't
matter, but at the very least the commit broke activating message list
items via the keyboard.

Add all (hopefully) the missing chain-ups to get the expected behavior
back.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2319
2020-03-27 10:39:58 +01:00
Florian Müllner
01a57206bc messageTray: Don't create notification policy on demand
This was changed in commit 8f15193b4 as a work-around for an ES6
class limitation, but now that Sources are GObject subclasses, we
no longer need to use that somewhat surprising pattern.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1131
2020-03-26 18:52:10 +00:00
Florian Müllner
279072795f messageTray: Only destroy policy after emitting ::destroy
Destroying the policy invalidates it, so accessing it from a
Source::destroy handler (for example to disconnect signal
handlers) currently results in warnings like:

Object .Gjs_ui_messageTray_NotificationApplicationPolicy
(0x7f8c7c0a64a0), has been already deallocated — impossible
to access it.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2238
2020-03-26 18:45:45 +00:00
Florian Müllner
c89d6a633a st/icon: Add a default fallback icon
The idea behind commit 3dd8ffc2bb to try harder to avoid empty
icon actors because of missing icons was sound, so implement that
behavior in StIcon itself:

If the main gicon was not found, and the fallback gicon isn't set or
wasn't found either, fall back to the standard 'missing-image' icon.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1127
2020-03-26 18:36:02 +00:00
Florian Müllner
fecc0c06ac Revert "texture-cache: Use image-missing image when no other icon was loaded"
The commit broke StIcon's :fallback-gicon property, as it relies on failure to load
an icon to determine that the fallback should be shown.

Luckily StIcon is the only user of st_texture_cache_load_gicon() (at least in
regular shell code), so we'll be able to implement the 'image-missing' fallback
there.

This reverts commit 3dd8ffc2bb.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1127
2020-03-26 18:36:02 +00:00
Florian Müllner
512862f2d7 extensionPrefs: Export parent window for prefs dialog
Now that OpenExtensionPrefs()'s parentWindow parameter is actually
supported, export the main application window so it can act as
transient parent to the prefs dialog.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1087
2020-03-26 18:32:30 +01:00
Florian Müllner
a90fcb7ddb dbusServices/extensions: Handle parentWindow parameter
Now that the service implements the preference dialog, it's time
to support OpenExtensionPrefs()'s parentWindow parameter and make
the dialog a transient of the external window if specified.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1087
2020-03-26 18:32:30 +01:00
Florian Müllner
6baf490aab shew: Add small library for dealing with external windows
In order to support OpenExtensionPrefs()'s parentWindow parameter,
we will need the ability to make a window transient to an external
window identified by a string handle.

This takes the corresponding code from xdg-desktop-portal-gtk and
brings it into an introspectable form, likewise the counterpart in
libportal to export a string handle for a GtkWindow.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1087
2020-03-26 18:32:30 +01:00
Ibai Oihanguren Sala
f971b8426d Update Basque translation 2020-03-26 15:11:16 +00:00
Michael Catanzaro
83c6b2ab48 Fix prompt for updates on end session dialog
Since PackageKit 1.11.1, the prompt to install updates on the end
session dialog has been (mostly) broken. The problem is that it only
works if PackageKit is running at the time the end session dialog is
opened; otherwise, our GDBusProxy has invalidated all of its properties,
which we read to see if update is possible. We need to autostart
PackageKit before reading its properties to fix this problem. That would
be easy if we were calling a method to see if an update or distro
upgrade were available, but since we're just checking a property, using
cached properties won't suffice. We'll have to manually check the
property value to ensure we autostart PackageKit.

Most of the code is written by Florian. Thanks Florian!

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/2276
2020-03-26 14:45:05 +00:00
Daniel van Vugt
24742f3566 layout: Show system background and animate on the same frame
Previously we'd show the system background and then wait till the
main loop was idle before beginning the shell startup animation.
This resulted in one initial frame that was always just the system
background.

Now we try to get both the system background and the startup animation
begun on the same first frame.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1102
2020-03-25 22:41:54 +00:00
Marco Trevisan (Treviño)
03a46be5c7 telepathyClient: Use proper Object to wrap different tpl messages
In telepathyClient we consider messages both Tpl.TextEvents and
Tpl.Messages, and we manually create JS objects to copy the properties we
care for each one. This may lead to objects not matching the interface we
want.

Instead, use an object with construct-only properties and two factory static
methods to initialize it.

Unfortunately we need to use the ChatMessageClass for the class name or
calling the static methods would trigger a gjs error as per [1].

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1113

[1] https://gitlab.gnome.org/GNOME/gjs/-/issues/310
2020-03-25 22:22:49 +00:00
Wolfgang Stöggl
9a26b970f9 Update German translation 2020-03-25 22:11:59 +00:00
Ray Strode
2ef71b62df data: ensure systemd environment is sanitized when shell exits
When mutter is acting as a display server it sets a number of
environment variables in the user's session. These variables
tell applications where the display server's sockets are.

When the shell exits at logout time it leaves these environment
variables in the systemd --user environment, which can confuse
subsequent sessions.

This commit clears up the environment on exit.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1129
2020-03-25 21:34:47 +00:00
Marco Trevisan (Treviño)
766288eec1 telepathyClient: Use GObjects based message objects
As per commit b5676a2a5 ChatNotification is a GObject, but I was wrongly
considering that it was using Tp.Message's as children, instead it just
uses custom-built objects to pass information around through signals.

Given gjs can only use GObjects as signal parameters, create a small wrapper
class to hold the ChatNotification messages and use it as signal parameter.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2449

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1112
2020-03-25 21:18:54 +00:00
Florian Müllner
53e8285cf0 lint: Sync configuration with gjs
Nothing big or scary, just a bump in the standard version which
allows us to use shiny things without eslint complaining :-)
2020-03-25 22:16:47 +01:00
Florian Müllner
ff844a2a81 main: Do not warn about missing GDM on each login
We now warn on startup if screen locking isn't available, however for
users who choose not to use GDM or logind, repeating the warning on
each login is more annoying than helpful.

Instead, limit the warning to the first login on which the screen lock
became unavailable. That way the notification will still serve the
intended purpose of informing the user, but without being perceived
as nagging.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432
2020-03-25 20:03:01 +00:00
Florian Müllner
78997cb7eb environment: Hook up touch_file to GFile prototype
We don't usually extend introspected types with our own API, but in
this case it's too tempting to make the helper functions usable with
Gio._promisify() ...

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432
2020-03-25 20:03:01 +00:00
Florian Müllner
f52574bd28 shell/util: Add touch_file_async() helper
Add a small helper method to asynchronously "touch" a file and return
whether the file was created or not.

As g_file_make_directory_with_parents() doesn't have an async variant,
we need a C helper to make the entire operation non-blocking.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432
2020-03-25 20:03:01 +00:00
Florian Müllner
e6a814fac8 extensionPrefs: Ensure up-to-date release version in metainfo
It's easy to forget to add a new <release> tag to the metainfo when
doing a new release.

Address this with an additional test if appstream-util is recent
enough to include the new validate-version command, so distcheck
fails when the metainfo wasn't updated.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1117
2020-03-25 19:56:52 +00:00
Michael Catanzaro
ddb85c03c3 endSessionDialog: apply updates by default
Users can still uncheck the box to avoid applying updates, but by
default we will encourage the user to update.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2427
2020-03-25 17:20:16 +00:00
Carlos Garnacho
66c4b1a8b6 st: Apply css foreground color to text as a PangoAttribute
Rely on the Pango renderer handling this properly, instead of tinting
the full ClutterText in the color specified through css.

Also set the caret color explicitly, since it used to be set as a side
effect of clutter_text_set_color(), but no longer is.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/850
2020-03-25 11:18:30 +00:00
Emin Tufan Çetin
11daf14e80 Update Turkish translation 2020-03-25 06:27:54 +00:00
Florian Müllner
35484151ce shell/screenshot: Throw error on failure
Commit da537cda43 moved the Shell.Screenshot API to GIO's async pattern,
but we never set the GError passed to the *_finish() functions and only
indicate failure by returning FALSE.

The expected behavior is to throw an error in that situation, so make sure
we do that.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1123
2020-03-24 11:29:53 +00:00
Florian Müllner
1bccbe7f11 shell/screenshot: Remove unnecessary NULL check
Since commit be5f5ec9d4, the output stream is created externally and
expected to be non-NULL, so the check is now pointless (in particular
*after* calling g_object_ref() on the stream).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1123
2020-03-24 11:29:53 +00:00
Florian Müllner
b4162afa65 shell/screenshot: Check preconditions of public API
It's good practice to guard public API against programmer errors,
so add the usual g_return*_if_fail() calls.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1123
2020-03-24 11:29:53 +00:00
Florian Müllner
eb51942a05 screenshot: Return error when stream creation fails
The Shell.Screenshot API expects valid output streams for writing the
completed screenshot, not NULL.

Handle this properly by returning an error instead of passing it on
to the screenshot.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1123
2020-03-24 11:29:53 +00:00
Florian Müllner
23e5cd4e10 dbusServices/extensions: Include Params module
It's unused and was removed in commit a0467bf875, which broke extensions
that rely on it in their preference widget.

As the removal only happened post-3.36.0, add it back until we branch.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2476
2020-03-24 10:38:24 +01:00
Florian Müllner
13f3f75303 ibusManager: Simplify code a bit
ibus_bus_request_name_async_finish() will throw an error on failure,
so we can move the error handling there instead of checking for the
return value, which saves one level of indentation.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1122
2020-03-24 08:27:27 +01:00
Mario Sanchez Prada
3dd8ffc2bb texture-cache: Use image-missing image when no other icon was loaded
If a given icon is not available for neither the current theme nor any
of the fallback options (default theme -"Adwaita", "gnome"...), the
call to gtk_icon_theme_lookup_by_gicon() will return a NULL value, which
returned by the Shell Toolkit as is, causing trouble in the Shell's JS
code when calling shell_app_create_icon_texture() to create an icon.

To at least mitigate the chances of this having this issue happening, we
should at least try to load the standad 'image-missing' icon from the
Icon Naming Specification spec when we receive a NULL here, so that
at least we try to show something to the user, even if it's ugly.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1121
2020-03-23 13:50:06 -03:00
Philip Withnall
541847d8b6 remoteSearch: Fix typos in log messages
I have this compulsion to rid the world of the typo ‘DBus’.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1121
2020-03-23 13:40:39 -03:00
Florian Müllner
fb9854c003 extensions-tool: Use new Extensions proxy
While sandboxing isn't a concern for the gnome-extensions command line
tool, using the Extensions proxy directly means that D-Bus methods are
called from the tool rather than gnome-shell, which allows the proxy
to auto-shutdown when done.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
c748b9de5c extensionPrefs: Use new Extensions proxy
We will soon sandbox the application, so it makes sense to not request
more access than strictly necessary (even with priviledged access like
extension management).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
34e85342d8 dbusServices/extensions: Take over prefs dialog from app
As outlined earlier, in order to turn the Extensions app into a properly
sandboxed application, we need to split out the extension prefs dialog
and move it elsewhere.

With "elsewhere" being the new Extensions D-Bus service, effectively
turning it into a shell extensions portal.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
91b7474d5a dbusServices/extensions: Proxy Extensions API
Similar to the previously added org.freedesktop.Notifications proxy,
this exposes the org.gnome.Shell.Extensions API and forwards any
request to the real implementation in gnome-shell.

The motivation differs though: We want to be able to package the
extension app as flatpak and distribute it separately, but the
extension prefs dialog is hard to impossible to sandbox:

 - filenames need translating between host and sandbox, and we
   can only do that in some cases (serializing/deserializing
   extensions), but not others (extension settings that refer
   to files)

 - system extensions install their GSettings schemas in the system
   path; the best we can do there is assume a host prefix of /usr
   and set GSETTINGS_SCHEMA_DIR in the flatpak (eeks)

 - extensions may rely on additional typelibs that are present on
   the host (for example because gnome-shell itself depends on
   them), but not inside the sandbox - unless we bundle all of
   gnome-shell's dependencies

 - if gjs/mozjs differ between host and sandbox, extensions must
   handle different runtimes for the extension and its prefs

And all those issues occur despite a very permissive sandbox (full
host filesystem access, full dconf access, full org.gnome.Shell
access (including Eval()!)).

This new service will give us an alternative place for handling
the preference dialog:

 - it runs outside of gnome-shell process, so can open windows

 - it runs on the host, so the extension's prefs get to run
   in the same namespace as the extension itself

That is, the service will provide portal-like functionality (albeit
not using the org.freedesktop.portal.* namespace, as extension
management is an inherently privileged operation).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
d76162c1c0 extensionPrefs: Stop handling UUIDs on the command line
We are jumping through quite some hoops to support showing only the
preference dialog when given a UUID on the command line.

As gnome-shell is about to stop calling out to us for the prefs dialog,
the reason for supporting this is going away, so remove all the special
handling.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
2b517e352d extensionPrefs: Use template for preference dialog
The dialog that contains the extension's preference widget has become
fairly complex over time, mostly due to the error handling.

It therefore makes sense to move it to a template, just like we did
for the main application window and extension rows.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106
2020-03-23 15:39:12 +00:00
Florian Müllner
08203c9c1e closeDialog: Remove transitions before resetting dialog
On X11, _onFocusChanged() updates the input region, as well as the
reactive-ness of the dialog's buttons.

That method is not only used as signal handlers (which are correctly
disconnected when the dialog is hidden), it also runs when the "show"
transition completes.

That's a problem if the transition is still ongoing when the dialog is
hidden, as it will then only complete when it is replaced by the "hide"
transition, after the this._dialog has been reset to null, and trying
to access the dialog's buttons results in an error.

Avoid this by explicitly removing all transition on hide before
resetting the dialog.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2467
2020-03-23 15:27:47 +00:00
Florian Müllner
d29e5765ba keyboard: Fix fallback layout when using variants
Commit c1ec7b2ff meant to fall back to the base layout in case
a variant like `fr+oss` is set up, but as we are checking for
'+' on the array rather than the layout name, the fallback only
"works" for a layout that is literally called '+', whoops.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2471
2020-03-23 16:20:38 +01:00
Florian Müllner
61beccf733 unlockDialog: Handle embedded newlines in notifications
Detailed notifications are meant to be single line, just as unexpanded
notification banners. So handle them the same way as in the message
list, and replace embedded newlines by spaces.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2463
2020-03-22 21:19:36 +01:00
Daniel van Vugt
ffb8bd5fa7 loginDialog: Retain native logo dimensions
So that the same logo may be used during boot and keeps its
dimensions on the login screen, appearing to never move.

Related to: https://bugs.launchpad.net/bugs/1867133

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1101
2020-03-22 15:06:36 +00:00
Florian Müllner
7f6e2ff36b extensions-tool: Use OpenExtensionPrefs() method
LaunchExtensionPrefs() was deprecated in commit fda938175e,
so switch to the replacement.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1097
2020-03-22 15:31:07 +01:00
Florian Müllner
7d94bfa642 extensionPrefs: Sync list visibility on status changes
We only show the list of system- and user extensions if corresponding
extensions are installed, however we only update the visibility
after loading the initial list of extensions.

As it's possible for the first user extension to be installed while the
app is open or the last one to be removed, we should also update the
list visibility after extension state changes.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1088
2020-03-22 13:30:09 +00:00
Yosef Or Boczko
eb3c857f23 Update Hebrew translation 2020-03-21 23:27:40 +00:00
Jordi Mas
a096ed37d6 Update Catalan translation 2020-03-21 23:37:37 +01:00
Goran Vidović
498a743c08 Update Croatian translation 2020-03-21 21:48:00 +00:00
Florian Müllner
2c91b6164c dbusServices: Allow to inhibit auto-shutdown
While we only shut down after a method call completed or (if the
interface has signals) the sender disconnects from the bus, services
may need to inhibit auto-shutdown for more specific reasons themselves,
for example when a method call kicks off an operation that should
complete before shutting down.

Add hold() and release() methods like Gio.Application for those cases.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1115
2020-03-21 20:16:22 +00:00
Florian Müllner
04352ae158 build: Add configuration summaries
Meson now has a summary() function to easily summarize the build
configuration after the project was configured, use that for some
fancy output when the feature is available.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1072
2020-03-21 20:44:43 +01:00
Florian Müllner
5c5dc03b78 build: Make bash-completion support optional
Whether we install bash-completion support currently depends on whether
the corresponding pkg-config dependency is found.

Turning this into a feature option keeps that behavior by default, but
also allows to explicitly enable or disable the support.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1072
2020-03-21 20:44:43 +01:00
Florian Müllner
8a89e22e8e build: Use fixed gettext domain for non-subproject extension-tool builds
Clarify how the option is supposed to be used by
 - ignoring it for non-subproject builds
 - enforcing that it is set for subproject builds

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1072
2020-03-21 20:44:43 +01:00
Florian Müllner
8f851e8adf build: Yield 'man' option to extension-tool subproject
Turns out meson has a build-in pattern of what we are doing, namely:
Set a subproject option to a parent project option of the same name.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1072
2020-03-21 20:44:43 +01:00
Florian Müllner
49e4757c0b build: Drop install argument from configure_file()
It requires meson 0.50.0 and is not necessary when install_dir
is specified, so just drop it.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1072
2020-03-21 20:44:43 +01:00
Florian Müllner
dc002a61eb build: Assert extension-tool version is bumped alongside gnome-shell
I always forget to keep the extension-tool version number in sync when
doing a new release. Given that it's unlikely that I'll do much better
in the future by myself, make distcheck fail when the versions don't
match.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1076
2020-03-21 18:00:25 +00:00
Марко Костић
bea3987f3e Update Serbian translation 2020-03-21 14:31:17 +00:00
Jan Tojnar
7cd37a4017 build: Add missing dependency to run-js-test
run-js-test requires girepository.h header file which is provided
by gobject-introspection. gobject-introspection is required by gjs,
another of our dependencies, so, usually, the header gets included
by inheriting the cflags from there but some distros interpret
pkg-config fields more strictly[1] so that will not be the case there.

Listing direct dependencies explicitly is a good practice any way
so let’s do that.

[1]: https://bugs.freedesktop.org/show_bug.cgi?id=105572

https://bugzilla.gnome.org/show_bug.cgi?id=787864
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1114
2020-03-21 02:58:17 +00:00
Florian Müllner
3d69fa8b9c extensionPrefs: Remove unused files
We include the regular Config module from js/misc, not the stripped-down
copy that was added in commit c8a4a9168.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1109
2020-03-21 01:50:59 +00:00
Danial Behzadi
3a52bfbc0f Update Persian translation 2020-03-20 23:36:44 +00:00
Jordi Mas
38c0f3bbf2 Update Catalan translation 2020-03-20 18:47:41 +01:00
Carlos Garnacho
7b1533caf7 padOsd: Add parameter type to keybinding-edited signal
This has a string argument, but none was defined.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/2451
2020-03-20 13:32:56 +01:00
Daniel Mustieles
afd83d929e Updated Spanish translation 2020-03-20 12:30:35 +01:00
Guillaume Bernard
d9e8a525de Update French translation 2020-03-20 07:38:32 +00:00
Kukuh Syafaat
b25142c517 Update Indonesian translation 2020-03-20 07:14:37 +00:00
Jonas Dreßler
6893fc3810 dateMenu: Show minutes for timezones that have minutes offset
Some timezones, like the one of Kathmandu don't only have hour-based
timezone offsets, but their timezones are also offset by minutes. So
instead of showing weird values like "+5.8", show the minutes properly
in a format like "+5:45".

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/2438
2020-03-19 21:16:47 +00:00
Jonas Dreßler
13ef33ae0a dateMenu: Clean up timezone offset calculation a bit
Use const variables and change some names to make showing minute-offsets
in the next commit a bit more straightforward.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1107
2020-03-19 21:16:47 +00:00
Florian Müllner
f8886468ce extensionPrefs: Initialize gettext
I misremembered that imports.package.start() would set up the correct
gettext domain, but the module only provides a convenience method
for doing that.

Use it to bring back translations in the Extensions app, whoops.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1108
2020-03-19 20:53:40 +00:00
Yuri Chornoivan
3bf0511f1b Update Ukrainian translation 2020-03-19 20:42:12 +00:00
Anders Jonsson
1b485427cf Update Swedish translation 2020-03-19 20:41:40 +00:00
Milo Casagrande
ebf04e3a95 Update Italian translation 2020-03-19 15:09:35 +00:00
Piotr Drąg
2f78b8428b Update Polish translation 2020-03-19 15:42:25 +01:00
Rafael Fontenelle
00eef6cd5d Update Brazilian Portuguese translation 2020-03-19 14:37:36 +00:00
Florian Müllner
5c031200ce extensionPrefs: Hook up kill switch to D-Bus property
Now that the org.gnome.Shell.Extensions interface exposes the
disable-user-extensions setting on D-Bus, we can use that instead
of the shell's GSettings.

In a future where we distribute the app separately as flatpak, this
will require one less hole in the sandbox.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:36 +00:00
Florian Müllner
4340260c49 extensionPrefs: Load D-Bus interface from own resource
Using the "regular" loadInterfaceXML() helper means less code duplication,
but it also ties us to the resource used by gnome-shell.

In order to untangle the extension app from core gnome-shell, change that
to load the interface from the existing data resource instead. While that
does involve reimplementing loadInterfaceXML(), it's not too bad actually
with the resource-loading code stripped (as the data resource is already
loaded by the package module).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:36 +00:00
Florian Müllner
96e534796f extensionPrefs: Make app D-Bus-activatable
This is the preferred way of launching applications nowadays.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:36 +00:00
Florian Müllner
3ee878491b extensionPrefs: Add metainfo
This is required for the app to appear properly in GNOME Software.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:36 +00:00
Florian Müllner
b92ddc0d39 extensionPrefs: Move desktop file and icons from top-level data
Another small step towards making the extensions app code
self-contained ...

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:35 +00:00
Florian Müllner
c8a4a91681 extensionPrefs: Move data/sources into subdirectories
As we will eventually move the code to a subproject, start arranging
it like a top-level srcdir.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:35 +00:00
Florian Müllner
e572d5d08c extensionPrefs: Use imports.package.start()
We want to make the extensions app code more self-contained to make it
easier to build separately, and ultimately make it available on flathub.

One complication we are facing is that it is currently all over the source
tree:
 - js/extensionPrefs for the main code
 - src for the launcher process
 - data for .desktop file and icons

Switching from a C launcher to the imports.package module allows us to
consolidate the first two, and will also take care of the annoying
setup bits (defining JS search path, extending GI lookup, loading
resources).

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1081
2020-03-19 14:27:35 +00:00
Reik Keutterling
9829d56bfa modemManager: fixed dbus path for GDBusProxy
Fix this runtime error:
JS ERROR: TypeError: this._proxy.SignalQuality is null
_reloadSignalQuality@resource:///org/gnome/shell/misc/modemManager.js:252:34
_init@resource:///org/gnome/shell/misc/modemManager.js:234:14
NMDeviceModem@resource:///org/gnome/shell/ui/status/network.js:517:34
_deviceAdded@resource:///org/gnome/shell/ui/status/network.js:1755:27

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1105
2020-03-19 12:23:26 +01:00
Peter Mráz
b30d999878 Update Slovak translation 2020-03-19 06:20:35 +00:00
Peter Mráz
35b62baf6e Update Slovak translation 2020-03-18 14:54:25 +00:00
Florian Müllner
39f61fc41c extensionSystem: Catch errors when updating extensions
Extension updates are installed at startup, so any errors that bubble
up uncaught will prevent the startup to complete.

While the most likely error reason was addressed in the previous commit
(pending update for a no-longer exitent extension), it makes sense to
catch any kind of corrupt updates to not interfere with shell startup.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2343
2020-03-17 14:19:20 +00:00
Florian Müllner
d3939a38a3 extensionDownloader: Remove pending updates with extension
When an extension is uninstalled, there is no point in keeping
a pending update: If the update didn't fail (which it currently
does), we would end up sneakily reinstalling the extension.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2343
2020-03-17 14:19:20 +00:00
Florian Müllner
b97fc02e57 networkAgent: Make searching VPN binaries asynchronous
Doing blocking IO in a graphical UI is bad, doing it in the compositor
is much much worse. So even if handling VPN requests is a relatively
rare event, doing it asynchronously is better.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2386
2020-03-17 14:07:15 +00:00
Florian Müllner
ea1adea24d shell/network-agent: Wrap nm_plugin_info_new_search_file()
While we can use the libnm API directly from JS, the call will
synchronously load the VPN service descriptions from disk.
Previously we were lowering the impact by caching the result,
but as we stopped doing that, it becomes more important to address
the issue properly and move it off to a thread.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2386
2020-03-17 14:07:15 +00:00
Florian Müllner
eb7533bbf1 networkAgent: Drop VPN plugin cache
libnm doesn't only search for plugins in the regular VPN plugin directory,
but also in the legacy location and the directory pointed to by the
NM_VPN_PLUGIN_DIR environment variable (if set).

We don't monitor the additional directories, so it's possible for our cache
to become outdated.

Instead of trying to play catch-up with libnm's internals, do what nm-applet
does and use the appropriate API to look up the plugin on each request.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2386
2020-03-17 14:07:15 +00:00
Florian Müllner
69ea038a8f extensionDownloader: Only check server if there's something to update
checkForUpdates() will currently always query the server for updates,
even when passing an empty vardict of installed extensions. We know
there won't be any updates in that case, so avoid a pointless network
request.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1100
2020-03-17 13:56:49 +00:00
Florian Müllner
b80115dc6e dateMenu: Don't ellipsize world clock time/tz
If we need to ellipsize, it should be the location name, not the time
or timezone offset.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1090
2020-03-17 13:44:30 +00:00
Florian Müllner
36b8dcbe07 a11y: Change HC icon theme first
There are two ways for applications to provide a high contrast icon:

 1. install an icon into the HighContrast theme
 2. install a symbolic icon into the default hicolor theme

The latter is preferred nowadays, and implemented in the high-contrast
CSS variant by enforcing the symbolic icon style.

However together with the way we currently enable/disable high-contrast,
this can lead to the following race:
 1. the GTK theme is changed from HighContrast
 2. we reload the default stylesheet
 3. the icon style changes to "regular", so we request a
    new icon from the HighContrast icon theme
 4. the icon theme is changed from HighContrast
 5. we evict existing icons from the cache
 6. we reload icons for the new icon theme; however as we
    find a pending request (from 3), we re-use it
 7. the request from 3 finishes, and we end up with a
    wrong icon in the cache

The simplest fix is to change the icon theme before the GTK theme: Unlike the
theme name, the icon style is encoded in the cache key, so we won't re-use
an old (and incorrect) request in that case.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2414
2020-03-17 12:45:25 +01:00
Stas Solovey
07fa5ef849 Update Russian translation 2020-03-15 21:14:23 +00:00
Cosimo Cecchi
d9a75412c3 modemManager: Look for property on correct object
The SignalQuality property is defined on the GDBusProxy, not the modem
JS object.
Fix this runtime warning:

JS WARNING: [resource:///org/gnome/shell/misc/modemManager.js 252]: reference to undefined property "SignalQuality"
JS ERROR: TypeError: this.SignalQuality is undefined
_reloadSignalQuality@resource:///org/gnome/shell/misc/modemManager.js:252:34
_init@resource:///org/gnome/shell/misc/modemManager.js:234:14
NMDeviceModem@resource:///org/gnome/shell/ui/status/network.js:517:34
_deviceAdded@resource:///org/gnome/shell/ui/status/network.js:1755:27

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1097
2020-03-14 22:59:59 +01:00
Florian Müllner
66f9a9df81 js: Always use AppSystem to lookup apps
There is no good reason for bypassing the application cache in
AppSystem and loading .desktop files again.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1093
2020-03-14 20:33:36 +01:00
Florian Müllner
4bfdd677e3 calendar: Adjust for evolution changes
When launching the default calendar application, we special-case
evolution to make sure it starts up with the calendar component.

This is currently broken in two ways:

 - evolution changed its .desktop file to use reverse DNS notation

 - as evolution can now be distributed via flatpak, we can no longer
   assume that 'evolution-calendar.desktop' exists when evolution does
   (even though we ship the .desktop file ourselves, it is considered
   invalid if the executable isn't found)

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1093
2020-03-14 20:31:00 +01:00
Florian Müllner
002160e524 main: Unwatch notification proxy name after auto-start
We only "watch" the 'org.gnome.Shell.Notifications' name to start the
service, not to actually monitor name owner changes; so unwatch the
name once that's done.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2381
2020-03-12 23:29:40 +01:00
Florian Müllner
f2df347ddb main: Activate notification proxy on start
At least for the time being, this looks like the easiest option to
launch the service:

 - we could add a systemd unit, but then we'd need to update the
   RequiredComponents in the fallback session definition as well,
   making it necessary for gnome-shell, gnome-shell-extensions and
   gnome-session to be updated to 3.36.1 in lockstep

 - autostart is problematic as it would make gnome-shell conflict
   with other notification daemons; also autostart is most useful
   with automatic shutdown, which would require tracking signal
   subscriber to determine when the service is unused

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/547
2020-03-12 16:09:27 +00:00
Florian Müllner
799bbdb503 dbusServices/notifications: Add a separate notification daemon
Add a small service that exposes the Fdo notification API under the
well-known name, and forwards any requests to the actual implementation
in the shell.

That way any app with permission to talk to org.freedesktop.Notifications
will get exactly that, and nothing more.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/547
2020-03-12 16:09:27 +00:00
Florian Müllner
1aff64a38b main: Stop owning the public org.freedesktop.Notifications name
For sandboxed apps, permission to talk to org.freedesktop.Notifications
looks innocent enough. However as all exported services share the same
connection to the session bus, that permission actually grants an app
access to *any* shell D-Bus API.

While we want apps to use the notification portal, it is still common
for apps to use libnotify, raw D-Bus calls or even notify-send.

We don't want to give those apps a way to circumvent most of the sandbox
restrictions, so stop owning the org.freedesktop.Notifications name.

In a next step we will implement a separate notification-daemon that
exposes the API on the well-known address and proxies any requests to
the real implementation in gnome-shell.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/547
2020-03-12 16:09:27 +00:00
Florian Müllner
574c560677 dbusServices: Add some base classes for small stand-alone services
There are a couple of D-Bus services that are currently provided by
gnome-shell for which it makes sense to move them fully or partially
into separate processes:

 - screen recording (performance)
 - FDO notifications (security)
 - Extensions (portalization)

Add some base classes and build system glue to take care of the
common boilerplate.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/547
2020-03-12 16:09:27 +00:00
Christian Hergert
f8db5aa106 app-cache: add ShellAppCache for GAppInfo caching
This caches GAppInfo so that the compositor thread does not have to perform
costly disk access to load them. Instead, they are loaded from a worker
thread and the ShellAppCache notifies of changes.

To simplify maintenance, ShellAppCache manages this directly and the
existing ShellAppSystem wraps the cache. We may want to graft these
together in the future, but now it provides the easiest way to backport
changes to older Shell releases.

Another source of compositor thread disk access was in determining the
name for an application directory. Translations are provided via GKeyFile
installed in "desktop-directories". Each time we would build the name
for a label (or update it) we would have to load all of these files.

Instead, the ShellAppCache caches that information and updates the cache
in bulk when those change. We can reduce this in the future to do less
work, but chances are these will come together anyway so that is probably
worth fixing if we ever come across it.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2282
2020-03-11 18:06:15 -07:00
Carlos Garnacho
b18469427e St: Ensure to update entry hint visibility with IM preedit
Commit 88ac339774 changed StEntry behavior so the text hint would
stay visible while focused, as long as the text buffer is empty.
However, IMs that use preedit still should count as "started typing",
while the text buffer is still officially empty.

To fix this, check on st_entry_update_hint_visibility() that there's
indeed no preedit buffer before showing the hint. We can't directly
listen to internal preedit buffer changes in ClutterText, so handle
preedit buffer updates through the ::cursor-changed signal that will
be indirectly emitted.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1084
2020-03-11 14:07:07 +01:00
Carlos Garnacho
72c4f148ef windowManager: Do not shutdown ibus/xsettings on X11 compositor restart
These paths are meant for Xwayland, not for X11 compositors being restarted
through alt-f2 + r. Maybe some signal analogous to init-xserver should be
added for Xwayland shutdown paths, but this signal we are currently
listening for is backend agnostic.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/2329
2020-03-11 11:31:45 +00:00
Alynx Zhou
94f6976ddd ibusManager: fix ibus launch error because of wrong method name
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1080
2020-03-11 09:30:15 +00:00
Tim Sabsch
be187f4149 Update German translation 2020-03-09 22:40:48 +00:00
Sebastian Keller
1b872c1195 st/texture-cache: Fix invalid memory write related to X11 window icons
st_texture_cache_bind_weak_notify calls g_clear_signal_handler which
then calls st_texture_cache_free_bind. st_texture_cache_free_bind frees
the bind structure, so by the time g_clear_signal_handler tries to write
bind->notify_signal_id, bind has already been freed.

Fix this by using g_signal_handler_disconnect instead.

This partially reverts 135d178d08

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/2334
2020-03-09 17:34:56 +00:00
Kjartan Maraas
89f2187d72 Update Norwegian Bokmål translation 2020-03-09 15:40:01 +00:00
Daniel Rusek
943df86cb0 Update Czech translation 2020-03-09 15:18:03 +00:00
Florian Müllner
a5a6c699c3 fileUtils: Remove some compatibility code
The condition was added to keep working with the then-stable version
of gjs. We already require a more recent version now, so the compat
code is effectively dead.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1077
2020-03-09 13:18:48 +00:00
Florian Müllner
a0467bf875 js: Remove unused files from resources
fileUtils hasn't used Params since 2013, but was still importing it
until commit a1534dab02 ...

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1077
2020-03-09 13:18:48 +00:00
sicklylife
b1da3ae772 Update Japanese translation 2020-03-09 12:47:10 +00:00
Marek Černocký
8af466e34d Updated Czech translation 2020-03-09 13:45:33 +01:00
sicklylife
66c7616892 Update Japanese translation 2020-03-09 12:35:24 +00:00
Sebastian Keller
c05098cd12 st/password-entry: Fix peek icon leak
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1078
2020-03-08 23:51:55 +01:00
Jordi Mas
4723dd1f4c Update Catalan translation 2020-03-08 21:57:46 +01:00
Марко Костић
7e4c32ec1f Update Serbian translation 2020-03-08 20:21:47 +00:00
Florian Müllner
3d443d5b17 extensionPrefs: Stop escaping extension name
The corresponding label no longer uses markup, so we can and should use
the unescaped name.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2305
2020-03-08 01:07:18 +00:00
Jonas Dreßler
3155d03d9e appDisplay: Reload folder views on installed app changes
Since the FolderViews are not connected to the "installed-changed"
signal, we need to reload their apps by calling _redisplay() when an app
is removed or installed. We can't connect to "installed-changed" inside
FolderView because we need to ensure _redisplay() of the FolderView is
called before AppView tries to access the apps of the folder inside
_refilterApps(). So reload the FolderViews inside AllViews _redisplay()
implementation to ensure everything is up to date before accessing the
apps of the folder.

Since the "apps-changed" signal of FolderIcon now indirectly triggers a
_redisplay() of the FolderViews, the 'changed' handler of FolderView is
now redundant and can be removed. Because of this, we also need to move
the emission of the "apps-changed" signal to the start of the signal
handler to make sure the view is updated before we try to access items
of the view.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1901

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
989c0ee49e appDisplay: Disconnect folder "changed" signal when actors are destroyed
We should disconnect the folders "changed" signal from the folder in
case the FolderView or FolderIcon is destroyed. While at it, also remove
the unused this._spaceReadySignalId of FolderIcon.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
69f6c43b60 appDisplay: Implement an addApp() method for FolderViews
Similar to removeApp(), implement addApp() in FolderView to make adding
folders to views a bit more obvious.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
bf2d012e40 appDisplay: Rename _redisplay() functions unrelated to BaseAppView
The _redisplay() function is usally used for subclasses of BaseAppView
which want to implement their own _redisplay() function, having that
function name in two classes which have nothing to do with BaseAppView
can be quite confusing. Make those names less confusing and call the
functions  _sync() and _rebuildMenu() instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
4d773a5ce9 appDisplay: Add back background color for folder icons
During this cycle the background color of the folder icons was removed
(probably fallout from the theme refactor), this was not intended
design-wise, so add it back.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
711d4ba65c appDisplay: Properly hide overlay scrollbar in folders
Use the scroll-view policy ST_POLICY_EXTERNAL to hide the scrollbar
instead of setting its css properties to hidden, where it can still be
clicked but isn't visible.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
4490463513 appDisplay: Remove the top padding from folder app grid
There already is a bottom padding defined for the name container of the
folder, so remove the top padding of the appGrid and make sure we can
show a few more icons inside the folder.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Jonas Dreßler
1ec5117715 appDisplay: Take the viewBox into account for the contentBox of folders
The viewBox has a border applied, so when we call adaptToSize using only
the content box of the container, the width of the border is not removed
from the content box and the grid will be allocated less space than what
we told it before using adaptToSize.

Fix that by adjusting the content box for the size of the viewBox, too.
This makes sure the correct amount of columns can be shown inside a
folder, since right now we only show 3 colums even though 4 would fit.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1011
2020-03-08 00:42:40 +00:00
Florian Müllner
bf367daaba build: Fix some harmless compiler warnings
Some (newer?) GCC versions complain when a g_auto variable isn't
initialized when declared, even when the initialization is guaranteed
to happen before the variable is used or goes out of scope.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2298
2020-03-08 00:34:36 +00:00
Florian Müllner
1de008f2d5 Bump extensions-tool version as well 2020-03-08 01:33:20 +01:00
237 changed files with 23032 additions and 12516 deletions

View File

@@ -1,9 +1,13 @@
include: 'https://gitlab.gnome.org/GNOME/citemplates/raw/master/flatpak/flatpak_ci_initiative.yml'
stages: stages:
- review - review
- build - build
- test - test
- deploy
variables: variables:
BUNDLE: "extensions-git.flatpak"
JS_LOG: "js-report.txt" JS_LOG: "js-report.txt"
POT_LOG: "pot-update.txt" POT_LOG: "pot-update.txt"
@@ -14,7 +18,7 @@ variables:
- merge_requests - merge_requests
check_commit_log: check_commit_log:
image: registry.gitlab.gnome.org/gnome/mutter/master:v3 image: registry.gitlab.gnome.org/gnome/mutter/master:v4
stage: review stage: review
variables: variables:
GIT_DEPTH: "100" GIT_DEPTH: "100"
@@ -61,7 +65,7 @@ no_template_check:
<<: *only_default <<: *only_default
build: build:
image: registry.gitlab.gnome.org/gnome/mutter/master:v3 image: registry.gitlab.gnome.org/gnome/mutter/master:v4
stage: build stage: build
before_script: before_script:
- .gitlab-ci/checkout-mutter.sh - .gitlab-ci/checkout-mutter.sh
@@ -79,7 +83,7 @@ build:
- build - build
test: test:
image: registry.gitlab.gnome.org/gnome/mutter/master:v3 image: registry.gitlab.gnome.org/gnome/mutter/master:v4
stage: test stage: test
variables: variables:
XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir" XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir"
@@ -96,7 +100,7 @@ test:
when: on_failure when: on_failure
test-pot: test-pot:
image: registry.gitlab.gnome.org/gnome/mutter/master:v3 image: registry.gitlab.gnome.org/gnome/mutter/master:v4
stage: test stage: test
before_script: before_script:
- ninja -C mutter/build install - ninja -C mutter/build install
@@ -110,3 +114,20 @@ test-pot:
' | tee $POT_LOG ' | tee $POT_LOG
- (! grep -q . $POT_LOG) - (! grep -q . $POT_LOG)
<<: *only_default <<: *only_default
flatpak:
stage: build
variables:
SUBPROJECT: "subprojects/extensions-app"
# Your manifest path
MANIFEST_PATH: "$SUBPROJECT/build-aux/flatpak/org.gnome.Extensions.json"
RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
FLATPAK_MODULE: "gnome-extensions-app"
APP_ID: "org.gnome.Extensions"
extends: .flatpak
<<: *only_default
nightly:
extends: '.publish_nightly'
variables:
BUNDLES: '$BUNDLE'

View File

@@ -6,6 +6,11 @@ globs=('*.js' '*.c')
# find source files that contain gettext keywords # find source files that contain gettext keywords
files=$(grep -lR ${globs[@]/#/--include=} '\(gettext\|[^I_)]_\)(' $srcdirs) files=$(grep -lR ${globs[@]/#/--include=} '\(gettext\|[^I_)]_\)(' $srcdirs)
# filter out excluded files
if [ -f po/POTFILES.skip ]; then
files=$(for f in $files; do ! grep -q ^$f po/POTFILES.skip && echo $f; done)
fi
# find those that aren't listed in POTFILES.in # find those that aren't listed in POTFILES.in
missing=$(for f in $files; do ! grep -q ^$f po/POTFILES.in && echo $f; done) missing=$(for f in $files; do ! grep -q ^$f po/POTFILES.in && echo $f; done)

View File

@@ -23,7 +23,7 @@ run_eslint() {
mkdir -p $(dirname $output) mkdir -p $(dirname $output)
touch $output touch $output
eslint -f unix ${!extra_args} -o $output js eslint -f unix ${!extra_args} -o $output js subprojects/extensions-app/js
} }
list_commit_range_additions() { list_commit_range_additions() {

View File

@@ -161,12 +161,16 @@ def convert_file(source_file, destination_path):
try: try:
xkb_name = locale_to_xkb(root["locale"], root["name"]) xkb_name = locale_to_xkb(root["locale"], root["name"])
except KeyError as e: except KeyError as e:
logging.warn(e) logging.warning(e)
return False return False
destination_file = os.path.join(destination_path, xkb_name + ".json") destination_file = os.path.join(destination_path, xkb_name + ".json")
with open(destination_file, 'w', encoding="utf-8") as dest_fd: try:
json.dump(root, dest_fd, ensure_ascii=False, indent=2, sort_keys=True) with open(destination_file, 'x', encoding="utf-8") as dest_fd:
json.dump(root, dest_fd, ensure_ascii=False, indent=2, sort_keys=True)
except FileExistsError as e:
logging.info("File %s exists, not updating", destination_file)
return False
logging.debug("written %s", destination_file) logging.debug("written %s", destination_file)

View File

@@ -1,12 +1,19 @@
<node> <node>
<interface name="org.gnome.Shell.CalendarServer"> <interface name="org.gnome.Shell.CalendarServer">
<method name="GetEvents"> <method name="SetTimeRange">
<arg type="x" direction="in" /> <arg type="x" name="since" direction="in"/>
<arg type="x" direction="in" /> <arg type="x" name="until" direction="in"/>
<arg type="b" direction="in" /> <arg type="b" name="force_reload" direction="in"/>
<arg type="a(sssbxxa{sv})" direction="out" />
</method> </method>
<signal name="EventsAddedOrUpdated">
<arg type="a(ssbxxa{sv})" name="events" direction="out"/>
</signal>
<signal name="EventsRemoved">
<arg type="as" name="ids" direction="out"/>
</signal>
<signal name="ClientDisappeared">
<arg type="s" name="source_uid" direction="out"/>
</signal>
<property name="HasCalendars" type="b" access="read" /> <property name="HasCalendars" type="b" access="read" />
<signal name="Changed" />
</interface> </interface>
</node> </node>

View File

@@ -19,6 +19,10 @@ Before=gnome-session-initialized.target
[Service] [Service]
Type=notify Type=notify
ExecStart=@bindir@/gnome-shell ExecStart=@bindir@/gnome-shell
# unset some environment variables that were set by the shell and won't work now that the shell is gone
ExecStopPost=-systemctl --user unset-environment GNOME_SETUP_DISPLAY WAYLAND_DISPLAY DISPLAY XAUTHORITY
# Exit code 1 means we are probably *not* dealing with an extension failure # Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1 SuccessExitStatus=1
# On wayland we cannot restart # On wayland we cannot restart

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,6 +1,6 @@
desktop_files = [ desktop_files = [
'org.gnome.Shell.desktop', 'org.gnome.Shell.desktop',
'org.gnome.Extensions.desktop', 'org.gnome.Shell.Extensions.desktop',
] ]
service_files = [] service_files = []

View File

@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
# Keep in sync with subprojects/extensions-app
Name=Extensions
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
Icon=org.gnome.Shell.Extensions
# Never launch this, just provide name+icon to portal dialog
Exec=false
OnlyShowIn=GNOME;
NoDisplay=true

View File

@@ -12,7 +12,9 @@
"w" "w"
], ],
[ [
"e" "e",
"é",
"ë"
], ],
[ [
"r" "r"
@@ -21,30 +23,58 @@
"t" "t"
], ],
[ [
"y" "y",
"ý",
"ÿ"
], ],
[ [
"u" "u",
"ú",
"ü",
"û",
"ù",
"ū"
], ],
[ [
"i" "i",
"í",
"ï"
], ],
[ [
"o" "o",
"ó",
"ô",
"ò",
"õ",
"œ",
"ō"
], ],
[ [
"p" "p"
],
[
"å"
] ]
], ],
[ [
[ [
"a" "a",
"á",
"ä",
"à",
"â",
"ã",
"ā"
], ],
[ [
"s" "s",
"ß",
"ś",
"š"
], ],
[ [
"d" "d",
"ð"
], ],
[ [
"f" "f"
@@ -62,7 +92,16 @@
"k" "k"
], ],
[ [
"l" "l",
"ł"
],
[
"ø",
"ö"
],
[
"æ",
"ä"
] ]
], ],
[ [
@@ -82,7 +121,9 @@
"b" "b"
], ],
[ [
"n" "n",
"ñ",
"ń"
], ],
[ [
"m" "m"
@@ -121,7 +162,9 @@
"W" "W"
], ],
[ [
"E" "E",
"É",
"Ë"
], ],
[ [
"R" "R"
@@ -130,30 +173,58 @@
"T" "T"
], ],
[ [
"Y" "Y",
"Ý",
"Ÿ"
], ],
[ [
"U" "U",
"Ú",
"Ü",
"Û",
"Ù",
"Ū"
], ],
[ [
"I" "I",
"Í",
"Ï"
], ],
[ [
"O" "O",
"Ó",
"Ô",
"Ò",
"Õ",
"Œ",
"Ō"
], ],
[ [
"P" "P"
],
[
"Å"
] ]
], ],
[ [
[ [
"A" "A",
"Á",
"Ä",
"À",
"Â",
"Ã",
"Ā"
], ],
[ [
"S" "S",
"SS",
"Ś",
"Š"
], ],
[ [
"D" "D",
"Ð"
], ],
[ [
"F" "F"
@@ -171,7 +242,16 @@
"K" "K"
], ],
[ [
"L" "L",
"Ł"
],
[
"Ø",
"Ö"
],
[
"Æ",
"Ä"
] ]
], ],
[ [
@@ -191,7 +271,9 @@
"B" "B"
], ],
[ [
"N" "N",
"Ñ",
"Ń"
], ],
[ [
"M" "M"
@@ -277,10 +359,10 @@
"#" "#"
], ],
[ [
"$", "",
"¢", "¢",
"£", "£",
"", "$",
"¥", "¥",
"₱" "₱"
], ],
@@ -419,13 +501,14 @@
"£" "£"
], ],
[ [
"¥"
],
[
"$",
"¢" "¢"
], ],
[ [
"" "¢"
],
[
"¥"
], ],
[ [
"^", "^",
@@ -504,4 +587,4 @@
], ],
"locale": "nb", "locale": "nb",
"name": "Norwegian Bokmål" "name": "Norwegian Bokmål"
} }

View File

@@ -35,9 +35,9 @@ $app_grid_fg_color: #fff;
} }
/* App Folders */ /* App Folders */
.app-folder { .app-well-app.app-folder {
.overview-icon { background-color: transparentize($osd_bg_color, 0.8);
} border-radius: $base_border_radius + 4px; // same as %icon_tile
} }
// expanded folder // expanded folder
@@ -73,10 +73,6 @@ $app_grid_fg_color: #fff;
& > StIcon { icon-size: 16px } & > StIcon { icon-size: 16px }
} }
} }
& StButton#vhandle,
& StButton#vhandle:hover,
& StButton#vhandle:active { background-color: transparent; }
} }
.app-folder-dialog-container { .app-folder-dialog-container {
padding: 12px; padding: 12px;

View File

@@ -153,9 +153,11 @@
} }
.calendar-day-with-events { .calendar-day-with-events {
color: lighten($fg_color,10%);
font-weight: bold;
background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg");
&.calendar-work-day {
color: lighten($fg_color,10%);
font-weight: bold;
}
} }
.calendar-other-month-day { .calendar-other-month-day {

View File

@@ -15,13 +15,11 @@
border: 1px solid transparent; border: 1px solid transparent;
&:outlined { &:outlined {
border: 1px solid darken($borders_color,5%); background-color: transparentize($osd_fg_color, 0.7);
background-color: transparentize($osd_fg_color, 0.9);
box-shadow: inset 0 2px 2px 0 rgba(0,0,0,0.4);
} }
&:selected { &:selected {
background-color: transparentize($osd_fg_color, 0.9); background-color: transparentize($osd_fg_color, 0.7);
color: $osd_fg_color; color: $osd_fg_color;
} }
} }

View File

@@ -1,10 +1,9 @@
#!/bin/env bash #!/bin/env bash
CLDR_LAYOUTS_TARBALL="http://www.unicode.org/Public/cldr/latest/keyboards.zip" CLDR_LAYOUTS_TARBALL="http://www.unicode.org/Public/cldr/latest/keyboards.zip"
CLDR2JSON_GIT="git://repo.or.cz/cldr2json.git"
WORKDIR=".osk-layout-workbench" WORKDIR=".osk-layout-workbench"
CLDR2JSON="$WORKDIR/cldr2json/cldr2json.py" CLDR2JSON="cldr2json/cldr2json.py"
SRCDIR="$WORKDIR/keyboards/android" SRCDIR="$WORKDIR/keyboards/android"
DESTDIR="osk-layouts" DESTDIR="osk-layouts"
GRESOURCE_FILE="gnome-shell-osk-layouts.gresource.xml" GRESOURCE_FILE="gnome-shell-osk-layouts.gresource.xml"
@@ -20,7 +19,6 @@ mkdir -p "osk-layouts"
# Download stuff on the work dir # Download stuff on the work dir
pushd $WORKDIR pushd $WORKDIR
gio copy $CLDR_LAYOUTS_TARBALL . gio copy $CLDR_LAYOUTS_TARBALL .
git clone $CLDR2JSON_GIT
unzip keyboards.zip unzip keyboards.zip
popd popd

View File

@@ -0,0 +1,5 @@
imports.package.start({
name: '@PACKAGE_NAME@',
prefix: '@prefix@',
libdir: '@libdir@',
});

View File

@@ -0,0 +1,3 @@
[D-BUS Service]
Name=@service@
Exec=@gjs@ @pkgdatadir@/@service@

View File

@@ -0,0 +1,177 @@
/* exported DBusService, ServiceImplementation */
const { Gio, GLib } = imports.gi;
const Signals = imports.signals;
const IDLE_SHUTDOWN_TIME = 2; // s
var ServiceImplementation = class {
constructor(info, objectPath) {
this._objectPath = objectPath;
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(info, this);
this._injectTracking('return_dbus_error');
this._injectTracking('return_error_literal');
this._injectTracking('return_gerror');
this._injectTracking('return_value');
this._injectTracking('return_value_with_unix_fd_list');
this._senders = new Map();
this._holdCount = 0;
this._hasSignals = this._dbusImpl.get_info().signals.length > 0;
this._shutdownTimeoutId = 0;
// subclasses may override this to disable automatic shutdown
this._autoShutdown = true;
}
// subclasses may override this to own additional names
register() {
}
export() {
this._dbusImpl.export(Gio.DBus.session, this._objectPath);
}
unexport() {
this._dbusImpl.unexport();
}
hold() {
this._holdCount++;
}
release() {
if (this._holdCount === 0) {
logError(new Error('Unmatched call to release()'));
return;
}
this._holdCount--;
if (this._holdCount === 0)
this._queueShutdownCheck();
}
/**
* _handleError:
* @param {Gio.DBusMethodInvocation}
* @param {Error}
*
* Complete @invocation with an appropriate error if @error is set;
* useful for implementing early returns from method implementations.
*
* @returns {bool} - true if @invocation was completed
*/
_handleError(invocation, error) {
if (error === null)
return false;
if (error instanceof GLib.Error) {
invocation.return_gerror(error);
} else {
let name = error.name;
if (!name.includes('.')) // likely a normal JS error
name = `org.gnome.gjs.JSError.${name}`;
invocation.return_dbus_error(name, error.message);
}
return true;
}
_maybeShutdown() {
if (!this._autoShutdown)
return;
if (this._holdCount > 0)
return;
this.emit('shutdown');
}
_queueShutdownCheck() {
if (this._shutdownTimeoutId)
GLib.source_remove(this._shutdownTimeoutId);
this._shutdownTimeoutId = GLib.timeout_add_seconds(
GLib.PRIORITY_DEFAULT, IDLE_SHUTDOWN_TIME,
() => {
this._shutdownTimeoutId = 0;
this._maybeShutdown();
return GLib.SOURCE_REMOVE;
});
}
_trackSender(sender) {
if (this._senders.has(sender))
return;
this.hold();
this._senders.set(sender,
this._dbusImpl.get_connection().watch_name(
sender,
Gio.BusNameWatcherFlags.NONE,
null,
() => this._untrackSender(sender)));
}
_untrackSender(sender) {
const id = this._senders.get(sender);
if (id)
this._dbusImpl.get_connection().unwatch_name(id);
if (this._senders.delete(sender))
this.release();
}
_injectTracking(methodName) {
const { prototype } = Gio.DBusMethodInvocation;
const origMethod = prototype[methodName];
const that = this;
prototype[methodName] = function (...args) {
origMethod.apply(this, args);
if (that._hasSignals)
that._trackSender(this.get_sender());
that._queueShutdownCheck();
};
}
};
Signals.addSignalMethods(ServiceImplementation.prototype);
var DBusService = class {
constructor(name, service) {
this._name = name;
this._service = service;
this._loop = new GLib.MainLoop(null, false);
this._service.connect('shutdown', () => this._loop.quit());
}
run() {
// Bail out when not running under gnome-shell
Gio.DBus.watch_name(Gio.BusType.SESSION,
'org.gnome.Shell',
Gio.BusNameWatcherFlags.NONE,
null,
() => this._loop.quit());
this._service.register();
Gio.DBus.own_name(Gio.BusType.SESSION,
this._name,
Gio.BusNameOwnerFlags.REPLACE,
() => this._service.export(),
null,
() => this._loop.quit());
this._loop.run();
}
};

View File

@@ -0,0 +1,2 @@
.expander-frame > * { border-top-width: 0; }
.expander-toolbar { border: 0 solid @borders; border-top-width: 1px; }

View File

@@ -0,0 +1,274 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ExtensionsService */
const { Gdk, Gio, GLib, GObject, Gtk, Shew } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const { loadInterfaceXML } = imports.misc.fileUtils;
const { ServiceImplementation } = imports.dbusService;
const ExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
const ExtensionsProxy = Gio.DBusProxy.makeProxyWrapper(ExtensionsIface);
var ExtensionsService = class extends ServiceImplementation {
constructor() {
super(ExtensionsIface, '/org/gnome/Shell/Extensions');
this._proxy = new ExtensionsProxy(Gio.DBus.session,
'org.gnome.Shell', '/org/gnome/Shell');
this._proxy.connectSignal('ExtensionStateChanged',
(proxy, sender, params) => {
this._dbusImpl.emit_signal('ExtensionStateChanged',
new GLib.Variant('(sa{sv})', params));
});
this._proxy.connect('g-properties-changed', () => {
this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
new GLib.Variant('b', this._proxy.UserExtensionsEnabled));
});
}
get ShellVersion() {
return this._proxy.ShellVersion;
}
get UserExtensionsEnabled() {
return this._proxy.UserExtensionsEnabled;
}
set UserExtensionsEnabled(enable) {
this._proxy.UserExtensionsEnabled = enable;
}
ListExtensionsAsync(params, invocation) {
this._proxy.ListExtensionsRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(a{sa{sv}})', res));
});
}
GetExtensionInfoAsync(params, invocation) {
this._proxy.GetExtensionInfoRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(a{sv})', res));
});
}
GetExtensionErrorsAsync(params, invocation) {
this._proxy.GetExtensionErrorsRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(as)', res));
});
}
InstallRemoteExtensionAsync(params, invocation) {
this._proxy.InstallRemoteExtensionRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(s)', res));
});
}
UninstallExtensionAsync(params, invocation) {
this._proxy.UninstallExtensionRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(b)', res));
});
}
EnableExtensionAsync(params, invocation) {
this._proxy.EnableExtensionRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(b)', res));
});
}
DisableExtensionAsync(params, invocation) {
this._proxy.DisableExtensionRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(b)', res));
});
}
LaunchExtensionPrefsAsync([uuid], invocation) {
this.OpenExtensionPrefsAsync([uuid, '', {}], invocation);
}
OpenExtensionPrefsAsync(params, invocation) {
const [uuid, parentWindow, options] = params;
this._proxy.GetExtensionInfoRemote(uuid, (res, error) => {
if (this._handleError(invocation, error))
return;
const [serialized] = res;
const extension = ExtensionUtils.deserializeExtension(serialized);
const window = new ExtensionPrefsDialog(extension);
window.realize();
let externalWindow = null;
if (parentWindow)
externalWindow = Shew.ExternalWindow.new_from_handle(parentWindow);
if (externalWindow)
externalWindow.set_parent_of(window.window);
if (options.modal)
window.modal = options.modal.get_boolean();
window.connect('destroy', () => this.release());
this.hold();
window.show();
invocation.return_value(null);
});
}
CheckForUpdatesAsync(params, invocation) {
this._proxy.CheckForUpdatesRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(null);
});
}
};
var ExtensionPrefsDialog = GObject.registerClass({
GTypeName: 'ExtensionPrefsDialog',
Template: 'resource:///org/gnome/Shell/Extensions/ui/extension-prefs-dialog.ui',
InternalChildren: [
'headerBar',
'stack',
'expander',
'expanderArrow',
'revealer',
'errorView',
],
}, class ExtensionPrefsDialog extends Gtk.Window {
_init(extension) {
super._init();
this._uuid = extension.uuid;
this._url = extension.metadata.url || '';
this._headerBar.title = extension.metadata.name;
this._actionGroup = new Gio.SimpleActionGroup();
this.insert_action_group('win', this._actionGroup);
this._initActions();
this._addCustomStylesheet();
this._gesture = new Gtk.GestureMultiPress({
widget: this._expander,
button: 0,
exclusive: true,
});
this._gesture.connect('released', (gesture, nPress) => {
if (nPress === 1)
this._revealer.reveal_child = !this._revealer.reveal_child;
});
this._revealer.connect('notify::reveal-child', () => {
this._expanderArrow.icon_name = this._revealer.reveal_child
? 'pan-down-symbolic'
: 'pan-end-symbolic';
});
try {
ExtensionUtils.installImporter(extension);
// give extension prefs access to their own extension object
ExtensionUtils.getCurrentExtension = () => extension;
const prefsModule = extension.imports.prefs;
prefsModule.init(extension.metadata);
const widget = prefsModule.buildPrefsWidget();
this._stack.add(widget);
this._stack.visible_child = widget;
} catch (e) {
this._setError(e);
}
}
_setError(exc) {
this._errorView.buffer.text = `${exc}\n\nStack trace:\n`;
// Indent stack trace.
this._errorView.buffer.text +=
exc.stack.split('\n').map(line => ` ${line}`).join('\n');
// markdown for pasting in gitlab issues
let lines = [
`The settings of extension ${this._uuid} had an error:`,
'```',
`${exc}`,
'```',
'',
'Stack trace:',
'```',
exc.stack.replace(/\n$/, ''), // stack without trailing newline
'```',
'',
];
this._errorMarkdown = lines.join('\n');
this._actionGroup.lookup('copy-error').enabled = true;
}
_initActions() {
let action;
action = new Gio.SimpleAction({
name: 'copy-error',
enabled: false,
});
action.connect('activate', () => {
const clipboard = Gtk.Clipboard.get_default(this.get_display());
clipboard.set_text(this._errorMarkdown, -1);
});
this._actionGroup.add_action(action);
action = new Gio.SimpleAction({
name: 'show-url',
enabled: this._url !== '',
});
action.connect('activate', () => {
Gio.AppInfo.launch_default_for_uri(this._url,
this.get_display().get_app_launch_context());
});
this._actionGroup.add_action(action);
}
_addCustomStylesheet() {
let provider = new Gtk.CssProvider();
let uri = 'resource:///org/gnome/Shell/Extensions/css/application.css';
try {
provider.load_from_file(Gio.File.new_for_uri(uri));
} catch (e) {
logError(e, 'Failed to add application style');
}
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
});

View File

@@ -0,0 +1,20 @@
/* exported main */
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.Gtk = '3.0';
const { Gtk } = imports.gi;
const pkg = imports.package;
const { DBusService } = imports.dbusService;
const { ExtensionsService } = imports.extensionsService;
function main() {
Gtk.init(null);
pkg.initFormat();
const service = new DBusService(
'org.gnome.Shell.Extensions',
new ExtensionsService());
service.run();
}

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="ExtensionPrefsDialog" parent="GtkWindow">
<property name="default_width">600</property>
<property name="default_height">400</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerBar">
<property name="visible">True</property>
<property name="show_close_button">True</property>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="hscrollbar_policy">never</property>
<property name="propagate_natural_height">True</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">100</property>
<property name="margin_bottom">60</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Somethings gone wrong</property>
<attributes>
<attribute name="scale" value="1.44"/> <!-- x-large -->
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Were very sorry, but theres been a problem: the settings for this extension cant be displayed. We recommend that you report the issue to the extension authors.</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin_top">12</property>
<child>
<object class="GtkFrame" id="expander">
<property name="visible">True</property>
<property name="hexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="margin">12</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="expanderArrow">
<property name="visible">True</property>
<property name="icon_name">pan-end-symbolic</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Technical Details</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkRevealer" id="revealer">
<property name="visible">True</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="shadow_type">in</property>
<style>
<class name="expander-frame"/>
</style>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkTextView" id="errorView">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="monospace">True</property>
<property name="editable">False</property>
<property name="wrap_mode">word</property>
<property name="left_margin">12</property>
<property name="right_margin">12</property>
<property name="top_margin">12</property>
<property name="bottom_margin">12</property>
</object>
</child>
<child>
<object class="GtkToolbar">
<property name="visible">True</property>
<style>
<class name="expander-toolbar"/>
</style>
<child>
<object class="GtkToolItem">
<property name="visible">True</property>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">win.copy-error</property>
<style>
<class name="flat"/>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">edit-copy-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparatorToolItem">
<property name="visible">True</property>
<property name="draw">False</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkToolItem">
<property name="visible">True</property>
<child>
<object class="GtkButton" id="homeButton">
<property name="visible"
bind-source="homeButton"
bind-property="sensitive"
bind-flags="sync-create"/>
<property name="label" translatable="yes">Homepage</property>
<property name="tooltip_text" translatable="yes">Visit extension homepage</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="action_name">win.show-url</property>
<style>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,42 @@
launcherconf = configuration_data()
launcherconf.set('PACKAGE_NAME', meson.project_name())
launcherconf.set('prefix', prefix)
launcherconf.set('libdir', libdir)
dbus_services = {
'org.gnome.Shell.Extensions': 'extensions',
'org.gnome.Shell.Notifications': 'notifications',
}
config_dir = '@0@/..'.format(meson.current_build_dir())
foreach service, dir : dbus_services
configure_file(
input: 'dbus-service.in',
output: service,
configuration: launcherconf,
install_dir: pkgdatadir,
)
serviceconf = configuration_data()
serviceconf.set('service', service)
serviceconf.set('gjs', gjs.path())
serviceconf.set('pkgdatadir', pkgdatadir)
configure_file(
input: 'dbus-service.service.in',
output: service + '.service',
configuration: serviceconf,
install_dir: servicedir
)
gnome.compile_resources(
service + '.src',
service + '.src.gresource.xml',
dependencies: [config_js],
source_dir: ['.', '..', dir, config_dir],
gresource_bundle: true,
install: true,
install_dir: pkgdatadir
)
endforeach

View File

@@ -0,0 +1,11 @@
/* exported main */
const { DBusService } = imports.dbusService;
const { NotificationDaemon } = imports.notificationDaemon;
function main() {
const service = new DBusService(
'org.gnome.Shell.Notifications',
new NotificationDaemon());
service.run();
}

View File

@@ -0,0 +1,80 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported NotificationDaemon */
const { Gio, GLib } = imports.gi;
const { loadInterfaceXML } = imports.misc.fileUtils;
const { ServiceImplementation } = imports.dbusService;
const NotificationsIface = loadInterfaceXML('org.freedesktop.Notifications');
const NotificationsProxy = Gio.DBusProxy.makeProxyWrapper(NotificationsIface);
var NotificationDaemon = class extends ServiceImplementation {
constructor() {
super(NotificationsIface, '/org/freedesktop/Notifications');
this._autoShutdown = false;
this._proxy = new NotificationsProxy(Gio.DBus.session,
'org.gnome.Shell',
'/org/freedesktop/Notifications',
(proxy, error) => {
if (error)
log(error.message);
});
this._proxy.connectSignal('ActionInvoked',
(proxy, sender, params) => {
this._dbusImpl.emit_signal('ActionInvoked',
new GLib.Variant('(us)', params));
});
this._proxy.connectSignal('NotificationClosed',
(proxy, sender, params) => {
this._dbusImpl.emit_signal('NotificationClosed',
new GLib.Variant('(uu)', params));
});
}
register() {
Gio.DBus.session.own_name(
'org.freedesktop.Notifications',
Gio.BusNameOwnerFlags.REPLACE,
null, null);
}
NotifyAsync(params, invocation) {
this._proxy.NotifyRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(u)', res));
});
}
CloseNotificationAsync(params, invocation) {
this._proxy.CloseNotificationRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(null);
});
}
GetCapabilitiesAsync(params, invocation) {
this._proxy.GetCapabilitiesRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(as)', res));
});
}
GetServerInformationAsync(params, invocation) {
this._proxy.GetServerInformationRemote(...params, (res, error) => {
if (this._handleError(invocation, error))
return;
invocation.return_value(new GLib.Variant('(ssss)', res));
});
}
};

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Shell/Extensions/js">
<file>main.js</file>
<file>extensionsService.js</file>
<file>dbusService.js</file>
<file>misc/config.js</file>
<file>misc/extensionUtils.js</file>
<file>misc/fileUtils.js</file>
<file>misc/params.js</file>
</gresource>
<gresource prefix="/org/gnome/Shell/Extensions">
<file>css/application.css</file>
<file>ui/extension-prefs-dialog.ui</file>
</gresource>
</gresources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Shell/Notifications/js">
<file>main.js</file>
<file>notificationDaemon.js</file>
<file>dbusService.js</file>
<file>misc/config.js</file>
<file>misc/fileUtils.js</file>
</gresource>
</gresources>

View File

@@ -120,7 +120,7 @@ var AuthPrompt = GObject.registerClass({
vfunc_key_press_event(keyPressEvent) { vfunc_key_press_event(keyPressEvent) {
if (keyPressEvent.keyval == Clutter.KEY_Escape) if (keyPressEvent.keyval == Clutter.KEY_Escape)
this.cancel(); this.cancel();
return Clutter.EVENT_PROPAGATE; return super.vfunc_key_press_event(keyPressEvent);
} }
_initEntryRow() { _initEntryRow() {
@@ -184,7 +184,7 @@ var AuthPrompt = GObject.registerClass({
}); });
this._defaultButtonWell.add_constraint(new Clutter.BindConstraint({ this._defaultButtonWell.add_constraint(new Clutter.BindConstraint({
source: this.cancelButton, source: this.cancelButton,
coordinate: Clutter.BindCoordinate.SIZE, coordinate: Clutter.BindCoordinate.WIDTH,
})); }));
this._mainBox.add_child(this._defaultButtonWell); this._mainBox.add_child(this._defaultButtonWell);
@@ -424,8 +424,13 @@ var AuthPrompt = GObject.registerClass({
} }
updateSensitivity(sensitive) { updateSensitivity(sensitive) {
if (this._entry.reactive === sensitive)
return;
this._entry.reactive = sensitive; this._entry.reactive = sensitive;
this._entry.clutter_text.editable = sensitive;
if (sensitive)
this._entry.grab_key_focus();
} }
vfunc_hide() { vfunc_hide() {

View File

@@ -35,7 +35,6 @@ const UserWidget = imports.ui.userWidget;
const _FADE_ANIMATION_TIME = 250; const _FADE_ANIMATION_TIME = 250;
const _SCROLL_ANIMATION_TIME = 500; const _SCROLL_ANIMATION_TIME = 500;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48;
var UserListItem = GObject.registerClass({ var UserListItem = GObject.registerClass({
Signals: { 'activate': {} }, Signals: { 'activate': {} },
@@ -178,6 +177,7 @@ var UserList = GObject.registerClass({
} }
vfunc_key_focus_in() { vfunc_key_focus_in() {
super.vfunc_key_focus_in();
this._moveFocusToItems(); this._moveFocusToItems();
} }
@@ -810,12 +810,13 @@ var LoginDialog = GObject.registerClass({
return; return;
this._logoBin.destroy_all_children(); this._logoBin.destroy_all_children();
if (this._logoFile && this._logoBin.resource_scale > 0) { const [valid, resourceScale] = this._logoBin.get_resource_scale();
if (this._logoFile && valid) {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile, this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile,
-1, _LOGO_ICON_HEIGHT, -1, -1,
scaleFactor, scaleFactor,
this._logoBin.resource_scale)); resourceScale));
} }
} }

View File

@@ -2,7 +2,7 @@
/* exported BANNER_MESSAGE_KEY, BANNER_MESSAGE_TEXT_KEY, LOGO_KEY, /* exported BANNER_MESSAGE_KEY, BANNER_MESSAGE_TEXT_KEY, LOGO_KEY,
DISABLE_USER_LIST_KEY, fadeInActor, fadeOutActor, cloneAndFadeOutActor */ DISABLE_USER_LIST_KEY, fadeInActor, fadeOutActor, cloneAndFadeOutActor */
const { Clutter, Gio, GLib } = imports.gi; const { Clutter, Gdm, Gio, GLib } = imports.gi;
const Signals = imports.signals; const Signals = imports.signals;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
@@ -12,6 +12,15 @@ const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const SmartcardManager = imports.misc.smartcardManager; const SmartcardManager = imports.misc.smartcardManager;
Gio._promisify(Gdm.Client.prototype,
'open_reauthentication_channel', 'open_reauthentication_channel_finish');
Gio._promisify(Gdm.Client.prototype,
'get_user_verifier', 'get_user_verifier_finish');
Gio._promisify(Gdm.UserVerifierProxy.prototype,
'call_begin_verification_for_user', 'call_begin_verification_for_user_finish');
Gio._promisify(Gdm.UserVerifierProxy.prototype,
'call_begin_verification', 'call_begin_verification_finish');
var PASSWORD_SERVICE_NAME = 'gdm-password'; var PASSWORD_SERVICE_NAME = 'gdm-password';
var FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; var FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
var SMARTCARD_SERVICE_NAME = 'gdm-smartcard'; var SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
@@ -168,14 +177,12 @@ var ShellUserVerifier = class {
this._checkForFingerprintReader(); this._checkForFingerprintReader();
if (userName) { // If possible, reauthenticate an already running session,
// If possible, reauthenticate an already running session, // so any session specific credentials get updated appropriately
// so any session specific credentials get updated appropriately if (userName)
this._client.open_reauthentication_channel(userName, this._cancellable, this._openReauthenticationChannel(userName);
this._reauthenticationChannelOpened.bind(this)); else
} else { this._getUserVerifier();
this._client.get_user_verifier(this._cancellable, this._userVerifierGot.bind(this));
}
} }
cancel() { cancel() {
@@ -339,10 +346,11 @@ var ShellUserVerifier = class {
this._verificationFailed(false); this._verificationFailed(false);
} }
_reauthenticationChannelOpened(client, result) { async _openReauthenticationChannel(userName) {
try { try {
this._clearUserVerifier(); this._clearUserVerifier();
this._userVerifier = client.open_reauthentication_channel_finish(result); this._userVerifier = await this._client.open_reauthentication_channel(
userName, this._cancellable);
} catch (e) { } catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return; return;
@@ -351,8 +359,7 @@ var ShellUserVerifier = class {
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there // Gdm emits org.freedesktop.DBus.Error.AccessDenied when there
// is no session to reauthenticate. Fall back to performing // is no session to reauthenticate. Fall back to performing
// verification from this login session // verification from this login session
client.get_user_verifier(this._cancellable, this._getUserVerifier();
this._userVerifierGot.bind(this));
return; return;
} }
@@ -366,10 +373,11 @@ var ShellUserVerifier = class {
this._hold.release(); this._hold.release();
} }
_userVerifierGot(client, result) { async _getUserVerifier() {
try { try {
this._clearUserVerifier(); this._clearUserVerifier();
this._userVerifier = client.get_user_verifier_finish(result); this._userVerifier =
await this._client.get_user_verifier(this._cancellable);
} catch (e) { } catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return; return;
@@ -421,35 +429,25 @@ var ShellUserVerifier = class {
} }
} }
_startService(serviceName) { async _startService(serviceName) {
this._hold.acquire(); this._hold.acquire();
if (this._userName) { try {
this._userVerifier.call_begin_verification_for_user(serviceName, this._userName, this._cancellable, (obj, result) => { if (this._userName) {
try { await this._userVerifier.call_begin_verification_for_user(
obj.call_begin_verification_for_user_finish(result); serviceName, this._userName, this._cancellable);
} catch (e) { } else {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) await this._userVerifier.call_begin_verification(
return; serviceName, this._cancellable);
this._reportInitError('Failed to start verification for user', e); }
return; } catch (e) {
} if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
this._hold.release(); this._reportInitError(this._userName
}); ? 'Failed to start verification for user'
} else { : 'Failed to start verification', e);
this._userVerifier.call_begin_verification(serviceName, this._cancellable, (obj, result) => { return;
try {
obj.call_begin_verification_finish(result);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
this._reportInitError('Failed to start verification', e);
return;
}
this._hold.release();
});
} }
this._hold.release();
} }
_beginVerification() { _beginVerification() {

View File

@@ -23,6 +23,7 @@
<file>misc/modemManager.js</file> <file>misc/modemManager.js</file>
<file>misc/objectManager.js</file> <file>misc/objectManager.js</file>
<file>misc/params.js</file> <file>misc/params.js</file>
<file>misc/parentalControlsManager.js</file>
<file>misc/permissionStore.js</file> <file>misc/permissionStore.js</file>
<file>misc/smartcardManager.js</file> <file>misc/smartcardManager.js</file>
<file>misc/systemActions.js</file> <file>misc/systemActions.js</file>
@@ -101,7 +102,6 @@
<file>ui/swipeTracker.js</file> <file>ui/swipeTracker.js</file>
<file>ui/switcherPopup.js</file> <file>ui/switcherPopup.js</file>
<file>ui/switchMonitor.js</file> <file>ui/switchMonitor.js</file>
<file>ui/tweener.js</file>
<file>ui/unlockDialog.js</file> <file>ui/unlockDialog.js</file>
<file>ui/userWidget.js</file> <file>ui/userWidget.js</file>
<file>ui/viewSelector.js</file> <file>ui/viewSelector.js</file>

View File

@@ -1,4 +1,5 @@
subdir('misc') subdir('misc')
subdir('dbusServices')
js_resources = gnome.compile_resources( js_resources = gnome.compile_resources(
'js-resources', 'js-resources.gresource.xml', 'js-resources', 'js-resources.gresource.xml',
@@ -13,10 +14,3 @@ portal_resources = gnome.compile_resources(
c_name: 'portal_js_resources', c_name: 'portal_js_resources',
dependencies: [config_js] dependencies: [config_js]
) )
prefs_resources = gnome.compile_resources(
'prefs-resources', 'prefs-resources.gresource.xml',
source_dir: ['.', meson.current_build_dir()],
c_name: 'prefs_js_resources',
dependencies: [config_js]
)

View File

@@ -15,6 +15,5 @@ var LOCALEDIR = '@datadir@/locale';
/* other standard directories */ /* other standard directories */
var LIBEXECDIR = '@libexecdir@'; var LIBEXECDIR = '@libexecdir@';
var PKGDATADIR = '@datadir@/@PACKAGE_NAME@'; var PKGDATADIR = '@datadir@/@PACKAGE_NAME@';
var VPNDIR = '@vpndir@';
/* g-i package versions */ /* g-i package versions */
var LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@' var LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@'

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ExtensionState, ExtensionType, getCurrentExtension, /* exported ExtensionState, ExtensionType, getCurrentExtension,
getSettings, initTranslations, isOutOfDate, installImporter, getSettings, initTranslations, openPrefs, isOutOfDate,
serializeExtension, deserializeExtension */ installImporter, serializeExtension, deserializeExtension */
// Common utils for the extension system and the extension // Common utils for the extension system and the extension
// preferences tool // preferences tool
@@ -153,6 +153,27 @@ function getSettings(schema) {
return new Gio.Settings({ settings_schema: schemaObj }); return new Gio.Settings({ settings_schema: schemaObj });
} }
/**
* openPrefs:
*
* Open the preference dialog of the current extension
*/
function openPrefs() {
const extension = getCurrentExtension();
if (!extension)
throw new Error('openPrefs() can only be called from extensions');
try {
const extensionManager = imports.ui.main.extensionManager;
extensionManager.openExtensionPrefs(extension.uuid, '', {});
} catch (e) {
if (e.name === 'ImportError')
throw new Error('openPrefs() cannot be called from preferences');
logError(e, 'Failed to open extension preferences');
}
}
/** /**
* versionCheck: * versionCheck:
* @param {string[]} required - an array of versions we're compatible with * @param {string[]} required - an array of versions we're compatible with

View File

@@ -76,19 +76,15 @@ function loadInterfaceXML(iface) {
_ifaceResource._register(); _ifaceResource._register();
} }
let xml = null;
let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`; let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`;
let f = Gio.File.new_for_uri(uri); let f = Gio.File.new_for_uri(uri);
try { try {
let [ok_, bytes] = f.load_contents(null); let [ok_, bytes] = f.load_contents(null);
if (bytes instanceof Uint8Array) return imports.byteArray.toString(bytes);
xml = imports.byteArray.toString(bytes);
else
xml = bytes.toString();
} catch (e) { } catch (e) {
log(`Failed to load D-Bus interface ${iface}`); log(`Failed to load D-Bus interface ${iface}`);
} }
return xml; return null;
} }

View File

@@ -6,6 +6,15 @@ const Signals = imports.signals;
const IBusCandidatePopup = imports.ui.ibusCandidatePopup; const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
Gio._promisify(IBus.Bus.prototype,
'list_engines_async', 'list_engines_async_finish');
Gio._promisify(IBus.Bus.prototype,
'request_name_async', 'request_name_async_finish');
Gio._promisify(IBus.Bus.prototype,
'get_global_engine_async', 'get_global_engine_async_finish');
Gio._promisify(IBus.Bus.prototype,
'set_global_engine_async', 'set_global_engine_async_finish');
// Ensure runtime version matches // Ensure runtime version matches
_checkIBusVersion(1, 5, 2); _checkIBusVersion(1, 5, 2);
@@ -66,7 +75,7 @@ var IBusManager = class {
let display = GLib.getenv('GNOME_SETUP_DISPLAY'); let display = GLib.getenv('GNOME_SETUP_DISPLAY');
if (display) if (display)
launcher.setenv('DISPLAY', display, true); launcher.setenv('DISPLAY', display, true);
launcher.launch(cmdLine); launcher.spawnv(cmdLine);
} catch (e) { } catch (e) {
log(`Failed to launch ibus-daemon: ${e.message}`); log(`Failed to launch ibus-daemon: ${e.message}`);
} }
@@ -102,16 +111,14 @@ var IBusManager = class {
_onConnected() { _onConnected() {
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this._ibus.list_engines_async(-1, this._cancellable, this._initEngines();
this._initEngines.bind(this)); this._initPanelService();
this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable,
this._initPanelService.bind(this));
} }
_initEngines(ibus, result) { async _initEngines() {
try { try {
let enginesList = this._ibus.list_engines_async_finish(result); const enginesList =
await this._ibus.list_engines_async(-1, this._cancellable);
for (let i = 0; i < enginesList.length; ++i) { for (let i = 0; i < enginesList.length; ++i) {
let name = enginesList[i].get_name(); let name = enginesList[i].get_name();
this._engines.set(name, enginesList[i]); this._engines.set(name, enginesList[i]);
@@ -126,56 +133,52 @@ var IBusManager = class {
} }
} }
_initPanelService(ibus, result) { async _initPanelService() {
let success = false;
try { try {
success = !!this._ibus.request_name_async_finish(result); await this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable);
} catch (e) { } catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return; logError(e);
logError(e); this._clear();
}
return;
} }
if (success) { this._panelService = new IBus.PanelService({
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(), connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL }); object_path: IBus.PATH_PANEL,
this._candidatePopup.setPanelService(this._panelService); });
this._panelService.connect('update-property', this._updateProperty.bind(this)); this._candidatePopup.setPanelService(this._panelService);
this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => { this._panelService.connect('update-property', this._updateProperty.bind(this));
let cursorLocation = { x, y, width: w, height: h }; this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => {
this.emit('set-cursor-location', cursorLocation); let cursorLocation = { x, y, width: w, height: h };
}); this.emit('set-cursor-location', cursorLocation);
this._panelService.connect('focus-in', (panel, path) => { });
if (!GLib.str_has_suffix(path, '/InputContext_1')) this._panelService.connect('focus-in', (panel, path) => {
this.emit('focus-in'); if (!GLib.str_has_suffix(path, '/InputContext_1'))
}); this.emit('focus-in');
this._panelService.connect('focus-out', () => this.emit('focus-out')); });
this._panelService.connect('focus-out', () => this.emit('focus-out'));
try { try {
// IBus versions older than 1.5.10 have a bug which // IBus versions older than 1.5.10 have a bug which
// causes spurious set-content-type emissions when // causes spurious set-content-type emissions when
// switching input focus that temporarily lose purpose // switching input focus that temporarily lose purpose
// and hints defeating its intended semantics and // and hints defeating its intended semantics and
// confusing users. We thus don't use it in that case. // confusing users. We thus don't use it in that case.
_checkIBusVersion(1, 5, 10); _checkIBusVersion(1, 5, 10);
this._panelService.connect('set-content-type', this._setContentType.bind(this)); this._panelService.connect('set-content-type', this._setContentType.bind(this));
} catch (e) { } catch (e) {
} }
try {
// If an engine is already active we need to get its properties // If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, res) => { const engine =
let engine; await this._ibus.get_global_engine_async(-1, this._cancellable);
try { this._engineChanged(this._ibus, engine.get_name());
engine = this._ibus.get_global_engine_async_finish(res);
if (!engine)
return;
} catch (e) {
return;
}
this._engineChanged(this._ibus, engine.get_name());
});
this._updateReadiness(); this._updateReadiness();
} else { } catch (e) {
this._clear();
} }
} }
@@ -224,7 +227,7 @@ var IBusManager = class {
return this._engines.get(id); return this._engines.get(id);
} }
setEngine(id, callback) { async setEngine(id, callback) {
// Send id even if id == this._currentEngineName // Send id even if id == this._currentEngineName
// because 'properties-registered' signal can be emitted // because 'properties-registered' signal can be emitted
// while this._ibusSources == null on a lock screen. // while this._ibusSources == null on a lock screen.
@@ -234,18 +237,16 @@ var IBusManager = class {
return; return;
} }
this._ibus.set_global_engine_async(id, try {
this._MAX_INPUT_SOURCE_ACTIVATION_TIME, await this._ibus.set_global_engine_async(id,
this._cancellable, (_bus, res) => { this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
try { this._cancellable);
this._ibus.set_global_engine_async_finish(res); } catch (e) {
} catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e);
logError(e); }
} if (callback)
if (callback) callback();
callback();
});
} }
preloadEngines(ids) { preloadEngines(ids) {

View File

@@ -4,6 +4,9 @@ const { Clutter, GLib, Gio, GObject, IBus } = imports.gi;
const Keyboard = imports.ui.status.keyboard; const Keyboard = imports.ui.status.keyboard;
Gio._promisify(IBus.Bus.prototype,
'create_input_context_async', 'create_input_context_async_finish');
var HIDE_PANEL_TIME = 50; var HIDE_PANEL_TIME = 50;
var InputMethod = GObject.registerClass( var InputMethod = GObject.registerClass(
@@ -46,15 +49,11 @@ class InputMethod extends Clutter.InputMethod {
this._currentSource = this._inputSourceManager.currentSource; this._currentSource = this._inputSourceManager.currentSource;
} }
_onConnected() { async _onConnected() {
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this._ibus.create_input_context_async('gnome-shell', -1,
this._cancellable, this._setContext.bind(this));
}
_setContext(bus, res) {
try { try {
this._context = this._ibus.create_input_context_async_finish(res); this._context = await this._ibus.create_input_context_async(
'gnome-shell', -1, this._cancellable);
} catch (e) { } catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
logError(e); logError(e);
@@ -97,8 +96,13 @@ class InputMethod extends Clutter.InputMethod {
this.commit(text.get_text()); this.commit(text.get_text());
} }
_onDeleteSurroundingText() { _onDeleteSurroundingText(_context, offset, nchars) {
this.delete_surrounding(); try {
this.delete_surrounding(offset, nchars);
} catch (e) {
// We may get out of bounds for negative offset on older mutter
this.delete_surrounding(0, nchars + offset);
}
} }
_onUpdatePreeditText(_context, text, pos, visible) { _onUpdatePreeditText(_context, text, pos, visible) {

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported getKeyboardManager, holdKeyboard, releaseKeyboard */ /* exported getKeyboardManager, holdKeyboard, releaseKeyboard */
const { GLib, GnomeDesktop, Meta } = imports.gi; const { GLib, GnomeDesktop } = imports.gi;
const Main = imports.ui.main; const Main = imports.ui.main;
@@ -62,11 +62,11 @@ var KeyboardManager = class {
return; return;
this._currentKeymap = { layouts, variants, options }; this._currentKeymap = { layouts, variants, options };
Meta.get_backend().set_keymap(layouts, variants, options); global.backend.set_keymap(layouts, variants, options);
} }
_applyLayoutGroupIndex(idx) { _applyLayoutGroupIndex(idx) {
Meta.get_backend().lock_layout_group(idx); global.backend.lock_layout_group(idx);
} }
apply(id) { apply(id) {

View File

@@ -50,25 +50,22 @@ function canLock() {
} }
function registerSessionWithGDM() { async function registerSessionWithGDM() {
log("Registering session with GDM"); log("Registering session with GDM");
Gio.DBus.system.call('org.gnome.DisplayManager', try {
'/org/gnome/DisplayManager/Manager', await Gio.DBus.system.call(
'org.gnome.DisplayManager.Manager', 'org.gnome.DisplayManager',
'RegisterSession', '/org/gnome/DisplayManager/Manager',
GLib.Variant.new('(a{sv})', [{}]), null, 'org.gnome.DisplayManager.Manager',
Gio.DBusCallFlags.NONE, -1, null, 'RegisterSession',
(source, result) => { GLib.Variant.new('(a{sv})', [{}]), null,
try { Gio.DBusCallFlags.NONE, -1, null);
source.call_finish(result); } catch (e) {
} catch (e) { if (!e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
if (!e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD)) log(`Error registering session with GDM: ${e.message}`);
log(`Error registering session with GDM: ${e.message}`); else
else log('Not calling RegisterSession(): method not exported, GDM too old?');
log("Not calling RegisterSession(): method not exported, GDM too old?"); }
}
}
);
} }
let _loginManager = null; let _loginManager = null;
@@ -174,24 +171,19 @@ var LoginManagerSystemd = class {
this._proxy.SuspendRemote(true); this._proxy.SuspendRemote(true);
} }
inhibit(reason, callback) { async inhibit(reason, callback) {
let inVariant = GLib.Variant.new('(ssss)', try {
['sleep', const inVariant = new GLib.Variant('(ssss)',
'GNOME Shell', ['sleep', 'GNOME Shell', reason, 'delay']);
reason, const [outVariant_, fdList] =
'delay']); await this._proxy.call_with_unix_fd_list('Inhibit',
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null, inVariant, 0, -1, null, null);
(proxy, result) => { const [fd] = fdList.steal_fds();
let fd = -1; callback(new Gio.UnixInputStream({ fd }));
try { } catch (e) {
let [outVariant_, fdList] = proxy.call_with_unix_fd_list_finish(result); logError(e, 'Error getting systemd inhibitor');
fd = fdList.steal_fds()[0]; callback(null);
callback(new Gio.UnixInputStream({ fd })); }
} catch (e) {
logError(e, "Error getting systemd inhibitor");
callback(null);
}
});
} }
_prepareForSleep(proxy, sender, [aboutToSuspend]) { _prepareForSleep(proxy, sender, [aboutToSuspend]) {

View File

@@ -7,7 +7,6 @@ jsconf.set10('HAVE_BLUETOOTH', bt_dep.found())
jsconf.set10('HAVE_NETWORKMANAGER', have_networkmanager) jsconf.set10('HAVE_NETWORKMANAGER', have_networkmanager)
jsconf.set('datadir', datadir) jsconf.set('datadir', datadir)
jsconf.set('libexecdir', libexecdir) jsconf.set('libexecdir', libexecdir)
jsconf.set('vpndir', vpndir)
config_js = configure_file( config_js = configure_file(
input: 'config.js.in', input: 'config.js.in',

View File

@@ -223,7 +223,7 @@ var BroadbandModem = GObject.registerClass({
}, class BroadbandModem extends ModemBase { }, class BroadbandModem extends ModemBase {
_init(path, capabilities) { _init(path, capabilities) {
super._init({ capabilities }); super._init({ capabilities });
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
@@ -249,7 +249,7 @@ var BroadbandModem = GObject.registerClass({
} }
_reloadSignalQuality() { _reloadSignalQuality() {
let [quality, recent_] = this.SignalQuality; let [quality, recent_] = this._proxy.SignalQuality;
this._setSignalQuality(quality); this._setSignalQuality(quality);
} }

View File

@@ -57,9 +57,7 @@ var ObjectManager = class {
// Start out inhibiting load until at least the proxy // Start out inhibiting load until at least the proxy
// manager is loaded and the remote objects are fetched // manager is loaded and the remote objects are fetched
this._numLoadInhibitors = 1; this._numLoadInhibitors = 1;
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, this._initManagerProxy();
this._cancellable,
this._onManagerProxyLoaded.bind(this));
} }
_tryToCompleteLoad() { _tryToCompleteLoad() {
@@ -73,7 +71,7 @@ var ObjectManager = class {
} }
} }
_addInterface(objectPath, interfaceName, onFinished) { async _addInterface(objectPath, interfaceName, onFinished) {
let info = this._interfaceInfos[interfaceName]; let info = this._interfaceInfos[interfaceName];
if (!info) { if (!info) {
@@ -89,40 +87,38 @@ var ObjectManager = class {
g_interface_info: info, g_interface_info: info,
g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START }); g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START });
proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable, (initable, result) => { try {
try { await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable);
initable.init_finish(result); } catch (e) {
} catch (e) { logError(e, `could not initialize proxy for interface ${interfaceName}`);
logError(e, `could not initialize proxy for interface ${interfaceName}`);
if (onFinished)
onFinished();
return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
} else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
if (onFinished) if (onFinished)
onFinished(); onFinished();
}); return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
} else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
if (onFinished)
onFinished();
} }
_removeInterface(objectPath, interfaceName) { _removeInterface(objectPath, interfaceName) {
@@ -151,9 +147,10 @@ var ObjectManager = class {
} }
} }
_onManagerProxyLoaded(initable, result) { async _initManagerProxy() {
try { try {
initable.init_finish(result); await this._managerProxy.init_async(
GLib.PRIORITY_DEFAULT, this._cancellable);
} catch (e) { } catch (e) {
logError(e, `could not initialize object manager for object ${this._serviceName}`); logError(e, `could not initialize object manager for object ${this._serviceName}`);

View File

@@ -0,0 +1,146 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
//
// Copyright (C) 2018, 2019, 2020 Endless Mobile, Inc.
//
// This is a GNOME Shell component to wrap the interactions over
// D-Bus with the malcontent library.
//
// Licensed under the GNU General Public License Version 2
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
/* exported getDefault */
const { Gio, GObject, Shell } = imports.gi;
// We require libmalcontent ≥ 0.6.0
const HAVE_MALCONTENT = imports.package.checkSymbol(
'Malcontent', '0', 'ManagerGetValueFlags');
var Malcontent = null;
if (HAVE_MALCONTENT) {
Malcontent = imports.gi.Malcontent;
Gio._promisify(Malcontent.Manager.prototype, 'get_app_filter_async', 'get_app_filter_finish');
}
let _singleton = null;
function getDefault() {
if (_singleton === null)
_singleton = new ParentalControlsManager();
return _singleton;
}
// A manager class which provides cached access to the constructing users
// parental controls settings. Its possible for the users parental controls
// to change at runtime if the Parental Controls application is used by an
// administrator from within the users session.
var ParentalControlsManager = GObject.registerClass({
Signals: {
'app-filter-changed': {},
},
}, class ParentalControlsManager extends GObject.Object {
_init() {
super._init();
this._initialized = false;
this._disabled = false;
this._appFilter = null;
this._initializeManager();
}
async _initializeManager() {
if (!HAVE_MALCONTENT) {
log('Skipping parental controls support as its disabled');
this._initialized = true;
this.emit('app-filter-changed');
return;
}
log(`Getting parental controls for user ${Shell.util_get_uid()}`);
try {
const connection = await Gio.DBus.get(Gio.BusType.SYSTEM, null);
this._manager = new Malcontent.Manager({ connection });
this._appFilter = await this._manager.get_app_filter_async(
Shell.util_get_uid(),
Malcontent.ManagerGetValueFlags.NONE,
null);
} catch (e) {
if (e.matches(Malcontent.ManagerError, Malcontent.ManagerError.DISABLED)) {
log('Parental controls globally disabled');
this._disabled = true;
} else {
logError(e, 'Failed to get parental controls settings');
return;
}
}
this._manager.connect('app-filter-changed', this._onAppFilterChanged.bind(this));
// Signal initialisation is complete.
this._initialized = true;
this.emit('app-filter-changed');
}
async _onAppFilterChanged(manager, uid) {
// Emit 'changed' signal only if app-filter is changed for currently logged-in user.
let currentUid = Shell.util_get_uid();
if (currentUid !== uid)
return;
try {
this._appFilter = await this._manager.get_app_filter_async(
currentUid,
Malcontent.ManagerGetValueFlags.NONE,
null);
this.emit('app-filter-changed');
} catch (e) {
// Log an error and keep the old app filter.
logError(e, `Failed to get new MctAppFilter for uid ${Shell.util_get_uid()} on app-filter-changed`);
}
}
get initialized() {
return this._initialized;
}
// Calculate whether the given app (a Gio.DesktopAppInfo) should be shown
// on the desktop, in search results, etc. The app should be shown if:
// - The .desktop file doesnt say it should be hidden.
// - The executable from the .desktop files Exec line isnt blacklisted in
// the users parental controls.
// - None of the flatpak app IDs from the X-Flatpak and the
// X-Flatpak-RenamedFrom lines are blacklisted in the users parental
// controls.
shouldShowApp(appInfo) {
// Quick decision?
if (!appInfo.should_show())
return false;
// Are parental controls enabled (at configure time or runtime)?
if (!HAVE_MALCONTENT || this._disabled)
return true;
// Have we finished initialising yet?
if (!this.initialized) {
log(`Warning: Hiding app because parental controls not yet initialised: ${appInfo.get_id()}`);
return false;
}
return this._appFilter.is_appinfo_allowed(appInfo);
}
});

View File

@@ -83,13 +83,17 @@ const SystemActions = GObject.registerClass({
this._canHavePowerOff = true; this._canHavePowerOff = true;
this._canHaveSuspend = true; this._canHaveSuspend = true;
function tokenizeKeywords(keywords) {
return keywords.split(';').map(keyword => GLib.str_tokenize_and_fold(keyword, null)).flat(2);
}
this._actions = new Map(); this._actions = new Map();
this._actions.set(POWER_OFF_ACTION_ID, { this._actions.set(POWER_OFF_ACTION_ID, {
// Translators: The name of the power-off action in search // Translators: The name of the power-off action in search
name: C_("search-result", "Power Off"), name: C_("search-result", "Power Off"),
iconName: 'system-shutdown-symbolic', iconName: 'system-shutdown-symbolic',
// Translators: A list of keywords that match the power-off action, separated by semicolons // Translators: A list of keywords that match the power-off action, separated by semicolons
keywords: _('power off;shutdown;reboot;restart;halt;stop').split(/[; ]/), keywords: tokenizeKeywords(_('power off;shutdown;reboot;restart;halt;stop')),
available: false, available: false,
}); });
this._actions.set(LOCK_SCREEN_ACTION_ID, { this._actions.set(LOCK_SCREEN_ACTION_ID, {
@@ -97,7 +101,7 @@ const SystemActions = GObject.registerClass({
name: C_("search-result", "Lock Screen"), name: C_("search-result", "Lock Screen"),
iconName: 'system-lock-screen-symbolic', iconName: 'system-lock-screen-symbolic',
// Translators: A list of keywords that match the lock screen action, separated by semicolons // Translators: A list of keywords that match the lock screen action, separated by semicolons
keywords: _("lock screen").split(/[; ]/), keywords: tokenizeKeywords(_('lock screen')),
available: false, available: false,
}); });
this._actions.set(LOGOUT_ACTION_ID, { this._actions.set(LOGOUT_ACTION_ID, {
@@ -105,7 +109,7 @@ const SystemActions = GObject.registerClass({
name: C_("search-result", "Log Out"), name: C_("search-result", "Log Out"),
iconName: 'application-exit-symbolic', iconName: 'application-exit-symbolic',
// Translators: A list of keywords that match the logout action, separated by semicolons // Translators: A list of keywords that match the logout action, separated by semicolons
keywords: _("logout;log out;sign off").split(/[; ]/), keywords: tokenizeKeywords(_('logout;log out;sign off')),
available: false, available: false,
}); });
this._actions.set(SUSPEND_ACTION_ID, { this._actions.set(SUSPEND_ACTION_ID, {
@@ -113,7 +117,7 @@ const SystemActions = GObject.registerClass({
name: C_("search-result", "Suspend"), name: C_("search-result", "Suspend"),
iconName: 'media-playback-pause-symbolic', iconName: 'media-playback-pause-symbolic',
// Translators: A list of keywords that match the suspend action, separated by semicolons // Translators: A list of keywords that match the suspend action, separated by semicolons
keywords: _("suspend;sleep").split(/[; ]/), keywords: tokenizeKeywords(_('suspend;sleep')),
available: false, available: false,
}); });
this._actions.set(SWITCH_USER_ACTION_ID, { this._actions.set(SWITCH_USER_ACTION_ID, {
@@ -121,14 +125,14 @@ const SystemActions = GObject.registerClass({
name: C_("search-result", "Switch User"), name: C_("search-result", "Switch User"),
iconName: 'system-switch-user-symbolic', iconName: 'system-switch-user-symbolic',
// Translators: A list of keywords that match the switch user action, separated by semicolons // Translators: A list of keywords that match the switch user action, separated by semicolons
keywords: _("switch user").split(/[; ]/), keywords: tokenizeKeywords(_('switch user')),
available: false, available: false,
}); });
this._actions.set(LOCK_ORIENTATION_ACTION_ID, { this._actions.set(LOCK_ORIENTATION_ACTION_ID, {
name: '', name: '',
iconName: '', iconName: '',
// Translators: A list of keywords that match the lock orientation action, separated by semicolons // Translators: A list of keywords that match the lock orientation action, separated by semicolons
keywords: _("lock orientation;unlock orientation;screen;rotation").split(/[; ]/), keywords: tokenizeKeywords(_('lock orientation;unlock orientation;screen;rotation')),
available: false, available: false,
}); });
@@ -277,7 +281,7 @@ const SystemActions = GObject.registerClass({
getMatchingActions(terms) { getMatchingActions(terms) {
// terms is a list of strings // terms is a list of strings
terms = terms.map(term => term.toLowerCase()); terms = terms.map(term => GLib.str_tokenize_and_fold(term, null)[0]);
let results = []; let results = [];

View File

@@ -7,6 +7,8 @@ const PermissionStore = imports.misc.permissionStore;
const { loadInterfaceXML } = imports.misc.fileUtils; const { loadInterfaceXML } = imports.misc.fileUtils;
Gio._promisify(Geoclue.Simple, 'new', 'new_finish');
const WeatherIntegrationIface = loadInterfaceXML('org.gnome.Shell.WeatherIntegration'); const WeatherIntegrationIface = loadInterfaceXML('org.gnome.Shell.WeatherIntegration');
const WEATHER_BUS_NAME = 'org.gnome.Weather'; const WEATHER_BUS_NAME = 'org.gnome.Weather';
@@ -79,16 +81,7 @@ var WeatherClient = class {
this._weatherApp = null; this._weatherApp = null;
this._weatherProxy = null; this._weatherProxy = null;
let nodeInfo = Gio.DBusNodeInfo.new_for_xml(WeatherIntegrationIface); this._createWeatherProxy();
Gio.DBusProxy.new(
Gio.DBus.session,
Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES,
nodeInfo.lookup_interface(WEATHER_INTEGRATION_IFACE),
WEATHER_BUS_NAME,
WEATHER_OBJECT_PATH,
WEATHER_INTEGRATION_IFACE,
null,
this._onWeatherProxyReady.bind(this));
this._settings = new Gio.Settings({ this._settings = new Gio.Settings({
schema_id: 'org.gnome.shell.weather', schema_id: 'org.gnome.shell.weather',
@@ -146,9 +139,17 @@ var WeatherClient = class {
(!this._needsAuth || this._weatherAuthorized); (!this._needsAuth || this._weatherAuthorized);
} }
_onWeatherProxyReady(o, res) { async _createWeatherProxy() {
const nodeInfo = Gio.DBusNodeInfo.new_for_xml(WeatherIntegrationIface);
try { try {
this._weatherProxy = Gio.DBusProxy.new_finish(res); this._weatherProxy = await Gio.DBusProxy.new(
Gio.DBus.session,
Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES,
nodeInfo.lookup_interface(WEATHER_INTEGRATION_IFACE),
WEATHER_BUS_NAME,
WEATHER_OBJECT_PATH,
WEATHER_INTEGRATION_IFACE,
null);
} catch (e) { } catch (e) {
log(`Failed to create GNOME Weather proxy: ${e}`); log(`Failed to create GNOME Weather proxy: ${e}`);
return; return;
@@ -239,25 +240,23 @@ var WeatherClient = class {
} }
} }
_startGClueService() { async _startGClueService() {
if (this._gclueStarting) if (this._gclueStarting)
return; return;
this._gclueStarting = true; this._gclueStarting = true;
Geoclue.Simple.new('org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null, try {
(o, res) => { this._gclueService = await Geoclue.Simple.new(
try { 'org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null);
this._gclueService = Geoclue.Simple.new_finish(res); } catch (e) {
} catch (e) { log(`Failed to connect to Geoclue2 service: ${e.message}`);
log(`Failed to connect to Geoclue2 service: ${e.message}`); this._setLocation(this._mostRecentLocation);
this._setLocation(this._mostRecentLocation); return;
return; }
} this._gclueStarted = true;
this._gclueStarted = true; this._gclueService.get_client().distance_threshold = 100;
this._gclueService.get_client().distance_threshold = 100; this._updateLocationMonitoring();
this._updateLocationMonitoring();
});
} }
_onGClueLocationChanged() { _onGClueLocationChanged() {

View File

@@ -5,6 +5,5 @@
<file>misc/config.js</file> <file>misc/config.js</file>
<file>misc/fileUtils.js</file> <file>misc/fileUtils.js</file>
<file>misc/params.js</file>
</gresource> </gresource>
</gresources> </gresources>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell">
<file>extensionPrefs/main.js</file>
<file>misc/config.js</file>
<file>misc/extensionUtils.js</file>
<file>misc/fileUtils.js</file>
<file>misc/params.js</file>
<file alias="css/application.css">extensionPrefs/css/application.css</file>
<file alias="ui/extension-row.ui">extensionPrefs/ui/extension-row.ui</file>
<file alias="ui/extensions-window.ui">extensionPrefs/ui/extensions-window.ui</file>
</gresource>
</gresources>

View File

@@ -15,8 +15,7 @@ class Animation extends St.Bin {
const themeContext = St.ThemeContext.get_for_stage(global.stage); const themeContext = St.ThemeContext.get_for_stage(global.stage);
super._init({ super._init({
width: width * themeContext.scale_factor, style: `width: ${width}px; height: ${height}px;`,
height: height * themeContext.scale_factor,
}); });
this.connect('destroy', this._onDestroy.bind(this)); this.connect('destroy', this._onDestroy.bind(this));

View File

@@ -10,6 +10,7 @@ const GrabHelper = imports.ui.grabHelper;
const IconGrid = imports.ui.iconGrid; const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main; const Main = imports.ui.main;
const PageIndicators = imports.ui.pageIndicators; const PageIndicators = imports.ui.pageIndicators;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search; const Search = imports.ui.search;
const SwipeTracker = imports.ui.swipeTracker; const SwipeTracker = imports.ui.swipeTracker;
@@ -71,15 +72,9 @@ function _getFolderName(folder) {
let name = folder.get_string('name'); let name = folder.get_string('name');
if (folder.get_boolean('translate')) { if (folder.get_boolean('translate')) {
let keyfile = new GLib.KeyFile(); let translated = Shell.util_get_translated_folder_name(name);
let path = 'desktop-directories/%s'.format(name); if (translated !== null)
return translated;
try {
keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
name = keyfile.get_locale_string('Desktop Entry', 'Name', null);
} catch (e) {
return name;
}
} }
return name; return name;
@@ -120,15 +115,10 @@ function _findBestFolderName(apps) {
}, commonCategories); }, commonCategories);
for (let category of commonCategories) { for (let category of commonCategories) {
let keyfile = new GLib.KeyFile(); const directory = '%s.directory'.format(category);
let path = 'desktop-directories/%s.directory'.format(category); const translated = Shell.util_get_translated_folder_name(directory);
if (translated !== null)
try { return translated;
keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
return keyfile.get_locale_string('Desktop Entry', 'Name', null);
} catch (e) {
continue;
}
} }
return null; return null;
@@ -169,6 +159,16 @@ var BaseAppView = GObject.registerClass({
this._items = new Map(); this._items = new Map();
this._orderedItems = []; this._orderedItems = [];
this._animateLaterId = 0;
this._viewLoadedHandlerId = 0;
this._viewIsReady = false;
// Filter the apps through the users parental controls.
this._parentalControlsManager = ParentalControlsManager.getDefault();
this._parentalControlsManager.connect('app-filter-changed', () => {
this._redisplay();
});
} }
_childFocused(_actor) { _childFocused(_actor) {
@@ -204,6 +204,7 @@ var BaseAppView = GObject.registerClass({
this._items.set(icon.id, icon); this._items.set(icon.id, icon);
}); });
this._viewIsReady = true;
this.emit('view-loaded'); this.emit('view-loaded');
} }
@@ -253,6 +254,18 @@ var BaseAppView = GObject.registerClass({
Main.overview.dash.showAppsButton); Main.overview.dash.showAppsButton);
} }
_clearAnimateLater() {
if (this._animateLaterId) {
Meta.later_remove(this._animateLaterId);
this._animateLaterId = 0;
}
if (this._viewLoadedHandlerId) {
this.disconnect(this._viewLoadedHandlerId);
this._viewLoadedHandlerId = 0;
}
this._grid.opacity = 255;
}
animate(animationDirection, onComplete) { animate(animationDirection, onComplete) {
if (onComplete) { if (onComplete) {
let animationDoneId = this._grid.connect('animation-done', () => { let animationDoneId = this._grid.connect('animation-done', () => {
@@ -261,16 +274,38 @@ var BaseAppView = GObject.registerClass({
}); });
} }
this._clearAnimateLater();
if (animationDirection == IconGrid.AnimationDirection.IN) { if (animationDirection == IconGrid.AnimationDirection.IN) {
let id = this._grid.connect('paint', () => { const doSpringAnimationLater = laterType => {
this._grid.disconnect(id); this._animateLaterId = Meta.later_add(laterType,
this._doSpringAnimation(animationDirection); () => {
}); this._animateLaterId = 0;
this._doSpringAnimation(animationDirection);
return GLib.SOURCE_REMOVE;
});
};
if (this._viewIsReady) {
this._grid.opacity = 0;
doSpringAnimationLater(Meta.LaterType.IDLE);
} else {
this._viewLoadedHandlerId = this.connect('view-loaded',
() => {
this._clearAnimateLater();
doSpringAnimationLater(Meta.LaterType.BEFORE_REDRAW);
});
}
} else { } else {
this._doSpringAnimation(animationDirection); this._doSpringAnimation(animationDirection);
} }
} }
vfunc_unmap() {
this._clearAnimateLater();
super.vfunc_unmap();
}
animateSwitch(animationDirection) { animateSwitch(animationDirection) {
this.remove_all_transitions(); this.remove_all_transitions();
this._grid.remove_all_transitions(); this._grid.remove_all_transitions();
@@ -307,14 +342,37 @@ var AllView = GObject.registerClass({
use_pagination: true, use_pagination: true,
}); });
this._grid._delegate = this;
this._stack = new St.Widget({
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
});
this.add_actor(this._stack);
let box = new St.BoxLayout({
vertical: true,
y_align: Clutter.ActorAlign.START,
});
box.add_child(this._grid);
this._scrollView = new St.ScrollView({ this._scrollView = new St.ScrollView({
style_class: 'all-apps', style_class: 'all-apps',
x_expand: true, x_expand: true,
y_expand: true, y_expand: true,
reactive: true, reactive: true,
}); });
this.add_actor(this._scrollView); this._scrollView.add_actor(box);
this._grid._delegate = this; this._stack.add_actor(this._scrollView);
this._eventBlocker = new St.Widget({
x_expand: true,
y_expand: true,
reactive: true,
visible: false,
});
this._stack.add_actor(this._eventBlocker);
this._scrollView.set_policy(St.PolicyType.NEVER, this._scrollView.set_policy(St.PolicyType.NEVER,
St.PolicyType.EXTERNAL); St.PolicyType.EXTERNAL);
@@ -335,24 +393,7 @@ var AllView = GObject.registerClass({
this._folderIcons = []; this._folderIcons = [];
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
let box = new St.BoxLayout({
vertical: true,
y_align: Clutter.ActorAlign.START,
});
this._grid.currentPage = 0; this._grid.currentPage = 0;
this._stack.add_actor(this._grid);
this._eventBlocker = new St.Widget({
x_expand: true,
y_expand: true,
reactive: true,
visible: false,
});
this._stack.add_actor(this._eventBlocker);
box.add_actor(this._stack);
this._scrollView.add_actor(box);
this._scrollView.connect('scroll-event', this._onScroll.bind(this)); this._scrollView.connect('scroll-event', this._onScroll.bind(this));
@@ -392,10 +433,12 @@ var AllView = GObject.registerClass({
this._redisplayWorkId = Main.initializeDeferredWork(this, this._redisplay.bind(this)); this._redisplayWorkId = Main.initializeDeferredWork(this, this._redisplay.bind(this));
Shell.AppSystem.get_default().connect('installed-changed', () => { Shell.AppSystem.get_default().connect('installed-changed', () => {
this._viewIsReady = false;
Main.queueDeferredWork(this._redisplayWorkId); Main.queueDeferredWork(this._redisplayWorkId);
}); });
this._folderSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); this._folderSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' });
this._folderSettings.connect('changed::folder-children', () => { this._folderSettings.connect('changed::folder-children', () => {
this._viewIsReady = false;
Main.queueDeferredWork(this._redisplayWorkId); Main.queueDeferredWork(this._redisplayWorkId);
}); });
@@ -431,6 +474,10 @@ var AllView = GObject.registerClass({
_redisplay() { _redisplay() {
super._redisplay(); super._redisplay();
this._folderIcons.forEach(icon => {
icon.view._redisplay();
});
this._refilterApps(); this._refilterApps();
} }
@@ -481,7 +528,7 @@ var AllView = GObject.registerClass({
} catch (e) { } catch (e) {
return false; return false;
} }
return appInfo.should_show(); return this._parentalControlsManager.shouldShowApp(appInfo);
}); });
let apps = this._appInfoList.map(app => app.get_id()); let apps = this._appInfoList.map(app => app.get_id());
@@ -591,7 +638,7 @@ var AllView = GObject.registerClass({
this._grid.currentPage = pageNumber; this._grid.currentPage = pageNumber;
// Tween the change between pages. // Animate the change between pages.
this._adjustment.ease(this._grid.getPageY(this._grid.currentPage), { this._adjustment.ease(this._grid.getPageY(this._grid.currentPage), {
mode: Clutter.AnimationMode.EASE_OUT_CUBIC, mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
duration: animate ? PAGE_SWITCH_TIME : 0, duration: animate ? PAGE_SWITCH_TIME : 0,
@@ -699,8 +746,6 @@ var AllView = GObject.registerClass({
// Toggle search entry // Toggle search entry
Main.overview.searchEntry.reactive = !isOpen; Main.overview.searchEntry.reactive = !isOpen;
Main.overview.searchEntry.clutter_text.reactive = !isOpen;
Main.overview.searchEntry.clutter_text.editable = !isOpen;
this._displayingPopup = isOpen; this._displayingPopup = isOpen;
}); });
@@ -973,7 +1018,7 @@ class FrequentView extends BaseAppView {
let favoritesWritable = global.settings.is_writable('favorite-apps'); let favoritesWritable = global.settings.is_writable('favorite-apps');
for (let i = 0; i < mostUsed.length; i++) { for (let i = 0; i < mostUsed.length; i++) {
if (!mostUsed[i].get_app_info().should_show()) if (!this._parentalControlsManager.shouldShowApp(mostUsed[i].get_app_info()))
continue; continue;
let appIcon = this._items.get(mostUsed[i].get_id()); let appIcon = this._items.get(mostUsed[i].get_id());
if (!appIcon) { if (!appIcon) {
@@ -1219,6 +1264,8 @@ var AppSearchProvider = class AppSearchProvider {
this.canLaunchSearch = false; this.canLaunchSearch = false;
this._systemActions = new SystemActions.getDefault(); this._systemActions = new SystemActions.getDefault();
this._parentalControlsManager = ParentalControlsManager.getDefault();
} }
getResultMetas(apps, callback) { getResultMetas(apps, callback) {
@@ -1253,14 +1300,27 @@ var AppSearchProvider = class AppSearchProvider {
} }
getInitialResultSet(terms, callback, _cancellable) { getInitialResultSet(terms, callback, _cancellable) {
// Defer until the parental controls manager is initialised, so the
// results can be filtered correctly.
if (!this._parentalControlsManager.initialized) {
let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => {
if (this._parentalControlsManager.initialized) {
this._parentalControlsManager.disconnect(initializedId);
this.getInitialResultSet(terms, callback, _cancellable);
}
});
return;
}
let query = terms.join(' '); let query = terms.join(' ');
let groups = Shell.AppSystem.search(query); let groups = Shell.AppSystem.search(query);
let usage = Shell.AppUsage.get_default(); let usage = Shell.AppUsage.get_default();
let results = []; let results = [];
groups.forEach(group => { groups.forEach(group => {
group = group.filter(appID => { group = group.filter(appID => {
let app = Gio.DesktopAppInfo.new(appID); const app = this._appSys.lookup_app(appID);
return app && app.should_show(); return app && this._parentalControlsManager.shouldShowApp(app.app_info);
}); });
results = results.concat(group.sort( results = results.concat(group.sort(
(a, b) => usage.compare(a, b) (a, b) => usage.compare(a, b)
@@ -1308,7 +1368,7 @@ class FolderView extends BaseAppView {
x_expand: true, x_expand: true,
y_expand: true, y_expand: true,
}); });
this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL);
this.add_actor(this._scrollView); this.add_actor(this._scrollView);
let scrollableContainer = new St.BoxLayout({ let scrollableContainer = new St.BoxLayout({
@@ -1324,7 +1384,6 @@ class FolderView extends BaseAppView {
action.connect('pan', this._onPan.bind(this)); action.connect('pan', this._onPan.bind(this));
this._scrollView.add_action(action); this._scrollView.add_action(action);
this._folder.connect('changed', this._redisplay.bind(this));
this._redisplay(); this._redisplay();
} }
@@ -1346,12 +1405,12 @@ class FolderView extends BaseAppView {
}); });
layout.hookup_style(icon); layout.hookup_style(icon);
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size); let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let numItems = this._orderedItems.length; let numItems = this._orderedItems.length;
let rtl = icon.get_text_direction() == Clutter.TextDirection.RTL; let rtl = icon.get_text_direction() == Clutter.TextDirection.RTL;
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
let bin = new St.Bin({ width: subSize * scale, height: subSize * scale }); const style = 'width: %dpx; height: %dpx;'.format(subSize, subSize);
let bin = new St.Bin({ style });
if (i < numItems) if (i < numItems)
bin.child = this._orderedItems[i].app.create_icon_texture(subSize); bin.child = this._orderedItems[i].app.create_icon_texture(subSize);
layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1); layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1);
@@ -1400,7 +1459,7 @@ class FolderView extends BaseAppView {
if (!app) if (!app)
return; return;
if (!app.get_app_info().should_show()) if (!this._parentalControlsManager.shouldShowApp(app.get_app_info()))
return; return;
if (apps.some(appIcon => appIcon.id == appId)) if (apps.some(appIcon => appIcon.id == appId))
@@ -1429,6 +1488,22 @@ class FolderView extends BaseAppView {
return apps; return apps;
} }
addApp(app) {
let folderApps = this._folder.get_strv('apps');
folderApps.push(app.id);
this._folder.set_strv('apps', folderApps);
// Also remove from 'excluded-apps' if the app id is listed
// there. This is only possible on categories-based folders.
let excludedApps = this._folder.get_strv('excluded-apps');
let index = excludedApps.indexOf(app.id);
if (index >= 0) {
excludedApps.splice(index, 1);
this._folder.set_strv('excluded-apps', excludedApps);
}
}
removeApp(app) { removeApp(app) {
let folderApps = this._folder.get_strv('apps'); let folderApps = this._folder.get_strv('apps');
let index = folderApps.indexOf(app.id); let index = folderApps.indexOf(app.id);
@@ -1459,8 +1534,6 @@ class FolderView extends BaseAppView {
} else { } else {
this._folder.set_strv('apps', folderApps); this._folder.set_strv('apps', folderApps);
} }
return true;
} }
}); });
@@ -1494,26 +1567,26 @@ var FolderIcon = GObject.registerClass({
this.view = new FolderView(this._folder, id, parentView); this.view = new FolderView(this._folder, id, parentView);
this._itemDragBeginId = Main.overview.connect( this._iconIsHovering = false;
'item-drag-begin', this._onDragBegin.bind(this));
this._itemDragEndId = Main.overview.connect(
'item-drag-end', this._onDragEnd.bind(this));
this.connect('destroy', this._onDestroy.bind(this)); this.connect('destroy', this._onDestroy.bind(this));
this._folder.connect('changed', this._redisplay.bind(this)); this._folderChangedId = this._folder.connect(
this._redisplay(); 'changed', this._sync.bind(this));
this._sync();
} }
_onDestroy() { _onDestroy() {
Main.overview.disconnect(this._itemDragBeginId); if (this._dragMonitor) {
Main.overview.disconnect(this._itemDragEndId); DND.removeDragMonitor(this._dragMonitor);
this._dragMonitor = null;
}
this.view.destroy(); this.view.destroy();
if (this._spaceReadySignalId) { if (this._folderChangedId) {
this._parentView.disconnect(this._spaceReadySignalId); this._folder.disconnect(this._folderChangedId);
this._spaceReadySignalId = 0; delete this._folderChangedId;
} }
if (this._dialog) if (this._dialog)
@@ -1541,29 +1614,32 @@ var FolderIcon = GObject.registerClass({
return this.view.getAllItems().map(item => item.id); return this.view.getAllItems().map(item => item.id);
} }
_onDragBegin() { _setHoveringByDnd(hovering) {
this._dragMonitor = { if (this._iconIsHovering == hovering)
dragMotion: this._onDragMotion.bind(this), return;
};
DND.addDragMonitor(this._dragMonitor); this._iconIsHovering = hovering;
if (hovering) {
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this),
};
DND.addDragMonitor(this._dragMonitor);
this.add_style_pseudo_class('drop');
} else {
DND.removeDragMonitor(this._dragMonitor);
this.remove_style_pseudo_class('drop');
}
} }
_onDragMotion(dragEvent) { _onDragMotion(dragEvent) {
let target = dragEvent.targetActor; if (!this.contains(dragEvent.targetActor) ||
!this._canAccept(dragEvent.source))
if (!this.contains(target) || !this._canAccept(dragEvent.source)) this._setHoveringByDnd(false);
this.remove_style_pseudo_class('drop');
else
this.add_style_pseudo_class('drop');
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
} }
_onDragEnd() {
this.remove_style_pseudo_class('drop');
DND.removeDragMonitor(this._dragMonitor);
}
_canAccept(source) { _canAccept(source) {
if (!(source instanceof AppIcon)) if (!(source instanceof AppIcon))
return false; return false;
@@ -1582,27 +1658,18 @@ var FolderIcon = GObject.registerClass({
if (!this._canAccept(source)) if (!this._canAccept(source))
return DND.DragMotionResult.NO_DROP; return DND.DragMotionResult.NO_DROP;
this._setHoveringByDnd(true);
return DND.DragMotionResult.MOVE_DROP; return DND.DragMotionResult.MOVE_DROP;
} }
acceptDrop(source) { acceptDrop(source) {
this._setHoveringByDnd(false);
if (!this._canAccept(source)) if (!this._canAccept(source))
return false; return false;
let app = source.app; this.view.addApp(source.app);
let folderApps = this._folder.get_strv('apps');
folderApps.push(app.id);
this._folder.set_strv('apps', folderApps);
// Also remove from 'excluded-apps' if the app id is listed
// there. This is only possible on categories-based folders.
let excludedApps = this._folder.get_strv('excluded-apps');
let index = excludedApps.indexOf(app.id);
if (index >= 0) {
excludedApps.splice(index, 1);
this._folder.set_strv('excluded-apps', excludedApps);
}
return true; return true;
} }
@@ -1617,11 +1684,11 @@ var FolderIcon = GObject.registerClass({
this.emit('name-changed'); this.emit('name-changed');
} }
_redisplay() { _sync() {
this.emit('apps-changed');
this._updateName(); this._updateName();
this.visible = this.view.getAllItems().length > 0; this.visible = this.view.getAllItems().length > 0;
this.icon.update(); this.icon.update();
this.emit('apps-changed');
} }
_createIcon(iconSize) { _createIcon(iconSize) {
@@ -1902,6 +1969,7 @@ var AppFolderDialog = GObject.registerClass({
vfunc_allocate(box, flags) { vfunc_allocate(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box); let contentBox = this.get_theme_node().get_content_box(box);
contentBox = this._viewBox.get_theme_node().get_content_box(contentBox);
let [, entryBoxHeight] = this._entryBox.get_size(); let [, entryBoxHeight] = this._entryBox.get_size();
let spacing = this._viewBox.layout_manager.spacing; let spacing = this._viewBox.layout_manager.spacing;
@@ -1910,6 +1978,8 @@ var AppFolderDialog = GObject.registerClass({
contentBox.get_width(), contentBox.get_width(),
contentBox.get_height() - entryBoxHeight - spacing); contentBox.get_height() - entryBoxHeight - spacing);
this._view._grid.topPadding = 0;
super.vfunc_allocate(box, flags); super.vfunc_allocate(box, flags);
// We can only start zooming after receiving an allocation // We can only start zooming after receiving an allocation
@@ -2025,7 +2095,6 @@ var AppIcon = GObject.registerClass({
this._delegate = this; this._delegate = this;
this._hasDndHover = false;
this._folderPreviewId = 0; this._folderPreviewId = 0;
// Get the isDraggable property without passing it on to the BaseIcon: // Get the isDraggable property without passing it on to the BaseIcon:
@@ -2074,11 +2143,7 @@ var AppIcon = GObject.registerClass({
}); });
} }
this._dragMonitor = null; this._otherIconIsHovering = false;
this._itemDragBeginId = Main.overview.connect(
'item-drag-begin', this._onDragBegin.bind(this));
this._itemDragEndId = Main.overview.connect(
'item-drag-end', this._onDragEnd.bind(this));
this._menuTimeoutId = 0; this._menuTimeoutId = 0;
this._stateChangedId = this.app.connect('notify::state', () => { this._stateChangedId = this.app.connect('notify::state', () => {
@@ -2090,9 +2155,6 @@ var AppIcon = GObject.registerClass({
} }
_onDestroy() { _onDestroy() {
Main.overview.disconnect(this._itemDragBeginId);
Main.overview.disconnect(this._itemDragEndId);
if (this._folderPreviewId > 0) { if (this._folderPreviewId > 0) {
GLib.source_remove(this._folderPreviewId); GLib.source_remove(this._folderPreviewId);
this._folderPreviewId = 0; this._folderPreviewId = 0;
@@ -2143,7 +2205,7 @@ var AppIcon = GObject.registerClass({
} }
vfunc_leave_event(crossingEvent) { vfunc_leave_event(crossingEvent) {
let ret = super.vfunc_leave_event(crossingEvent); const ret = super.vfunc_leave_event(crossingEvent);
this.fake_release(); this.fake_release();
this._removeMenuTimeout(); this._removeMenuTimeout();
@@ -2151,22 +2213,22 @@ var AppIcon = GObject.registerClass({
} }
vfunc_button_press_event(buttonEvent) { vfunc_button_press_event(buttonEvent) {
super.vfunc_button_press_event(buttonEvent); const ret = super.vfunc_button_press_event(buttonEvent);
if (buttonEvent.button == 1) { if (buttonEvent.button == 1) {
this._setPopupTimeout(); this._setPopupTimeout();
} else if (buttonEvent.button == 3) { } else if (buttonEvent.button == 3) {
this.popupMenu(); this.popupMenu();
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
} }
return Clutter.EVENT_PROPAGATE; return ret;
} }
vfunc_touch_event(touchEvent) { vfunc_touch_event(touchEvent) {
super.vfunc_touch_event(touchEvent); const ret = super.vfunc_touch_event(touchEvent);
if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN)
this._setPopupTimeout(); this._setPopupTimeout();
return Clutter.EVENT_PROPAGATE; return ret;
} }
vfunc_clicked(button) { vfunc_clicked(button) {
@@ -2339,7 +2401,17 @@ var AppIcon = GObject.registerClass({
} }
_setHoveringByDnd(hovering) { _setHoveringByDnd(hovering) {
if (this._otherIconIsHovering == hovering)
return;
this._otherIconIsHovering = hovering;
if (hovering) { if (hovering) {
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this),
};
DND.addDragMonitor(this._dragMonitor);
if (this._folderPreviewId > 0) if (this._folderPreviewId > 0)
return; return;
@@ -2351,6 +2423,8 @@ var AppIcon = GObject.registerClass({
return GLib.SOURCE_REMOVE; return GLib.SOURCE_REMOVE;
}); });
} else { } else {
DND.removeDragMonitor(this._dragMonitor);
if (this._folderPreviewId > 0) { if (this._folderPreviewId > 0) {
GLib.source_remove(this._folderPreviewId); GLib.source_remove(this._folderPreviewId);
this._folderPreviewId = 0; this._folderPreviewId = 0;
@@ -2360,32 +2434,13 @@ var AppIcon = GObject.registerClass({
} }
} }
_onDragBegin() {
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this),
};
DND.addDragMonitor(this._dragMonitor);
}
_onDragMotion(dragEvent) { _onDragMotion(dragEvent) {
let target = dragEvent.targetActor; if (!this.contains(dragEvent.targetActor))
let isHovering = target == this || this.contains(target); this._setHoveringByDnd(false);
let canDrop = this._canAccept(dragEvent.source);
let hasDndHover = isHovering && canDrop;
if (this._hasDndHover != hasDndHover) {
this._setHoveringByDnd(hasDndHover);
this._hasDndHover = hasDndHover;
}
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
} }
_onDragEnd() {
this.remove_style_pseudo_class('drop');
DND.removeDragMonitor(this._dragMonitor);
}
handleDragOver(source) { handleDragOver(source) {
if (source == this) if (source == this)
return DND.DragMotionResult.NO_DROP; return DND.DragMotionResult.NO_DROP;
@@ -2393,6 +2448,8 @@ var AppIcon = GObject.registerClass({
if (!this._canAccept(source)) if (!this._canAccept(source))
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
this._setHoveringByDnd(true);
return DND.DragMotionResult.MOVE_DROP; return DND.DragMotionResult.MOVE_DROP;
} }
@@ -2437,7 +2494,7 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
Main.uiGroup.add_actor(this.actor); Main.uiGroup.add_actor(this.actor);
} }
_redisplay() { _rebuildMenu() {
this.removeAll(); this.removeAll();
let windows = this._source.app.get_windows().filter( let windows = this._source.app.get_windows().filter(
@@ -2523,19 +2580,18 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) { if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) {
this._appendSeparator(); this._appendSeparator();
let item = this._appendMenuItem(_("Show Details")); let item = this._appendMenuItem(_("Show Details"));
item.connect('activate', () => { item.connect('activate', async () => {
let id = this._source.app.get_id(); let id = this._source.app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']); let args = GLib.Variant.new('(ss)', [id, '']);
Gio.DBus.get(Gio.BusType.SESSION, null, (o, res) => { const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
let bus = Gio.DBus.get_finish(res); bus.call(
bus.call('org.gnome.Software', 'org.gnome.Software',
'/org/gnome/Software', '/org/gnome/Software',
'org.gtk.Actions', 'Activate', 'org.gtk.Actions', 'Activate',
GLib.Variant.new('(sava{sv})', new GLib.Variant.new(
['details', [args], null]), '(sava{sv})', ['details', [args], null]),
null, 0, -1, null, null); null, 0, -1, null);
Main.overview.hide(); Main.overview.hide();
});
}); });
} }
} }
@@ -2554,7 +2610,7 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
} }
popup(_activatingButton) { popup(_activatingButton) {
this._redisplay(); this._rebuildMenu();
this.open(); this.open();
} }
}; };

View File

@@ -2,6 +2,7 @@
/* exported getAppFavorites */ /* exported getAppFavorites */
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const Signals = imports.signals; const Signals = imports.signals;
const Main = imports.ui.main; const Main = imports.ui.main;
@@ -64,6 +65,13 @@ const RENAMED_DESKTOP_IDS = {
class AppFavorites { class AppFavorites {
constructor() { constructor() {
// Filter the apps through the users parental controls.
this._parentalControlsManager = ParentalControlsManager.getDefault();
this._parentalControlsManager.connect('app-filter-changed', () => {
this.reload();
this.emit('changed');
});
this.FAVORITE_APPS_KEY = 'favorite-apps'; this.FAVORITE_APPS_KEY = 'favorite-apps';
this._favorites = {}; this._favorites = {};
global.settings.connect('changed::%s'.format(this.FAVORITE_APPS_KEY), this._onFavsChanged.bind(this)); global.settings.connect('changed::%s'.format(this.FAVORITE_APPS_KEY), this._onFavsChanged.bind(this));
@@ -95,7 +103,7 @@ class AppFavorites {
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
let apps = ids.map(id => appSys.lookup_app(id)) let apps = ids.map(id => appSys.lookup_app(id))
.filter(app => app != null); .filter(app => app !== null && this._parentalControlsManager.shouldShowApp(app.app_info));
this._favorites = {}; this._favorites = {};
for (let i = 0; i < apps.length; i++) { for (let i = 0; i < apps.length; i++) {
let app = apps[i]; let app = apps[i];
@@ -134,6 +142,9 @@ class AppFavorites {
if (!app) if (!app)
return false; return false;
if (!this._parentalControlsManager.shouldShowApp(app.app_info))
return false;
let ids = this._getIds(); let ids = this._getIds();
if (pos == -1) if (pos == -1)
ids.push(appId); ids.push(appId);

View File

@@ -199,46 +199,52 @@ class DBusEventSource extends EventSourceBase {
this._initialized = false; this._initialized = false;
this._dbusProxy = new CalendarServer(); this._dbusProxy = new CalendarServer();
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, (object, result) => { this._initProxy();
let loaded = false; }
try { async _initProxy() {
this._dbusProxy.init_finish(result); let loaded = false;
loaded = true;
} catch (e) { try {
if (e.matches(Gio.DBusError, Gio.DBusError.TIMED_OUT)) { await this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null);
// Ignore timeouts and install signals as normal, because with high loaded = true;
// probability the service will appear later on, and we will get a } catch (e) {
// NameOwnerChanged which will finish loading // Ignore timeouts and install signals as normal, because with high
// // probability the service will appear later on, and we will get a
// (But still _initialized to false, because the proxy does not know // NameOwnerChanged which will finish loading
// about the HasCalendars property and would cause an exception trying //
// to read it) // (But still _initialized to false, because the proxy does not know
} else { // about the HasCalendars property and would cause an exception trying
log('Error loading calendars: %s'.format(e.message)); // to read it)
return; if (!e.matches(Gio.DBusError, Gio.DBusError.TIMED_OUT)) {
} log('Error loading calendars: %s'.format(e.message));
return;
} }
}
this._dbusProxy.connectSignal('Changed', this._onChanged.bind(this)); this._dbusProxy.connectSignal('EventsAddedOrUpdated',
this._onEventsAddedOrUpdated.bind(this));
this._dbusProxy.connectSignal('EventsRemoved',
this._onEventsRemoved.bind(this));
this._dbusProxy.connectSignal('ClientDisappeared',
this._onClientDisappeared.bind(this));
this._dbusProxy.connect('notify::g-name-owner', () => { this._dbusProxy.connect('notify::g-name-owner', () => {
if (this._dbusProxy.g_name_owner) if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
});
this._dbusProxy.connect('g-properties-changed', () => {
this.notify('has-calendars');
});
this._initialized = loaded;
if (loaded) {
this.notify('has-calendars');
this._onNameAppeared(); this._onNameAppeared();
} else
this._onNameVanished();
}); });
this._dbusProxy.connect('g-properties-changed', () => {
this.notify('has-calendars');
});
this._initialized = loaded;
if (loaded) {
this.notify('has-calendars');
this._onNameAppeared();
}
} }
destroy() { destroy() {
@@ -257,7 +263,7 @@ class DBusEventSource extends EventSourceBase {
} }
_resetCache() { _resetCache() {
this._events = []; this._events = new Map();
this._lastRequestBegin = null; this._lastRequestBegin = null;
this._lastRequestEnd = null; this._lastRequestEnd = null;
} }
@@ -273,28 +279,47 @@ class DBusEventSource extends EventSourceBase {
this.emit('changed'); this.emit('changed');
} }
_onChanged() { _onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) {
this._loadEvents(false); const [appointments = []] = argArray;
let changed = false;
for (let n = 0; n < appointments.length; n++) {
const [id, summary, allDay, startTime, endTime] = appointments[n];
const date = new Date(startTime * 1000);
const end = new Date(endTime * 1000);
let event = new CalendarEvent(id, date, end, summary, allDay);
this._events.set(event.id, event);
changed = true;
}
if (changed)
this.emit('changed');
} }
_onEventsReceived(results, _error) { _onEventsRemoved(dbusProxy, nameOwner, argArray) {
let newEvents = []; const [ids = []] = argArray;
let appointments = results[0] || [];
for (let n = 0; n < appointments.length; n++) {
let a = appointments[n];
let date = new Date(a[4] * 1000);
let end = new Date(a[5] * 1000);
let id = a[0];
let summary = a[1];
let allDay = a[3];
let event = new CalendarEvent(id, date, end, summary, allDay);
newEvents.push(event);
}
newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime());
this._events = newEvents; let changed = false;
this._isLoading = false; for (const id of ids)
this.emit('changed'); changed |= this._events.delete(id);
if (changed)
this.emit('changed');
}
_onClientDisappeared(dbusProxy, nameOwner, argArray) {
let [sourceUid = ''] = argArray;
sourceUid += '\n';
let changed = false;
for (const id of this._events.keys()) {
if (id.startsWith(sourceUid))
changed |= this._events.delete(id);
}
if (changed)
this.emit('changed');
} }
_loadEvents(forceReload) { _loadEvents(forceReload) {
@@ -303,33 +328,38 @@ class DBusEventSource extends EventSourceBase {
return; return;
if (this._curRequestBegin && this._curRequestEnd) { if (this._curRequestBegin && this._curRequestEnd) {
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, if (forceReload) {
this._curRequestEnd.getTime() / 1000, this._events.clear();
forceReload, this.emit('changed');
this._onEventsReceived.bind(this), }
Gio.DBusCallFlags.NONE); this._dbusProxy.SetTimeRangeRemote(
this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
Gio.DBusCallFlags.NONE);
} }
} }
requestRange(begin, end) { requestRange(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this._isLoading = true;
this._lastRequestBegin = begin; this._lastRequestBegin = begin;
this._lastRequestEnd = end; this._lastRequestEnd = end;
this._curRequestBegin = begin; this._curRequestBegin = begin;
this._curRequestEnd = end; this._curRequestEnd = end;
this._loadEvents(false); this._loadEvents(true);
}
}
*_getFilteredEvents(begin, end) {
for (const event of this._events.values()) {
if (_dateIntervalsOverlap(event.date, event.end, begin, end))
yield event;
} }
} }
getEvents(begin, end) { getEvents(begin, end) {
let result = []; let result = [...this._getFilteredEvents(begin, end)];
for (let n = 0; n < this._events.length; n++) {
let event = this._events[n];
if (_dateIntervalsOverlap(event.date, event.end, begin, end))
result.push(event);
}
result.sort((event1, event2) => { result.sort((event1, event2) => {
// sort events by end time on ending day // sort events by end time on ending day
let d1 = event1.date < begin && event1.end <= end ? event1.end : event1.date; let d1 = event1.date < begin && event1.end <= end ? event1.end : event1.date;
@@ -343,12 +373,8 @@ class DBusEventSource extends EventSourceBase {
let dayBegin = _getBeginningOfDay(day); let dayBegin = _getBeginningOfDay(day);
let dayEnd = _getEndOfDay(day); let dayEnd = _getEndOfDay(day);
let events = this.getEvents(dayBegin, dayEnd); const { done } = this._getFilteredEvents(dayBegin, dayEnd).next();
return !done;
if (events.length == 0)
return false;
return true;
} }
}); });
@@ -700,12 +726,11 @@ var Calendar = GObject.registerClass({
var EventMessage = GObject.registerClass( var EventMessage = GObject.registerClass(
class EventMessage extends MessageList.Message { class EventMessage extends MessageList.Message {
_init(event, date) { _init(event, date) {
super._init('', event.summary); super._init('', '');
this._event = event;
this._date = date; this._date = date;
this.setTitle(this._formatEventTime()); this.update(event);
this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' }); this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
this.setIcon(this._icon); this.setIcon(this._icon);
@@ -717,6 +742,13 @@ class EventMessage extends MessageList.Message {
super.vfunc_style_changed(); super.vfunc_style_changed();
} }
update(event) {
this._event = event;
this.setTitle(this._formatEventTime());
this.setBody(event.summary);
}
_formatEventTime() { _formatEventTime() {
let periodBegin = _getBeginningOfDay(this._date); let periodBegin = _getBeginningOfDay(this._date);
let periodEnd = _getEndOfDay(this._date); let periodEnd = _getEndOfDay(this._date);
@@ -837,8 +869,9 @@ class EventsSection extends MessageList.MessageListSection {
this._title.connect('clicked', this._onTitleClicked.bind(this)); this._title.connect('clicked', this._onTitleClicked.bind(this));
this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this)); this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this));
Shell.AppSystem.get_default().connect('installed-changed', this._appSys = Shell.AppSystem.get_default();
this._appInstalledChanged.bind(this)); this._appSys.connect('installed-changed',
this._appInstalledChanged.bind(this));
this._appInstalledChanged(); this._appInstalledChanged();
} }
@@ -873,7 +906,7 @@ class EventsSection extends MessageList.MessageListSection {
} }
_reloadEvents() { _reloadEvents() {
if (this._eventSource.isLoading) if (this._eventSource.isLoading || this._reloading)
return; return;
this._reloading = true; this._reloading = true;
@@ -899,6 +932,7 @@ class EventsSection extends MessageList.MessageListSection {
this._messageById.set(event.id, message); this._messageById.set(event.id, message);
this.addMessage(message, false); this.addMessage(message, false);
} else { } else {
message.update(event);
this.moveMessage(message, i, false); this.moveMessage(message, i, false);
} }
} }
@@ -931,10 +965,13 @@ class EventsSection extends MessageList.MessageListSection {
Main.overview.hide(); Main.overview.hide();
Main.panel.closeCalendar(); Main.panel.closeCalendar();
let app = this._getCalendarApp(); let appInfo = this._getCalendarApp();
if (app.get_id() == 'evolution.desktop') if (appInfo.get_id() === 'org.gnome.Evolution.desktop') {
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop'); let app = this._appSys.lookup_app('evolution-calendar.desktop');
app.launch([], global.create_app_launch_context(0, -1)); if (app)
appInfo = app.app_info;
}
appInfo.launch([], global.create_app_launch_context(0, -1));
} }
setDate(date) { setDate(date) {
@@ -1149,17 +1186,22 @@ class CalendarMessageList extends St.Widget {
let hbox = new St.BoxLayout({ style_class: 'message-list-controls' }); let hbox = new St.BoxLayout({ style_class: 'message-list-controls' });
box.add_child(hbox); box.add_child(hbox);
hbox.add_child(new St.Label({ const dndLabel = new St.Label({
text: _('Do Not Disturb'), text: _('Do Not Disturb'),
y_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER,
})); });
hbox.add_child(dndLabel);
this._dndSwitch = new DoNotDisturbSwitch(); this._dndSwitch = new DoNotDisturbSwitch();
this._dndButton = new St.Button({ this._dndButton = new St.Button({
can_focus: true, can_focus: true,
toggle_mode: true,
child: this._dndSwitch, child: this._dndSwitch,
label_actor: dndLabel,
}); });
this._dndButton.connect('clicked', () => this._dndSwitch.toggle()); this._dndButton.bind_property('checked',
this._dndSwitch, 'state',
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE);
hbox.add_child(this._dndButton); hbox.add_child(this._dndButton);
this._clearButton = new St.Button({ this._clearButton = new St.Button({

View File

@@ -1,5 +1,5 @@
/* exported CheckBox */ /* exported CheckBox */
const { Clutter, GObject, Pango, St } = imports.gi; const { Atk, Clutter, GObject, Pango, St } = imports.gi;
var CheckBox = GObject.registerClass( var CheckBox = GObject.registerClass(
class CheckBox extends St.Button { class CheckBox extends St.Button {
@@ -15,6 +15,7 @@ class CheckBox extends St.Button {
toggle_mode: true, toggle_mode: true,
can_focus: true, can_focus: true,
}); });
this.set_accessible_role(Atk.Role.CHECK_BOX);
this._box = new St.Bin({ y_align: Clutter.ActorAlign.START }); this._box = new St.Bin({ y_align: Clutter.ActorAlign.START });
container.add_actor(this._box); container.add_actor(this._box);
@@ -22,6 +23,7 @@ class CheckBox extends St.Button {
this._label = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); this._label = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
this._label.clutter_text.set_line_wrap(true); this._label.clutter_text.set_line_wrap(true);
this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE); this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this.set_label_actor(this._label);
container.add_actor(this._label); container.add_actor(this._label);
if (label) if (label)

View File

@@ -188,6 +188,8 @@ var CloseDialog = GObject.registerClass({
global.stage.disconnect(this._keyFocusChangedId); global.stage.disconnect(this._keyFocusChangedId);
this._keyFocusChangedId = 0; this._keyFocusChangedId = 0;
this._dialog._dialog.remove_all_transitions();
let dialog = this._dialog; let dialog = this._dialog;
this._dialog = null; this._dialog = null;
this._removeWindowEffect(); this._removeWindowEffect();

View File

@@ -111,15 +111,11 @@ class KeyringDialog extends ModalDialog.ModalDialog {
} }
_updateSensitivity(sensitive) { _updateSensitivity(sensitive) {
if (this._passwordEntry) { if (this._passwordEntry)
this._passwordEntry.reactive = sensitive; this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
}
if (this._confirmEntry) { if (this._confirmEntry)
this._confirmEntry.reactive = sensitive; this._confirmEntry.reactive = sensitive;
this._confirmEntry.clutter_text.editable = sensitive;
}
this._continueButton.can_focus = sensitive; this._continueButton.can_focus = sensitive;
this._continueButton.reactive = sensitive; this._continueButton.reactive = sensitive;

View File

@@ -4,13 +4,16 @@
const { Clutter, Gio, GLib, GObject, NM, Pango, Shell, St } = imports.gi; const { Clutter, Gio, GLib, GObject, NM, Pango, Shell, St } = imports.gi;
const Signals = imports.signals; const Signals = imports.signals;
const Config = imports.misc.config;
const Dialog = imports.ui.dialog; const Dialog = imports.ui.dialog;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
Gio._promisify(Shell.NetworkAgent.prototype, 'init_async', 'init_finish');
Gio._promisify(Shell.NetworkAgent.prototype,
'search_vpn_plugin', 'search_vpn_plugin_finish');
const VPN_UI_GROUP = 'VPN Plugin UI'; const VPN_UI_GROUP = 'VPN Plugin UI';
var NetworkSecretDialog = GObject.registerClass( var NetworkSecretDialog = GObject.registerClass(
@@ -480,39 +483,37 @@ var VPNRequestHandler = class {
} }
} }
_readStdoutOldStyle() { async _readStdoutOldStyle() {
this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null, (stream, result) => { const [line, len_] =
let [line, len_] = this._dataStdout.read_line_finish_utf8(result); await this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null);
if (line == null) { if (line === null) {
// end of file // end of file
this._stdout.close(null); this._stdout.close(null);
return; return;
} }
this._vpnChildProcessLineOldStyle(line); this._vpnChildProcessLineOldStyle(line);
// try to read more! // try to read more!
this._readStdoutOldStyle(); this._readStdoutOldStyle();
});
} }
_readStdoutNewStyle() { async _readStdoutNewStyle() {
this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => { const cnt =
let cnt = this._dataStdout.fill_finish(result); await this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null);
if (cnt == 0) { if (cnt === 0) {
// end of file // end of file
this._showNewStyleDialog(); this._showNewStyleDialog();
this._stdout.close(null); this._stdout.close(null);
return; return;
} }
// Try to read more // Try to read more
this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size()); this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
this._readStdoutNewStyle(); this._readStdoutNewStyle();
});
} }
_showNewStyleDialog() { _showNewStyleDialog() {
@@ -615,27 +616,21 @@ var NetworkAgent = class {
this._vpnRequests = { }; this._vpnRequests = { };
this._notifications = { }; this._notifications = { };
this._pluginDir = Gio.file_new_for_path(Config.VPNDIR);
try {
let monitor = this._pluginDir.monitor(Gio.FileMonitorFlags.NONE, null);
monitor.connect('changed', () => (this._vpnCacheBuilt = false));
} catch (e) {
log('Failed to create monitor for VPN plugin dir: %s'.format(e.message));
}
this._native.connect('new-request', this._newRequest.bind(this)); this._native.connect('new-request', this._newRequest.bind(this));
this._native.connect('cancel-request', this._cancelRequest.bind(this)); this._native.connect('cancel-request', this._cancelRequest.bind(this));
this._initialized = false; this._initialized = false;
this._native.init_async(GLib.PRIORITY_DEFAULT, null, (o, res) => { this._initNative();
try { }
this._native.init_finish(res);
this._initialized = true; async _initNative() {
} catch (e) { try {
this._native = null; await this._native.init_async(GLib.PRIORITY_DEFAULT, null);
logError(e, 'error initializing the NetworkManager Agent'); this._initialized = true;
} } catch (e) {
}); this._native = null;
logError(e, 'error initializing the NetworkManager Agent');
}
} }
enable() { enable() {
@@ -766,13 +761,11 @@ var NetworkAgent = class {
} }
} }
_vpnRequest(requestId, connection, hints, flags) { async _vpnRequest(requestId, connection, hints, flags) {
let vpnSetting = connection.get_setting_vpn(); let vpnSetting = connection.get_setting_vpn();
let serviceType = vpnSetting.service_type; let serviceType = vpnSetting.service_type;
this._buildVPNServiceCache(); let binary = await this._findAuthBinary(serviceType);
let binary = this._vpnBinaries[serviceType];
if (!binary) { if (!binary) {
log('Invalid VPN service type (cannot find authentication binary)'); log('Invalid VPN service type (cannot find authentication binary)');
@@ -788,36 +781,30 @@ var NetworkAgent = class {
this._vpnRequests[requestId] = vpnRequest; this._vpnRequests[requestId] = vpnRequest;
} }
_buildVPNServiceCache() { async _findAuthBinary(serviceType) {
if (this._vpnCacheBuilt) let plugin;
return;
this._vpnCacheBuilt = true; try {
this._vpnBinaries = { }; plugin = await this._native.search_vpn_plugin(serviceType);
} catch (e) {
logError(e);
return null;
}
NM.VpnPluginInfo.list_load().forEach(plugin => { const fileName = plugin.get_auth_dialog();
let service = plugin.get_service(); if (!GLib.file_test(fileName, GLib.FileTest.IS_EXECUTABLE)) {
let fileName = plugin.get_auth_dialog(); log('VPN plugin at %s is not executable'.format(fileName));
let supportsHints = plugin.supports_hints(); return null;
let externalUIMode = false; }
let prop = plugin.lookup_property('GNOME', 'supports-external-ui-mode'); const prop = plugin.lookup_property('GNOME', 'supports-external-ui-mode');
if (prop) { const trimmedProp = prop ? prop.trim().toLowerCase() : '';
prop = prop.trim().toLowerCase();
externalUIMode = ['true', 'yes', 'on', '1'].includes(prop);
}
if (GLib.file_test(fileName, GLib.FileTest.IS_EXECUTABLE)) { return {
let binary = { fileName, externalUIMode, supportsHints }; fileName,
this._vpnBinaries[service] = binary; supportsHints: plugin.supports_hints(),
externalUIMode: ['true', 'yes', 'on', '1'].includes(trimmedProp),
plugin.get_aliases().forEach(alias => { };
this._vpnBinaries[alias] = binary;
});
} else {
log('VPN plugin at %s is not executable'.format(fileName));
}
});
} }
}; };
var Component = NetworkAgent; var Component = NetworkAgent;

View File

@@ -327,12 +327,16 @@ var AuthenticationDialog = GObject.registerClass({
} }
let resetDialog = () => { let resetDialog = () => {
this._sessionRequestTimeoutId = 0;
if (this.state != ModalDialog.State.OPENED) if (this.state != ModalDialog.State.OPENED)
return; return GLib.SOURCE_REMOVE;
this._passwordEntry.hide(); this._passwordEntry.hide();
this._cancelButton.grab_key_focus(); this._cancelButton.grab_key_focus();
this._okButton.reactive = false; this._okButton.reactive = false;
return GLib.SOURCE_REMOVE;
}; };
if (delay) { if (delay) {

View File

@@ -2,12 +2,19 @@
/* exported Component */ /* exported Component */
const { Clutter, Gio, GLib, GObject, St } = imports.gi; const { Clutter, Gio, GLib, GObject, St } = imports.gi;
const Lang = imports.lang;
var Tpl = null; var Tpl = null;
var Tp = null; var Tp = null;
try { try {
({ TelepathyGLib: Tp, TelepathyLogger: Tpl } = imports.gi); ({ TelepathyGLib: Tp, TelepathyLogger: Tpl } = imports.gi);
Gio._promisify(Tp.Channel.prototype, 'close_async', 'close_finish');
Gio._promisify(Tp.Channel.prototype,
'send_message_async', 'send_message_finish');
Gio._promisify(Tp.ChannelDispatchOperation.prototype,
'claim_with_async', 'claim_with_finish');
Gio._promisify(Tpl.LogManager.prototype,
'get_filtered_events_async', 'get_filtered_events_finish');
} catch (e) { } catch (e) {
log('Telepathy is not available, chat integration will be disabled.'); log('Telepathy is not available, chat integration will be disabled.');
} }
@@ -40,36 +47,59 @@ var NotificationDirection = {
RECEIVED: 'chat-received', RECEIVED: 'chat-received',
}; };
function makeMessageFromTpMessage(tpMessage, direction) { const ChatMessage = HAVE_TP ? GObject.registerClass({
let [text, flags_] = tpMessage.to_text(); Properties: {
'message-type': GObject.ParamSpec.int(
'message-type', 'message-type', 'message-type',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
Math.min(...Object.values(Tp.ChannelTextMessageType)),
Math.max(...Object.values(Tp.ChannelTextMessageType)),
Tp.ChannelTextMessageType.NORMAL),
'text': GObject.ParamSpec.string(
'text', 'text', 'text',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
null),
'sender': GObject.ParamSpec.string(
'sender', 'sender', 'sender',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
null),
'timestamp': GObject.ParamSpec.int64(
'timestamp', 'timestamp', 'timestamp',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
0, Number.MAX_SAFE_INTEGER, 0),
'direction': GObject.ParamSpec.string(
'direction', 'direction', 'direction',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
null),
},
}, class ChatMessageClass extends GObject.Object {
static newFromTpMessage(tpMessage, direction) {
return new ChatMessage({
'message-type': tpMessage.get_message_type(),
'text': tpMessage.to_text()[0],
'sender': tpMessage.sender.alias,
'timestamp': direction === NotificationDirection.RECEIVED
? tpMessage.get_received_timestamp() : tpMessage.get_sent_timestamp(),
direction,
});
}
let timestamp = tpMessage.get_sent_timestamp(); static newFromTplTextEvent(tplTextEvent) {
if (timestamp == 0) let direction =
timestamp = tpMessage.get_received_timestamp(); tplTextEvent.get_sender().get_entity_type() === Tpl.EntityType.SELF
? NotificationDirection.SENT : NotificationDirection.RECEIVED;
return { return new ChatMessage({
messageType: tpMessage.get_message_type(), 'message-type': tplTextEvent.get_message_type(),
text, 'text': tplTextEvent.get_message(),
sender: tpMessage.sender.alias, 'sender': tplTextEvent.get_sender().get_alias(),
timestamp, 'timestamp': tplTextEvent.get_timestamp(),
direction, direction,
}; });
} }
}) : null;
function makeMessageFromTplEvent(event) {
let sent = event.get_sender().get_entity_type() == Tpl.EntityType.SELF;
let direction = sent ? NotificationDirection.SENT : NotificationDirection.RECEIVED;
return {
messageType: event.get_message_type(),
text: event.get_message(),
sender: event.get_sender().get_alias(),
timestamp: event.get_timestamp(),
direction,
};
}
var TelepathyComponent = class { var TelepathyComponent = class {
constructor() { constructor() {
this._client = null; this._client = null;
@@ -193,7 +223,7 @@ class TelepathyClient extends Tp.BaseClient {
// We can only handle text channel, so close any other channel // We can only handle text channel, so close any other channel
if (!(channel instanceof Tp.TextChannel)) { if (!(channel instanceof Tp.TextChannel)) {
channel.close_async(null); channel.close_async();
continue; continue;
} }
@@ -239,7 +269,7 @@ class TelepathyClient extends Tp.BaseClient {
} }
} }
_approveTextChannel(account, conn, channel, dispatchOp, context) { async _approveTextChannel(account, conn, channel, dispatchOp, context) {
let [targetHandle_, targetHandleType] = channel.get_handle(); let [targetHandle_, targetHandleType] = channel.get_handle();
if (targetHandleType != Tp.HandleType.CONTACT) { if (targetHandleType != Tp.HandleType.CONTACT) {
@@ -248,17 +278,15 @@ class TelepathyClient extends Tp.BaseClient {
return; return;
} }
// Approve private text channels right away as we are going to handle it
dispatchOp.claim_with_async(this, (o, result) => {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, [channel], false);
} catch (err) {
log('Failed to Claim channel: %s'.format(err.toString()));
}
});
context.accept(); context.accept();
// Approve private text channels right away as we are going to handle it
try {
await dispatchOp.claim_with_async(this);
this._handlingChannels(account, conn, [channel], false);
} catch (err) {
log('Failed to Claim channel: %s'.format(err.toString()));
}
} }
_delegatedChannelsCb(_client, _channels) { _delegatedChannelsCb(_client, _channels) {
@@ -270,12 +298,12 @@ class TelepathyClient extends Tp.BaseClient {
var ChatSource = HAVE_TP ? GObject.registerClass( var ChatSource = HAVE_TP ? GObject.registerClass(
class ChatSource extends MessageTray.Source { class ChatSource extends MessageTray.Source {
_init(account, conn, channel, contact, client) { _init(account, conn, channel, contact, client) {
super._init(contact.get_alias());
this._account = account; this._account = account;
this._contact = contact; this._contact = contact;
this._client = client; this._client = client;
super._init(contact.get_alias());
this.isChat = true; this.isChat = true;
this._pendingMessages = []; this._pendingMessages = [];
@@ -419,19 +447,16 @@ class ChatSource extends MessageTray.Source {
} }
} }
_getLogMessages() { async _getLogMessages() {
let logManager = Tpl.LogManager.dup_singleton(); let logManager = Tpl.LogManager.dup_singleton();
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT); let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
logManager.get_filtered_events_async(this._account, entity, const [events] = await logManager.get_filtered_events_async(
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES, this._account, entity,
null, this._displayPendingMessages.bind(this)); Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
} null);
_displayPendingMessages(logManager, result) { let logMessages = events.map(e => ChatMessage.newFromTplTextEvent(e));
let [success_, events] = logManager.get_filtered_events_finish(result);
let logMessages = events.map(makeMessageFromTplEvent);
this._ensureNotification(); this._ensureNotification();
let pendingTpMessages = this._channel.get_pending_messages(); let pendingTpMessages = this._channel.get_pending_messages();
@@ -443,7 +468,8 @@ class ChatSource extends MessageTray.Source {
if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT) if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT)
continue; continue;
pendingMessages.push(makeMessageFromTpMessage(message, NotificationDirection.RECEIVED)); pendingMessages.push(ChatMessage.newFromTpMessage(message,
NotificationDirection.RECEIVED));
this._pendingMessages.push(message); this._pendingMessages.push(message);
} }
@@ -486,9 +512,7 @@ class ChatSource extends MessageTray.Source {
this._ackMessages(); this._ackMessages();
// The chat box has been destroyed so it can't // The chat box has been destroyed so it can't
// handle the channel any more. // handle the channel any more.
this._channel.close_async((channel, result) => { this._channel.close_async();
channel.close_finish(result);
});
} else { } else {
// Don't indicate any unread messages when the notification // Don't indicate any unread messages when the notification
// that represents them has been destroyed. // that represents them has been destroyed.
@@ -541,7 +565,8 @@ class ChatSource extends MessageTray.Source {
this._pendingMessages.push(message); this._pendingMessages.push(message);
this.countUpdated(); this.countUpdated();
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED); message = ChatMessage.newFromTpMessage(message,
NotificationDirection.RECEIVED);
this._notification.appendMessage(message); this._notification.appendMessage(message);
// Wait a bit before notifying for the received message, a handler // Wait a bit before notifying for the received message, a handler
@@ -566,7 +591,8 @@ class ChatSource extends MessageTray.Source {
// our client and other clients as well. // our client and other clients as well.
_messageSent(channel, message, _flags, _token) { _messageSent(channel, message, _flags, _token) {
this._ensureNotification(); this._ensureNotification();
message = makeMessageFromTpMessage(message, NotificationDirection.SENT); message = ChatMessage.newFromTpMessage(message,
NotificationDirection.SENT);
this._notification.appendMessage(message); this._notification.appendMessage(message);
} }
@@ -584,9 +610,7 @@ class ChatSource extends MessageTray.Source {
} }
let msg = Tp.ClientMessage.new_text(type, text); let msg = Tp.ClientMessage.new_text(type, text);
this._channel.send_message_async(msg, 0, (src, result) => { this._channel.send_message_async(msg, 0);
this._channel.send_message_finish(result);
});
} }
setChatState(state) { setChatState(state) {
@@ -630,11 +654,19 @@ class ChatSource extends MessageTray.Source {
} }
}) : null; }) : null;
const ChatNotificationMessage = HAVE_TP ? GObject.registerClass(
class ChatNotificationMessage extends GObject.Object {
_init(props = {}) {
super._init();
this.set(props);
}
}) : null;
var ChatNotification = HAVE_TP ? GObject.registerClass({ var ChatNotification = HAVE_TP ? GObject.registerClass({
Signals: { Signals: {
'message-removed': { param_types: [Tp.Message.$gtype] }, 'message-removed': { param_types: [ChatNotificationMessage.$gtype] },
'message-added': { param_types: [Tp.Message.$gtype] }, 'message-added': { param_types: [ChatNotificationMessage.$gtype] },
'timestamp-changed': { param_types: [Tp.Message.$gtype] }, 'timestamp-changed': { param_types: [ChatNotificationMessage.$gtype] },
}, },
}, class ChatNotification extends MessageTray.Notification { }, class ChatNotification extends MessageTray.Notification {
_init(source) { _init(source) {
@@ -735,21 +767,24 @@ var ChatNotification = HAVE_TP ? GObject.registerClass({
styles: [], styles: [],
timestamp: currentTime, timestamp: currentTime,
noTimestamp: false }); noTimestamp: false });
const { noTimestamp } = props;
delete props.noTimestamp;
// Reset the old message timeout // Reset the old message timeout
if (this._timestampTimeoutId) if (this._timestampTimeoutId)
GLib.source_remove(this._timestampTimeoutId); GLib.source_remove(this._timestampTimeoutId);
this._timestampTimeoutId = 0; this._timestampTimeoutId = 0;
let message = { realMessage: props.group != 'meta', let message = new ChatNotificationMessage({
showTimestamp: false }; realMessage: props.group !== 'meta',
Lang.copyProperties(props, message); showTimestamp: false,
delete message.noTimestamp; ...props,
});
this.messages.unshift(message); this.messages.unshift(message);
this.emit('message-added', message); this.emit('message-added', message);
if (!props.noTimestamp) { if (!noTimestamp) {
let timestamp = props.timestamp; let timestamp = props.timestamp;
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) { if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
this.appendTimestamp(); this.appendTimestamp();

View File

@@ -180,14 +180,27 @@ class WorldClocksSection extends St.Button {
let time = new St.Label({ style_class: 'world-clocks-time' }); let time = new St.Label({ style_class: 'world-clocks-time' });
let otherOffset = this._getTimeAtLocation(l).get_utc_offset(); const utcOffset = this._getTimeAtLocation(l).get_utc_offset();
let offset = (otherOffset - localOffset) / GLib.TIME_SPAN_HOUR; const offsetCurrentTz = utcOffset - localOffset;
let fmt = Math.trunc(offset) == offset ? '%s%.0f' : '%s%.1f'; const offsetHours = Math.abs(offsetCurrentTz) / GLib.TIME_SPAN_HOUR;
let prefix = offset >= 0 ? '+' : '-'; const offsetMinutes =
let tz = new St.Label({ style_class: 'world-clocks-timezone', (Math.abs(offsetCurrentTz) % GLib.TIME_SPAN_HOUR) /
text: fmt.format(prefix, Math.abs(offset)), GLib.TIME_SPAN_MINUTE;
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER }); const prefix = offsetCurrentTz >= 0 ? '+' : '-';
const text = offsetMinutes === 0
? '%s%d'.format(prefix, offsetHours)
: '%s%d\u2236%d'.format(prefix, offsetHours, offsetMinutes);
const tz = new St.Label({
style_class: 'world-clocks-timezone',
text,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER,
});
time.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
tz.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
if (this._grid.text_direction == Clutter.TextDirection.RTL) { if (this._grid.text_direction == Clutter.TextDirection.RTL) {
layout.attach(tz, 0, i + 1, 1, 1); layout.attach(tz, 0, i + 1, 1, 1);
@@ -268,13 +281,13 @@ class WeatherSection extends St.Button {
this.child = box; this.child = box;
let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' }); let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' });
titleBox.add_child(new St.Label({ this._titleLabel = new St.Label({
style_class: 'weather-header', style_class: 'weather-header',
x_align: Clutter.ActorAlign.START, x_align: Clutter.ActorAlign.START,
x_expand: true, x_expand: true,
y_align: Clutter.ActorAlign.END, y_align: Clutter.ActorAlign.END,
text: _('Weather'), });
})); titleBox.add_child(this._titleLabel);
box.add_child(titleBox); box.add_child(titleBox);
this._titleLocation = new St.Label({ this._titleLocation = new St.Label({
@@ -384,21 +397,28 @@ class WeatherSection extends St.Button {
layout.attach(label, 0, 0, 1, 1); layout.attach(label, 0, 0, 1, 1);
} }
_findBestLocationName(loc) {
const locName = loc.get_name();
if (loc.get_level() === GWeather.LocationLevel.CITY ||
!loc.has_coords())
return locName;
const world = GWeather.Location.get_world();
const city = world.find_nearest_city(...loc.get_coords());
const cityName = city.get_name();
return locName.includes(cityName) ? cityName : locName;
}
_updateForecasts() { _updateForecasts() {
this._forecastGrid.destroy_all_children(); this._forecastGrid.destroy_all_children();
if (!this._weatherClient.hasLocation) { if (!this._weatherClient.hasLocation)
this._setStatusLabel(_("Select a location…"));
return; return;
}
let info = this._weatherClient.info; const { info } = this._weatherClient;
let loc = info.get_location(); this._titleLocation.text = this._findBestLocationName(info.location);
if (loc.get_level() !== GWeather.LocationLevel.CITY && loc.has_coords()) {
let world = GWeather.Location.get_world();
loc = world.find_nearest_city(...loc.get_coords());
}
this._titleLocation.text = loc.get_name();
if (this._weatherClient.loading) { if (this._weatherClient.loading) {
this._setStatusLabel(_("Loading…")); this._setStatusLabel(_("Loading…"));
@@ -422,6 +442,12 @@ class WeatherSection extends St.Button {
if (!this.visible) if (!this.visible)
return; return;
if (this._weatherClient.hasLocation)
this._titleLabel.text = _('Weather');
else
this._titleLabel.text = _('Select weather location…');
this._forecastGrid.visible = this._weatherClient.hasLocation;
this._titleLocation.visible = this._weatherClient.hasLocation; this._titleLocation.visible = this._weatherClient.hasLocation;
this._updateForecasts(); this._updateForecasts();

View File

@@ -278,7 +278,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog'); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
} }
_onPkOfflineProxyCreated(proxy, error) { async _onPkOfflineProxyCreated(proxy, error) {
if (error) { if (error) {
log(error.message); log(error.message);
return; return;
@@ -293,15 +293,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
} }
// It only makes sense to check for this permission if PackageKit is available. // It only makes sense to check for this permission if PackageKit is available.
Polkit.Permission.new( try {
'org.freedesktop.packagekit.trigger-offline-update', null, null, this._updatesPermission = await Polkit.Permission.new(
(source, res) => { 'org.freedesktop.packagekit.trigger-offline-update', null, null);
try { } catch (e) {
this._updatesPermission = Polkit.Permission.new_finish(res); log('No permission to trigger offline updates: %s'.format(e.toString()));
} catch (e) { }
log('No permission to trigger offline updates: %s'.format(e.toString()));
}
});
} }
_onDestroy() { _onDestroy() {
@@ -346,10 +343,8 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
// Use a different description when we are installing a system upgrade // Use a different description when we are installing a system upgrade
// if the PackageKit proxy is available (i.e. PackageKit is available). // if the PackageKit proxy is available (i.e. PackageKit is available).
if (this._pkOfflineProxy && dialogContent.upgradeDescription) { if (dialogContent.upgradeDescription) {
let name = this._pkOfflineProxy.PreparedUpgrade['name'].deep_unpack(); const { name, version } = this._updateInfo.PreparedUpgrade;
let version = this._pkOfflineProxy.PreparedUpgrade['version'].deep_unpack();
if (name != null && version != null) if (name != null && version != null)
description = dialogContent.upgradeDescription(name, version); description = dialogContent.upgradeDescription(name, version);
} }
@@ -608,16 +603,46 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
}); });
} }
OpenAsync(parameters, invocation) { async _getUpdateInfo() {
const connection = this._pkOfflineProxy.get_connection();
const reply = await connection.call(
this._pkOfflineProxy.g_name,
this._pkOfflineProxy.g_object_path,
'org.freedesktop.DBus.Properties',
'GetAll',
new GLib.Variant('(s)', [this._pkOfflineProxy.g_interface_name]),
null,
Gio.DBusCallFlags.NONE,
-1,
null);
const [info] = reply.recursiveUnpack();
return info;
}
async OpenAsync(parameters, invocation) {
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters; let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
this._totalSecondsToStayOpen = totalSecondsToStayOpen; this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._type = type; this._type = type;
try {
this._updateInfo = await this._getUpdateInfo();
} catch (e) {
if (this._pkOfflineProxy !== null)
log('Failed to get update info from PackageKit: %s'.format(e.message));
this._updateInfo = {
UpdateTriggered: false,
UpdatePrepared: false,
UpgradeTriggered: false,
PreparedUpgrade: {},
};
}
// Only consider updates and upgrades if PackageKit is available. // Only consider updates and upgrades if PackageKit is available.
if (this._pkOfflineProxy && this._type == DialogType.RESTART) { if (this._pkOfflineProxy && this._type == DialogType.RESTART) {
if (this._pkOfflineProxy.UpdateTriggered) if (this._updateInfo.UpdateTriggered)
this._type = DialogType.UPDATE_RESTART; this._type = DialogType.UPDATE_RESTART;
else if (this._pkOfflineProxy.UpgradeTriggered) else if (this._updateInfo.UpgradeTriggered)
this._type = DialogType.UPGRADE_RESTART; this._type = DialogType.UPGRADE_RESTART;
} }
@@ -646,14 +671,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
if (dialogContent.showOtherSessions) if (dialogContent.showOtherSessions)
this._loadSessions(); this._loadSessions();
// Only consider updates and upgrades if PackageKit is available. let updateTriggered = this._updateInfo.UpdateTriggered;
let updateTriggered = this._pkOfflineProxy ? this._pkOfflineProxy.UpdateTriggered : false; let updatePrepared = this._updateInfo.UpdatePrepared;
let updatePrepared = this._pkOfflineProxy ? this._pkOfflineProxy.UpdatePrepared : false;
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed; let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || ''); _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed; this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed;
this._checkBox.checked = updatePrepared && updateTriggered; this._checkBox.checked = this._checkBox.visible;
// We show the warning either together with the checkbox, or when // We show the warning either together with the checkbox, or when
// updates have already been triggered, but the user doesn't have // updates have already been triggered, but the user doesn't have

View File

@@ -10,10 +10,21 @@ imports.gi.versions.Gtk = '3.0';
imports.gi.versions.TelepathyGLib = '0.12'; imports.gi.versions.TelepathyGLib = '0.12';
imports.gi.versions.TelepathyLogger = '0.2'; imports.gi.versions.TelepathyLogger = '0.2';
const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; const { Clutter, Gio, GLib, GObject, Meta, Polkit, Shell, St } = imports.gi;
const Gettext = imports.gettext; const Gettext = imports.gettext;
const System = imports.system; const System = imports.system;
Gio._promisify(Gio.DataInputStream.prototype, 'fill_async', 'fill_finish');
Gio._promisify(Gio.DataInputStream.prototype,
'read_line_async', 'read_line_finish');
Gio._promisify(Gio.DBus, 'get', 'get_finish');
Gio._promisify(Gio.DBusConnection.prototype, 'call', 'call_finish');
Gio._promisify(Gio.DBusProxy, 'new', 'new_finish');
Gio._promisify(Gio.DBusProxy.prototype, 'init_async', 'init_finish');
Gio._promisify(Gio.DBusProxy.prototype,
'call_with_unix_fd_list', 'call_with_unix_fd_list_finish');
Gio._promisify(Polkit.Permission, 'new', 'new_finish');
let _localTimeZone = null; let _localTimeZone = null;
// We can't import shell JS modules yet, because they may have // We can't import shell JS modules yet, because they may have
@@ -234,16 +245,15 @@ function _loggingFunc(...args) {
} }
function init() { function init() {
// Add some bindings to the global JS namespace; (gjs keeps the web // Add some bindings to the global JS namespace
// browser convention of having that namespace be called 'window'.) globalThis.global = Shell.Global.get();
window.global = Shell.Global.get();
window.log = _loggingFunc; globalThis.log = _loggingFunc;
window._ = Gettext.gettext; globalThis._ = Gettext.gettext;
window.C_ = Gettext.pgettext; globalThis.C_ = Gettext.pgettext;
window.ngettext = Gettext.ngettext; globalThis.ngettext = Gettext.ngettext;
window.N_ = s => s; globalThis.N_ = s => s;
GObject.gtypeNameBasedOnJSPath = true; GObject.gtypeNameBasedOnJSPath = true;
@@ -288,6 +298,13 @@ function init() {
}, },
}); });
Gio._LocalFilePrototype.touch_async = function (callback) {
Shell.util_touch_file_async(this, callback);
};
Gio._LocalFilePrototype.touch_finish = function (result) {
return Shell.util_touch_file_finish(this, result);
};
St.set_slow_down_factor = function (factor) { St.set_slow_down_factor = function (factor) {
let { stack } = new Error(); let { stack } = new Error();
log(`St.set_slow_down_factor() is deprecated, use St.Settings.slow_down_factor\n${stack}`); log(`St.set_slow_down_factor() is deprecated, use St.Settings.slow_down_factor\n${stack}`);
@@ -338,9 +355,7 @@ function init() {
// OK, now things are initialized enough that we can import shell JS // OK, now things are initialized enough that we can import shell JS
const Format = imports.format; const Format = imports.format;
const Tweener = imports.ui.tweener;
Tweener.init();
String.prototype.format = Format.format; String.prototype.format = Format.format;
} }

View File

@@ -56,6 +56,15 @@ function uninstallExtension(uuid) {
return false; return false;
FileUtils.recursivelyDeleteDir(extension.dir, true); FileUtils.recursivelyDeleteDir(extension.dir, true);
try {
const updatesDir = Gio.File.new_for_path(GLib.build_filenamev(
[global.userdatadir, 'extension-updates', extension.uuid]));
FileUtils.recursivelyDeleteDir(updatesDir, true);
} catch (e) {
// not an error
}
return true; return true;
} }
@@ -99,6 +108,9 @@ function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
} }
function downloadExtensionUpdate(uuid) { function downloadExtensionUpdate(uuid) {
if (!Main.extensionManager.updatesSupported)
return;
let dir = Gio.File.new_for_path( let dir = Gio.File.new_for_path(
GLib.build_filenamev([global.userdatadir, 'extension-updates', uuid])); GLib.build_filenamev([global.userdatadir, 'extension-updates', uuid]));
@@ -117,6 +129,9 @@ function downloadExtensionUpdate(uuid) {
} }
function checkForUpdates() { function checkForUpdates() {
if (!Main.extensionManager.updatesSupported)
return;
let metadatas = {}; let metadatas = {};
Main.extensionManager.getUuids().forEach(uuid => { Main.extensionManager.getUuids().forEach(uuid => {
let extension = Main.extensionManager.lookup(uuid); let extension = Main.extensionManager.lookup(uuid);
@@ -127,6 +142,9 @@ function checkForUpdates() {
metadatas[uuid] = extension.metadata; metadatas[uuid] = extension.metadata;
}); });
if (Object.keys(metadatas).length === 0)
return; // nothing to update
let versionCheck = global.settings.get_boolean( let versionCheck = global.settings.get_boolean(
'disable-extension-version-validation'); 'disable-extension-version-validation');
let params = { let params = {
@@ -144,9 +162,7 @@ function checkForUpdates() {
let operations = JSON.parse(message.response_body.data); let operations = JSON.parse(message.response_body.data);
for (let uuid in operations) { for (let uuid in operations) {
let operation = operations[uuid]; let operation = operations[uuid];
if (operation == 'blacklist') if (operation === 'upgrade' || operation === 'downgrade')
uninstallExtension(uuid);
else if (operation == 'upgrade' || operation == 'downgrade')
downloadExtensionUpdate(uuid); downloadExtensionUpdate(uuid);
} }
}); });

View File

@@ -60,6 +60,11 @@ var ExtensionManager = class {
ExtensionDownloader.checkForUpdates(); ExtensionDownloader.checkForUpdates();
} }
get updatesSupported() {
const appSys = Shell.AppSystem.get_default();
return appSys.lookup_app('org.gnome.Extensions.desktop') !== null;
}
lookup(uuid) { lookup(uuid) {
return this._extensions.get(uuid); return this._extensions.get(uuid);
} }
@@ -210,6 +215,24 @@ var ExtensionManager = class {
return true; return true;
} }
openExtensionPrefs(uuid, parentWindow, options) {
const extension = this.lookup(uuid);
if (!extension || !extension.hasPrefs)
return false;
Gio.DBus.session.call(
'org.gnome.Shell.Extensions',
'/org/gnome/Shell/Extensions',
'org.gnome.Shell.Extensions',
'OpenExtensionPrefs',
new GLib.Variant('(ssa{sv})', [uuid, parentWindow, options]),
null,
Gio.DBusCallFlags.NONE,
-1,
null);
return true;
}
notifyExtensionUpdate(uuid) { notifyExtensionUpdate(uuid) {
let extension = this.lookup(uuid); let extension = this.lookup(uuid);
if (!extension) if (!extension)
@@ -481,6 +504,9 @@ var ExtensionManager = class {
} }
_installExtensionUpdates() { _installExtensionUpdates() {
if (!this.updatesSupported)
return;
FileUtils.collectFromDatadirs('extension-updates', true, (dir, info) => { FileUtils.collectFromDatadirs('extension-updates', true, (dir, info) => {
let fileType = info.get_file_type(); let fileType = info.get_file_type();
if (fileType !== Gio.FileType.DIRECTORY) if (fileType !== Gio.FileType.DIRECTORY)
@@ -489,9 +515,14 @@ var ExtensionManager = class {
let extensionDir = Gio.File.new_for_path( let extensionDir = Gio.File.new_for_path(
GLib.build_filenamev([global.userdatadir, 'extensions', uuid])); GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
FileUtils.recursivelyDeleteDir(extensionDir, false); try {
FileUtils.recursivelyMoveDir(dir, extensionDir); FileUtils.recursivelyDeleteDir(extensionDir, false);
FileUtils.recursivelyDeleteDir(dir, true); FileUtils.recursivelyMoveDir(dir, extensionDir);
} catch (e) {
log('Failed to install extension updates for %s'.format(uuid));
} finally {
FileUtils.recursivelyDeleteDir(dir, true);
}
}); });
} }

View File

@@ -114,8 +114,11 @@ class BaseIcon extends St.Bin {
if (this._setSizeManually) { if (this._setSizeManually) {
size = this.iconSize; size = this.iconSize;
} else { } else {
const { scaleFactor } =
St.ThemeContext.get_for_stage(global.stage);
let [found, len] = node.lookup_length('icon-size', false); let [found, len] = node.lookup_length('icon-size', false);
size = found ? len : ICON_SIZE; size = found ? len / scaleFactor : ICON_SIZE;
} }
if (this.iconSize == size && this._iconBin.child) if (this.iconSize == size && this._iconBin.child)

View File

@@ -498,7 +498,7 @@ var Key = GObject.registerClass({
var KeyboardModel = class { var KeyboardModel = class {
constructor(groupName) { constructor(groupName) {
let names = [groupName]; let names = [groupName];
if (names.includes('+')) if (groupName.includes('+'))
names.push(groupName.replace(/\+.*/, '')); names.push(groupName.replace(/\+.*/, ''));
names.push('us'); names.push('us');
@@ -1119,11 +1119,12 @@ var KeyboardManager = class KeyBoardManager {
this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this)); this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this));
this._lastDevice = null; this._lastDevice = null;
Meta.get_backend().connect('last-device-changed', (backend, device) => { global.backend.connect('last-device-changed', (backend, device) => {
if (device.get_device_name().indexOf('XTEST') < 0) { if (device.device_type === Clutter.InputDeviceType.KEYBOARD_DEVICE)
this._lastDevice = device; return;
this._syncEnabled();
} this._lastDevice = device;
this._syncEnabled();
}); });
this._syncEnabled(); this._syncEnabled();
} }
@@ -1148,9 +1149,9 @@ var KeyboardManager = class KeyBoardManager {
this._keyboard = new Keyboard(); this._keyboard = new Keyboard();
} else if (!enabled && this._keyboard) { } else if (!enabled && this._keyboard) {
this._keyboard.setCursorLocation(null); this._keyboard.setCursorLocation(null);
Main.layoutManager.hideKeyboard(true);
this._keyboard.destroy(); this._keyboard.destroy();
this._keyboard = null; this._keyboard = null;
Main.layoutManager.hideKeyboard(true);
} }
} }
@@ -1868,6 +1869,10 @@ var KeyboardController = class {
Main.inputMethod.disconnect(this._notifyContentPurposeId); Main.inputMethod.disconnect(this._notifyContentPurposeId);
Main.inputMethod.disconnect(this._notifyContentHintsId); Main.inputMethod.disconnect(this._notifyContentHintsId);
Main.inputMethod.disconnect(this._notifyInputPanelStateId); Main.inputMethod.disconnect(this._notifyInputPanelStateId);
// Make sure any buttons pressed by the virtual device are released
// immediately instead of waiting for the next GC cycle
this._virtualDevice.run_dispose();
} }
_onSourcesModified() { _onSourcesModified() {

View File

@@ -612,10 +612,20 @@ var LayoutManager = GObject.registerClass({
let signalId = this._systemBackground.connect('loaded', () => { let signalId = this._systemBackground.connect('loaded', () => {
this._systemBackground.disconnect(signalId); this._systemBackground.disconnect(signalId);
this._systemBackground.show();
global.stage.show();
this._prepareStartupAnimation(); // We're mostly prepared for the startup animation
// now, but since a lot is going on asynchronously
// during startup, let's defer the startup animation
// until the event loop is uncontended and idle.
// This helps to prevent us from running the animation
// when the system is bogged down
const id = GLib.idle_add(GLib.PRIORITY_LOW, () => {
this._systemBackground.show();
global.stage.show();
this._prepareStartupAnimation();
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] Startup Animation');
}); });
} }
@@ -672,17 +682,7 @@ var LayoutManager = GObject.registerClass({
this.emit('startup-prepared'); this.emit('startup-prepared');
// We're mostly prepared for the startup animation this._startupAnimation();
// now, but since a lot is going on asynchronously
// during startup, let's defer the startup animation
// until the event loop is uncontended and idle.
// This helps to prevent us from running the animation
// when the system is bogged down
let id = GLib.idle_add(GLib.PRIORITY_LOW, () => {
this._startupAnimation();
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] this._startupAnimation');
} }
_startupAnimation() { _startupAnimation() {
@@ -765,7 +765,7 @@ var LayoutManager = GObject.registerClass({
this._keyboardHeightNotifyId = 0; this._keyboardHeightNotifyId = 0;
} }
this.keyboardBox.ease({ this.keyboardBox.ease({
translation_y: this.keyboardBox.height, translation_y: 0,
opacity: 0, opacity: 0,
duration: immediate ? 0 : KEYBOARD_ANIMATION_TIME, duration: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_IN_QUAD, mode: Clutter.AnimationMode.EASE_IN_QUAD,

View File

@@ -37,8 +37,8 @@ const LG_ANIMATION_TIME = 500;
function _getAutoCompleteGlobalKeywords() { function _getAutoCompleteGlobalKeywords() {
const keywords = ['true', 'false', 'null', 'new']; const keywords = ['true', 'false', 'null', 'new'];
// Don't add the private properties of window (i.e., ones starting with '_') // Don't add the private properties of globalThis (i.e., ones starting with '_')
const windowProperties = Object.getOwnPropertyNames(window).filter( const windowProperties = Object.getOwnPropertyNames(globalThis).filter(
a => a.charAt(0) != '_' a => a.charAt(0) != '_'
); );
const headerProperties = JsParse.getDeclaredConstants(commandHeader); const headerProperties = JsParse.getDeclaredConstants(commandHeader);
@@ -1127,7 +1127,7 @@ class LookingGlass extends St.BoxLayout {
else if (symbol == Clutter.KEY_Page_Down) else if (symbol == Clutter.KEY_Page_Down)
this._notebook.nextTab(); this._notebook.nextTab();
} }
return Clutter.EVENT_PROPAGATE; return super.vfunc_key_press_event(keyPressEvent);
} }
open() { open() {

View File

@@ -46,6 +46,7 @@ const XdndHandler = imports.ui.xdndHandler;
const KbdA11yDialog = imports.ui.kbdA11yDialog; const KbdA11yDialog = imports.ui.kbdA11yDialog;
const LocatePointer = imports.ui.locatePointer; const LocatePointer = imports.ui.locatePointer;
const PointerA11yTimeout = imports.ui.pointerA11yTimeout; const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const STICKY_KEYS_ENABLE = 'stickykeys-enable'; const STICKY_KEYS_ENABLE = 'stickykeys-enable';
@@ -93,6 +94,11 @@ let _a11ySettings = null;
let _themeResource = null; let _themeResource = null;
let _oskResource = null; let _oskResource = null;
Gio._promisify(Gio._LocalFilePrototype, 'delete_async', 'delete_finish');
Gio._promisify(Gio._LocalFilePrototype, 'touch_async', 'touch_finish');
let _remoteAccessInhibited = false;
function _sessionUpdated() { function _sessionUpdated() {
if (sessionMode.isPrimary) if (sessionMode.isPrimary)
_loadDefaultStylesheet(); _loadDefaultStylesheet();
@@ -117,24 +123,41 @@ function _sessionUpdated() {
if (lookingGlass) if (lookingGlass)
lookingGlass.close(); lookingGlass.close();
} }
let remoteAccessController = global.backend.get_remote_access_controller();
if (remoteAccessController) {
if (sessionMode.allowScreencast && _remoteAccessInhibited) {
remoteAccessController.uninhibit_remote_access();
_remoteAccessInhibited = false;
} else if (!sessionMode.allowScreencast && !_remoteAccessInhibited) {
remoteAccessController.inhibit_remote_access();
_remoteAccessInhibited = true;
}
}
} }
function start() { function start() {
// These are here so we don't break compatibility. // These are here so we don't break compatibility.
global.logError = window.log; global.logError = globalThis.log;
global.log = window.log; global.log = globalThis.log;
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', (global, msg, detail) => { global.connect('notify-error', (global, msg, detail) => {
notifyError(msg, detail); notifyError(msg, detail);
}); });
Gio.DesktopAppInfo.set_desktop_env('GNOME'); let currentDesktop = GLib.getenv('XDG_CURRENT_DESKTOP');
if (!currentDesktop || !currentDesktop.split(':').includes('GNOME'))
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
sessionMode.connect('updated', _sessionUpdated); sessionMode.connect('updated', _sessionUpdated);
St.Settings.get().connect('notify::gtk-theme', _loadDefaultStylesheet); St.Settings.get().connect('notify::gtk-theme', _loadDefaultStylesheet);
// Initialize ParentalControlsManager before the UI
ParentalControlsManager.getDefault();
_initializeUI(); _initializeUI();
shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus(); shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
@@ -142,6 +165,11 @@ function start() {
shellDBusService = new ShellDBus.GnomeShell(); shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
const watchId = Gio.DBus.session.watch_name('org.gnome.Shell.Notifications',
Gio.BusNameWatcherFlags.AUTO_START,
bus => bus.unwatch_name(watchId),
bus => bus.unwatch_name(watchId));
_sessionUpdated(); _sessionUpdated();
} }
@@ -271,11 +299,8 @@ function _initializeUI() {
} }
if (sessionMode.currentMode !== 'gdm' && if (sessionMode.currentMode !== 'gdm' &&
sessionMode.currentMode !== 'initial-setup' && sessionMode.currentMode !== 'initial-setup')
screenShield === null) { _handleLockScreenWarning();
notify(_('Screen Lock disabled'),
_('Screen Locking requires the GNOME display manager.'));
}
LoginManager.registerSessionWithGDM(); LoginManager.registerSessionWithGDM();
@@ -288,6 +313,32 @@ function _initializeUI() {
}); });
} }
async function _handleLockScreenWarning() {
const path = '%s/lock-warning-shown'.format(global.userdatadir);
const file = Gio.File.new_for_path(path);
const hasLockScreen = screenShield !== null;
if (hasLockScreen) {
try {
await file.delete_async(0, null);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
logError(e);
}
} else {
try {
if (!await file.touch_async())
return;
} catch (e) {
logError(e);
}
notify(
_('Screen Lock disabled'),
_('Screen Locking requires the GNOME display manager.'));
}
}
function _getStylesheet(name) { function _getStylesheet(name) {
let stylesheet; let stylesheet;
@@ -764,7 +815,7 @@ function showRestartMessage(message) {
var AnimationsSettings = class { var AnimationsSettings = class {
constructor() { constructor() {
let backend = Meta.get_backend(); let backend = global.backend;
if (!backend.is_rendering_hardware_accelerated()) { if (!backend.is_rendering_hardware_accelerated()) {
St.Settings.get().inhibit_animations(); St.Settings.get().inhibit_animations();
return; return;

View File

@@ -530,7 +530,7 @@ var Message = GObject.registerClass({
this.close(); this.close();
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
} }
return Clutter.EVENT_PROPAGATE; return super.vfunc_key_press_event(keyEvent);
} }
}); });

View File

@@ -136,29 +136,22 @@ var FocusGrabber = class FocusGrabber {
var NotificationPolicy = GObject.registerClass({ var NotificationPolicy = GObject.registerClass({
Properties: { Properties: {
'enable': GObject.ParamSpec.boolean( 'enable': GObject.ParamSpec.boolean(
'enable', 'enable', 'enable', 'enable', 'enable', 'enable', GObject.ParamFlags.READABLE, true),
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
true),
'enable-sound': GObject.ParamSpec.boolean( 'enable-sound': GObject.ParamSpec.boolean(
'enable-sound', 'enable-sound', 'enable-sound', 'enable-sound', 'enable-sound', 'enable-sound',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READABLE, true),
true),
'show-banners': GObject.ParamSpec.boolean( 'show-banners': GObject.ParamSpec.boolean(
'show-banners', 'show-banners', 'show-banners', 'show-banners', 'show-banners', 'show-banners',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READABLE, true),
true),
'force-expanded': GObject.ParamSpec.boolean( 'force-expanded': GObject.ParamSpec.boolean(
'force-expanded', 'force-expanded', 'force-expanded', 'force-expanded', 'force-expanded', 'force-expanded',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READABLE, false),
false),
'show-in-lock-screen': GObject.ParamSpec.boolean( 'show-in-lock-screen': GObject.ParamSpec.boolean(
'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READABLE, false),
false),
'details-in-lock-screen': GObject.ParamSpec.boolean( 'details-in-lock-screen': GObject.ParamSpec.boolean(
'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READABLE, false),
false),
}, },
}, class NotificationPolicy extends GObject.Object { }, class NotificationPolicy extends GObject.Object {
// Do nothing for the default policy. These methods are only useful for the // Do nothing for the default policy. These methods are only useful for the
@@ -170,23 +163,23 @@ var NotificationPolicy = GObject.registerClass({
} }
get enableSound() { get enableSound() {
return this.enable_sound; return true;
} }
get showBanners() { get showBanners() {
return this.show_banners; return true;
} }
get forceExpanded() { get forceExpanded() {
return this.force_expanded; return false;
} }
get showInLockScreen() { get showInLockScreen() {
return this.show_in_lock_screen; return false;
} }
get detailsInLockScreen() { get detailsInLockScreen() {
return this.details_in_lock_screen; return false;
} }
}); });
@@ -762,12 +755,10 @@ var Source = GObject.registerClass({
this.notifications = []; this.notifications = [];
this._policy = null; this._policy = this._createPolicy();
} }
get policy() { get policy() {
if (!this._policy)
this._policy = this._createPolicy();
return this._policy; return this._policy;
} }
@@ -880,8 +871,6 @@ var Source = GObject.registerClass({
} }
destroy(reason) { destroy(reason) {
this.policy.destroy();
let notifications = this.notifications; let notifications = this.notifications;
this.notifications = []; this.notifications = [];
@@ -890,6 +879,7 @@ var Source = GObject.registerClass({
this.emit('destroy', reason); this.emit('destroy', reason);
this.policy.destroy();
this.run_dispose(); this.run_dispose();
} }

View File

@@ -416,11 +416,11 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
var FdoNotificationDaemonSource = GObject.registerClass( var FdoNotificationDaemonSource = GObject.registerClass(
class FdoNotificationDaemonSource extends MessageTray.Source { class FdoNotificationDaemonSource extends MessageTray.Source {
_init(title, pid, sender, appId) { _init(title, pid, sender, appId) {
super._init(title);
this.pid = pid; this.pid = pid;
this.app = this._getApp(appId); this.app = this._getApp(appId);
super._init(title);
this.initialTitle = title; this.initialTitle = title;
if (this.app) if (this.app)
@@ -631,12 +631,12 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
if (!app) if (!app)
throw new InvalidAppError(); throw new InvalidAppError();
super._init(app.get_name());
this._appId = appId; this._appId = appId;
this._app = app; this._app = app;
this._objectPath = objectPath; this._objectPath = objectPath;
super._init(app.get_name());
this._notifications = {}; this._notifications = {};
this._notificationPending = false; this._notificationPending = false;
} }

View File

@@ -89,7 +89,7 @@ var PadChooser = GObject.registerClass({
}); });
var KeybindingEntry = GObject.registerClass({ var KeybindingEntry = GObject.registerClass({
Signals: { 'keybinding-edited': {} }, Signals: { 'keybinding-edited': { param_types: [GObject.TYPE_STRING] } },
}, class KeybindingEntry extends St.Entry { }, class KeybindingEntry extends St.Entry {
_init() { _init() {
super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' }); super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' });

View File

@@ -90,18 +90,16 @@ class AppMenu extends PopupMenu.PopupMenu {
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._detailsItem = this.addAction(_("Show Details"), () => { this._detailsItem = this.addAction(_('Show Details'), async () => {
let id = this._app.get_id(); let id = this._app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']); let args = GLib.Variant.new('(ss)', [id, '']);
Gio.DBus.get(Gio.BusType.SESSION, null, (o, res) => { const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
let bus = Gio.DBus.get_finish(res); bus.call(
bus.call('org.gnome.Software', 'org.gnome.Software',
'/org/gnome/Software', '/org/gnome/Software',
'org.gtk.Actions', 'Activate', 'org.gtk.Actions', 'Activate',
GLib.Variant.new('(sava{sv})', new GLib.Variant('(sava{sv})', ['details', [args], null]),
['details', [args], null]), null, 0, -1, null);
null, 0, -1, null, null);
});
}); });
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@@ -283,7 +281,7 @@ var AppMenuButton = GObject.registerClass({
this.remove_all_transitions(); this.remove_all_transitions();
this.ease({ this.ease({
opacity: 0, opacity: 0,
mode: Clutter.Animation.EASE_OUT_QUAD, mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: Overview.ANIMATION_TIME, duration: Overview.ANIMATION_TIME,
onComplete: () => this.hide(), onComplete: () => this.hide(),
}); });
@@ -964,7 +962,7 @@ class Panel extends St.Widget {
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
} }
return Clutter.EVENT_PROPAGATE; return super.vfunc_key_press_event(keyEvent);
} }
_toggleMenu(indicator) { _toggleMenu(indicator) {

View File

@@ -204,7 +204,7 @@ var RemoteSearchProvider = class {
g_interface_info: proxyInfo, g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name, g_interface_name: proxyInfo.name,
gFlags }); gFlags });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null); this.proxy.init_async(GLib.PRIORITY_DEFAULT, null);
this.appInfo = appInfo; this.appInfo = appInfo;
this.id = appInfo.get_id(); this.id = appInfo.get_id();
@@ -247,7 +247,7 @@ var RemoteSearchProvider = class {
if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return; return;
log('Received error from DBus search provider %s: %s'.format(this.id, String(error))); log('Received error from D-Bus search provider %s: %s'.format(this.id, String(error)));
callback([]); callback([]);
return; return;
} }
@@ -274,7 +274,7 @@ var RemoteSearchProvider = class {
_getResultMetasFinished(results, error, callback) { _getResultMetasFinished(results, error, callback) {
if (error) { if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error))); log('Received error from D-Bus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
callback([]); callback([]);
return; return;
} }

View File

@@ -198,7 +198,7 @@ var ScreenShield = class {
let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY); let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY); let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
let inhibit = this._loginSession && this._loginSession.Active && let inhibit = this._loginSession && this._loginSession.Active &&
!this._isActive && lockEnabled && !lockLocked; !this._isActive && lockEnabled && !lockLocked && Main.sessionMode.unlockDialog;
if (inhibit) { if (inhibit) {
this._loginManager.inhibit(_("GNOME needs to lock the screen"), this._loginManager.inhibit(_("GNOME needs to lock the screen"),
inhibitor => { inhibitor => {
@@ -345,7 +345,7 @@ var ScreenShield = class {
this._lockDialogGroup.remove_all_transitions(); this._lockDialogGroup.remove_all_transitions();
if (animate) { if (animate) {
// Tween the lock screen out of screen // Animate the lock screen out of screen
// if velocity is not specified (i.e. we come here from pressing ESC), // if velocity is not specified (i.e. we come here from pressing ESC),
// use the same speed regardless of original position // use the same speed regardless of original position
// if velocity is specified, it's in pixels per milliseconds // if velocity is specified, it's in pixels per milliseconds
@@ -498,6 +498,8 @@ var ScreenShield = class {
if (Main.sessionMode.currentMode == 'unlock-dialog') if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog'); Main.sessionMode.popMode('unlock-dialog');
this.emit('wake-up-screen');
if (this._isGreeter) { if (this._isGreeter) {
// We don't want to "deactivate" any more than // We don't want to "deactivate" any more than
// this. In particular, we don't want to drop // this. In particular, we don't want to drop
@@ -519,6 +521,9 @@ var ScreenShield = class {
this._isModal = false; this._isModal = false;
} }
this._longLightbox.lightOff();
this._shortLightbox.lightOff();
this._lockDialogGroup.ease({ this._lockDialogGroup.ease({
translation_y: -global.screen_height, translation_y: -global.screen_height,
duration: Overview.ANIMATION_TIME, duration: Overview.ANIMATION_TIME,
@@ -533,8 +538,6 @@ var ScreenShield = class {
this._dialog = null; this._dialog = null;
} }
this._longLightbox.lightOff();
this._shortLightbox.lightOff();
this.actor.hide(); this.actor.hide();
if (this._becameActiveId != 0) { if (this._becameActiveId != 0) {
@@ -558,7 +561,8 @@ var ScreenShield = class {
if (this._activationTime == 0) if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time(); this._activationTime = GLib.get_monotonic_time();
this._ensureUnlockDialog(true); if (!this._ensureUnlockDialog(true))
return;
this.actor.show(); this.actor.show();

View File

@@ -7,6 +7,13 @@ const GrabHelper = imports.ui.grabHelper;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
Gio._promisify(Shell.Screenshot.prototype, 'pick_color', 'pick_color_finish');
Gio._promisify(Shell.Screenshot.prototype, 'screenshot', 'screenshot_finish');
Gio._promisify(Shell.Screenshot.prototype,
'screenshot_window', 'screenshot_window_finish');
Gio._promisify(Shell.Screenshot.prototype,
'screenshot_area', 'screenshot_area_finish');
const { loadInterfaceXML } = imports.misc.fileUtils; const { loadInterfaceXML } = imports.misc.fileUtils;
const ScreenshotIface = loadInterfaceXML('org.gnome.Shell.Screenshot'); const ScreenshotIface = loadInterfaceXML('org.gnome.Shell.Screenshot');
@@ -84,7 +91,7 @@ var ScreenshotService = class {
} }
} }
_createStream(filename) { _createStream(filename, invocation) {
if (filename == '') if (filename == '')
return [Gio.MemoryOutputStream.new_resizable(), null]; return [Gio.MemoryOutputStream.new_resizable(), null];
@@ -94,6 +101,7 @@ var ScreenshotService = class {
let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
return [stream, file]; return [stream, file];
} catch (e) { } catch (e) {
invocation.return_value(GLib.Variant.new('(bs)', [false, '']));
return [null, null]; return [null, null];
} }
} }
@@ -104,23 +112,22 @@ var ScreenshotService = class {
return [stream, file]; return [stream, file];
} catch (e) { } catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
return [null, null]; break;
} }
} }
invocation.return_value(GLib.Variant.new('(bs)', [false, '']));
return [null, null]; return [null, null];
} }
_onScreenshotComplete(result, area, stream, file, flash, invocation) { _onScreenshotComplete(area, stream, file, flash, invocation) {
if (result) { if (flash) {
if (flash) { let flashspot = new Flashspot(area);
let flashspot = new Flashspot(area); flashspot.fire(() => {
flashspot.fire(() => {
this._removeShooterForSender(invocation.get_sender());
});
} else {
this._removeShooterForSender(invocation.get_sender()); this._removeShooterForSender(invocation.get_sender());
} });
} else {
this._removeShooterForSender(invocation.get_sender());
} }
stream.close(null); stream.close(null);
@@ -134,7 +141,7 @@ var ScreenshotService = class {
clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes); clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
} }
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]); let retval = GLib.Variant.new('(bs)', [true, filenameUsed]);
invocation.return_value(retval); invocation.return_value(retval);
} }
@@ -156,7 +163,7 @@ var ScreenshotService = class {
return [x, y, width, height]; return [x, y, width, height];
} }
ScreenshotAreaAsync(params, invocation) { async ScreenshotAreaAsync(params, invocation) {
let [x, y, width, height, flash, filename] = params; let [x, y, width, height, flash, filename] = params;
[x, y, width, height] = this._scaleArea(x, y, width, height); [x, y, width, height] = this._scaleArea(x, y, width, height);
if (!this._checkArea(x, y, width, height)) { if (!this._checkArea(x, y, width, height)) {
@@ -169,61 +176,57 @@ var ScreenshotService = class {
if (!screenshot) if (!screenshot)
return; return;
let [stream, file] = this._createStream(filename); let [stream, file] = this._createStream(filename, invocation);
if (!stream)
return;
screenshot.screenshot_area(x, y, width, height, stream, try {
(o, res) => { let [area] =
try { await screenshot.screenshot_area(x, y, width, height, stream);
let [result, area] = this._onScreenshotComplete(area, stream, file, flash, invocation);
screenshot.screenshot_area_finish(res); } catch (e) {
this._onScreenshotComplete( this._removeShooterForSender(invocation.get_sender());
result, area, stream, file, flash, invocation); invocation.return_value(new GLib.Variant('(bs)', [false, '']));
} catch (e) { }
invocation.return_gerror(e);
}
});
} }
ScreenshotWindowAsync(params, invocation) { async ScreenshotWindowAsync(params, invocation) {
let [includeFrame, includeCursor, flash, filename] = params; let [includeFrame, includeCursor, flash, filename] = params;
let screenshot = this._createScreenshot(invocation); let screenshot = this._createScreenshot(invocation);
if (!screenshot) if (!screenshot)
return; return;
let [stream, file] = this._createStream(filename); let [stream, file] = this._createStream(filename, invocation);
if (!stream)
return;
screenshot.screenshot_window(includeFrame, includeCursor, stream, try {
(o, res) => { let [area] =
try { await screenshot.screenshot_window(includeFrame, includeCursor, stream);
let [result, area] = this._onScreenshotComplete(area, stream, file, flash, invocation);
screenshot.screenshot_window_finish(res); } catch (e) {
this._onScreenshotComplete( this._removeShooterForSender(invocation.get_sender());
result, area, stream, file, flash, invocation); invocation.return_value(new GLib.Variant('(bs)', [false, '']));
} catch (e) { }
invocation.return_gerror(e);
}
});
} }
ScreenshotAsync(params, invocation) { async ScreenshotAsync(params, invocation) {
let [includeCursor, flash, filename] = params; let [includeCursor, flash, filename] = params;
let screenshot = this._createScreenshot(invocation); let screenshot = this._createScreenshot(invocation);
if (!screenshot) if (!screenshot)
return; return;
let [stream, file] = this._createStream(filename); let [stream, file] = this._createStream(filename, invocation);
if (!stream)
return;
screenshot.screenshot(includeCursor, stream, try {
(o, res) => { let [area] = await screenshot.screenshot(includeCursor, stream);
try { this._onScreenshotComplete(area, stream, file, flash, invocation);
let [result, area] = } catch (e) {
screenshot.screenshot_finish(res); this._removeShooterForSender(invocation.get_sender());
this._onScreenshotComplete( invocation.return_value(new GLib.Variant('(bs)', [false, '']));
result, area, stream, file, flash, invocation); }
} catch (e) {
invocation.return_gerror(e);
}
});
} }
async SelectAreaAsync(params, invocation) { async SelectAreaAsync(params, invocation) {
@@ -264,19 +267,17 @@ var ScreenshotService = class {
if (!screenshot) if (!screenshot)
return; return;
screenshot.pick_color(coords.x, coords.y, (_o, res) => { const [color] = await screenshot.pick_color(coords.x, coords.y);
let [success_, color] = screenshot.pick_color_finish(res); const { red, green, blue } = color;
let { red, green, blue } = color; const retval = GLib.Variant.new('(a{sv})', [{
let retval = GLib.Variant.new('(a{sv})', [{ color: GLib.Variant.new('(ddd)', [
color: GLib.Variant.new('(ddd)', [ red / 255.0,
red / 255.0, green / 255.0,
green / 255.0, blue / 255.0,
blue / 255.0, ]),
]), }]);
}]); this._removeShooterForSender(invocation.get_sender());
this._removeShooterForSender(invocation.get_sender()); invocation.return_value(retval);
invocation.return_value(retval);
});
} catch (e) { } catch (e) {
invocation.return_error_literal( invocation.return_error_literal(
Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,

View File

@@ -6,6 +6,7 @@ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const AppDisplay = imports.ui.appDisplay; const AppDisplay = imports.ui.appDisplay;
const IconGrid = imports.ui.iconGrid; const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main; const Main = imports.ui.main;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const RemoteSearch = imports.ui.remoteSearch; const RemoteSearch = imports.ui.remoteSearch;
const Util = imports.misc.util; const Util = imports.misc.util;
@@ -431,6 +432,9 @@ var SearchResultsView = GObject.registerClass({
_init() { _init() {
super._init({ name: 'searchResults', vertical: true }); super._init({ name: 'searchResults', vertical: true });
this._parentalControlsManager = ParentalControlsManager.getDefault();
this._parentalControlsManager.connect('app-filter-changed', this._reloadRemoteProviders.bind(this));
this._content = new MaxWidthBox({ this._content = new MaxWidthBox({
name: 'searchResultsContent', name: 'searchResultsContent',
vertical: true, vertical: true,
@@ -505,6 +509,11 @@ var SearchResultsView = GObject.registerClass({
_registerProvider(provider) { _registerProvider(provider) {
provider.searchInProgress = false; provider.searchInProgress = false;
// Filter out unwanted providers.
if (provider.appInfo && !this._parentalControlsManager.shouldShowApp(provider.appInfo))
return;
this._providers.push(provider); this._providers.push(provider);
this._ensureProviderDisplay(provider); this._ensureProviderDisplay(provider);
} }

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported GnomeShell, ScreenSaverDBus */ /* exported GnomeShell, ScreenSaverDBus */
const { Gio, GLib, Meta, Shell } = imports.gi; const { Gio, GLib, Meta } = imports.gi;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionDownloader = imports.ui.extensionDownloader; const ExtensionDownloader = imports.ui.extensionDownloader;
@@ -315,21 +315,15 @@ var GnomeShellExtensions = class {
this.OpenExtensionPrefs(uuid, '', {}); this.OpenExtensionPrefs(uuid, '', {});
} }
OpenExtensionPrefs(uuid, _parentWindow, _options) { OpenExtensionPrefs(uuid, parentWindow, options) {
let appSys = Shell.AppSystem.get_default(); Main.extensionManager.openExtensionPrefs(uuid, parentWindow, options);
let app = appSys.lookup_app('org.gnome.Extensions.desktop');
let info = app.get_app_info();
let timestamp = global.display.get_current_time_roundtrip();
info.launch_uris([`extension:///${uuid}`],
global.create_app_launch_context(timestamp, -1));
} }
ReloadExtension(uuid) { ReloadExtensionAsync(params, invocation) {
let extension = Main.extensionManager.lookup(uuid); invocation.return_error_literal(
if (!extension) Gio.DBusError,
return; Gio.DBusError.NOT_SUPPORTED,
'ReloadExtension is deprecated and does not work');
Main.extensionManager.reloadExtension(extension);
} }
CheckForUpdates() { CheckForUpdates() {

View File

@@ -447,12 +447,8 @@ var ShellMountPasswordDialog = GObject.registerClass({
let useKeyfiles = this._keyfilesCheckbox.checked; let useKeyfiles = this._keyfilesCheckbox.checked;
this._passwordEntry.reactive = !useKeyfiles; this._passwordEntry.reactive = !useKeyfiles;
this._passwordEntry.can_focus = !useKeyfiles; this._passwordEntry.can_focus = !useKeyfiles;
this._passwordEntry.clutter_text.editable = !useKeyfiles;
this._passwordEntry.clutter_text.selectable = !useKeyfiles;
this._pimEntry.reactive = !useKeyfiles; this._pimEntry.reactive = !useKeyfiles;
this._pimEntry.can_focus = !useKeyfiles; this._pimEntry.can_focus = !useKeyfiles;
this._pimEntry.clutter_text.editable = !useKeyfiles;
this._pimEntry.clutter_text.selectable = !useKeyfiles;
this._rememberChoice.reactive = !useKeyfiles; this._rememberChoice.reactive = !useKeyfiles;
this._rememberChoice.can_focus = !useKeyfiles; this._rememberChoice.can_focus = !useKeyfiles;
this._keyfilesLabel.visible = useKeyfiles; this._keyfilesLabel.visible = useKeyfiles;

View File

@@ -186,7 +186,7 @@ var Slider = GObject.registerClass({
this.value = Math.max(0, Math.min(this._value + delta, this._maxValue)); this.value = Math.max(0, Math.min(this._value + delta, this._maxValue));
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
} }
return Clutter.EVENT_PROPAGATE; return super.vfunc_key_press_event(keyPressEvent);
} }
_moveHandle(absX, _absY) { _moveHandle(absX, _absY) {

View File

@@ -139,14 +139,14 @@ class ATIndicator extends PanelMenu.Button {
interfaceSettings.is_writable(KEY_ICON_THEME), interfaceSettings.is_writable(KEY_ICON_THEME),
enabled => { enabled => {
if (enabled) { if (enabled) {
interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
interfaceSettings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME); interfaceSettings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
} else if (!hasHC) { } else if (!hasHC) {
interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme);
interfaceSettings.set_string(KEY_ICON_THEME, iconTheme); interfaceSettings.set_string(KEY_ICON_THEME, iconTheme);
interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme);
} else { } else {
interfaceSettings.reset(KEY_GTK_THEME);
interfaceSettings.reset(KEY_ICON_THEME); interfaceSettings.reset(KEY_ICON_THEME);
interfaceSettings.reset(KEY_GTK_THEME);
} }
}); });

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */ /* exported Indicator */
const { Gio, GnomeBluetooth, GObject } = imports.gi; const { Gio, GLib, GnomeBluetooth, GObject } = imports.gi;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
@@ -35,7 +35,7 @@ class Indicator extends PanelMenu.SystemIndicator {
this._sync(); this._sync();
}); });
this._proxy.connect('g-properties-changed', this._sync.bind(this)); this._proxy.connect('g-properties-changed', this._queueSync.bind(this));
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic'; this._item.icon.icon_name = 'bluetooth-active-symbolic';
@@ -49,15 +49,27 @@ class Indicator extends PanelMenu.SystemIndicator {
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item); this.menu.addMenuItem(this._item);
this._syncId = 0;
this._adapter = null;
this._client = new GnomeBluetooth.Client(); this._client = new GnomeBluetooth.Client();
this._model = this._client.get_model(); this._model = this._client.get_model();
this._model.connect('row-changed', this._sync.bind(this)); this._model.connect('row-deleted', this._queueSync.bind(this));
this._model.connect('row-deleted', this._sync.bind(this)); this._model.connect('row-changed', this._queueSync.bind(this));
this._model.connect('row-inserted', this._sync.bind(this)); this._model.connect('row-inserted', this._sync.bind(this));
Main.sessionMode.connect('updated', this._sync.bind(this)); Main.sessionMode.connect('updated', this._sync.bind(this));
this._sync(); this._sync();
} }
_setHadSetupDevices(value) {
if (this._hadSetupDevices === value)
return;
this._hadSetupDevices = value;
global.settings.set_boolean(
HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
}
_getDefaultAdapter() { _getDefaultAdapter() {
let [ret, iter] = this._model.get_iter_first(); let [ret, iter] = this._model.get_iter_first();
while (ret) { while (ret) {
@@ -72,46 +84,53 @@ class Indicator extends PanelMenu.SystemIndicator {
return null; return null;
} }
// nDevices is the number of devices setup for the current default _getDeviceInfos(adapter) {
// adapter if one exists and is powered. If unpowered or unavailable,
// nDevice is "1" if it had setup devices associated to it the last
// time it was seen, and "-1" if not.
//
// nConnectedDevices is the number of devices connected to the default
// adapter if one exists and is powered, or -1 if it's not available.
_getNDevices() {
let adapter = this._getDefaultAdapter();
if (!adapter) if (!adapter)
return [this._hadSetupDevices ? 1 : -1, -1]; return [];
let nConnectedDevices = 0; let deviceInfos = [];
let nDevices = 0;
let [ret, iter] = this._model.iter_children(adapter); let [ret, iter] = this._model.iter_children(adapter);
while (ret) { while (ret) {
let isConnected = this._model.get_value(iter, const isPaired = this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED); GnomeBluetooth.Column.PAIRED);
if (isConnected) const isTrusted = this._model.get_value(iter,
nConnectedDevices++; GnomeBluetooth.Column.TRUSTED);
if (isPaired || isTrusted) {
deviceInfos.push({
connected: this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED),
name: this._model.get_value(iter,
GnomeBluetooth.Column.ALIAS),
});
}
let isPaired = this._model.get_value(iter,
GnomeBluetooth.Column.PAIRED);
let isTrusted = this._model.get_value(iter,
GnomeBluetooth.Column.TRUSTED);
if (isPaired || isTrusted)
nDevices++;
ret = this._model.iter_next(iter); ret = this._model.iter_next(iter);
} }
if (this._hadSetupDevices != (nDevices > 0)) { return deviceInfos;
this._hadSetupDevices = !this._hadSetupDevices; }
global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
}
return [nDevices, nConnectedDevices]; _queueSync() {
if (this._syncId)
return;
this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._syncId = 0;
this._sync();
return GLib.SOURCE_REMOVE;
});
} }
_sync() { _sync() {
let [nDevices, nConnectedDevices] = this._getNDevices(); let adapter = this._getDefaultAdapter();
let devices = this._getDeviceInfos(adapter);
const connectedDevices = devices.filter(dev => dev.connected);
const nConnectedDevices = connectedDevices.length;
if (adapter && this._adapter)
this._setHadSetupDevices(devices.length > 0);
this._adapter = adapter;
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive); this.menu.setSensitive(sensitive);
@@ -119,19 +138,21 @@ class Indicator extends PanelMenu.SystemIndicator {
// Remember if there were setup devices and show the menu // Remember if there were setup devices and show the menu
// if we've seen setup devices and we're not hard blocked // if we've seen setup devices and we're not hard blocked
if (nDevices > 0) if (this._hadSetupDevices)
this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode; this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode;
else else
this._item.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; this._item.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
if (nConnectedDevices > 0) if (nConnectedDevices > 1)
/* Translators: this is the number of connected bluetooth devices */ /* Translators: this is the number of connected bluetooth devices */
this._item.label.text = ngettext("%d Connected", "%d Connected", nConnectedDevices).format(nConnectedDevices); this._item.label.text = ngettext('%d Connected", "%d Connected', nConnectedDevices).format(nConnectedDevices);
else if (nConnectedDevices == -1) else if (nConnectedDevices === 1)
this._item.label.text = _("Off"); this._item.label.text = connectedDevices[0].name;
else if (adapter === null)
this._item.label.text = _('Bluetooth Off');
else else
this._item.label.text = _("On"); this._item.label.text = _('Bluetooth On');
this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off"); this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _('Turn On') : _('Turn Off');
} }
}); });

View File

@@ -199,36 +199,36 @@ var InputSourceSystemSettings = class extends InputSourceSettings {
this._reload.bind(this)); this._reload.bind(this));
} }
_reload() { async _reload() {
Gio.DBus.system.call(this._BUS_NAME, let props;
this._BUS_PATH, try {
this._BUS_PROPS_IFACE, const result = await Gio.DBus.system.call(
'GetAll', this._BUS_NAME,
new GLib.Variant('(s)', [this._BUS_IFACE]), this._BUS_PATH,
null, Gio.DBusCallFlags.NONE, -1, null, this._BUS_PROPS_IFACE,
(conn, result) => { 'GetAll',
let props; new GLib.Variant('(s)', [this._BUS_IFACE]),
try { null, Gio.DBusCallFlags.NONE, -1, null);
props = conn.call_finish(result).deep_unpack()[0]; [props] = result.deep_unpack();
} catch (e) { } catch (e) {
log('Could not get properties from %s'.format(this._BUS_NAME)); log('Could not get properties from %s'.format(this._BUS_NAME));
return; return;
} }
let layouts = props['X11Layout'].unpack();
let variants = props['X11Variant'].unpack();
let options = props['X11Options'].unpack();
if (layouts != this._layouts || const layouts = props['X11Layout'].unpack();
variants != this._variants) { const variants = props['X11Variant'].unpack();
this._layouts = layouts; const options = props['X11Options'].unpack();
this._variants = variants;
this._emitInputSourcesChanged(); if (layouts !== this._layouts ||
} variants !== this._variants) {
if (options != this._options) { this._layouts = layouts;
this._options = options; this._variants = variants;
this._emitKeyboardOptionsChanged(); this._emitInputSourcesChanged();
} }
}); if (options !== this._options) {
this._options = options;
this._emitKeyboardOptionsChanged();
}
} }
get inputSources() { get inputSources() {

View File

@@ -15,6 +15,10 @@ const Util = imports.misc.util;
const { loadInterfaceXML } = imports.misc.fileUtils; const { loadInterfaceXML } = imports.misc.fileUtils;
Gio._promisify(NM.Client, 'new_async', 'new_finish');
Gio._promisify(NM.Client.prototype,
'check_connectivity_async', 'check_connectivity_finish');
const NMConnectionCategory = { const NMConnectionCategory = {
INVALID: 'invalid', INVALID: 'invalid',
WIRED: 'wired', WIRED: 'wired',
@@ -1627,11 +1631,11 @@ class Indicator extends PanelMenu.SystemIndicator {
this._ctypes[NM.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NM.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NM.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN; this._ctypes[NM.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
NM.Client.new_async(null, this._clientGot.bind(this)); this._getClient();
} }
_clientGot(obj, result) { async _getClient() {
this._client = NM.Client.new_finish(result); this._client = await NM.Client.new_async(null);
this._activeConnections = []; this._activeConnections = [];
this._connections = []; this._connections = [];
@@ -1982,7 +1986,7 @@ class Indicator extends PanelMenu.SystemIndicator {
} }
} }
_portalHelperDone(proxy, emitter, parameters) { async _portalHelperDone(proxy, emitter, parameters) {
let [path, result] = parameters; let [path, result] = parameters;
if (result == PortalHelperResult.CANCELLED) { if (result == PortalHelperResult.CANCELLED) {
@@ -1993,13 +1997,11 @@ class Indicator extends PanelMenu.SystemIndicator {
} else if (result == PortalHelperResult.COMPLETED) { } else if (result == PortalHelperResult.COMPLETED) {
this._closeConnectivityCheck(path); this._closeConnectivityCheck(path);
} else if (result == PortalHelperResult.RECHECK) { } else if (result == PortalHelperResult.RECHECK) {
this._client.check_connectivity_async(null, (client, res) => { try {
try { const state = await this._client.check_connectivity_async(null);
let state = client.check_connectivity_finish(res); if (state >= NM.ConnectivityState.FULL)
if (state >= NM.ConnectivityState.FULL) this._closeConnectivityCheck(path);
this._closeConnectivityCheck(path); } catch (e) { }
} catch (e) { }
});
} else { } else {
log('Invalid result from portal helper: %s'.format(result)); log('Invalid result from portal helper: %s'.format(result));
} }

View File

@@ -11,8 +11,7 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
_init() { _init() {
super._init(); super._init();
let backend = Meta.get_backend(); let controller = global.backend.get_remote_access_controller();
let controller = backend.get_remote_access_controller();
if (!controller) if (!controller)
return; return;

View File

@@ -52,22 +52,21 @@ const BOLT_DBUS_PATH = '/org/freedesktop/bolt';
var Client = class { var Client = class {
constructor() { constructor() {
this._proxy = null; this._proxy = null;
let nodeInfo = Gio.DBusNodeInfo.new_for_xml(BoltClientInterface);
Gio.DBusProxy.new(Gio.DBus.system,
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
nodeInfo.lookup_interface(BOLT_DBUS_CLIENT_IFACE),
BOLT_DBUS_NAME,
BOLT_DBUS_PATH,
BOLT_DBUS_CLIENT_IFACE,
null,
this._onProxyReady.bind(this));
this.probing = false; this.probing = false;
this._getProxy();
} }
_onProxyReady(o, res) { async _getProxy() {
let nodeInfo = Gio.DBusNodeInfo.new_for_xml(BoltClientInterface);
try { try {
this._proxy = Gio.DBusProxy.new_finish(res); this._proxy = await Gio.DBusProxy.new(
Gio.DBus.system,
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
nodeInfo.lookup_interface(BOLT_DBUS_CLIENT_IFACE),
BOLT_DBUS_NAME,
BOLT_DBUS_PATH,
BOLT_DBUS_CLIENT_IFACE,
null);
} catch (e) { } catch (e) {
log('error creating bolt proxy: %s'.format(e.message)); log('error creating bolt proxy: %s'.format(e.message));
return; return;
@@ -243,14 +242,15 @@ class Indicator extends PanelMenu.SystemIndicator {
this._source = null; this._source = null;
this._perm = null; this._perm = null;
this._createPermission();
}
Polkit.Permission.new('org.freedesktop.bolt.enroll', null, null, (source, res) => { async _createPermission() {
try { try {
this._perm = Polkit.Permission.new_finish(res); this._perm = await Polkit.Permission.new('org.freedesktop.bolt.enroll', null, null);
} catch (e) { } catch (e) {
log('Failed to get PolKit permission: %s'.format(e.toString())); log('Failed to get PolKit permission: %s'.format(e.toString()));
} }
});
} }
_onDestroy() { _onDestroy() {

View File

@@ -138,11 +138,12 @@ var StreamSlider = class {
} }
_notifyVolumeChange() { _notifyVolumeChange() {
if (this._stream.state === Gvc.MixerStreamState.RUNNING)
return; // feedback not necessary while playing
if (this._volumeCancellable) if (this._volumeCancellable)
this._volumeCancellable.cancel(); this._volumeCancellable.cancel();
this._volumeCancellable = null;
if (this._stream.state === Gvc.MixerStreamState.RUNNING)
return; // feedback not necessary while playing
this._volumeCancellable = new Gio.Cancellable(); this._volumeCancellable = new Gio.Cancellable();
let player = global.display.get_sound_player(); let player = global.display.get_sound_player();

View File

@@ -317,7 +317,7 @@ var SwitcherPopup = GObject.registerClass({
this.ease({ this.ease({
opacity: 0, opacity: 0,
duration: POPUP_FADE_OUT_TIME, duration: POPUP_FADE_OUT_TIME,
mode: Clutter.Animation.EASE_OUT_QUAD, mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.destroy(), onComplete: () => this.destroy(),
}); });
} else { } else {

View File

@@ -1,228 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported init, addCaller, addTween, getTweenCount, removeTweens,
pauseTweens, resumeTweens, registerSpecialProperty,
registerSpecialPropertyModifier, registerSpecialPropertySplitter */
const { Clutter, GLib, Shell } = imports.gi;
const Signals = imports.signals;
const Tweener = imports.tweener.tweener;
const { adjustAnimationTime } = imports.ui.environment;
// This is a wrapper around imports.tweener.tweener that adds a bit of
// Clutter integration. If the tweening target is a Clutter.Actor, then
// the tweenings will automatically be removed if the actor is destroyed.
// ActionScript Tweener methods that imports.tweener.tweener doesn't
// currently implement: getTweens, getVersion, registerTransition,
// setTimeScale, updateTime.
// imports.tweener.tweener methods that we don't re-export:
// pauseAllTweens, removeAllTweens, resumeAllTweens. (It would be hard
// to clean up properly after removeAllTweens, and also, any code that
// calls any of these is almost certainly wrong anyway, because they
// affect the entire application.)
// Called from Main.start
function init() {
Tweener.setFrameTicker(new ClutterFrameTicker());
}
function addCaller(target, tweeningParameters) {
_wrapTweening(target, tweeningParameters);
Tweener.addCaller(target, tweeningParameters);
}
function addTween(target, tweeningParameters) {
_wrapTweening(target, tweeningParameters);
Tweener.addTween(target, tweeningParameters);
}
function _wrapTweening(target, tweeningParameters) {
let state = _getTweenState(target);
if (!state.destroyedId) {
if (target instanceof Clutter.Actor) {
state.actor = target;
state.destroyedId = target.connect('destroy', _actorDestroyed);
} else if (target.actor && target.actor instanceof Clutter.Actor) {
state.actor = target.actor;
state.destroyedId = target.actor.connect('destroy', () => _actorDestroyed(target));
}
}
let { time, delay } = tweeningParameters;
if (!isNaN(time))
tweeningParameters['time'] = adjustAnimationTime(1000 * time) / 1000;
if (!isNaN(delay))
tweeningParameters['delay'] = adjustAnimationTime(1000 * delay) / 1000;
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
}
function _getTweenState(target) {
// If we were paranoid, we could keep a plist mapping targets to
// states... but we're not that paranoid.
if (!target.__ShellTweenerState)
target.__ShellTweenerState = {};
return target.__ShellTweenerState;
}
function _resetTweenState(target) {
let state = target.__ShellTweenerState;
if (state) {
if (state.destroyedId)
state.actor.disconnect(state.destroyedId);
}
target.__ShellTweenerState = {};
}
function _addHandler(target, params, name, handler) {
if (params[name]) {
let oldHandler = params[name];
let oldScope = params[`${name}Scope`];
let oldParams = params[`${name}Params`];
let eventScope = oldScope ? oldScope : target;
params[name] = () => {
oldHandler.apply(eventScope, oldParams);
handler(target);
};
} else {
params[name] = () => handler(target);
}
}
function _actorDestroyed(target) {
_resetTweenState(target);
Tweener.removeTweens(target);
}
function _tweenCompleted(target) {
if (!isTweening(target))
_resetTweenState(target);
}
function getTweenCount(scope) {
return Tweener.getTweenCount(scope);
}
// imports.tweener.tweener doesn't provide this method (which exists
// in the ActionScript version) but it's easy to implement.
function isTweening(scope) {
return Tweener.getTweenCount(scope) != 0;
}
function removeTweens(...args) {
if (Tweener.removeTweens(args)) {
let [scope] = args;
// If we just removed the last active tween, clean up
if (Tweener.getTweenCount(scope) == 0)
_tweenCompleted(scope);
return true;
} else {
return false;
}
}
function pauseTweens(...args) {
return Tweener.pauseTweens(...args);
}
function resumeTweens(...args) {
return Tweener.resumeTweens(...args);
}
function registerSpecialProperty(...args) {
Tweener.registerSpecialProperty(...args);
}
function registerSpecialPropertyModifier(name, modifyFunction, getFunction) {
Tweener.registerSpecialPropertyModifier(name, modifyFunction, getFunction);
}
function registerSpecialPropertySplitter(name, splitFunction, parameters) {
Tweener.registerSpecialPropertySplitter(name, splitFunction, parameters);
}
// The 'FrameTicker' object is an object used to feed new frames to
// Tweener so it can update values and redraw. The default frame
// ticker for Tweener just uses a simple timeout at a fixed frame rate
// and has no idea of "catching up" by dropping frames.
//
// We substitute it with custom frame ticker here that connects
// Tweener to a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a
// whole lot more sophisticated than a simple timeout at a fixed frame
// rate, but at least it knows how to drop frames. (See
// HippoAnimationManager for a more sophisticated view of continuous
// time updates; even better is to pay attention to the vertical
// vblank and sync to that when possible.)
//
var ClutterFrameTicker = class {
constructor() {
// We don't have a finite duration; tweener will tell us to stop
// when we need to stop, so use 1000 seconds as "infinity", and
// set the timeline to loop. Doing this means we have to track
// time ourselves, since clutter timeline's time will cycle
// instead of strictly increase.
this._timeline = new Clutter.Timeline({ duration: 1000 * 1000 });
this._timeline.set_loop(true);
this._startTime = -1;
this._currentTime = -1;
this._timeline.connect('new-frame', () => {
this._onNewFrame();
});
let perfLog = Shell.PerfLog.get_default();
perfLog.define_event("tweener.framePrepareStart",
"Start of a new animation frame",
"");
perfLog.define_event("tweener.framePrepareDone",
"Finished preparing frame",
"");
}
get FRAME_RATE() {
return 60;
}
_onNewFrame() {
// If there is a lot of setup to start the animation, then
// first frame number we get from clutter might be a long ways
// into the animation (or the animation might even be done).
// That looks bad, so we always start at the first frame of the
// animation then only do frame dropping from there.
if (this._startTime < 0)
this._startTime = GLib.get_monotonic_time() / 1000.0;
// currentTime is in milliseconds
let perfLog = Shell.PerfLog.get_default();
this._currentTime = GLib.get_monotonic_time() / 1000.0 - this._startTime;
perfLog.event("tweener.framePrepareStart");
this.emit('prepare-frame');
perfLog.event("tweener.framePrepareDone");
}
getTime() {
return this._currentTime;
}
start() {
this._timeline.start();
global.begin_work();
}
stop() {
this._timeline.stop();
this._startTime = -1;
this._currentTime = -1;
global.end_work();
}
};
Signals.addSignalMethods(ClutterFrameTicker.prototype);

View File

@@ -115,7 +115,7 @@ var NotificationsBox = GObject.registerClass({
box.add_child(textBox); box.add_child(textBox);
let title = new St.Label({ let title = new St.Label({
text: source.title, text: source.title.replace(/\n/g, ' '),
style_class: 'unlock-dialog-notification-label', style_class: 'unlock-dialog-notification-label',
}); });
textBox.add(title); textBox.add(title);
@@ -129,9 +129,10 @@ var NotificationsBox = GObject.registerClass({
let body = ''; let body = '';
if (n.bannerBodyText) { if (n.bannerBodyText) {
const bodyText = n.bannerBodyText.replace(/\n/g, ' ');
body = n.bannerBodyMarkup body = n.bannerBodyMarkup
? n.bannerBodyText ? bodyText
: GLib.markup_escape_text(n.bannerBodyText, -1); : GLib.markup_escape_text(bodyText, -1);
} }
let label = new St.Label({ style_class: 'unlock-dialog-notification-count-text' }); let label = new St.Label({ style_class: 'unlock-dialog-notification-count-text' });
@@ -604,7 +605,7 @@ var UnlockDialog = GObject.registerClass({
this._showPrompt(); this._showPrompt();
if (GLib.unichar_isgraph(unichar)) if (GLib.unichar_isgraph(unichar))
this.addCharacter(unichar); this._authPrompt.addCharacter(unichar);
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
} }
@@ -760,7 +761,7 @@ var UnlockDialog = GObject.registerClass({
} }
_escape() { _escape() {
if (this.allowCancel) if (this._authPrompt && this.allowCancel)
this._authPrompt.cancel(); this._authPrompt.cancel();
} }
@@ -834,11 +835,6 @@ var UnlockDialog = GObject.registerClass({
this._authPrompt.cancel(); this._authPrompt.cancel();
} }
addCharacter(unichar) {
this._showPrompt();
this._authPrompt.addCharacter(unichar);
}
finish(onComplete) { finish(onComplete) {
this._ensureAuthPrompt(); this._ensureAuthPrompt();
this._authPrompt.finish(onComplete); this._authPrompt.finish(onComplete);

View File

@@ -57,11 +57,11 @@ var WindowAttentionHandler = class {
var WindowAttentionSource = GObject.registerClass( var WindowAttentionSource = GObject.registerClass(
class WindowAttentionSource extends MessageTray.Source { class WindowAttentionSource extends MessageTray.Source {
_init(app, window) { _init(app, window) {
super._init(app.get_name());
this._window = window; this._window = window;
this._app = app; this._app = app;
super._init(app.get_name());
this.signalIDs = []; this.signalIDs = [];
this.signalIDs.push(this._window.connect('notify::demands-attention', this.signalIDs.push(this._window.connect('notify::demands-attention',
this._sync.bind(this))); this._sync.bind(this)));

View File

@@ -82,8 +82,10 @@ class DisplayChangeDialog extends ModalDialog.ModalDialog {
} }
_formatCountDown() { _formatCountDown() {
let fmt = ngettext("Settings changes will revert in %d second", const fmt = ngettext(
"Settings changes will revert in %d seconds"); 'Settings changes will revert in %d second',
'Settings changes will revert in %d seconds',
this._countDown);
return fmt.format(this._countDown); return fmt.format(this._countDown);
} }
@@ -924,6 +926,8 @@ var WindowManager = class {
return true; return true;
}); });
global.display.connect('x11-display-closing', () => { global.display.connect('x11-display-closing', () => {
if (!Meta.is_wayland_compositor())
return;
Shell.util_stop_systemd_unit('gsd-xsettings.target', 'fail'); Shell.util_stop_systemd_unit('gsd-xsettings.target', 'fail');
IBusManager.getIBusManager().restartDaemon(); IBusManager.getIBusManager().restartDaemon();
}); });

View File

@@ -404,7 +404,7 @@ var WindowClone = GObject.registerClass({
return true; return true;
} }
return false; return super.vfunc_key_press_event(keyEvent);
} }
_onClicked() { _onClicked() {

View File

@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { Clutter, Meta } = imports.gi; const { Clutter } = imports.gi;
const Signals = imports.signals; const Signals = imports.signals;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
@@ -17,7 +17,7 @@ var XdndHandler = class {
Main.uiGroup.add_actor(this._dummy); Main.uiGroup.add_actor(this._dummy);
this._dummy.hide(); this._dummy.hide();
var dnd = Meta.get_backend().get_dnd(); var dnd = global.backend.get_dnd();
dnd.connect('dnd-enter', this._onEnter.bind(this)); dnd.connect('dnd-enter', this._onEnter.bind(this));
dnd.connect('dnd-position-change', this._onPositionChanged.bind(this)); dnd.connect('dnd-position-change', this._onPositionChanged.bind(this));
dnd.connect('dnd-leave', this._onLeave.bind(this)); dnd.connect('dnd-leave', this._onLeave.bind(this));

View File

@@ -218,12 +218,12 @@ globals:
ARGV: readonly ARGV: readonly
Debugger: readonly Debugger: readonly
GIRepositoryGType: readonly GIRepositoryGType: readonly
globalThis: readonly
imports: readonly imports: readonly
Intl: readonly Intl: readonly
log: readonly log: readonly
logError: readonly logError: readonly
print: readonly print: readonly
printerr: readonly printerr: readonly
window: readonly
parserOptions: parserOptions:
ecmaVersion: 2017 ecmaVersion: 2019

View File

@@ -17,7 +17,6 @@ rules:
overrides: overrides:
- files: js/** - files: js/**
excludedFiles: excludedFiles:
- js/extensionPrefs/*
- js/portalHelper/* - js/portalHelper/*
globals: globals:
global: readonly global: readonly
@@ -25,3 +24,8 @@ overrides:
C_: readonly C_: readonly
N_: readonly N_: readonly
ngettext: readonly ngettext: readonly
- files: subprojects/extensions-app/js/**
globals:
_: readonly
C_: readonly
N_: readonly

Some files were not shown because too many files have changed in this diff Show More