[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1052229: bookworm-pu (pre-approval): gnome-shell/43.9-0+deb12u1



Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: gnome-shell@packages.debian.org, debian-gtk-gnome@lists.debian.org
Control: affects -1 + src:gnome-shell

[ Reason ]
Several new upstream bugfix releases. I've been trying to get these into
a suitable state for a stable update since 12.1, but every time I've
been testing one long enough to think about asking for upload approval,
there have been more bugfixes upstream and the cycle starts again.

This is probably going to be the last upstream release in the 43.x series,
although we might get a 43.10.

[ Impact ]
Various fixes for crashes and other bugs. This also converts the fix
for CVE-2023-43090 (which was fixed via a DSA) from a patch to part of
the upstream source.

[ Tests ]
A prerelease build differing only in changelog and version is available at
https://people.debian.org/~smcv/12.3/pool/main/g/gnome-shell/
and is in use on my household's bookworm laptop/desktop systems, with
no obvious regressions seen immediately (but I only installed it recently).
The diff is not small and the 12.2 deadline is coming up, so I think we
should continue testing this until after 12.2 is out, and then upload. I
would appreciate any testing that the rest of the GNOME team can provide.

43.7-1 was in testing for a while, and 43.7-2 was briefly in unstable
before it was superseded by version 44. I also tested bookworm backports
of 43.7-2 and 43.8 on my household's bookworm laptop/desktop systems for
a while.

I confirmed that CVE-2023-43090 is not reproducible in this version.
I generally haven't specifically attempted to reproduce other bugs.

[ Risks ]
I am not any sort of expert on compositor development, but upstream
have generally been good about backporting only bug fixes to their
stable branches. There have been some regressions in the past because
this stuff is difficult. If there are regressions from these changes,
they're likely to be of the same magnitude as the bugs that were fixed.

The changes in subprojects/gvc/ are a larger diffstat than I would like
(including performance improvements as well as bug fixes), but also
relatively straightforward if we look closely.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
      - the diff given here is between patched trees, using the fix for
        CVE-2023-43090 in bookworm-security as its reference point, and
        is lightly filtered to avoid wasting the release team's time
        (see top of diff for the exact filterdiff command)
  [x] the issue is verified as fixed in unstable

[ Changes ]

js/misc/ibusManager.js:
    - Add missing environment variables required to launch ibus-daemon
      (gnome-shell#6998, fixed in 44.5 for unstable)
    - Report focus events to ibus when using Wayland (gnome-shell#6415)
      and fix a regression caused by the initial version of that change
      (both changes were in 44.4 for unstable)

js/misc/inputMethod.js:
    - Better compatibility with Debian 12's version of ibus
      (gnome-shell#6405, fixed in 44.4 for unstable)

js/misc/parentalControlsManager.js:
    - Don't log an error when AccountsService signals a change while
      parental controls are disabled globally
      (gnome-shell#6749, fixed in 44.3 for unstable)

js/misc/weather.js:
    - When showing weather, avoid getting meaningless location names like
      "WiFi" or "GeoIP" from GeoClue >= 2.7 (fixed in 44.4 for unstable)

js/ui/barLevel.js, js/ui/slider.js:
    - Reverse the direction of volume/brightness sliders in right-to-left
      locales (Arabic, Hebrew) to match user expectations
      (fixed in 44.5 for unstable)

js/ui/keyboard.js:
    - When using the on-screen keyboard in numeric mode, don't go back to
      alphabetical mode after each keypress
      (gnome-shell#5763, fixed in 44.5 for unstable)

js/ui/lookingGlass.js:
    - In the "looking glass" debug interface, cope with objects that
      cannot be converted to string (fixed in 44.4 for unstable)

js/ui/magnifier.js:
    - Apply hotspot translation to mouse cursor while using magnifier,
      fixing an offset between the visble pointer position and the actual
      pointer (gnome-shell#4584, fixed in 44.3 for unstable)

js/ui/messageList.js:
    - Allow notifications to be dismissed with backspace key in addition to
      delete key, for easier use on laptops where the delete key might be
      missing or hard to reach (gnome-shell#5789, fixed in 44.4 for unstable)

js/ui/quickSettings.js:
    - Avoid sliders in quick settings (volume, etc.) being reported to
      accessibility tools as their own parent object (gnome-shell#6686,
      fixed in 44.4 for unstable)

js/ui/workspace.js:
    - Keep rounded corners on the overview's view of the desktop, even
      after the background image changes
      (gnome-shell#4125, fixed in 44.3 for unstable)

po/POTFILES.in:
- Make an extra file translatable (fixed in 44.4 in unstable)

po/POTFILES.skip:
- Skip translations for a file that isn't user-visible

po/*.po: Translation updates

src/st-viewport.c:
    - Improve GNOME Shell app grid performance by avoiding repainting
      monitors other than the one it is displayed on (gnome-shell#6819,
      fixed in 44.4 for unstable)
      - this change is only fully effective when combined with mutter 43.8
    - Align scrolled viewports to the pixel grid to avoid jitter visible
      during scrolling (gnome-shell#6835, fixed in 44.4 for unstable)

src/main.c:
    Skip final cleanup during exit.
    This has been implicated in various crashes during exit, which cause
    gnome-shell to disable extensions during the next startup. Leaking some
    memory at this point does not matter since the process is exiting anyway.
    The crashes during exit are believed to have been fixed in 44.beta, but
    those changes are too intrusive to be suitable for a backport.
    (Closes: #1038972, not needed in unstable since 44.beta)

subprojects/gvc/:
    - Update subprojects/gvc subproject to the same version used in sid
      (gnome-shell#6842, same change is in 44.4 in unstable)
      + Fix duplicate devices shown when reconnecting to PulseAudio
      + Fix possible use-after-free crashes on PulseAudio/Pipewire restart
      + Improve choice of default icons
      + Performance improvements

.gitlab-ci/: Upstream CI changes, filtered out of the diff

[ Other info ]
I've only tested this in conjunction with an accompanying mutter update,
and it would be useful for a release team member to coordinate processing
of the two updates.

For my reference, the attached diff corresponds to packaging
commit 80171eefd, and the matching prerelease builds are versioned as
43.9-0+deb12u1~43.7+2+48+g80171eefd.

    smcv
Diff with patches applied, filtered through:
filterdiff -p1 \
    -x'.gitlab-ci/*' \
    -x'debian/patches/*.patch' \
    -x'po/*.po' \
    -xsubprojects/gvc/.gitlab-ci.yml

diff --git a/NEWS b/NEWS
index 87adbee86..a38286f72 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,40 @@
+43.9
+====
+* Fix exposing window previews on lock screen via keyboard shortcuts
+  [Florian; !2944]
+* Improve handling of latched vs. locked modes in OSK [Carlos; !2945]
+* Reverse slider direction in RTL locales [Khalid; !2943]
+* Misc. bug fixes and cleanups [xiaofan; !2947]
+
+Contributors:
+  Carlos Garnacho, Florian Müllner, Khalid Abu Shawarib, xiaofan
+
+Translators:
+  Daniel Rusek [cs]
+
+43.8
+====
+* Fix accessibility of quick settings sliders [Lukáš; !2762]
+* Allow notification dismissal with backspace [Chris; !2435]
+* Misc. bug fixes and cleanups [Florian, Takao, Carlos, Brendan, Daniel, Jonas;
+  !2814, !2842, !2849, !2668, !2666, !2876, !2729, !2828, !2904]
+
+Contributors:
+  Takao Fujiwara, Carlos Garnacho, Chris Heywood, Florian Müllner,
+  Lukáš Tyrychtr, Daniel van Vugt, Brendan William, Jonas Ådahl
+
+Translators:
+  Danial Behzadi [fa]
+
+43.7
+====
+* Fix cursor offset when using magnifier [Андрей; !2780]
+* Fix missing workspace borders after wallpaper changes [Florian; !2801]
+* Misc. bug fixes and cleanups [Florian; !2796]
+
+Contributors:
+  Florian Müllner, Андрей Гриценко
+
 43.6
 ====
 * Fix stuck authentication dialog in remote sessions [Joan; !2761]
diff --git a/debian/changelog b/debian/changelog
index 2816f8952..552645432 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,79 @@
+gnome-shell (43.9-0+deb12u1) UNRELEASED; urgency=medium
+
+  * d/control.in, d/gbp.conf: Use debian/bookworm branch
+  * New upstream stable release 43.8
+    - Allow notifications to be dismissed with backspace key in addition to
+      delete key, for easier use on laptops where the delete key might be
+      missing or hard to reach (gnome-shell#5789)
+    - Update subprojects/gvc subproject to the same version used in 44.4
+      (gnome-shell#6842)
+      + Fix duplicate devices shown when reconnecting to PulseAudio
+      + Fix possible use-after-free crashes on PulseAudio/Pipewire restart
+      + Improve choice of default icons
+      + Performance improvements
+    - Avoid sliders in quick settings (volume, etc.) being reported to
+      accessibility tools as their own parent object (gnome-shell#6686)
+    - Better compatibility with Debian 12's version of ibus
+      (gnome-shell#6405)
+    - Report focus events to ibus when using Wayland (gnome-shell#6415)
+    - In the "looking glass" debug interface, cope with objects that
+      cannot be converted to string
+    - When showing weather, avoid getting meaningless location names like
+      "WiFi" or "GeoIP" from GeoClue >= 2.7
+    - Improve GNOME Shell app grid performance by avoiding repainting
+      monitors other than the one it is displayed on (gnome-shell#6819)
+    - Align scrolled viewports to the pixel grid to avoid jitter visible
+      during scrolling (gnome-shell#6835)
+    - Support translated strings in more files
+    - Upstream CI adjustments not relevant to Debian
+    - All other changes were already in 43.7-1 or 43.7-2
+  * New upstream stable release 43.9
+    - Avoid exposing window previews on lock screen via keyboard shortcuts
+      (CVE-2023-43090, gnome-shell#6990; previously fixed for bookworm via
+      patches in 43.6-1~deb12u2)
+    - Improve handling of latched vs. locked modes in on-screen keyboard
+      (gnome-shell#5763)
+    - Reverse slider direction in RTL locales (gnome-shell#5107)
+    - Add missing environment variables required to launch ibus-daemon
+      (gnome-shell#6998)
+    - Translation updates
+  * d/patches: Drop patches that were included in the new upstream releases
+
+ -- Simon McVittie <smcv@debian.org>  Sun, 17 Sep 2023 16:36:01 +0100
+
+gnome-shell (43.7-2) unstable; urgency=medium
+
+  * Team upload
+  * d/p/main-Leak-the-GJS-context-and-ShellGlobal.patch:
+    Add patch from Fedora to skip final cleanup during exit.
+    This has been implicated in various crashes during exit, which cause
+    gnome-shell to disable extensions during the next startup. Leaking some
+    memory at this point does not matter since the process is exiting anyway.
+    The crashes during exit are believed to have been fixed in 44.beta, but
+    those changes are too intrusive to be suitable for a backport.
+    (Closes: #1038972)
+
+ -- Simon McVittie <smcv@debian.org>  Thu, 17 Aug 2023 10:46:44 +0100
+
+gnome-shell (43.7-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream stable release
+    - Apply hotspot translation to mouse cursor while using magnifier,
+      fixing an offset between the visble pointer position and the actual
+      pointer (gnome-shell#4584, also fixed in 44.3)
+    - Don't log an error when AccountsService signals a change while
+      parental controls are disabled globally
+      (gnome-shell#6749, also fixed in 44.3)
+    - Keep rounded corners on the overview's view of the desktop, even
+      after the background image changes
+      (gnome-shell#4125, also fixed in 44.3)
+  * d/patches: Update to upstream gnome-43 branch commit 43.7-1-g0d51f199e
+    - Translation update: fa
+  * d/gbp.conf, d/control.in: Use debian/trixie branch
+
+ -- Simon McVittie <smcv@debian.org>  Thu, 10 Aug 2023 10:33:35 +0100
+
 gnome-shell (43.6-1~deb12u2) bookworm-security; urgency=high
 
   * Team upload
diff --git a/debian/patches/series b/debian/patches/series
index bed0752ec..cbdcdddc1 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,2 @@
-screenshot-Do-not-wrongly-enable-window-button.patch
-screenshot-Only-handle-mode-switch-shortcut-when-supporte.patch
 debian/gnome-shell-extension-prefs-Give-Debian-specific-advice.patch
 debian/Revert-build-Port-to-gcr4.patch
diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js
index 969556dc2..214db558e 100644
--- a/js/misc/ibusManager.js
+++ b/js/misc/ibusManager.js
@@ -108,17 +108,9 @@ var IBusManager = class extends Signals.EventEmitter {
 
     _spawn(extraArgs = []) {
         try {
-            let cmdLine = ['ibus-daemon', '--panel', 'disable', ...extraArgs];
-            let env = [];
-
-            this._tryAppendEnv(env, 'DBUS_SESSION_BUS_ADDRESS');
-            this._tryAppendEnv(env, 'WAYLAND_DISPLAY');
-            this._tryAppendEnv(env, 'HOME');
-            this._tryAppendEnv(env, 'LANG');
-            this._tryAppendEnv(env, 'LC_CTYPE');
-            this._tryAppendEnv(env, 'COMPOSE_FILE');
-            this._tryAppendEnv(env, 'DISPLAY');
-
+            const cmdLine = ['ibus-daemon', '--panel', 'disable', ...extraArgs];
+            const launchContext = global.create_app_launch_context(0, -1);
+            const env = launchContext.get_environment();
             // Use DO_NOT_REAP_CHILD to avoid adouble-fork internally
             // since ibus-daemon refuses to start with init as its parent.
             const [success_, pid] = GLib.spawn_async(
diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js
index e008e6411..e01eac8de 100644
--- a/js/misc/inputMethod.js
+++ b/js/misc/inputMethod.js
@@ -12,6 +12,8 @@ Gio._promisify(IBus.InputContext.prototype,
 
 var HIDE_PANEL_TIME = 50;
 
+const HAVE_REQUIRE_SURROUNDING_TEXT = GObject.signal_lookup('require-surrounding-text', IBus.InputContext);
+
 var InputMethod = GObject.registerClass({
     Signals: {
         'surrounding-text-set': {},
@@ -76,7 +78,6 @@ var InputMethod = GObject.registerClass({
 
         this._context.set_client_commit_preedit(true);
         this._context.connect('commit-text', this._onCommitText.bind(this));
-        this._context.connect('require-surrounding-text', this._onRequireSurroundingText.bind(this));
         this._context.connect('delete-surrounding-text', this._onDeleteSurroundingText.bind(this));
         this._context.connect('update-preedit-text-with-mode', this._onUpdatePreeditText.bind(this));
         this._context.connect('show-preedit-text', this._onShowPreeditText.bind(this));
@@ -84,6 +85,9 @@ var InputMethod = GObject.registerClass({
         this._context.connect('forward-key-event', this._onForwardKeyEvent.bind(this));
         this._context.connect('destroy', this._clear.bind(this));
 
+        if (HAVE_REQUIRE_SURROUNDING_TEXT)
+            this._context.connect('require-surrounding-text', this._onRequireSurroundingText.bind(this));
+
         Main.keyboard.connectObject('visibility-changed', () => this._updateCapabilities());
 
         this._updateCapabilities();
@@ -180,6 +184,7 @@ var InputMethod = GObject.registerClass({
     vfunc_focus_in(focus) {
         this._currentFocus = focus;
         if (this._context) {
+            this.update();
             this._context.focus_in();
             this._emitRequestSurrounding();
         }
@@ -363,7 +368,6 @@ var InputMethod = GObject.registerClass({
     _fullReset() {
         this._context.set_content_type(0, 0);
         this._context.set_cursor_location(0, 0, 0, 0);
-        this._context.set_capabilities(0);
         this._context.reset();
     }
 
@@ -372,9 +376,11 @@ var InputMethod = GObject.registerClass({
             return;
         this._updateCapabilities();
         this._context.set_content_type(this._purpose, this._hints);
-        this._context.set_cursor_location(
-            this._cursorRect.x, this._cursorRect.y,
-            this._cursorRect.width, this._cursorRect.height);
+        if (this._cursorRect) {
+            this._context.set_cursor_location(
+                this._cursorRect.x, this._cursorRect.y,
+                this._cursorRect.width, this._cursorRect.height);
+        }
         this._emitRequestSurrounding();
     }
 });
diff --git a/js/misc/parentalControlsManager.js b/js/misc/parentalControlsManager.js
index e2192caea..48f0ca2dd 100644
--- a/js/misc/parentalControlsManager.js
+++ b/js/misc/parentalControlsManager.js
@@ -74,18 +74,10 @@ var ParentalControlsManager = GObject.registerClass({
         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);
+            this._appFilter = await this._getAppFilter();
         } catch (e) {
-            if (e.matches(Malcontent.ManagerError, Malcontent.ManagerError.DISABLED)) {
-                console.debug('Parental controls globally disabled');
-                this._disabled = true;
-            } else {
-                logError(e, 'Failed to get parental controls settings');
-                return;
-            }
+            logError(e, 'Failed to get parental controls settings');
+            return;
         }
 
         this._manager.connect('app-filter-changed', this._onAppFilterChanged.bind(this));
@@ -95,6 +87,25 @@ var ParentalControlsManager = GObject.registerClass({
         this.emit('app-filter-changed');
     }
 
+    async _getAppFilter() {
+        let appFilter = null;
+
+        try {
+            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))
+                throw e;
+
+            console.debug('Parental controls globally disabled');
+            this._disabled = true;
+        }
+
+        return appFilter;
+    }
+
     async _onAppFilterChanged(manager, uid) {
         // Emit 'changed' signal only if app-filter is changed for currently logged-in user.
         let currentUid = Shell.util_get_uid();
@@ -102,10 +113,7 @@ var ParentalControlsManager = GObject.registerClass({
             return;
 
         try {
-            this._appFilter = await this._manager.get_app_filter_async(
-                currentUid,
-                Malcontent.ManagerGetValueFlags.NONE,
-                null);
+            this._appFilter = await this._getAppFilter();
             this.emit('app-filter-changed');
         } catch (e) {
             // Log an error and keep the old app filter.
diff --git a/js/misc/weather.js b/js/misc/weather.js
index a01b832b8..2aa340ab7 100644
--- a/js/misc/weather.js
+++ b/js/misc/weather.js
@@ -267,10 +267,11 @@ var WeatherClient = class extends Signals.EventEmitter {
 
     _onGClueLocationChanged() {
         let geoLocation = this._gclueService.location;
-        let location = GWeather.Location.new_detached(geoLocation.description,
-                                                      null,
-                                                      geoLocation.latitude,
-                                                      geoLocation.longitude);
+        // Provide empty name so GWeather sets location name
+        const location = GWeather.Location.new_detached('',
+            null,
+            geoLocation.latitude,
+            geoLocation.longitude);
         this._setLocation(location);
     }
 
diff --git a/js/ui/barLevel.js b/js/ui/barLevel.js
index 25d483528..da5b34a9f 100644
--- a/js/ui/barLevel.js
+++ b/js/ui/barLevel.js
@@ -98,6 +98,7 @@ var BarLevel = GObject.registerClass({
         let cr = this.get_context();
         let themeNode = this.get_theme_node();
         let [width, height] = this.get_surface_size();
+        const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
 
         let barLevelHeight = themeNode.get_length('-barlevel-height');
         let barLevelBorderRadius = Math.min(width, barLevelHeight) / 2;
@@ -124,20 +125,36 @@ var BarLevel = GObject.registerClass({
         const TAU = Math.PI * 2;
 
         let endX = 0;
-        if (this._maxValue > 0)
-            endX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * this._value / this._maxValue;
+        if (this._maxValue > 0) {
+            let progress = this._value / this._maxValue;
+            if (rtl)
+                progress = 1 - progress;
+            endX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * progress;
+        }
+
+        let overdriveRatio = this._overdriveStart / this._maxValue;
+        if (rtl)
+            overdriveRatio = 1 - overdriveRatio;
+        let overdriveSeparatorX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * overdriveRatio;
 
-        let overdriveSeparatorX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * this._overdriveStart / this._maxValue;
         let overdriveActive = this._overdriveStart !== this._maxValue;
         let overdriveSeparatorWidth = 0;
         if (overdriveActive)
             overdriveSeparatorWidth = themeNode.get_length('-barlevel-overdrive-separator-width');
 
+        let xcArcStart = barLevelBorderRadius + barLevelBorderWidth;
+        let xcArcEnd = width - xcArcStart;
+        if (rtl)
+            [xcArcStart, xcArcEnd] = [xcArcEnd, xcArcStart];
+
         /* background bar */
-        cr.arc(width - barLevelBorderRadius - barLevelBorderWidth, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+        if (!rtl)
+            cr.arc(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+        else
+            cr.arcNegative(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
         cr.lineTo(endX, (height + barLevelHeight) / 2);
         cr.lineTo(endX, (height - barLevelHeight) / 2);
-        cr.lineTo(width - barLevelBorderRadius - barLevelBorderWidth, (height - barLevelHeight) / 2);
+        cr.lineTo(xcArcEnd, (height - barLevelHeight) / 2);
         Clutter.cairo_set_source_color(cr, barLevelColor);
         cr.fillPreserve();
         Clutter.cairo_set_source_color(cr, barLevelBorderColor);
@@ -145,11 +162,17 @@ var BarLevel = GObject.registerClass({
         cr.stroke();
 
         /* normal progress bar */
-        let x = Math.min(endX, overdriveSeparatorX - overdriveSeparatorWidth / 2);
-        cr.arc(barLevelBorderRadius + barLevelBorderWidth, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4));
+        let x = 0;
+        if (!rtl) {
+            x = Math.min(endX, overdriveSeparatorX - overdriveSeparatorWidth / 2);
+            cr.arc(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4));
+        } else {
+            x = Math.max(endX, overdriveSeparatorX + overdriveSeparatorWidth / 2);
+            cr.arcNegative(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4));
+        }
         cr.lineTo(x, (height - barLevelHeight) / 2);
         cr.lineTo(x, (height + barLevelHeight) / 2);
-        cr.lineTo(barLevelBorderRadius + barLevelBorderWidth, (height + barLevelHeight) / 2);
+        cr.lineTo(xcArcStart, (height + barLevelHeight) / 2);
         if (this._value > 0)
             Clutter.cairo_set_source_color(cr, barLevelActiveColor);
         cr.fillPreserve();
@@ -158,7 +181,10 @@ var BarLevel = GObject.registerClass({
         cr.stroke();
 
         /* overdrive progress barLevel */
-        x = Math.min(endX, overdriveSeparatorX) + overdriveSeparatorWidth / 2;
+        if (!rtl)
+            x = Math.min(endX, overdriveSeparatorX) + overdriveSeparatorWidth / 2;
+        else
+            x = Math.max(endX, overdriveSeparatorX) - overdriveSeparatorWidth / 2;
         if (this._value > this._overdriveStart) {
             cr.moveTo(x, (height - barLevelHeight) / 2);
             cr.lineTo(endX, (height - barLevelHeight) / 2);
@@ -178,9 +204,15 @@ var BarLevel = GObject.registerClass({
                 Clutter.cairo_set_source_color(cr, barLevelActiveColor);
             else
                 Clutter.cairo_set_source_color(cr, barLevelOverdriveColor);
-            cr.arc(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
-            cr.lineTo(Math.floor(endX), (height + barLevelHeight) / 2);
-            cr.lineTo(Math.floor(endX), (height - barLevelHeight) / 2);
+            if (!rtl) {
+                cr.arc(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+                cr.lineTo(Math.floor(endX), (height + barLevelHeight) / 2);
+                cr.lineTo(Math.floor(endX), (height - barLevelHeight) / 2);
+            } else {
+                cr.arcNegative(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+                cr.lineTo(Math.ceil(endX), (height + barLevelHeight) / 2);
+                cr.lineTo(Math.ceil(endX), (height - barLevelHeight) / 2);
+            }
             cr.lineTo(endX, (height - barLevelHeight) / 2);
             cr.fillPreserve();
             cr.setLineWidth(barLevelBorderWidth);
diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js
index b3c4cbe32..be128d34c 100644
--- a/js/ui/keyboard.js
+++ b/js/ui/keyboard.js
@@ -1492,6 +1492,7 @@ var Keyboard = GObject.registerClass({
 
             let layout = new KeyContainer();
             layout.shiftKeys = [];
+            layout.mode = currentLevel.mode;
 
             this._loadRows(currentLevel, level, levels.length, layout);
             layers[level] = layout;
@@ -1527,7 +1528,10 @@ var Keyboard = GObject.registerClass({
 
             if (key.action !== 'modifier') {
                 button.connect('commit', (_actor, keyval, str) => {
-                    this._commitAction(keyval, str);
+                    this._commitAction(keyval, str).then(() => {
+                        if (layout.mode === 'latched' && !this._latched)
+                            this._setActiveLayer(0);
+                    });
                 });
             }
 
@@ -1607,9 +1611,6 @@ var Keyboard = GObject.registerClass({
                 });
             }
         }
-
-        if (!this._latched)
-            this._setActiveLayer(0);
     }
 
     _previousWordPosition(text, cursor) {
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
index f3af5638e..6b6b65f7a 100644
--- a/js/ui/lookingGlass.js
+++ b/js/ui/lookingGlass.js
@@ -261,6 +261,11 @@ function objectToString(o) {
     if (typeof o == typeof objectToString) {
         // special case this since the default is way, way too verbose
         return '<js function>';
+    } else if (o && o.toString === undefined) {
+        // eeks, something unprintable. we'll have to guess, probably a module
+        return typeof o === 'object' && !(o instanceof Object)
+            ? '<module>'
+            : '<unknown>';
     } else {
         return `${o}`;
     }
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
index f4012dedf..bd6904758 100644
--- a/js/ui/magnifier.js
+++ b/js/ui/magnifier.js
@@ -103,12 +103,15 @@ var Magnifier = class Magnifier extends Signals.EventEmitter {
         this._mouseSprite = new Clutter.Actor({ request_mode: Clutter.RequestMode.CONTENT_SIZE });
         this._mouseSprite.content = new MouseSpriteContent();
 
+        this._cursorRoot = new Clutter.Actor();
+        this._cursorRoot.add_actor(this._mouseSprite);
+
         // Create the first ZoomRegion and initialize it according to the
         // magnification settings.
 
         [this.xMouse, this.yMouse] = global.get_pointer();
 
-        let aZoomRegion = new ZoomRegion(this, this._mouseSprite);
+        let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
         this._zoomRegions.push(aZoomRegion);
         this._settingsInit(aZoomRegion);
         aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
@@ -283,7 +286,7 @@ var Magnifier = class Magnifier extends Signals.EventEmitter {
      * @returns {ZoomRegion} the newly created ZoomRegion.
      */
     createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) {
-        let zoomRegion = new ZoomRegion(this, this._mouseSprite);
+        let zoomRegion = new ZoomRegion(this, this._cursorRoot);
         zoomRegion.setViewPort(viewPort);
 
         // We ignore the redundant width/height on the ROI
diff --git a/js/ui/messageList.js b/js/ui/messageList.js
index 29e76752b..c910ca7cf 100644
--- a/js/ui/messageList.js
+++ b/js/ui/messageList.js
@@ -533,8 +533,9 @@ var Message = GObject.registerClass({
     vfunc_key_press_event(keyEvent) {
         let keysym = keyEvent.keyval;
 
-        if (keysym == Clutter.KEY_Delete ||
-            keysym == Clutter.KEY_KP_Delete) {
+        if (keysym === Clutter.KEY_Delete ||
+            keysym === Clutter.KEY_KP_Delete ||
+            keysym === Clutter.KEY_BackSpace) {
             if (this.canClose()) {
                 this.close();
                 return Clutter.EVENT_STOP;
diff --git a/js/ui/quickSettings.js b/js/ui/quickSettings.js
index f35a15c3d..7cfd4f4e7 100644
--- a/js/ui/quickSettings.js
+++ b/js/ui/quickSettings.js
@@ -193,7 +193,10 @@ var QuickSlider = GObject.registerClass({
         });
         box.add_child(sliderBin);
 
-        sliderBin.set_accessible(this.slider.get_accessible());
+        // Make the slider bin transparent for a11y
+        const sliderAccessible = this.slider.get_accessible();
+        sliderAccessible.set_parent(sliderBin.get_parent().get_accessible());
+        sliderBin.set_accessible(sliderAccessible);
         sliderBin.connect('event', (bin, event) => this.slider.event(event, false));
 
         this._menuButton = new St.Button({
diff --git a/js/ui/slider.js b/js/ui/slider.js
index 6ca76fec0..849599d24 100644
--- a/js/ui/slider.js
+++ b/js/ui/slider.js
@@ -36,6 +36,7 @@ var Slider = GObject.registerClass({
         let cr = this.get_context();
         let themeNode = this.get_theme_node();
         let [width, height] = this.get_surface_size();
+        const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
 
         let handleRadius = themeNode.get_length('-slider-handle-radius');
 
@@ -44,10 +45,13 @@ var Slider = GObject.registerClass({
             themeNode.lookup_color('-slider-handle-border-color', false);
 
         const ceiledHandleRadius = Math.ceil(handleRadius + handleBorderWidth);
-        const handleX = ceiledHandleRadius +
-            (width - 2 * ceiledHandleRadius) * this._value / this._maxValue;
         const handleY = height / 2;
 
+        let handleX = ceiledHandleRadius +
+            (width - 2 * ceiledHandleRadius) * this._value / this._maxValue;
+        if (rtl)
+            handleX = width - handleX;
+
         let color = themeNode.get_foreground_color();
         Clutter.cairo_set_source_color(cr, color);
         cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
@@ -189,9 +193,13 @@ var Slider = GObject.registerClass({
     _moveHandle(absX, _absY) {
         let relX, sliderX;
         [sliderX] = this.get_transformed_position();
+        const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+        let width = this._barLevelWidth;
+
         relX = absX - sliderX;
+        if (rtl)
+            relX = width - relX;
 
-        let width = this._barLevelWidth;
         let handleRadius = this.get_theme_node().get_length('-slider-handle-radius');
 
         let newvalue;
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 0069cdd00..bf631a5f0 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -981,6 +981,11 @@ class WorkspaceBackground extends Shell.WorkspaceBackground {
             useContentSize: false,
         });
 
+        this._bgManager.connect('changed', () => {
+            this._updateRoundedClipBounds();
+            this._updateBorderRadius();
+        });
+
         global.display.connectObject('workareas-changed', () => {
             this._workarea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
             this._updateRoundedClipBounds();
diff --git a/meson.build b/meson.build
index 8b04a7e9c..84dba1a6f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('gnome-shell', 'c',
-  version: '43.6',
+  version: '43.9',
   meson_version: '>= 0.58.0',
   license: 'GPLv2+'
 )
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b647698e4..0ba8752fa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@ data/50-gnome-shell-screenshots.xml
 data/50-gnome-shell-system.xml
 data/org.gnome.Shell.desktop.in.in
 data/org.gnome.shell.gschema.xml.in
+data/org.gnome.Shell.Extensions.desktop.in.in
 data/org.gnome.Shell.PortalHelper.desktop.in.in
 js/dbusServices/extensions/ui/extension-error-page.ui
 js/gdm/authPrompt.js
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 0c8451d64..dd694f761 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,3 +1,4 @@
+data/gnome-shell-overrides-migration.desktop.in
 data/org.gnome.Shell@wayland.service.in
 data/org.gnome.Shell@x11.service.in
 js/ui/init.js
diff --git a/src/main.c b/src/main.c
index 29275cda0..2311a74bd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -589,9 +589,11 @@ main (int argc, char **argv)
 
   shell_profiler_shutdown ();
 
+#if 0
   g_debug ("Doing final cleanup");
   _shell_global_destroy_gjs_context (shell_global_get ());
   g_object_unref (shell_global_get ());
+#endif
 
   return ecode;
 }
diff --git a/src/st/st-viewport.c b/src/st/st-viewport.c
index 3f0cdbb46..e6b912766 100644
--- a/src/st/st-viewport.c
+++ b/src/st/st-viewport.c
@@ -370,10 +370,10 @@ st_viewport_apply_transform (ClutterActor      *actor,
   graphene_point3d_t p = GRAPHENE_POINT3D_INIT_ZERO;
 
   if (priv->hadjustment)
-    p.x = -get_hadjustment_value (viewport);
+    p.x = -(int)get_hadjustment_value (viewport);
 
   if (priv->vadjustment)
-    p.y = -st_adjustment_get_value (priv->vadjustment);
+    p.y = -(int)st_adjustment_get_value (priv->vadjustment);
 
   graphene_matrix_translate (matrix, &p);
 
@@ -384,8 +384,8 @@ st_viewport_apply_transform (ClutterActor      *actor,
  * up or the background and borders will be drawn in the wrong place */
 static void
 get_border_paint_offsets (StViewport *viewport,
-                          double     *x,
-                          double     *y)
+                          int        *x,
+                          int        *y)
 {
   StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
 
@@ -408,7 +408,7 @@ st_viewport_paint (ClutterActor        *actor,
   StViewport *viewport = ST_VIEWPORT (actor);
   StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-  double x, y;
+  int x, y;
   ClutterActorBox allocation_box;
   ClutterActorBox content_box;
   ClutterActor *child;
@@ -418,7 +418,7 @@ st_viewport_paint (ClutterActor        *actor,
   if (x != 0 || y != 0)
     {
       cogl_framebuffer_push_matrix (fb);
-      cogl_framebuffer_translate (fb, (int)x, (int)y, 0);
+      cogl_framebuffer_translate (fb, x, y, 0);
     }
 
   st_widget_paint_background (ST_WIDGET (actor), paint_context);
@@ -465,7 +465,7 @@ st_viewport_pick (ClutterActor       *actor,
   StViewport *viewport = ST_VIEWPORT (actor);
   StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-  double x, y;
+  int x, y;
   g_autoptr (ClutterActorBox) allocation_box = NULL;
   ClutterActorBox content_box;
   ClutterActor *child;
@@ -506,8 +506,7 @@ st_viewport_get_paint_volume (ClutterActor       *actor,
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
   ClutterActorBox allocation_box;
   ClutterActorBox content_box;
-  graphene_point3d_t origin;
-  double x, y, lower, upper;
+  int x, y;
 
   /* Setting the paint volume does not make sense when we don't have any allocation */
   if (!clutter_actor_has_allocation (actor))
@@ -524,35 +523,9 @@ st_viewport_get_paint_volume (ClutterActor       *actor,
 
       clutter_actor_get_allocation_box (actor, &allocation_box);
       st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
-      origin.x = content_box.x1 - allocation_box.x1;
-      origin.y = content_box.y1 - allocation_box.y2;
-      origin.z = 0.f;
 
-      if (priv->hadjustment)
-        {
-          g_object_get (priv->hadjustment,
-                        "lower", &lower,
-                        "upper", &upper,
-                        NULL);
-          width = upper - lower;
-        }
-      else
-        {
-          width = content_box.x2 - content_box.x1;
-        }
-
-      if (priv->vadjustment)
-        {
-          g_object_get (priv->vadjustment,
-                        "lower", &lower,
-                        "upper", &upper,
-                        NULL);
-          height = upper - lower;
-        }
-      else
-        {
-          height = content_box.y2 - content_box.y1;
-        }
+      width = content_box.x2 - content_box.x1;
+      height = content_box.y2 - content_box.y1;
 
       clutter_paint_volume_set_width (volume, width);
       clutter_paint_volume_set_height (volume, height);
@@ -570,6 +543,8 @@ st_viewport_get_paint_volume (ClutterActor       *actor,
   get_border_paint_offsets (viewport, &x, &y);
   if (x != 0 || y != 0)
     {
+      graphene_point3d_t origin;
+
       clutter_paint_volume_get_origin (volume, &origin);
       origin.x += x;
       origin.y += y;
diff --git a/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in b/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
index eebbc8d30..8ce63322e 100644
--- a/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
+++ b/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
@@ -39,6 +39,9 @@
   </description>
 
   <releases>
+    <release version="43.9" date="2023-09-16"/>
+    <release version="43.8" date="2023-08-22"/>
+    <release version="43.7" date="2023-07-04"/>
     <release version="43.6" date="2023-06-03"/>
     <release version="43.5" date="2023-04-24"/>
     <release version="43.4" date="2023-03-19"/>
diff --git a/subprojects/extensions-app/meson.build b/subprojects/extensions-app/meson.build
index 77af7a529..dfb28dc3b 100644
--- a/subprojects/extensions-app/meson.build
+++ b/subprojects/extensions-app/meson.build
@@ -1,5 +1,5 @@
 project('gnome-extensions-app',
-  version: '43.6',
+  version: '43.9',
   meson_version: '>= 0.58.0',
   license: 'GPLv2+'
 )
diff --git a/subprojects/extensions-tool/meson.build b/subprojects/extensions-tool/meson.build
index 17e3bbe4f..11e48d969 100644
--- a/subprojects/extensions-tool/meson.build
+++ b/subprojects/extensions-tool/meson.build
@@ -1,5 +1,5 @@
 project('gnome-extensions-tool', 'c',
-  version: '43.6',
+  version: '43.9',
   meson_version: '>= 0.58.0',
   license: 'GPLv2+'
 )
diff --git a/subprojects/gvc/gvc-channel-map.c b/subprojects/gvc/gvc-channel-map.c
index bf4d737d5..688a451ae 100644
--- a/subprojects/gvc/gvc-channel-map.c
+++ b/subprojects/gvc/gvc-channel-map.c
@@ -166,8 +166,7 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__BOOLEAN,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 }
 
diff --git a/subprojects/gvc/gvc-mixer-card.c b/subprojects/gvc/gvc-mixer-card.c
index 93be4dad7..39f59ca58 100644
--- a/subprojects/gvc/gvc-mixer-card.c
+++ b/subprojects/gvc/gvc-mixer-card.c
@@ -61,7 +61,9 @@ enum
         PROP_ICON_NAME,
         PROP_PROFILE,
         PROP_HUMAN_PROFILE,
+        N_PROPS
 };
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
 
 static void     gvc_mixer_card_finalize   (GObject            *object);
 
@@ -117,7 +119,7 @@ gvc_mixer_card_set_name (GvcMixerCard *card,
 
         g_free (card->priv->name);
         card->priv->name = g_strdup (name);
-        g_object_notify (G_OBJECT (card), "name");
+        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_NAME]);
 
         return TRUE;
 }
@@ -137,7 +139,7 @@ gvc_mixer_card_set_icon_name (GvcMixerCard *card,
 
         g_free (card->priv->icon_name);
         card->priv->icon_name = g_strdup (icon_name);
-        g_object_notify (G_OBJECT (card), "icon-name");
+        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_ICON_NAME]);
 
         return TRUE;
 }
@@ -191,7 +193,7 @@ gvc_mixer_card_set_profile (GvcMixerCard *card,
                 }
         }
 
-        g_object_notify (G_OBJECT (card), "profile");
+        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_PROFILE]);
 
         return TRUE;
 }
@@ -468,54 +470,42 @@ gvc_mixer_card_class_init (GvcMixerCardClass *klass)
         gobject_class->set_property = gvc_mixer_card_set_property;
         gobject_class->get_property = gvc_mixer_card_get_property;
 
-        g_object_class_install_property (gobject_class,
-                                         PROP_INDEX,
-                                         g_param_spec_ulong ("index",
-                                                             "Index",
-                                                             "The index for this card",
-                                                             0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_ID,
-                                         g_param_spec_ulong ("id",
-                                                             "id",
-                                                             "The id for this card",
-                                                             0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_PA_CONTEXT,
-                                         g_param_spec_pointer ("pa-context",
-                                                               "PulseAudio context",
-                                                               "The PulseAudio context for this card",
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_NAME,
-                                         g_param_spec_string ("name",
-                                                              "Name",
-                                                              "Name to display for this card",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_ICON_NAME,
-                                         g_param_spec_string ("icon-name",
-                                                              "Icon Name",
-                                                              "Name of icon to display for this card",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_PROFILE,
-                                         g_param_spec_string ("profile",
-                                                              "Profile",
-                                                              "Name of current profile for this card",
-                                                              NULL,
-                                                              G_PARAM_READWRITE));
-        g_object_class_install_property (gobject_class,
-                                         PROP_HUMAN_PROFILE,
-                                         g_param_spec_string ("human-profile",
-                                                              "Profile (Human readable)",
-                                                              "Name of current profile for this card in human readable form",
-                                                              NULL,
-                                                              G_PARAM_READABLE));
+        obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
+                                                    "Index",
+                                                    "The index for this card",
+                                                    0, G_MAXULONG, 0,
+                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_ID] = g_param_spec_ulong ("id",
+                                                 "id",
+                                                 "The id for this card",
+                                                 0, G_MAXULONG, 0,
+                                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
+                                                           "PulseAudio context",
+                                                           "The PulseAudio context for this card",
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_NAME] = g_param_spec_string ("name",
+                                                    "Name",
+                                                    "Name to display for this card",
+                                                    NULL,
+                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
+                                                         "Icon Name",
+                                                         "Name of icon to display for this card",
+                                                         NULL,
+                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_PROFILE] = g_param_spec_string ("profile",
+                                                       "Profile",
+                                                       "Name of current profile for this card",
+                                                       NULL,
+                                                       G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_HUMAN_PROFILE] = g_param_spec_string ("human-profile",
+                                                             "Profile (Human readable)",
+                                                             "Name of current profile for this card in human readable form",
+                                                             NULL,
+                                                             G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);
+
+        g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
 }
 
 static void
diff --git a/subprojects/gvc/gvc-mixer-control.c b/subprojects/gvc/gvc-mixer-control.c
index 6218a1ba7..b603b7713 100644
--- a/subprojects/gvc/gvc-mixer-control.c
+++ b/subprojects/gvc/gvc-mixer-control.c
@@ -54,8 +54,10 @@
 
 enum {
         PROP_0,
-        PROP_NAME
+        PROP_NAME,
+        N_PROPS
 };
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
 
 struct GvcMixerControlPrivate
 {
@@ -1234,7 +1236,7 @@ match_stream_with_devices (GvcMixerControl    *control,
 
         for (d = devices; d != NULL; d = d->next) {
                 GvcMixerUIDevice *device;
-                gint              device_stream_id;
+                guint             device_stream_id;
                 gchar            *device_port_name;
                 gchar            *origin;
                 gchar            *description;
@@ -1276,7 +1278,7 @@ match_stream_with_devices (GvcMixerControl    *control,
                                          stream_id);
 
                                 g_object_set (G_OBJECT (device),
-                                              "stream-id", (gint)stream_id,
+                                              "stream-id", stream_id,
                                               NULL);
                                 in_possession = TRUE;
                         }
@@ -1322,7 +1324,6 @@ sync_devices (GvcMixerControl *control,
         const GList *stream_ports;
         const GList *n = NULL;
         gboolean     is_output = !GVC_IS_MIXER_SOURCE (stream);
-        gint         stream_port_count = 0;
 
         stream_ports = gvc_mixer_stream_get_ports (stream);
 
@@ -1366,7 +1367,7 @@ sync_devices (GvcMixerControl *control,
                         }
 
                         g_object_set (G_OBJECT (device),
-                                      "stream-id", (gint)gvc_mixer_stream_get_id (stream),
+                                      "stream-id", gvc_mixer_stream_get_id (stream),
                                       "description", gvc_mixer_stream_get_description (stream),
                                       "origin", "", /*Leave it empty for these special cases*/
                                       "port-name", NULL,
@@ -1376,7 +1377,7 @@ sync_devices (GvcMixerControl *control,
                         GObject *object;
 
                         object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
-                                               "stream-id", (gint)gvc_mixer_stream_get_id (stream),
+                                               "stream-id", gvc_mixer_stream_get_id (stream),
                                                "description", gvc_mixer_stream_get_description (stream),
                                                "origin", "", /* Leave it empty for these special cases */
                                                "port-name", NULL,
@@ -1402,7 +1403,6 @@ sync_devices (GvcMixerControl *control,
 
                 GvcMixerStreamPort *stream_port;
                 stream_port = n->data;
-                stream_port_count ++;
 
                 if (match_stream_with_devices (control, stream_port, stream))
                         continue;
@@ -1806,7 +1806,7 @@ update_sink_input (GvcMixerControl          *control,
 
         set_application_id_from_proplist (stream, info->proplist);
         set_is_event_stream_from_proplist (stream, info->proplist);
-        set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
+        set_icon_name_from_proplist (stream, info->proplist, "application-x-executable");
         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
         gvc_mixer_stream_set_is_muted (stream, info->mute);
         gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
@@ -2874,7 +2874,7 @@ update_event_role_stream (GvcMixerControl                  *control,
         max_volume = pa_cvolume_max (&info->volume);
 
         gvc_mixer_stream_set_name (stream, _("System Sounds"));
-        gvc_mixer_stream_set_icon_name (stream, "emblem-system-symbolic");
+        gvc_mixer_stream_set_icon_name (stream, "audio-x-generic");
         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
         gvc_mixer_stream_set_is_muted (stream, info->mute);
 
@@ -3119,6 +3119,9 @@ remove_card (GvcMixerControl *control,
 
                 g_object_get (G_OBJECT (device), "card", &card, NULL);
 
+                if (card == NULL)
+                        continue;
+
                 if (gvc_mixer_card_get_index (card) == index) {
                         g_signal_emit (G_OBJECT (control),
                                        signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
@@ -3434,15 +3437,22 @@ gvc_mixer_new_pa_context (GvcMixerControl *self)
 }
 
 static void
-remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
+remove_all_items (GvcMixerControl *control,
+                  GHashTable *hash_table,
+                  void (*remove_item)(GvcMixerControl *control, guint index))
 {
         GHashTableIter iter;
         gpointer key, value;
 
         g_hash_table_iter_init (&iter, hash_table);
         while (g_hash_table_iter_next (&iter, &key, &value)) {
-                remove_stream (control, value);
-                g_hash_table_iter_remove (&iter);
+                if (remove_item) {
+                        remove_item (control, GPOINTER_TO_UINT (key));
+                        g_hash_table_remove (hash_table, key);
+                        g_hash_table_iter_init (&iter, hash_table);
+                } else {
+                        g_hash_table_iter_remove (&iter);
+                }
         }
 }
 
@@ -3450,11 +3460,22 @@ static gboolean
 idle_reconnect (gpointer data)
 {
         GvcMixerControl *control = GVC_MIXER_CONTROL (data);
-        GHashTableIter iter;
-        gpointer key, value;
 
         g_return_val_if_fail (control, FALSE);
 
+        g_debug ("Reconnect: clean up all objects");
+
+        remove_all_items (control, control->priv->sinks, remove_sink);
+        remove_all_items (control, control->priv->sources, remove_source);
+        remove_all_items (control, control->priv->sink_inputs, remove_sink_input);
+        remove_all_items (control, control->priv->source_outputs, remove_source_output);
+        remove_all_items (control, control->priv->cards, remove_card);
+        remove_all_items (control, control->priv->ui_inputs, NULL);
+        remove_all_items (control, control->priv->ui_outputs, NULL);
+        remove_all_items (control, control->priv->clients, remove_client);
+
+        g_debug ("Reconnect: make new connection");
+
         if (control->priv->pa_context) {
                 pa_context_unref (control->priv->pa_context);
                 control->priv->pa_context = NULL;
@@ -3462,15 +3483,6 @@ idle_reconnect (gpointer data)
                 gvc_mixer_new_pa_context (control);
         }
 
-        remove_all_streams (control, control->priv->sinks);
-        remove_all_streams (control, control->priv->sources);
-        remove_all_streams (control, control->priv->sink_inputs);
-        remove_all_streams (control, control->priv->source_outputs);
-
-        g_hash_table_iter_init (&iter, control->priv->clients);
-        while (g_hash_table_iter_next (&iter, &key, &value))
-                g_hash_table_iter_remove (&iter);
-
         gvc_mixer_control_open (control); /* cannot fail */
 
         control->priv->reconnect_id = 0;
@@ -3628,7 +3640,7 @@ gvc_mixer_control_set_property (GObject       *object,
         case PROP_NAME:
                 g_free (self->priv->name);
                 self->priv->name = g_value_dup_string (value);
-                g_object_notify (G_OBJECT (self), "name");
+                g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3684,133 +3696,117 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
         object_class->set_property = gvc_mixer_control_set_property;
         object_class->get_property = gvc_mixer_control_get_property;
 
-        g_object_class_install_property (object_class,
-                                         PROP_NAME,
-                                         g_param_spec_string ("name",
-                                                              "Name",
-                                                              "Name to display for this mixer control",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+        obj_props[PROP_NAME] = g_param_spec_string ("name",
+                                                    "Name",
+                                                    "Name to display for this mixer control",
+                                                    NULL,
+                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        g_object_class_install_properties (object_class, N_PROPS, obj_props);
 
         signals [STATE_CHANGED] =
                 g_signal_new ("state-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [STREAM_ADDED] =
                 g_signal_new ("stream-added",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [STREAM_REMOVED] =
                 g_signal_new ("stream-removed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [STREAM_CHANGED] =
                 g_signal_new ("stream-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, stream_changed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [AUDIO_DEVICE_SELECTION_NEEDED] =
                 g_signal_new ("audio-device-selection-needed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               0,
-                              NULL, NULL,
-                              g_cclosure_marshal_generic,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_UINT);
         signals [CARD_ADDED] =
                 g_signal_new ("card-added",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [CARD_REMOVED] =
                 g_signal_new ("card-removed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [DEFAULT_SINK_CHANGED] =
                 g_signal_new ("default-sink-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [DEFAULT_SOURCE_CHANGED] =
                 g_signal_new ("default-source-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [ACTIVE_OUTPUT_UPDATE] =
                 g_signal_new ("active-output-update",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [ACTIVE_INPUT_UPDATE] =
                 g_signal_new ("active-input-update",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [OUTPUT_ADDED] =
                 g_signal_new ("output-added",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, output_added),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [INPUT_ADDED] =
                 g_signal_new ("input-added",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, input_added),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [OUTPUT_REMOVED] =
                 g_signal_new ("output-removed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, output_removed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
         signals [INPUT_REMOVED] =
                 g_signal_new ("input-removed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcMixerControlClass, input_removed),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__UINT,
+                              NULL, NULL, NULL,
                               G_TYPE_NONE, 1, G_TYPE_UINT);
 }
 
diff --git a/subprojects/gvc/gvc-mixer-event-role.c b/subprojects/gvc/gvc-mixer-event-role.c
index 9f5e26ad9..272edb04c 100644
--- a/subprojects/gvc/gvc-mixer-event-role.c
+++ b/subprojects/gvc/gvc-mixer-event-role.c
@@ -42,8 +42,10 @@ struct GvcMixerEventRolePrivate
 enum
 {
         PROP_0,
-        PROP_DEVICE
+        PROP_DEVICE,
+        N_PROPS
 };
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
 
 static void     gvc_mixer_event_role_finalize   (GObject            *object);
 
@@ -115,7 +117,7 @@ gvc_mixer_event_role_set_device (GvcMixerEventRole *role,
 
         g_free (role->priv->device);
         role->priv->device = g_strdup (device);
-        g_object_notify (G_OBJECT (role), "device");
+        g_object_notify_by_pspec (G_OBJECT (role), obj_props[PROP_DEVICE]);
 
         return TRUE;
 }
@@ -169,13 +171,12 @@ gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
         stream_class->push_volume = gvc_mixer_event_role_push_volume;
         stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;
 
-        g_object_class_install_property (object_class,
-                                         PROP_DEVICE,
-                                         g_param_spec_string ("device",
-                                                              "Device",
-                                                              "Device",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        obj_props[PROP_DEVICE] = g_param_spec_string ("device",
+                                                      "Device",
+                                                      "Device",
+                                                      NULL,
+                                                      G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        g_object_class_install_properties (object_class, N_PROPS, obj_props);
 }
 
 static void
diff --git a/subprojects/gvc/gvc-mixer-stream.c b/subprojects/gvc/gvc-mixer-stream.c
index c324900b9..f9bcc40a2 100644
--- a/subprojects/gvc/gvc-mixer-stream.c
+++ b/subprojects/gvc/gvc-mixer-stream.c
@@ -83,7 +83,9 @@ enum
         PROP_CARD_INDEX,
         PROP_PORT,
         PROP_STATE,
+        N_PROPS
 };
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
 
 static void     gvc_mixer_stream_finalize   (GObject            *object);
 
@@ -198,7 +200,7 @@ gvc_mixer_stream_set_volume (GvcMixerStream *stream,
 
         if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
                 gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
-                g_object_notify (G_OBJECT (stream), "volume");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
                 return TRUE;
         }
 
@@ -218,7 +220,7 @@ gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
 
         if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
                 gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
-                g_object_notify (G_OBJECT (stream), "volume");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
         }
 
         return TRUE;
@@ -246,7 +248,7 @@ gvc_mixer_stream_set_is_muted  (GvcMixerStream *stream,
 
         if (is_muted != stream->priv->is_muted) {
                 stream->priv->is_muted = is_muted;
-                g_object_notify (G_OBJECT (stream), "is-muted");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_MUTED]);
         }
 
         return TRUE;
@@ -260,7 +262,7 @@ gvc_mixer_stream_set_can_decibel  (GvcMixerStream *stream,
 
         if (can_decibel != stream->priv->can_decibel) {
                 stream->priv->can_decibel = can_decibel;
-                g_object_notify (G_OBJECT (stream), "can-decibel");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CAN_DECIBEL]);
         }
 
         return TRUE;
@@ -288,7 +290,7 @@ gvc_mixer_stream_set_name (GvcMixerStream *stream,
 
         g_free (stream->priv->name);
         stream->priv->name = g_strdup (name);
-        g_object_notify (G_OBJECT (stream), "name");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_NAME]);
 
         return TRUE;
 }
@@ -301,7 +303,7 @@ gvc_mixer_stream_set_description (GvcMixerStream *stream,
 
         g_free (stream->priv->description);
         stream->priv->description = g_strdup (description);
-        g_object_notify (G_OBJECT (stream), "description");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_DESCRIPTION]);
 
         return TRUE;
 }
@@ -321,7 +323,7 @@ gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
         stream->priv->is_event_stream = is_event_stream;
-        g_object_notify (G_OBJECT (stream), "is-event-stream");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_EVENT_STREAM]);
 
         return TRUE;
 }
@@ -341,7 +343,7 @@ gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
         stream->priv->is_virtual = is_virtual;
-        g_object_notify (G_OBJECT (stream), "is-virtual");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_VIRTUAL]);
 
         return TRUE;
 }
@@ -361,7 +363,7 @@ gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
 
         g_free (stream->priv->application_id);
         stream->priv->application_id = g_strdup (application_id);
-        g_object_notify (G_OBJECT (stream), "application-id");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_APPLICATION_ID]);
 
         return TRUE;
 }
@@ -374,7 +376,7 @@ on_channel_map_volume_changed (GvcChannelMap  *channel_map,
         if (set == TRUE)
                 gvc_mixer_stream_push_volume (stream);
 
-        g_object_notify (G_OBJECT (stream), "volume");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
 }
 
 static gboolean
@@ -402,7 +404,7 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
                                   G_CALLBACK (on_channel_map_volume_changed),
                                   stream);
 
-                g_object_notify (G_OBJECT (stream), "channel-map");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CHANNEL_MAP]);
         }
 
         return TRUE;
@@ -452,7 +454,7 @@ gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
 
         g_free (stream->priv->icon_name);
         stream->priv->icon_name = g_strdup (icon_name);
-        g_object_notify (G_OBJECT (stream), "icon-name");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_ICON_NAME]);
 
         return TRUE;
 }
@@ -465,7 +467,7 @@ gvc_mixer_stream_set_form_factor (GvcMixerStream *stream,
 
         g_free (stream->priv->form_factor);
         stream->priv->form_factor = g_strdup (form_factor);
-        g_object_notify (G_OBJECT (stream), "form-factor");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_FORM_FACTOR]);
 
         return TRUE;
 }
@@ -478,7 +480,7 @@ gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream,
 
         g_free (stream->priv->sysfs_path);
         stream->priv->sysfs_path = g_strdup (sysfs_path);
-        g_object_notify (G_OBJECT (stream), "sysfs-path");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_SYSFS_PATH]);
 
         return TRUE;
 }
@@ -558,7 +560,7 @@ gvc_mixer_stream_set_port (GvcMixerStream *stream,
                 }
         }
 
-        g_object_notify (G_OBJECT (stream), "port");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_PORT]);
 
         return TRUE;
 }
@@ -591,7 +593,7 @@ gvc_mixer_stream_set_state (GvcMixerStream      *stream,
 
         if (stream->priv->state != state) {
                 stream->priv->state = state;
-                g_object_notify (G_OBJECT (stream), "state");
+                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_STATE]);
         }
 
         return TRUE;
@@ -645,7 +647,7 @@ gvc_mixer_stream_set_card_index (GvcMixerStream *stream,
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
         stream->priv->card_index = card_index;
-        g_object_notify (G_OBJECT (stream), "card-index");
+        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CARD_INDEX]);
 
         return TRUE;
 }
@@ -897,140 +899,103 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
         klass->change_port = gvc_mixer_stream_real_change_port;
         klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
 
-        g_object_class_install_property (gobject_class,
-                                         PROP_INDEX,
-                                         g_param_spec_ulong ("index",
-                                                             "Index",
-                                                             "The index for this stream",
-                                                             0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_ID,
-                                         g_param_spec_ulong ("id",
-                                                             "id",
-                                                             "The id for this stream",
-                                                             0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_CHANNEL_MAP,
-                                         g_param_spec_object ("channel-map",
-                                                              "channel map",
-                                                              "The channel map for this stream",
-                                                              GVC_TYPE_CHANNEL_MAP,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_PA_CONTEXT,
-                                         g_param_spec_pointer ("pa-context",
-                                                               "PulseAudio context",
-                                                               "The PulseAudio context for this stream",
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
-        g_object_class_install_property (gobject_class,
-                                         PROP_VOLUME,
-                                         g_param_spec_ulong ("volume",
-                                                             "Volume",
-                                                             "The volume for this stream",
-                                                             0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE));
-        g_object_class_install_property (gobject_class,
-                                         PROP_DECIBEL,
-                                         g_param_spec_double ("decibel",
-                                                              "Decibel",
-                                                              "The decibel level for this stream",
-                                                              -G_MAXDOUBLE, G_MAXDOUBLE, 0,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-
-        g_object_class_install_property (gobject_class,
-                                         PROP_NAME,
-                                         g_param_spec_string ("name",
-                                                              "Name",
-                                                              "Name to display for this stream",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_DESCRIPTION,
-                                         g_param_spec_string ("description",
-                                                              "Description",
-                                                              "Description to display for this stream",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_APPLICATION_ID,
-                                         g_param_spec_string ("application-id",
+        obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
+                                                    "Index",
+                                                    "The index for this stream",
+                                                    0, G_MAXULONG, 0,
+                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_ID] = g_param_spec_ulong ("id",
+                                                 "id",
+                                                 "The id for this stream",
+                                                 0, G_MAXULONG, 0,
+                                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_CHANNEL_MAP] = g_param_spec_object ("channel-map",
+                                                           "channel map",
+                                                           "The channel map for this stream",
+                                                           GVC_TYPE_CHANNEL_MAP,
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
+                                                           "PulseAudio context",
+                                                           "The PulseAudio context for this stream",
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_VOLUME] = g_param_spec_ulong ("volume",
+                                                     "Volume",
+                                                     "The volume for this stream",
+                                                     0, G_MAXULONG, 0,
+                                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_DECIBEL] = g_param_spec_double ("decibel",
+                                                       "Decibel",
+                                                       "The decibel level for this stream",
+                                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_NAME] = g_param_spec_string ("name",
+                                                    "Name",
+                                                    "Name to display for this stream",
+                                                    NULL,
+                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_DESCRIPTION] = g_param_spec_string ("description",
+                                                           "Description",
+                                                           "Description to display for this stream",
+                                                           NULL,
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_APPLICATION_ID] = g_param_spec_string ("application-id",
                                                               "Application identifier",
                                                               "Application identifier for this stream",
                                                               NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_ICON_NAME,
-                                         g_param_spec_string ("icon-name",
-                                                              "Icon Name",
-                                                              "Name of icon to display for this stream",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_FORM_FACTOR,
-                                         g_param_spec_string ("form-factor",
-                                                              "Form Factor",
-                                                              "Device form factor for this stream, as reported by PulseAudio",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_SYSFS_PATH,
-                                         g_param_spec_string ("sysfs-path",
-                                                              "Sysfs path",
-                                                              "Sysfs path for the device associated with this stream",
-                                                              NULL,
-                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_IS_MUTED,
-                                         g_param_spec_boolean ("is-muted",
-                                                               "is muted",
-                                                               "Whether stream is muted",
-                                                               FALSE,
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_CAN_DECIBEL,
-                                         g_param_spec_boolean ("can-decibel",
-                                                               "can decibel",
-                                                               "Whether stream volume can be converted to decibel units",
-                                                               FALSE,
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_IS_EVENT_STREAM,
-                                         g_param_spec_boolean ("is-event-stream",
-                                                               "is event stream",
-                                                               "Whether stream's role is to play an event",
-                                                               FALSE,
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_IS_VIRTUAL,
-                                         g_param_spec_boolean ("is-virtual",
-                                                               "is virtual stream",
-                                                               "Whether the stream is virtual",
-                                                               FALSE,
-                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-        g_object_class_install_property (gobject_class,
-                                         PROP_PORT,
-                                         g_param_spec_string ("port",
-                                                              "Port",
-                                                              "The name of the current port for this stream",
-                                                              NULL,
-                                                              G_PARAM_READWRITE));
-        g_object_class_install_property (gobject_class,
-                                         PROP_STATE,
-                                         g_param_spec_enum ("state",
-                                                            "State",
-                                                            "The current state of this stream",
-                                                            GVC_TYPE_MIXER_STREAM_STATE,
-                                                            GVC_STREAM_STATE_INVALID,
-                                                            G_PARAM_READWRITE));
-        g_object_class_install_property (gobject_class,
-                                         PROP_CARD_INDEX,
-                                         g_param_spec_long ("card-index",
-                                                             "Card index",
-                                                             "The index of the card for this stream",
-                                                             PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
+                                                         "Icon Name",
+                                                         "Name of icon to display for this stream",
+                                                         NULL,
+                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_FORM_FACTOR] = g_param_spec_string ("form-factor",
+                                                           "Form Factor",
+                                                           "Device form factor for this stream, as reported by PulseAudio",
+                                                           NULL,
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_SYSFS_PATH] = g_param_spec_string ("sysfs-path",
+                                                          "Sysfs path",
+                                                          "Sysfs path for the device associated with this stream",
+                                                          NULL,
+                                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_IS_MUTED] = g_param_spec_boolean ("is-muted",
+                                                         "is muted",
+                                                         "Whether stream is muted",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_CAN_DECIBEL] = g_param_spec_boolean ("can-decibel",
+                                                            "can decibel",
+                                                            "Whether stream volume can be converted to decibel units",
+                                                            FALSE,
+                                                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_IS_EVENT_STREAM] = g_param_spec_boolean ("is-event-stream",
+                                                                "is event stream",
+                                                                "Whether stream's role is to play an event",
+                                                                FALSE,
+                                                                G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_IS_VIRTUAL] = g_param_spec_boolean ("is-virtual",
+                                                           "is virtual stream",
+                                                           "Whether the stream is virtual",
+                                                           FALSE,
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_PORT] = g_param_spec_string ("port",
+                                                    "Port",
+                                                    "The name of the current port for this stream",
+                                                    NULL,
+                                                    G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_STATE] = g_param_spec_enum ("state",
+                                                   "State",
+                                                   "The current state of this stream",
+                                                   GVC_TYPE_MIXER_STREAM_STATE,
+                                                   GVC_STREAM_STATE_INVALID,
+                                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+        obj_props[PROP_CARD_INDEX] = g_param_spec_long ("card-index",
+                                                        "Card index",
+                                                        "The index of the card for this stream",
+                                                        PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX,
+                                                        G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+
+        g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
 }
 
 static void
diff --git a/subprojects/gvc/gvc-mixer-ui-device.c b/subprojects/gvc/gvc-mixer-ui-device.c
index f7dd33ec1..db1a694bb 100644
--- a/subprojects/gvc/gvc-mixer-ui-device.c
+++ b/subprojects/gvc/gvc-mixer-ui-device.c
@@ -55,7 +55,9 @@ enum
         PROP_UI_DEVICE_TYPE,
         PROP_PORT_AVAILABLE,
         PROP_ICON_NAME,
+        N_PROPS
 };
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
 
 static void     gvc_mixer_ui_device_finalize   (GObject               *object);
 
@@ -224,7 +226,6 @@ static void
 gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass)
 {
         GObjectClass* object_class = G_OBJECT_CLASS (klass);
-        GParamSpec *pspec;
 
         object_class->constructor = gvc_mixer_ui_device_constructor;
         object_class->dispose = gvc_mixer_ui_device_dispose;
@@ -232,62 +233,64 @@ gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass)
         object_class->set_property = gvc_mixer_ui_device_set_property;
         object_class->get_property = gvc_mixer_ui_device_get_property;
 
-        pspec = g_param_spec_string ("description",
+        obj_props[PROP_DESC_LINE_1] =
+                g_param_spec_string ("description",
                                      "Description construct prop",
                                      "Set first line description",
                                      "no-name-set",
-                                     G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_DESC_LINE_1, pspec);
+                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_string ("origin",
+        obj_props[PROP_DESC_LINE_2] =
+                g_param_spec_string ("origin",
                                      "origin construct prop",
                                      "Set second line description name",
                                      "no-name-set",
-                                     G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_DESC_LINE_2, pspec);
+                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_pointer ("card",
+        obj_props[PROP_CARD] =
+                g_param_spec_pointer ("card",
                                       "Card from pulse",
                                       "Set/Get card",
-                                      G_PARAM_READWRITE);
+                                      G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        g_object_class_install_property (object_class, PROP_CARD, pspec);
-
-        pspec = g_param_spec_string ("port-name",
+        obj_props[PROP_PORT_NAME] =
+                g_param_spec_string ("port-name",
                                      "port-name construct prop",
                                      "Set port-name",
                                      NULL,
-                                     G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_PORT_NAME, pspec);
+                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_uint ("stream-id",
+        obj_props[PROP_STREAM_ID] =
+                g_param_spec_uint ("stream-id",
                                    "stream id assigned by gvc-stream",
                                    "Set/Get stream id",
                                    0,
                                    G_MAXUINT,
                                    GVC_MIXER_UI_DEVICE_INVALID,
-                                   G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_STREAM_ID, pspec);
+                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_uint ("type",
+        obj_props[PROP_UI_DEVICE_TYPE] =
+                g_param_spec_uint ("type",
                                    "ui-device type",
                                    "determine whether its an input and output",
-                                   0, 1, 0, G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_UI_DEVICE_TYPE, pspec);
+                                   0, 1, 0,
+                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_boolean ("port-available",
+        obj_props[PROP_PORT_AVAILABLE] =
+                g_param_spec_boolean ("port-available",
                                       "available",
                                       "determine whether this port is available",
                                       FALSE,
-                                      G_PARAM_READWRITE);
-        g_object_class_install_property (object_class, PROP_PORT_AVAILABLE, pspec);
+                                      G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
-        pspec = g_param_spec_string ("icon-name",
+        obj_props[PROP_ICON_NAME] =
+                g_param_spec_string ("icon-name",
                                      "Icon Name",
                                      "Name of icon to display for this card",
                                      NULL,
-                                     G_PARAM_READWRITE|G_PARAM_CONSTRUCT);
-        g_object_class_install_property (object_class, PROP_ICON_NAME, pspec);
+                                     G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+
+        g_object_class_install_properties (object_class, N_PROPS, obj_props);
 }
 
 /* Removes the part of the string that starts with skip_prefix
@@ -650,7 +653,7 @@ gvc_mixer_ui_device_set_icon_name (GvcMixerUIDevice *device,
 
         g_free (device->priv->icon_name);
         device->priv->icon_name = g_strdup (icon_name);
-        g_object_notify (G_OBJECT (device), "icon-name");
+        g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_ICON_NAME]);
 }
 
 
diff --git a/subprojects/shew/meson.build b/subprojects/shew/meson.build
index ac0f62724..58a2499f8 100644
--- a/subprojects/shew/meson.build
+++ b/subprojects/shew/meson.build
@@ -1,5 +1,5 @@
 project('shew', 'c',
-  version: '43.6',
+  version: '43.9',
   meson_version: '>= 0.58.0',
   license: 'LGPLv2+',
 )

Reply to: