Compare commits

..

190 Commits

Author SHA1 Message Date
5d1c241f53 Bump version to 3.36.5
Update NEWS.
2020-08-11 22:26:17 +02:00
cb9bb62a21 st/private: Multiply position in fb coordinates with resource scale
The framebuffer we use for rendering shadows is scaled by the resource
scale, that means we also need to offset coordinates when translating
them to the framebuffers coordinate system.

So far we forgot to do that when translating the framebuffer using the
position of the actor, which lead to small rendering bugs of
text-shadows for actors allocated at non-zero origins. To fix that,
simply multiply those positions with the actors resource scale.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1390
2020-08-11 21:53:57 +02:00
bde127b893 rfkill: Always sync state on construction
This fixes an issue where the indicator can be out of sync until the
RfkillManager (used by it) properties change.

The problem is that multiple instances of the indicator will use
the same RfkillManager instance (getRfkillManager() returns a singleton)
that only guarantees to emit the changed signal in two scenarios:
when the D-Bus proxy connects and when the proxy properties change.

If by the time an indicator is instantiated the RfkillManager's D-Bus
proxy is already connected, that indicator would only sync its state
when the RfkillManager properties change.

Let's fix that by always syncing the state on construction - in the worst
case scenario the RfkillManager's D-Bus proxy won't have connected yet
and the indicator state will be temporarily out of sync but once it gets
connected the indicator will sync again with the correct state.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1386
2020-08-11 21:53:20 +02:00
51537cf31c panelMenu: Destroy menu before chaining up
This avoid some (harmless but annoying) warnings, and is closer to
the original code prior to commit fc342fe8c5 and 557b232c89.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3022
2020-08-11 21:52:38 +02:00
a3d2cfa503 popupMenu: Ungrab when removing active menu
While we do have some handling for removing the active menu, it has
been a no-op for years. The bit that we really care about from the
PopupMenuManager's point of view is the existing grab though. Drop
that instead of calling _closeMenu() directly; ungrabbing will still
call the method indirectly, and it will still be a no-op :-)

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3022
2020-08-11 21:52:38 +02:00
f36e4b6ed5 layout: Only show ripple animation when overview was toggled
On X11, clients can grab keyboard on pointer (for example for popup
menus), and as a result the pushModal() call when opening the overview
fails.

However when the hot corner was used to toggle the overview, we still
show the ripple animation in that case, which is confusing as the action
did not actually happen.

Fix this by only showing the ripples if the overview is animating after
calling toggle(), as that should be a reliable indication of whether
the call was successful.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3005
2020-08-11 21:51:59 +02:00
40a003e5ac keyboard: Fix missing icons in Keypad
This is more fallout from commit 57669bca1b.

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


(cherry picked from commit 6b78f58a75)
2020-08-09 14:06:31 +00:00
5fa6996210 appDisplay: Schedule relayout after adaptToSize on app folder icon grid
AppFolderDialog was calling adaptToSize from its alloc vfunc, which
changed the spacing of the icon grid after its size used to calculate
the adjustment for scrolling had already been determined. This was
resulting in the app folder not being able to scroll all the way to the
end the first time it has been opened.

Fix this by scheduling a relayout. This however can not be done
immediately after the adaptToSize call on the iconGrid, because this is
called from within an alloc vfunc. So instead use Meta.later_add to
ensure it gets called after the alloc, but before the next redraw.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2535
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1378
2020-08-08 11:31:06 +00:00
5dba928154 theme: Reduce icon grid spacing
Now that the icons are correctly scaled down and don't cause overlaps
with the spacing anymore, the spacing between icons causes them to be
smaller than they were before. This only affects low resolutions,
because the spacing property is used as a minimum spacing for the app
grid. On higher resolutions the spacing is always larger than this
minimum.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1375
2020-08-06 15:36:26 +00:00
ab9fb0f351 style: Swap text-align in RTL locales
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3037


(cherry picked from commit d0dab5a6d1)
2020-08-03 16:47:14 +00:00
c2ba11425c background: Mark pattern backgrounds as loaded
Otherwise we don't let GNOME Shell startup to proceed. Noticed
by accidentally running on the memory GSettings backend.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1381
2020-07-30 16:52:39 +02:00
98ab6ae70d loginDialog: Reset auth prompt on vt switch before fade in
At the moment, if a user switches to the login screen vt,
the login screen fades in whatever was on screen prior, and
then does a reset.

It makes more sense to reset first, so we fade in what the
user is going to interact with instead of what they interacted
with before.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2997
(cherry picked from commit 13137aad9d)
2020-07-27 16:52:49 -04:00
2bf544e272 Revert "workspacesView: Avoid setting invalid geometries on views"
This reverts commit 67b9386b4b.

For not yet known reasons this caused a regression on the stable branch. Further
more it appears not to be needed, as no work depending on it has been backported
so far.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2971
2020-07-22 20:07:49 +02:00
8f6c64f607 Revert "workspacesView: Only animate on show() when geometries are already set"
This reverts commit bda8ba5ed1.

For not yet known reasons this caused a regression on the stable branch. Further
more it appears not to be needed, as no work depending on it has been backported
so far.
2020-07-22 20:01:25 +02:00
5f509855e2 backgroundManager: Always emit 'loaded' signal
As backgrounds are cached, it is possible that we never emit the
'loaded' signal added in commit f386103bc1. We are relying on the
signal though, so do the same as Background and emit the signal
from an idle if the background was already loaded.

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


(cherry picked from commit 5c550daecb)
2020-07-22 12:25:17 +00:00
7097796c3f Delay login animation until after wallpaper has loaded
Currently, the login animation can occur before the user's wallpaper has
been loaded. When this happens, we wind up displaying a solid blue
background for half a second or so before the proper background is
displayed. This looks jarring and bad. It's great that we can start
GNOME quickly, but starting up before the wallpaper is ready is *too*
quickly.

I've been meaning to fix this since 2014. Better late than never! We can
just have BackgroundManager emit a loaded signal the first time it loads
its first background, and have the startup animation code wait for that
before proceeding.

Some of this code is by Florian, who helped with promisifying. Thanks!

https://bugzilla.gnome.org/show_bug.cgi?id=734996
2020-07-22 03:57:25 +02:00
1e87315554 popupMenu: Handle keypress if numlock is enabled
On Wayland, navigating menus with the keyboard would not open drop-down
menus when NumLock is enabled.

That's old issue (gnome-shell#550) that was not completely fixed with
commit 88556226 because the lock mask needs to be filtered out in
_onKeyPress() as well.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/550
2020-07-21 17:42:52 +02:00
6b86b6a3e2 iconGrid: Account for non-icon content and padding/spacing when scaling
_updateIconSizes() was assuming that the icon is the only content of
an item when scaling the icon size to ensure the item size matches
_fixedHItemSize/_fixedVItemSize. However the icon may have padding and
there might be a label and spacing between the icon and the label. This
resulted in items being larger than their slots.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2234
2020-07-20 18:11:09 +00:00
ab59b74124 calendar-server: Remove delay before event emission
The timeout seems to have been carried over from the old code that
relied on gnome-shell calling 'GetEvents' after every 'Changed' signal
where it was used to throttle the signal. In the new code where
calendar-server is sending the changes themselves via signals this is no
longer necessary and actually causes a delay when switching between
months.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2992
2020-07-20 14:09:30 +02:00
2f61d9e94b keyboard: Fix missing key icons in numeric level
Those were missed in commit 57669bca1b.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2631
2020-07-20 01:37:55 +02:00
b8a7291e1d keyboard: Move named icons into subdirectories
While GtkIconTheme does look up icons in the toplevel icons resource
path, it will only use them as ultimate fallback. That is, if the
icon theme (or the hicolor fallback) include a "keyboard" icon, it
will be used over the "keyboard-enter-symbolic" icon in the resource.

Moving the icons to appropriate subdirectories gives them higher
priority than the fallback names, and thus fixes the issue.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2631
2020-07-20 01:37:53 +02:00
222919cfc1 Update Brazilian Portuguese translation 2020-07-18 13:43:51 +00:00
db30fbe3e9 Update Brazilian Portuguese translation 2020-07-17 21:36:10 +00:00
03062d0d9d extensionDownloader: Fix check for updates with several extensions
When having several extensions installed checking for updates fails.
This is because we are using GET and query params and since we are
sending all the metadata of the extension the server returns 502
when the URL is too long. This error code is ignored safely.

It is only needed to send the version of the extension to check if it
has updates.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2962
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1363


(cherry picked from commit f0d2509dc3)
2020-07-16 00:33:27 +00:00
ac2066673f Update Chinese (China) translation 2020-07-12 02:34:36 +00:00
19f6281f84 shell/window-tracker: Tighten sandbox ID prefix check
Since commit b60836932 we only allow WM_CLASS matches for sandboxed
applications if the found app's ID is prefixed by the sandbox ID.

The existing check still has a hole in it though: "org.example.Foo"
and "org.example.FooDevel" are different applications, yet the former
is a prefix of the latter.

So tighten the check by including a trailing "." in the checked prefix;
this excludes cases like the above, while still working for the regular
case of a single .desktop file because our app IDs include the ".desktop"
suffix.

Spotted by wjt.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1357
2020-07-08 19:45:43 +02:00
7dce433978 Bump version to 3.36.4
Update NEWS.
2020-07-07 21:05:18 +02:00
96699b996c status: Pass scroll events to volume and brightness sliders
Sliders can be operated by mouse scroll, but the mouse has to be over
the slider control.  Make the brightness and volume system menu entries
forward scroll events to the sliders they contain so that scrolling
anywhere on the menu item operates the slider.

Closes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2795
2020-07-07 20:59:09 +02:00
390431c5e0 extensions-tool: Escape '\' and '"' in json string
If user-input string contains '\' and/or '"', extensions-tool
generates invalid json.
This fixes that by escaping '\' and '"'.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1279
2020-07-07 20:59:09 +02:00
afb405782c dateMenu: Do not ellipsize clock
This addresses the issue with ellipsized clock that occurs when using
extensions that move the clock from the middle to the side of the top
bar.

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

Signed-off-by: MOZGIII <mike-n@narod.ru>
2020-07-07 20:59:09 +02:00
12b31e6bd0 altTab: Remove down arrow when removing an app from switcher
The arrow of the removed app was still left in the list with the
visibility of the arrow still depending on the original list order. This
could either lead to apps with just one window now suddenly having a
down arrow or apps with multiple windows not having one. If the last
window in the list had a down arrow, it would have been displayed
outside the window switcher.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1340
2020-07-07 20:59:09 +02:00
d76c219026 keyboard: Request a bigger size in portrait orientation
In portrait orientation, we set the height to the preferred height
for the monitor width (or, if smaller, a third o the screen height).

However as the forWidth currently doesn't make a difference, the height
is effectively controlled by the natural height of the keys - which is
rather small.

Address this by making AspectContainer request an appropriate preferred
size based on the fixed ratio.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2349
2020-07-07 20:59:09 +02:00
d26b320ab7 keyboard: Fix setting height in portrait orientation
get_preferred_height() returns both the minimum and natural height,
not a single size. Math.min() doesn't handle that and returns NaN,
whoops.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2349
2020-07-07 20:59:09 +02:00
132a8bb53e shell/window-tracker: Match on WM_CLASS first
Currently our heuristics for matching a window to its app check
for the application ID before the WM_CLASS, as the ID is more
reliable in so far that it is outside the application's control
and so it cannot use it to spoof a different application.

However this also prevents applications with multiple .desktop
files like LibreOffice from matching any .desktop files other
than the one under the main ID.

Since we now no longer allow the WM_CLASS to match a .desktop
file that doesn't belong to the sandboxed application, we can
fix that issue by checking the WM_CLASS first, without opening
the door to spoofing.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/219
2020-07-07 20:59:09 +02:00
c79251101d shell/window-tracker: Enforce prefix for sandboxed applications
At least flatpak (no idea about snap, sorry) enforces that all .desktop
files exported by a sandboxed app use the application ID as prefix.

Add the same check when trying to find a match based on the WM_CLASS,
to prevent sandboxed apps from matching a .desktop file they do not
own.

At the moment this is unlikely as we check for a match on the
sandboxed app ID first, but we are about to change that.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/219
2020-07-07 20:59:09 +02:00
f7dc59e370 shell/window-tracker: Minor simplification
Switching to autocleanup gives us a better separation between the
app/no-app cases.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/219
2020-07-07 20:59:09 +02:00
d0d91c49b8 inhibitShortcutsDialog: Enable line wrapping for additional label
The inhibitShortcutsDialog can show an additional label which explains
how to restore shortcuts. This label is not managed by the
MessageDialogContent, so we need to enable line wrapping and disable
ellipsization ourselves.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1336
2020-07-07 20:59:09 +02:00
6b6045578c dialog: Return GLib.SOURCE_REMOVE instead of false
This is more readable than just returning false.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1336
2020-07-07 20:59:09 +02:00
b5f141f596 dialog: Check whether text changed when setting title or description
As usually with GObject setters, we should check whether the property
actually changed before setting the value and notifying the property. So
check whether the title or description text actually changed before
setting it.

This fixes a bug which makes the title flicker and change its size,
because when updating the title we remove the "leightweight" css class
and reapply it inside a later, which makes the title appear larger for
one frame.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1336
2020-07-07 20:59:09 +02:00
0790503f16 st/viewport: Only extend child allocation when scrolled
When scrolled, the container's allocation is smaller than the allocation
of the content. To account for that, commit 2717ca9d08 added the
additional size reported by the layout manager to the content allocation.

However as it did so unconditionally, we now allow children to extend
outside the parent even when *not* scrolled, which breaks any constraints
set on the container (like "width" or "max-height").

Fix this by only extending the child allocation in scrollable dimensions.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2491
2020-07-07 20:59:09 +02:00
222698954f Update Romanian translation 2020-07-06 11:37:06 +00:00
25a8f484e4 unlockDialog: Fix scale-factor handling on multihead
The blur effect needs to take the scale-factor into account, so we
listen for scale changes. However we set up the signal handler when
creating a background, which is repeated for each monitor, and every
time the monitor configuration changes. But we only disconnect the
last handler that was connected, and only when we are destroyed,
not when recreating backgrounds.

Fix this by splitting out updating the effect parameters to a separate
method that iterates over all backgrounds, so we can simply set up the
handler from the constructor.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1341
2020-06-30 15:42:30 +02:00
fae7ba52dc Revert "st-label: Keep labels fully pre-rendered on the GPU"
This reverts commit 96f1d1b08d.

There have been reports of issues around this commit so back out from
it on the stable branch.

See https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2937
2020-06-29 15:53:21 +02:00
9f87ffc054 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-06-28 01:41:18 +02:00
2e8ade4da0 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-06-28 01:39:15 +02:00
235ffa29dc 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-06-28 01:39:06 +02:00
8d2a87781e 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-06-28 01:37:48 +02:00
d14bf9a12a calendar-server: Add missing spaces
... according to coding style.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-06-28 01:37:38 +02:00
12f2053613 calendar-server: Replace tabs with spaces
... according to the coding style.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
2020-06-28 01:35:18 +02:00
0abbd52646 theme: Make world clock times tabular
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2825


(cherry picked from commit 71b3b03b2f)
2020-06-26 10:22:34 +00:00
96f1d1b08d st-label: Keep labels fully pre-rendered on the GPU
The performance of the icon grid was being hindered by a large number
of primitives (a few hundred) being copied from the CPU to the GPU on
each frame. This was first noticed in mutter#971 but we failed to
investigate all the issues at the time.

You can also see the high number using `COGL_DEBUG=batching` or
`COGL_DEBUG=disable-texturing`. So now it's obvious that high number is
every letter of every label being uploaded as a separate quad. Let's not
do that and instead treat the whole label as a single quad/texture.

Measured performance on an i7-7700 at UHD 3840x2160:

Journal entries per frame on the icon grid:
 * Before: 288 (18 KB copied from CPU to GPU)
 * After:   73 ( 4 KB copied from CPU to GPU)

Spring animation:
 * Before: 20-30 FPS, avg 22/peak 45 milliseconds per frame
 * After:  30-40 FPS, avg 14/peak 28 milliseconds per frame

Scrolling the icon grid:
 * Before: 15 FPS, 50 milliseconds per frame
 * After:  30 FPS, 28 milliseconds per frame

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

(cherry picked from commit ae338af1e8)
2020-06-25 21:18:21 +02:00
d3384d29e4 Update Kazakh translation 2020-06-25 04:34:15 +00:00
6de2fd6324 Update Kazakh translation 2020-06-24 15:23:39 +00:00
a9c74ed78f overview: Define ANIMATION_TIME earlier
Commit c7e597cf72 tried to improve the slide animations when entering
the overview by using the same time as the overall overview animation,
but in fact broke the animation most of the times.

That is because the Overview imports OverviewControls before defining
the ANIMATION_TIME variable, so any javascript code that is evaluated
during that import will see the value as "undefined" (which is converted
to 0 for the animation).

Fix this by moving the ANIMATION_TIME variable before the imports instead
of the usual placement.

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

(cherry picked from commit 26d27fdbf8)
2020-06-24 16:03:41 +02:00
c02d0f3a7d overviewControls: Animate sidebars the same duration as windows
When you tap Super and see the sidebars and windows slide, it looks more
cohesive if those animations complete at the same time.

Previously there were 0.09 seconds difference between the two animations
which was enough to make it look slightly buggy. Now it doesn't.

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

(cherry picked from commit c7e597cf72)
2020-06-23 21:21:52 +02:00
bd84de38a3 Update Friulian translation 2020-06-21 08:41:09 +00:00
bda8ba5ed1 workspacesView: Only animate on show() when geometries are already set
Animating the window clones of the overview requires the fullGeometry
and the actualGeometry to be set, which they won't be when showing the
overview for the first time. So don't even try to animate the window
clones in that case because the geometries will still be null and
accessing them in workspace.js will throw errors.

The workspace views will still get the correct layout as soon as the
allocations happen because syncing the geometries will trigger updating
the window positions. Since animations are disabled for position changes
when syncing the geometry though, we won't get an animation and the
clones will jump into place. That's not a regression though since before
this change we also didn't animate in that case because the geometries
used were simply wrong (the actualGeometry was 0-sized as explained in
the last commit).

If we wanted to fix the initial animation of the overview, we'd have to
always enable animations of the window clones when syncing geometries,
but that would break the animation of the workspace when hovering the
workspaceThumbnail slider, because right now those animations are "glued
together" using the actualGeometry, so they would get out of sync.

The reason there are no errors happening in workspace.js with the
existing code is that due to a bug in Clutter the fullGeometry of
WorkspacesDisplay gets set very early while mapping the WorkspacesViews
(because the overviews ControlsManager gets an allocation during the
resource scale calculation of a ClutterClone, see
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1181), so it
won't be set to null anymore when calling
WorkspacesView.animateToOverview().

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119
2020-06-14 17:15:01 +02:00
67b9386b4b workspacesView: Avoid setting invalid geometries on views
The fullGeometry and the actualGeometry of the WorkspacesDisplay are set
from the allocation of the overviews ControlsManager and the
WorkspacesDisplay, that means they're only valid after those actors got
their allocations during Clutters allocation cycle.

Since WorkspacesDisplay._updateWorkspacesViews() is already called while
showing/mapping the WorkspacesDisplay, that allocation cycle didn't
happen yet and we end up either setting the geometries of the views to
null (in case of the fullGeometry) or to something wrong (a 0-sized
allocation in case of the actualGeometry).

So avoid setting invalid geometries on the views by initializing both
the fullGeometry and the actualGeometry to null, and then only updating
the geometries of the views after they're set to a correct value.

Note that this means we won't correctly animate the overview the first
time we open it since the animation depends on the geometries being set,
but is being started from show(), which means no allocations have
happened yet. In practice this introduces no regression though since
before this change we simply used incorrect geometries (see the 0-sized
allocation mentioned above) on the initial opening and the animation
didn't work either.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119
2020-06-14 17:15:01 +02:00
62a34c5116 Update Japanese translation 2020-06-11 10:27:21 +00:00
d1ed344cef Update Japanese translation 2020-06-11 10:22:37 +00:00
9ccd343764 unlockDialog: Set Switch User Button via _updateUserSwitchVisibility
This commit will set the button invisible when the user's can_switch
is false (e.g. when the session is remote) or user-switch-enabled is
disabled.

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


(cherry picked from commit d2cf13eff4)
2020-06-10 16:14:31 +00:00
61c7092184 Updated Slovenian translation 2020-06-10 14:11:36 +02:00
b2454bd1b2 st/entry: Fix leak when copying or cutting text using shortcuts
clutter_text_get_selection() creates a copy of the selected text which
gets passed to st_clipboard_set_text() which creates its own copy. The
copy returned by clutter_text_get_selection() however never got free'd.

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


(cherry picked from commit fdfcacf1db)
2020-06-08 15:32:50 +00:00
ff73f76527 shell-mime-sniffer: Ignore invalid file content type
The shell mime sniffer goes through all the files in a directory,
however in case a file content type is not recognized, the GIO function
g_file_info_get_content_type() may return NULL, causing a crash when
looking up into the content type tables, as they are supposed to contain
strings only and they use `g_str_hash` has func, which doesn't support
NULL values.

So, in case we get an invalid content type, let's just ignore it,
without adding it to the cache as we do in the nautilus code that was
inspiring the sniffer.

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


(cherry picked from commit 46547ae027)
2020-06-05 15:22:00 +00:00
b642f9275e Bump version to 3.36.3
Update NEWS.
2020-06-03 01:38:26 +02:00
4220cd6624 extensionSystem: Prevent broken updates
Spidermonkey caches imports, which means that uninstalling an
old extension version and installing a new one doesn't work as
expected: If the previous version was loaded, then its code will
be imported instead.

For the last couple of releases this has been a reliable source
of extension bug reports after major GNOME updates. Thankfully
chrome-gnome-shell removed its update support in favor of our
built-in support now, but users may still use older versions
or perform those actions manually, so it still makes sense to
catch this case and set an appropriate error.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1248
2020-06-03 01:37:39 +02:00
47bcc09516 notificationDaemon: Try harder to find a matching app
Unlike the desktop-entry hint, the app name is not optional. That
doesn't mean that we'll be able to match it to a .desktop file,
but we can at least try if we fail to match on PID or desktop-entry.

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


(cherry picked from commit b487846c0a)
2020-06-02 23:33:48 +00:00
1889a975ce Updated Slovenian translation 2020-06-01 22:09:31 +02:00
4b8e090d19 Updated Lithuanian translation 2020-05-31 22:50:19 +03:00
e74e691d84 notificationDaemon: Fix grouping by PID
For fd.o notifications, we are taking the sender's PID into
account when associating notifications with sources (mainly
to deal with notify-send).

This broke when the implementation under the well-known name
was moved into a separate service, as the implementation in
gnome-shell will now always see the public notification-daemon
as sender.

Restore the old behavior by resolving the sender PID in the
separate service, and pass it as hint to the implementation
in gnome-shell.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2592
2020-05-29 21:59:10 +02:00
3ad2baede0 padOsd: Apply specific CSS to Button/Leader SVG classes
Applying a fill operation on the Leader line path seems to close
it, resulting in filled polygon. Bug or not this is not the intended
result here, we can do less ambiguously by not specifying the fill
CSS property to the Leader class.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
3c69cb5677 padOsd: Keep label coordinates in image coordinates
Apply the necessary transforms to map those coordinates to actor
positions in the allocate phase. This all fits since it's the place
where we do know the size the actor will have.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
bd3f8de1e3 padOsd: Cache label coordinates/arrangements
This is actually static for a given PadDiagram, as it always represents
a single device.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
27455c4458 padOsd: Only allocate child labels within allocate vfunc
Make both start/stop edition and label updates queue a relayout, and
only deal with child allocations in the allocate method.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
5fca21b943 padOsd: Drop needless call
If we got this far, we are dealing with an already known label.
There's no need for this check.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
064633f4d5 padOsd: Use map to store misc action label data
We'll be adding more stuff here, so it's a bit inconvenient to keep
it an array.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
159ac3f180 padOsd: Move all coord/existence checks to _addLabel()
Drops some repetitive code. Also rely completely on the label/leader
elements being found in order to find buttons/rings/strips.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:57 +02:00
ba435e5f2d padOsd: Make label coordinates API "private"
This is only called internally, and only needed there.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:56 +02:00
b0a12fee51 padOsd: Move button/ring/strip label creation to PadDiagram
It's the natural container of those. We can create all those labels
internally, and only expose the updateLabels() method to update them
wholesome.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:56 +02:00
b5591fef10 padOsd: Fix double styling
We set the StLabel style property, there's no need to re-apply the
large/bold text style via markup. Makes the StLabel text size consistent
across editable state changes.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:56 +02:00
04e8ebcb2d padOsd: Disable ellipsizing in title label
We make the label text large and let it ellipsize. It ends up doing
so instead of allowing the label to expand. This title is important
and we don't want it to be ellipsized, so ensure that won't happen.

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

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1290
2020-05-29 20:26:56 +02:00
99ebef504d screenshot: Properly clean up if PickColor() is cancelled
We currently only remove the screenshot operation from the shooter
map if the color pick operation completed successfully, but not if
it was cancelled. As a result, we now reject any further requests
from the same sender because we assume that there is an ongoing
operation.

Fix this by moving the cleanup to a finally clause that runs for
both code paths.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1288
2020-05-29 10:46:13 +02:00
c8d91b2c30 screenshot: Return an error when operation is blocked
Right now _createScreenshot() returns a tuple that indicates failure
when a sender requests a screenshot operation before a previously
started operation finished.

However that doesn't work for the PickColor() method, as it uses a
different return type than the other methods.

Address this by returning an error instead, which works in any case;
arguably trying to start multiple operations in parallel is an error
by the caller more than it is a failed operation anyway.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1288
2020-05-29 10:45:06 +02:00
4099c446da Update Korean translation 2020-05-27 23:50:25 +00:00
d625618d1b power: Fix icon when discharging at 100%
The special-case for a fill level of 100 introduced in commit 5fd52e99d3
should only apply when charging, for the discharging state there's a
proper battery-level-100-symbolic icon.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2286
2020-05-26 23:25:54 +02:00
a09feb4b47 st/texture-cache: Cancel pending requests on icon-theme changes
As outlined in commit 36b8dcbe07, we can end up with wrong icons
if the icon theme changes right after a GTK theme change to/from
HighContrast triggered a theme reload.

That's because when we reload icons for the new icon theme, there
are already pending requests due to the icon-style change; those
requests are simply re-used for the new icons, with the existing
icon infos from the old theme.

The above commit applied a simple work-around by changing the
icon theme before the GTK theme, but that only works for the
HighContrast switch in our own UI.

It turns out that Settings also uses the "wrong" order, so the
issue still reproduces with the Universal Access panel.

So instead of relying on everything changing the settings in the
order we expect, cancel all ongoing requests on icon-theme changes.

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


(cherry picked from commit d81237b9d6)
2020-05-25 13:14:26 +00:00
bc3c1e5a68 Update German translation 2020-05-24 16:06:24 +00:00
b148a8bc60 Update Slovak translation 2020-05-24 07:27:55 +00:00
2327fc9287 st/theme: Remove entry from files_by_stylesheet after emitting signal
Since e06109c23c we keep old theme nodes
valid during the emission of the "custom-stylesheets-changed" signal.

It turns out that we might still look up the file of a stylesheet using
the files_by_stylesheet hashtable during the emission of that signal,
causing a crash because the assertion in _st_theme_resolve_url() fails.

So fix that and remove the stylesheet entry from the files_by_stylesheet
hashtable after emitting the "custom-stylesheets-changed" signal. And to
be consistent, also remove the entry from the stylesheets_by_file
hashtable after emitting the signal.

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


(cherry picked from commit 0f947d4ff9)
2020-05-21 22:47:15 +00:00
28407c95b3 extensions-app: Reset switch handle when it becomes insensitive
Currently the switches handle position reflects the requested
extension state (as in: the user (de)activated the switch),
while the actual extension state is reflected by the underlying
state.

That doesn't work well when the switch is insensitive though (for
example on error), because the desaturation neuters the color
that reflects the state.

Address this by resetting the switch handle to the state when
making it insensitive.

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


(cherry picked from commit d2583aa47b)
2020-05-21 21:43:35 +00:00
e210d3138b extensionSystem: Update canChange on error
Whether or not an extension has errors influences the 'canChange'
property, but so far we only update it for errors that occur when
initializing the extension, not when an extension is enabled later.

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


(cherry picked from commit 3309031fd1)
2020-05-21 21:42:44 +00:00
3f24721c76 dateMenu: Remove unused property
This is a left-over from an earlier iteration where the session's
presence status was used rather than the GSetting.

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


(cherry picked from commit 4e2ae30a47)
2020-05-18 23:39:55 +00:00
1a045bb210 calendar: Fix initial do-not-disturb state
The do-not-disturb button and the contained switch are
tied together via a bidirectional property binding.

However it still matters which objects are used as source
and target, as that will determine the initial state: Right
now the (unchecked) button is used as source, which means
that do-not-disturb is turned off on startup.

We want the state to be preserved, so swap source and target
to let the switch (that is bound to the underlying GSetting)
control the initial state.

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


(cherry picked from commit 32bc064d10)
2020-05-18 17:02:13 +00:00
db2917479a Update Serbian translation 2020-05-17 19:22:39 +00:00
5815f9ac0e Update Finnish translation 2020-05-16 11:02:59 +00:00
e84d10bf87 dateMenu: Update timezone offsets when timezone changes
Adds a signal handler to update the timezone offsets whenever the
user changes the system timezone.

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


(cherry picked from commit 64a3ecf9b1)
2020-05-15 21:21:32 +00:00
40481314d2 extensionDownloader: Replace deprecated Soup.SessionAsync
Soup.SessionAsync by default sets timeout and idle_timeout to 0. This
causes connections to hang around in state CLOSE_WAIT forever after the
remote host has closed the connection.

To fix this, we could set timeout and idle_timeout manually. However,
Soup.SessionAsync is marked as deprecated anyway and should be replaced
by Soup.Session. Doing so also sets a default timeout of 60 seconds.

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


(cherry picked from commit a47e0f9845)
2020-05-15 16:22:35 +00:00
daf25f273d Update Turkish translation 2020-05-15 03:04:25 +00:00
bc6a3329b4 Update Indonesian translation 2020-05-14 09:42:52 +00:00
d3934bd685 extensionSystem: Disable extension before unloading stylesheet
Removing a stylesheet from the theme will trigger a style update. There's
little point in updating the extension actors that are about to be destroyed
(hopefully), so call the extension's disable() function first.

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


(cherry picked from commit 6d5e93b00b)
2020-05-13 19:15:37 +00:00
504d4b7ea4 mpris: Prevent DBus race when creating media players
Sometimes an MPRIS media player will create and then destroy an object
before the signals that handle the object's destruction can be created.
This verifies that the object still exists after the necessary signals
have been created.

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


(cherry picked from commit f526e592fe)
2020-05-13 18:00:55 +00:00
fde8519002 windowManager: Allow xwayland startup to complete without systemd
Since commit 0ecddafc20 gsd-xsettings startup has been made conditional
on the systemd user instance being available at runtime. While that is
correct, it means that completing xwayland startup is also conditional
now.

We always want xwayland startup to go ahead, so wait for the XSettings
plugin to appear on the bus when gsd-xsettings is launched by gnome-session
and complete the task immediately if startup fails.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1252
2020-05-13 19:51:09 +02:00
e8ae2ea432 shell: Fix return value if the systemd unit is actually successfully run
This got broken by last minute changes in commit 2d56395921. There's no change
too simple.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1242
2020-05-13 19:50:57 +02:00
57e67e0f31 shell: Check we are in a systemd environment before starting systemd units
Avoid starting/stopping the gsd-xsettings systemd unit if the shell itself
was not started via systemd. In the lack of a user session manager, we
have no means to neatly start/stop services, so should rely on the good
ol' gnome-session to do this for us.

This changes the return value meaning slightly, TRUE means "service did
start", FALSE otherwise. The error is only raised if we ought to start,
but it produced an error somehow.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1238
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2755
2020-05-13 19:50:21 +02:00
894ca0dd07 windowManager: Handle return value/errors from systemd unit calls
These may produce errors, and return a value indicating we should
proceed further. Check for those when starting/stopping gsd-xsettings.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1238
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2755
2020-05-13 19:50:20 +02:00
5f945294c8 Update Japanese translation 2020-05-13 15:16:38 +00:00
a4d0f15edb Update Japanese translation 2020-05-13 15:07:29 +00:00
f781bdbcc8 Update Romanian translation 2020-05-12 20:10:31 +00:00
e8e79173d9 Update French translation 2020-05-12 14:14:34 +00:00
884a2623a0 Update Swedish translation 2020-05-11 22:35:45 +00:00
fbdb56eb6d Update Italian translation 2020-05-11 15:52:39 +00:00
e2c5e471c0 Updated Spanish translation 2020-05-11 17:29:09 +02:00
34306d7326 Update Ukrainian translation 2020-05-11 14:05:38 +00:00
c3bdaf7e41 extensions-app: Fix translatable string
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1232
2020-05-11 13:54:29 +00:00
c9950442d9 workspacesView: Lower scroll timeout
Looks like 250ms is too high, lower it to 150ms.

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

(cherry picked from commit e59ca7053b)
2020-05-11 13:34:57 +02:00
5cf104933e appDisplay: Lower scroll timeout
Looks like 250ms is too high, lower it to 150ms.

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

(cherry picked from commit 6895592a7b)
2020-05-11 13:34:36 +02:00
79aeabcf76 Update Romanian translation 2020-05-08 16:28:31 +00:00
1974724cb1 systemActions: Use 'system-log-out-symbolic' for logout
Changing the icon to 'system-log-out-symbolic' has no visual
change in a default GNOME setup since both 'system-log-out-symbolic'
and 'application-exit-symbolic' are the same in adwaita-icon-theme
(at the time of writing), however, other icon themes differentiate
between the two icons so pointing to the appropriate icon name
is the right thing to do.

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

(cherry picked from commit 7d60f418e7)
2020-05-07 19:43:05 +00:00
d159bbfd79 Updated Spanish translation 2020-05-07 16:48:09 +02:00
881d330bc0 panel: Center app icon
Giving the icon extra space may distort it, so center it vertically.

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


(cherry picked from commit a9d73b1017)
2020-05-07 13:10:26 +00:00
6a9ce0f18c Update British English translation 2020-05-07 10:48:02 +00:00
473a666cc0 Update Dutch translation 2020-05-06 11:13:18 +00:00
4872d5969c messageTray: Add missing property getter
gjs has gotten less forgiving about missing getters/setters, and
commit 6aa1b817 missed the missing getter in the base policy class.

Most notifications use a policy subclass that already provides a
getter, but at least Main.notify() and friends don't; unbreak them
by fixing the base class.

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


(cherry picked from commit 5e254666b0)
2020-05-04 14:34:24 +00:00
048796d145 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


(cherry picked from commit 6aa1b817c9)
2020-05-04 14:33:50 +00:00
dfc016894e Updated Spanish translation 2020-05-04 09:57:25 +02:00
7b19a6f9b6 Update Polish translation
Fixes https://gitlab.gnome.org/Teams/Translation/pl/-/issues/6
2020-05-02 21:44:46 +02:00
e4199c71cc Update Slovak translation 2020-05-02 14:07:10 +00:00
abb401eaa8 appFavorites: Add eog to rename list
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1233


(cherry picked from commit 31cd8f738c)
2020-05-01 22:42:55 +00:00
118676bffb st/theme: Unref CRStylesheet after removing old theme nodes
Since we now remove all theme nodes on a stylesheet change (ie.
StTheme's "custom-stylesheets-changed" signal) instead of only
invalidating them, those nodes may not be accessed anymore as soon as
"custom-stylesheets-changed" is emitted.

It turned out though that when comparing them to the newly generated
nodes in `st_widget_recompute_style()` using the
`st_theme_node_paint/geometry_equal()` functions, the properties of the
old nodes will still be accessed, causing a crash since the
CRDeclarations are already freed.

To fix that, keep the reference to the CRStylesheet, which owns the
CRDeclarations used by the theme nodes, around a bit longer, so it's
still possible to access the CRDeclarations inside the
"custom-stylesheets-changed" signal handler. This allows us to compare
the old theme nodes to the new ones since the CSS properties of both are
still valid.

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


(cherry picked from commit e06109c23c)
2020-05-01 14:32:38 +00:00
dff855942e Update Chinese (Taiwan) translation 2020-04-30 17:31:56 +00:00
1d3c1b1ee3 Add small gnome-shell-extension-prefs script
Commit d76162c1c0 removed the ability to pass an extension UUID to
the Extensions app, when we moved the dialog to a portal and made
gnome-shell use it instead of spawning the extensions app.

However that missed that many extensions called out to the app to
open their own prefs.

While extensions are encouraged to switch to the new openPrefs()
convenience method added in commit 8030d9ad32, restore the old
behavior with a small script under the old gnome-shell-extension-prefs
name that either calls out to the portal or launches the app.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1220
2020-04-30 00:57:27 +02:00
7b75e9f5ed extensions-app: Rename executable
We kept the old binary name when overhauling the tool for the Extensions
app to avoid unnecessary churn for packagers/distributors.

However we now have a reason to "free" the old name, so rename the binary
to match the (sub)project name.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1220
2020-04-30 00:57:27 +02:00
4336952822 Update Friulian translation 2020-04-29 22:25:52 +00:00
2a2634439c Bump version to 3.36.2
Update NEWS.
2020-04-29 22:39:21 +02:00
0d2d8cf623 Revert "endSessionDialog: apply updates by default"
Turns out it's not that clear in which cases we want to apply updates
and in which ones we shouldn't. For example one case is when the device
is running on battery, where we currently display a warning, but still
install updates by default.

So let's revert that again for now and hopefully work out a proper
concept for updates in the future.

This reverts commit ddb85c03c3.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1228
2020-04-29 19:51:05 +00:00
d64b1e6efb main: Unset the right prevFocus actor after the focus stack got shifted
When a modal that's not on top of the modalActorFocusStack gets popped,
we shift the focus stack as described in popModal() to ensure the chain
remains correct. That however destroys the association of a modal actor
and its prevFocus actor on the focus stack, because the prevFocus actors
are now moved to different entries of the stack.

Now when a prevFocus actor gets destroyed, we don't handle that case
correctly and search for the modal actor that was associated with the
prevFocus actor before the stack was shifted, which means we end up
unsetting the wrong prevFocus actor.

So fix that and search the stack for the prevFocus actor which is being
destroyed instead to unset the correct entry.

Thanks to Florian Müllner for figuring out the actual issue and
proposing this fix.

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


(cherry picked from commit d3880c0bff)
2020-04-29 17:36:06 +00:00
bbf3a09e2a shell Always draw the cursor ourself when capturing stage
Mutter changed to never draw the cursor with the used API, as it can't
be done in a predictable manner, so always draw it ourself.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1224
2020-04-29 12:38:01 +00:00
e57a4e0937 st/theme-context: Also remove theme nodes on stylesheet changes
StThemeNodes are built around the assumption that they're "immutable",
that means they are created once with certain parameters (that resolve
to certain css properties) and then replaced with new ones in case those
parameters changed.

Changes to the internal information stored by theme nodes (i.e. the css
properties, font names or the cached scale-factor) are not all handled
the same though: For changes to the font or the scale-factor we remove
all theme nodes from the cache and let the widgets which are on stage
generate new theme nodes. For changes to the css properties/the
stylesheet, we invalidate the properties of all theme nodes but keep
them in the cache using `_st_theme_node_reset_for_stylesheet_change()`.

So be a bit more consistent and handle changes to the css-properties/the
stylesheet stored by StThemeNodes the same way as changes to the font or
scale-factor.

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


(cherry picked from commit 4dfa39457e)
2020-04-28 20:11:15 +00:00
443d615edf 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


(cherry picked from commit 7d7a15f978)
2020-04-28 20:05:23 +00:00
006d0e8521 theme: tone down weekend with events
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2588


(cherry picked from commit e8f5a842b1)
2020-04-28 20:04:18 +00:00
1a5a01622a 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


(cherry picked from commit 3199620a83)
2020-04-28 20:03:44 +00:00
38e31b9f0d st: Ensure to reset all widget theme nodes
Theme node invalidation stops at unmapped widgets, and widgets
that forget to chain up to the default ::style-changed implementation.
This may leave stale nodes that were invalidated on
StThemeContext::changed, but are still set on widgets, and maybe
used for CSS property lookups.

Make sure that theme node invalidation happens always by moving
propagation outside the vfunc, and ensure the theme nodes are reset
across the full actor tree. Emission of ::style-changed, and obtaining
a new theme node may be delayed till when the actor is mapped.

The theme node is also cleared after unparenting an actor to avoid
stale references.

This ensures that all widgets get their theme node cleared after
stylesheet changes, instead of maybe being left with a theme node
that's been cleared of all its properties.

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

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

(cherry picked from commit 325ff73c5b)
2020-04-28 22:00:32 +02:00
fb7e70d562 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


(cherry picked from commit 8d139bbd95)
2020-04-28 16:45:27 +00:00
63a6c0d6f9 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


(cherry picked from commit fb1bb291eb)
2020-04-28 16:45:08 +00:00
05c1a6dce6 Update Czech translation 2020-04-28 16:37:25 +00:00
4097f7943f altTab: Set allocation before allocating children
Just as with the last commit, we should not break the assumption made by
Clutter that parents have their allocation set before their children get
allocated, so fix that here, too.

In this case we have to fix it by chaining up to the parent vfunc
override and updating the allocation once more before allocating the
`this._label` child.

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


(cherry picked from commit 49d6db34b7)
2020-04-28 16:24:25 +00:00
802c2fe532 boxpointer: Call set_allocation before allocating child
It's important to update the allocation of the parent before allocating
its children, it's an assumption we make in a lot of places.

This broke resource scale calculation for boxpointers and their
children when multiple monitors with different scales are used and the
primary monitor is not positioned at x=0, y=0.

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


(cherry picked from commit 63a0e521fd)
2020-04-28 16:24:05 +00:00
9497ddd68a overviewControls: Remove transitions before updating adjustment value
The workspace switcher blocks state updates while the indicator is
animating. Since commit 9c1940ef9d the indicator is considered to
be animating when the workspace adjustment's value doesn't equal the
active workspace.

There is one case though where this breaks badly: When a workspace
is inserted before the active one, the adjustment's upper and value
properties are changed without transitions. But if that change happens
while there's an ongoing transition to the previously active workspace,
the value gets out of sync with the active workspace and we end up
blocking state updates indefinitely.

Fix this by removing any transitions before setting the adjustment
value.

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

(cherry picked from commit 2e80995f19)
2020-04-28 10:14:16 +02:00
890f5b591f keyboard: Add missing setter
We override the :visible property for the keyboard actor, but don't
provide a corresponding setter. The property is therefore read-only
on the javascript level, and any attempt to set it will fail.

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


(cherry picked from commit 3ba4304da9)
2020-04-27 22:04:16 +00:00
5b0d013229 Update Czech translation 2020-04-22 12:51:51 +00:00
60ee6ab4b7 Update Brazilian Portuguese translation 2020-04-22 12:32:56 +00:00
18a5c74362 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


(cherry picked from commit b191e9ef91)
2020-04-17 22:59:00 +00:00
4895032054 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


(cherry picked from commit 5ec5978d4a)
2020-04-17 22:36:04 +00:00
6b24ab572c 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


(cherry picked from commit f4d90bc127)
2020-04-17 16:59:38 +00:00
35a3320851 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


(cherry picked from commit 3fac0632a8)
2020-04-13 15:05:18 +00:00
156e05fe90 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


(cherry picked from commit c2b518929d)
2020-04-13 15:04:31 +00:00
cc0fac895c Update Finnish translation 2020-04-13 15:03:18 +00:00
a6783692c5 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 14:37:35 -03:00
1c27b68bcc 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 14:37:35 -03:00
717c05a288 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 14:37:35 -03:00
e68604b1aa 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 14:37:35 -03:00
0368ad29e9 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 14:37:35 -03:00
b982ce394e 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


(cherry picked from commit 85846d88f0)
2020-04-06 15:00:05 +00:00
025647f585 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


(cherry picked from commit bc973b80d7)
2020-04-06 14:59:45 +00:00
7125b726ad 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


(cherry picked from commit 6b7c85b079)
2020-04-05 13:48:39 +00:00
aebfab7207 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


(cherry picked from commit 360f5b1642)
2020-04-05 12:46:11 +00:00
698bd5b3a9 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


(cherry picked from commit 1ca39e8586)
2020-04-04 19:40:10 +00:00
51e9f19f2f 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


(cherry picked from commit 07deda593a)
2020-04-04 19:30:49 +00:00
6d38a4a7b3 Update Latvian translation 2020-04-04 16:14:40 +00:00
dfcc5ffb1e 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


(cherry picked from commit fbe2e30f38)
2020-04-03 15:09:24 +00:00
8b80a4cf4d 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


(cherry picked from commit fb6ead2881)
2020-04-03 15:08:58 +00:00
01e894c028 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

(cherry picked from commit 7ff7fb5d3b)
2020-04-03 17:00:30 +02:00
856adfd1f1 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

(cherry picked from commit 8030d9ad32)
2020-04-03 17:00:07 +02:00
efee3aa749 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

(cherry picked from commit 45bc850715)
2020-04-03 16:58:15 +02:00
15e72da648 workspace: Fix chaining up
Gah, accidentally dropped the 'vfunc' prefix :-(

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1172
2020-04-03 15:47:01 +02:00
3f8bd1db25 extensions-app: Do not expand headerbar switch
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2563
2020-04-03 15:47:01 +02:00
3a863ee341 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:03:18 +00:00
654a7af929 environment: Move g_dbus_connection_call() promisification
Commit 83c6b2ab promisified the method in endSessionDialog, which means
that after the module is imported, every caller will get the promisifed
version. That can be a bit surprising in completely unrelated modules,
so commit 764527c8 (on master) moved the promisification of more common
methods into environment, as that's initialized early and expected to be
shared between anything else.

Do the same for the call() method on the stable branch.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2551
2020-04-03 02:03:18 +00:00
8dd9cbac7f 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


(cherry picked from commit 343b3351f1)
2020-04-03 00:40:34 +00:00
331db650dd 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


(cherry picked from commit a9df4e7516)
2020-04-02 21:10:30 +00:00
428d38179d Update Serbian translation 2020-04-02 19:43:03 +00:00
fe9708ebd8 Update Basque translation 2020-04-02 16:15:32 +00:00
8398769321 Update Finnish translation 2020-04-02 09:44:36 +00:00
768c08ba9d 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


(cherry picked from commit a0def23940)
2020-04-01 03:58:56 +00:00
69426cbfda Update Persian translation 2020-04-01 01:42:46 +00:00
9f968e7378 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


(cherry picked from commit f49b58cf97)
2020-03-31 17:12:35 +00:00
1ab5e6973a Update Chinese (Taiwan) translation 2020-03-31 16:07:19 +00:00
1dea3341ec Update Friulian translation 2020-03-31 11:14:15 +00:00
8fda054dc5 Bump version to 3.36.1
Update NEWS.
2020-03-31 00:27:47 +02:00
e989684602 extensions-app/metainfo: Point screenshots to stable branch 2020-03-30 21:32:17 +02:00
241 changed files with 17934 additions and 24369 deletions

View File

@ -9,6 +9,7 @@ stages:
variables:
BUNDLE: "extensions-git.flatpak"
JS_LOG: "js-report.txt"
POT_LOG: "pot-update.txt"
.only_default: &only_default
only:
@ -17,7 +18,7 @@ variables:
- merge_requests
check_commit_log:
image: registry.gitlab.gnome.org/gnome/mutter/master:v4
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: review
variables:
GIT_DEPTH: "100"
@ -27,10 +28,10 @@ check_commit_log:
- merge_requests
js_check:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: review
script:
- find js -name '*.js' -exec js68 -c -s '{}' ';' 2>&1 | tee $JS_LOG
- find js -name '*.js' -exec js60 -c -s '{}' ';' 2>&1 | tee $JS_LOG
- (! grep -q . $JS_LOG)
<<: *only_default
artifacts:
@ -39,7 +40,7 @@ js_check:
when: on_failure
eslint:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: review
script:
- ./.gitlab-ci/run-eslint.sh
@ -50,23 +51,22 @@ eslint:
when: always
potfile_check:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: review
script:
- ./.gitlab-ci/check-potfiles.sh
<<: *only_default
no_template_check:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: review
script:
- ./.gitlab-ci/check-template-strings.sh
<<: *only_default
build:
image: registry.gitlab.gnome.org/gnome/mutter/master:v4
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: build
needs: []
before_script:
- .gitlab-ci/checkout-mutter.sh
- meson mutter mutter/build --prefix=/usr -Dtests=false
@ -83,9 +83,8 @@ build:
- build
test:
image: registry.gitlab.gnome.org/gnome/mutter/master:v4
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: test
needs: ["build"]
variables:
XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir"
NO_AT_BRIDGE: "1"
@ -100,9 +99,24 @@ test:
- build/meson-logs/testlog.txt
when: on_failure
test-pot:
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: test
before_script:
- ninja -C mutter/build install
script:
# Check that pot files are generated correctly:
# https://savannah.gnu.org/bugs/?50920#comment5
- ninja -C build gnome-shell-pot 2>&1 | awk '
BEGIN { start=0; }
start==1 { print $0; }
/gnome-shell-pot/ { start=1; }
' | tee $POT_LOG
- (! grep -q . $POT_LOG)
<<: *only_default
flatpak:
stage: build
needs: []
variables:
SUBPROJECT: "subprojects/extensions-app"
# Your manifest path
@ -110,7 +124,11 @@ flatpak:
RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
FLATPAK_MODULE: "gnome-extensions-app"
APP_ID: "org.gnome.Extensions"
MESON_ARGS: "$SUBPROJECT"
extends: .flatpak
before_script:
- flatpak run --command=$SUBPROJECT/generate-translations.sh
--filesystem=host org.gnome.Sdk//master
<<: *only_default
nightly:

View File

@ -1,24 +0,0 @@
# Rebuild and push with
#
# cd .gitlab-ci/
# podman build --format docker --no-cache -t registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2 .
# podman push registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v2
#
FROM registry.fedoraproject.org/fedora:32
RUN dnf -y update && dnf -y upgrade && \
dnf install -y 'dnf-command(copr)' git && \
# For syntax checks with `find . -name '*.js' -exec js68 -c -s '{}' ';'`
dnf install -y findutils mozjs68-devel && \
# For static analysis with eslint
dnf install -y nodejs && \
npm install -g eslint && \
# Shameless plug for my own tooling; useful for generating zip
dnf copr enable -y fmuellner/gnome-shell-ci && \
dnf install -y gnome-extensions-tool meson && \
dnf clean all

View File

@ -0,0 +1,18 @@
FROM registry.fedoraproject.org/fedora:latest
RUN dnf -y update && dnf -y upgrade && \
dnf install -y 'dnf-command(copr)' git && \
# For syntax checks with `find . -name '*.js' -exec js60 -c -s '{}' ';'`
dnf install -y findutils mozjs60-devel && \
# For static analysis with eslint
dnf install -y nodejs && \
npm install -g eslint && \
# Shameless plug for my own tooling; useful for generating zip
dnf copr enable -y fmuellner/gnome-shell-ci && \
dnf install -y gnome-extensions-tool meson && \
dnf clean all && \
rm -rf /var/cache/dnf

View File

@ -6,11 +6,6 @@ globs=('*.js' '*.c')
# find source files that contain gettext keywords
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
missing=$(for f in $files; do ! grep -q ^$f po/POTFILES.in && echo $f; done)

View File

@ -18,14 +18,12 @@ run_eslint() {
local extra_args=ARGS_$1
local output_var=OUTPUT_$1
local output=${!output_var}
local cache=.eslintcache-${1,,}
# ensure output exists even if eslint doesn't report any errors
mkdir -p $(dirname $output)
touch $output
eslint -f unix --cache --cache-location $cache ${!extra_args} -o $output \
js subprojects/extensions-app/js
eslint -f unix ${!extra_args} -o $output js subprojects/extensions-app/js
}
list_commit_range_additions() {

222
NEWS
View File

@ -1,154 +1,168 @@
3.37.90
=======
3.36.5
======
* Fix extension updates when many extensions are installed [Jeremias; !1363]
* Fix missing icons in on-screen keyboard [Emre; #2631, #3007]
* Fix delay when showing calendar events [Sebastian; #2992]
* Allow rearranging items in app picker [Georges; !1284]
* Fix app picker regressions on small displays [Sebastian; #2234, !1375, !1378]
* Fix top bar navigation when NumLock is active [Olivier; #550]
* Delay login animation until wallpaper has loaded [Michael; #734996]
* Revert changes that caused mispositioning in overview in multi-monitor setups
[Robert; #2971]
* Reset auth prompt on login screen on VT switch before fade in [Ray; #2997]
* Move screencasting into a separate service [Jonas Å.; !1372]
* Replace loaded terms with more descriptive one [Olivier; !1393]
* Add "Boot Options" support to restart dialog [Hans; !199]
* Move "Restart" into a separate menu item/dialog [Florian; #2202]
* Default to not installing updates on low battery [Michael; #2717]
* Misc. bug fixes and cleanups [Florian, Daniel V., Georges, Jonas Å.,
Daniel G., Carlos, Benjamin, Piotr, Andre, Jonas D., Andy; !1357, !1356,
#2969, #2969, !1358, !1371, #3005, !1380, #3022, !1381, !895, !1387, !1386,
!1385, #3037, !1389, !1390, !1391, !1383, !1399, #2983, !1403]
* Fix stuck grab when destroying open popup menu [Florian; #3022]
* Misc. bug fixes and cleanups [Florian, Carlos, Andre, Jonas; !1357, !1371,
!1381, #3037, #3005, !1386, !1390]
Contributors:
Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Piotr Drąg, Jonas Dreßler,
Olivier Fourdan, Carlos Garnacho, Hans de Goede, Andy Holmes,
Sebastian Keller, Andre Moreira Magalhaes, Daniel García Moreno,
Florian Müllner, Georges Basile Stavracas Neto, Jeremias Ortega, Ray Strode,
Emre Uyguroglu, Daniel van Vugt
Michael Catanzaro, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
Sebastian Keller, Robert Mader, Andre Moreira Magalhaes, Florian Müllner,
Jeremias Ortega, Ray Strode, Emre Uyguroglu
Translators:
Tim Sabsch [de], Boyuan Yang [zh_CN], Fabio Tomat [fur],
Efstathios Iosifidis [el], Rafael Fontenelle [pt_BR], Yuri Chornoivan [uk],
Daniel Șerbănescu [ro], Jordi Mas [ca], Daniel Mustieles [es],
Emin Tufan Çetin [tr], Asier Sarasua Garmendia [eu]
Boyuan Yang [zh_CN], Rafael Fontenelle [pt_BR]
3.37.3
3.36.4
======
* Refactor and clean up window picker
[Jonas D., Florian; !1297, !1298, !1305, !1345, !1353]
* Move calendar events out of notifications list [Florian; !1282]
* Refine app folder dialogs [Georges; !1301]
* Hide switch-user button on lock screen if unsupported [Chingkai; #2687]
* Refactor and clean up app picker pagination [Georges; !1271]
* Add API to retrieve specified mimetypes from clipboards [Carlos; !1321]
* Support prepending workspace with horizontal layouts [Florian; #2916]
* Update microphone icon on input volume changes [fludixx; #2902]
* Cache labels on GPU [Daniel; !1329]
* Fix regressions in redesigned modal dialogs [Florian, Jonas D.; #2491, !1336]
* Use GIcon for all application icons [Florian; !1342]
* Support pre-authenticated logins in vmware environments [yun341; #1983]
* Improve world clocks styling [PrOF-kk; #2825]
* Improve calendar-server performance [Florian, Milan; #1875]
* Fix regressions in redesigned modal dialogs [Florian, Jonas; #2491, !1336]
* Better support sandboxed apps with multiple .desktop files [Florian; #219]
* Fix on-screen keyboard size in portrait orientation [Florian; #2349]
* Plugged leaks [Sebastian, Daniel, Florian; !1306, !1319, !1341]
* Misc. bug fixes and cleanups [Jonas D., Georges, Marco, Florian, Sebastian,
MOZGIII, Daniel, Mariana, Jonas Å.; !1296, !1295, #2643, !1300, !1309,
!1119, #2901, !1313, !1251, !1285, !1307, !1318, !1310, !1320, !1327, !1315,
!1289, !1331, !1332, !1333, !1334, !1340, !1287, !1308, !1346, !1299, !1343,
!1351, !1352, !1322]
* Support scrolling anywhere in slider menu items [Peter; #2795]
* Fixed crash [Marco; #2643]
* Plugged leaks [Sebastian, Florian; !1306, !1341]
* Misc. bug fixes and cleanups [Jonas, Daniel, Florian, Sebastian, MOZGIII,
Koki; !1119, !1289, !1331, !1192, !1340, !1327, !1279]
Contributors:
Marco Trevisan (Treviño), Chingkai, Jonas Dreßler, Carlos Garnacho,
Sebastian Keller, MOZGIII, Florian Müllner, Georges Basile Stavracas Neto,
Mariana Picolo, Daniel van Vugt, fludixx, yun341, Jonas Ådahl
Marco Trevisan (Treviño), Chingkai, Milan Crha, Jonas Dreßler, Koki Fukuda,
Sebastian Keller, MOZGIII, Robert Mader, Florian Müllner, PrOF-kk,
Peter Simonyi, Daniel van Vugt
Translators:
Daniel Mustieles [es], Boyuan Yang [zh_CN], Yuri Chornoivan [uk],
Jordi Mas [ca], sicklylife [ja], Emin Tufan Çetin [tr],
Baurzhan Muftakhidinov [kk], Florentina Mușat [ro], Aurimas Černius [lt],
Rūdolfs Mazurs [lv]
Matej Urbančič [sl], sicklylife [ja], Fabio Tomat [fur],
Baurzhan Muftakhidinov [kk], Daniel Șerbănescu [ro]
3.37.2
3.36.3
======
* Add support for "PrefersNonDefaultGPU" desktop key [Bastien; !1226]
* Add gnome-shell-extension-prefs wrapper for compatibility [Florian; !1220]
* Fix distorted fallback icons in top bar [Florian; #2578]
* Lower timeout for scrolling in overview [Alexander; #2602]
* Only start systemd units when running under systemd
[Carlos, Florian; #2755, !1242, !1252]
* Fix "ghost" media controls [Bryan; #2776]
* Fix "ghost" media controls [Bryan; #2776]
* Fix zombie sockets from extensions downloader [Michael; #2774]
* Update world clocks offsets when timezone changes [Bryan; #2209]
* Support scrolling anywhere in slider menu items [Peter; #2795]
* Fix "Do Not Disturb" setting getting reset on startup [Florian; #2804]
* Only allow updates for extensions that aren't cached [Florian; !1248]
* Fix matching notifications by PID [Florian; #2592]
* Indicate extension errors in Extensions app [Florian; #2337]
* Add clipboard API for querying supported mimetypes [Andy; #2819]
* Add preview to color picker [Florian; #451]
* Improve world clocks styling [PrOF-kk; #2825]
* Remove Frequent view from app picker [Georges; !880]
* Fix pad OSD glitches [Carlos; !1290]
* Expose actor tree in looking glass [Georges; !1292]
* Fixed crashes [Jonas D., Florian; #2709, #2757]
* Misc. bug fixes and cleanups [Florian, AsciiWolf, Michael, Piotr, Ting-Wei,
Amr, Alexander, Bryan, Georges, Jonas D., Andy, Björn, Koki, Carlos; !1229,
!1231, !1233, !1235, #2578, #2735, #2751, #2602, #2777, !1249, #2796, !1268,
!1269, !1265, !1245, !1273, #2816, !1274, !1263, !1188, !1276, #2652, !1277,
!1281, #2286, !1267, !1286, !1279, !1288, !1293, !1294, !1291]
* Fix matching notifications by PID [Florian; #2592]
* Only allow updates for extensions that aren't cached [Florian; !1248]
* Fixed crashes [Jonas, Florian; #2709, #2757]
* Misc. bug fixes and cleanups [Michael, Piotr, Philip, Florian, Amr,
AsciiWolf; !1233, !1205, !1229, #2751, !1232, #2796, !1249, !1263,
!1277, #2286, !1288, !1291]
Contributors:
AsciiWolf, Michael Catanzaro, Björn Daase, Jonas Dreßler, Bryan Dunsmore,
Koki Fukuda, Carlos Garnacho, Andy Holmes, Amr Ibrahim, Soslan Khubulov,
Ting-Wei Lan, Michael Lass, Alexander Mikhaylenko, Florian Müllner,
Georges Basile Stavracas Neto, Bastien Nocera, PrOF-kk, Peter Simonyi
AsciiWolf, Michael Catanzaro, Philip Chimento, Jonas Dreßler, Bryan Dunsmore,
Carlos Garnacho, Amr Ibrahim, Michael Lass, Alexander Mikhaylenko,
Florian Müllner
Translators:
Fabio Tomat [fur], Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk],
Dušan Kazik [sk], Piotr Drąg [pl], Soslan Khubulov [os],
Daniel Mustieles [es], Nathan Follens [nl], Bruce Cowan [en_GB],
Florentina Mușat [ro], Milo Casagrande [it], Anders Jonsson [sv],
Charles Monzat [fr], Danial Behzadi [fa], sicklylife [ja], Kukuh Syafaat [id],
Jordi Mas [ca], Emin Tufan Çetin [tr], Jiri Grönroos [fi], Марко Костић [sr],
Christian Kirbach [de], Changwoo Ryu [ko], Matej Urbančič [sl]
Fabio Tomat [fur], Cheng-Chia Tseng [zh_TW], Dušan Kazik [sk],
Piotr Drąg [pl], Daniel Mustieles [es], Nathan Follens [nl],
Bruce Cowan [en_GB], Florentina Mușat [ro], Yuri Chornoivan [uk],
Milo Casagrande [it], Anders Jonsson [sv], Charles Monzat [fr],
Daniel Șerbănescu [ro], sicklylife [ja], Kukuh Syafaat [id],
Emin Tufan Çetin [tr], Jiri Grönroos [fi], Марко Костић [sr],
Christian Kirbach [de], Changwoo Ryu [ko], Aurimas Černius [lt],
Matej Urbančič [sl]
3.37.1
3.36.2
======
* Improve bluetooth submenu title [Mariana; #2340]
* Add openPrefs() convenience method for extensions [Florian; !1163]
* Bring back support for empty StIcons [Andre, Jonas D.; !1173, !1178]
* Wake up screen when unlocking programmatically [Florian; !1158]
* Improve extensions tool error reporting [Florian; #2391]
* Improve handling of scale-factor changes [Georges; !1176]
* Tone down weekend days with events in calendar [Jakub; #2588]
* Fix showing bluetooth submenu when devices were set up [Florian; !1174]
* Add support for parental controls filtering [Philip W.; !465]
* Provide alternative extension templates [Florian; !812]
* Improve weather section's empty state [Mariana; #2179]
* Fix translations of folder names [Florian; #2623]
* Drop Tweener [Jonas Å.; !1200]
* Match ASCII alternatives of system actions [Will; #2688]
* Fix delay on lock screen after entering wrong password [Jonas D.; #2655]
* Use globalThis instead of window [Andy; #2322]
* Inhibit remote access when disabled by session mode [Jonas Å.; !1210]
* Improve calendar-server performance [Milan; #1875]
* Add gnome-shell-extension-prefs wrapper for compatibility [Florian; !1220]
* Match ASCII alternatives of system actions [Will; #2688]
* Tone down weekend days with events in calendar [Jakub; #2588]
* Fix area screenshots on multi-monitor systems [Jonas Å; !1224]
* Fix stuck lock screen after unlock [Jonas D., Florian; #2446]
* Fixed crashes [Jonas D., Florian, Carlos; #2584, #2625, !1223, !1218]
* Misc. bug fixes and cleanups [Florian, Jonas Å., Marco, Andre, Georges,
Jonas D., Jan, Philip Ch.,, Xiaoguang, Will, Jordan, Matthew, qarmin;
!1126, !1155, !1156, !1165, !1168, !1169, #2551, #2563, !1172, !1175, !1179,
!1160, #2562, #2578, !1184, #2559, !1186, #2607, !1191, !1194, !1199, !1203,
#2649, #2628, !1205, !1206, !1208, !1207, !1211, !1214, !1213, !1192, !1217,
!1219, #1615, #2691, !1094, !1177]
* Misc. bug fixes and cleanups [Jonas Å., Marco, Andre, Florian, Jonas D.;
!1155, !1156, !1169, !1168, #2551, #2563, !1172, !1179, !1160, #2562, #2578,
!1203, #2649, #2628, #2691, #1615, #2607, !1228]
Contributors:
Marco Trevisan (Treviño), Philip Chimento, Milan Crha, Jonas Dreßler,
Carlos Garnacho, Andy Holmes, Matthew Leeds, Andre Moreira Magalhaes,
Florian Müllner, Georges Basile Stavracas Neto, Jordan Petridis,
Mariana Picolo, Jakub Steiner, Will Thompson, Jan Tojnar, Xiaoguang Wang,
Philip Withnall, qarmin, Jonas Ådahl
Marco Trevisan (Treviño), Jonas Dreßler, Carlos Garnacho,
Andre Moreira Magalhaes, Florian Müllner, Georges Basile Stavracas Neto,
Jakub Steiner, Will Thompson, Jonas Ådahl
Translators:
Fabio Tomat [fur], Cheng-Chia Tseng [zh_TW], Danial Behzadi [fa],
Jiri Grönroos [fi], Ibai Oihanguren Sala [eu], Марко Костић [sr],
Rūdolfs Mazurs [lv], Yuri Chornoivan [uk], Carmen Bianca BAKKER [eo],
Dingzhong Chen [zh_CN], Rafael Fontenelle [pt_BR], Petr Kovář [cs],
Asier Sarasua Garmendia [eu], Daniel Mustieles [es], Emin Tufan Çetin [tr]
Rūdolfs Mazurs [lv], Rafael Fontenelle [pt_BR], Petr Kovář [cs],
Daniel Rusek [cs]
3.36.1
======
* Improve app folders [Jonas D.; !1011]
* Fix launching ibus daemon [Alynx; !1080]
* Do not shutdown ibus/xsettings on X11 compositor restart [Carlos; #2329]
* Hide hint text in entries when preedit is used [Carlos; !1084]
* Do not load app infos on main thread [Christian; #2282]
* Don't expose FDO Notifications interface on main bus name [Florian; !547]
* Fix icon of mobile broadband connections [Cosimo, Reik; !1097, !1105]
* Fix high-contrast/symbolic icon mix-up [Florian; #2414]
* Don't ellipsize times in world clock [Florian; !1090]
* Only check for extension updates if there are any extensions [Florian; !1100]
* Fix crash when trying to update removed extensions [Florian; #2343]
* Make Extensions app available as flatpak [Florian; !1081, !1106, !1087, !1133]
* Display fractional timezones as hours:minutes [Jonas D.; #2438]
* Fix assigning pad keybindings [Carlos; #2451]
* Handle embedded newlines in lock screen notifications [Florian; #2463]
* Fix OSK layout fallback for unsupported variants [Florian; #2471]
* Do not apply text color to color glyphs (emojis) [Carlos; #850]
* Check "Install pending software updates" by default [Michael; #2427]
* Do not warn about missing GDM on each login [Florian; #2432]
* Fix telepathy chat notifications [Marco; !1112]
* Fix offline updates support in end session dialog [Michael; #2276]
* Fix activating notifications by keyboard [Florian; #2319]
* Remove handling of 'blacklisted' extensions [Florian; !1132]
* Only update extensions if Extensions app is installed [Florian; #2346]
* Improve Norwegian on-screen-keyboard layout [Bjørn; !1073]
* Fix IM support for deleting surrounding text [Takao; !477]
* Fix blur effect with fractional scaling [Jonas D.; !1000]
* Use better location name in weather section [Florian; #2468]
* Fix glitch in sound feedback on volume changes [Florian; !1147]
* Fix on-screen keyboard regressions [Jonas D.; !1142]
* Improve screen-reader support [Luke; #2508, #2517]
* Fix password entry resize on login/lock screen [Florian; #2423]
* Fix crash when opening app picker [Jonas Å.; !1154]
* Misc. bug fixes and cleanups [Florian, Sebastian, Jan, Daniel, Philip, Mario,
Ray, Marco, Jonas D., Carlos, Georges; #2298, #2305, !1078, !1077, #2334,
#2381, !1093, !1098, #2386, !1108, !1109, !1114, !1076, !1072, !1115, !1088,
!1101, #2467, !1121, !1122, #2476, !1123, !1117, !1129, !1113, !1102, !1127,
#2238, !1131, !1135, !1136, !849, #2504, #2371, !1146, !1141, #2510, !1150]
Contributors:
Marco Trevisan (Treviño), Michael Catanzaro, Cosimo Cecchi, Jonas Dreßler,
Takao Fujiwara, Carlos Garnacho, Christian Hergert, Sebastian Keller,
Reik Keutterling, Bjørn Lie, Florian Müllner, Jwtiyar Nariman,
Georges Basile Stavracas Neto, Mario Sanchez Prada, Ray Strode, Jan Tojnar,
Daniel van Vugt, Philip Withnall, Luke Yelavich, Alynx Zhou, Jonas Ådahl
Translators:
Марко Костић [sr], Jordi Mas [ca], sicklylife [ja], Marek Černocký [cs],
Daniel Rusek [cs], Kjartan Maraas [nb], Tim Sabsch [de], Stas Solovey [ru],
Peter Mráz [sk], Rafael Fontenelle [pt_BR], Piotr Drąg [pl],
Milo Casagrande [it], Anders Jonsson [sv], Yuri Chornoivan [uk],
Kukuh Syafaat [id], Guillaume Bernard [fr], Daniel Mustieles [es],
Danial Behzadi [fa], Goran Vidović [hr], Yosef Or Boczko [he],
Emin Tufan Çetin [tr], Wolfgang Stöggl [de], Ibai Oihanguren Sala [eu],
Jwtiyar Nariman [ckb], Aurimas Černius [lt]
3.36.0
======

View File

@ -20,12 +20,6 @@
<method name="ListSessions">
<arg name="sessions" type="a(susso)" direction="out"/>
</method>
<method name="CanRebootToBootLoaderMenu">
<arg type="s" direction="out"/>
</method>
<method name="SetRebootToBootLoaderMenu">
<arg type="t" direction="in"/>
</method>
<signal name="PrepareForSleep">
<arg type="b" direction="out"/>
</signal>

View File

@ -1,191 +0,0 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Mutter.ScreenCast:
@short_description: Screen cast interface
This API is private and not intended to be used outside of the integrated
system that uses libmutter. No compatibility between versions are
promised.
-->
<interface name="org.gnome.Mutter.ScreenCast">
<!--
CreateSession:
@properties: Properties
@session_path: Path to the new session object
* "remote-desktop-session-id" (s): The ID of a remote desktop session.
Remote desktop driven screen casts
are started and stopped by the remote
desktop session.
* "disable-animations" (b): Set to "true" if the screen cast application
would prefer animations to be globally
disabled, while the session is running. Default
is "false". Available since version 3.
-->
<method name="CreateSession">
<arg name="properties" type="a{sv}" direction="in" />
<arg name="session_path" type="o" direction="out" />
</method>
<!--
Version:
@short_description: API version
-->
<property name="Version" type="i" access="read" />
</interface>
<!--
org.gnome.Mutter.ScreenCast.Session:
@short_description: Screen cast session
-->
<interface name="org.gnome.Mutter.ScreenCast.Session">
<!--
Start:
Start the screen cast session
-->
<method name="Start" />
<!--
Stop:
Stop the screen cast session
-->
<method name="Stop" />
<!--
Closed:
The session has closed.
-->
<signal name="Closed" />
<!--
RecordMonitor:
@connector: Connector of the monitor to record
@properties: Properties
@stream_path: Path to the new stream object
Record a single monitor.
Available @properties include:
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
Available since API version 2.
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
Available cursor mode values:
0: hidden - cursor is not included in the stream
1: embedded - cursor is included in the framebuffer
2: metadata - cursor is included as metadata in the PipeWire stream
-->
<method name="RecordMonitor">
<arg name="connector" type="s" direction="in" />
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
<!--
RecordWindow:
@properties: Properties used determining what window to select
@stream_path: Path to the new stream object
Supported since API version 2.
Record a single window. The cursor will not be included.
Available @properties include:
* "window-id" (t): Id of the window to record.
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see RecordMonitor).
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
-->
<method name="RecordWindow">
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
<!--
RecordArea:
@x: X position of the recorded area
@y: Y position of the recorded area
@width: width of the recorded area
@height: height of the recorded area
@properties: Properties
@stream_path: Path to the new stream object
Record an area of the stage. The coordinates are in stage coordinates.
The size of the stream does not necessarily match the size of the
recorded area, and will depend on DPI scale of the affected monitors.
Available @properties include:
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
Available since API version 2.
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
Available cursor mode values:
0: hidden - cursor is not included in the stream
1: embedded - cursor is included in the framebuffer
2: metadata - cursor is included as metadata in the PipeWire stream
-->
<method name="RecordArea">
<arg name="x" type="i" direction="in" />
<arg name="y" type="i" direction="in" />
<arg name="width" type="i" direction="in" />
<arg name="height" type="i" direction="in" />
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
</interface>
<!--
org.gnome.Mutter.ScreenCast.Stream:
@short_description: Screen cast stream
-->
<interface name="org.gnome.Mutter.ScreenCast.Stream">
<!--
PipeWireStreamAdded:
@short_description: Pipewire stream added
A signal emitted when PipeWire stream for the screen cast stream has
been created. The @node_id corresponds to the PipeWire stream node.
-->
<signal name="PipeWireStreamAdded">
<annotation name="org.gtk.GDBus.C.Name" value="pipewire-stream-added"/>
<arg name="node_id" type="u" direction="out" />
</signal>
<!--
Parameters:
@short_description: Optional stream parameters
Available parameters include:
* "position" (ii): Position of the source of the stream in the
compositor coordinate space.
* "size" (ii): Size of the source of the stream in the compositor
coordinate space.
-->
<property name="Parameters" type="a{sv}" access="read" />
</interface>
</node>

View File

@ -70,14 +70,6 @@
-->
<property name="AnimationsEnabled" type="b" access="read"/>
<!--
ScreenSize:
@short_description: The size of the screen
Since: 3
-->
<property name="ScreenSize" type="(ii)" access="read"/>
<property name="version" type="u" access="read"/>
</interface>
</node>

View File

@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/org/gnome/shell/dbus-interfaces">
<file preprocess="xml-stripblanks">net.hadess.SensorProxy.xml</file>
<file preprocess="xml-stripblanks">net.hadess.SwitcherooControl.xml</file>
<file preprocess="xml-stripblanks">org.freedesktop.Application.xml</file>
<file preprocess="xml-stripblanks">org.freedesktop.bolt1.Device.xml</file>
<file preprocess="xml-stripblanks">org.freedesktop.bolt1.Manager.xml</file>
@ -28,7 +29,6 @@
<file preprocess="xml-stripblanks">org.freedesktop.UPower.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Magnifier.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Magnifier.ZoomRegion.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Mutter.ScreenCast.xml</file>
<file preprocess="xml-stripblanks">org.gnome.ScreenSaver.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SessionManager.EndSessionDialog.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SessionManager.Inhibitor.xml</file>

View File

@ -6,7 +6,6 @@
<file>checkbox-off-focused.svg</file>
<file>checkbox-off.svg</file>
<file>checkbox.svg</file>
<file alias="icons/scalable/actions/color-pick.svg">color-pick.svg</file>
<file>dash-placeholder.svg</file>
<file>gnome-shell.css</file>
<file>gnome-shell-high-contrast.css</file>

View File

@ -1,7 +1,7 @@
[Unit]
Description=GNOME Shell on Wayland
# On wayland, force a session shutdown
OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-shutdown.target
OnFailure=gnome-shell-disable-extensions.service gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
CollectMode=inactive-or-failed
RefuseManualStart=on
@ -13,21 +13,18 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
# The units already conflict because they use the same BusName
#Conflicts=gnome-shell-x11.service
[Service]
Slice=session.slice
Type=notify
# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I
# with systemd >= 245. Also, the current solution is kind of painful
# as systemd had a bug where it retries the condition.
# Only start if the template instance matches the session type.
ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
ExecStart=@bindir@/gnome-shell
# Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1
# 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
SuccessExitStatus=1
# On wayland we cannot restart
Restart=no
# Kill any stubborn child processes after this long

View File

@ -6,5 +6,5 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
Wants=org.gnome.Shell@wayland.service
Wants=org.gnome.Shell@x11.service
Requires=gnome-shell-wayland.service
After=gnome-shell-wayland.service

View File

@ -1,7 +1,7 @@
[Unit]
Description=GNOME Shell on X11
# On X11, try to show the GNOME Session Failed screen
OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-failed.target
OnFailure=gnome-shell-disable-extensions.service gnome-session-failed.target
OnFailureJobMode=replace
CollectMode=inactive-or-failed
RefuseManualStart=on
@ -13,24 +13,18 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
# The units already conflict because they use the same BusName
#Conflicts=gnome-shell-wayland.service
# Limit startup frequency more than the default
StartLimitIntervalSec=15s
StartLimitBurst=3
[Service]
Slice=session.slice
Type=notify
# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I
# with systemd >= 245. Also, the current solution is kind of painful
# as systemd had a bug where it retries the condition.
# Only start if the template instance matches the session type.
ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
ExecStart=@bindir@/gnome-shell
# Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1
# On X11 we do not need to unset any variables
# On X11 we want to restart on-success (Alt+F2 + r) and on-failure.
Restart=always
# Do not wait before restarting the shell

View File

@ -0,0 +1,10 @@
[Unit]
Description=GNOME Shell on X11
DefaultDependencies=no
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
Requires=gnome-shell-x11.service
After=gnome-shell-x11.service

View File

@ -101,21 +101,22 @@ if have_systemd
unitconf.set('bindir', bindir)
configure_file(
input: 'org.gnome.Shell@x11.service.in',
output: 'org.gnome.Shell@x11.service',
input: 'gnome-shell-x11.service.in',
output: 'gnome-shell-x11.service',
configuration: unitconf,
install_dir: systemduserunitdir
)
configure_file(
input: 'org.gnome.Shell@wayland.service.in',
output: 'org.gnome.Shell@wayland.service',
input: 'gnome-shell-wayland.service.in',
output: 'gnome-shell-wayland.service',
configuration: unitconf,
install_dir: systemduserunitdir
)
units = files('org.gnome.Shell.target',
'org.gnome.Shell-disable-extensions.service')
units = files('gnome-shell-x11.target',
'gnome-shell-wayland.target',
'gnome-shell-disable-extensions.service')
install_data(units, install_dir: systemduserunitdir)
endif

View File

@ -109,17 +109,6 @@
the shell.
</description>
</key>
<key name="app-picker-layout" type="aa{sv}">
<default>[]</default>
<summary>Layout of the app picker</summary>
<description>
Layout of the app picker. Each entry in the array is a page. Pages are
stored in the order they appear in GNOME Shell. Each page contains an
“application id” → 'data' pair. Currently, the following values are
stored as 'data':
• “position”: the position of the application icon in the page
</description>
</key>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
</schema>

View File

@ -1,94 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="5.4116011mm"
height="5.1374583mm"
viewBox="0 0 5.4116011 5.1374583"
version="1.1"
id="svg5595"
inkscape:version="0.92.4 (unknown)"
sodipodi:docname="color-pick.svg">
<defs
id="defs5589">
<filter
inkscape:collect="always"
x="-0.10291173"
width="1.2058235"
y="-0.065432459"
height="1.1308649"
id="filter5601"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.610872"
id="feGaussianBlur5603" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="39.387731"
inkscape:cy="12.554326"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5592">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-103.12753,-146.26461)">
<circle
r="8.4810486"
cy="9.82623"
cx="10.226647"
id="circle7584"
style="color:#000000;display:inline;overflow:visible;opacity:0.6;vector-effect:none;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter:url(#filter5601)"
transform="matrix(0.26458333,0,0,0.26458333,103.12753,146.26461)" />
<path
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.26399338;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 108.07728,148.64122 c 0,1.2393 -1.00465,2.24394 -2.24395,2.24394 -1.23929,0 -2.24716,-1.00465 -2.25221,-2.24394 l -0.009,-2.24458 2.26136,6.4e-4 c 1.2393,3.4e-4 2.24395,1.00464 2.24395,2.24394 z"
id="path7523-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssscss" />
<circle
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#50dbb5;fill-opacity:1;stroke:none;stroke-width:0.36885914;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="path7482-1"
cx="105.83707"
cy="148.64352"
r="1.844296" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,15 +1,21 @@
/* App Grid */
$app_icon_size: 96px;
$app_icon_padding: 24px;
// app icons
.icon-grid {
row-spacing: $base_spacing * 6;
column-spacing: $base_spacing * 6;
max-row-spacing: $base_spacing * 12;
max-column-spacing: $base_spacing * 12;
-shell-grid-horizontal-item-size: $app_icon_size + $app_icon_padding * 2;
-shell-grid-vertical-item-size: $app_icon_size + $app_icon_padding * 2;
spacing: $base_spacing * 4;
.overview-icon {
icon-size: $app_icon_size;
}
}
//.app-display { spacing: 20px; }
/* App Icons */
$app_grid_fg_color: #fff;
@ -38,8 +44,8 @@ $app_grid_fg_color: #fff;
.app-folder-dialog {
border-radius: $modal_radius * 1.5;
border: 1px solid $osd_outer_borders_color;
spacing: 12px;
background-color: transparentize(darken($osd_bg_color,10%), 0.05);
padding: 12px;
& .folder-name-container {
padding: 24px 36px 0;
@ -48,7 +54,7 @@ $app_grid_fg_color: #fff;
& .folder-name-label,
& .folder-name-entry {
font-size: 18pt;
font-weight: 800;
font-weight: bold;
}
& .folder-name-entry { width: 300px }
@ -67,24 +73,11 @@ $app_grid_fg_color: #fff;
& > StIcon { icon-size: 16px }
}
}
& .icon-grid {
row-spacing: $base_spacing * 2;
column-spacing: $base_spacing * 5;
}
& .page-indicators {
margin-bottom: 18px;
.page-indicator {
padding: 15px 12px;
}
}
}
.app-folder-dialog-container {
padding: 12px;
width: 620px;
height: 620px;
width: 800px;
height: 600px;
}
.app-folder-icon {
@ -130,11 +123,15 @@ $app_grid_fg_color: #fff;
}
// Some hacks I don't even know
.all-apps {
.all-apps,
.frequent-apps > StBoxLayout {
// horizontal padding to make sure scrollbars or dash don't overlap content
padding: 0px 88px 10px 88px;
}
// Label when no frequent apps
.no-frequent-applications-label { @extend %status_text; }
// shutdown and other actions in the grid
.system-action-icon {
background-color: rgba(0,0,0,0.8);
@ -142,3 +139,44 @@ $app_grid_fg_color: #fff;
border-radius: 99px;
icon-size: $app_icon_size * 0.5;
}
/* Frequent | All toggle */
// container
.app-view-controls {
padding-bottom: 32px;
}
// buttons
.app-view-control {
padding: 4px 32px;
margin: 0 4px;
&, &:hover, &:checked {
@include button(undecorated);
color: darken($osd_fg_color, 25%);
}
&:hover {
color: $osd_fg_color;
box-shadow: inset 0 -2px darken($osd_fg_color, 25%);
}
&:active {
box-shadow: inset 0 -2px $osd_fg_color;
}
&:checked {
color: $osd_fg_color;
box-shadow: inset 0 -2px $selected_bg_color;
}
&:first-child {
border-right-width: 0;
border-radius: 0;
}
&:last-child {
border-radius: 0;
}
}

View File

@ -177,32 +177,6 @@
}
}
/* Events */
.events-button {
@include notification_bubble;
padding: $base_padding * 2;
.events-box {
spacing: $base_spacing;
}
.events-list {
spacing: 2 * $base_spacing;
}
.events-title {
color: desaturate(darken($fg_color,40%), 10%);
font-weight: bold;
margin-bottom: $base_margin;
}
.event-time {
color: darken($fg_color,20%);
font-feature-settings: "tnum";
@include fontsize($base_font_size - 1);
}
}
/* World clocks */
.world-clocks-button {
@include notification_bubble;

View File

@ -1,7 +1,5 @@
/* Looking Glass */
$text_fg_color: #ccc;
// Dialog
#LookingGlassDialog {
background-color: $osd_bg_color;
@ -54,11 +52,6 @@ $text_fg_color: #ccc;
&:hover { color: lighten($link_color, 10%); }
&:active { color: darken($link_color, 10%); }
}
.actor-link {
color: $text_fg_color;
&:hover { color: lighten($text_fg_color, 20%); }
&:active { color: darken($text_fg_color, 20%); }
}
}
.lg-completions-text {

View File

@ -54,10 +54,6 @@
@extend %status_text;
}
.grid-search-results {
spacing: $base_spacing * 6;
}
// Search results with icons
.grid-search-result {
@extend %app-well-app;

View File

@ -1,20 +1,19 @@
/* Window Picker */
$window_picker_spacing: $base_spacing; // 6px
$window_picker_padding: $base_padding * 2; // 12px
$window_picker_spacing: $base_spacing * 2; // 16px
$window_picker_padding: $base_padding * 2; // 16px
$window_thumbnail_border_color:transparentize($selected_fg_color, 0.65);
$window_close_button_size: 24px;
$window_close_button_padding: 3px;
$window_clone_border_size: 6px;
// Window picker
.window-picker {
// Space between window thumbnails
spacing: $window_picker_spacing;
-horizontal-spacing: $window_picker_spacing;
-vertical-spacing: $window_picker_spacing;
// Padding for container around window thumbnails
padding: $window_picker_padding;
@ -23,7 +22,7 @@ $window_clone_border_size: 6px;
// Borders on window thumbnails
.window-clone-border {
border-width: $window_clone_border_size;
border-width: 6px;
border-style: solid;
border-color: $window_thumbnail_border_color;
border-radius: $base_border_radius + 2;
@ -55,6 +54,8 @@ $window_clone_border_size: 6px;
width: $window_close_button_size;
box-shadow: -1px 1px 5px 0px rgba(0,0,0,0.5);
-shell-close-overlap: $window_close_button_size * 0.5;
&:hover {
background-color: lighten($selected_bg_color, 5%);
}

View File

@ -3,8 +3,13 @@ private_headers = [
'gactionobservable.h',
'gactionobserver.h',
'shell-network-agent.h',
'shell-recorder-src.h'
]
if not enable_recorder
private_headers += 'shell-recorder.h'
endif
exclude_directories = [
'calendar-server',
'hotplug-sniffer',

View File

@ -25,8 +25,6 @@ var ServiceImplementation = class {
// subclasses may override this to disable automatic shutdown
this._autoShutdown = true;
this._queueShutdownCheck();
}
// subclasses may override this to own additional names

View File

@ -8,12 +8,6 @@ dbus_services = {
'org.gnome.Shell.Notifications': 'notifications',
}
if enable_recorder
dbus_services += {
'org.gnome.Shell.Screencast': 'screencast',
}
endif
config_dir = '@0@/..'.format(meson.current_build_dir())
foreach service, dir : dbus_services

View File

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

View File

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

View File

@ -1,458 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ScreencastService */
const { Gio, GLib, Gst } = imports.gi;
const { loadInterfaceXML, loadSubInterfaceXML } = imports.misc.fileUtils;
const { ServiceImplementation } = imports.dbusService;
const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
const IntrospectIface = loadInterfaceXML('org.gnome.Shell.Introspect');
const IntrospectProxy = Gio.DBusProxy.makeProxyWrapper(IntrospectIface);
const ScreenCastIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast', 'org.gnome.Mutter.ScreenCast');
const ScreenCastSessionIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast.Session', 'org.gnome.Mutter.ScreenCast');
const ScreenCastStreamIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast.Stream', 'org.gnome.Mutter.ScreenCast');
const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastIface);
const ScreenCastSessionProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastSessionIface);
const ScreenCastStreamProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastStreamIface);
const DEFAULT_PIPELINE = 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux';
const DEFAULT_FRAMERATE = 30;
const DEFAULT_DRAW_CURSOR = true;
const PipelineState = {
INIT: 0,
PLAYING: 1,
FLUSHING: 2,
STOPPED: 3,
};
const SessionState = {
INIT: 0,
ACTIVE: 1,
STOPPED: 2,
};
var Recorder = class {
constructor(sessionPath, x, y, width, height, filePath, options,
invocation,
onErrorCallback) {
this._startInvocation = invocation;
this._dbusConnection = invocation.get_connection();
this._onErrorCallback = onErrorCallback;
this._stopInvocation = null;
this._pipelineIsPlaying = false;
this._sessionIsActive = false;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._filePath = filePath;
this._pipelineString = DEFAULT_PIPELINE;
this._framerate = DEFAULT_FRAMERATE;
this._drawCursor = DEFAULT_DRAW_CURSOR;
this._applyOptions(options);
this._watchSender(invocation.get_sender());
this._initSession(sessionPath);
}
_applyOptions(options) {
for (const option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'] !== undefined)
this._pipelineString = options['pipeline'];
if (options['framerate'] !== undefined)
this._framerate = options['framerate'];
if ('draw-cursor' in options)
this._drawCursor = options['draw-cursor'];
}
_watchSender(sender) {
this._nameWatchId = this._dbusConnection.watch_name(
sender,
Gio.BusNameWatcherFlags.NONE,
null,
this._senderVanished.bind(this));
}
_unwatchSender() {
if (this._nameWatchId !== 0) {
this._dbusConnection.unwatch_name(this._nameWatchId);
this._nameWatchId = 0;
}
}
_senderVanished() {
this._unwatchSender();
this.stopRecording(null);
}
_notifyStopped() {
this._unwatchSender();
if (this._onStartedCallback)
this._onStartedCallback(this, false);
else if (this._onStoppedCallback)
this._onStoppedCallback(this);
else
this._onErrorCallback(this);
}
_onSessionClosed() {
switch (this._pipelineState) {
case PipelineState.STOPPED:
break;
default:
this._pipeline.set_state(Gst.State.NULL);
log(`Unexpected pipeline state: ${this._pipelineState}`);
break;
}
this._notifyStopped();
}
_initSession(sessionPath) {
this._sessionProxy = new ScreenCastSessionProxy(Gio.DBus.session,
'org.gnome.Mutter.ScreenCast',
sessionPath);
this._sessionProxy.connectSignal('Closed', this._onSessionClosed.bind(this));
}
_startPipeline(nodeId) {
this._ensurePipeline(nodeId);
const bus = this._pipeline.get_bus();
bus.add_watch(bus, this._onBusMessage.bind(this));
this._pipeline.set_state(Gst.State.PLAYING);
this._pipelineState = PipelineState.PLAYING;
this._onStartedCallback(this, true);
this._onStartedCallback = null;
}
startRecording(onStartedCallback) {
this._onStartedCallback = onStartedCallback;
const [streamPath] = this._sessionProxy.RecordAreaSync(
this._x, this._y,
this._width, this._height,
{
'is-recording': GLib.Variant.new('b', true),
'cursor-mode': GLib.Variant.new('u', this._drawCursor ? 1 : 0),
});
this._streamProxy = new ScreenCastStreamProxy(Gio.DBus.session,
'org.gnome.ScreenCast.Stream',
streamPath);
this._streamProxy.connectSignal('PipeWireStreamAdded',
(proxy, sender, params) => {
const [nodeId] = params;
this._startPipeline(nodeId);
});
this._sessionProxy.StartSync();
this._sessionState = SessionState.ACTIVE;
}
stopRecording(onStoppedCallback) {
this._pipelineState = PipelineState.FLUSHING;
this._onStoppedCallback = onStoppedCallback;
this._pipeline.send_event(Gst.Event.new_eos());
}
_stopSession() {
this._sessionProxy.StopSync();
this._sessionState = SessionState.STOPPED;
}
_onBusMessage(bus, message, _) {
switch (message.type) {
case Gst.MessageType.EOS:
this._pipeline.set_state(Gst.State.NULL);
switch (this._pipelineState) {
case PipelineState.FLUSHING:
this._pipelineState = PipelineState.STOPPED;
break;
default:
break;
}
switch (this._sessionState) {
case SessionState.ACTIVE:
this._stopSession();
break;
case SessionState.STOPPED:
this._notifyStopped();
break;
default:
break;
}
break;
default:
break;
}
return true;
}
_substituteThreadCount(pipelineDescr) {
const numProcessors = GLib.get_num_processors();
const numThreads = Math.min(Math.max(1, numProcessors), 64);
return pipelineDescr.replace(/%T/, numThreads);
}
_ensurePipeline(nodeId) {
const framerate = this._framerate;
let fullPipeline = `
pipewiresrc path=${nodeId}
do-timestamp=true
keepalive-time=1000
resend-last=true !
video/x-raw,max-framerate=${framerate}/1 !
videoconvert !
${this._pipelineString} !
filesink location="${this._filePath}"`;
fullPipeline = this._substituteThreadCount(fullPipeline);
this._pipeline = Gst.parse_launch_full(fullPipeline,
null,
Gst.ParseFlags.FATAL_ERRORS);
}
};
var ScreencastService = class extends ServiceImplementation {
constructor() {
super(ScreencastIface, '/org/gnome/Shell/Screencast');
Gst.init(null);
this._recorders = new Map();
this._senders = new Map();
this._lockdownSettings = new Gio.Settings({
schema_id: 'org.gnome.desktop.lockdown',
});
this._proxy = new ScreenCastProxy(Gio.DBus.session,
'org.gnome.Mutter.ScreenCast',
'/org/gnome/Mutter/ScreenCast');
this._introspectProxy = new IntrospectProxy(Gio.DBus.session,
'org.gnome.Shell.Introspect',
'/org/gnome/Shell/Introspect');
}
_removeRecorder(sender) {
this._recorders.delete(sender);
if (this._recorders.size === 0)
this.release();
}
_addRecorder(sender, recorder) {
this._recorders.set(sender, recorder);
if (this._recorders.size === 1)
this.hold();
}
_getAbsolutePath(filename) {
if (GLib.path_is_absolute(filename))
return filename;
let videoDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS);
if (!GLib.file_test(videoDir, GLib.FileTest.EXISTS))
videoDir = GLib.get_home_dir();
return GLib.build_filenamev([videoDir, filename]);
}
_generateFilePath(template) {
let filename = '';
let escape = false;
[...template].forEach(c => {
if (escape) {
switch (c) {
case '%':
filename += '%';
break;
case 'd': {
const datetime = GLib.DateTime.new_now_local();
const datestr = datetime.format('%0x');
const datestrEscaped = datestr.replace(/\//g, '-');
filename += datestrEscaped;
break;
}
case 't': {
const datetime = GLib.DateTime.new_now_local();
const datestr = datetime.format('%0X');
const datestrEscaped = datestr.replace(/\//g, ':');
filename += datestrEscaped;
break;
}
default:
log(`Warning: Unknown escape ${c}`);
}
escape = false;
} else if (c === '%') {
escape = true;
} else {
filename += c;
}
});
if (escape)
filename += '%';
return this._getAbsolutePath(filename);
}
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const sender = invocation.get_sender();
if (this._recorders.get(sender)) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const [sessionPath] = this._proxy.CreateSessionSync({});
const [fileTemplate, options] = params;
const [screenWidth, screenHeight] = this._introspectProxy.ScreenSize;
const filePath = this._generateFilePath(fileTemplate);
let recorder;
try {
recorder = new Recorder(
sessionPath,
0, 0,
screenWidth, screenHeight,
fileTemplate,
options,
invocation,
_recorder => this._removeRecorder(sender));
} catch (error) {
log(`Failed to create recorder: ${error.message}`);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
this._addRecorder(sender, recorder);
try {
recorder.startRecording(
(_, result) => {
if (result) {
returnValue = [true, filePath];
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
} else {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
});
} catch (error) {
log(`Failed to start recorder: ${error.message}`);
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
}
ScreencastAreaAsync(params, invocation) {
let returnValue = [false, ''];
if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const sender = invocation.get_sender();
if (this._recorders.get(sender)) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const [sessionPath] = this._proxy.CreateSessionSync({});
const [x, y, width, height, fileTemplate, options] = params;
const filePath = this._generateFilePath(fileTemplate);
let recorder;
try {
recorder = new Recorder(
sessionPath,
x, y,
width, height,
filePath,
options,
invocation,
_recorder => this._removeRecorder(sender));
} catch (error) {
log(`Failed to create recorder: ${error.message}`);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
this._addRecorder(sender, recorder);
try {
recorder.startRecording(
(_, result) => {
if (result) {
returnValue = [true, filePath];
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
} else {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
});
} catch (error) {
log(`Failed to start recorder: ${error.message}`);
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
}
StopScreencastAsync(params, invocation) {
const sender = invocation.get_sender();
const recorder = this._recorders.get(sender);
if (!recorder) {
invocation.return_value(GLib.Variant.new('(b)', [false]));
return;
}
recorder.stopRecording(() => {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(b)', [true]));
});
}
};

View File

@ -71,7 +71,7 @@ var AuthPrompt = GObject.registerClass({
this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
this._userVerifier.connect('reset', this._onReset.bind(this));
this._userVerifier.connect('smartcard-status-changed', this._onSmartcardStatusChanged.bind(this));
this._userVerifier.connect('credential-manager-authenticated', this._onCredentialManagerAuthenticated.bind(this));
this._userVerifier.connect('ovirt-user-authenticated', this._onOVirtUserAuthenticated.bind(this));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('destroy', this._onDestroy.bind(this));
@ -242,7 +242,7 @@ var AuthPrompt = GObject.registerClass({
this.emit('prompted');
}
_onCredentialManagerAuthenticated() {
_onOVirtUserAuthenticated() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
}

View File

@ -1,24 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported CredentialManager */
class CredentialManager {
constructor(service) {
this._token = null;
this._service = service;
this._authenticatedSignalId = null;
}
get token() {
return this._token;
}
set token(t) {
this._token = t;
if (this._token)
this.emit('user-authenticated', this._token);
}
get service() {
return this._service;
}
}

View File

@ -589,8 +589,8 @@ var LoginDialog = GObject.registerClass({
return actorBox;
}
vfunc_allocate(dialogBox) {
this.set_allocation(dialogBox);
vfunc_allocate(dialogBox, flags) {
this.set_allocation(dialogBox, flags);
let themeNode = this.get_theme_node();
dialogBox = themeNode.get_content_box(dialogBox);
@ -719,19 +719,19 @@ var LoginDialog = GObject.registerClass({
// Finally hand out the allocations
if (bannerAllocation)
this._bannerView.allocate(bannerAllocation);
this._bannerView.allocate(bannerAllocation, flags);
if (authPromptAllocation)
this._authPrompt.allocate(authPromptAllocation);
this._authPrompt.allocate(authPromptAllocation, flags);
if (userSelectionAllocation)
this._userSelectionBox.allocate(userSelectionAllocation);
this._userSelectionBox.allocate(userSelectionAllocation, flags);
if (logoAllocation)
this._logoBin.allocate(logoAllocation);
this._logoBin.allocate(logoAllocation, flags);
if (sessionMenuButtonAllocation)
this._sessionMenuButton.allocate(sessionMenuButtonAllocation);
this._sessionMenuButton.allocate(sessionMenuButtonAllocation, flags);
}
_ensureUserListLoaded() {
@ -810,13 +810,12 @@ var LoginDialog = GObject.registerClass({
return;
this._logoBin.destroy_all_children();
const resourceScale = this._logoBin.get_resource_scale();
if (this._logoFile) {
if (this._logoFile && this._logoBin.resource_scale > 0) {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile,
-1, -1,
scaleFactor,
resourceScale));
this._logoBin.resource_scale));
}
}

View File

@ -3,9 +3,6 @@
const Gio = imports.gi.Gio;
const Signals = imports.signals;
const Credential = imports.gdm.credentialManager;
var SERVICE_NAME = 'gdm-ovirtcred';
const OVirtCredentialsIface = `
<node>
@ -31,14 +28,30 @@ function OVirtCredentials() {
return self;
}
var OVirtCredentialsManager = class OVirtCredentialsManager extends Credential.CredentialManager {
var OVirtCredentialsManager = class {
constructor() {
super(SERVICE_NAME);
this._token = null;
this._credentials = new OVirtCredentials();
this._credentials.connectSignal('UserAuthenticated',
(proxy, sender, [token]) => {
this.token = token;
});
this._onUserAuthenticated.bind(this));
}
_onUserAuthenticated(proxy, sender, [token]) {
this._token = token;
this.emit('user-authenticated', token);
}
hasToken() {
return this._token != null;
}
getToken() {
return this._token;
}
resetToken() {
this._token = null;
}
};
Signals.addSignalMethods(OVirtCredentialsManager.prototype);

View File

@ -2,29 +2,20 @@
/* exported BANNER_MESSAGE_KEY, BANNER_MESSAGE_TEXT_KEY, LOGO_KEY,
DISABLE_USER_LIST_KEY, fadeInActor, fadeOutActor, cloneAndFadeOutActor */
const { Clutter, Gdm, Gio, GLib } = imports.gi;
const { Clutter, Gio, GLib } = imports.gi;
const Signals = imports.signals;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const OVirt = imports.gdm.oVirt;
const Vmware = imports.gdm.vmware;
const Main = imports.ui.main;
const Params = imports.misc.params;
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 FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
var SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
var OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
var FADE_ANIMATION_TIME = 160;
var CLONE_FADE_ANIMATION_TIME = 250;
@ -160,20 +151,13 @@ var ShellUserVerifier = class {
this._failCounter = 0;
this._credentialManagers = {};
this._credentialManagers[OVirt.SERVICE_NAME] = OVirt.getOVirtCredentialsManager();
this._credentialManagers[Vmware.SERVICE_NAME] = Vmware.getVmwareCredentialsManager();
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
for (let service in this._credentialManagers) {
if (this._credentialManagers[service].token) {
this._onCredentialManagerAuthenticated(this._credentialManagers[service],
this._credentialManagers[service].token);
}
if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._credentialManagers[service]._authenticatedSignalId =
this._credentialManagers[service].connect('user-authenticated',
this._onCredentialManagerAuthenticated.bind(this));
}
this._oVirtUserAuthenticatedId = this._oVirtCredentialsManager.connect('user-authenticated',
this._oVirtUserAuthenticated.bind(this));
}
begin(userName, hold) {
@ -184,12 +168,14 @@ var ShellUserVerifier = class {
this._checkForFingerprintReader();
// If possible, reauthenticate an already running session,
// so any session specific credentials get updated appropriately
if (userName)
this._openReauthenticationChannel(userName);
else
this._getUserVerifier();
if (userName) {
// If possible, reauthenticate an already running session,
// so any session specific credentials get updated appropriately
this._client.open_reauthentication_channel(userName, this._cancellable,
this._reauthenticationChannelOpened.bind(this));
} else {
this._client.get_user_verifier(this._cancellable, this._userVerifierGot.bind(this));
}
}
cancel() {
@ -229,11 +215,8 @@ var ShellUserVerifier = class {
this._smartcardManager.disconnect(this._smartcardRemovedId);
this._smartcardManager = null;
for (let service in this._credentialManagers) {
let credentialManager = this._credentialManagers[service];
credentialManager.disconnect(credentialManager._authenticatedSignalId);
credentialManager = null;
}
this._oVirtCredentialsManager.disconnect(this._oVirtUserAuthenticatedId);
this._oVirtCredentialsManager = null;
}
answerQuery(serviceName, answer) {
@ -321,9 +304,9 @@ var ShellUserVerifier = class {
});
}
_onCredentialManagerAuthenticated(credentialManager, _token) {
this._preemptingService = credentialManager.service;
this.emit('credential-manager-authenticated');
_oVirtUserAuthenticated(_token) {
this._preemptingService = OVIRT_SERVICE_NAME;
this.emit('ovirt-user-authenticated');
}
_checkForSmartcard() {
@ -356,11 +339,10 @@ var ShellUserVerifier = class {
this._verificationFailed(false);
}
async _openReauthenticationChannel(userName) {
_reauthenticationChannelOpened(client, result) {
try {
this._clearUserVerifier();
this._userVerifier = await this._client.open_reauthentication_channel(
userName, this._cancellable);
this._userVerifier = client.open_reauthentication_channel_finish(result);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
@ -369,7 +351,8 @@ var ShellUserVerifier = class {
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there
// is no session to reauthenticate. Fall back to performing
// verification from this login session
this._getUserVerifier();
client.get_user_verifier(this._cancellable,
this._userVerifierGot.bind(this));
return;
}
@ -383,11 +366,10 @@ var ShellUserVerifier = class {
this._hold.release();
}
async _getUserVerifier() {
_userVerifierGot(client, result) {
try {
this._clearUserVerifier();
this._userVerifier =
await this._client.get_user_verifier(this._cancellable);
this._userVerifier = client.get_user_verifier_finish(result);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
@ -439,25 +421,35 @@ var ShellUserVerifier = class {
}
}
async _startService(serviceName) {
_startService(serviceName) {
this._hold.acquire();
try {
if (this._userName) {
await this._userVerifier.call_begin_verification_for_user(
serviceName, this._userName, this._cancellable);
} else {
await this._userVerifier.call_begin_verification(
serviceName, this._cancellable);
}
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
this._reportInitError(this._userName
? 'Failed to start verification for user'
: 'Failed to start verification', e);
return;
if (this._userName) {
this._userVerifier.call_begin_verification_for_user(serviceName, this._userName, this._cancellable, (obj, result) => {
try {
obj.call_begin_verification_for_user_finish(result);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
this._reportInitError('Failed to start verification for user', e);
return;
}
this._hold.release();
});
} else {
this._userVerifier.call_begin_verification(serviceName, this._cancellable, (obj, result) => {
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() {
@ -500,12 +492,9 @@ var ShellUserVerifier = class {
if (!this.serviceIsForeground(serviceName))
return;
let token = null;
if (this._credentialManagers[serviceName])
token = this._credentialManagers[serviceName].token;
if (token) {
this.answerQuery(serviceName, token);
if (serviceName == OVIRT_SERVICE_NAME) {
// The only question asked by this service is "Token?"
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
return;
}
@ -573,10 +562,8 @@ var ShellUserVerifier = class {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication
// mechanism.
let foregroundService = Object.keys(this._credentialManagers).find(service =>
this.serviceIsForeground(service));
if (foregroundService) {
this._credentialManagers[foregroundService].token = null;
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false);
return;

View File

@ -1,54 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported getVmwareCredentialsManager */
const Gio = imports.gi.Gio;
const Signals = imports.signals;
const Credential = imports.gdm.credentialManager;
const dbusPath = '/org/vmware/viewagent/Credentials';
const dbusInterface = 'org.vmware.viewagent.Credentials';
var SERVICE_NAME = 'gdm-vmwcred';
const VmwareCredentialsIface = '<node> \
<interface name="' + dbusInterface + '"> \
<signal name="UserAuthenticated"> \
<arg type="s" name="token"/> \
</signal> \
</interface> \
</node>';
const VmwareCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(VmwareCredentialsIface);
let _vmwareCredentialsManager = null;
function VmwareCredentials() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: VmwareCredentialsInfo.name,
g_interface_info: VmwareCredentialsInfo,
g_name: dbusInterface,
g_object_path: dbusPath,
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
}
var VmwareCredentialsManager = class VmwareCredentialsManager extends Credential.CredentialManager {
constructor() {
super(SERVICE_NAME);
this._credentials = new VmwareCredentials();
this._credentials.connectSignal('UserAuthenticated',
(proxy, sender, [token]) => {
this.token = token;
});
}
};
Signals.addSignalMethods(VmwareCredentialsManager.prototype);
function getVmwareCredentialsManager() {
if (!_vmwareCredentialsManager)
_vmwareCredentialsManager = new VmwareCredentialsManager();
return _vmwareCredentialsManager;
}

View File

@ -6,8 +6,6 @@
<file>gdm/fingerprint.js</file>
<file>gdm/loginDialog.js</file>
<file>gdm/oVirt.js</file>
<file>gdm/credentialManager.js</file>
<file>gdm/vmware.js</file>
<file>gdm/realmd.js</file>
<file>gdm/util.js</file>
@ -25,14 +23,12 @@
<file>misc/modemManager.js</file>
<file>misc/objectManager.js</file>
<file>misc/params.js</file>
<file>misc/parentalControlsManager.js</file>
<file>misc/permissionStore.js</file>
<file>misc/smartcardManager.js</file>
<file>misc/systemActions.js</file>
<file>misc/util.js</file>
<file>misc/weather.js</file>
<file>perf/basic.js</file>
<file>perf/core.js</file>
<file>perf/hwtest.js</file>
@ -93,6 +89,7 @@
<file>ui/ripples.js</file>
<file>ui/runDialog.js</file>
<file>ui/screenShield.js</file>
<file>ui/screencast.js</file>
<file>ui/screenshot.js</file>
<file>ui/scripting.js</file>
<file>ui/search.js</file>
@ -104,13 +101,13 @@
<file>ui/swipeTracker.js</file>
<file>ui/switcherPopup.js</file>
<file>ui/switchMonitor.js</file>
<file>ui/tweener.js</file>
<file>ui/unlockDialog.js</file>
<file>ui/userWidget.js</file>
<file>ui/viewSelector.js</file>
<file>ui/windowAttentionHandler.js</file>
<file>ui/windowMenu.js</file>
<file>ui/windowManager.js</file>
<file>ui/windowPreview.js</file>
<file>ui/workspace.js</file>
<file>ui/workspaceSwitcherPopup.js</file>
<file>ui/workspaceThumbnail.js</file>
@ -137,6 +134,7 @@
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>
<file>ui/status/remoteAccess.js</file>
<file>ui/status/screencast.js</file>
<file>ui/status/system.js</file>
<file>ui/status/thunderbolt.js</file>
</gresource>

View File

@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported collectFromDatadirs, recursivelyDeleteDir,
recursivelyMoveDir, loadInterfaceXML, loadSubInterfaceXML */
recursivelyMoveDir, loadInterfaceXML */
const { Gio, GLib } = imports.gi;
const Config = imports.misc.config;
@ -67,19 +67,14 @@ function recursivelyMoveDir(srcDir, destDir) {
}
let _ifaceResource = null;
function ensureIfaceResource() {
if (_ifaceResource)
return;
// don't use global.datadir so the method is usable from tests/tools
let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
_ifaceResource = Gio.Resource.load(path);
_ifaceResource._register();
}
function loadInterfaceXML(iface) {
ensureIfaceResource();
if (!_ifaceResource) {
// don't use global.datadir so the method is usable from tests/tools
let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
_ifaceResource = Gio.Resource.load(path);
_ifaceResource._register();
}
let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`;
let f = Gio.File.new_for_uri(uri);
@ -93,25 +88,3 @@ function loadInterfaceXML(iface) {
return null;
}
function loadSubInterfaceXML(iface, ifaceFile) {
let xml = loadInterfaceXML(ifaceFile);
if (!xml)
return null;
let ifaceStartTag = `<interface name="${iface}">`;
let ifaceStopTag = '</interface>';
let ifaceStartIndex = xml.indexOf(ifaceStartTag);
let ifaceEndIndex = xml.indexOf(ifaceStopTag, ifaceStartIndex + 1) + ifaceStopTag.length;
let xmlHeader = '<!DOCTYPE node PUBLIC\n' +
'\'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\'\n' +
'\'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\'>\n' +
'<node>\n';
let xmlFooter = '</node>';
return (
xmlHeader +
xml.substr(ifaceStartIndex, ifaceEndIndex - ifaceStartIndex) +
xmlFooter);
}

View File

@ -6,15 +6,6 @@ const Signals = imports.signals;
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
_checkIBusVersion(1, 5, 2);
@ -111,14 +102,16 @@ var IBusManager = class {
_onConnected() {
this._cancellable = new Gio.Cancellable();
this._initEngines();
this._initPanelService();
this._ibus.list_engines_async(-1, this._cancellable,
this._initEngines.bind(this));
this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable,
this._initPanelService.bind(this));
}
async _initEngines() {
_initEngines(ibus, result) {
try {
const enginesList =
await this._ibus.list_engines_async(-1, this._cancellable);
let enginesList = this._ibus.list_engines_async_finish(result);
for (let i = 0; i < enginesList.length; ++i) {
let name = enginesList[i].get_name();
this._engines.set(name, enginesList[i]);
@ -133,10 +126,9 @@ var IBusManager = class {
}
}
async _initPanelService() {
_initPanelService(ibus, result) {
try {
await this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable);
this._ibus.request_name_async_finish(result);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
logError(e);
@ -171,15 +163,19 @@ var IBusManager = class {
this._panelService.connect('set-content-type', this._setContentType.bind(this));
} catch (e) {
}
this._updateReadiness();
try {
// If an engine is already active we need to get its properties
const engine =
await this._ibus.get_global_engine_async(-1, this._cancellable);
// If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, res) => {
let engine;
try {
engine = this._ibus.get_global_engine_async_finish(res);
if (!engine)
return;
} catch (e) {
return;
}
this._engineChanged(this._ibus, engine.get_name());
} catch (e) {
}
});
this._updateReadiness();
}
_updateReadiness() {
@ -227,7 +223,7 @@ var IBusManager = class {
return this._engines.get(id);
}
async setEngine(id, callback) {
setEngine(id, callback) {
// Send id even if id == this._currentEngineName
// because 'properties-registered' signal can be emitted
// while this._ibusSources == null on a lock screen.
@ -237,16 +233,18 @@ var IBusManager = class {
return;
}
try {
await this._ibus.set_global_engine_async(id,
this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
this._cancellable);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
logError(e);
}
if (callback)
callback();
this._ibus.set_global_engine_async(id,
this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
this._cancellable, (_bus, res) => {
try {
this._ibus.set_global_engine_async_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
logError(e);
}
if (callback)
callback();
});
}
preloadEngines(ids) {

View File

@ -4,9 +4,6 @@ const { Clutter, GLib, Gio, GObject, IBus } = imports.gi;
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 InputMethod = GObject.registerClass(
@ -49,11 +46,15 @@ class InputMethod extends Clutter.InputMethod {
this._currentSource = this._inputSourceManager.currentSource;
}
async _onConnected() {
_onConnected() {
this._cancellable = new Gio.Cancellable();
this._ibus.create_input_context_async('gnome-shell', -1,
this._cancellable, this._setContext.bind(this));
}
_setContext(bus, res) {
try {
this._context = await this._ibus.create_input_context_async(
'gnome-shell', -1, this._cancellable);
this._context = this._ibus.create_input_context_async_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
logError(e);

View File

@ -3,9 +3,9 @@ const { Gio, GLib, Meta, Shell, St } = imports.gi;
const INTROSPECT_SCHEMA = 'org.gnome.shell';
const INTROSPECT_KEY = 'introspect';
const APP_ALLOWLIST = ['org.freedesktop.impl.portal.desktop.gtk'];
const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk'];
const INTROSPECT_DBUS_API_VERSION = 3;
const INTROSPECT_DBUS_API_VERSION = 2;
const { loadInterfaceXML } = imports.misc.fileUtils;
@ -46,24 +46,19 @@ var IntrospectService = class {
this._syncRunningApplications();
this._allowlistMap = new Map();
APP_ALLOWLIST.forEach(appName => {
this._whitelistMap = new Map();
APP_WHITELIST.forEach(appName => {
Gio.DBus.watch_name(Gio.BusType.SESSION,
appName,
Gio.BusNameWatcherFlags.NONE,
(conn, name, owner) => this._allowlistMap.set(name, owner),
(conn, name) => this._allowlistMap.delete(name));
(conn, name, owner) => this._whitelistMap.set(name, owner),
(conn, name) => this._whitelistMap.delete(name));
});
this._settings = St.Settings.get();
this._settings.connect('notify::enable-animations',
this._syncAnimationsEnabled.bind(this));
this._syncAnimationsEnabled();
const monitorManager = Meta.MonitorManager.get();
monitorManager.connect('monitors-changed',
this._syncScreenSize.bind(this));
this._syncScreenSize();
}
_isStandaloneApp(app) {
@ -74,8 +69,8 @@ var IntrospectService = class {
return this._introspectSettings.get_boolean(INTROSPECT_KEY);
}
_isSenderAllowed(sender) {
return [...this._allowlistMap.values()].includes(sender);
_isSenderWhitelisted(sender) {
return [...this._whitelistMap.values()].includes(sender);
}
_getSandboxedAppId(app) {
@ -138,7 +133,7 @@ var IntrospectService = class {
if (this._isIntrospectEnabled())
return true;
if (this._isSenderAllowed(invocation.get_sender()))
if (this._isSenderWhitelisted(invocation.get_sender()))
return true;
return false;
@ -214,28 +209,10 @@ var IntrospectService = class {
}
}
_syncScreenSize() {
const oldScreenWidth = this._screenWidth;
const oldScreenHeight = this._screenHeight;
this._screenWidth = global.screen_width;
this._screenHeight = global.screen_height;
if (oldScreenWidth !== this._screenWidth ||
oldScreenHeight !== this._screenHeight) {
const variant = new GLib.Variant('(ii)',
[this._screenWidth, this._screenHeight]);
this._dbusImpl.emit_property_changed('ScreenSize', variant);
}
}
get AnimationsEnabled() {
return this._animationsEnabled;
}
get ScreenSize() {
return [this._screenWidth, this._screenHeight];
}
get version() {
return INTROSPECT_DBUS_API_VERSION;
}

View File

@ -24,7 +24,8 @@ function getCompletions(text, commandHeader, globalCompletionList) {
[expr_, base, attrHead] = matches;
methods = getPropertyNamesFromExpression(base, commandHeader).filter(
attr => attr.slice(0, attrHead.length) === attrHead);
attr => attr.slice(0, attrHead.length) == attrHead
);
}
// Look for the empty expression or partially entered words
@ -33,7 +34,8 @@ function getCompletions(text, commandHeader, globalCompletionList) {
if (text == '' || matches) {
[expr_, attrHead] = matches;
methods = globalCompletionList.filter(
attr => attr.slice(0, attrHead.length) === attrHead);
attr => attr.slice(0, attrHead.length) == attrHead
);
}
}

View File

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

View File

@ -50,22 +50,25 @@ function canLock() {
}
async function registerSessionWithGDM() {
function registerSessionWithGDM() {
log("Registering session with GDM");
try {
await Gio.DBus.system.call(
'org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.gnome.DisplayManager.Manager',
'RegisterSession',
GLib.Variant.new('(a{sv})', [{}]), null,
Gio.DBusCallFlags.NONE, -1, null);
} catch (e) {
if (!e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
log(`Error registering session with GDM: ${e.message}`);
else
log('Not calling RegisterSession(): method not exported, GDM too old?');
}
Gio.DBus.system.call('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.gnome.DisplayManager.Manager',
'RegisterSession',
GLib.Variant.new('(a{sv})', [{}]), null,
Gio.DBusCallFlags.NONE, -1, null,
(source, result) => {
try {
source.call_finish(result);
} catch (e) {
if (!e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
log(`Error registering session with GDM: ${e.message}`);
else
log("Not calling RegisterSession(): method not exported, GDM too old?");
}
}
);
}
let _loginManager = null;
@ -158,23 +161,6 @@ var LoginManagerSystemd = class {
});
}
canRebootToBootLoaderMenu(asyncCallback) {
this._proxy.CanRebootToBootLoaderMenuRemote((result, error) => {
if (error) {
asyncCallback(false, false);
} else {
const needsAuth = result[0] === 'challenge';
const canRebootToBootLoaderMenu = needsAuth || result[0] === 'yes';
asyncCallback(canRebootToBootLoaderMenu, needsAuth);
}
});
}
setRebootToBootLoaderMenu() {
/* Parameter is timeout in usec, show to menu for 60 seconds */
this._proxy.SetRebootToBootLoaderMenuRemote(60000000);
}
listSessions(asyncCallback) {
this._proxy.ListSessionsRemote((result, error) => {
if (error)
@ -188,19 +174,24 @@ var LoginManagerSystemd = class {
this._proxy.SuspendRemote(true);
}
async inhibit(reason, callback) {
try {
const inVariant = new GLib.Variant('(ssss)',
['sleep', 'GNOME Shell', reason, 'delay']);
const [outVariant_, fdList] =
await this._proxy.call_with_unix_fd_list('Inhibit',
inVariant, 0, -1, null, null);
const [fd] = fdList.steal_fds();
callback(new Gio.UnixInputStream({ fd }));
} catch (e) {
logError(e, 'Error getting systemd inhibitor');
callback(null);
}
inhibit(reason, callback) {
let inVariant = GLib.Variant.new('(ssss)',
['sleep',
'GNOME Shell',
reason,
'delay']);
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
(proxy, result) => {
let fd = -1;
try {
let [outVariant_, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds()[0];
callback(new Gio.UnixInputStream({ fd }));
} catch (e) {
logError(e, "Error getting systemd inhibitor");
callback(null);
}
});
}
_prepareForSleep(proxy, sender, [aboutToSuspend]) {
@ -220,13 +211,6 @@ var LoginManagerDummy = class {
asyncCallback(false, false);
}
canRebootToBootLoaderMenu(asyncCallback) {
asyncCallback(false, false);
}
setRebootToBootLoaderMenu() {
}
listSessions(asyncCallback) {
asyncCallback([]);
}

View File

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

View File

@ -1,146 +0,0 @@
// -*- 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 denied in
// the users parental controls.
// - None of the flatpak app IDs from the X-Flatpak and the
// X-Flatpak-RenamedFrom lines are denied 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

@ -21,7 +21,6 @@ const SENSOR_OBJECT_PATH = '/net/hadess/SensorProxy';
const SensorProxyInterface = loadInterfaceXML('net.hadess.SensorProxy');
const POWER_OFF_ACTION_ID = 'power-off';
const RESTART_ACTION_ID = 'restart';
const LOCK_SCREEN_ACTION_ID = 'lock-screen';
const LOGOUT_ACTION_ID = 'logout';
const SUSPEND_ACTION_ID = 'suspend';
@ -41,38 +40,41 @@ function getDefault() {
const SystemActions = GObject.registerClass({
Properties: {
'can-power-off': GObject.ParamSpec.boolean(
'can-power-off', 'can-power-off', 'can-power-off',
GObject.ParamFlags.READABLE,
false),
'can-restart': GObject.ParamSpec.boolean(
'can-restart', 'can-restart', 'can-restart',
GObject.ParamFlags.READABLE,
false),
'can-suspend': GObject.ParamSpec.boolean(
'can-suspend', 'can-suspend', 'can-suspend',
GObject.ParamFlags.READABLE,
false),
'can-lock-screen': GObject.ParamSpec.boolean(
'can-lock-screen', 'can-lock-screen', 'can-lock-screen',
GObject.ParamFlags.READABLE,
false),
'can-switch-user': GObject.ParamSpec.boolean(
'can-switch-user', 'can-switch-user', 'can-switch-user',
GObject.ParamFlags.READABLE,
false),
'can-logout': GObject.ParamSpec.boolean(
'can-logout', 'can-logout', 'can-logout',
GObject.ParamFlags.READABLE,
false),
'can-lock-orientation': GObject.ParamSpec.boolean(
'can-lock-orientation', 'can-lock-orientation', 'can-lock-orientation',
GObject.ParamFlags.READABLE,
false),
'orientation-lock-icon': GObject.ParamSpec.string(
'orientation-lock-icon', 'orientation-lock-icon', 'orientation-lock-icon',
GObject.ParamFlags.READWRITE,
null),
'can-power-off': GObject.ParamSpec.boolean('can-power-off',
'can-power-off',
'can-power-off',
GObject.ParamFlags.READABLE,
false),
'can-suspend': GObject.ParamSpec.boolean('can-suspend',
'can-suspend',
'can-suspend',
GObject.ParamFlags.READABLE,
false),
'can-lock-screen': GObject.ParamSpec.boolean('can-lock-screen',
'can-lock-screen',
'can-lock-screen',
GObject.ParamFlags.READABLE,
false),
'can-switch-user': GObject.ParamSpec.boolean('can-switch-user',
'can-switch-user',
'can-switch-user',
GObject.ParamFlags.READABLE,
false),
'can-logout': GObject.ParamSpec.boolean('can-logout',
'can-logout',
'can-logout',
GObject.ParamFlags.READABLE,
false),
'can-lock-orientation': GObject.ParamSpec.boolean('can-lock-orientation',
'can-lock-orientation',
'can-lock-orientation',
GObject.ParamFlags.READABLE,
false),
'orientation-lock-icon': GObject.ParamSpec.string('orientation-lock-icon',
'orientation-lock-icon',
'orientation-lock-icon',
GObject.ParamFlags.READWRITE,
null),
},
}, class SystemActions extends GObject.Object {
_init() {
@ -91,15 +93,7 @@ const SystemActions = GObject.registerClass({
name: C_("search-result", "Power Off"),
iconName: 'system-shutdown-symbolic',
// Translators: A list of keywords that match the power-off action, separated by semicolons
keywords: tokenizeKeywords(_('power off;shutdown;halt;stop')),
available: false,
});
this._actions.set(RESTART_ACTION_ID, {
// Translators: The name of the restart action in search
name: C_('search-result', 'Restart'),
iconName: 'system-reboot-symbolic',
// Translators: A list of keywords that match the restart action, separated by semicolons
keywords: tokenizeKeywords(_('reboot;restart;')),
keywords: tokenizeKeywords(_('power off;shutdown;reboot;restart;halt;stop')),
available: false,
});
this._actions.set(LOCK_SCREEN_ACTION_ID, {
@ -209,11 +203,6 @@ const SystemActions = GObject.registerClass({
return this._actions.get(POWER_OFF_ACTION_ID).available;
}
// eslint-disable-next-line camelcase
get can_restart() {
return this._actions.get(RESTART_ACTION_ID).available;
}
// eslint-disable-next-line camelcase
get can_suspend() {
return this._actions.get(SUSPEND_ACTION_ID).available;
@ -317,9 +306,6 @@ const SystemActions = GObject.registerClass({
case POWER_OFF_ACTION_ID:
this.activatePowerOff();
break;
case RESTART_ACTION_ID:
this.activateRestart();
break;
case LOCK_SCREEN_ACTION_ID:
this.activateLockScreen();
break;
@ -361,9 +347,6 @@ const SystemActions = GObject.registerClass({
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
this._actions.get(POWER_OFF_ACTION_ID).available = this._canHavePowerOff && !disabled;
this.notify('can-power-off');
this._actions.get(RESTART_ACTION_ID).available = this._canHavePowerOff && !disabled;
this.notify('can-restart');
}
_updateHaveSuspend() {
@ -462,13 +445,6 @@ const SystemActions = GObject.registerClass({
this._session.ShutdownRemote(0);
}
activateRestart() {
if (!this._actions.get(RESTART_ACTION_ID).available)
throw new Error('The restart action is not available!');
this._session.RebootRemote();
}
activateSuspend() {
if (!this._actions.get(SUSPEND_ACTION_ID).available)
throw new Error('The suspend action is not available!');

View File

@ -1,9 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine,
formatTime, formatTimeSpan, createTimeLabel, insertSorted,
ensureActorVisibleInScrollView, wiggle */
makeCloseButton, ensureActorVisibleInScrollView, wiggle */
const { Clutter, Gio, GLib, Shell, St, GnomeDesktop } = imports.gi;
const { Clutter, Gio, GLib, GObject, Shell, St, GnomeDesktop } = imports.gi;
const Gettext = imports.gettext;
const Main = imports.ui.main;
@ -363,6 +363,51 @@ function insertSorted(array, val, cmp) {
return pos;
}
var CloseButton = GObject.registerClass(
class CloseButton extends St.Button {
_init(boxpointer) {
super._init({
style_class: 'notification-close',
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.START,
});
this._boxPointer = boxpointer;
if (boxpointer)
this._boxPointer.connect('arrow-side-changed', this._sync.bind(this));
}
_computeBoxPointerOffset() {
if (!this._boxPointer || !this._boxPointer.get_stage())
return 0;
let side = this._boxPointer.arrowSide;
if (side == St.Side.TOP)
return this._boxPointer.getArrowHeight();
else
return 0;
}
_sync() {
let themeNode = this.get_theme_node();
let offY = this._computeBoxPointerOffset();
this.translation_x = themeNode.get_length('-shell-close-overlap-x');
this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
}
vfunc_style_changed() {
this._sync();
super.vfunc_style_changed();
}
});
function makeCloseButton(boxpointer) {
return new CloseButton(boxpointer);
}
function ensureActorVisibleInScrollView(scrollView, actor) {
let adjustment = scrollView.vscroll.adjustment;
let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values();

View File

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

View File

@ -1,146 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported run, finish, script_topBarNavDone, script_notificationShowDone,
script_notificationCloseDone, script_overviewShowDone,
script_applicationsShowStart, script_applicationsShowDone, METRICS,
*/
/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */
const { St } = imports.gi;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Scripting = imports.ui.scripting;
// This script tests the most important (basic) functionality of the shell.
var METRICS = {};
async function run() {
/* eslint-disable no-await-in-loop */
Scripting.defineScriptEvent('topBarNavStart', 'Starting to navigate the top bar');
Scripting.defineScriptEvent('topBarNavDone', 'Done navigating the top bar');
Scripting.defineScriptEvent('notificationShowStart', 'Showing a notification');
Scripting.defineScriptEvent('notificationShowDone', 'Done showing a notification');
Scripting.defineScriptEvent('notificationCloseStart', 'Closing a notification');
Scripting.defineScriptEvent('notificationCloseDone', 'Done closing a notification');
Scripting.defineScriptEvent('overviewShowStart', 'Starting to show the overview');
Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing');
Scripting.defineScriptEvent('applicationsShowStart', 'Starting to switch to applications view');
Scripting.defineScriptEvent('applicationsShowDone', 'Done switching to applications view');
Main.overview.connect('shown',
() => Scripting.scriptEvent('overviewShowDone'));
await Scripting.sleep(1000);
// navigate through top bar
Scripting.scriptEvent('topBarNavStart');
Main.panel.statusArea.aggregateMenu.menu.open();
await Scripting.sleep(400);
const { menuManager } = Main.panel;
while (menuManager.activeMenu &&
Main.panel.navigate_focus(menuManager.activeMenu.sourceActor,
St.DirectionType.TAB_BACKWARD, false))
await Scripting.sleep(400);
Scripting.scriptEvent('topBarNavDone');
await Scripting.sleep(1000);
// notification
const source = new MessageTray.SystemNotificationSource();
Main.messageTray.add(source);
Scripting.scriptEvent('notificationShowStart');
source.connect('notification-show',
() => Scripting.scriptEvent('notificationShowDone'));
const notification = new MessageTray.Notification(source,
'A test notification');
source.showNotification(notification);
await Scripting.sleep(400);
Main.panel.statusArea.dateMenu.menu.open();
await Scripting.sleep(400);
Scripting.scriptEvent('notificationCloseStart');
notification.connect('destroy',
() => Scripting.scriptEvent('notificationCloseDone'));
notification.destroy();
await Scripting.sleep(400);
Main.panel.statusArea.dateMenu.menu.close();
await Scripting.waitLeisure();
await Scripting.sleep(1000);
// overview (window picker)
Scripting.scriptEvent('overviewShowStart');
Main.overview.show();
await Scripting.waitLeisure();
Main.overview.hide();
await Scripting.waitLeisure();
await Scripting.sleep(1000);
// overview (app picker)
Main.overview.show();
await Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = true;
await Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = false;
await Scripting.waitLeisure();
Main.overview.hide();
await Scripting.waitLeisure();
/* eslint-enable no-await-in-loop */
}
let topBarNav = false;
let notificationShown = false;
let notificationClosed = false;
let windowPickerShown = false;
let appPickerShown = false;
function script_topBarNavDone() {
topBarNav = true;
}
function script_notificationShowDone() {
notificationShown = true;
}
function script_notificationCloseDone() {
notificationClosed = true;
}
function script_overviewShowDone() {
windowPickerShown = true;
}
function script_applicationsShowDone() {
appPickerShown = true;
}
function finish() {
if (!topBarNav)
throw new Error('Failed to navigate top bar');
if (!notificationShown)
throw new Error('Failed to show notification');
if (!notificationClosed)
throw new Error('Failed to close notification');
if (!windowPickerShown)
throw new Error('Failed to show window picker');
if (!appPickerShown)
throw new Error('Failed to show app picker');
}

View File

@ -70,8 +70,7 @@ let WINDOW_CONFIGS = [
{ width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' },
];
async function run() {
/* eslint-disable no-await-in-loop */
function *run() {
Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview");
@ -85,7 +84,7 @@ async function run() {
Scripting.scriptEvent('overviewShowDone');
});
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
// We go to the overview twice for each configuration; the first time
@ -93,50 +92,49 @@ async function run() {
// a clean set of numbers.
if ((i % 2) == 0) {
let config = WINDOW_CONFIGS[i / 2];
await Scripting.destroyTestWindows();
yield Scripting.destroyTestWindows();
for (let k = 0; k < config.count; k++) {
await Scripting.createTestWindow({ width: config.width,
yield Scripting.createTestWindow({ width: config.width,
height: config.height,
alpha: config.alpha,
maximized: config.maximized });
}
await Scripting.waitTestWindows();
await Scripting.sleep(1000);
await Scripting.waitLeisure();
yield Scripting.waitTestWindows();
yield Scripting.sleep(1000);
yield Scripting.waitLeisure();
}
Scripting.scriptEvent('overviewShowStart');
Main.overview.show();
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
Main.overview.hide();
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
System.gc();
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.collectStatistics();
Scripting.scriptEvent('afterShowHide');
}
await Scripting.destroyTestWindows();
await Scripting.sleep(1000);
yield Scripting.destroyTestWindows();
yield Scripting.sleep(1000);
Main.overview.show();
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = true;
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = false;
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
}
/* eslint-enable no-await-in-loop */
}
let showingOverview = false;

View File

@ -94,8 +94,7 @@ function extractBootTimestamp() {
return result;
}
async function run() {
/* eslint-disable no-await-in-loop */
function *run() {
Scripting.defineScriptEvent("desktopShown", "Finished initial animation");
Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
@ -111,7 +110,7 @@ async function run() {
Scripting.defineScriptEvent("geditLaunch", "gedit application launch");
Scripting.defineScriptEvent("geditFirstFrame", "first frame of gedit window drawn");
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
Scripting.scriptEvent('desktopShown');
let interfaceSettings = new Gio.Settings({
@ -121,22 +120,22 @@ async function run() {
Scripting.scriptEvent('overviewShowStart');
Main.overview.show();
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
Scripting.scriptEvent('overviewShowDone');
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = true;
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Main.overview.hide();
await Scripting.waitLeisure();
yield Scripting.waitLeisure();
// --------------------- //
// Tests of redraw speed //
@ -146,46 +145,46 @@ async function run() {
global.frame_finish_timestamp = true;
for (let k = 0; k < 5; k++)
await Scripting.createTestWindow({ maximized: true });
await Scripting.waitTestWindows();
yield Scripting.createTestWindow({ maximized: true });
yield Scripting.waitTestWindows();
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.scriptEvent('mainViewDrawStart');
await waitAndDraw(1000);
yield waitAndDraw(1000);
Scripting.scriptEvent('mainViewDrawDone');
Main.overview.show();
Scripting.waitLeisure();
await Scripting.sleep(1500);
yield Scripting.sleep(1500);
Scripting.scriptEvent('overviewDrawStart');
await waitAndDraw(1000);
yield waitAndDraw(1000);
Scripting.scriptEvent('overviewDrawDone');
await Scripting.destroyTestWindows();
yield Scripting.destroyTestWindows();
Main.overview.hide();
await Scripting.createTestWindow({ maximized: true,
yield Scripting.createTestWindow({ maximized: true,
redraws: true });
await Scripting.waitTestWindows();
yield Scripting.waitTestWindows();
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.scriptEvent('redrawTestStart');
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.scriptEvent('redrawTestDone');
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
Scripting.scriptEvent('collectTimings');
await Scripting.destroyTestWindows();
yield Scripting.destroyTestWindows();
global.frame_timestamps = false;
global.frame_finish_timestamp = false;
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('org.gnome.gedit.desktop');
@ -198,22 +197,21 @@ async function run() {
throw new Error('gedit was already running');
while (windows.length == 0) {
await waitSignal(global.display, 'window-created');
yield waitSignal(global.display, 'window-created');
windows = app.get_windows();
}
let actor = windows[0].get_compositor_private();
await waitSignal(actor, 'first-frame');
yield waitSignal(actor, 'first-frame');
Scripting.scriptEvent('geditFirstFrame');
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
windows[0].delete(global.get_current_time());
await Scripting.sleep(1000);
yield Scripting.sleep(1000);
interfaceSettings.set_boolean('enable-animations', true);
/* eslint-enable no-await-in-loop */
}
let overviewShowStart;

View File

@ -68,8 +68,8 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
this._items = this._switcherList.icons;
}
vfunc_allocate(box) {
super.vfunc_allocate(box);
vfunc_allocate(box, flags) {
super.vfunc_allocate(box, flags);
// Allocate the thumbnails
// We try to avoid overflowing the screen so we base the resulting size on
@ -102,7 +102,7 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
this._thumbnails.addClones(primary.y + primary.height - bottomPadding - childBox.y1);
let [, childNaturalHeight] = this._thumbnails.get_preferred_height(-1);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._thumbnails.allocate(childBox);
this._thumbnails.allocate(childBox, flags);
}
}
@ -681,7 +681,8 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = allWindows.filter(
w => windowTracker.get_window_app(w) === appIcon.app);
w => windowTracker.get_window_app(w) == appIcon.app
);
if (appIcon.cachedWindows.length > 0)
this._addIcon(appIcon);
}
@ -749,9 +750,9 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
return super.vfunc_get_preferred_height(forWidth);
}
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
// Allocate the main list items
super.vfunc_allocate(box);
super.vfunc_allocate(box, flags);
let contentBox = this.get_theme_node().get_content_box(box);
@ -766,7 +767,7 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
childBox.x2 = childBox.x1 + arrowWidth;
childBox.y1 = contentBox.y1 + itemBox.y2 + arrowHeight;
childBox.y2 = childBox.y1 + arrowHeight;
this._arrows[i].allocate(childBox);
this._arrows[i].allocate(childBox, flags);
}
}
@ -1059,7 +1060,7 @@ class WindowSwitcher extends SwitcherPopup.SwitcherList {
return [minHeight, natHeight];
}
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
let themeNode = this.get_theme_node();
let contentBox = themeNode.get_content_box(box);
const labelHeight = this._label.height;
@ -1067,20 +1068,20 @@ class WindowSwitcher extends SwitcherPopup.SwitcherList {
labelHeight + themeNode.get_padding(St.Side.BOTTOM);
box.y2 -= totalLabelHeight;
super.vfunc_allocate(box);
super.vfunc_allocate(box, flags);
// Hooking up the parent vfunc will call this.set_allocation() with
// the height without the label height, so call it again with the
// correct size here.
box.y2 += totalLabelHeight;
this.set_allocation(box);
this.set_allocation(box, flags);
const childBox = new Clutter.ActorBox();
childBox.x1 = contentBox.x1;
childBox.x2 = contentBox.x2;
childBox.y2 = contentBox.y2;
childBox.y1 = childBox.y2 - labelHeight;
this._label.allocate(childBox);
this._label.allocate(childBox, flags);
}
highlight(index, justOutline) {

View File

@ -60,7 +60,7 @@ class Animation extends St.Bin {
}
_loadFile(file, width, height) {
const resourceScale = this.get_resource_scale();
let [validResourceScale, resourceScale] = this.get_resource_scale();
let wasPlaying = this._isPlaying;
if (this._isPlaying)
@ -69,6 +69,12 @@ class Animation extends St.Bin {
this._isLoaded = false;
this.destroy_all_children();
if (!validResourceScale) {
if (wasPlaying)
this.play();
return;
}
let textureCache = St.TextureCache.get_default();
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._animations = textureCache.load_sliced_image(file, width, height,

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
/* exported getAppFavorites */
const Shell = imports.gi.Shell;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const Signals = imports.signals;
const Main = imports.ui.main;
@ -66,13 +65,6 @@ const RENAMED_DESKTOP_IDS = {
class AppFavorites {
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._favorites = {};
global.settings.connect('changed::%s'.format(this.FAVORITE_APPS_KEY), this._onFavsChanged.bind(this));
@ -104,7 +96,7 @@ class AppFavorites {
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
let apps = ids.map(id => appSys.lookup_app(id))
.filter(app => app !== null && this._parentalControlsManager.shouldShowApp(app.app_info));
.filter(app => app != null);
this._favorites = {};
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
@ -143,9 +135,6 @@ class AppFavorites {
if (!app)
return false;
if (!this._parentalControlsManager.shouldShowApp(app.app_info))
return false;
let ids = this._getIds();
if (pos == -1)
ids.push(appId);

View File

@ -147,8 +147,9 @@ var AudioDeviceSelectionDBus = class AudioDeviceSelectionDBus {
_onDeviceSelected(dialog, device) {
let connection = this._dbusImpl.get_connection();
let info = this._dbusImpl.get_info();
const deviceName = Object.keys(AudioDevice)
.filter(dev => AudioDevice[dev] === device)[0].toLowerCase();
let deviceName = Object.keys(AudioDevice).filter(
dev => AudioDevice[dev] == device
)[0].toLowerCase();
connection.emit_signal(this._audioSelectionDialog._sender,
this._dbusImpl.get_object_path(),
info ? info.name : null,

View File

@ -514,8 +514,8 @@ var SystemBackground = GObject.registerClass({
super._init({
meta_display: global.display,
monitor: 0,
background: _systemBackground,
});
this.content.background = _systemBackground;
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.emit('loaded');
@ -714,18 +714,13 @@ var BackgroundManager = class BackgroundManager {
}
let newBackgroundActor = this._createBackgroundActor();
const oldContent = this.backgroundActor.content;
const newContent = newBackgroundActor.content;
newContent.vignette_sharpness = oldContent.vignette_sharpness;
newContent.brightness = oldContent.brightness;
newBackgroundActor.vignette_sharpness = this.backgroundActor.vignette_sharpness;
newBackgroundActor.brightness = this.backgroundActor.brightness;
newBackgroundActor.visible = this.backgroundActor.visible;
this._newBackgroundActor = newBackgroundActor;
const { background } = newBackgroundActor.content;
let background = newBackgroundActor.background;
if (background.isLoaded) {
this._swapBackgroundActor();
@ -745,8 +740,6 @@ var BackgroundManager = class BackgroundManager {
let backgroundActor = new Meta.BackgroundActor({
meta_display: global.display,
monitor: this._monitorIndex,
});
backgroundActor.content.set({
background,
vignette: this._vignette,
vignette_sharpness: 0.5,

View File

@ -30,8 +30,8 @@ var BarLevel = GObject.registerClass({
accessible_role: Atk.Role.LEVEL_BAR,
};
super._init(Object.assign(defaultParams, params));
this.connect('notify::allocation', () => {
this._barLevelWidth = this.allocation.get_width();
this.connect('allocation-changed', (actor, box) => {
this._barLevelWidth = box.get_width();
});
this._customAccessible = St.GenericAccessible.new_for_actor(this);

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported BoxPointer */
const { Clutter, GObject, St } = imports.gi;
const { Clutter, GObject, Shell, St } = imports.gi;
const Main = imports.ui.main;
@ -196,13 +196,13 @@ var BoxPointer = GObject.registerClass({
return themeNode.adjust_preferred_height(...height);
}
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
if (this._sourceActor && this._sourceActor.mapped) {
this._reposition(box);
this._updateFlip(box);
}
this.set_allocation(box);
this.set_allocation(box, flags);
let themeNode = this.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
@ -214,7 +214,7 @@ var BoxPointer = GObject.registerClass({
childBox.y1 = 0;
childBox.x2 = availWidth;
childBox.y2 = availHeight;
this._border.allocate(childBox);
this._border.allocate(childBox, flags);
childBox.x1 = borderWidth;
childBox.y1 = borderWidth;
@ -234,7 +234,7 @@ var BoxPointer = GObject.registerClass({
childBox.x2 -= rise;
break;
}
this.bin.allocate(childBox);
this.bin.allocate(childBox, flags);
}
_drawBorder(area) {
@ -453,16 +453,15 @@ var BoxPointer = GObject.registerClass({
let alignment = this._arrowAlignment;
let monitorIndex = Main.layoutManager.findIndexForActor(sourceActor);
this._sourceExtents = sourceActor.get_transformed_extents();
this._sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
this._workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
let sourceTopLeft = this._sourceExtents.get_top_left();
let sourceBottomRight = this._sourceExtents.get_bottom_right();
let sourceCenterX = sourceTopLeft.x + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
let sourceCenterY = sourceTopLeft.y + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
let sourceAllocation = this._sourceAllocation;
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
let [, , natWidth, natHeight] = this.get_preferred_size();
// We also want to keep it onscreen, and separated from the
@ -482,16 +481,16 @@ var BoxPointer = GObject.registerClass({
switch (this._arrowSide) {
case St.Side.TOP:
resY = sourceBottomRight.y + gap;
resY = sourceAllocation.y2 + gap;
break;
case St.Side.BOTTOM:
resY = sourceTopLeft.y - natHeight - gap;
resY = sourceAllocation.y1 - natHeight - gap;
break;
case St.Side.LEFT:
resX = sourceBottomRight.x + gap;
resX = sourceAllocation.x2 + gap;
break;
case St.Side.RIGHT:
resX = sourceTopLeft.x - natWidth - gap;
resX = sourceAllocation.x1 - natWidth - gap;
break;
}
@ -587,30 +586,29 @@ var BoxPointer = GObject.registerClass({
}
_calculateArrowSide(arrowSide) {
let sourceTopLeft = this._sourceExtents.get_top_left();
let sourceBottomRight = this._sourceExtents.get_bottom_right();
let sourceAllocation = this._sourceAllocation;
let [, , boxWidth, boxHeight] = this.get_preferred_size();
let workarea = this._workArea;
switch (arrowSide) {
case St.Side.TOP:
if (sourceBottomRight.y + boxHeight > workarea.y + workarea.height &&
boxHeight < sourceTopLeft.y - workarea.y)
if (sourceAllocation.y2 + boxHeight > workarea.y + workarea.height &&
boxHeight < sourceAllocation.y1 - workarea.y)
return St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
if (sourceTopLeft.y - boxHeight < workarea.y &&
boxHeight < workarea.y + workarea.height - sourceBottomRight.y)
if (sourceAllocation.y1 - boxHeight < workarea.y &&
boxHeight < workarea.y + workarea.height - sourceAllocation.y2)
return St.Side.TOP;
break;
case St.Side.LEFT:
if (sourceBottomRight.x + boxWidth > workarea.x + workarea.width &&
boxWidth < sourceTopLeft.x - workarea.x)
if (sourceAllocation.x2 + boxWidth > workarea.x + workarea.width &&
boxWidth < sourceAllocation.x1 - workarea.x)
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (sourceTopLeft.x - boxWidth < workarea.x &&
boxWidth < workarea.x + workarea.width - sourceBottomRight.x)
if (sourceAllocation.x1 - boxWidth < workarea.x &&
boxWidth < workarea.x + workarea.width - sourceAllocation.x2)
return St.Side.LEFT;
break;
}

View File

@ -14,6 +14,7 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
var MSECS_IN_DAY = 24 * 60 * 60 * 1000;
var SHOW_WEEKDATE_KEY = 'show-weekdate';
var ELLIPSIS_CHAR = '\u2026';
var MESSAGE_ICON_SIZE = -1; // pick up from CSS
@ -31,6 +32,10 @@ function sameDay(dateA, dateB) {
return sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
}
function isToday(date) {
return sameDay(new Date(), date);
}
function _isWorkDay(date) {
/* Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */
let days = C_('calendar-no-work', "06");
@ -194,52 +199,51 @@ class DBusEventSource extends EventSourceBase {
this._initialized = false;
this._dbusProxy = new CalendarServer();
this._initProxy();
}
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, (object, result) => {
let loaded = false;
async _initProxy() {
let loaded = false;
try {
await this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null);
loaded = true;
} catch (e) {
// Ignore timeouts and install signals as normal, because with high
// probability the service will appear later on, and we will get a
// NameOwnerChanged which will finish loading
//
// (But still _initialized to false, because the proxy does not know
// about the HasCalendars property and would cause an exception trying
// to read it)
if (!e.matches(Gio.DBusError, Gio.DBusError.TIMED_OUT)) {
log('Error loading calendars: %s'.format(e.message));
return;
try {
this._dbusProxy.init_finish(result);
loaded = true;
} catch (e) {
if (e.matches(Gio.DBusError, Gio.DBusError.TIMED_OUT)) {
// Ignore timeouts and install signals as normal, because with high
// probability the service will appear later on, and we will get a
// NameOwnerChanged which will finish loading
//
// (But still _initialized to false, because the proxy does not know
// about the HasCalendars property and would cause an exception trying
// to read it)
} else {
log('Error loading calendars: %s'.format(e.message));
return;
}
}
}
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.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', () => {
if (this._dbusProxy.g_name_owner)
this._dbusProxy.connect('notify::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();
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() {
@ -718,6 +722,67 @@ var Calendar = GObject.registerClass({
}
});
var EventMessage = GObject.registerClass(
class EventMessage extends MessageList.Message {
_init(event, date) {
super._init('', '');
this._date = date;
this.update(event);
this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
this.setIcon(this._icon);
}
vfunc_style_changed() {
let iconVisible = this.get_parent().has_style_pseudo_class('first-child');
this._icon.opacity = iconVisible ? 255 : 0;
super.vfunc_style_changed();
}
update(event) {
this._event = event;
this.setTitle(this._formatEventTime());
this.setBody(event.summary);
}
_formatEventTime() {
let periodBegin = _getBeginningOfDay(this._date);
let periodEnd = _getEndOfDay(this._date);
let allDay = this._event.allDay || (this._event.date <= periodBegin &&
this._event.end >= periodEnd);
let title;
if (allDay) {
/* Translators: Shown in calendar event list for all day events
* Keep it short, best if you can use less then 10 characters
*/
title = C_("event list time", "All Day");
} else {
let date = this._event.date >= periodBegin
? this._event.date
: this._event.end;
title = Util.formatTime(date, { timeOnly: true });
}
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
if (this._event.date < periodBegin && !this._event.allDay) {
if (rtl)
title = '%s%s'.format(title, ELLIPSIS_CHAR);
else
title = '%s%s'.format(ELLIPSIS_CHAR, title);
}
if (this._event.end > periodEnd && !this._event.allDay) {
if (rtl)
title = '%s%s'.format(ELLIPSIS_CHAR, title);
else
title = '%s%s'.format(title, ELLIPSIS_CHAR);
}
return title;
}
});
var NotificationMessage = GObject.registerClass(
class NotificationMessage extends MessageList.Message {
_init(notification) {
@ -783,6 +848,149 @@ class NotificationMessage extends MessageList.Message {
}
});
var EventsSection = GObject.registerClass(
class EventsSection extends MessageList.MessageListSection {
_init() {
super._init();
this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', this._reloadEvents.bind(this));
this._eventSource = new EmptyEventSource();
this._messageById = new Map();
this._title = new St.Button({ style_class: 'events-section-title',
label: '',
can_focus: true });
this._title.child.x_align = Clutter.ActorAlign.START;
this.insert_child_below(this._title, null);
this._title.connect('clicked', this._onTitleClicked.bind(this));
this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this));
this._appSys = Shell.AppSystem.get_default();
this._appSys.connect('installed-changed',
this._appInstalledChanged.bind(this));
this._appInstalledChanged();
}
setEventSource(eventSource) {
if (!(eventSource instanceof EventSourceBase))
throw new Error('Event source is not valid type');
this._eventSource = eventSource;
this._eventSource.connect('changed', this._reloadEvents.bind(this));
}
get allowed() {
return Main.sessionMode.showCalendarEvents;
}
_updateTitle() {
this._title.visible = !isToday(this._date);
if (!this._title.visible)
return;
let dayFormat;
let now = new Date();
if (sameYear(this._date, now)) {
/* Translators: Shown on calendar heading when selected day occurs on current year */
dayFormat = Shell.util_translate_time_string(NC_("calendar heading", "%A, %B %-d"));
} else {
/* Translators: Shown on calendar heading when selected day occurs on different year */
dayFormat = Shell.util_translate_time_string(NC_("calendar heading", "%A, %B %-d, %Y"));
}
this._title.label = this._date.toLocaleFormat(dayFormat);
}
_reloadEvents() {
if (this._eventSource.isLoading || this._reloading)
return;
this._reloading = true;
let periodBegin = _getBeginningOfDay(this._date);
let periodEnd = _getEndOfDay(this._date);
let events = this._eventSource.getEvents(periodBegin, periodEnd);
let ids = events.map(e => e.id);
this._messageById.forEach((message, id) => {
if (ids.includes(id))
return;
this._messageById.delete(id);
this.removeMessage(message);
});
for (let i = 0; i < events.length; i++) {
let event = events[i];
let message = this._messageById.get(event.id);
if (!message) {
message = new EventMessage(event, this._date);
this._messageById.set(event.id, message);
this.addMessage(message, false);
} else {
message.update(event);
this.moveMessage(message, i, false);
}
}
this._reloading = false;
this._sync();
}
_appInstalledChanged() {
this._calendarApp = undefined;
this._title.reactive = this._getCalendarApp() != null;
}
_getCalendarApp() {
if (this._calendarApp !== undefined)
return this._calendarApp;
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
if (apps && (apps.length > 0)) {
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
let defaultInRecommended = apps.some(a => a.equal(app));
this._calendarApp = defaultInRecommended ? app : apps[0];
} else {
this._calendarApp = null;
}
return this._calendarApp;
}
_onTitleClicked() {
Main.overview.hide();
Main.panel.closeCalendar();
let appInfo = this._getCalendarApp();
if (appInfo.get_id() === 'org.gnome.Evolution.desktop') {
let app = this._appSys.lookup_app('evolution-calendar.desktop');
if (app)
appInfo = app.app_info;
}
appInfo.launch([], global.create_app_launch_context(0, -1));
}
setDate(date) {
super.setDate(date);
this._updateTitle();
this._reloadEvents();
}
_shouldShow() {
return !this.empty || !isToday(this._date);
}
_sync() {
if (this._reloading)
return;
super._sync();
}
});
var TimeLabel = GObject.registerClass(
class NotificationTimeLabel extends St.Label {
_init(datetime) {
@ -879,6 +1087,10 @@ class NotificationSection extends MessageList.MessageListSection {
});
super.vfunc_map();
}
_shouldShow() {
return !this.empty && isToday(this._date);
}
});
var Placeholder = GObject.registerClass(
@ -887,13 +1099,41 @@ class Placeholder extends St.BoxLayout {
super._init({ style_class: 'message-list-placeholder', vertical: true });
this._date = new Date();
const file = Gio.File.new_for_uri(
'resource:///org/gnome/shell/theme/no-notifications.svg');
this._icon = new St.Icon({ gicon: new Gio.FileIcon({ file }) });
let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
let otherFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
this._todayIcon = new Gio.FileIcon({ file: todayFile });
this._otherIcon = new Gio.FileIcon({ file: otherFile });
this._icon = new St.Icon();
this.add_actor(this._icon);
this._label = new St.Label({ text: _('No Notifications') });
this._label = new St.Label();
this.add_actor(this._label);
this._sync();
}
setDate(date) {
if (sameDay(this._date, date))
return;
this._date = date;
this._sync();
}
_sync() {
let today = isToday(this._date);
if (today && this._icon.gicon == this._todayIcon)
return;
if (!today && this._icon.gicon == this._otherIcon)
return;
if (today) {
this._icon.gicon = this._todayIcon;
this._label.text = _("No Notifications");
} else {
this._icon.gicon = this._otherIcon;
this._label.text = _("No Events");
}
}
});
@ -994,6 +1234,9 @@ class CalendarMessageList extends St.Widget {
this._notificationSection = new NotificationSection();
this._addSection(this._notificationSection);
this._eventsSection = new EventsSection();
this._addSection(this._eventsSection);
Main.sessionMode.connect('updated', this._sync.bind(this));
}
@ -1029,4 +1272,13 @@ class CalendarMessageList extends St.Widget {
let canClear = sections.some(s => s.canClear && s.visible);
this._clearButton.reactive = canClear;
}
setEventSource(eventSource) {
this._eventsSection.setEventSource(eventSource);
}
setDate(date) {
this._sectionList.get_children().forEach(s => s.setDate(date));
this._placeholder.setDate(date);
}
});

View File

@ -13,13 +13,17 @@ var ComponentManager = class {
_sessionUpdated() {
let newEnabledComponents = Main.sessionMode.components;
newEnabledComponents
.filter(name => !this._enabledComponents.includes(name))
.forEach(name => this._enableComponent(name));
newEnabledComponents.filter(
name => !this._enabledComponents.includes(name)
).forEach(name => {
this._enableComponent(name);
});
this._enabledComponents
.filter(name => !newEnabledComponents.includes(name))
.forEach(name => this._disableComponent(name));
this._enabledComponents.filter(
name => !newEnabledComponents.includes(name)
).forEach(name => {
this._disableComponent(name);
});
this._enabledComponents = newEnabledComponents;
}

View File

@ -125,7 +125,8 @@ var ContentTypeDiscoverer = class {
_emitCallback(mount, contentTypes = []) {
// we're not interested in win32 software content types here
contentTypes = contentTypes.filter(
type => type !== 'x-content/win32-software');
type => type != 'x-content/win32-software'
);
let apps = [];
contentTypes.forEach(type => {

View File

@ -10,7 +10,6 @@ const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
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');
@ -483,37 +482,39 @@ var VPNRequestHandler = class {
}
}
async _readStdoutOldStyle() {
const [line, len_] =
await this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null);
_readStdoutOldStyle() {
this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null, (stream, result) => {
let [line, len_] = this._dataStdout.read_line_finish_utf8(result);
if (line === null) {
// end of file
this._stdout.close(null);
return;
}
if (line == null) {
// end of file
this._stdout.close(null);
return;
}
this._vpnChildProcessLineOldStyle(line);
this._vpnChildProcessLineOldStyle(line);
// try to read more!
this._readStdoutOldStyle();
// try to read more!
this._readStdoutOldStyle();
});
}
async _readStdoutNewStyle() {
const cnt =
await this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null);
_readStdoutNewStyle() {
this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => {
let cnt = this._dataStdout.fill_finish(result);
if (cnt === 0) {
// end of file
this._showNewStyleDialog();
if (cnt == 0) {
// end of file
this._showNewStyleDialog();
this._stdout.close(null);
return;
}
this._stdout.close(null);
return;
}
// Try to read more
this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
this._readStdoutNewStyle();
// Try to read more
this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
this._readStdoutNewStyle();
});
}
_showNewStyleDialog() {
@ -620,17 +621,15 @@ var NetworkAgent = class {
this._native.connect('cancel-request', this._cancelRequest.bind(this));
this._initialized = false;
this._initNative();
}
async _initNative() {
try {
await this._native.init_async(GLib.PRIORITY_DEFAULT, null);
this._initialized = true;
} catch (e) {
this._native = null;
logError(e, 'error initializing the NetworkManager Agent');
}
this._native.init_async(GLib.PRIORITY_DEFAULT, null, (o, res) => {
try {
this._native.init_finish(res);
this._initialized = true;
} catch (e) {
this._native = null;
logError(e, 'error initializing the NetworkManager Agent');
}
});
}
enable() {

View File

@ -330,13 +330,11 @@ var AuthenticationDialog = GObject.registerClass({
this._sessionRequestTimeoutId = 0;
if (this.state != ModalDialog.State.OPENED)
return GLib.SOURCE_REMOVE;
return;
this._passwordEntry.hide();
this._cancelButton.grab_key_focus();
this._okButton.reactive = false;
return GLib.SOURCE_REMOVE;
};
if (delay) {

View File

@ -7,14 +7,6 @@ var Tpl = null;
var Tp = null;
try {
({ 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) {
log('Telepathy is not available, chat integration will be disabled.');
}
@ -223,7 +215,7 @@ class TelepathyClient extends Tp.BaseClient {
// We can only handle text channel, so close any other channel
if (!(channel instanceof Tp.TextChannel)) {
channel.close_async();
channel.close_async(null);
continue;
}
@ -269,7 +261,7 @@ class TelepathyClient extends Tp.BaseClient {
}
}
async _approveTextChannel(account, conn, channel, dispatchOp, context) {
_approveTextChannel(account, conn, channel, dispatchOp, context) {
let [targetHandle_, targetHandleType] = channel.get_handle();
if (targetHandleType != Tp.HandleType.CONTACT) {
@ -278,15 +270,17 @@ class TelepathyClient extends Tp.BaseClient {
return;
}
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()));
}
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();
}
_delegatedChannelsCb(_client, _channels) {
@ -447,14 +441,17 @@ class ChatSource extends MessageTray.Source {
}
}
async _getLogMessages() {
_getLogMessages() {
let logManager = Tpl.LogManager.dup_singleton();
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
const [events] = await logManager.get_filtered_events_async(
this._account, entity,
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
null);
logManager.get_filtered_events_async(this._account, entity,
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
null, this._displayPendingMessages.bind(this));
}
_displayPendingMessages(logManager, result) {
let [success_, events] = logManager.get_filtered_events_finish(result);
let logMessages = events.map(e => ChatMessage.newFromTplTextEvent(e));
this._ensureNotification();
@ -512,7 +509,9 @@ class ChatSource extends MessageTray.Source {
this._ackMessages();
// The chat box has been destroyed so it can't
// handle the channel any more.
this._channel.close_async();
this._channel.close_async((channel, result) => {
channel.close_finish(result);
});
} else {
// Don't indicate any unread messages when the notification
// that represents them has been destroyed.
@ -610,7 +609,9 @@ class ChatSource extends MessageTray.Source {
}
let msg = Tp.ClientMessage.new_text(type, text);
this._channel.send_message_async(msg, 0);
this._channel.send_message_async(msg, 0, (src, result) => {
this._channel.send_message_finish(result);
});
}
setChatState(state) {

View File

@ -292,11 +292,11 @@ class DashActor extends St.Widget {
});
}
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box);
let availWidth = contentBox.x2 - contentBox.x1;
this.set_allocation(box);
this.set_allocation(box, flags);
let [appIcons, showAppsButton] = this.get_children();
let [, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
@ -306,11 +306,11 @@ class DashActor extends St.Widget {
childBox.y1 = contentBox.y1;
childBox.x2 = contentBox.x2;
childBox.y2 = contentBox.y2 - showAppsNatHeight;
appIcons.allocate(childBox);
appIcons.allocate(childBox, flags);
childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox);
showAppsButton.allocate(childBox, flags);
}
vfunc_get_preferred_height(forWidth) {
@ -815,12 +815,7 @@ var Dash = GObject.registerClass({
else
pos = 0; // always insert at the top when dash is empty
// Put the placeholder after the last favorite if we are not
// in the favorites zone
if (pos > numFavorites)
pos = numFavorites;
if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) {
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
this._dragPlaceholderPos = pos;
// Don't allow positioning before or after self
@ -848,6 +843,11 @@ var Dash = GObject.registerClass({
this._dragPlaceholder.show(fadeIn);
}
// Remove the drag placeholder if we are not in the
// "favorites zone"
if (pos > numFavorites)
this._clearDragPlaceholder();
if (!this._dragPlaceholder)
return DND.DragMotionResult.NO_DROP;

View File

@ -13,11 +13,7 @@ const System = imports.system;
const { loadInterfaceXML } = imports.misc.fileUtils;
const NC_ = (context, str) => '%s\u0004%s'.format(context, str);
const T_ = Shell.util_translate_time_string;
const MAX_FORECASTS = 5;
const ELLIPSIS_CHAR = '\u2026';
const ClocksIntegrationIface = loadInterfaceXML('org.gnome.Shell.ClocksIntegration');
const ClocksProxy = Gio.DBusProxy.makeProxyWrapper(ClocksIntegrationIface);
@ -88,188 +84,6 @@ class TodayButton extends St.Button {
}
});
var EventsSection = GObject.registerClass(
class EventsSection extends St.Button {
_init() {
super._init({
style_class: 'events-button',
can_focus: true,
x_expand: true,
child: new St.BoxLayout({
style_class: 'events-box',
vertical: true,
x_expand: true,
}),
});
this._startDate = null;
this._endDate = null;
this._eventSource = null;
this._calendarApp = null;
this._title = new St.Label({
style_class: 'events-title',
});
this.child.add_child(this._title);
this._eventsList = new St.BoxLayout({
style_class: 'events-list',
vertical: true,
x_expand: true,
});
this.child.add_child(this._eventsList);
this._appSys = Shell.AppSystem.get_default();
this._appSys.connect('installed-changed',
this._appInstalledChanged.bind(this));
this._appInstalledChanged();
}
setDate(date) {
const day = [date.getFullYear(), date.getMonth(), date.getDate()];
this._startDate = new Date(...day);
this._endDate = new Date(...day, 23, 59, 59, 999);
this._updateTitle();
this._reloadEvents();
}
setEventSource(eventSource) {
if (!(eventSource instanceof Calendar.EventSourceBase))
throw new Error('Event source is not valid type');
this._eventSource = eventSource;
this._eventSource.connect('changed', this._reloadEvents.bind(this));
this._eventSource.connect('notify::has-calendars',
this._sync.bind(this));
this._sync();
}
_updateTitle() {
/* Translators: Shown on calendar heading when selected day occurs on current year */
const sameYearFormat = T_(NC_('calendar heading', '%B %-d'));
/* Translators: Shown on calendar heading when selected day occurs on different year */
const otherYearFormat = T_(NC_('calendar heading', '%B %-d %Y'));
const timeSpanDay = GLib.TIME_SPAN_DAY / 1000;
const now = new Date();
if (this._startDate <= now && now <= this._endDate)
this._title.text = _('Today');
else if (this._endDate < now && now - this._endDate < timeSpanDay)
this._title.text = _('Yesterday');
else if (this._startDate > now && this._startDate - now < timeSpanDay)
this._title.text = _('Tomorrow');
else if (this._startDate.getFullYear() === now.getFullYear())
this._title.text = this._startDate.toLocaleFormat(sameYearFormat);
else
this._title.text = this._startDate.toLocaleFormat(otherYearFormat);
}
_formatEventTime(event) {
const allDay = event.allDay ||
(event.date <= this._startDate && event.end >= this._endDate);
let title;
if (allDay) {
/* Translators: Shown in calendar event list for all day events
* Keep it short, best if you can use less then 10 characters
*/
title = C_('event list time', 'All Day');
} else {
let date = event.date >= this._startDate ? event.date : event.end;
title = Util.formatTime(date, { timeOnly: true });
}
const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
if (event.date < this._startDate && !event.allDay) {
if (rtl)
title = '%s%s'.format(title, ELLIPSIS_CHAR);
else
title = '%s%s'.format(ELLIPSIS_CHAR, title);
}
if (event.end > this._endDate && !event.allDay) {
if (rtl)
title = '%s%s'.format(ELLIPSIS_CHAR, title);
else
title = '%s%s'.format(title, ELLIPSIS_CHAR);
}
return title;
}
_reloadEvents() {
if (this._eventSource.isLoading || this._reloading)
return;
this._reloading = true;
[...this._eventsList].forEach(c => c.destroy());
const events =
this._eventSource.getEvents(this._startDate, this._endDate);
for (let event of events) {
const box = new St.BoxLayout({
style_class: 'event-box',
vertical: true,
});
box.add(new St.Label({
text: event.summary,
style_class: 'event-summary',
}));
box.add(new St.Label({
text: this._formatEventTime(event),
style_class: 'event-time',
}));
this._eventsList.add_child(box);
}
if (this._eventsList.get_n_children() === 0) {
const placeholder = new St.Label({
text: _('No Events'),
style_class: 'event-placeholder',
});
this._eventsList.add_child(placeholder);
}
this._reloading = false;
this._sync();
}
vfunc_clicked() {
Main.overview.hide();
Main.panel.closeCalendar();
let appInfo = this._calendarApp;
if (appInfo.get_id() === 'org.gnome.Evolution.desktop') {
const app = this._appSys.lookup_app('evolution-calendar.desktop');
if (app)
appInfo = app.app_info;
}
appInfo.launch([], global.create_app_launch_context(0, -1));
}
_appInstalledChanged() {
const apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
if (apps && (apps.length > 0)) {
const app = Gio.AppInfo.get_default_for_type('text/calendar', false);
const defaultInRecommended = apps.some(a => a.equal(app));
this._calendarApp = defaultInRecommended ? app : apps[0];
} else {
this._calendarApp = null;
}
return this._sync();
}
_sync() {
this.visible = this._eventSource && this._eventSource.hasCalendars;
this.reactive = this._calendarApp !== null;
}
});
var WorldClocksSection = GObject.registerClass(
class WorldClocksSection extends St.Button {
_init() {
@ -486,13 +300,13 @@ class WeatherSection extends St.Button {
this.child = box;
let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' });
this._titleLabel = new St.Label({
titleBox.add_child(new St.Label({
style_class: 'weather-header',
x_align: Clutter.ActorAlign.START,
x_expand: true,
y_align: Clutter.ActorAlign.END,
});
titleBox.add_child(this._titleLabel);
text: _('Weather'),
}));
box.add_child(titleBox);
this._titleLocation = new St.Label({
@ -619,8 +433,10 @@ class WeatherSection extends St.Button {
_updateForecasts() {
this._forecastGrid.destroy_all_children();
if (!this._weatherClient.hasLocation)
if (!this._weatherClient.hasLocation) {
this._setStatusLabel(_("Select a location…"));
return;
}
const { info } = this._weatherClient;
this._titleLocation.text = this._findBestLocationName(info.location);
@ -647,12 +463,6 @@ class WeatherSection extends St.Button {
if (!this.visible)
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._updateForecasts();
@ -751,8 +561,8 @@ class FreezableBinLayout extends Clutter.BinLayout {
return this._savedHeight;
}
vfunc_allocate(container, allocation) {
super.vfunc_allocate(container, allocation);
vfunc_allocate(container, allocation, flags) {
super.vfunc_allocate(container, allocation, flags);
let [width, height] = allocation.get_size();
this._savedWidth = [width, width];
@ -819,7 +629,7 @@ class DateMenuButton extends PanelMenu.Button {
this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
let date = _gDateTimeToDate(datetime);
layout.frozen = !_isToday(date);
this._eventsItem.setDate(date);
this._messageList.setDate(date);
});
this.menu.connect('open-state-changed', (menu, isOpen) => {
@ -828,7 +638,7 @@ class DateMenuButton extends PanelMenu.Button {
let now = new Date();
this._calendar.setDate(now);
this._date.setDate(now);
this._eventsItem.setDate(now);
this._messageList.setDate(now);
}
});
@ -859,9 +669,6 @@ class DateMenuButton extends PanelMenu.Button {
style_class: 'datemenu-displays-box' });
this._displaysSection.add_actor(displaysBox);
this._eventsItem = new EventsSection();
displaysBox.add_child(this._eventsItem);
this._clocksItem = new WorldClocksSection();
displaysBox.add_child(this._clocksItem);
@ -887,7 +694,7 @@ class DateMenuButton extends PanelMenu.Button {
this._eventSource.destroy();
this._calendar.setEventSource(eventSource);
this._eventsItem.setEventSource(eventSource);
this._messageList.setEventSource(eventSource);
this._eventSource = eventSource;
}

View File

@ -375,29 +375,19 @@ var _Draggable = class _Draggable {
this._dragActorSource = undefined;
this._dragOrigParent = this.actor.get_parent();
this._dragActorHadFixedPos = this._dragActor.fixed_position_set;
this._dragOrigX = this._dragActor.allocation.x1;
this._dragOrigY = this._dragActor.allocation.y1;
this._dragOrigWidth = this._dragActor.allocation.get_width();
this._dragOrigHeight = this._dragActor.allocation.get_height();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
// When the actor gets reparented to the uiGroup, it will be
// allocated its preferred size, so use that size instead of the
// current allocation size.
const [, newAllocatedWidth] = this._dragActor.get_preferred_width(-1);
const [, newAllocatedHeight] = this._dragActor.get_preferred_height(-1);
const transformedExtents = this._dragActor.get_transformed_extents();
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
this._dragActor.set_scale(
transformedExtents.get_width() / newAllocatedWidth,
transformedExtents.get_height() / newAllocatedHeight);
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this._dragActor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
this._dragOffsetX = transformedExtents.origin.x - this._dragStartX;
this._dragOffsetY = transformedExtents.origin.y - this._dragStartY;
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);
@ -427,10 +417,6 @@ var _Draggable = class _Draggable {
this._dragOffsetX -= transX;
this._dragOffsetY -= transY;
this._dragActor.set_position(
this._dragX + this._dragOffsetX,
this._dragY + this._dragOffsetY);
if (this._dragActorMaxSize != undefined) {
let [scaledWidth, scaledHeight] = this._dragActor.get_transformed_size();
let currentSize = Math.max(scaledWidth, scaledHeight);
@ -649,15 +635,9 @@ var _Draggable = class _Draggable {
if (parentWidth != 0)
parentScale = parentScaledWidth / parentWidth;
// Also adjust for the difference in the original actor width
// and the width it is now (children of uiGroup always get
// allocated their preferred size)
const childScaleX =
this._dragOrigWidth / this._dragActor.allocation.get_width();
x = parentX + parentScale * this._dragOrigX;
y = parentY + parentScale * this._dragOrigY;
scale = this._dragOrigScale * parentScale * childScaleX;
scale = this._dragOrigScale * parentScale;
} else {
// Snap back actor to its original stage position
x = this._snapBackX;
@ -738,10 +718,7 @@ var _Draggable = class _Draggable {
Main.uiGroup.remove_child(this._dragActor);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
if (this._dragActorHadFixedPos)
dragActor.set_position(this._dragOrigX, this._dragOrigY);
else
dragActor.fixed_position_set = false;
dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else {
dragActor.destroy();
}

View File

@ -18,7 +18,7 @@
*/
const { AccountsService, Clutter, Gio,
GLib, GObject, Pango, Polkit, Shell, St, UPowerGlib: UPower } = imports.gi;
GLib, GObject, Pango, Polkit, Shell, St } = imports.gi;
const CheckBox = imports.ui.checkBox;
const Dialog = imports.ui.dialog;
@ -31,30 +31,24 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
const _ITEM_ICON_SIZE = 64;
const LOW_BATTERY_THRESHOLD = 30;
const EndSessionDialogIface = loadInterfaceXML('org.gnome.SessionManager.EndSessionDialog');
const logoutDialogContent = {
subjectWithUser: C_("title", "Log Out %s"),
subject: C_("title", "Log Out"),
descriptionWithUser(user, seconds) {
return ngettext(
'%s will be logged out automatically in %d second.',
'%s will be logged out automatically in %d seconds.',
seconds).format(user, seconds);
return ngettext("%s will be logged out automatically in %d second.",
"%s will be logged out automatically in %d seconds.",
seconds).format(user, seconds);
},
description(seconds) {
return ngettext(
'You will be logged out automatically in %d second.',
'You will be logged out automatically in %d seconds.',
seconds).format(seconds);
return ngettext("You will be logged out automatically in %d second.",
"You will be logged out automatically in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: false,
confirmButtons: [{
signal: 'ConfirmedLogout',
label: C_('button', 'Log Out'),
}],
confirmButtons: [{ signal: 'ConfirmedLogout',
label: C_("button", "Log Out") }],
showOtherSessions: false,
};
@ -62,36 +56,30 @@ const shutdownDialogContent = {
subject: C_("title", "Power Off"),
subjectWithUpdates: C_("title", "Install Updates & Power Off"),
description(seconds) {
return ngettext(
'The system will power off automatically in %d second.',
'The system will power off automatically in %d seconds.',
seconds).format(seconds);
return ngettext("The system will power off automatically in %d second.",
"The system will power off automatically in %d seconds.",
seconds).format(seconds);
},
checkBoxText: C_("checkbox", "Install pending software updates"),
showBatteryWarning: true,
confirmButtons: [{
signal: 'ConfirmedShutdown',
label: C_('button', 'Power Off'),
}],
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
label: C_("button", "Power Off") }],
iconName: 'system-shutdown-symbolic',
showOtherSessions: true,
};
const restartDialogContent = {
subject: C_("title", "Restart"),
subjectWithUpdates: C_('title', 'Install Updates & Restart'),
description(seconds) {
return ngettext(
'The system will restart automatically in %d second.',
'The system will restart automatically in %d seconds.',
seconds).format(seconds);
return ngettext("The system will restart automatically in %d second.",
"The system will restart automatically in %d seconds.",
seconds).format(seconds);
},
checkBoxText: C_('checkbox', 'Install pending software updates'),
showBatteryWarning: true,
confirmButtons: [{
signal: 'ConfirmedReboot',
label: C_('button', 'Restart'),
}],
showBatteryWarning: false,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'view-refresh-symbolic',
showOtherSessions: true,
};
@ -100,16 +88,13 @@ const restartUpdateDialogContent = {
subject: C_("title", "Restart & Install Updates"),
description(seconds) {
return ngettext(
'The system will automatically restart and install updates in %d second.',
'The system will automatically restart and install updates in %d seconds.',
seconds).format(seconds);
return ngettext("The system will automatically restart and install updates in %d second.",
"The system will automatically restart and install updates in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: true,
confirmButtons: [{
signal: 'ConfirmedReboot',
label: C_('button', 'Restart &amp; Install'),
}],
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart &amp; Install") }],
unusedFutureButtonForTranslation: C_("button", "Install &amp; Power Off"),
unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
iconName: 'view-refresh-symbolic',
@ -127,10 +112,8 @@ const restartUpgradeDialogContent = {
},
disableTimer: true,
showBatteryWarning: false,
confirmButtons: [{
signal: 'ConfirmedReboot',
label: C_('button', 'Restart &amp; Install'),
}],
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart &amp; Install") }],
iconName: 'view-refresh-symbolic',
showOtherSessions: true,
};
@ -159,7 +142,7 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline');
const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
const UPowerIface = loadInterfaceXML('org.freedesktop.UPower.Device');
const UPowerIface = loadInterfaceXML('org.freedesktop.UPower');
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
function findAppFromInhibitor(inhibitor) {
@ -230,11 +213,6 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
destroyOnClose: false });
this._loginManager = LoginManager.getLoginManager();
this._loginManager.canRebootToBootLoaderMenu(
(canRebootToBootLoaderMenu, unusedNeedsAuth) => {
this._canRebootToBootLoaderMenu = canRebootToBootLoaderMenu;
});
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._updatesPermission = null;
@ -246,7 +224,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._powerProxy = new UPowerProxy(Gio.DBus.system,
'org.freedesktop.UPower',
'/org/freedesktop/UPower/devices/DisplayDevice',
'/org/freedesktop/UPower',
(proxy, error) => {
if (error) {
log(error.message);
@ -261,9 +239,6 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._totalSecondsToStayOpen = 0;
this._applications = [];
this._sessions = [];
this._capturedEventId = 0;
this._rebootButton = null;
this._rebootButtonAlt = null;
this.connect('destroy',
this._onDestroy.bind(this));
@ -281,7 +256,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._batteryWarning = new St.Label({
style_class: 'end-session-dialog-battery-warning',
text: _('Low battery power: please plug in before installing updates.'),
text: _('Running on battery power: Please plug in before installing updates.'),
});
this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._batteryWarning.clutter_text.line_wrap = true;
@ -303,7 +278,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
}
async _onPkOfflineProxyCreated(proxy, error) {
_onPkOfflineProxyCreated(proxy, error) {
if (error) {
log(error.message);
return;
@ -318,12 +293,15 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
}
// It only makes sense to check for this permission if PackageKit is available.
try {
this._updatesPermission = await Polkit.Permission.new(
'org.freedesktop.packagekit.trigger-offline-update', null, null);
} catch (e) {
log('No permission to trigger offline updates: %s'.format(e.toString()));
}
Polkit.Permission.new(
'org.freedesktop.packagekit.trigger-offline-update', null, null,
(source, res) => {
try {
this._updatesPermission = Polkit.Permission.new_finish(res);
} catch (e) {
log('No permission to trigger offline updates: %s'.format(e.toString()));
}
});
}
_onDestroy() {
@ -331,32 +309,6 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._user.disconnect(this._userChangedId);
}
_isDischargingBattery() {
return this._powerProxy.IsPresent &&
this._powerProxy.State !== UPower.DeviceState.CHARGING &&
this._powerProxy.State !== UPower.DeviceState.FULLY_CHARGED;
}
_isBatteryLow() {
return this._isDischargingBattery() && this._powerProxy.Percentage < LOW_BATTERY_THRESHOLD;
}
_shouldShowLowBatteryWarning(dialogContent) {
if (!dialogContent.showBatteryWarning)
return false;
if (!this._isBatteryLow())
return false;
if (this._checkBox.checked)
return true;
// Show the warning if updates have already been triggered, but
// the user doesn't have enough permissions to cancel them.
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
return this._updateInfo.UpdatePrepared && this._updateInfo.UpdateTriggered && !updatesAllowed;
}
_sync() {
let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED;
if (!open)
@ -370,7 +322,10 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
if (dialogContent.subjectWithUpdates && this._checkBox.checked)
subject = dialogContent.subjectWithUpdates;
this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
if (dialogContent.showBatteryWarning) {
this._batteryWarning.visible =
this._powerProxy.OnBattery && this._checkBox.checked;
}
let description;
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
@ -411,38 +366,16 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._sessionSection.visible = hasSessions;
}
_onCapturedEvent(actor, event) {
let altEnabled = false;
let type = event.type();
if (type !== Clutter.EventType.KEY_PRESS && type !== Clutter.EventType.KEY_RELEASE)
return Clutter.EVENT_PROPAGATE;
let key = event.get_key_symbol();
if (key !== Clutter.KEY_Alt_L && key !== Clutter.KEY_Alt_R)
return Clutter.EVENT_PROPAGATE;
if (type === Clutter.EventType.KEY_PRESS)
altEnabled = true;
this._rebootButton.visible = !altEnabled;
this._rebootButtonAlt.visible = altEnabled;
return Clutter.EVENT_PROPAGATE;
}
_updateButtons() {
this.clearButtons();
this.addButton({ action: this.cancel.bind(this),
label: _("Cancel"),
key: Clutter.KEY_Escape });
let dialogContent = DialogContent[this._type];
let buttons = [{ action: this.cancel.bind(this),
label: _("Cancel"),
key: Clutter.KEY_Escape }];
for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
let signal = dialogContent.confirmButtons[i].signal;
let label = dialogContent.confirmButtons[i].label;
let button = this.addButton({
buttons.push({
action: () => {
this.close(true);
let signalId = this.connect('closed', () => {
@ -452,34 +385,9 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
},
label,
});
// Add Alt "Boot Options" option to the Reboot button
if (this._canRebootToBootLoaderMenu && signal === 'ConfirmedReboot') {
this._rebootButton = button;
this._rebootButtonAlt = this.addButton({
action: () => {
this.close(true);
let signalId = this.connect('closed', () => {
this.disconnect(signalId);
this._confirmRebootToBootLoaderMenu();
});
},
label: C_('button', 'Boot Options'),
});
this._rebootButtonAlt.visible = false;
this._capturedEventId = global.stage.connect('captured-event',
this._onCapturedEvent.bind(this));
}
}
}
_stopAltCapture() {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
this._rebootButton = null;
this._rebootButtonAlt = null;
this.setButtons(buttons);
}
close(skipSignal) {
@ -491,21 +399,14 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
cancel() {
this._stopTimer();
this._stopAltCapture();
this._dbusImpl.emit_signal('Canceled', null);
this.close();
}
_confirmRebootToBootLoaderMenu() {
this._loginManager.setRebootToBootLoaderMenu();
this._confirm('ConfirmedReboot');
}
_confirm(signal) {
let callback = () => {
this._fadeOutDialog();
this._stopTimer();
this._stopAltCapture();
this._dbusImpl.emit_signal(signal, null);
};
@ -773,17 +674,19 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
if (dialogContent.showOtherSessions)
this._loadSessions();
let updateTriggered = this._updateInfo.UpdateTriggered;
let updatePrepared = this._updateInfo.UpdatePrepared;
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
this._checkBox.visible = dialogContent.checkBoxText && this._updateInfo.UpdatePrepared && updatesAllowed;
this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed;
this._checkBox.checked = updatePrepared && updateTriggered;
if (this._type === DialogType.UPGRADE_RESTART)
this._checkBox.checked = this._checkBox.visible && this._updateInfo.UpdateTriggered && !this._isDischargingBattery();
else
this._checkBox.checked = this._checkBox.visible && !this._isBatteryLow();
this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
// We show the warning either together with the checkbox, or when
// updates have already been triggered, but the user doesn't have
// enough permissions to cancel them.
this._batteryWarning.visible = dialogContent.showBatteryWarning &&
(this._checkBox.visible || updatePrepared && updateTriggered && !updatesAllowed);
this._updateButtons();

View File

@ -10,20 +10,11 @@ imports.gi.versions.Gtk = '3.0';
imports.gi.versions.TelepathyGLib = '0.12';
imports.gi.versions.TelepathyLogger = '0.2';
const { Clutter, Gio, GLib, GObject, Meta, Polkit, Shell, St } = imports.gi;
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const Gettext = imports.gettext;
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;
@ -134,14 +125,7 @@ function _easeActor(actor, params) {
actor.set_easing_mode(params.mode);
delete params.mode;
const prepare = () => {
Meta.disable_unredirect_for_display(global.display);
global.begin_work();
};
const cleanup = () => {
Meta.enable_unredirect_for_display(global.display);
global.end_work();
};
let cleanup = () => Meta.enable_unredirect_for_display(global.display);
let callback = _makeEaseCallback(params, cleanup);
// cancel overwritten transitions
@ -156,9 +140,9 @@ function _easeActor(actor, params) {
.find(t => t !== null);
if (transition && transition.delay)
transition.connect('started', () => prepare());
transition.connect('started', () => Meta.disable_unredirect_for_display(global.display));
else
prepare();
Meta.disable_unredirect_for_display(global.display);
if (transition) {
transition.set({ repeatCount, autoReverse });
@ -198,14 +182,7 @@ function _easeActorProperty(actor, propName, target, params) {
if (actor instanceof Clutter.Actor && !actor.mapped)
duration = 0;
const prepare = () => {
Meta.disable_unredirect_for_display(global.display);
global.begin_work();
};
const cleanup = () => {
Meta.enable_unredirect_for_display(global.display);
global.end_work();
};
let cleanup = () => Meta.enable_unredirect_for_display(global.display);
let callback = _makeEaseCallback(params, cleanup);
// cancel overwritten transition
@ -217,7 +194,7 @@ function _easeActorProperty(actor, propName, target, params) {
if (!isReversed)
obj[prop] = target;
prepare();
Meta.disable_unredirect_for_display(global.display);
callback(true);
return;
@ -236,9 +213,9 @@ function _easeActorProperty(actor, propName, target, params) {
transition.set_to(target);
if (transition.delay)
transition.connect('started', () => prepare());
transition.connect('started', () => Meta.disable_unredirect_for_display(global.display));
else
prepare();
Meta.disable_unredirect_for_display(global.display);
transition.connect('stopped', (t, finished) => callback(finished));
}
@ -259,15 +236,16 @@ function _loggingFunc(...args) {
}
function init() {
// Add some bindings to the global JS namespace
globalThis.global = Shell.Global.get();
// Add some bindings to the global JS namespace; (gjs keeps the web
// browser convention of having that namespace be called 'window'.)
window.global = Shell.Global.get();
globalThis.log = _loggingFunc;
window.log = _loggingFunc;
globalThis._ = Gettext.gettext;
globalThis.C_ = Gettext.pgettext;
globalThis.ngettext = Gettext.ngettext;
globalThis.N_ = s => s;
window._ = Gettext.gettext;
window.C_ = Gettext.pgettext;
window.ngettext = Gettext.ngettext;
window.N_ = s => s;
GObject.gtypeNameBasedOnJSPath = true;
@ -299,11 +277,6 @@ function init() {
_easeActorProperty(this, 'value', target, params);
};
Clutter.Actor.prototype[Symbol.iterator] = function* () {
for (let c = this.get_first_child(); c; c = c.get_next_sibling())
yield c;
};
Clutter.Actor.prototype.toString = function () {
return St.describe_actor(this);
};
@ -374,12 +347,10 @@ function init() {
// OK, now things are initialized enough that we can import shell JS
const Format = imports.format;
const Tweener = imports.ui.tweener;
Tweener.init();
String.prototype.format = Format.format;
Math.clamp = function (x, lower, upper) {
return Math.min(Math.max(x, lower), upper);
};
}
// adjustAnimationTime:

View File

@ -230,7 +230,8 @@ var ExtensionManager = class {
null,
Gio.DBusCallFlags.NONE,
-1,
null);
null,
(conn, res) => conn.call_finish(res));
return true;
}
@ -260,8 +261,7 @@ var ExtensionManager = class {
if (!extension)
return;
const message = error instanceof Error
? error.message : error.toString();
let message = error.toString();
extension.error = message;
extension.state = ExtensionState.ERROR;
@ -486,15 +486,19 @@ var ExtensionManager = class {
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions
.filter(uuid => !this._enabledExtensions.includes(uuid))
.forEach(uuid => this._callExtensionEnable(uuid));
newEnabledExtensions.filter(
uuid => !this._enabledExtensions.includes(uuid)
).forEach(uuid => {
this._callExtensionEnable(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
this._extensionOrder
.filter(uuid => !newEnabledExtensions.includes(uuid))
.reverse().forEach(uuid => this._callExtensionDisable(uuid));
this._extensionOrder.filter(
uuid => !newEnabledExtensions.includes(uuid)
).reverse().forEach(uuid => {
this._callExtensionDisable(uuid);
});
this._enabledExtensions = newEnabledExtensions;
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ const PermissionStore = imports.misc.permissionStore;
const WAYLAND_KEYBINDINGS_SCHEMA = 'org.gnome.mutter.wayland.keybindings';
const APP_ALLOWLIST = ['gnome-control-center.desktop'];
const APP_WHITELIST = ['gnome-control-center.desktop'];
const APP_PERMISSIONS_TABLE = 'gnome';
const APP_PERMISSIONS_ID = 'shortcuts-inhibitor';
const GRANTED = 'GRANTED';
@ -118,7 +118,7 @@ var InhibitShortcutsDialog = GObject.registerClass({
}
vfunc_show() {
if (this._app && APP_ALLOWLIST.includes(this._app.get_id())) {
if (this._app && APP_WHITELIST.includes(this._app.get_id())) {
this._emitResponse(DialogResponse.ALLOW);
return;
}

View File

@ -79,7 +79,7 @@ class AspectContainer extends St.Widget {
return [min, nat];
}
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
if (box.get_width() > 0 && box.get_height() > 0) {
let sizeRatio = box.get_width() / box.get_height();
@ -97,7 +97,7 @@ class AspectContainer extends St.Widget {
}
}
super.vfunc_allocate(box);
super.vfunc_allocate(box, flags);
}
});
@ -740,7 +740,7 @@ var EmojiPager = GObject.registerClass({
_onPan(action) {
let [dist_, dx, dy_] = action.get_motion_delta(0);
this.delta += dx;
this.delta = this.delta + dx;
if (this._currentKey != null) {
this._currentKey.cancel();
@ -953,7 +953,8 @@ var EmojiSelection = GObject.registerClass({
this.add_child(this._emojiPager);
this._pageIndicator = new PageIndicators.PageIndicators(
Clutter.Orientation.HORIZONTAL);
Clutter.Orientation.HORIZONTAL
);
this.add_child(this._pageIndicator);
this._pageIndicator.setReactive(false);
@ -1136,7 +1137,7 @@ var KeyboardManager = class KeyBoardManager {
this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this));
this._lastDevice = null;
global.backend.connect('last-device-changed', (backend, device) => {
Meta.get_backend().connect('last-device-changed', (backend, device) => {
if (device.device_type === Clutter.InputDeviceType.KEYBOARD_DEVICE)
return;

View File

@ -6,6 +6,7 @@ const Signals = imports.signals;
const Background = imports.ui.background;
const BackgroundMenu = imports.ui.backgroundMenu;
const LoginManager = imports.misc.loginManager;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
@ -245,7 +246,7 @@ var LayoutManager = GObject.registerClass({
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true,
trackFullscreen: true });
this.panelBox.connect('notify::allocation',
this.panelBox.connect('allocation-changed',
this._panelBoxChanged.bind(this));
this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
@ -294,6 +295,18 @@ var LayoutManager = GObject.registerClass({
monitorManager.connect('monitors-changed',
this._monitorsChanged.bind(this));
this._monitorsChanged();
// NVIDIA drivers don't preserve FBO contents across
// suspend/resume, see
// https://bugzilla.gnome.org/show_bug.cgi?id=739178
if (Shell.util_need_background_refresh()) {
LoginManager.getLoginManager().connect('prepare-for-sleep',
(lm, suspending) => {
if (suspending)
return;
Meta.Background.refresh_all();
});
}
}
// This is called by Main after everything else is constructed
@ -1202,8 +1215,7 @@ class HotCorner extends Clutter.Actor {
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
this._corner.set_position(this.width - this._corner.width, 0);
this.set_pivot_point(1.0, 0.0);
this.translation_x = -this.width;
this.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}

View File

@ -27,11 +27,13 @@ var RadialShaderEffect = GObject.registerClass({
'brightness': GObject.ParamSpec.float(
'brightness', 'brightness', 'brightness',
GObject.ParamFlags.READWRITE,
0, 1, 1),
0, 1, 1
),
'sharpness': GObject.ParamSpec.float(
'sharpness', 'sharpness', 'sharpness',
GObject.ParamFlags.READWRITE,
0, 1, 0),
0, 1, 0
),
},
}, class RadialShaderEffect extends Shell.GLSLEffect {
_init(params) {

View File

@ -37,9 +37,10 @@ const LG_ANIMATION_TIME = 500;
function _getAutoCompleteGlobalKeywords() {
const keywords = ['true', 'false', 'null', 'new'];
// Don't add the private properties of globalThis (i.e., ones starting with '_')
const windowProperties = Object.getOwnPropertyNames(globalThis).filter(
a => a.charAt(0) !== '_');
// Don't add the private properties of window (i.e., ones starting with '_')
const windowProperties = Object.getOwnPropertyNames(window).filter(
a => a.charAt(0) != '_'
);
const headerProperties = JsParse.getDeclaredConstants(commandHeader);
return keywords.concat(windowProperties).concat(headerProperties);
@ -554,8 +555,8 @@ var Inspector = GObject.registerClass({
this._lookingGlass = lookingGlass;
}
vfunc_allocate(box) {
this.set_allocation(box);
vfunc_allocate(box, flags) {
this.set_allocation(box, flags);
if (!this._eventHandler)
return;
@ -570,7 +571,7 @@ var Inspector = GObject.registerClass({
childBox.x2 = childBox.x1 + natWidth;
childBox.y1 = primary.y + Math.floor((primary.height - natHeight) / 2);
childBox.y2 = childBox.y1 + natHeight;
this._eventHandler.allocate(childBox);
this._eventHandler.allocate(childBox, flags);
}
_close() {
@ -802,191 +803,6 @@ var Extensions = GObject.registerClass({
}
});
var ActorLink = GObject.registerClass({
Signals: {
'inspect-actor': {},
},
}, class ActorLink extends St.Button {
_init(actor) {
this._arrow = new St.Icon({
icon_name: 'pan-end-symbolic',
icon_size: 8,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
});
const label = new St.Label({
text: actor.toString(),
x_align: Clutter.ActorAlign.START,
});
const inspectButton = new St.Button({
child: new St.Icon({
icon_name: 'insert-object-symbolic',
icon_size: 12,
y_align: Clutter.ActorAlign.CENTER,
}),
reactive: true,
x_expand: true,
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER,
});
inspectButton.connect('clicked', () => this.emit('inspect-actor'));
const box = new St.BoxLayout();
box.add_child(this._arrow);
box.add_child(label);
box.add_child(inspectButton);
super._init({
reactive: true,
track_hover: true,
toggle_mode: true,
style_class: 'actor-link',
child: box,
x_align: Clutter.ActorAlign.START,
});
this._actor = actor;
}
vfunc_clicked() {
this._arrow.ease({
rotation_angle_z: this.checked ? 90 : 0,
duration: 250,
});
}
});
var ActorTreeViewer = GObject.registerClass(
class ActorTreeViewer extends St.BoxLayout {
_init(lookingGlass) {
super._init();
this._lookingGlass = lookingGlass;
this._actorData = new Map();
}
_showActorChildren(actor) {
const data = this._actorData.get(actor);
if (!data || data.visible)
return;
data.visible = true;
data.actorAddedId = actor.connect('actor-added', (container, child) => {
this._addActor(data.children, child);
});
data.actorRemovedId = actor.connect('actor-removed', (container, child) => {
this._removeActor(child);
});
for (let child of actor)
this._addActor(data.children, child);
}
_hideActorChildren(actor) {
const data = this._actorData.get(actor);
if (!data || !data.visible)
return;
for (let child of actor)
this._removeActor(child);
data.visible = false;
if (data.actorAddedId > 0) {
actor.disconnect(data.actorAddedId);
data.actorAddedId = 0;
}
if (data.actorRemovedId > 0) {
actor.disconnect(data.actorRemovedId);
data.actorRemovedId = 0;
}
data.children.remove_all_children();
}
_addActor(container, actor) {
if (this._actorData.has(actor))
return;
if (actor === this._lookingGlass)
return;
const button = new ActorLink(actor);
button.connect('notify::checked', () => {
this._lookingGlass.setBorderPaintTarget(actor);
if (button.checked)
this._showActorChildren(actor);
else
this._hideActorChildren(actor);
});
button.connect('inspect-actor', () => {
this._lookingGlass.inspectObject(actor, button);
});
const mainContainer = new St.BoxLayout({ vertical: true });
const childrenContainer = new St.BoxLayout({
vertical: true,
style: 'padding: 0 0 0 18px',
});
mainContainer.add_child(button);
mainContainer.add_child(childrenContainer);
this._actorData.set(actor, {
button,
container: mainContainer,
children: childrenContainer,
visible: false,
actorAddedId: 0,
actorRemovedId: 0,
actorDestroyedId: actor.connect('destroy', () => this._removeActor(actor)),
});
let belowChild = null;
const nextSibling = actor.get_next_sibling();
if (nextSibling && this._actorData.has(nextSibling))
belowChild = this._actorData.get(nextSibling).container;
container.insert_child_above(mainContainer, belowChild);
}
_removeActor(actor) {
const data = this._actorData.get(actor);
if (!data)
return;
for (let child of actor)
this._removeActor(child);
if (data.actorAddedId > 0) {
actor.disconnect(data.actorAddedId);
data.actorAddedId = 0;
}
if (data.actorRemovedId > 0) {
actor.disconnect(data.actorRemovedId);
data.actorRemovedId = 0;
}
if (data.actorDestroyedId > 0) {
actor.disconnect(data.actorDestroyedId);
data.actorDestroyedId = 0;
}
data.container.destroy();
this._actorData.delete(actor);
}
vfunc_map() {
super.vfunc_map();
this._addActor(this, global.stage);
}
vfunc_unmap() {
super.vfunc_unmap();
this._removeActor(global.stage);
}
});
var LookingGlass = GObject.registerClass(
class LookingGlass extends St.BoxLayout {
_init() {
@ -1018,9 +834,9 @@ class LookingGlass extends St.BoxLayout {
Main.uiGroup.add_actor(this);
Main.uiGroup.set_child_below_sibling(this,
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.connect('notify::allocation',
Main.layoutManager.panelBox.connect('allocation-changed',
this._queueResize.bind(this));
Main.layoutManager.keyboardBox.connect('notify::allocation',
Main.layoutManager.keyboardBox.connect('allocation-changed',
this._queueResize.bind(this));
this._objInspector = new ObjInspector(this);
@ -1102,9 +918,6 @@ class LookingGlass extends St.BoxLayout {
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions);
this._actorTreeViewer = new ActorTreeViewer(this);
notebook.appendPage('Actors', this._actorTreeViewer);
this._entry.clutter_text.connect('activate', (o, _e) => {
// Hide any completions we are currently showing
this._hideCompletions();

View File

@ -489,10 +489,7 @@ var Magnifier = class Magnifier {
_updateMouseSprite() {
this._updateSpriteTexture();
let [xHot, yHot] = this._cursorTracker.get_hot();
this._mouseSprite.set({
translation_x: -xHot,
translation_y: -yHot,
});
this._mouseSprite.set_anchor_point(xHot, yHot);
}
_updateSpriteTexture() {
@ -646,7 +643,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setClampScrollingAtEdges(
!this._settings.get_boolean(CLAMP_MODE_KEY));
!this._settings.get_boolean(CLAMP_MODE_KEY)
);
}
}
@ -654,7 +652,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setMouseTrackingMode(
this._settings.get_enum(MOUSE_TRACKING_KEY));
this._settings.get_enum(MOUSE_TRACKING_KEY)
);
}
}
@ -662,7 +661,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setFocusTrackingMode(
this._settings.get_enum(FOCUS_TRACKING_KEY));
this._settings.get_enum(FOCUS_TRACKING_KEY)
);
}
}
@ -670,7 +670,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setCaretTrackingMode(
this._settings.get_enum(CARET_TRACKING_KEY));
this._settings.get_enum(CARET_TRACKING_KEY)
);
}
}
@ -678,7 +679,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setInvertLightness(
this._settings.get_boolean(INVERT_LIGHTNESS_KEY));
this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
);
}
}
@ -686,7 +688,8 @@ var Magnifier = class Magnifier {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setColorSaturation(
this._settings.get_double(COLOR_SATURATION_KEY));
this._settings.get_double(COLOR_SATURATION_KEY)
);
}
}
@ -1938,8 +1941,9 @@ var MagShaderEffects = class MagShaderEffects {
// it modifies the brightness and/or contrast.
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
this._brightnessContrast.set_enabled(
bRed !== NO_CHANGE || bGreen !== NO_CHANGE || bBlue !== NO_CHANGE ||
cRed !== NO_CHANGE || cGreen !== NO_CHANGE || cBlue !== NO_CHANGE);
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE
);
}
/**
@ -1966,7 +1970,8 @@ var MagShaderEffects = class MagShaderEffects {
// a null first argument.
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
this._brightnessContrast.set_enabled(
cRed !== NO_CHANGE || cGreen !== NO_CHANGE || cBlue !== NO_CHANGE ||
bRed !== NO_CHANGE || bGreen !== NO_CHANGE || bBlue !== NO_CHANGE);
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
);
}
};

View File

@ -3,10 +3,10 @@
ctrlAltTabManager, padOsdService, osdWindowManager,
osdMonitorLabeler, shellMountOpDBusService, shellDBusService,
shellAccessDialogDBusService, shellAudioSelectionDBusService,
screenSaverDBus, uiGroup, magnifier, xdndHandler, keyboard,
kbdA11yDialog, introspectService, start, pushModal, popModal,
activateWindow, createLookingGlass, initializeDeferredWork,
getThemeStylesheet, setThemeStylesheet */
screenSaverDBus, screencastService, uiGroup, magnifier,
xdndHandler, keyboard, kbdA11yDialog, introspectService,
start, pushModal, popModal, activateWindow, createLookingGlass,
initializeDeferredWork, getThemeStylesheet, setThemeStylesheet */
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
@ -34,6 +34,7 @@ const LoginManager = imports.misc.loginManager;
const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Screencast = imports.ui.screencast;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
@ -45,7 +46,6 @@ const XdndHandler = imports.ui.xdndHandler;
const KbdA11yDialog = imports.ui.kbdA11yDialog;
const LocatePointer = imports.ui.locatePointer;
const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
@ -73,6 +73,7 @@ var shellAudioSelectionDBusService = null;
var shellDBusService = null;
var shellMountOpDBusService = null;
var screenSaverDBus = null;
var screencastService = null;
var modalCount = 0;
var actionMode = Shell.ActionMode.NONE;
var modalActorFocusStack = [];
@ -95,8 +96,6 @@ let _oskResource = null;
Gio._promisify(Gio._LocalFilePrototype, 'delete_async', 'delete_finish');
Gio._promisify(Gio._LocalFilePrototype, 'touch_async', 'touch_finish');
let _remoteAccessInhibited = false;
function _sessionUpdated() {
if (sessionMode.isPrimary)
_loadDefaultStylesheet();
@ -121,23 +120,12 @@ function _sessionUpdated() {
if (lookingGlass)
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() {
// These are here so we don't break compatibility.
global.logError = globalThis.log;
global.log = globalThis.log;
global.logError = window.log;
global.log = window.log;
// Chain up async errors reported from C
global.connect('notify-error', (global, msg, detail) => {
@ -152,10 +140,6 @@ function start() {
sessionMode.connect('updated', _sessionUpdated);
St.Settings.get().connect('notify::gtk-theme', _loadDefaultStylesheet);
// Initialize ParentalControlsManager before the UI
ParentalControlsManager.getDefault();
_initializeUI();
shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
@ -198,6 +182,7 @@ function _initializeUI() {
uiGroup = layoutManager.uiGroup;
padOsdService = new PadOsd.PadOsdService();
screencastService = new Screencast.ScreencastService();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindowManager = new OsdWindow.OsdWindowManager();
@ -814,7 +799,7 @@ function showRestartMessage(message) {
var AnimationsSettings = class {
constructor() {
let backend = global.backend;
let backend = Meta.get_backend();
if (!backend.is_rendering_hardware_accelerated()) {
St.Settings.get().inhibit_animations();
return;

View File

@ -4,6 +4,7 @@ const { Atk, Clutter, Gio, GLib,
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Calendar = imports.ui.calendar;
const Util = imports.misc.util;
var MESSAGE_ANIMATION_TIME = 100;
@ -284,12 +285,12 @@ var LabelExpanderLayout = GObject.registerClass({
return [min, nat];
}
vfunc_allocate(container, box) {
vfunc_allocate(container, box, flags) {
for (let i = 0; i < container.get_n_children(); i++) {
let child = container.get_child_at_index(i);
if (child.visible)
child.allocate(box);
child.allocate(box, flags);
}
}
@ -571,6 +572,7 @@ var MessageListSection = GObject.registerClass({
Main.sessionMode.disconnect(id);
});
this._date = new Date();
this._empty = true;
this._canClear = false;
this._sync();
@ -596,6 +598,13 @@ var MessageListSection = GObject.registerClass({
return true;
}
setDate(date) {
if (Calendar.sameDay(date, this._date))
return;
this._date = date;
this._sync();
}
addMessage(message, animate) {
this.addMessageAtIndex(message, -1, animate);
}

View File

@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported NotificationPolicy, NotificationGenericPolicy,
NotificationApplicationPolicy, Source, SourceActor,
NotificationApplicationPolicy, Source, SourceActor, SourceActorWithLabel,
SystemNotificationSource, MessageTray */
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
@ -655,6 +655,77 @@ class SourceActor extends St.Widget {
}
});
var SourceActorWithLabel = GObject.registerClass(
class SourceActorWithLabel extends SourceActor {
_init(source, size) {
super._init(source, size);
this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel,
layout_manager: new Clutter.BinLayout() });
this._counterBin.hide();
this._counterBin.connect('style-changed', () => {
let themeNode = this._counterBin.get_theme_node();
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
});
this.add_actor(this._counterBin);
this._countUpdatedId = this._source.connect('notify::count', this._updateCount.bind(this));
this._updateCount();
this.connect('destroy', () => {
this._source.disconnect(this._countUpdatedId);
});
}
vfunc_allocate(box, flags) {
super.vfunc_allocate(box, flags);
let childBox = new Clutter.ActorBox();
let [, , naturalWidth, naturalHeight] = this._counterBin.get_preferred_size();
let direction = this.get_text_direction();
if (direction == Clutter.TextDirection.LTR) {
// allocate on the right in LTR
childBox.x1 = box.x2 - naturalWidth;
childBox.x2 = box.x2;
} else {
// allocate on the left in RTL
childBox.x1 = 0;
childBox.x2 = naturalWidth;
}
childBox.y1 = box.y2 - naturalHeight;
childBox.y2 = box.y2;
this._counterBin.allocate(childBox, flags);
}
_updateCount() {
if (this._actorDestroyed)
return;
this._counterBin.visible = this._source.countVisible;
let text;
if (this._source.count < 100)
text = this._source.count.toString();
else
text = String.fromCharCode(0x22EF); // midline horizontal ellipsis
this._counterLabel.set_text(text);
}
});
var Source = GObject.registerClass({
Properties: {
'count': GObject.ParamSpec.int(
@ -1093,7 +1164,8 @@ var MessageTray = GObject.registerClass({
this._onNotificationDestroy.bind(this));
this._notificationQueue.push(notification);
this._notificationQueue.sort(
(n1, n2) => n2.urgency - n1.urgency);
(n1, n2) => n2.urgency - n1.urgency
);
this.emit('queue-changed');
}
}

View File

@ -2,6 +2,7 @@
const { Gio, GObject, Shell, St } = imports.gi;
const Signals = imports.signals;
const Calendar = imports.ui.calendar;
const Main = imports.ui.main;
const MessageList = imports.ui.messageList;
@ -251,6 +252,10 @@ class MediaSection extends MessageList.MessageListSection {
this._onProxyReady.bind(this));
}
_shouldShow() {
return !this.empty && Calendar.isToday(this._date);
}
get allowed() {
return !Main.sessionMode.isGreeter;
}

View File

@ -245,11 +245,11 @@ var Overview = class {
_unshadeBackgrounds() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
backgrounds[i].ease_property('@content.brightness', 1.0, {
backgrounds[i].ease_property('brightness', 1.0, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('@content.vignette-sharpness', 0.0, {
backgrounds[i].ease_property('vignette-sharpness', 0.0, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
@ -259,16 +259,14 @@ var Overview = class {
_shadeBackgrounds() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
backgrounds[i].ease_property('@content.brightness',
Lightbox.VIGNETTE_BRIGHTNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('@content.vignette-sharpness',
Lightbox.VIGNETTE_SHARPNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('brightness', Lightbox.VIGNETTE_BRIGHTNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('vignette-sharpness', Lightbox.VIGNETTE_SHARPNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
@ -403,7 +401,8 @@ var Overview = class {
_getDesktopClone() {
let windows = global.get_window_actors().filter(
w => w.meta_window.get_window_type() === Meta.WindowType.DESKTOP);
w => w.meta_window.get_window_type() == Meta.WindowType.DESKTOP
);
if (windows.length == 0)
return null;
@ -440,19 +439,19 @@ var Overview = class {
this.emit('windows-restacked', stackIndices);
}
beginItemDrag(source) {
this.emit('item-drag-begin', source);
beginItemDrag(_source) {
this.emit('item-drag-begin');
this._inItemDrag = true;
}
cancelledItemDrag(source) {
this.emit('item-drag-cancelled', source);
cancelledItemDrag(_source) {
this.emit('item-drag-cancelled');
}
endItemDrag(source) {
endItemDrag(_source) {
if (!this._inItemDrag)
return;
this.emit('item-drag-end', source);
this.emit('item-drag-end');
this._inItemDrag = false;
}
@ -578,7 +577,7 @@ var Overview = class {
this._activationTime = GLib.get_monotonic_time() / GLib.USEC_PER_SEC;
Meta.disable_unredirect_for_display(global.display);
this.viewSelector.animateToOverview();
this.viewSelector.show();
this._overview.opacity = 0;
this._overview.ease({

View File

@ -52,7 +52,7 @@ var SlideLayout = GObject.registerClass({
return [minWidth, natWidth];
}
vfunc_allocate(container, box) {
vfunc_allocate(container, box, flags) {
let child = container.get_first_child();
let availWidth = Math.round(box.x2 - box.x1);
@ -73,7 +73,7 @@ var SlideLayout = GObject.registerClass({
actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox);
child.allocate(actorBox, flags);
}
// eslint-disable-next-line camelcase
@ -395,10 +395,10 @@ class DashSpacer extends St.Widget {
});
var ControlsLayout = GObject.registerClass({
Signals: { 'allocation-changed': {} },
Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
}, class ControlsLayout extends Clutter.BinLayout {
vfunc_allocate(container, box) {
super.vfunc_allocate(container, box);
vfunc_allocate(container, box, flags) {
super.vfunc_allocate(container, box, flags);
this.emit('allocation-changed');
}
});
@ -423,7 +423,6 @@ class ControlsManager extends St.Widget {
let activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
this._workspaceAdjustment = new St.Adjustment({
actor: this,
value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
@ -455,6 +454,8 @@ class ControlsManager extends St.Widget {
this._group.add_child(this.viewSelector);
this._group.add_actor(this._thumbnailsSlider);
layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this));
Main.overview.connect('showing', this._updateSpacerVisibility.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
@ -477,6 +478,26 @@ class ControlsManager extends St.Widget {
this._workspaceAdjustment.value = activeIndex;
}
_updateWorkspacesGeometry() {
let [x, y] = this.get_transformed_position();
let [width, height] = this.get_transformed_size();
let geometry = { x, y, width, height };
let spacing = this.get_theme_node().get_length('spacing');
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
geometry.width -= dashWidth;
geometry.width -= thumbnailsWidth;
if (this.get_text_direction() == Clutter.TextDirection.LTR)
geometry.x += dashWidth;
else
geometry.x += thumbnailsWidth;
this.viewSelector.setWorkspacesFullGeometry(geometry);
}
_setVisibility() {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview

View File

@ -436,11 +436,11 @@ var PadDiagram = GObject.registerClass({
childBox.y1 = y - natHeight / 2;
childBox.y2 = y + natHeight / 2;
child.allocate(childBox);
child.allocate(childBox, 0);
}
vfunc_allocate(box) {
super.vfunc_allocate(box);
vfunc_allocate(box, flags) {
super.vfunc_allocate(box, flags);
if (this._handle === null)
return;

View File

@ -120,10 +120,6 @@ var PageIndicators = GObject.registerClass({
for (let i = 0; i < children.length; i++)
this._updateIndicator(children[i], i);
}
get nPages() {
return this._nPages;
}
});
var AnimatedPageIndicators = GObject.registerClass(

View File

@ -90,16 +90,18 @@ class AppMenu extends PopupMenu.PopupMenu {
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._detailsItem = this.addAction(_('Show Details'), async () => {
this._detailsItem = this.addAction(_("Show Details"), () => {
let id = this._app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']);
const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
bus.call(
'org.gnome.Software',
'/org/gnome/Software',
'org.gtk.Actions', 'Activate',
new GLib.Variant('(sava{sv})', ['details', [args], null]),
null, 0, -1, null);
Gio.DBus.get(Gio.BusType.SESSION, null, (o, res) => {
let bus = Gio.DBus.get_finish(res);
bus.call('org.gnome.Software',
'/org/gnome/Software',
'org.gtk.Actions', 'Activate',
GLib.Variant.new('(sava{sv})',
['details', [args], null]),
null, 0, -1, null);
});
});
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -284,7 +286,7 @@ var AppMenuButton = GObject.registerClass({
this.remove_all_transitions();
this.ease({
opacity: 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
mode: Clutter.Animation.EASE_OUT_QUAD,
duration: Overview.ANIMATION_TIME,
onComplete: () => this.hide(),
});
@ -675,7 +677,7 @@ class PanelCorner extends St.DrawingArea {
let borderWidth = node.get_length('-panel-corner-border-width');
this.set_size(cornerRadius, borderWidth + cornerRadius);
this.translation_y = -borderWidth;
this.set_anchor_point(0, borderWidth);
}
});
@ -736,11 +738,13 @@ class AggregateMenu extends PanelMenu.Button {
this._volume = new imports.ui.status.volume.Indicator();
this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.Indicator();
this._screencast = new imports.ui.status.screencast.Indicator();
this._location = new imports.ui.status.location.Indicator();
this._nightLight = new imports.ui.status.nightLight.Indicator();
this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
this._indicators.add_child(this._thunderbolt);
this._indicators.add_child(this._screencast);
this._indicators.add_child(this._location);
this._indicators.add_child(this._nightLight);
if (this._network)
@ -840,8 +844,8 @@ class Panel extends St.Widget {
return [0, 0];
}
vfunc_allocate(box) {
this.set_allocation(box);
vfunc_allocate(box, flags) {
this.set_allocation(box, flags);
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
@ -877,13 +881,13 @@ class Panel extends St.Widget {
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox);
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox);
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
@ -897,7 +901,7 @@ class Panel extends St.Widget {
0);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox);
this._rightBox.allocate(childBox, flags);
let cornerWidth, cornerHeight;
@ -907,7 +911,7 @@ class Panel extends St.Widget {
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.allocate(childBox);
this._leftCorner.allocate(childBox, flags);
[, cornerWidth] = this._rightCorner.get_preferred_width(-1);
[, cornerHeight] = this._rightCorner.get_preferred_height(-1);
@ -915,7 +919,7 @@ class Panel extends St.Widget {
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.allocate(childBox);
this._rightCorner.allocate(childBox, flags);
}
_tryDragWindow(event) {
@ -1154,9 +1158,10 @@ class Panel extends St.Widget {
_getDraggableWindowForPosition(stageX) {
let workspaceManager = global.workspace_manager;
const windows = workspaceManager.get_active_workspace().list_windows();
const allWindowsByStacking =
global.display.sort_windows_by_stacking(windows).reverse();
let workspace = workspaceManager.get_active_workspace();
let allWindowsByStacking = global.display.sort_windows_by_stacking(
workspace.list_windows()
).reverse();
return allWindowsByStacking.find(metaWindow => {
let rect = metaWindow.get_frame_rect();

View File

@ -59,8 +59,8 @@ class ButtonBox extends St.Widget {
return [0, 0];
}
vfunc_allocate(box) {
this.set_allocation(box);
vfunc_allocate(box, flags) {
this.set_allocation(box, flags);
let child = this.get_first_child();
if (!child)
@ -83,7 +83,7 @@ class ButtonBox extends St.Widget {
childBox.y1 = 0;
childBox.y2 = availHeight;
child.allocate(childBox);
child.allocate(childBox, flags);
}
_onDestroy() {

View File

@ -204,7 +204,7 @@ var RemoteSearchProvider = class {
g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name,
gFlags });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null);
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
this.appInfo = appInfo;
this.id = appInfo.get_id();

View File

@ -198,7 +198,7 @@ var ScreenShield = class {
let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
let inhibit = this._loginSession && this._loginSession.Active &&
!this._isActive && lockEnabled && !lockLocked && Main.sessionMode.unlockDialog;
!this._isActive && lockEnabled && !lockLocked;
if (inhibit) {
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
inhibitor => {
@ -345,7 +345,7 @@ var ScreenShield = class {
this._lockDialogGroup.remove_all_transitions();
if (animate) {
// Animate the lock screen out of screen
// Tween the lock screen out of screen
// if velocity is not specified (i.e. we come here from pressing ESC),
// use the same speed regardless of original position
// if velocity is specified, it's in pixels per milliseconds
@ -561,8 +561,7 @@ var ScreenShield = class {
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
if (!this._ensureUnlockDialog(true))
return;
this._ensureUnlockDialog(true);
this.actor.show();

146
js/ui/screencast.js Normal file
View File

@ -0,0 +1,146 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { Gio, GLib, Shell } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
const { loadInterfaceXML } = imports.misc.fileUtils;
const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
var ScreencastService = class {
constructor() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Map();
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
}
get isRecording() {
return this._recorders.size > 0;
}
_ensureRecorderForSender(sender) {
let recorder = this._recorders.get(sender);
if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage,
display: global.display });
recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
this._onNameVanished.bind(this));
this._recorders.set(sender, recorder);
this.emit('updated');
}
return recorder;
}
_sessionUpdated() {
if (Main.sessionMode.allowScreencast)
return;
for (let sender of this._recorders.keys())
this._stopRecordingForSender(sender);
}
_onNameVanished(connection, name) {
this._stopRecordingForSender(name);
}
_stopRecordingForSender(sender) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
recorder.close();
this._recorders.delete(sender);
this.emit('updated');
return true;
}
_applyOptionalParameters(recorder, options) {
for (let option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'])
recorder.set_pipeline(options['pipeline']);
if (options['framerate'])
recorder.set_framerate(options['framerate']);
if ('draw-cursor' in options)
recorder.set_draw_cursor(options['draw-cursor']);
}
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
if (!success)
this._stopRecordingForSender(sender);
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
ScreencastAreaAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
if (!success)
this._stopRecordingForSender(sender);
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
StopScreencastAsync(params, invocation) {
let success = this._stopRecordingForSender(invocation.get_sender());
invocation.return_value(GLib.Variant.new('(b)', [success]));
}
};
Signals.addSignalMethods(ScreencastService.prototype);

View File

@ -1,19 +1,12 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ScreenshotService */
const { Clutter, Gio, GObject, GLib, Meta, Shell, St } = imports.gi;
const { Clutter, Graphene, Gio, GObject, GLib, Meta, Shell, St } = imports.gi;
const GrabHelper = imports.ui.grabHelper;
const Lightbox = imports.ui.lightbox;
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 ScreenshotIface = loadInterfaceXML('org.gnome.Shell.Screenshot');
@ -165,7 +158,7 @@ var ScreenshotService = class {
return [x, y, width, height];
}
async ScreenshotAreaAsync(params, invocation) {
ScreenshotAreaAsync(params, invocation) {
let [x, y, width, height, flash, filename] = params;
[x, y, width, height] = this._scaleArea(x, y, width, height);
if (!this._checkArea(x, y, width, height)) {
@ -182,17 +175,21 @@ var ScreenshotService = class {
if (!stream)
return;
try {
let [area] =
await screenshot.screenshot_area(x, y, width, height, stream);
this._onScreenshotComplete(area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
screenshot.screenshot_area(x, y, width, height, stream,
(o, res) => {
try {
let [success_, area] =
screenshot.screenshot_area_finish(res);
this._onScreenshotComplete(
area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
});
}
async ScreenshotWindowAsync(params, invocation) {
ScreenshotWindowAsync(params, invocation) {
let [includeFrame, includeCursor, flash, filename] = params;
let screenshot = this._createScreenshot(invocation);
if (!screenshot)
@ -202,17 +199,21 @@ var ScreenshotService = class {
if (!stream)
return;
try {
let [area] =
await screenshot.screenshot_window(includeFrame, includeCursor, stream);
this._onScreenshotComplete(area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
screenshot.screenshot_window(includeFrame, includeCursor, stream,
(o, res) => {
try {
let [success_, area] =
screenshot.screenshot_window_finish(res);
this._onScreenshotComplete(
area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
});
}
async ScreenshotAsync(params, invocation) {
ScreenshotAsync(params, invocation) {
let [includeCursor, flash, filename] = params;
let screenshot = this._createScreenshot(invocation);
if (!screenshot)
@ -222,13 +223,18 @@ var ScreenshotService = class {
if (!stream)
return;
try {
let [area] = await screenshot.screenshot(includeCursor, stream);
this._onScreenshotComplete(area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
screenshot.screenshot(includeCursor, stream,
(o, res) => {
try {
let [success_, area] =
screenshot.screenshot_finish(res);
this._onScreenshotComplete(
area, stream, file, flash, invocation);
} catch (e) {
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(new GLib.Variant('(bs)', [false, '']));
}
});
}
async SelectAreaAsync(params, invocation) {
@ -261,22 +267,27 @@ var ScreenshotService = class {
}
async PickColorAsync(params, invocation) {
const screenshot = this._createScreenshot(invocation, false);
if (!screenshot)
return;
const pickPixel = new PickPixel(screenshot);
let pickPixel = new PickPixel();
try {
const color = await pickPixel.pickAsync();
const { red, green, blue } = color;
const retval = GLib.Variant.new('(a{sv})', [{
color: GLib.Variant.new('(ddd)', [
red / 255.0,
green / 255.0,
blue / 255.0,
]),
}]);
invocation.return_value(retval);
const coords = await pickPixel.pickAsync();
let screenshot = this._createScreenshot(invocation, false);
if (!screenshot)
return;
screenshot.pick_color(coords.x, coords.y, (_o, res) => {
let [success_, color] = screenshot.pick_color_finish(res);
let { red, green, blue } = color;
let retval = GLib.Variant.new('(a{sv})', [{
color: GLib.Variant.new('(ddd)', [
red / 255.0,
green / 255.0,
blue / 255.0,
]),
}]);
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(retval);
});
} catch (e) {
invocation.return_error_literal(
Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
@ -380,145 +391,12 @@ class SelectArea extends St.Widget {
}
});
var RecolorEffect = GObject.registerClass({
Properties: {
color: GObject.ParamSpec.boxed(
'color', 'color', 'replacement color',
GObject.ParamFlags.WRITABLE,
Clutter.Color.$gtype),
chroma: GObject.ParamSpec.boxed(
'chroma', 'chroma', 'color to replace',
GObject.ParamFlags.WRITABLE,
Clutter.Color.$gtype),
threshold: GObject.ParamSpec.float(
'threshold', 'threshold', 'threshold',
GObject.ParamFlags.WRITABLE,
0.0, 1.0, 0.0),
smoothing: GObject.ParamSpec.float(
'smoothing', 'smoothing', 'smoothing',
GObject.ParamFlags.WRITABLE,
0.0, 1.0, 0.0),
},
}, class RecolorEffect extends Shell.GLSLEffect {
_init(params) {
this._color = new Clutter.Color();
this._chroma = new Clutter.Color();
this._threshold = 0;
this._smoothing = 0;
this._colorLocation = null;
this._chromaLocation = null;
this._thresholdLocation = null;
this._smoothingLocation = null;
super._init(params);
this._colorLocation = this.get_uniform_location('recolor_color');
this._chromaLocation = this.get_uniform_location('chroma_color');
this._thresholdLocation = this.get_uniform_location('threshold');
this._smoothingLocation = this.get_uniform_location('smoothing');
this._updateColorUniform(this._colorLocation, this._color);
this._updateColorUniform(this._chromaLocation, this._chroma);
this._updateFloatUniform(this._thresholdLocation, this._threshold);
this._updateFloatUniform(this._smoothingLocation, this._smoothing);
}
_updateColorUniform(location, color) {
if (!location)
return;
this.set_uniform_float(location,
3, [color.red / 255, color.green / 255, color.blue / 255]);
this.queue_repaint();
}
_updateFloatUniform(location, value) {
if (!location)
return;
this.set_uniform_float(location, 1, [value]);
this.queue_repaint();
}
set color(c) {
if (this._color.equal(c))
return;
this._color = c;
this.notify('color');
this._updateColorUniform(this._colorLocation, this._color);
}
set chroma(c) {
if (this._chroma.equal(c))
return;
this._chroma = c;
this.notify('chroma');
this._updateColorUniform(this._chromaLocation, this._chroma);
}
set threshold(value) {
if (this._threshold === value)
return;
this._threshold = value;
this.notify('threshold');
this._updateFloatUniform(this._thresholdLocation, this._threshold);
}
set smoothing(value) {
if (this._smoothing === value)
return;
this._smoothing = value;
this.notify('smoothing');
this._updateFloatUniform(this._smoothingLocation, this._smoothing);
}
vfunc_build_pipeline() {
// Conversion parameters from https://en.wikipedia.org/wiki/YCbCr
const decl = `
vec3 rgb2yCrCb(vec3 c) { \n
float y = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b; \n
float cr = 0.7133 * (c.r - y); \n
float cb = 0.5643 * (c.b - y); \n
return vec3(y, cr, cb); \n
} \n
\n
uniform vec3 chroma_color; \n
uniform vec3 recolor_color; \n
uniform float threshold; \n
uniform float smoothing; \n`;
const src = `
vec3 mask = rgb2yCrCb(chroma_color.rgb); \n
vec3 yCrCb = rgb2yCrCb(cogl_color_out.rgb); \n
float blend = \n
smoothstep(threshold, \n
threshold + smoothing, \n
distance(yCrCb.gb, mask.gb)); \n
cogl_color_out.rgb = \n
mix(recolor_color, cogl_color_out.rgb, blend); \n`;
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, decl, src, false);
}
});
var PickPixel = GObject.registerClass(
class PickPixel extends St.Widget {
_init(screenshot) {
_init() {
super._init({ visible: false, reactive: true });
this._screenshot = screenshot;
this._result = null;
this._color = null;
this._inPick = false;
Main.uiGroup.add_actor(this);
@ -527,44 +405,16 @@ class PickPixel extends St.Widget {
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this.add_constraint(constraint);
const action = new Clutter.ClickAction();
action.connect('clicked', async () => {
await this._pickColor(...action.get_coords());
this._result = this._color;
this._grabHelper.ungrab();
});
this.add_action(action);
this._recolorEffect = new RecolorEffect({
chroma: new Clutter.Color({
red: 80,
green: 219,
blue: 181,
}),
threshold: 0.04,
smoothing: 0.07,
});
this._previewCursor = new St.Icon({
icon_name: 'color-pick',
icon_size: Meta.prefs_get_cursor_size(),
effect: this._recolorEffect,
visible: false,
});
Main.uiGroup.add_actor(this._previewCursor);
}
async pickAsync() {
global.display.set_cursor(Meta.Cursor.BLANK);
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this, null);
this.show();
this._pickColor(...global.get_pointer());
await this._grabHelper.grabAsync({ actor: this });
global.display.set_cursor(Meta.Cursor.DEFAULT);
this._previewCursor.destroy();
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.destroy();
@ -574,25 +424,10 @@ class PickPixel extends St.Widget {
return this._result;
}
async _pickColor(x, y) {
if (this._inPick)
return;
this._inPick = true;
this._previewCursor.set_position(x, y);
[this._color] = await this._screenshot.pick_color(x, y);
this._inPick = false;
if (!this._color)
return;
this._recolorEffect.color = this._color;
this._previewCursor.show();
}
vfunc_motion_event(motionEvent) {
const { x, y } = motionEvent;
this._pickColor(x, y);
vfunc_button_release_event(buttonEvent) {
let { x, y } = buttonEvent;
this._result = new Graphene.Point({ x, y });
this._grabHelper.ungrab();
return Clutter.EVENT_PROPAGATE;
}
});

View File

@ -21,13 +21,16 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
// When scripting an automated test we want to make a series of calls
// in a linear fashion, but we also want to be able to let the main
// loop run so actions can finish. For this reason we write the script
// as an async function that uses await when it wants to let the main
// as a generator function that yields when it want to let the main
// loop run.
//
// await Scripting.sleep(1000);
// yield Scripting.sleep(1000);
// main.overview.show();
// await Scripting.waitLeisure();
// yield Scripting.waitLeisure();
//
// While it isn't important to the person writing the script, the actual
// yielded result is a function that the caller uses to provide the
// callback for resuming the script.
/**
* sleep:
@ -282,11 +285,13 @@ function _collect(scriptModule, outputFile) {
}
async function _runPerfScript(scriptModule, outputFile) {
try {
await scriptModule.run();
} catch (err) {
log(`Script failed: ${err}\n${err.stack}`);
Meta.exit(Meta.ExitCode.ERROR);
for (let step of scriptModule.run()) {
try {
await step; // eslint-disable-line no-await-in-loop
} catch (err) {
log(`Script failed: ${err}\n${err.stack}`);
Meta.exit(Meta.ExitCode.ERROR);
}
}
try {

View File

@ -6,17 +6,17 @@ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const AppDisplay = imports.ui.appDisplay;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const ParentalControlsManager = imports.misc.parentalControlsManager;
const RemoteSearch = imports.ui.remoteSearch;
const Util = imports.misc.util;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
var MAX_LIST_SEARCH_RESULTS_ROWS = 5;
var MAX_GRID_SEARCH_RESULTS_ROWS = 1;
var MaxWidthBox = GObject.registerClass(
class MaxWidthBox extends St.BoxLayout {
vfunc_allocate(box) {
vfunc_allocate(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
@ -28,7 +28,7 @@ class MaxWidthBox extends St.BoxLayout {
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
super.vfunc_allocate(adjustedBox);
super.vfunc_allocate(adjustedBox, flags);
}
});
@ -219,7 +219,8 @@ var SearchResultsBase = GObject.registerClass({
_ensureResultActors(results, callback) {
let metasNeeded = results.filter(
resultId => this._resultDisplays[resultId] === undefined);
resultId => this._resultDisplays[resultId] === undefined
);
if (metasNeeded.length === 0) {
callback(true);
@ -348,140 +349,18 @@ class ListSearchResults extends SearchResultsBase {
}
});
var GridSearchResultsLayout = GObject.registerClass({
Properties: {
'spacing': GObject.ParamSpec.int('spacing', 'Spacing', 'Spacing',
GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0),
},
}, class GridSearchResultsLayout extends Clutter.LayoutManager {
_init() {
super._init();
this._spacing = 0;
}
vfunc_set_container(container) {
this._container = container;
}
vfunc_get_preferred_width(container, forHeight) {
let minWidth = 0;
let natWidth = 0;
let first = true;
for (let child of container) {
if (!child.visible)
continue;
const [childMinWidth, childNatWidth] = child.get_preferred_width(forHeight);
minWidth = Math.max(minWidth, childMinWidth);
natWidth += childNatWidth;
if (first)
first = false;
else
natWidth += this._spacing;
}
return [minWidth, natWidth];
}
vfunc_get_preferred_height(container, forWidth) {
let minHeight = 0;
let natHeight = 0;
for (let child of container) {
if (!child.visible)
continue;
const [childMinHeight, childNatHeight] = child.get_preferred_height(forWidth);
minHeight = Math.max(minHeight, childMinHeight);
natHeight = Math.max(natHeight, childNatHeight);
}
return [minHeight, natHeight];
}
vfunc_allocate(container, box) {
const width = box.get_width();
const childBox = new Clutter.ActorBox();
childBox.x1 = 0;
childBox.y1 = 0;
let first = true;
for (let child of container) {
if (!child.visible)
continue;
if (first)
first = false;
else
childBox.x1 += this._spacing;
const [childWidth] = child.get_preferred_width(-1);
const [childHeight] = child.get_preferred_height(-1);
childBox.set_size(childWidth, childHeight);
if (childBox.x1 + childWidth > width)
return;
child.allocate(childBox);
childBox.x1 += childWidth;
}
}
columnsForWidth(width) {
if (!this._container)
return -1;
const [minWidth] = this.get_preferred_width(this._container, -1);
if (minWidth === 0)
return -1;
let nCols = 0;
while (width > minWidth) {
width -= minWidth;
if (nCols > 0)
width -= this._spacing;
nCols++;
}
return nCols;
}
get spacing() {
return this._spacing;
}
set spacing(v) {
if (this._spacing === v)
return;
this._spacing = v;
this.layout_changed();
}
});
var GridSearchResults = GObject.registerClass(
class GridSearchResults extends SearchResultsBase {
_init(provider, resultsView) {
super._init(provider, resultsView);
this._grid = new St.Widget({ style_class: 'grid-search-results' });
this._grid.layout_manager = new GridSearchResultsLayout();
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._grid.connect('style-changed', () => {
const node = this._grid.get_theme_node();
this._grid.layout_manager.spacing = node.get_length('spacing');
});
this._bin = new St.Bin({ x_align: Clutter.ActorAlign.CENTER });
this._bin.set_child(this._grid);
this._resultDisplayBin.set_child(new St.Bin({
child: this._grid,
x_align: Clutter.ActorAlign.CENTER,
}));
this._resultDisplayBin.set_child(this._bin);
}
_onDestroy() {
@ -521,11 +400,12 @@ class GridSearchResults extends SearchResultsBase {
if (width == 0)
return -1;
return this._grid.layout_manager.columnsForWidth(width);
let nCols = this._grid.columnsForWidth(width);
return nCols * this._grid.getRowLimit();
}
_clearResultDisplay() {
this._grid.remove_all_children();
this._grid.removeAll();
}
_createResultDisplay(meta) {
@ -534,15 +414,14 @@ class GridSearchResults extends SearchResultsBase {
}
_addItem(display) {
this._grid.add_child(display);
this._grid.addItem(display);
}
getFirstResult() {
for (let child of this._grid) {
if (child.visible)
return child;
}
return null;
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0);
else
return null;
}
});
@ -552,9 +431,6 @@ var SearchResultsView = GObject.registerClass({
_init() {
super._init({ name: 'searchResults', vertical: true });
this._parentalControlsManager = ParentalControlsManager.getDefault();
this._parentalControlsManager.connect('app-filter-changed', this._reloadRemoteProviders.bind(this));
this._content = new MaxWidthBox({
name: 'searchResultsContent',
vertical: true,
@ -629,11 +505,6 @@ var SearchResultsView = GObject.registerClass({
_registerProvider(provider) {
provider.searchInProgress = false;
// Filter out unwanted providers.
if (provider.appInfo && !this._parentalControlsManager.shouldShowApp(provider.appInfo))
return;
this._providers.push(provider);
this._ensureProviderDisplay(provider);
}

View File

@ -113,10 +113,10 @@ function _loadMode(file, info) {
}
_modes[modeName] = {};
const excludedProps = ['unlockDialog'];
let propBlacklist = ['unlockDialog'];
for (let prop in _modes[DEFAULT_MODE]) {
if (newMode[prop] !== undefined &&
!excludedProps.includes(prop))
!propBlacklist.includes(prop))
_modes[modeName][prop] = newMode[prop];
}
_modes[modeName]['isPrimary'] = true;

View File

@ -295,8 +295,8 @@ var ShellMountPasswordDialog = GObject.registerClass({
this._keyfilesLabel = new St.Label({ visible: false });
this._keyfilesLabel.clutter_text.set_markup(
/* Translators: %s is the Disks application */
_('To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead.')
.format(disksApp.get_name()));
_("To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead.").format(disksApp.get_name())
);
this._keyfilesLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._keyfilesLabel.clutter_text.line_wrap = true;
content.add_child(this._keyfilesLabel);
@ -464,7 +464,8 @@ var ShellMountPasswordDialog = GObject.registerClass({
/* Translators: %s is the Disks application */
_("Unable to start %s").format(app.get_name()),
/* Translators: %s is the Disks application */
_('Couldnt find the %s application').format(app.get_name()));
_("Couldnt find the %s application").format(app.get_name())
);
}
this._onCancelButton();
}

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
const { Gio, GLib, GnomeBluetooth, GObject } = imports.gi;
const { Gio, GnomeBluetooth, GObject } = imports.gi;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
@ -35,7 +35,7 @@ class Indicator extends PanelMenu.SystemIndicator {
this._sync();
});
this._proxy.connect('g-properties-changed', this._queueSync.bind(this));
this._proxy.connect('g-properties-changed', this._sync.bind(this));
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
@ -49,27 +49,15 @@ class Indicator extends PanelMenu.SystemIndicator {
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item);
this._syncId = 0;
this._adapter = null;
this._client = new GnomeBluetooth.Client();
this._model = this._client.get_model();
this._model.connect('row-deleted', this._queueSync.bind(this));
this._model.connect('row-changed', this._queueSync.bind(this));
this._model.connect('row-changed', this._sync.bind(this));
this._model.connect('row-deleted', this._sync.bind(this));
this._model.connect('row-inserted', this._sync.bind(this));
Main.sessionMode.connect('updated', this._sync.bind(this));
this._sync();
}
_setHadSetupDevices(value) {
if (this._hadSetupDevices === value)
return;
this._hadSetupDevices = value;
global.settings.set_boolean(
HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
}
_getDefaultAdapter() {
let [ret, iter] = this._model.get_iter_first();
while (ret) {
@ -84,53 +72,46 @@ class Indicator extends PanelMenu.SystemIndicator {
return null;
}
_getDeviceInfos(adapter) {
// nDevices is the number of devices setup for the current default
// 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)
return [];
return [this._hadSetupDevices ? 1 : -1, -1];
let deviceInfos = [];
let nConnectedDevices = 0;
let nDevices = 0;
let [ret, iter] = this._model.iter_children(adapter);
while (ret) {
const isPaired = this._model.get_value(iter,
GnomeBluetooth.Column.PAIRED);
const isTrusted = this._model.get_value(iter,
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 isConnected = this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED);
if (isConnected)
nConnectedDevices++;
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);
}
return deviceInfos;
}
if (this._hadSetupDevices != (nDevices > 0)) {
this._hadSetupDevices = !this._hadSetupDevices;
global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
}
_queueSync() {
if (this._syncId)
return;
this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._syncId = 0;
this._sync();
return GLib.SOURCE_REMOVE;
});
return [nDevices, nConnectedDevices];
}
_sync() {
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 [nDevices, nConnectedDevices] = this._getNDevices();
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive);
@ -138,21 +119,19 @@ class Indicator extends PanelMenu.SystemIndicator {
// Remember if there were setup devices and show the menu
// if we've seen setup devices and we're not hard blocked
if (this._hadSetupDevices)
if (nDevices > 0)
this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode;
else
this._item.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
if (nConnectedDevices > 1)
if (nConnectedDevices > 0)
/* Translators: this is the number of connected bluetooth devices */
this._item.label.text = ngettext('%d Connected", "%d Connected', nConnectedDevices).format(nConnectedDevices);
else if (nConnectedDevices === 1)
this._item.label.text = connectedDevices[0].name;
else if (adapter === null)
this._item.label.text = _('Bluetooth Off');
this._item.label.text = ngettext("%d Connected", "%d Connected", nConnectedDevices).format(nConnectedDevices);
else if (nConnectedDevices == -1)
this._item.label.text = _("Off");
else
this._item.label.text = _('Bluetooth On');
this._item.label.text = _("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));
}
async _reload() {
let props;
try {
const result = await Gio.DBus.system.call(
this._BUS_NAME,
this._BUS_PATH,
this._BUS_PROPS_IFACE,
'GetAll',
new GLib.Variant('(s)', [this._BUS_IFACE]),
null, Gio.DBusCallFlags.NONE, -1, null);
[props] = result.deep_unpack();
} catch (e) {
log('Could not get properties from %s'.format(this._BUS_NAME));
return;
}
_reload() {
Gio.DBus.system.call(this._BUS_NAME,
this._BUS_PATH,
this._BUS_PROPS_IFACE,
'GetAll',
new GLib.Variant('(s)', [this._BUS_IFACE]),
null, Gio.DBusCallFlags.NONE, -1, null,
(conn, result) => {
let props;
try {
props = conn.call_finish(result).deep_unpack()[0];
} catch (e) {
log('Could not get properties from %s'.format(this._BUS_NAME));
return;
}
let layouts = props['X11Layout'].unpack();
let variants = props['X11Variant'].unpack();
let options = props['X11Options'].unpack();
const layouts = props['X11Layout'].unpack();
const variants = props['X11Variant'].unpack();
const options = props['X11Options'].unpack();
if (layouts !== this._layouts ||
variants !== this._variants) {
this._layouts = layouts;
this._variants = variants;
this._emitInputSourcesChanged();
}
if (options !== this._options) {
this._options = options;
this._emitKeyboardOptionsChanged();
}
if (layouts != this._layouts ||
variants != this._variants) {
this._layouts = layouts;
this._variants = variants;
this._emitInputSourcesChanged();
}
if (options != this._options) {
this._options = options;
this._emitKeyboardOptionsChanged();
}
});
}
get inputSources() {
@ -805,8 +805,8 @@ class InputSourceIndicatorContainer extends St.Widget {
}, [0, 0]);
}
vfunc_allocate(box) {
this.set_allocation(box);
vfunc_allocate(box, flags) {
this.set_allocation(box, flags);
// translate box to (0, 0)
box.x2 -= box.x1;
@ -815,7 +815,7 @@ class InputSourceIndicatorContainer extends St.Widget {
box.y1 = 0;
this.get_children().forEach(c => {
c.allocate_align_fill(box, 0.5, 0.5, false, false);
c.allocate_align_fill(box, 0.5, 0.5, false, false, flags);
});
}
});

View File

@ -225,6 +225,10 @@ class Indicator extends PanelMenu.SystemIndicator {
}
});
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
var AppAuthorizer = class {
constructor(desktopId, reqAccuracyLevel, permStoreProxy, maxAccuracyLevel) {
this.desktopId = desktopId;
@ -309,8 +313,9 @@ var AppAuthorizer = class {
_completeAuth() {
if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
this._accuracyLevel = Math.clamp(this._accuracyLevel,
0, this._maxAccuracyLevel);
this._accuracyLevel = clamp(this._accuracyLevel,
0,
this._maxAccuracyLevel);
}
this._saveToPermissionStore();

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