diff -pruN 49.0-1/debian/README.Ubuntu 49.0-1ubuntu1/debian/README.Ubuntu
--- 49.0-1/debian/README.Ubuntu	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/README.Ubuntu	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,61 @@
+# Reporting Shell theme changes to Yaru.
+
+Our default theme is Yaru, it means that any changes, especially breaking changes (meaning, changes that can't be done
+without a theme update) needs to be coordinated between the gnome-shell updates and Yaru. Probably with  a **Breaks:** stenza
+in `debian/control`
+
+## What should be reported?
+
+The upstream GNOME Shell `data/theme/` directory is used as a basis for our Yaru GNOME Shell theme so any change made upstream needs to be reflected in our derivative version. Most of the sass files are under the `gnome-shell-sass/` subdirectory.
+
+Note that Yaru has its own `meson.build` file. Changes there shouldn't be in general reported apart if new assets are installed.
+It should be a simple assessment in general if you need to do anything on the build system side.
+
+## Structure
+
+Ensure you cloned the Yaru repository via `git clone https://github.com/ubuntu/yaru`. The shell theme is under `gnome-shell`.
+It contains two subdirectories:
+ * `upstream/` is the latest upstream sass source we based on. This is the reference.
+ * `src` is the Yaru theme, which is `upstream/` + the yaru modifications.
+
+Basically diffing `upstream` and `src` provides you the difference between the default GNOME Shell theme and our Yaru Shell theme.
+
+## Reporting changes
+
+### Checking what to report
+
+Note: you should do that either recursively, or directly on each file that was modified for the new GNOME Shell release.
+Basically, you can run: `diff -r yaru/gnome-shell/upstream new-gnome-shell-version/data/theme | lsdiff` to get the list between
+latest upstream snapshot in Yaru and current upstream changes.
+
+The easiest way is a 3-way merge diff and manually reporting what makes sense. I personally used `meld` for this and run:
+```
+$ meld yaru/gnome-shell/upstream/<path_to_modified_file_in_new_version> new-gnome-shell-version/data/theme/<path_to_modified_file_in_new_version> yaru/gnome-shell/src/<path_to_modified_file_in_new_version>
+```
+
+Note: as diff, meld can infer the filename (only repeat it once) as long as you point to the correct directory.
+
+You will see a window with 3 columns:
+ * New changes are visible between the first two columns (diff between old upstream and new upstream). You report them,
+as you may see it fit (clicking meld arrow or manually) on the third column.
+ * Second to third column diff is consequently the new additional delta you are introducing, if needed. It should be most
+of the time be null (no diff) or minimal once you are done.
+
+Commit all those changes, don't forget the svg assets.
+
+### Taking a new upstream snapshot
+
+Once done, snapshot the new GNOME Shell upstream directory we based our changes on for the next Shell update involving a new 3 way merge:
+```
+$ rm -r yaru/gnome-shell/upstream/
+$ cp -a new-gnome-shell-version/data/theme/ yaru/gnome-shell/upstream/
+```
+
+### Changing package coordination
+
+Once again, if the changes are breaking, updates the **Breaks:** or other fields between **gnome-shell** and
+**yaru-theme-gnome-shell** to ensure people are getting both updates simultaneously.
+
+### Submitting
+
+Commit that, and submit a MP against https://github.com/ubuntu/yaru.
diff -pruN 49.0-1/debian/changelog 49.0-1ubuntu1/debian/changelog
--- 49.0-1/debian/changelog	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/changelog	2025-09-24 16:23:55.000000000 +0000
@@ -1,3 +1,18 @@
+gnome-shell (49.0-1ubuntu1) questing; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Drop applied upstream
+  * debian/patches: Refresh
+  * d/tests/control: Use a more robust xvfb-run command-line
+
+  [ Simon McVittie ]
+  * d/p/slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch:
+    Add patch from upstream to fix a UI freeze when touching quick menu
+    sliders (volume, brightness) on a touchscreen (LP: #2125013)
+  * d/rules: Use a more robust xvfb-run command-line in tests
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 24 Sep 2025 18:23:55 +0200
+
 gnome-shell (49.0-1) experimental; urgency=medium
 
   [ Jeremy Bícha ]
@@ -17,108 +32,180 @@ gnome-shell (49.0-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 16 Sep 2025 20:49:15 -0400
 
-gnome-shell (48.5-1) unstable; urgency=medium
+gnome-shell (49~rc-0ubuntu2) questing; urgency=medium
+
+  * debian/patch: Add fix for brightness manager handle globalScale
+  * debian/control: Require glib 2.86.0 as build dependency
+    While it's technically not a build dependency, if we build with older
+    GNOME shell, we end up generating a Shell typelib that refers to
+    DesktopAppInfo symbols inside Gio, rather than in GioUnix and this
+    causes an immediate crash as soon as we try to get an appInfo from a
+    shell application in JS
+  * debian: Bump dependencies on GLib and gjs
+  * d/p: Add patches to fix tests compilation dependencies
+  * debian/tests: Add autopkgtests for GNOME Shell
+  * debian/tests: Run tests on the installed gnome-shell.
+    Run the autopkgtests running the upstream-provided test scripts using
+    the gnome-shell in the archive installed as it is.
+    When in ubuntu, we are also using the ubuntu profile.
+  * debian/tests/control: Install the ubuntu shell extensions in shell tests.
+    When running the shell tests also install the ubuntu extensions, so that
+    we can ensure that they are not breaking anything relevant.
+    We are not checking (yet) if the extensions are working, but a test for
+    this can be added at later point
+  * debian/tests/control: Ignore i386 on ubuntu
+  * d/p/prompting-extension: Improve error if snap application PID has not
+    been found
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 12 Sep 2025 18:34:30 +0200
+
+gnome-shell (49~rc-0ubuntu1) questing; urgency=medium
+
+  * New upstream release:
+    - Fix leak with custom themes (LP: #2121786)
+    - Prefer physical scroll directions on sliders (LP: #1738676)
+    - Use a clearer icon for active wifi connections without AP (LP: #2120734)
+    - Use GioUnix for DesktopAppInfo (LP: #2120780)
+  * d/p/ubuntu-authd:
+    - Do not emit verification-complete signal.
+      When the verification is completed from the authd side, the PAM stack
+      that follows can still fail for some other reasons, so do not
+      preemptively mark the verification as completed until we're really done.
+      This is not a security issue since the signal is not leading to user
+      access until the PAM stack is completed, but it may still make the shell
+      to be in a wrong state in case the authentication does not complete.
+    - Fix cancel handling with only one broker / auth method.
+      In case only one broker (unlikely, but potentially possible) or an
+      authentication method is available, the "go back" request did not work
+      properly, so fix these cases by checking the current state
+    - Cleanup the broker and auth mode selection.
+      We can factorize some repeated code
+    - Autoselect the first broker / auth mode if only received.
+      If we only have one broker or an auth mode, it does not make sense to
+      show a choice list, we can just proceed with it
+    - Do not explicitly start an auth mechanism.
+      Submitting a changed mechanisms list is enough to trigger the machinery
+      to switch the default mechanism, since we already sort the mechanisms by
+      their priority and gdm will pick them in order.
+      Instead before we might ended up selecting the same mechanism twice
+    - Do not re-start a challenge for the same auth mode.
+      It may lead to cancelling the current one for no reason
+    - Do not show intermediate UIs if uneeded
+    - Never show the QR code intro button.
+      It's just an unnecessary extra step for us
+    - Do not wait for auto-selection while going back.
+      As per previous commits we are now waiting for potential auto-selection
+      to happen before asking the user to pick a choice, to prevent this to
+      slow down the "going back" experience, we can ignore the timeout when
+      going backwards, since there's no risk of auto-selection to happen
+    - authPrompt preserve the text when switching entry visibility.
+      Avoid replacing lockscreen password entry contents when visibility
+      changes
+    - Do not retry verification on explicit user cancellation.
+      When going back (or hitting escape) key while authentication is in
+      progress, once we've reached the initial stage, we can just mark the
+      authentication as failed to avoid the default logic to retry until the
+      max-retries are reached.
+      This is something that authd handles internally, so we do not have any
+      need for this
+    - Do not reset the authentication at every lockscreen tap.
+      Upstream code is affected by this bug that is more annoying in a
+      MFA-setup, where just tapping the lockscreen may lead the authentication
+      to be restarted.
+      So stop this, by handling the tap event only when it matters
+  * d/p: Use a better fix for the key-focus of the choice list
+  * d/p: Drop patches, applied upstream
+  * d/p: Refresh patch indexes
+  * debian/control:
+    - Update build dependencies
+    - Require newer ubuntu mutter package version
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 03 Sep 2025 08:17:26 +0200
+
+gnome-shell (49~beta.1-0ubuntu3) questing; urgency=medium
+
+  * Revert "d/p: Keep support being started via legacy desktop file"
+    GNOME Shell now can start only via newer gnome-session (>= 49~beta), backed
+    by systemd, but we do not depend on it technically, so it's just better to
+    break that.
+  * d/control: Add a Breaks on older gnome and ubuntu sessions.
+    As per the fallback .desktop file removal, gnome shell requires newer
+    gnome session or ubuntu session.
+    However, this is not a dependency, so let's just instruct apt to update
+    the shell together with a newer version of the session
+  * d/p/ubuntu-authd: Do not fail verification on PAM error messages.
+    We used to mimic the old behavior of gnome shell, by failing on any PAM
+    error message, this is breaking PAM stacks where a module fails with a
+    non-fatal error though, such as when a password change is required or
+    when authd is not reachable for whatever reason. (LP: #2120634)
+  * d/p/ubuntu/snapd-prompting-extension: Remove linting not required files.
+    And also move the patch into the ubuntu namespace
+  * d/p: Add more complete re-implementation of Gio.DesktopAppInfo
+  * d/p: Cherry-pick upstream quicksettings connections to enterprise AP fix
+    (LP: #2120331)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 20 Aug 2025 05:44:02 +0200
+
+gnome-shell (49~beta.1-0ubuntu2) questing; urgency=medium
+
+  * d/p: Add missing GDesktopAppIcon methods
+  * d/p: Include gnome-shell snapd prompting extension
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 14 Aug 2025 20:21:29 +0200
+
+gnome-shell (49~beta.1-0ubuntu1) questing; urgency=medium
 
   * New upstream release
-  * debian/gnome-shell.gsettings-override: update Yelp to org.gnome.Yelp
-  * Remove tray-icons patches applied in new release
-  * Remove st-theme-node patch: alternative fix applied in new release
+  * d/p: Refresh
+  * debian/control: Bump build dependencies
+  * debian/patches: Cherry pick further upstream fixes
+  * d/p: Fix include path for girepository headers
 
- -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 15 Sep 2025 18:41:10 -0400
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 12 Aug 2025 04:56:37 +0200
 
-gnome-shell (48.4-1) unstable; urgency=medium
+gnome-shell (49~beta-0ubuntu1) questing; urgency=medium
 
-  * Team upload
-  * New upstream stable release
-    - network: If a network has no ID, don't treat it as available,
-      avoiding breaking the network menu
-      (gnome-shell!3785 upstream)
-    - Improve URL recognition heuristic for notifications so that non-URLs do
-      not become a link
-      (gnome-shell#8517 upstream)
-    - In gdm, improve efficiency of user list
-      (gnome-shell!3799 upstream)
-    - Fix signal order when taking a screenshot interactively is triggered
-      via D-Bus, for example from xdg-desktop-portal
-      (gnome-shell#8499 upstream)
-    - Improve cursor scaling on systems with different-DPI monitors when using
-      the Magnifier accessibility tool
-      (gnome-shell!475 upstream)
-    - In sliders like volume and brightness, avoid drawing part of the bar
-      over the handle in RTL locales
-      (gnome-shell!3817 upstream)
-    - Improve robustness of signal connections in the Thunderbolt and
-      smart-card code
-      (gnome-shell!3796 upstream)
-    - Code cleanups in extensions management service
-      (part of gnome-shell!3750 upstream)
-    - Translation updates
-  * d/control: Bump gjs version to 1.81.2 as per meson.build.
-    No practical effect, 1.82.x is already in trixie.
-  * d/gbp.conf: Use debian/forky branch for uploads targeting forky.
-    We'll stick to 48.x in testing/unstable for now, to get better testing
-    for future 48.x updates in trixie. Preliminary 49.x packaging for
-    experimental is already using the debian/latest branch.
+  [ Marco Trevisan (Treviño) ]
+  * New upstream release
+  * d/p: Refresh patches
+  * d/control: Bump build dependencies
+  * d/p: Refresh, dropping applied upstream
+  * d/p: Keep support being started via legacy desktop file.
+    With older gnome-session this is still required.
+    Drop the patches as soon as gnome-session 49 will be ready for this.
 
- -- Simon McVittie <smcv@debian.org>  Fri, 08 Aug 2025 09:19:26 +0100
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Sat, 09 Aug 2025 04:13:57 +0200
 
-gnome-shell (48.3-1) unstable; urgency=medium
+gnome-shell (49~alpha.1-0ubuntu2) questing; urgency=medium
 
-  * Team upload
-  * New upstream stable release
-    - Accessibility improvements (LP: #2115948)
-      + In the Alt+F2 "run" dialog, provide the dialog title to screen
-        readers and other accessibility tools (gnome-shell!3736 upstream)
-      + In the calendar/notifications menu, label the Events and Weather
-        areas for accessibility tools (gnome-shell!3769 upstream)
-      + In popup menus, tell accessibility tools that separators are
-        not useful to select (gnome-shell!3773 upstream)
-      + In text entry widgets, send the hint (placeholder text) to
-        accessibility tools (gnome-shell!3179 upstream)
-      + In text entry widgets, make the label available to accessibility
-        tools (gnome-shell!3179 upstream)
-      + When running as a gdm greeter, if there is an error such as a wrong
-        password, send it to the screen reader as well as displaying it
-        (gnome-shell!3765 upstream)
-    - When prompting for a password change via Gcr, if the repeated password
-      doesn't match, re-enable the UI widgets so the user can try again
-      (gnome-shell!3757 upstream)
-    - Always update on-screen keyboard layout to reflect the purpose hint,
-      so that the expected features appear (alphanumeric vs. numeric-only,
-      emoji chooser shown or hidden, etc.)
-      (gnome-shell#8377 upstream)
-    - Limit the length of the input method indicator by counting grapheme
-      clusters rather than UTF-16 string lengths, allowing input methods like
-      ibus-typing-booster to use emoji as their indicator
-      (gnome-shell#1026 upstream)
-    - Avoid the top bar disappearing when doing a 3-finger downward swipe
-      gesture on a touchscreen
-      (gnome-shell#7456 upstream)
-    - If Decibels is installed, put it in the Utilities folder as intended
-      (Decibels is not yet in Debian, ITP: #1100886)
-      (gnome-shell!3728 upstream)
-    - If the mouse button modifier (normally Super, i.e. the Windows key)
-      is set to a combination of modifiers such as <Control><Super>, only
-      make the mouse wheel switch between desktops if *all* of those
-      modifiers are held
-      (mutter#4129 upstream)
-    - Avoid flooding the systemd journal with old time-limit transitions if
-      debug messages are enabled
-      (gnome-shell!3740 upstream)
-    - Reuse one proxy object for all communication with the gdm greeter,
-      which is more efficient and can mitigate per-connection memory
-      leaks in gdm
-      (gnome-shell!3749 upstream)
-    - Better build-time compatibility with non-glibc libc
-      (gnome-shell#8457 upstream, no practical effect on Debian)
-    - Remove redundant eslint hints
-      (gnome-shell!3756 upstream, no practical effect since they're comments)
-    - Translation updates
-    - CI changes with no effect on Debian
-  * d/patches: Re-export patch series (no functional changes)
-  * d/control: Update Homepage field (Closes: #1100764)
+  * d/control: Drop dependency xserver-xorg-legacy
+
+ -- Alessandro Astone <alessandro.astone@canonical.com>  Mon, 04 Aug 2025 16:02:00 +0200
+
+<<<<<<<
+gnome-shell (49~alpha.1-0ubuntu1) questing; urgency=medium
+
+  * Merge with debian, containing a new upstream release
+  * d/patches: Refresh
+  * debian/rules: Update stylesheet regeneration script path
+  * d/p/ubuntu-authd: Ensure that error messages are always shown on failure
+  * d/p/ubuntu-authd: Fix handling of authentication switching for wait layouts
+  * d/p/ubuntu-authd: Drop some delta with upstream and fix greeter buttons
+  * d/p/ubuntu-authd: Move login options button to the leftmost side
+  * d/p: Cleanup the yaru spinner patch
 
- -- Simon McVittie <smcv@debian.org>  Sun, 13 Jul 2025 12:24:58 +0100
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 11 Jul 2025 06:16:31 +0200
+
+=======
+>>>>>>>
+gnome-shell (48.5-1) unstable; urgency=medium
+
+  * New upstream release
+  * debian/gnome-shell.gsettings-override: update Yelp to org.gnome.Yelp
+  * Remove tray-icons patches applied in new release
+  * Remove st-theme-node patch: alternative fix applied in new release
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 15 Sep 2025 18:41:10 -0400
 
 gnome-shell (48.2-3) unstable; urgency=medium
 
@@ -198,6 +285,18 @@ gnome-shell (48.1-1) unstable; urgency=m
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 17 Apr 2025 13:38:58 -0400
 
+gnome-shell (48.0-1ubuntu2) questing; urgency=medium
+
+  * d/p/authd: Notify messages on authentication Next too
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 26 May 2025 14:46:39 +0200
+
+gnome-shell (48.0-1ubuntu1) plucky; urgency=medium
+
+  * Merge with Debian
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 21 Mar 2025 09:15:46 -0400
+
 gnome-shell (48.0-1) unstable; urgency=medium
 
   * New upstream release
@@ -205,6 +304,43 @@ gnome-shell (48.0-1) unstable; urgency=m
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 21 Mar 2025 09:15:07 -0400
 
+gnome-shell (48~rc-2ubuntu5) plucky; urgency=medium
+
+  * d/p/authd: Update generated proto files to match new protocol
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 21 Mar 2025 01:48:18 +0100
+
+gnome-shell (48~rc-2ubuntu4) plucky; urgency=medium
+
+  * d/p/ubuntu-authd: Update authd authentication to support latest protocol
+  * debian/control: Add breaks on authd << 0.4.2~
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 21 Mar 2025 01:06:46 +0100
+
+gnome-shell (48~rc-2ubuntu3) plucky; urgency=medium
+
+  * d/p: Use native accent colors instead of yaru flavored stylesheets
+  * d/p: Drop patch to use CSS foreground for highlighted text
+  * d/p: Use yaru accent colors when yaru is used
+  * d/p: Optimize yaru accent colors for contrast
+  * debian/control: Bump recommends on yaru-theme-gnome-shell 25.04
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 14 Mar 2025 07:21:56 +0100
+
+gnome-shell (48~rc-2ubuntu2) plucky; urgency=medium
+
+  * Disable startup sound by default: it can be re-enabled
+    in Settings > Sound > Startup Sound
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 13 Mar 2025 10:55:46 -0400
+
+gnome-shell (48~rc-2ubuntu1) plucky; urgency=medium
+
+  * Merge with Debian
+  * Remove 2 patches applied in new release
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 06 Mar 2025 12:11:37 -0500
+
 gnome-shell (48~rc-2) unstable; urgency=high
 
   * Release to unstable
@@ -233,6 +369,18 @@ gnome-shell (48~beta-4) unstable; urgenc
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Sun, 02 Mar 2025 05:40:03 -0500
 
+gnome-shell (48~beta-3ubuntu1) plucky; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * Merge with debian
+  * debian/salsa-ci: Use ubuntu recipes
+  * debian/patches/ubuntu-authd: Rebase as per upstream changes
+
+  [ Daniel van Vugt ]
+  * Add missing import to the magnifier patch (LP: #2098745)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Sat, 22 Feb 2025 05:54:30 +0100
+
 gnome-shell (48~beta-3) experimental; urgency=medium
 
   * debian/control: Set both upper and lower bounds for gnome-shell-common
@@ -267,6 +415,14 @@ gnome-shell (48~beta-2) experimental; ur
 
  -- Simon McVittie <smcv@debian.org>  Sat, 15 Feb 2025 16:47:57 +0000
 
+gnome-shell (48~beta-1ubuntu1) plucky; urgency=medium
+
+  * Merge with Debian
+  * Refresh patches
+  * Temporarily disable authd patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 11 Feb 2025 22:17:37 -0500
+
 gnome-shell (48~beta-1) experimental; urgency=medium
 
   * New upstream release
@@ -325,6 +481,53 @@ gnome-shell (47.0-3) unstable; urgency=m
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 04 Oct 2024 19:14:09 -0400
 
+gnome-shell (47.0-2ubuntu3.1) plucky; urgency=medium
+
+  * No-change upload to plucky
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 22 Oct 2024 11:38:25 +0200
+
+gnome-shell (47.0-2ubuntu3) oracular; urgency=medium
+
+  * d/p: Fix toggling lockscreen view using Escape (LP: #2084308)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Sat, 12 Oct 2024 04:57:30 +0200
+
+gnome-shell (47.0-2ubuntu2) oracular; urgency=medium
+
+  * d/p/ubuntu/secure-mode-extension: Return null on invalid extension.
+    The upstream code checks whether the returned extension is null, not
+    undefined, so we may end up returning an accepted values that will fail
+    later when parsing the extension content (LP: #2083615)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 03 Oct 2024 16:55:49 +0200
+
+gnome-shell (47.0-2ubuntu1) oracular; urgency=medium
+
+  [ Alessandro Astone ]
+  * Merge with Debian (LP: #2081646). Remaining changes:
+    - Add Build-Depends: terser (for authd)
+    - Add Depends: ubuntu-wallpapers
+    - Add Recommends:
+      + ubuntu-session (| gnome-session) to have the Ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default Ubuntu theming
+    - Move from Recommends to Suggests: gnome-browser-connector
+    - debian/gnome-shell-common.alternatives: Install tweaked GDM theme
+    - Add debian/ubuntu-session-mods/ubuntu.json & install it with
+      gnome-shell-common
+    - Add many ubuntu-specific patches
+    - Add patches to support authd authentication in GDM
+
+  [ Robert Malz ]
+  * d/p: Fix default service discovery (LP: #2065432)
+
+  [ Marco Trevisan (Treviño) ]
+  * d/p/appMenu-snap-store-details: Activate snap store app on click
+    (LP: #2081716)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 02 Oct 2024 01:58:19 +0200
+
 gnome-shell (47.0-2) experimental; urgency=medium
 
   * Update gnome-browser-connector binary package name
@@ -333,6 +536,32 @@ gnome-shell (47.0-2) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 20 Sep 2024 08:46:12 -0400
 
+gnome-shell (47.0-1ubuntu2) oracular; urgency=medium
+
+  * d/p: Show app details menu for snap applications
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 16 Sep 2024 23:58:16 +0200
+
+gnome-shell (47.0-1ubuntu1) oracular; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add Build-Depends: terser (for authd)
+    - Add Depends: ubuntu-wallpapers
+    - Add Recommends:
+      + ubuntu-session (| gnome-session) to have the Ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default Ubuntu theming
+    - Move from Recommends to Suggests: chrome-gnome-shell
+    - debian/gnome-shell-common.alternatives: Install tweaked GDM theme
+    - Add debian/ubuntu-session-mods/ubuntu.json & install it with
+      gnome-shell-common
+    - Add many patches
+  * Remove patch applied in new release
+  * Refresh patches
+  * Update gnome-browser-connector binary package name
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 16 Sep 2024 07:49:10 -0400
+
 gnome-shell (47.0-1) experimental; urgency=medium
 
   [ Jeremy Bícha ]
@@ -346,6 +575,52 @@ gnome-shell (47.0-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Sat, 14 Sep 2024 21:16:08 -0400
 
+gnome-shell (47~rc-3ubuntu2) oracular; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * d/p/ubuntu-authd: Define the service name just once
+  * d/p/pwa: Fix handling of non-sandboxed apps, or invalid apps.
+
+  [ Nathan Pratta Teodosio ]
+  * Add chromium-snap-pwa.patch:
+    Fixes tab and dock grouping of progressive web applications
+    for the Chromium snap (LP: #2007652).
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 16 Sep 2024 11:44:24 +0200
+
+gnome-shell (47~rc-3ubuntu1) oracular; urgency=medium
+
+  [ Jeremy Bícha ]
+  * Merge with Debian. Remaining changes:
+    - Add Build-Depends: terser (for authd)
+    - Add Depends: ubuntu-wallpapers
+    - Add Recommends:
+      + ubuntu-session (| gnome-session) to have the Ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default Ubuntu theming
+    - Move from Recommends to Suggests: chrome-gnome-shell
+    - debian/gnome-shell-common.alternatives: Install tweaked GDM theme
+    - Add debian/ubuntu-session-mods/ubuntu.json & install it with
+      gnome-shell-common
+    - Add many patches
+
+  [ Marco Trevisan (Treviño) ]
+  * d/p: Ensure PAM messages are fully notified on failures
+  * d/p/ubuntu-authd: Improve notification of authentication error messages.
+    Ensure that messages are visible enough time for the user being able to
+    read them.
+  * d/p/ubuntu-authd: Cleanup patches so that they're split more correctly.
+    No final content change
+  * d/p/ubuntu-authd: Improve theming and consistence of the login auth list
+    items
+
+  [ Daniel van Vugt ]
+  * Add shell-app-Warn-instead-of-crashing-if-disposed-before-sta.patch
+    to avoid crashing the shell if an app misbehaves coincidentally close
+    to a garbage collection run (LP: #2037055)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 11 Sep 2024 03:47:31 +0200
+
 gnome-shell (47~rc-3) experimental; urgency=medium
 
   * Disable the portal helper popup window and use the notification/browser
@@ -370,6 +645,40 @@ gnome-shell (47~rc-1) experimental; urge
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 06 Sep 2024 12:07:18 -0400
 
+gnome-shell (47~beta-1ubuntu2) oracular; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * d/p: Update accent color patches to support new accent-color key
+  * d/p/ubuntu: Add support for brown accent color
+  * d/p/ubuntu: Add support for playing a startup sound
+  * debian/control: Bump dependency on desktop schemas 47~beta-1ubuntu2
+
+  [ Jeremy Bícha ]
+  * Disable the portal helper popup window and use the notification/browser
+    method instead. Hardening related to CVE-2024-36472
+  * Drop now unused Depends: gir1.2-webkit-6.0
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 15 Aug 2024 09:48:11 -0400
+
+gnome-shell (47~beta-1ubuntu1) oracular; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add Build-Depends: terser (for authd)
+    - Add Depends: ubuntu-wallpapers
+    - Add Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move from Recommends to Suggests: chrome-gnome-shell
+    - debian/gnome-shell-common.alternatives: Install tweaked GDM theme
+    - Add debian/ubuntu-session-mods/ubuntu.json & install it with
+      gnome-shell-common
+    - Add many patches
+  * Refresh patches
+  * Disable Yaru accent color patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 07 Aug 2024 20:30:40 -0400
+
 gnome-shell (47~beta-1) experimental; urgency=medium
 
   * New upstream release
@@ -378,6 +687,23 @@ gnome-shell (47~beta-1) experimental; ur
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 05 Aug 2024 12:39:55 -0400
 
+gnome-shell (46.4-1ubuntu1) oracular; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add Build-Depends: terser (for authd)
+    - Add Depends: ubuntu-wallpapers
+    - Add Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move from Recommends to Suggests: chrome-gnome-shell
+    - debian/gnome-shell-common.alternatives: Install tweaked GDM theme
+    - Add debian/ubuntu-session-mods/ubuntu.json & install it with
+      gnome-shell-common
+    - Add many patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 05 Aug 2024 11:27:44 -0400
+
 gnome-shell (46.4-1) unstable; urgency=medium
 
   * New upstream release
@@ -420,6 +746,26 @@ gnome-shell (46.3.1-2) unstable; urgency
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 12 Jul 2024 18:29:07 -0400
 
+gnome-shell (46.3.1-1ubuntu1) oracular; urgency=medium
+
+  * Merge with Debian
+  * debian/patches: Refresh
+  * d/p/ubuntu-authd: Add custom button to the qrcode view and automatic ack.
+    Do not require manual intervention to dismiss the qrcode view and start
+    the qrcode authentication, and support regenerating the qrcode using
+    a custom button (LP: #2072710, #2072711)
+  * d/p/ubuntu-authd: Add support for showing the login code in the qrcode view
+    (LP: #2072714)
+  * d/p/ubuntu-authd: Cleanup prompt view when changing auth mode.
+    When switching from an auth mode to another we need to hide the uneeded
+    items and cleanup the messages, do it so that switching form password
+    auth, to a field-less view works as expected (LP: #2072718)
+  * d/p/ubuntu-authd: Fix key navigation on choice list and waiting items.
+    Make possible to use key-navigation automatically when such views are
+    shown (LP: #2072716)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 15 Jul 2024 15:08:36 +0200
+
 gnome-shell (46.3.1-1) experimental; urgency=medium
 
   [ Marco Trevisan (Treviño) ]
@@ -441,6 +787,57 @@ gnome-shell (46.3.1-1) experimental; urg
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 11 Jul 2024 00:02:12 +0200
 
+gnome-shell (46.2-1ubuntu4) oracular; urgency=medium
+
+  * Add st-theme-node-Forget-properties-cache-on-stylesheet-chang.patch
+    to fix crashes that occur when locking the screen from within the overview.
+    But more generally can occur whenever any extension that modifies
+    stylesheets (like ubuntu-dock or dash-to-dock) is unloaded (LP: #2069559).
+
+ -- Daniel van Vugt <daniel.van.vugt@canonical.com>  Wed, 03 Jul 2024 14:37:10 +0800
+
+gnome-shell (46.2-1ubuntu3) oracular; urgency=medium
+
+  * d/p/ubuntu-authd: Revert unwanted change causing ibus not to launch
+    (LP: #2069381)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 14 Jun 2024 06:45:11 +0200
+
+gnome-shell (46.2-1ubuntu2) oracular; urgency=medium
+
+  * debian/patches/ubuntu-authd: Properly handle new-password messages requests
+    (LP: #2068080)
+  * d/p/ubuntu-authd: Add translatable js files to POTFILES.in (LP: #2068912)
+  * d/p: Do not call unsafe functions when spawning new processes (LP: #2019776)
+  * d/p: Resize tray icon windows to respect their actor representation
+    (LP: #2012388)
+  * d/p: Do not make tray icons to take any input event directly (LP: #2012388)
+  * debian/patches/ubuntu-authd: Properly handle new-password messages requests
+    (LP: #2068080)
+  * d/p/ubuntu-authd: Add translatable js files to POTFILES.in (LP: #2068912)
+  * d/p: Do not call unsafe functions when spawning new processes (LP: #2019776)
+  * d/p: Resize tray icon windows to respect their actor representation
+    (LP: #2012388)
+  * d/p: Do not make tray icons to take any input event directly (LP: #2012388)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 14 Jun 2024 05:47:07 +0200
+
+gnome-shell (46.2-1ubuntu1) oracular; urgency=medium
+
+  [ Jeremy Bícha ]
+  * Merge with Debian
+  * Refresh patches
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches/ubuntu-authd: Properly draw qr-codes for longer URIs
+    (LP: #2067610)
+  * debian/patches/ubuntu-authd: Fix dialog text color when using light theme
+    (LP: #2067661)
+  * debian/patches/ubuntu-authd: Use right colors to draw the QR code in
+    light mode (LP: #2067661)
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 31 May 2024 07:14:43 -0400
+
 gnome-shell (46.2-1) experimental; urgency=medium
 
   * New upstream release:
@@ -453,6 +850,13 @@ gnome-shell (46.2-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 31 May 2024 08:03:22 -0400
 
+gnome-shell (46.1-1ubuntu1) oracular; urgency=medium
+
+  * Merge with Debian
+  * Refresh patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 13 May 2024 15:38:15 +0200
+
 gnome-shell (46.1-1) experimental; urgency=medium
 
   * New upstream release
@@ -486,6 +890,107 @@ gnome-shell (46.0-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 19 Mar 2024 15:36:16 -0400
 
+gnome-shell (46.0-0ubuntu5.1) noble-security; urgency=medium
+
+  * Fix compatibility with glib2.0 security update
+    - debian/patches/correct_expected_bus_name.patch: correct expected bus
+      name for streams in js/dbusServices/screencast/screencastService.js.
+
+ -- Marc Deslauriers <marc.deslauriers@ubuntu.com>  Tue, 07 May 2024 17:03:32 -0400
+
+gnome-shell (46.0-0ubuntu5) noble; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Drop loginDialog-Create-footer-container.patch - it's buggy and still
+    under active development (LP: #2060893)
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches/ubuntu-authd: Hide the right button if there are no
+    sessions to choose (LP: #2061153)
+  * debian/patches/ubuntu-authd: Do not start an unavailable authentication
+    service (LP: #2060575)
+  * d/p/ubuntu-authd: Fix selection of multi-factor auth modes (related to
+    lp:2060546)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 17 Apr 2024 07:45:42 +0200
+
+gnome-shell (46.0-0ubuntu4) noble; urgency=medium
+
+  * debian/changelog: Add missing 46.0-0ubuntu3 changelog
+  * debian/patches/ubuntu-authd: Handle various races on cancellation
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 08 Apr 2024 02:31:14 +0200
+
+gnome-shell (46.0-0ubuntu3.1) noble; urgency=medium
+
+  * debian/patces/gdm: Update gdm login notification to current API
+  * debian/patches/authd: Cleanup code and fix mechanism switch handling
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 02 Apr 2024 01:03:41 +0200
+
+gnome-shell (46.0-0ubuntu3) noble; urgency=medium
+
+  * No-change rebuild for mutter ABI changes on armhf.
+
+ -- Matthias Klose <doko@ubuntu.com>  Mon, 25 Mar 2024 19:27:16 +0100
+
+gnome-shell (46.0-0ubuntu2) noble; urgency=medium
+
+  * Add again patches that were uploaded as part of 46~beta-0ubuntu3 and that
+    got overwritten:
+    - debian/patches: Add support for ubuntu authd gdm interface
+    - debian: Build depend on terser and minify authd protocol
+    - debian/rules: Regenerate the css files before building
+    - debian/patches/ubuntu-authd: Add a gsettings to disable the gdm support
+  * debian/changelog: Update with contents of the lost changes
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 22 Mar 2024 10:16:01 +0100
+
+gnome-shell (46.0-0ubuntu1) noble; urgency=medium
+
+  * New upstream release
+  * Bump minimum mutter to 46.0
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 18 Mar 2024 17:23:29 -0400
+
+gnome-shell (46~rc-0ubuntu1) noble; urgency=medium
+
+  * New upstream release
+  * Bump minimum mutter to 46~rc
+  * Drop fingerprint patch applied in new release
+  * Refresh patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 07 Mar 2024 17:27:34 -0500
+
+gnome-shell (46~beta-0ubuntu3) noble; urgency=medium
+
+  * debian/patches: Cherry-pick upstream fixes for fingerprint authentication
+  * debian/patches: Add support for ubuntu authd - authentication daemon
+  * debian: Build depend on terser and minify authd protocol
+  * debian/rules: Regenerate the css files before building
+  * debian/patches/ubuntu-authd: Add a gsettings to disable the gdm support
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 29 Feb 2024 23:48:54 +0100
+
+gnome-shell (46~beta-0ubuntu2) noble; urgency=medium
+
+  * Build-Depend on libgtk-3-dev
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 29 Feb 2024 00:08:24 -0500
+
+gnome-shell (46~beta-0ubuntu1) noble; urgency=medium
+
+  * New upstream release
+  * Refresh patches
+  * Build with gcr4
+  * Bump minimum mutter to 46~beta
+  * Bump minimum gsettings-desktop-schemas
+  * debian/gnome-shell.install: GNOME Shell no longer provides
+    xdg-desktop-portal components
+  * Skip shell build tests for now since they are flaky
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 28 Feb 2024 23:51:36 -0500
+
 gnome-shell (45.3-2) experimental; urgency=medium
 
   * Merge packaging changes from unstable
@@ -495,6 +1000,79 @@ gnome-shell (45.3-2) experimental; urgen
 
  -- Simon McVittie <smcv@debian.org>  Mon, 15 Jan 2024 17:46:37 +0000
 
+gnome-shell (45.3-1ubuntu1) noble; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - Add debian/ubuntu-session-mods/ubuntu.json
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+  * Refresh patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 09 Jan 2024 15:28:52 -0500
+
 gnome-shell (44.9-2) unstable; urgency=high
 
   * Team upload
@@ -558,6 +1136,85 @@ gnome-shell (45.2-2) experimental; urgen
 
  -- Simon McVittie <smcv@debian.org>  Tue, 12 Dec 2023 15:09:38 +0000
 
+gnome-shell (45.2-1ubuntu1) noble; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - Add debian/ubuntu-session-mods/ubuntu.json
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+  * Refresh patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 05 Dec 2023 10:20:07 -0500
+
+gnome-shell (45.2-1) experimental; urgency=medium
+
+  * New upstream release (LP: #2045667)
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 05 Dec 2023 10:15:19 -0500
+
 gnome-shell (44.7-1) unstable; urgency=medium
 
   * Team upload
@@ -567,11 +1224,78 @@ gnome-shell (44.7-1) unstable; urgency=m
 
  -- Simon McVittie <smcv@debian.org>  Tue, 12 Dec 2023 13:30:06 +0000
 
-gnome-shell (45.2-1) experimental; urgency=medium
+gnome-shell (45.1-1ubuntu1) noble; urgency=medium
 
-  * New upstream release (LP: #2045667)
+  * Merge with Debian. Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - Add debian/ubuntu-session-mods/ubuntu.json
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+  * Drop 2 patches applied in new release
 
- -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 05 Dec 2023 10:15:19 -0500
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 08 Nov 2023 15:34:45 +0200
 
 gnome-shell (45.1-1) experimental; urgency=medium
 
@@ -584,6 +1308,86 @@ gnome-shell (45.1-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 08 Nov 2023 15:31:57 +0200
 
+gnome-shell (45.0-1ubuntu2) mantic; urgency=medium
+
+  * Cherry-pick 2 commits from future 45.1:
+    - Fix crash at shutdown (LP: #2036889)
+    - Fix indefinite inhibition of direct scanout when closing the Overview
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Tue, 03 Oct 2023 14:16:54 -0400
+
+gnome-shell (45.0-1ubuntu1) mantic; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - Add debian/ubuntu-session-mods/ubuntu.json
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 18 Sep 2023 11:22:07 -0400
+
 gnome-shell (45.0-1) experimental; urgency=medium
 
   * New upstream release
@@ -607,6 +1411,60 @@ gnome-shell (45~rc-1) experimental; urge
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Sun, 10 Sep 2023 21:19:28 -0400
 
+gnome-shell (45~rc-0ubuntu3) mantic; urgency=medium
+
+  * Restore dark mode patch accidentally dropped when packaging 45
+    (LP: #2035036)
+  * Refresh patches
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Sun, 10 Sep 2023 15:59:19 -0400
+
+gnome-shell (45~rc-0ubuntu2) mantic; urgency=medium
+
+  * Update magnifier sprite scaling patch with latest version proposed upstream
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Sat, 09 Sep 2023 11:17:39 -0400
+
+gnome-shell (45~rc-0ubuntu1) mantic; urgency=medium
+
+  * New upstream release
+  * debian/control.in: Bump minimum mutter to 45~rc
+  * Drop cherry-picked patches applied in new release
+  * Refresh patches
+  * Add patch to avoid using Tecla because it isn't ready here yet
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 06 Sep 2023 15:34:13 -0400
+
+gnome-shell (45~beta.1-0ubuntu2) mantic; urgency=medium
+
+  [ Jeremy Bícha ]
+  * Don't Build-Depend on gtk3
+  * debian/control.in: Depend on gir1.2-webkit-6.0 instead of 4.1
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/ubuntu.json: Prefer light color scheme
+  * debian/patches: Cherry-pick patches from upstream main branch
+  * debian/control: Recommends more recent version of yaru theme
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 28 Aug 2023 23:49:34 +0200
+
+gnome-shell (45~beta.1-0ubuntu1) mantic; urgency=medium
+
+  [ Jeremy Bícha ]
+  * New upstream release
+  * Build with mutter 45 beta
+  * debian/rules: Drop obsolete configure flag
+  * debian/gnome-shell.install: gnome-shell-perf-tool -> gnome-shell-test-tool
+  * Refresh patches
+  * Drop patch to use "Favorite" instead of "Pin"
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Make desktop detection to work with ESM modules
+  * debian/patches: Handle loading of multiple yaru variants again
+  * debian/patches: Cherry-pick various upstream fixes
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 18 Aug 2023 01:12:53 +0200
+
 gnome-shell (44.5-2) unstable; urgency=medium
 
   * Team upload
@@ -640,172 +1498,78 @@ gnome-shell (44.4-1) unstable; urgency=m
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 01 Sep 2023 11:13:37 -0400
 
-gnome-shell (44.3-5) unstable; urgency=medium
-
-  * Team upload
-  * Clean up obsolete gnome-shell-overrides-migration.desktop on upgrade
-    (Closes: #1050193)
-
- -- Simon McVittie <smcv@debian.org>  Mon, 21 Aug 2023 21:08:56 +0100
-
-gnome-shell (44.3-4) unstable; urgency=medium
-
-  * Team upload
-  * Merge with version 43.7-2 from unstable.
-    All changes in 43.7 were also included in 44.3.
-    - d/p/main-Leak-the-GJS-context-and-ShellGlobal.patch:
-      Drop patch, not needed in 44.x
-  * Upload to unstable (part of transition: #1043144)
+gnome-shell (44.3-1ubuntu1) mantic; urgency=medium
 
- -- Simon McVittie <smcv@debian.org>  Sun, 20 Aug 2023 22:58:15 +0100
+  * Merge with Debian (LP: #2026586). Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
 
-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) unstable; urgency=medium
-
-  * New upstream stable release 43.5
-    - Fix a regression in 43~beta involving detection of hotplugged media
-      with autorunnable content (gnome-shell!2745)
-    - Make search results fill unused space as intended (gnome-shell#5924)
-    - Improve matching of app StartupWMClass to a .desktop file, giving
-      priority to apps that were not hidden by OnlyShowIn under the current
-      desktop environment, in particular preferring gnome-system-monitor's
-      non-KDE-specific .desktop file while running GNOME (gnome-shell!2721)
-    - Fix assertion failures after a window preview is destroyed
-      (gnome-shell#5512, gnome-shell#6065)
-    - Avoid destroying labels twice, most commonly when using
-      gnome-shell-extension-dash-to-dock (gnome-shell!2739)
-    - Avoid keyboard navigation focus getting stuck on top bar buttons with
-      no associated menu (gnome-shell!2734, might help #1032319)
-    - Fix queued notifications getting into a state where they can no
-      longer be removed (gnome-shell!2736)
-    - Receive notifications of removed objects from D-Bus ObjectManager
-      instances correctly (gnome-shell!2730)
-    - Fix a cursor appearing at 0,0 in screenshots that should not
-      include it (gnome-shell!2702)
-    - Update visibility of workspaces in workspace switcher when required
-      (gnome-shell#6519)
-    - After 60 second timeout in logout/reboot/poweroff confirmation
-      dialog, do the requested action instead of leaving the Shell in a
-      broken state (gnome-shell#6506)
-    - Fix an assertion failure during shutdown (gnome-shell#6512)
-    - Fix an assertion failure if Geoclue isn't D-Bus-activatable
-      (gnome-shell!2689)
-    - Fix a regression in which the cursor would not be included in
-      screenshots since mutter 43.1 (gnome-shell!2710)
-    - Upstream CI fixes not relevant to Debian
-    - Translation update: zh_CN
-  * New upstream stable release 43.6
-    - Fix a crash when a window preview is destroyed (gnome-shell#6570)
-    - When cancelling the polkit agent prompt while using
-      gnome-remote-desktop, don't break subsequent polkit prompts
-      (gnome-shell!2761)
-    - Fix "TypeError: this._dragActor is null" warnings related to
-      drag-and-drop (gnome-shell!2770)
-    - Fix input method popup getting stuck on screen during engine changes
-      (gnome-shell#6717)
-    - Translation update: pt_BR
-  * d/control.in: Build-depend on mutter 43.5, for a newly-public utility
-    function needed by gnome-shell!2710
-  * d/patches: Drop ab translation patch, applied upstream
-
- -- Simon McVittie <smcv@debian.org>  Sat, 10 Jun 2023 21:17:13 +0100
-
-gnome-shell (43.4-1) unstable; urgency=medium
-
-  * Team upload
-  * New upstream release
-    - Fix memory leaks when the list of wireless networks is refreshed
-      (GNOME/gnome-shell!2652)
-    - Stop tracking drag-and-drop source object when destroyed
-      (part of GNOME/gnome-shell!2318)
-    - Translation update: fr
-    - All other changes were included in 43.3-2 and 43.3-3
-  * Drop patches added by 43.3-2 and 43.3-3, included in upstream 43.4
-  * d/patches: Update to gnome-43 branch commit 43.4-1-g3499d2e87
-    - Translation update: ab
-
- -- Simon McVittie <smcv@debian.org>  Mon, 10 Apr 2023 14:07:38 +0100
-
-gnome-shell (43.3-3) unstable; urgency=medium
-
-  * Team upload
-  * d/p/overview-Don-t-claim-to-be-SHOWN-when-HIDDEN-during-start.patch,
-    d/p/overview-Hide-when-failing-to-take-grab-at-end-of-startup.patch:
-    Fix regression in 43.3 which could cause the shell to become
-    unresponsive, for example if a display is plugged or unplugged during
-    the startup animation. (Closes: #1032497)
-
- -- Simon McVittie <smcv@debian.org>  Wed, 08 Mar 2023 11:09:54 +0000
-
-gnome-shell (43.3-2) unstable; urgency=medium
-
-  * Team upload
-  * d/patches: Add more translation updates from upstream:
-    de, hu, lt, pl, sl, sr
-    (upstream gnome-43 branch up to commit 43.3-13-g6f7ff15ec)
-
- -- Simon McVittie <smcv@debian.org>  Thu, 02 Mar 2023 09:14:28 +0000
-
-gnome-shell (44.3-3) experimental; urgency=medium
-
-  * Team upload
-  * d/patches: Update to upstream gnome-44 branch commit 44.3-5-g29e6369e8
-    - Translation update: es
-  * d/p/tests-Use-different-suites.patch, d/rules:
-    Skip tests that would run the whole Shell on mips* architectures.
-    GNOME Shell is reported to work correctly on mips64el machines with an
-    AMD GPU, but crashes when using llvmpipe, which appears to be an LLVM
-    issue rather than a GNOME Shell issue; one test also crashes when using
-    softpipe. (Helps: #1042980)
-
- -- Simon McVittie <smcv@debian.org>  Wed, 09 Aug 2023 11:18:11 +0100
-
-gnome-shell (44.3-2) experimental; urgency=medium
-
-  * Team upload
-  * Update to upstream gnome-44 branch commit 44.3-4-g1635c3713
-    - Fix ELF header parsing on mips64el and riscv64 (Helps: #1042980)
-    - Translation updates: be, cs, es
-  * d/rules: Increate arbitrary test timeouts.
-    The default test timeout is 30 seconds, but the perf-* tests take more
-    like 45 seconds on mips64el. (Helps: #1042980)
-  * d/rules: Run tests with softpipe instead of llvmpipe on mips family.
-    This works around apparent LLVM/Mesa issues, similar to #993550
-    originally reported against gtk4. (Helps: #1042980)
-
- -- Simon McVittie <smcv@debian.org>  Sun, 06 Aug 2023 17:10:40 +0100
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 07 Jul 2023 09:33:58 -0400
 
 gnome-shell (44.3-1) experimental; urgency=medium
 
@@ -813,6 +1577,79 @@ gnome-shell (44.3-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 07 Jul 2023 09:32:03 -0400
 
+gnome-shell (44.2-1ubuntu1) mantic; urgency=medium
+
+  * Merge with Debian (LP: #2022961). Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 05 Jun 2023 12:59:17 -0400
+
 gnome-shell (44.2-1) experimental; urgency=medium
 
   * New upstream release
@@ -820,6 +1657,80 @@ gnome-shell (44.2-1) experimental; urgen
 
  -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 05 Jun 2023 12:58:02 -0400
 
+gnome-shell (44.1-1ubuntu1) mantic; urgency=medium
+
+  * Merge with Debian (LP: #2020277, LP: #1968383, LP: #1968390, LP: #2016007)
+  * Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+    - ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Sun, 21 May 2023 16:52:00 -0400
+
 gnome-shell (44.1-1) experimental; urgency=medium
 
   [ Jarred Wilson ]
@@ -833,6 +1744,98 @@ gnome-shell (44.1-1) experimental; urgen
 
  -- Jarred Wilson <jarred.wilson@canonical.com>  Sun, 21 May 2023 12:50:23 -0400
 
+gnome-shell (44.0-2ubuntu3) lunar; urgency=medium
+
+  * Cherry-pick fix for installing extensions (LP: #2013073)
+  * Add proposed patch to fix running gnome-shell on RISC-V (LP: #2012068)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 31 Mar 2023 10:18:23 -0400
+
+gnome-shell (44.0-2ubuntu2) lunar; urgency=medium
+
+  * debian/patches: Cherry-pick more upstream fixes from stable branch
+  * debian/patches: Give more priority to desktop files matching startup-wm
+    class (LP: #2012645)
+  * debian/patches: Do not move snap apps to GNOME apps scope (LP: #2011806)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 27 Mar 2023 21:19:20 +0200
+
+gnome-shell (44.0-2ubuntu1) lunar; urgency=medium
+
+  * Merge with debian, containing a new upstream release
+  * debian/patches: Reload shell theme on color scheme changes
+  * debian/patches: Do not crash on st settings updates (LP: #2012021)
+  * debian/patches: Refresh
+  * Remaining changes with debian:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 21 Mar 2023 05:41:22 +0100
+
 gnome-shell (44.0-2) experimental; urgency=medium
 
   * debian/control: Bump dependency on mutter 44
@@ -848,6 +1851,92 @@ gnome-shell (44.0-1) experimental; urgen
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 21 Mar 2023 05:20:26 +0100
 
+gnome-shell (44~rc-1ubuntu2) lunar; urgency=medium
+
+  * d/ubuntu-session-mods/ubuntu: Add Tiling Assistant as built-in extension
+    This will ensure that if the extension is installed, it can be enabled from
+    settings, without having to enable all the user extensions.
+    It doesn't imply a shell dependency on it.
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 16 Mar 2023 00:17:02 +0100
+
+gnome-shell (44~rc-1ubuntu1) lunar; urgency=medium
+
+  [ Jeremy Bicha ]
+  * Merge with Debian. Remaining differences:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+  * Don't ignore test failures
+
+  [ Marco Trevisan (Treviño) ]
+  * Refresh patches
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 14 Mar 2023 13:35:25 -0400
+
 gnome-shell (44~rc-1) experimental; urgency=medium
 
   [ Jeremy Bicha ]
@@ -864,6 +1953,88 @@ gnome-shell (44~rc-1) experimental; urge
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 08 Mar 2023 02:59:16 +0100
 
+gnome-shell (44~beta-1ubuntu1) lunar; urgency=medium
+
+  [ Jeremy Bicha ]
+  * Merge with Debian, containing new upstream release:
+    - Do not deference symlinks when uninstalling an extension (LP: #1998529)
+  * Remaining changes with debian:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+    * Rebase patches
+    * Drop main-Avoid-meta-finalize.patch: maybe not needed any more
+    * Fix typo in patch
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Ensure GSETTINGS_SCHEMA_DIR is honored in tests
+  * debian/patches/ubuntu: Fix description typos to mute lintian
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 24 Feb 2023 06:29:15 +0100
+
 gnome-shell (44~beta-1) experimental; urgency=medium
 
   * New upstream release
@@ -927,6 +2098,80 @@ gnome-shell (43.2-2) unstable; urgency=m
 
  -- Simon McVittie <smcv@debian.org>  Sun, 29 Jan 2023 15:26:02 +0000
 
+gnome-shell (43.2-1ubuntu1) lunar; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 17 Jan 2023 11:13:09 -0500
+
 gnome-shell (43.2-1) unstable; urgency=medium
 
   * New upstream release (Closes: #1028972)
@@ -935,6 +2180,81 @@ gnome-shell (43.2-1) unstable; urgency=m
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 17 Jan 2023 11:09:16 -0500
 
+gnome-shell (43.1-2ubuntu1) lunar; urgency=medium
+
+  * Merge with Debian (LP: #1995134, LP: #1997481). Remaining changes:
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + ubuntu-wallpapers
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Move some Recommends to Suggests:
+      + chrome-gnome-shell
+      + gnome-backgrounds
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+  * Drop gst_init_check patch: fixed a different way in 43.1
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 19 Sep 2022 14:50:17 -0400
+
 gnome-shell (43.1-2) unstable; urgency=medium
 
   * Team upload
@@ -975,6 +2295,84 @@ gnome-shell (43.0-2) unstable; urgency=m
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 27 Sep 2022 19:10:41 -0400
 
+gnome-shell (43.0-1ubuntu2) kinetic; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Avoid deadlocking in gst_init_check() on startup (LP: #1988488)
+gnome-shell (43.0-1ubuntu1) kinetic; urgency=medium
+
+  * Merge with Debian, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 19 Sep 2022 14:50:17 -0400
+
 gnome-shell (43.0-1) experimental; urgency=medium
 
   * Team upload
@@ -986,6 +2384,87 @@ gnome-shell (43.0-1) experimental; urgen
 
  -- Simon McVittie <smcv@debian.org>  Sun, 18 Sep 2022 20:02:40 +0100
 
+gnome-shell (43~rc-1ubuntu2) kinetic; urgency=medium
+
+  * Suggest instead of depend on gnome-backgrounds
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 08 Sep 2022 21:27:52 -0400
+
+gnome-shell (43~rc-1ubuntu1) kinetic; urgency=medium
+
+  * Merge with Debian, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+  * Drop obsolete maintainer scripts handling Ubuntu theme migration
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 08 Sep 2022 17:03:28 -0400
+
 gnome-shell (43~rc-1) experimental; urgency=medium
 
   * New upstream release
@@ -1000,6 +2479,100 @@ gnome-shell (43~beta-2) experimental; ur
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 05 Sep 2022 17:40:40 -0400
 
+gnome-shell (43~beta-1ubuntu2) kinetic; urgency=medium
+
+  * Build with libsoup3
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 25 Aug 2022 16:50:58 -0400
+
+gnome-shell (43~beta-1ubuntu1) kinetic; urgency=medium
+
+  [ Jeremy Bicha ]
+  * Merge with Debian, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Break gnome-shell-extension-desktop-icons (<< 68ubuntu20.10.1)
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch,
+    - ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+  * debian/patches: Refresh
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches:
+    - Update GDM fingerprint init patch
+    - Update debug improvements patches
+    - Update search provider patch to support XUbuntuCancel
+    - Refresh
+    - Move ubuntu settings handling to St.Settings
+    - Add support for toggling dark mode with Yaru variants
+  * debian/control: Bump recommends on newer Yaru theme
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 25 Aug 2022 01:58:50 +0200
+
 gnome-shell (43~beta-1) experimental; urgency=medium
 
   * New upstream release
@@ -1009,6 +2582,82 @@ gnome-shell (43~beta-1) experimental; ur
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Sun, 21 Aug 2022 10:53:56 -0400
 
+gnome-shell (42.4-1ubuntu1) kinetic; urgency=medium
+
+  * Merge with Debian, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Break gnome-shell-extension-desktop-icons (<< 68ubuntu20.10.1)
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+  * debian/patches: Refresh
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 11 Aug 2022 17:31:32 -0400
+
 gnome-shell (42.4-2) unstable; urgency=medium
 
   * Build with libsoup3
@@ -1024,6 +2673,81 @@ gnome-shell (42.4-1) unstable; urgency=m
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 11 Aug 2022 23:14:29 +0200
 
+gnome-shell (42.3.1-2ubuntu1) kinetic; urgency=medium
+
+  * Merge with Debian (LP: #1981937) Remaining changes with Debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Break gnome-shell-extension-desktop-icons (<< 68ubuntu20.10.1)
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 18 Jul 2022 07:39:38 +0200
+
 gnome-shell (42.3.1-2) unstable; urgency=medium
 
   [ Marco Trevisan (Treviño) ]
@@ -1036,6 +2760,82 @@ gnome-shell (42.3.1-2) unstable; urgency
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 18 Jul 2022 07:36:31 +0200
 
+gnome-shell (42.3.1-1ubuntu1) kinetic; urgency=medium
+
+  * Merge with Debian (LP: #1981937) Remaining changes with Debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+      + gnome-remote-desktop to provide remote desktop support by default
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Break gnome-shell-extension-desktop-icons (<< 68ubuntu20.10.1)
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - Update debian/gbp.conf with Ubuntu settings
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - debian/patches: Do not hang & crash if fingerprint service fails to start
+      (LP: #1962566)
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch
+    - u/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+    - ubuntu/resolve_alternate_theme_path.patch
+    - ubuntu/secure_mode_extension.patch
+    - ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+    - ubuntu/configure_login_screen.patch
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - debian/patches: Compute system background color from theme (LP: #1965727)
+    - ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+      (LP: #1965727)
+    - debian/patches: Ensure St.Entry's `selected-color` CSS property is
+      honored (LP: #1878998)
+    - ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+    - d/p/use-favorites-strings: Only apply this to ubuntu session
+    - debian/patches: Support configuring icons resource to use for mode
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - u/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+      (LP: #1964458)
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - ubuntu/Revert-dash-Use-pin-instead-of-favorites
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Sun, 17 Jul 2022 22:54:29 +0200
+
 gnome-shell (42.3.1-1) unstable; urgency=medium
 
   * New upstream release
@@ -1100,6 +2900,124 @@ gnome-shell (42.0-3) unstable; urgency=m
 
  -- Simon McVittie <smcv@debian.org>  Wed, 13 Apr 2022 16:47:53 +0100
 
+gnome-shell (42.2-0ubuntu2) kinetic; urgency=medium
+
+  * Drop two patches that support the old gnome-control-center
+    desktop file (LP: #1980245):
+    - js-Support-legacy-GNOME-Control-Center-now-Settings-dbus-.patch
+    - Revert-ui-Rename-gnome-control-center-to-org.gnome.Settin.patch
+
+ -- Nathan Pratta Teodosio <nathan.teodosio@canonical.com>  Thu, 30 Jun 2022 08:23:15 -0300
+
+gnome-shell (42.2-0ubuntu1) kinetic; urgency=medium
+
+  * New upstream release (LP: #1977776)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 06 Jun 2022 20:30:49 -0400
+
+gnome-shell (42.1-0ubuntu1) kinetic; urgency=medium
+
+  * New upstream release (LP: #1973373, LP: #1968911)
+  * Drop patches applied in new release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 13 May 2022 16:32:49 -0400
+
+gnome-shell (42.0-2ubuntu2) kinetic; urgency=medium
+
+  * Use libgweather4 instead of old libgweather
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 29 Apr 2022 13:31:08 -0400
+
+gnome-shell (42.0-2ubuntu1) jammy; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Cherry-pick upstream fixes targetting 42.1
+  * debian/patches: Compute system ackground color from theme (LP: #1965727)
+  * ubuntu/configure-login-screen.patch: Use bg color for initial system bg
+    (LP: #1965727)
+  * debian/patches: Ensure St.Entry's `selected-color` CSS property is honored
+    (LP: #1878998)
+  * ubuntu/support-loading-Yaru-variants: Handle dark/light variants better
+  * d/p/main-Avoid-meta-finalize: Leak gjs context only on ubiquity sessions
+    (LP: #1964458)
+  * d/p/use-favorites-strings: Only apply this to ubuntu session
+  * debian/patches: Do not hang and crash if fingerprint service fails to start
+    (LP: #1962566)
+  * debian: Use gnomebluetooth-3.0 as dependency and revert patches disabling it
+    (LP: #1738838, #1968364, #1964600)
+
+  [ Jeremy Bicha ]
+  * Use libgweather4 instead of old libgweather (LP: #1964600)
+  * Add patch to work around meson issue (Debian 1008189)
+  * releasing package gnome-shell version 42.0-2
+  * debian/control.in: Recommend power-profiles-daemon for power mode feature
+
+  [ Daniel van Vugt ]
+  * main-Avoid-meta-finalize.patch: Leak GJS to work around LP: #1964458
+
+  [ Gunnar Hjalmarsson ]
+  * Revert "dash: Use pin instead of favorites"
+
+  [ Jeremy Bicha ]
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+      + gnome-remote-desktop to provide remote desktop support by default
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - debian/patches: Support configuring icons resource to use for mode
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - Revert-st-Apply-css-foreground-color-to-text-as-a-PangoAt.patch:
+      + Ensure selected-text foreground color is preserved
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 13 Apr 2022 03:30:47 +0200
+
 gnome-shell (42.0-2) unstable; urgency=medium
 
   [ Marco Trevisan (Treviño) ]
@@ -1116,6 +3034,79 @@ gnome-shell (42.0-2) unstable; urgency=m
 
  -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 28 Mar 2022 11:14:05 -0400
 
+gnome-shell (42.0-1ubuntu1) jammy; urgency=medium
+
+  [ Jeremy Bicha ]
+  * Merge with Debian.
+  * Add patches to revert switch to new gnome-bluetooth
+  * Revert "debian/control.in: Switch to gnome-bluetooth3"
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Refresh
+  * debian/patches: Fix monitor switching configuration (LP: #1964496)
+  * debian/patches: Overview, remove desktop fade logic (LP: #1965072)
+  * debian/patches: Cherry-pick various upstream fixes targeting 42.1
+  * js: Support legacy GNOME Control Center (now Settings) dbus name
+  * shellDBus: Actually make ScreenTransitionAsync async
+
+  [ Jeremy Bicha ]
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+      + gnome-remote-desktop to provide remote desktop support by default
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - debian/patches: Support configuring icons resource to use for mode
+    - debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 28 Mar 2022 16:31:13 +0200
+
 gnome-shell (42.0-1) experimental; urgency=medium
 
   * New upstream release
@@ -1134,6 +3125,83 @@ gnome-shell (42~rc-1) experimental; urge
 
  -- Jeremy Bicha <jeremy.bicha@canonical.com>  Wed, 09 Mar 2022 13:18:51 -0500
 
+gnome-shell (42~beta-1ubuntu3) jammy; urgency=medium
+
+  * debian/patches: Adapt the theme-switcher code to keep gtk and shell in sync
+    We only used dark theme for the shell before while now it will match the
+    rest of the system.
+  * debian/patch: Fix a crash on shadow computation (LP: #1947315)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 18 Mar 2022 05:38:36 +0100
+
+gnome-shell (42~beta-1ubuntu2) jammy; urgency=medium
+
+  * Add patch to restore Settings item in system menu, because we're
+    using the older gnome-control-center.desktop) (LP: #1964136)
+
+ -- Jeremy Bicha <jeremy.bicha@canonical.com>  Thu, 10 Mar 2022 09:01:53 -0500
+
+gnome-shell (42~beta-1ubuntu1) jammy; urgency=medium
+
+  * Merge with debian
+  * debian/patches: Refresh
+  * debian/patches: Support configuring icons resource to use for mode
+  * debian/ubuntu-session-mods/ubuntu.json: Use Yaru's gnome-shell icons
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+      + gnome-remote-desktop to provide remote desktop support by default
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch:
+      + Support loading iconsResourceName from session file
+    - ubuntu/main-Support-loading-multiple-Yaru-theme-variants.patch:
+      + Support loading Yaru theme variants (for accent color)
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 25 Feb 2022 01:59:17 +0100
+
 gnome-shell (42~beta-1) experimental; urgency=medium
 
   * New upstream release:
@@ -1170,6 +3238,66 @@ gnome-shell (41.3-2) unstable; urgency=m
 
  -- Simon McVittie <smcv@debian.org>  Tue, 15 Feb 2022 13:11:19 +0000
 
+gnome-shell (41.3-1ubuntu1) jammy; urgency=medium
+
+  * Merge with debian, containing various upstream fixes:
+    - Window size is wrong after exiting full-screen (LP: #1947467)
+  * debian/patches: Refresh to support GNOME 41 codebase
+  * debian/patches: Remove patch to make overview starting optional
+    This can be handled fully by the extensions that need it, so let's
+    handle this in dash-to-dock instead that in upstream code
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+      + gnome-remote-desktop to provide remote desktop support by default
+    - Moved some Recommends to Suggests:
+      + chrome-gnome-shell
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+  * debian/patches: Remove patch to make overview starting optional
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 27 Jan 2022 04:09:39 +0100
+
 gnome-shell (41.3-1) unstable; urgency=medium
 
   * New upstream release:
@@ -1230,6 +3358,73 @@ gnome-shell (41.0-1) experimental; urgen
 
  -- Simon McVittie <smcv@debian.org>  Sun, 10 Oct 2021 21:26:33 +0100
 
+gnome-shell (40.5-1ubuntu2) impish; urgency=medium
+
+  [ Daniel van Vugt ]
+  * debian/patches: Avoid full meta context finalization.
+    To workaround (prevent) shutdown crashes (LP: #1936826, #1942031, #1942121,
+    LP: #1943406, #1944054, #1945010, #1945116, #1944388)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 05 Oct 2021 04:52:11 +0200
+
+gnome-shell (40.5-1ubuntu1) impish; urgency=medium
+
+  * Merge with debian
+  * debian/patches: Refresh and update to current upstream
+  * debian/patches: Move ubuntu-only patches into the ubuntu namespace
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - ubuntu/layout-Make-starting-in-the-overview-optional.patch:
+      + Makes dock replace overview easier
+    - ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch:
+      + Ensure windows don't get maximized under the panels / dock
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+  [ Jeremy Bicha ]
+  * Revert "Skip ppc64el tests to fix FTBFS"
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Sat, 25 Sep 2021 13:07:30 +0200
+
 gnome-shell (40.5-1) unstable; urgency=medium
 
   * New upstream release:
@@ -1339,6 +3534,66 @@ gnome-shell (40.4-1) experimental; urgen
 
  -- Simon McVittie <smcv@debian.org>  Tue, 31 Aug 2021 10:03:33 +0100
 
+gnome-shell (40.2-1ubuntu6) impish; urgency=medium
+
+  * d/rules:
+    - Drop dh_translations workaround. gettext used by LP has been
+      updated (LP: #1756547).
+    - Fix Lintian warning about DEB_HOST_ARCH assignment
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Wed, 01 Sep 2021 15:44:38 +0200
+
+gnome-shell (40.2-1ubuntu5) impish; urgency=medium
+
+  * Fix translation issues (LP: #1941954):
+    - Really fix translation import problem as reported in LP: #1756547
+    - Add libgtk-3-bin to Build-Depends for dh_translations
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Sat, 28 Aug 2021 20:13:36 +0200
+
+gnome-shell (40.2-1ubuntu4) impish; urgency=medium
+
+  * debian/rules:
+    - Skip tests when building for ppc64el to fix FTBFS (LP: #1941792)
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Fri, 27 Aug 2021 16:20:11 +0200
+
+gnome-shell (40.2-1ubuntu3) impish; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Add layout-Make-starting-in-the-overview-optional.patch:
+    - So that extensions like Ubuntu Dock have the option of preventing
+      the shell starting in the overview. (LP: #1940925)
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Thu, 26 Aug 2021 19:47:55 +0200
+
+gnome-shell (40.2-1ubuntu2) impish; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Add workspace-Remove-skip-taskbar-windows-while-the-over.patch to stop the
+    desktop icons window appearing like an app window in the overview on
+    startup (LP: #1936643)
+
+ -- Iain Lane <iain.lane@canonical.com>  Thu, 12 Aug 2021 09:41:01 +0100
+
+gnome-shell (40.2-1ubuntu1) impish; urgency=medium
+
+  * Merge with debian
+  * debian/control:
+    - Bump Breaks on yaru-theme-gnome-shell 21.10.1
+    - Stop suggesting removed package gnome-themes-standard-data
+    - Recommends newer version of yaru theme
+  * debian/gnome-shell-common.pre{rm,inst}:
+    - Remove deprecated update-alternatives actions
+  * debian/gnome-shell-common.alternatives:
+    - Use dh_installalternatives
+    - Rename gdm3-theme to gdm-theme
+  * debian/source_gnome-shell.py: Support gdm name only
+  * debian/control: Add missing dependency on gir1.2-gtk-4.0
+  * d/p/ubuntu/gdm_alternatives.patch: Use gdm basename for custom stylesheets
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 07 Jul 2021 09:30:42 +0200
+
 gnome-shell (40.2-1) experimental; urgency=medium
 
   * New upstream release:
@@ -1414,6 +3669,96 @@ gnome-shell (3.38.4-2) experimental; urg
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 26 May 2021 13:37:14 +0200
 
+gnome-shell (3.38.4-1ubuntu3) impish; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Add workspacesView-Only-use-valid-allocations-when-avail.patch.
+    To stop bogus values being used in _updateWorkspacesActualGeometry(),
+    which would cause the overview to fail to toggle. (LP: #1922353)
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Refresh
+  * debian/patches: Cherry-pick upstream calendar-server crash fix
+    (LP: #1915929)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 31 May 2021 18:51:34 +0200
+
+gnome-shell (3.38.4-1ubuntu2) hirsute; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Add layout-Try-to-allocate-before-getting-size-of-tracke.patch.
+    To fix LP: #1919979 and also to re-fix LP: #1917939
+    (LP: #1919979, #1917939)
+  * Drop layout-Allow-updating-struts-in-Overview.patch.
+    Because it caused a regression and doesn't seem to have been necessary
+    anyway. (LP: #1922772)
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches: Fix suggestions box actor population in non-latin languages
+    (LP: #1914230)
+  * debian/patches: Enable to use Escape to cancel auth requests (with limits)
+    (LP: #1921929)
+  * debian/patches: Refresh
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 15 Apr 2021 06:20:27 +0200
+
+gnome-shell (3.38.4-1ubuntu1) hirsute; urgency=medium
+
+  * Merge with debian, containing a new upstream release
+  * debian/control: (Build-)Depends on mutter 3.38.4
+  * debian/patches:
+    - Refresh
+    - Remove input-thread related patches
+    - Fix handling of OSK codes in keyboard (LP: #1918738)
+    - Ensure Cancel button uses proper arguments (LP: #1918666)
+    - Allow updating struts in Overview (LP: #1917939)
+    - Configure Login Scree: cleanup code to match shell's JS
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 26 Mar 2021 07:25:17 +0100
+
 gnome-shell (3.38.4-1) unstable; urgency=medium
 
   * Team upload
@@ -1441,6 +3786,27 @@ gnome-shell (3.38.3-4) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Tue, 09 Mar 2021 20:35:00 +0000
 
+gnome-shell (3.38.3-3ubuntu2) hirsute; urgency=medium
+
+  [ Didier Roche ]
+  [ Jean-Baptiste Lallement ]
+  * debian/patches/ubuntu/configure_login_screen.patch:
+    Make GDM background configurable with gsettings (LP: #1918613)
+
+ -- Didier Roche <didrocks@ubuntu.com>  Thu, 25 Mar 2021 10:44:17 +0100
+
+gnome-shell (3.38.3-3ubuntu1) hirsute; urgency=medium
+
+  * debian/patches: Switch to the new API to get actor from inputdevice
+    (LP: #1690719)
+  * debian/control: Bump dependency on mutter with input thread support
+  * debian/patches: Update to MetaCursorTracker API change
+  * debian/patches: Do not forward device IDs
+  * debian/ubuntu-session-mods/ubuntu.json: Use desktop-icons-ng extension by
+    default (LP: #1916511)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 26 Feb 2021 00:51:19 +0100
+
 gnome-shell (3.38.3-3) unstable; urgency=medium
 
   * Team upload
@@ -1455,6 +3821,29 @@ gnome-shell (3.38.3-3) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Tue, 23 Feb 2021 09:28:02 +0000
 
+gnome-shell (3.38.3-2ubuntu2) hirsute; urgency=medium
+
+  * d/p/gdm-Don-t-try-to-retry-authenticating-when-the-service-is.patch:
+    - gdm: Don't try to retry authenticating when the service is unavailable
+      (LP: #1915570)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 16 Feb 2021 05:16:49 +0100
+
+gnome-shell (3.38.3-2ubuntu1) hirsute; urgency=medium
+
+  * Merge with debian, containing new upstream release (LP: #1915089)
+  * debian/patches: Correctly handle login cancellation and failures.
+    Ensure the GDM workers are terminated before restarting a new auth
+    request, allowing fingerprint authentication to be restarted.
+    (LP: #1915066)
+  * debian/patches: Show errors on fingerprint failures and limit retries.
+    (LP: #1865838)
+  * debian/control: BD on gstreamer and pipewire, depend on gstreamer1.0-pipewire.
+    Enable building with pipewire now that is in main and add runtime
+    dependency on gstreamer-pipewire to gnome-shell (LP: #1901391).
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 09 Feb 2021 05:16:10 +0100
+
 gnome-shell (3.38.3-2) unstable; urgency=medium
 
   * Team upload
@@ -1474,6 +3863,63 @@ gnome-shell (3.38.3-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Thu, 14 Jan 2021 20:13:26 +0000
 
+gnome-shell (3.38.2-1ubuntu1) hirsute; urgency=medium
+
+  * Merge with debian, containing new upstream release (LP: #1908161):
+    - Fixed crash in meta_window_actor_thaw (LP: #1897765)
+    - Fixed crash in st_bin_destroy (LP: #1898005)
+  * debian/patches:
+    - Refreshed
+    - Revert-appDisplay-baseAppView-Cleanup-animate.patch:
+      + Revert a 3.38.2 change that proved to cause regressions
+    - screenshot-Grab-screenshot-during-paint-on-X11.patch:
+      + Correctly take screenshots for fullscreen games (LP: #1908164)
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 15 Dec 2020 02:42:26 +0100
+
 gnome-shell (3.38.2-1) unstable; urgency=medium
 
   * Team upload
@@ -1525,6 +3971,63 @@ gnome-shell (3.38.1-2) unstable; urgency
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 04 Nov 2020 17:26:00 +0100
 
+gnome-shell (3.38.1-1ubuntu2) hirsute; urgency=medium
+
+  * debian/patches/modemManager_Add-property-getters.patch:
+    - Cherry picked upstream commit to fix issue with missing "Mobile
+      Broadband" item in the system menu (LP: #1902829).
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Wed, 04 Nov 2020 16:59:17 +0100
+
+gnome-shell (3.38.1-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream release, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+  * debian/patches: Refresh
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Sun, 11 Oct 2020 02:43:11 +0200
+
 gnome-shell (3.38.1-1) unstable; urgency=medium
 
   * New upstream release
@@ -1542,6 +4045,66 @@ gnome-shell (3.38.0-2) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Fri, 25 Sep 2020 10:25:25 +0100
 
+gnome-shell (3.38.0-1ubuntu2) groovy; urgency=medium
+
+  * debian/control:
+    - Revert "Require a known-working version of Pipewire",
+      Revert "Depend on gstreamer1.0-pipewire",
+      Revert "Enable screencasting support now that pipewire 0.3 is in
+      experimental":
+      + We don't support pipewire in yet
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 21 Sep 2020 18:40:51 +0200
+
+gnome-shell (3.38.0-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream release, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+  * debian/patches: Refresh
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 16 Sep 2020 15:37:47 +0200
+
 gnome-shell (3.38.0-1) experimental; urgency=medium
 
   * New upstream release
@@ -1582,6 +4145,61 @@ gnome-shell (3.37.92-1) experimental; ur
 
  -- Simon McVittie <smcv@debian.org>  Mon, 07 Sep 2020 20:01:41 +0100
 
+gnome-shell (3.37.91-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream release, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+  * debian/patches: Refreshed
+  * debian/control:
+    - Set breaks on upcoming core extensions. This update is going to break
+      them, so we need extensions updates before migra
+    - Set breaks on upcoming yaru theme.
+      There are not big deals using the current yaru, but better to wait for a
+      suynced one.
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 27 Aug 2020 23:14:01 +0200
+
 gnome-shell (3.37.91-1) experimental; urgency=medium
 
   * New upstream release:
@@ -1634,6 +4252,63 @@ gnome-shell (3.36.5-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Wed, 12 Aug 2020 21:28:22 +0100
 
+gnome-shell (3.36.4-1ubuntu2~build1) groovy; urgency=medium
+
+  * Rebuild with the new libedataserver soname
+
+ -- Ken VanDine <ken.vandine@canonical.com>  Tue, 18 Aug 2020 12:59:03 -0400
+
+gnome-shell (3.36.4-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream stable release, remaining changes:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+  * d/p/ubuntu/gdm_alternatives.patch,
+    d/p/ubuntu/secure_mode_extension.patch:
+    - Refreshed as pr upstream changes
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 20 Jul 2020 18:18:57 +0200
+
 gnome-shell (3.36.4-1) unstable; urgency=medium
 
   * New upstream stable release (LP: #1888060)
@@ -1662,6 +4337,67 @@ gnome-shell (3.36.4-1) unstable; urgency
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 20 Jul 2020 15:17:47 +0200
 
+gnome-shell (3.36.3-1ubuntu2) groovy; urgency=medium
+
+  * d/p/shell-mime-sniffer-Ignore-invalid-file-content-type.patch:
+    - gnome-shell-sniffer, don't crash when opening files with invalid content
+      type (LP: #1865300)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 05 Jun 2020 20:14:47 +0200
+
+gnome-shell (3.36.3-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream release:
+    - Fix app icon aspect ratio in the panel (LP: #1872026)
+    - Ensure that Do not disturb is persistent across sessions (LP: #1873692)
+    - Fix Theme node crashes (LP: #1877774, LP: #1877760)
+  * debian/patches: Refresh
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 04 Jun 2020 22:19:02 +0200
+
 gnome-shell (3.36.3-1) unstable; urgency=medium
 
   * New upstream release (LP: #1881973)
@@ -1695,6 +4431,56 @@ gnome-shell (3.36.2+64+ge74e691d8-1) exp
 
  -- Simon McVittie <smcv@debian.org>  Sat, 30 May 2020 15:48:25 +0100
 
+gnome-shell (3.36.2-1ubuntu1) groovy; urgency=medium
+
+  * Merge with debian, including new upstream release (LP: #1877212)
+  * debian/patches: Refresh
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 07 May 2020 03:57:06 +0200
+
 gnome-shell (3.36.2-1) unstable; urgency=medium
 
   * Team upload
@@ -1745,15 +4531,35 @@ gnome-shell (3.36.1+git20200417-1) unsta
 
   [ Jeremy Bicha ]
   * Have gnome-shell-extension-prefs recommend chrome-gnome-shell
+    (LP: #1866841)
     - gnome-shell-extension-prefs recommends visiting
       extensions.gnome.org to install extensions but that
       requires chrome-gnome-shell to be installed
     - GNOME Software 3.36 no longer offers installing
       GNOME Shell extensions
-    (LP: #1866841)
 
  -- Simon McVittie <smcv@debian.org>  Tue, 21 Apr 2020 13:46:21 +0100
 
+gnome-shell (3.36.1-5ubuntu2) focal; urgency=medium
+
+  * Have gnome-shell-extension-prefs recommend chrome-gnome-shell
+    (LP: #1866841)
+    - gnome-shell-extension-prefs recommends visiting
+      extensions.gnome.org to install extensions but that
+      requires chrome-gnome-shell to be installed
+    - GNOME Software 3.36 no longer offers installing
+      GNOME Shell extensions
+
+ -- Jeremy Bicha <jbicha@debian.org>  Tue, 21 Apr 2020 07:48:41 -0400
+
+gnome-shell (3.36.1-5ubuntu1) focal; urgency=medium
+
+  * Merge with debian
+  * debian/patches: appDisplay: Look up directory- instead of category
+    translations (LP: #1872434)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 15 Apr 2020 03:00:46 +0200
+
 gnome-shell (3.36.1-5) unstable; urgency=medium
 
   * Team upload
@@ -1774,6 +4580,70 @@ gnome-shell (3.34.4-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Tue, 25 Feb 2020 16:26:07 +0000
 
+gnome-shell (3.36.1-4ubuntu1) focal; urgency=medium
+
+  * Merge with debian, with new upstream releases and cherry-picked fixes:
+    - Lockscreen password field is stretched on enter (LP: #1869916)
+    - Downscaled lockscreen background with fractional scaling (LP: #1866851)
+    - Volume keys doesn't always produce a sound (LP: #1870958)
+    - Shell crashes when using show-apps button in dock (LP: #1868659)
+    - App folders improvements
+    - Don't load app infos in the main thread
+  * debian/patches:
+    - Refreshed
+  * d/p/ubuntu/desktop_detect.patch:
+    - No need to include desktop.js into the prefs gresources anymore
+  * Remaining changes with debian:
+    - Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    - Add some Recommends:
+      + ubuntu-session (| gnome-session) to have the ubuntu session available
+      + xserver-xorg-legacy
+      + yaru-theme-gnome-shell for the default ubuntu theming
+    - Update debian/gbp.conf with Ubuntu settings
+    - gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    - ubuntu/desktop_detect.patch:
+      + add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    - ubuntu/smarter_alt_tab.patch:
+      + quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    - ubuntu/lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - ubuntu/lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - ubuntu/background_login.patch
+      + Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    - ubuntu/gdm_alternatives.patch
+      + Add support for GDM3 theme alternatives
+    - optional-hot-corner.patch
+      + enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    - main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      + Improve debug JS tracing for crash reports
+    - st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      + Fix crash on theme changes
+    - ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      + stop searches when requested from UI
+    - magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      + Show monitor scaled cursor when magnifier is enabled
+    - Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+  [ Daniel van Vugt ]
+  * d/p/ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch:
+     Stop the Ubuntu logo from disappearing and fading back in on the login
+     screen (LP: #1867133)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 09 Apr 2020 14:57:47 +0200
+
 gnome-shell (3.36.1-4) experimental; urgency=medium
 
   * Team upload
@@ -1789,7 +4659,7 @@ gnome-shell (3.36.1-4) experimental; urg
 gnome-shell (3.36.1-3) experimental; urgency=medium
 
   * Team upload
-  * Require an ibus version where #955769 (LP#1869641) has been fixed
+  * Require an ibus version where #955769 (LP: #1869641) has been fixed
   * d/patches: Update from upstream gnome-3-36 branch up to commit
     3.36.1-31-ga6783692c
 
@@ -1827,6 +4697,88 @@ gnome-shell (3.36.1-1) experimental; urg
 
  -- Simon McVittie <smcv@debian.org>  Fri, 03 Apr 2020 14:23:59 +0100
 
+gnome-shell (3.36.0-2ubuntu2) focal; urgency=medium
+
+  * d/p/overview-Fade-in-out-over-the-desktop-instead-of-replacin.patch:
+    - Drop patch to fade in/out the desktop instead of replacing it, it causes
+      some bugs. (LP: #1868896, #1868911)
+  * d/p/keyboard-Don-t-include-keyboard-devices-when-updating-las.patch,
+    d/p/keyboard-Hide-keyboardBox-after-destroying-the-keyboard.patch,
+    d/p/layout-Use-translation_y-of-0-to-hide-keyboard.patch:
+    - Fix OSK leaves dask mask after being hidden (LP: #1845623)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 30 Mar 2020 17:49:05 +0100
+
+gnome-shell (3.36.0-2ubuntu1) focal; urgency=medium
+
+  * Merge with Debian. Ubuntu bugs resolved since 3.35.91-1ubuntu2:
+    - gnome-shell crashed when enable-animations was false (LP: #1866044)
+    - Overview animations were not reliably smooth (LP: #1725180)
+    - Higher than necessary CPU usage on mouse movement (LP: #1848951)
+    - Extension's preferences did not load (LP: #1866146)
+  * debian/control:
+    - Breaks yaru (<< 20.04.3~)
+    - Breaks gnome-session (<< 3.35.3-1ubuntu4~) as per gdm yaru gresources
+    - Breaks gnome-shell-extension-ubuntu-dock (<< 67ubuntu20.04.3)
+    - Update Vcs-* to point to salsa ubuntu branches
+  * ubuntu.json: Use Yaru gresource file for theming in ubuntu mode
+  * debian/patches: Refresh
+  * d/p/overview-Fade-in-out-over-the-desktop-instead-of-replacin.patch:
+    - Fade in/out the desktop instead of replacing it (LP: #1847712)
+  * d/p/ubuntu/gdm_alternatives.patch:
+    - Set the default theme resource for gdm session to 'gdm3-theme.gresource'
+    - Alias the default gnome-shell.css to gdm3.css
+  * gnome-shell-common.install: Don't install compiled css file anymore
+  * gnome-shell-common.{preinst,prerm}:
+    - Remove obsolete alternative gdm3.css symlink
+  * gnome-shell-common.postinst:
+    - Set gnome-shell-theme.gresource as alternative for gdm3-theme.gresource
+  * Remaining changes from debian:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+    + Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 20 Mar 2020 17:29:04 +0000
+
 gnome-shell (3.36.0-2) experimental; urgency=medium
 
   * Split gnome-shell-extension-prefs out into its own binary package.
@@ -1878,6 +4830,84 @@ gnome-shell (3.35.92-1) experimental; ur
 
  -- Iain Lane <laney@debian.org>  Tue, 03 Mar 2020 16:37:58 +0000
 
+gnome-shell (3.35.91-1ubuntu2) focal; urgency=medium
+
+  * control: Fix package for Yaru Breaks. It's called `yaru-theme-gnome-shell`
+    and not `yaru-theme`
+  * control: Bump Breaks on appindicator and desktop-icons.
+    These have been updated for GNOME 3.36
+
+ -- Iain Lane <iain.lane@canonical.com>  Thu, 27 Feb 2020 15:35:57 +0000
+
+gnome-shell (3.35.91-1ubuntu1) focal; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+    + Break gnome-shell-extension-appindicator (<< 30)
+    + Break gnome-shell-extension-desktop-icons (<< 19.01.3+git20190814)
+    + Break gnome-shell-extension-ubuntu-dock (<< 67ubuntu20.04.1)
+    + Break yaru-theme (<< 20.04.2)
+  * debian/control:
+    - Bump breaks on gnome-shell-extension-ubuntu-dock (<< 67ubuntu20.04.1)
+    - Bump breaks on gnome-shell-extension-dashtodock (<< 67+git20200225-1)
+    - Bump breaks on yaru-theme (<< 20.04.2)
+    - Build depend and Depend on gjs 1.58.1-2ubuntu1
+  * d/p/st-scroll-view-Remove-scrollbars-references-on-dispose.patch,
+  * d/p/volume-Add-back-sound-feedback-on-scroll.patch:
+    - Dropped, applied upstream
+  * d/p/ubuntu/background_login.patch:
+    - Simplify: just change the default background color to match Yaru
+  * d/p/ubuntu/lock_on_suspend.patch:
+    - Update to use string formatting not to confuse gettext with templates
+  * d/p/ubuntu/meson-Depend-on-gjs-1.58.1.patch:
+    - Lower the dependency on gjs 1.58.1
+  * debian/patches:
+    - Refreshed
+  * debian/{control,patches}: Build depend on gjs 1.58.1-2ubuntu1
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 26 Feb 2020 14:36:57 +0100
+
 gnome-shell (3.35.91-1) experimental; urgency=medium
 
   [ Marco Trevisan (Treviño) ]
@@ -1904,6 +4934,61 @@ gnome-shell (3.35.91-1) experimental; ur
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 24 Feb 2020 18:48:37 +0000
 
+gnome-shell (3.34.3-1ubuntu1) focal; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+
+ -- Iain Lane <iain.lane@canonical.com>  Mon, 06 Jan 2020 17:54:48 +0000
+
 gnome-shell (3.34.3-1) unstable; urgency=medium
 
   * New upstream release
@@ -1934,6 +5019,61 @@ gnome-shell (3.34.2-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Mon, 16 Dec 2019 16:55:02 +0000
 
+gnome-shell (3.34.1+git20191024-1ubuntu1) focal; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+
+ -- Iain Lane <iain.lane@canonical.com>  Fri, 25 Oct 2019 17:01:12 +0100
+
 gnome-shell (3.34.1+git20191024-1) unstable; urgency=medium
 
   * New upstream snapshot release
@@ -1944,6 +5084,62 @@ gnome-shell (3.34.1+git20191024-1) unsta
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 25 Oct 2019 13:36:08 +0100
 
+gnome-shell (3.34.1-1ubuntu1) eoan; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+  * Refresh patches through gbp-pq
+
+ -- Iain Lane <iain.lane@canonical.com>  Wed, 09 Oct 2019 12:18:14 +0100
+
 gnome-shell (3.34.1-1) unstable; urgency=medium
 
   * New upstream release
@@ -1977,6 +5173,61 @@ gnome-shell (3.34.0-2) unstable; urgency
 
  -- Andreas Henriksson <andreas@fatal.se>  Mon, 30 Sep 2019 14:29:53 +0200
 
+gnome-shell (3.34.0-1ubuntu1) eoan; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+
+ -- Iain Lane <iain.lane@canonical.com>  Tue, 10 Sep 2019 13:13:52 +0100
+
 gnome-shell (3.34.0-1) experimental; urgency=medium
 
   * New upstream release
@@ -1989,6 +5240,68 @@ gnome-shell (3.34.0-1) experimental; urg
 
  -- Iain Lane <laney@debian.org>  Tue, 10 Sep 2019 10:51:44 +0100
 
+gnome-shell (3.33.92-1ubuntu1) eoan; urgency=medium
+
+  [ Iain Lane ]
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+  * Refresh patches
+
+  [ Daniel van Vugt ]
+  * background_login.patch: Match 19.10 Yaru purple login screen with noise.
+    This way the login animation will appear to expand over the login screen
+    (system background) instead of suddenly replacing it.
+
+ -- Iain Lane <iain.lane@canonical.com>  Thu, 05 Sep 2019 18:31:55 +0100
+
 gnome-shell (3.33.92-1) experimental; urgency=medium
 
   * New upstream release
@@ -2010,6 +5323,75 @@ gnome-shell (3.33.92-1) experimental; ur
 
  -- Iain Lane <laney@debian.org>  Thu, 05 Sep 2019 18:15:19 +0100
 
+gnome-shell (3.33.91-1ubuntu1) eoan; urgency=medium
+
+  [ Didier Roche ]
+  * Read mode extension update and load
+  * Avoid hardcoding set of ubuntu mode extensions
+
+  [ Marco Trevisan (Treviño) ]
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+  * ubuntu/block_mode_extension_update.patch:
+    - Dropped, as can be handled in a nicer way as per upstream changes
+  * optional-hot-corner.patch:
+    - Dropped as similar implementation is included upstream
+
+  [ Iain Lane ]
+  * Bump yaru-theme Breaks. We need an updated version, otherwise (at least)
+    the Activities search is broken.
+  * Bump Breaks on default shell extensions
+
+ -- Iain Lane <iain.lane@canonical.com>  Sat, 24 Aug 2019 09:08:42 +0100
+
 gnome-shell (3.33.91-1) experimental; urgency=medium
 
   * New upstream release
@@ -2081,6 +5463,63 @@ gnome-shell (3.33.90-1) experimental; ur
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 13 Aug 2019 11:12:43 +0100
 
+gnome-shell (3.32.2-2ubuntu1) eoan; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+
+ -- Iain Lane <iain.lane@canonical.com>  Thu, 27 Jun 2019 18:02:14 +0100
+
 gnome-shell (3.32.2-2) experimental; urgency=medium
 
   * debian/source_gnome-shell.py:
@@ -2096,6 +5535,63 @@ gnome-shell (3.32.2-2) experimental; urg
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 27 Jun 2019 17:54:16 +0100
 
+gnome-shell (3.32.2-1ubuntu1) eoan; urgency=medium
+
+  * Merge with Debian. Remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+
+ -- Iain Lane <iain.lane@canonical.com>  Tue, 21 May 2019 17:40:26 +0100
+
 gnome-shell (3.32.2-1) experimental; urgency=medium
 
   * New upstream release
@@ -2104,6 +5600,73 @@ gnome-shell (3.32.2-1) experimental; urg
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 21 May 2019 17:19:41 +0100
 
+gnome-shell (3.32.1-1ubuntu1) eoan; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * Merge with debian
+    - Update to 3.32.1 upstream version (LP: #1826936)
+    - Fixes Icon disappears from favorites (LP: #1822846)
+    - Fix applications aren't focused in Wayland session (LP: #1826176)
+    remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+  * d/p/magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+    - Handle the no-monitor case (LP: #1826797)
+
+  [ Andrea Azzarone ]
+  * d/p/volume-Add-back-sound-feedback-on-scroll.patch: Add missing import
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 01 May 2019 10:23:16 +0100
+
 gnome-shell (3.32.1-1) experimental; urgency=medium
 
   [ Simon McVittie ]
@@ -2117,6 +5680,71 @@ gnome-shell (3.32.1-1) experimental; urg
 
  -- Laurent Bigonville <bigon@debian.org>  Mon, 22 Apr 2019 15:45:49 +0200
 
+gnome-shell (3.32.0+git20190410-1ubuntu1) disco; urgency=medium
+
+  * Merging with debian git snapshot (LP: #1820775, LP: #1818790),
+    remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+      - Show monitor scaled cursor when magnifier is enabled
+  * d/p/magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+    - Dropped the merged part, kept the fix for cursor scaling.
+  * d/p/optional-hot-corner.patch,
+    d/p/sessionMode-add-support-for-debugFlags-parameter.patch,
+    d/p/ubuntu/lock_on_suspend.patch,
+    d/p/ubuntu/resolve_alternate_theme_path.patch:
+    - Refreshed.
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 10 Apr 2019 20:15:39 -0500
+
 gnome-shell (3.32.0+git20190410-1) experimental; urgency=medium
 
   * Use debhelper-compat 12 and BD on dh-sequence-{gnome,gir}
@@ -2131,6 +5759,69 @@ gnome-shell (3.32.0+git20190410-1) exper
 
  -- Iain Lane <laney@debian.org>  Wed, 10 Apr 2019 16:36:31 +0100
 
+gnome-shell (3.32.0-1ubuntu2) UNRELEASED; urgency=medium
+
+  * d/p/magnifier-Show-cursor-when-magnifier-is-enabled-and-scale.patch:
+    - magnifier: Show cursor when magnifier is enabled and scale it to
+      match monitor scaling (LP: #1818790)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 27 Mar 2019 16:51:27 +0100
+
+gnome-shell (3.32.0-1ubuntu1) disco; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+
+ -- Iain Lane <iain.lane@canonical.com>  Tue, 12 Mar 2019 13:29:02 +0000
+
 gnome-shell (3.32.0-1) experimental; urgency=medium
 
   * New upstream release
@@ -2146,6 +5837,82 @@ gnome-shell (3.32.0-1) experimental; urg
 
  -- Iain Lane <laney@debian.org>  Tue, 12 Mar 2019 12:50:26 +0000
 
+gnome-shell (3.31.92-1ubuntu1) disco; urgency=medium
+
+  [ Daniel van Vugt ]
+  * Update ubuntu/background_login.patch to match the new login screen
+    (system background) design from Yaru for a smooth login animation
+    (LP: #1813119).
+
+  [ Iain Lane ]
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + debian/patches/ubuntu/background_login.patch:
+      - match Yaru theme in the ubuntu session instead of our previous GDM
+        background
+    + debian/patches/ubuntu/resolve_alternate_theme_path.patch:
+      - ensure we resolve finale theme file path to correctly load assets
+        under gdm
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+    + Add desktop-icons to the Ubuntu session by default
+    + Break nautilus << 3.30. We don't want two things trying to handle the
+      desktop.
+  * debian/patches/*: Refresh.
+      - d/p/st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch:
+        Drop, this is now upstream.
+
+ -- Iain Lane <iain.lane@canonical.com>  Wed, 06 Mar 2019 14:58:20 +0000
+
 gnome-shell (3.31.92-1) experimental; urgency=medium
 
   * New upstream release
@@ -2174,6 +5941,103 @@ gnome-shell (3.31.92-1) experimental; ur
 
  -- Iain Lane <laney@debian.org>  Wed, 06 Mar 2019 14:39:17 +0000
 
+gnome-shell (3.31.90-1ubuntu1) disco; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + debian/patches/ubuntu/background_login.patch:
+      - match Yaru theme in the ubuntu session instead of our previous GDM
+        background
+    + debian/patches/ubuntu/resolve_alternate_theme_path.patch:
+      - ensure we resolve finale theme file path to correctly load assets
+        under gdm
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+    + Add desktop-icons to the Ubuntu session by default
+    + Break nautilus << 3.30. We don't want two things trying to handle the
+      desktop.
+  * d/p/st-button-Ignore-pointer-emulated-touch-events.patch,
+    d/p/keyboard-Do-not-call-KeyboardManager.holdKeyboard-with-se.patch:
+    - Removed, as applied upstreamy
+  * d/p/optional-hot-corner.patch,
+    d/p/background_login.patch,
+    d/p/ubuntu/lightdm-user-switching.patch,
+    d/p/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+    - Updated as per ES6 javascript usage upstream
+  * d/p/global-make-possible-to-set-debug-flags-dynamically.patch,
+    d/p/main-add-backtrace-crashes-all-and-backtrace-all.patch,
+    d/p/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+    d/p/main-show-an-error-message-on-gnome-shell-crash.patch,
+    d/p/sessionMode-add-support-for-debugFlags-parameter.patch,
+    d/p/st-button-Ignore-pointer-emulated-touch-events.patch,
+    d/p/st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+    d/p/st-scroll-view-Remove-scrollbars-references-on-dispose.patch,
+    d/p/st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch,
+    d/p/ubuntu/block_mode_extension_update.patch,
+    d/p/ubuntu/desktop_detect.patch,
+    d/p/ubuntu/gdm_alternatives.patch,
+    d/p/ubuntu/lock_on_suspend.patch,
+    d/p/ubuntu/resolve_alternate_theme_path.patch,
+    d/p/volume-Add-back-sound-feedback-on-scroll.patch:
+    - Refreshed
+
+  [ Iain Lane ]
+  * Bump ubuntu-dock Breaks version; we need to update for this shell
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 21 Feb 2019 15:46:50 +0000
+
 gnome-shell (3.31.90-1) experimental; urgency=medium
 
   [ Marco Trevisan (Treviño) ]
@@ -2237,6 +6101,92 @@ gnome-shell (3.30.2-3) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Wed, 06 Feb 2019 09:46:52 +0000
 
+gnome-shell (3.30.2-2ubuntu2) disco; urgency=medium
+
+  * debian/patches/ubuntu/smarter_alt_tab.patch:
+    Dropped, now that application switching is set to super-tab instead of
+    alt-tab
+  * Merge debian/patches/ubuntu/gdm.patch and
+    debian/patches/ubuntu/gdm_alternatives.patch:
+    - gdm_alternatives was a patch on a patch, merged them and remove
+      gdm.patch
+
+ -- Didier Roche <didrocks@ubuntu.com>  Thu, 07 Feb 2019 11:51:47 +0100
+
+gnome-shell (3.30.2-2ubuntu1) disco; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + keyboard-Do-not-call-KeyboardManager.holdKeyboard-with-se.patch:
+      - Cherry-pick upstream commit to prevent focus stealing on password fields
+        in firefox when ibus is used
+    + debian/patches/ubuntu/background_login.patch:
+      - match Yaru theme in the ubuntu session instead of our previous GDM
+        background
+    + debian/patches/ubuntu/resolve_alternate_theme_path.patch:
+      - ensure we resolve finale theme file path to correctly load assets
+        under gdm
+    + debian/patches/st-button-Ignore-pointer-emulated-touch-events.patch:
+      - Don't emit two click events on touch under X11
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+    + Add desktop-icons to the Ubuntu session by default
+    + Break nautilus << 3.30. We don't want two things trying to handle the
+      desktop.
+
+ -- Iain Lane <iain.lane@canonical.com>  Fri, 25 Jan 2019 13:24:52 +0000
+
 gnome-shell (3.30.2-2) unstable; urgency=medium
 
   [ Jeremy Bicha ]
@@ -2251,6 +6201,80 @@ gnome-shell (3.30.2-2) unstable; urgency
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 25 Jan 2019 11:52:36 +0000
 
+gnome-shell (3.30.2-1ubuntu1) disco; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + keyboard-Do-not-call-KeyboardManager.holdKeyboard-with-se.patch:
+      - Cherry-pick upstream commit to prevent focus stealing on password fields
+        in firefox when ibus is used
+    + debian/patches/ubuntu/background_login.patch:
+      - match Yaru theme in the ubuntu session instead of our previous GDM
+        background
+    + debian/patches/ubuntu/resolve_alternate_theme_path.patch:
+      - ensure we resolve finale theme file path to correctly load assets
+        under gdm
+    + debian/patches/st-button-Ignore-pointer-emulated-touch-events.patch:
+      - Don't emit two click events on touch under X11
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+    + Add desktop-icons to the Ubuntu session by default
+    + Break nautilus << 3.30. We don't want two things trying to handle the
+      desktop.
+
+ -- Iain Lane <iain.lane@canonical.com>  Fri, 11 Jan 2019 18:09:26 +0000
+
 gnome-shell (3.30.2-1) unstable; urgency=medium
 
   * Team upload
@@ -2280,6 +6304,104 @@ gnome-shell (3.30.1-3) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Tue, 06 Nov 2018 09:55:55 +0000
 
+gnome-shell (3.30.1-2ubuntu4) disco; urgency=medium
+
+  * Add desktop-icons to the Ubuntu session by default
+  * Break nautilus << 3.30. We don't want two things trying to handle the
+    desktop.
+
+ -- Iain Lane <iain.lane@canonical.com>  Mon, 17 Dec 2018 12:22:32 +0000
+
+gnome-shell (3.30.1-2ubuntu3) disco; urgency=medium
+
+  * Cherry-pick upstream commit to prevent focus stealing on password fields
+    in firefox when ibus is used (LP: #1765304)
+
+ -- Iain Lane <iain.lane@canonical.com>  Wed, 05 Dec 2018 12:53:32 +0000
+
+gnome-shell (3.30.1-2ubuntu2) disco; urgency=medium
+
+  [ Didier Roche ]
+  * debian/patches/ubuntu/background_login.patch:
+    - match Yaru theme in the ubuntu session instead of our previous GDM
+      background (LP: #1789356)
+  * debian/patches/ubuntu/resolve_alternate_theme_path.patch:
+    - ensure we resolve finale theme file path to correctly load assets
+      under gdm (LP: #1798747)
+
+  [ Marco Trevisan (Treviño) ]
+  * debian/patches/st-button-Ignore-pointer-emulated-touch-events.patch:
+    - Don't emit two click events on touch under X11 (LP: #1745888)
+
+ -- Iain Lane <laney@debian.org>  Wed, 07 Nov 2018 11:05:30 +0000
+
+gnome-shell (3.30.1-2ubuntu1) cosmic; urgency=medium
+
+  * Merge with debian (LP: #1796837), remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - stop searches when requested from UI
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+  * js-main-Throw-error-if-no-valid-default-stylesheet-is-fou.patch,
+    viewSelector-Cancel-search-on-overview-hidden.patch
+    search-Cancel-search-provider-operations-on-clear.patch
+    search-Ignore-search-provider-results-metas-if-search-is-.patch: Dropped,
+    applied upstream.
+  * ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+    Refreshed.
+
+ -- Iain Lane <iain.lane@canonical.com>  Wed, 10 Oct 2018 09:52:13 +0100
+
 gnome-shell (3.30.1-2) unstable; urgency=medium
 
   * Team upload
@@ -2299,6 +6421,76 @@ gnome-shell (3.30.1-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Tue, 09 Oct 2018 10:24:16 +0100
 
+gnome-shell (3.30.0-3ubuntu1) cosmic; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + js-ui-Choose-some-actors-to-cache-on-the-GPU.patch
+      - Improve rendering of shell elements moving rendering to GPU
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + js-main-Throw-error-if-no-valid-default-stylesheet-is-fou.patch:
+      - Show errors if no stylesheet is found
+    + search-Cancel-search-provider-operations-on-clear.patch,
+      search-Ignore-search-provider-results-metas-if-search-is-.patch,
+      viewSelector-Cancel-search-on-overview-hidden.patch,
+      search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - Add support for cancelling remote search providers when the overlay
+        is closed (and actually stop searches when requested from UI
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+      - Run dh_install with --fail-missing
+  * Reorder patch series to put Ubuntu patches last
+
+ -- Iain Lane <iain.lane@canonical.com>  Mon, 01 Oct 2018 13:45:42 +0100
+
 gnome-shell (3.30.0-3) unstable; urgency=medium
 
   * Backport the patches recommended by upstream for 3.30 (Closes: #904560)
@@ -2315,6 +6507,82 @@ gnome-shell (3.30.0-2) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Thu, 20 Sep 2018 11:26:04 +0100
 
+gnome-shell (3.30.0-1ubuntu2) cosmic; urgency=medium
+
+  * ubuntu/desktop_detect.patch:
+    + Added missing misc/desktop.js to pref's gresource.xml (LP: #1790996)
+
+ -- Shem Pasamba <shemgp@gmail.com>  Thu, 06 Sep 2018 16:41:39 +0800
+
+gnome-shell (3.30.0-1ubuntu1) cosmic; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + js-ui-Choose-some-actors-to-cache-on-the-GPU.patch
+      - Improve rendering of shell elements moving rendering to GPU
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + js-main-Throw-error-if-no-valid-default-stylesheet-is-fou.patch:
+      - Show errors if no stylesheet is found
+    + search-Cancel-search-provider-operations-on-clear.patch,
+      search-Ignore-search-provider-results-metas-if-search-is-.patch,
+      viewSelector-Cancel-search-on-overview-hidden.patch,
+      search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+      - Add support for cancelling remote search providers when the overlay
+        is closed (and actually stop searches when requested from UI
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+      - Run dh_install with --fail-missing
+
+ -- Didier Roche <didrocks@ubuntu.com>  Tue, 04 Sep 2018 19:20:58 +0200
+
 gnome-shell (3.30.0-1) unstable; urgency=medium
 
   [ Didier Roche ]
@@ -2327,6 +6595,80 @@ gnome-shell (3.30.0-1) unstable; urgency
 
  -- Didier Roche <didrocks@ubuntu.com>  Wed, 05 Sep 2018 12:06:24 +0100
 
+gnome-shell (3.29.92-1ubuntu1) cosmic; urgency=medium
+
+  * Merge with debian, remaining changes:
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+      - yaru-theme-gnome-shell for the default ubuntu theming
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + ubuntu/desktop_detect.patch:
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + ubuntu/smarter_alt_tab.patch:
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + ubuntu/lightdm-user-switching.patch:
+      - Allow user switching when using LightDM.
+    + ubuntu/lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu/gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu/background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu/gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu/block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + js-ui-Choose-some-actors-to-cache-on-the-GPU.patch
+      - Improve rendering of shell elements moving rendering to GPU
+    + main-show-an-error-message-on-gnome-shell-crash.patch,
+      global-make-possible-to-set-debug-flags-dynamically.patch,
+      main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+      main-add-backtrace-crashes-all-and-backtrace-all.patch,
+      sessionMode-add-support-for-debugFlags-parameter.patch:
+      - Improve debug JS tracing for crash reports
+    + st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+      st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+      - Fix crash on theme changes
+    + js-main-Throw-error-if-no-valid-default-stylesheet-is-fou.patch:
+      - Show errors if no stylesheet is found
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+      - Run dh_install with --fail-missing
+  * Refresh patches
+  * d/p/st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch:
+    - Updated as per upstream review
+  * d/p/js-ui-Choose-some-actors-to-cache-on-the-GPU.patch:
+    - Removed (applied upstream)
+  * d/p/search-Cancel-search-provider-operations-on-clear.patch,
+    d/p/search-Ignore-search-provider-results-metas-if-search-is-.patch,
+    d/p/viewSelector-Cancel-search-on-overview-hidden.patch,
+    d/p/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch:
+    - Add support for cancelling remote search providers when the overlay
+      is closed (and actually stop searches when requested from UI, LP: #1756826)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 30 Aug 2018 17:59:08 -0500
+
 gnome-shell (3.29.92-1) experimental; urgency=medium
 
   * Team upload
@@ -2367,6 +6709,93 @@ gnome-shell (3.29.91-1) experimental; ur
 
  -- Simon McVittie <smcv@debian.org>  Mon, 20 Aug 2018 21:32:20 +0100
 
+gnome-shell (3.29.90-2ubuntu1) cosmic; urgency=medium
+
+  * Merge with debian, remaining changes
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode
+    + gnome-shell-common.prerm: Remove deprecated ubuntu theme alternative
+    + 50_add_ubuntu_desktop_detect.patch
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + 71_smarter_alt_tab.patch
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + ubuntu-lightdm-user-switching.patch
+      - Allow user switching when using LightDM.
+    + ubuntu_lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu_gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu_background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu_gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu_block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + js-ui-Choose-some-actors-to-cache-on-the-GPU.patch
+      - Improve rendering of shell elements moving rendering to GPU
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+      - Run dh_install with fail-missing
+  * d/p/ubuntu_background_login.patch,
+    d/p/ubuntu_block_mode_extension_update.patch,
+    d/p/50_add_ubuntu_desktop_detect.patch,
+    d/p/ubuntu_gdm.patch,
+    d/p/ubuntu_gdm_alternatives.patch,
+    d/p/ubuntu-lightdm-user-switching.patch,
+    d/p/ubuntu_lock_on_suspend.patch,
+    d/p/71_smarter_alt_tab.patch:
+    - Using Gbp-Pq Topic to ubuntu (so they get moved to debian/patches/ubuntu)
+  * d/p/main-show-a-critical-message-on-gnome-shell-crash.patch,
+    d/p/global-make-possible-to-set-debug-flags-dynamically.patch,
+    d/p/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch,
+    d/p/main-add-backtrace-crashes-all-and-backtrace-all.patch,
+    d/p/sessionMode-add-support-for-debugFlags-parameter.patch:
+    - Allow to get better debug informations on crashes, and in case interactive
+      user informations
+  * d/p/st-scroll-view-Handle-the-case-where-scrollbars-are-NULL.patch,
+    d/p/st-scroll-view-Remove-scrollbars-references-on-dispose.patch:
+    - Handle NULL scroll bars in st-scroll-view (LP: #1725312)
+  * d/p/js-main-Throw-error-if-no-valid-default-stylesheet-is-fou.patch:
+    - Be informative if gnome-shell tries to load an invalid default theme
+  * debian/ubuntu-session-mods/ubuntu.json:
+    - Enable JS stacktrace printing on gnome-shell crashes using debugFlags
+
+  [ Didier Roche ]
+  * Remove ubuntu.css and related assets:
+    - Don't install them anymore, as we rely on Yaru as our default theme.
+    - Remove older alternative on upgrades to this version and removals.
+  * Set Yaru theme as default for the ubuntu mod (LP: #1783571)
+  * Set default gdm theme as upstream GDM. Will be overriden in the Yaru
+    package.
+
+  [ Iain Lane ]
+  * debian/control{,.in}: Make gnome-shell-common Recommend
+    yaru-theme-gnome-shell, since we reference its css file in our
+    styleSheetName.
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Fri, 17 Aug 2018 14:38:47 +0100
+
 gnome-shell (3.29.90-2) experimental; urgency=medium
 
   * Team upload
@@ -2388,6 +6817,91 @@ gnome-shell (3.29.90-2) experimental; ur
 
  -- Simon McVittie <smcv@debian.org>  Sat, 04 Aug 2018 16:32:57 +0100
 
+gnome-shell (3.29.90-1ubuntu2) cosmic; urgency=medium
+
+  * Thanks component-mismatches, chrome-gnome-shell should indeed be a
+    suggests.🐚
+
+ -- Iain Lane <laney@ubuntu.com>  Fri, 03 Aug 2018 12:15:20 +0100
+
+gnome-shell (3.29.90-1ubuntu1) cosmic; urgency=medium
+
+  [ Didier Roche ]
+  * Add debian/README.Ubuntu with instructions on how to update the Yaru
+    theme.
+
+  [ Marco Trevisan (Treviño) ]
+  * Merge back with debian
+    + Replace gnome-backgrounds dep with ubuntu-wallpapers and Suggests
+      gnome-themes-standard-data, gnome-backgrounds
+    + Add some Recommends:
+      - ubuntu-session (| gnome-session) to have the ubuntu session available
+      - xserver-xorg-legacy
+    + Update debian/gbp.conf with Ubuntu settings
+    + gnome-shell-common.install: Install Ubuntu mode and Shell theme
+    + gnome-shell-common.{postinst,prerm}: Set up the Ubuntu css theme as an
+      alternative
+    + 50_add_ubuntu_desktop_detect.patch
+      - add caching for desktop detection to avoid querying the current
+        desktop env variable as iterate through the list each time. For the
+        time of the Shell process, we can expect this env variable to stay
+        stable.
+    + 71_smarter_alt_tab.patch
+      - quick alt-tab (without showing up the switcher) switch only between
+        the last window of the last 2 applications to be focused instead of
+        raising all windows of those apps.
+    + optional-hot-corner.patch
+      - enable patch proposed by upstream developer already in package (but
+        not in series) to add a settings for optional hot corner activation.
+    + ubuntu-lightdm-user-switching.patch
+      - Allow user switching when using LightDM.
+    + ubuntu_lock_on_suspend.patch
+      - Respect Ubuntu's lock-on-suspend setting.
+    + ubuntu_gdm.patch
+      - as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+        styling by default, not impacting the gnome user session though.
+    + ubuntu_background_login.patch
+      - Change default background color as we modified the default GDM color
+        for our ubuntu session. Change it as well here, still applying the
+        background noise loading.
+    + ubuntu_gdm_alternatives.patch
+      - Add support for GDM3 theme alternatives
+    + ubuntu_block_mode_extension_update.patch
+      - Don't allow ubuntu mode extension to update
+    + volume-Add-back-sound-feedback-on-scroll.patch
+      - Fix regression causing missing feedback on volume slider scroll
+    + st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch
+      - Fix possible crash on cache loading
+    + js-ui-Choose-some-actors-to-cache-on-the-GPU.patch
+      - Improve rendering of shell elements moving rendering to GPU
+    + debian/rules:
+      - Run dh_translations and work around an issue with Rosetta and plural
+        translations
+      - Modify upstream theme assets for the Ubuntu them
+      - Run dh_install with fail-missing
+  * d/p/volume-Add-back-sound-feedback-on-scroll.patch:
+    - add missing GLib import causing a JS error
+  * debian/ubuntu-session-mods/ubuntu.css:
+    - update rules as per upstream changes
+  * d/p/41-handle-logind-fail.patch,
+    d/p/70_allow_sound_above_100.patch,
+    d/p/workaround_crasher_fractional_scaling.patch,
+    d/p/shell-ignore-invalid-window-monitor-index.patch,
+    d/p/StIcon-only-compute-shadow-pipeline-when-the-texture-is-p.patch,
+    d/p/js-fix-invalid-access-errors.patch,
+    d/p/workspace-fix-repositioned-windows-in-activities.patch,
+    d/p/authPrompt-Unset-preemptiveAnswer-on-reset.patch,
+    d/p/authPrompt-Do-not-enable-sensitivity-if-retries-are-disal.patch,
+    d/p/gdm-util-Always-allow-to-retry-login-in-unlock-mode.patch:
+    - Removed obsolete or merged upstream patches
+
+  [ Iain Lane ]
+  * debian/control{,.in}: Break old versions of
+    gnome-shell-extension-ubuntu-dock and yaru-theme, both of which need to be
+    updated to work with this version of Shell.
+
+ -- Iain Lane <laney@ubuntu.com>  Thu, 02 Aug 2018 15:17:49 +0100
+
 gnome-shell (3.29.90-1) experimental; urgency=medium
 
   * Team upload
@@ -2461,6 +6975,30 @@ gnome-shell (3.28.3-1) unstable; urgency
 
  -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 19 Jul 2018 20:05:52 +0100
 
+gnome-shell (3.28.3-0ubuntu2) cosmic; urgency=medium
+
+  * debian/patch/js-fix-invalid-access-errors.patch:
+    - Updated to include upstream requested changes and removing
+      wrongly preserved code from previous refresh (LP: #1783363)
+  * debian/patches/workspace-fix-repositioned-windows-in-activities.patch:
+    - Cherry-pick from upstream 3.28 branch
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Wed, 25 Jul 2018 01:40:50 +0200
+
+gnome-shell (3.28.3-0ubuntu1) cosmic; urgency=medium
+
+  * debian/patches/30-remoteMenu-Prevent-the-shell-from-becoming-unrespons.patch,
+    debian/patches/magnifier.js-Fix-zoom-juddering.patch,
+    debian/patches/st-label-Unset-clutter-text-instance-on-disposal.patch,
+    debian/patches/st-texture-cache-Don-t-add-NULL-textures-to-cache.patch,
+    debian/patches/st-texture-cache-Save-cairo-surfaces-to-a-different-map.patch,
+    debian/patches/ui-Theme-lookup-should-respect-XDG_DATA_DIRS.patch:
+    - Drop patches applied on the 3.28 branch
+  * debian/patches/js-fix-invalid-access-errors.patch:
+    - Refreshed to reflect upstream changes
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 24 Jul 2018 12:29:20 +0100
+
 gnome-shell (3.28.2-2) unstable; urgency=medium
 
   * Team upload
@@ -2502,6 +7040,43 @@ gnome-shell (3.28.2-1) unstable; urgency
 
  -- Simon McVittie <smcv@debian.org>  Thu, 17 May 2018 10:37:37 +0100
 
+gnome-shell (3.28.2-0ubuntu1) cosmic; urgency=medium
+
+  [ Olivier Tilloy ]
+  * New upstream release
+    - fixes valid password rejection at login screen (LP: #1765261)
+  * Drop patches applied upstream:
+    - debian/patches/polkitAgent-Guard-against-repeated-close-calls.patch
+    - debian/patches/popupMenu-Fix-wrong-call-to-clutter_actor_add_child.patch
+    - debian/patches/workspaceThumbnail-initialize-porthole-based-on-workArea.patch
+    - debian/patches/workspaceThumbnail-only-update-_porthole-if-the-overview-.patch
+    - debian/patches/workspaceThumbnail-rebuild-thumbnails-if-workareas-size-c.patch
+
+  [ Andrea Azzarone ]
+  * debian/patches/ubuntu_lock_on_suspend.patch: inhibit suspend until the
+    screen is locked also in the case where automatic screen lock is disabled
+    and screen lock on suspend is enabled (LP: #1768786)
+
+  [ Marco Trevisan (Treviño) ]
+  * Cherry pick upstream patches:
+    - debian/patches/st-label-Unset-clutter-text-instance-on-disposal.patch (LP: #1714989)
+  * debian/patches/st-texture-cache-Don-t-add-NULL-textures-to-cache.patch:
+    - Cherry pick updated version from upstream, splitted in:
+    + debian/patches/st-texture-cache-Don-t-add-NULL-textures-to-cache.patch
+    + debian/patches/st-texture-cache-Save-cairo-surfaces-to-a-different-map.patch
+  * debian/patches/authPrompt-Do-not-enable-sensitivity-if-retries-are-disal.patch
+    debian/patches/authPrompt-Unset-preemptiveAnswer-on-reset.patch
+    debian/patches/gdm-util-Always-allow-to-retry-login-in-unlock-mode.patch:
+    - GDM gnome-shell greeter fix to fix unneeded login attempts (LP: #1777956)
+  * debian/patches/series:
+    - reorder to apply upstream cherry-picks before the others
+
+  [ Daniel van Vugt ]
+  * debian/patches/magnifier.js-Fix-zoom-juddering.patch:
+    - magnifier.js: Fix zoom juddering (LP: #1691675)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Thu, 21 Jun 2018 01:59:11 +0200
+
 gnome-shell (3.28.1-1) unstable; urgency=medium
 
   [ Jeremy Bicha ]
@@ -2535,6 +7110,57 @@ gnome-shell (3.28.1-1) unstable; urgency
 
  -- Jeremy Bicha <jbicha@debian.org>  Tue, 08 May 2018 09:00:35 -0400
 
+gnome-shell (3.28.1-0ubuntu2) bionic; urgency=medium
+
+  [ Marco Trevisan (Treviño) ]
+  * js-ui-Choose-some-actors-to-cache-on-the-GPU.patch:
+    - Improve rendering of shell elements moving rendering to GPU
+      (LP: #1744001)
+
+  [ Jeremy Bicha ]
+  * Cherry-pick polkitAgent-Guard-against-repeated-close-calls.patch:
+    - Fix PolicyKit authorization prompt after cancelling once (LP: #1765353)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Mon, 23 Apr 2018 15:29:28 -0500
+
+gnome-shell (3.28.1-0ubuntu1) bionic; urgency=medium
+
+  [ Jeremy Bicha ]
+  * New upstream release
+  * Drop obsolete patches:
+    - 27-nm-libexec-path.patch
+    - fix-wayland-vbox-crash.patch
+
+  [ Marco Trevisan (Treviño) ]
+  Cherry-pick patches applied to upstream git master:
+  * ui-Theme-lookup-should-respect-XDG_DATA_DIRS.patch:
+    - Renamed from ubuntu_theme_lookup_xdg.patch and cherry-picked from
+      upstream
+  * workspaceThumbnail-only-update-_porthole-if-the-overview-.patch:
+    workspaceThumbnail-rebuild-thumbnails-if-workareas-size-c.patch:
+    workspaceThumbnail-initialize-porthole-based-on-workArea.patch:
+    - Cherry-picks from upstream, avoid unneeded computations in activities
+  * popupMenu-Fix-wrong-call-to-clutter_actor_add_child.patch:
+    - Cherry-pick from upstream
+  * volume-Add-back-sound-feedback-on-scroll.patch:
+    - Fix regression causing missing feedback on volume slider scroll
+
+  Add patches proposed upstream:
+  * shell-ignore-invalid-window-monitor-index.patch:
+    - Fix crash on accessing on invalid monitor windows (LP: #1724439)
+  * StIcon-only-compute-shadow-pipeline-when-the-texture-is-p.patch:
+    - Don't try to compute shadows on not allocated icons (LP: #1723378)
+  * js-fix-invalid-access-errors.patch:
+    - Fix javascript errors (LP: #1747566)
+  * workspace-fix-repositioned-windows-in-activities.patch:
+    - Ensure windows thumbnails coordinates are correct (LP: #1653153)
+  * st-texture-cache-Cancel-sliced-image-loading-on-target-ac.patch:
+    - Fix possible crash on cache loading
+  * st-texture-cache-Don-t-add-NULL-textures-to-cache.patch:
+    - Fix crash when deleting NULL textures from cache (LP: #1754445)
+
+ -- Marco Trevisan (Treviño) <marco@ubuntu.com>  Tue, 17 Apr 2018 14:40:17 -0400
+
 gnome-shell (3.28.0-1) unstable; urgency=medium
 
   [ Simon McVittie ]
@@ -2542,6 +7168,41 @@ gnome-shell (3.28.0-1) unstable; urgency
 
  -- Jeremy Bicha <jbicha@debian.org>  Sun, 18 Mar 2018 20:16:20 -0400
 
+gnome-shell (3.28.0-0ubuntu5) bionic; urgency=medium
+
+  * No-change rebuild for translations (LP: #1756547)
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Sat, 07 Apr 2018 15:58:00 +0200
+
+gnome-shell (3.28.0-0ubuntu4) bionic; urgency=medium
+
+  * debian/rules:
+    - Drop javascript comments in gnome-shell.pot so the currently used
+      version of gettext in LP gets more permissive wrt cases where
+      the number of %d modifiers in a translation differs from what's
+      in msgid (LP: #1756547).
+
+ -- Gunnar Hjalmarsson <gunnarhj@ubuntu.com>  Fri, 06 Apr 2018 15:58:00 +0200
+
+gnome-shell (3.28.0-0ubuntu3) bionic; urgency=medium
+
+  * Recommend bolt for Thunderbolt 3 support (LP: #1759538)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Wed, 04 Apr 2018 10:04:50 -0400
+
+gnome-shell (3.28.0-0ubuntu2) bionic; urgency=medium
+
+  * debian/patches/ubuntu_theme_lookup_xdg.patch:
+    - theme lookup should respect XDG_DATA_DIRS (LP: #1760039)
+
+ -- Didier Roche <didrocks@ubuntu.com>  Fri, 30 Mar 2018 10:21:27 +0200
+
+gnome-shell (3.28.0-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 15 Mar 2018 10:39:16 -0400
+
 gnome-shell (3.27.92-2) unstable; urgency=medium
 
   [ Simon McVittie ]
@@ -2561,6 +7222,13 @@ gnome-shell (3.27.92-1) experimental; ur
 
  -- Jeremy Bicha <jbicha@debian.org>  Mon, 05 Mar 2018 20:51:14 -0500
 
+gnome-shell (3.27.92-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release candidate
+  * Drop api patch applied in new release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 06 Mar 2018 05:38:48 -0500
+
 gnome-shell (3.27.91-1) experimental; urgency=medium
 
   * New upstream development release (LP: #1751070)
@@ -2574,6 +7242,45 @@ gnome-shell (3.27.91-1) experimental; ur
 
  -- Jeremy Bicha <jbicha@debian.org>  Fri, 23 Feb 2018 18:45:14 -0500
 
+gnome-shell (3.27.91-0ubuntu1) bionic; urgency=medium
+
+  [ Didier Roche ]
+  * New upstream version (LP: #1751070, LP: #1722725,
+    LP: #1714989, LP: #1724557, LP: #1723857, LP: #1744970):
+    - debian/patches/70_allow_sound_above_100.patch,
+      debian/patches/ubuntu-lightdm-user-switching.patch patches
+      refreshed for new release. Removed ': function()' syntax as
+      upstreamed removed them in those context.
+    - debian/patches/ubuntu_panel_center_date_workarea.patch removed as
+      upstreamed now.
+    - debian/control.in, debian/rules:
+      bump deps, add sassc and libnm-dev which is now required.
+      drop caribou dep, as the OSK is now built-in.
+    - debian/rules:
+      use now with renamed build options.
+  * debian/ubuntu-session-mods/ubuntu.css, debian/rules:
+    - adapt to new GNOME Shell theme including OSK.
+    - color shift keys in orange in OSK and use differente svg.
+  * debian/rules:
+    - some duplication removal.
+  * debian/patches/27-nm-libexec-path.patch,
+    debian/patches/71_smarter_alt_tab.patch,
+    debian/patches/fix-wayland-vbox-crash.patch,
+    debian/patches/optional-hot-corner.patch,
+    debian/patches/ubuntu_block_mode_extension_update.patch,
+    debian/patches/ubuntu_gdm.patch,
+    debian/patches/ubuntu_lock_on_suspend.patch:
+    - unfuzzed
+  * Cherry-pick git_ea0770ae22a6c34797db4343cf7d2f59bb8d68f6.patch:
+    - upstream mutter API bump.
+  * debian/gnome-shell-common.install, debian/rules:
+    - the .css files aren't shipped anymore, so copy from the built version.
+
+  [ Jeremy Bicha ]
+  * Update NetworkManager dependencies
+
+ -- Didier Roche <didrocks@ubuntu.com>  Fri, 02 Mar 2018 10:04:06 +0100
+
 gnome-shell (3.26.2-5) experimental; urgency=medium
 
   * Team upload
@@ -2641,6 +7348,52 @@ gnome-shell (3.26.2-1) unstable; urgency
 
  -- Michael Biebl <biebl@debian.org>  Sun, 05 Nov 2017 20:11:21 +0100
 
+gnome-shell (3.26.2-0ubuntu3) bionic; urgency=medium
+
+  * debian/patches/ubuntu_panel_center_date_workarea.patch:
+    Align date entry relative to workarea. That way, title bars are
+    centered relative to date entry. (LP: #1716432)
+  * :
+    Don't allow ubuntu mode extension to update
+    Ensure that no update is proposed or loaded if sideloaded (always
+    prefer system version) on the ubuntu session.
+    We want to ensure that the default code running is going through
+    our QA and security team process than being loaded from a 3rd
+    party website.
+    Also, that will enable us to upload newer versions on GNOME
+    extension website while still letting older ubuntu release versions
+    running expected extension version.
+    Note that the patch set is simplified with a hardcoded extension list
+    until https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1 is merged.
+    The patch set is too large otherwise to keep it maintainable as a distro
+    patch.
+    See also https://bugzilla.gnome.org/show_bug.cgi?id=789852.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Thu, 15 Feb 2018 15:25:18 +0100
+
+gnome-shell (3.26.2-0ubuntu2) bionic; urgency=medium
+
+  * No-change rebuild for libical soname change.
+
+ -- Matthias Klose <doko@ubuntu.com>  Sat, 09 Dec 2017 11:41:33 +0000
+
+gnome-shell (3.26.2-0ubuntu1) bionic; urgency=medium
+
+  [ Jeremy Bicha ]
+  * New upstream release (LP: #1731053)
+  * debian/control.in:
+    - Drop redundant Suggests on gir1.2-gdm-1.0
+  * Drop patches applied in new release:
+    - fix_reset_initial_focus.patch
+    - git_layout-unset-when-headless.patch
+
+  [ Didier Roche ]
+  * Modify debian/patches/70_allow_sound_above_100.patch:
+    - Fix a crasher when people log into the ubuntu session without installing
+      gsettings-ubuntu-schemas (LP: #1712798)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Wed, 08 Nov 2017 16:19:02 -0500
+
 gnome-shell (3.26.1-3) unstable; urgency=medium
 
   * Cherry-pick git_popupmenu-icon-via-string.patch:
@@ -2687,6 +7440,71 @@ gnome-shell (3.26.1-1) experimental; urg
 
  -- Simon McVittie <smcv@debian.org>  Fri, 06 Oct 2017 10:35:46 +0100
 
+gnome-shell (3.26.1-0ubuntu6) bionic; urgency=medium
+
+  * Upstream-proposed fix for lid close crash with wayland and virtualbox.
+    (wayland is behaving badly with vbox dynamic resizing)
+    (LP: #1724810)
+
+ -- Gianfranco Costamagna <locutusofborg@debian.org>  Tue, 31 Oct 2017 08:55:10 +0100
+
+gnome-shell (3.26.1-0ubuntu5) artful; urgency=medium
+
+  [ Didier Roche ]
+  * Reenable headless patches and cherry-pick now that the gdm fix
+    is in mutter. (LP: #1725153)
+  * Add dep on latest mutter to ensure people don't end up in a non working
+    UI state again.
+
+  [ Jeremy Bicha ]
+  * Restore dependency on gir1.2-gdm-1.0 (LP: #1725288)
+
+ -- Didier Roche <didrocks@ubuntu.com>  Thu, 19 Oct 2017 08:38:31 +0200
+
+gnome-shell (3.26.1-0ubuntu4) artful; urgency=medium
+
+  * revert_023b50e7a7002226d6176bede22930d96da074a9.patch,
+    revert_5c37facc083078564faaeec4aa58084857c56ee1.patch,
+    and comment git_layout-unset-when-headless.patch:
+    - Revert 2 upstream commits and cherry-pick to remove headless mode
+      which shadows Shell crashing and thus fallback to Xorg, resulting in
+      some user configs not having any UI on startup. (LP: #1723577)
+
+ -- Didier Roche <didrocks@ubuntu.com>  Wed, 18 Oct 2017 15:05:32 +0200
+
+gnome-shell (3.26.1-0ubuntu3) artful; urgency=medium
+
+  * Cherry-pick git_layout-unset-when-headless.patch from gnome-3-26
+    branch to fix crash bug (LP: #1717170)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 13 Oct 2017 16:10:59 -0400
+
+gnome-shell (3.26.1-0ubuntu2) artful; urgency=medium
+
+  [ Andrea Azzarone ]
+  * debian/patches/workaround_crasher_fractional_scaling.patch:
+    workaround a popular crash for now when fractional scaling is enabled.
+    Andrea is working on a proper fix/debug (LP: #1714542)
+
+
+  [ Didier Roche ]
+  * debian/patches/fix_reset_initial_focus.patch:
+    - cherry-pick fixed proposed by upstream to avoid regression when keyboard
+      is used the second time a modal dialog is drawn.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Tue, 10 Oct 2017 10:09:56 +0200
+
+gnome-shell (3.26.1-0ubuntu1) artful; urgency=medium
+
+  * New upstream release
+  * debian/ubuntu-session-mods/ubuntu.css: workaround font blurryness
+    under wayland by providing a slight stroke around the font matching
+    windows title bar. (LP: #1714459)
+  * Remove 72_consistent_alt_key_above_tab_behavior.patch: upstreamed
+  * Remove run-unit-tests.patch: was cherry-picked
+
+ -- Didier Roche <didrocks@ubuntu.com>  Fri, 06 Oct 2017 09:36:54 +0200
+
 gnome-shell (3.26.0-2) experimental; urgency=medium
 
   * Team upload
@@ -2708,6 +7526,20 @@ gnome-shell (3.26.0-1) experimental; urg
 
  -- Simon McVittie <smcv@debian.org>  Wed, 13 Sep 2017 09:43:11 +0100
 
+gnome-shell (3.26.0-0ubuntu2) artful; urgency=medium
+
+  * fix typo in comment for ubuntu.css
+  * Make St checkbox orange instead of blue, as other icons in our theme.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Wed, 20 Sep 2017 11:57:56 +0200
+
+gnome-shell (3.26.0-0ubuntu1) artful; urgency=medium
+
+  * New upstream release
+  * Dep on newer mutter.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Fri, 15 Sep 2017 07:51:56 +0200
+
 gnome-shell (3.25.91-4) experimental; urgency=medium
 
   * debian/control.in:
@@ -2766,6 +7598,349 @@ gnome-shell (3.25.91-1) experimental; ur
 
  -- Simon McVittie <smcv@debian.org>  Thu, 31 Aug 2017 12:24:23 +0100
 
+gnome-shell (3.25.91-0ubuntu5) artful; urgency=medium
+
+  Changes for ubuntu session:
+  * 71_smarter_alt_tab.patch
+    - quick alt-tab (without showing up the switcher) switch only between
+      the last window of the last 2 applications to be focused instead of
+      raising all windows of those apps. (LP: #1716921)
+  * 72_consistent_alt_key_above_tab_behavior.patch:
+    - ensure alt + key above tab, when the switcher is opened, focus the first
+      window and not directly the second one of an application. This is
+      relevant as the select window part of the switcher is only raising the
+      selected window, not the whole application. (LP: #1716872)
+
+ -- Didier Roche <didrocks@ubuntu.com>  Tue, 12 Sep 2017 09:40:45 +0200
+
+gnome-shell (3.25.91-0ubuntu4) artful; urgency=medium
+
+  [ Jeremy Soller ]
+  * Change path of gdm3 stylesheet (LP: #1715722)
+  * Add alternatives for gdm3 stylesheet, setting ubuntu.css as the default
+
+  [ Didier Roche ]
+  * debian/gnome-shell-common.install:
+    Install upstream theme for gdm if vanilla gnome theme is selected.
+
+ -- Jeremy Soller <jeremy@system76.com>  Fri, 08 Sep 2017 10:12:58 -0600
+
+gnome-shell (3.25.91-0ubuntu3) artful; urgency=medium
+
+  * Ship new ubuntu css theme enabled in the ubuntu session.
+    We don't generate is from a regexp list or patch as it has complex
+    data structure (removal of some parts of stenzas, same source color
+    mapping to different destination colors depending as corresponding
+    to different elements).
+    Right now, we prefer to do a 3-way merge of the theme and look working
+    upstream for better theming support.
+    Some summary of the changes are:
+    - use ubuntu colors for panel, and most graphical elements (dialog box,
+      sliders, menu items, buttons highlights).
+    - remove rounded corners as they don't play well with nautilus showing up
+      desktop and non black panel theme.
+    - change blue grey scheme by orange grey (LP: #1690966)
+    - change gdm assets and background. Theme it aubergine instead of
+      a wallpaper as it matches the boot sequence, and can't be easily changed
+      by the user.
+    - ensure lock screen matches gdm and shell theming.
+    Make the theme maintainable still overriding on purpose some properties
+    instead of changing them in context.
+  * Theme new assets on build (close and toggle buttons)
+  * Add upstream high contrast theme to ubuntu mode. This will default to
+    adwaita with upstream colors for accessibility.
+  * debian/patches/50_add_ubuntu_desktop_detect.patch:
+    - add caching for desktop detection to avoid querying the current desktop
+      env variable as iterate through the list each time. For the time of the
+      Shell process, we can expect this env variable to stay stable.
+  * debian/patches/ubuntu_background_login.patch:
+    - Change default background color as we modified the default GDM color
+      for our ubuntu session. Change it as well here, still applying the
+      background noise loading.
+      This screen is appearing for a slip second when the Shell is loading.
+  * debian/rules:
+    - Use ninja to generate translations. Thanks Jeremy.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Mon, 04 Sep 2017 09:24:21 +0200
+
+gnome-shell (3.25.91-0ubuntu2) artful; urgency=medium
+
+  * debian/rules:
+    - Don't use variable when passing libexecdir for meson build
+    - Change --list-missing to --fail-missing to catch this kind of problem
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 01 Sep 2017 10:38:12 -0400
+
+gnome-shell (3.25.91-0ubuntu1) artful; urgency=medium
+
+  [ Jeremy Bicha ]
+  * New upstream release (LP: #1712800)
+  * Build with meson
+  * debian/control.in:
+    - Build-depend on libasound2-dev
+    - Bump minimum libglib2.0-dev to 2.53.0
+    - Update dependencies for mutter 3.25.90
+  * debian/rules:
+    - Drop disable-new-dtags flag, no longer needed
+    - Drop dh_girepository override, doesn't work with meson
+      and apparently not needed
+  * Refresh patches
+  * Drop git patches applied in new release
+  * Add run-unit-tests.patch: Run unit tests with meson
+
+  [ Tim Lunn ]
+  * New upstream release
+  * debian/control.in
+    - Update dependencies for mutter 3.25.91
+  * debian/patches:
+    - ubuntu_lock_on_suspend.patch: Rebased on changes introduced
+      in commit 9c3b3320f
+    - optional-libgdm.patch: disabled, this should not be required
+      since Ubuntu is using gdm
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Wed, 30 Aug 2017 14:46:42 -0400
+
+gnome-shell (3.24.3-0ubuntu7) artful; urgency=medium
+
+  * Add appindicator extension enabled by default in the ubuntu session
+
+ -- Didier Roche <didrocks@ubuntu.com>  Wed, 23 Aug 2017 08:13:25 +0200
+
+gnome-shell (3.24.3-0ubuntu6) artful; urgency=medium
+
+  * debian/rules: Ignore unit test failures on armhf
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 21 Aug 2017 11:46:36 -0400
+
+gnome-shell (3.24.3-0ubuntu5) artful; urgency=medium
+
+  * Rebuild against gjs 1.49 / mozjs52
+  * Backport patches from GNOME 3.26 to fix errors with new gjs/mozjs
+    - git_define-constant-before-using.patch
+    - git_define-properties-with-var.patch
+    - git_messageTray-use-spread-syntax.patch
+    - git_workspace-use-haspointer.patch
+  * Backport patch to fix some warnings with new gjs/mozjs
+    - git_define-classes-with-var.patch
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 21 Aug 2017 10:19:38 -0400
+
+gnome-shell (3.24.3-0ubuntu4) artful; urgency=medium
+
+  * Enable ubuntu-dock in ubuntu mode.
+  * debian/patches/optional-hot-corner.patch:
+    - enable patch proposed by upstream developer already in package
+      (but not in series) to add a settings for optional hot corner
+      activation.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Fri, 18 Aug 2017 08:22:29 +0200
+
+gnome-shell (3.24.3-0ubuntu3) artful; urgency=medium
+
+  * Add patch to allow setting volume above 100%:
+    - debian/patches/70_allow_sound_above_100.patch, which allows the volume
+      slider in the aggregatemenu to reflect and set correct current volume
+      position. (LP: #1706524)
+  * Add debian/patches/50_add_ubuntu_desktop_detect.patch to centralize
+    current desktop detection.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Wed, 16 Aug 2017 08:29:17 +0200
+
+gnome-shell (3.24.3-0ubuntu2) artful; urgency=medium
+
+  * Ship ubuntu GNOME Shell mode which will be a slightely modified
+    mode compared to upstream vanilla GNOME Shell one:
+    reference a cutomized ubuntu.css, with some based rules changes
+    from the upstream one, like default font, no symbolic icon in the
+    appmenu for coherence with other set of icons not having symbolic ones.
+    We use regexp rules based on the source shell css, so that it's always
+    in sync with shell theming on any update. (LP: #1698795)
+  * Dropped debian/patches/ubuntu_font.patch, not needed anymore as we will
+    have our own theme.
+  * debian/patches/ubuntu_gdm.patch:
+    as gdm is system-wide and not session-wide, ensure gdm has an ubuntu
+    styling by default, not impacting the gnome user session though.
+  * Bump Standard-Version to latest
+  * Remove debian/gnome-shell.gsettings-override, as this is now in
+    ubuntu-settings, and we will have per-session overrides there.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Mon, 14 Aug 2017 09:03:41 +0200
+
+gnome-shell (3.24.3-0ubuntu1) artful; urgency=medium
+
+  * New upstream release
+  * Drop fix-markup-test.patch: Applied in new release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Sun, 23 Jul 2017 13:08:04 -0400
+
+gnome-shell (3.24.2-0ubuntu7) artful; urgency=medium
+
+  [ Robert Ancell ]
+  * debian/control:
+    - Set Vcs-Bzr
+
+  [ Didier Roche ]
+  * debian/control*:
+    - Recommends ubuntu-session | gnome-session to ensure people have one
+      session installed if they install the Shell rather than depends on
+      gnome-session.
+
+ -- Didier Roche <didrocks@ubuntu.com>  Tue, 20 Jun 2017 11:59:13 +0200
+
+gnome-shell (3.24.2-0ubuntu6) artful; urgency=medium
+
+  * debian/patches/optional-libgdm.patch:
+    - Handle libgdm gsettings schema not being installed (LP: #1695212)
+
+ -- Robert Ancell <robert.ancell@canonical.com>  Tue, 06 Jun 2017 20:30:53 +1200
+
+gnome-shell (3.24.2-0ubuntu5) artful; urgency=medium
+
+  * debian/control:
+    - Make gir1.2-gdm-1.0 a suggests so it doesn't have to be in main
+  * debian/patches/optional-libgdm.patch:
+    - Handle libgdm not being installed
+
+ -- Robert Ancell <robert.ancell@canonical.com>  Thu, 01 Jun 2017 21:23:51 +1200
+
+gnome-shell (3.24.2-0ubuntu4) artful; urgency=medium
+
+  * Have dh_auto_test run the unit tests
+  * depend/control.in:
+    - Build-depend on packages needed for the tests
+  * Add debian/patches/fix-markup-test.patch:
+    - Update one of the unit tests
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 18 May 2017 15:26:41 -0400
+
+gnome-shell (3.24.2-0ubuntu3) artful; urgency=medium
+
+  * Extend ubuntu-lightdm-user-switching.patch to allow
+    locking screen using LightDM (LP: #1684205)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Sun, 14 May 2017 21:42:39 -0400
+
+gnome-shell (3.24.2-0ubuntu2) artful; urgency=medium
+
+  * Depend on caribou (LP: #1589240)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Sat, 13 May 2017 17:35:48 -0400
+
+gnome-shell (3.24.2-0ubuntu1) artful; urgency=medium
+
+  * New upstream release (LP: #1690182)
+  * debian/control.in:
+    - Restore iio-sensor-proxy to Recommends since it was already in main
+    - Recommend gnome-user-guide again too
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 28 Apr 2017 13:29:39 -0400
+
+gnome-shell (3.24.1-0ubuntu3) artful; urgency=medium
+
+  * Add ubuntu_font.patch:
+    - Use Ubuntu font
+  * debian/rules, debian/control.in:
+    - Don't depend on adwaita-icon-theme-full
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 27 Apr 2017 08:32:39 -0400
+
+gnome-shell (3.24.1-0ubuntu2) artful; urgency=medium
+
+  [ Ken VanDine ]
+  * debian/control.in:
+    - Suggest switcheroo-control & chrome-gnome-shell instead of recommend
+    - Dropped gnome-contacts from recommends, let's use the seed to select
+      apps
+
+  [ Simon McVittie]
+  * Drop Telepathy g-i bindings from Depends to Suggests now that they
+    are runtime-optional upstream
+
+  [ Jeremy Bicha ]
+  * Lower more depends and recommends to suggests to smooth Main inclusion:
+    - adwaita-icon-theme-full
+    - gnome-themes-standard-data
+    - gnome-user-guide
+    - iio-sensor-proxy
+    - gdm3
+  * Replace gnome-backgrounds dependency with ubuntu-wallpapers
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 25 Apr 2017 22:01:55 -0400
+
+gnome-shell (3.24.1-0ubuntu1) zesty; urgency=medium
+
+  * New upstream release (LP: #1682238)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Wed, 12 Apr 2017 17:40:02 -0400
+
+gnome-shell (3.24.0-0ubuntu2) zesty; urgency=medium
+
+  * Recommend switcheroo-control for dual GPU support (LP: #1671828)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 23 Mar 2017 21:46:25 -0400
+
+gnome-shell (3.24.0-0ubuntu1) zesty; urgency=medium
+
+  * New upstream translations release.
+  * Bump minimum mutter to 3.24.0
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 20 Mar 2017 20:07:54 -0400
+
+gnome-shell (3.23.92-0ubuntu1) zesty; urgency=medium
+
+  * New upstream release
+  * debian/control.in:
+    - Drop obsolete Build-Depends against telepathy-glib
+    - Bump minimum mutter to 3.23.92
+  * Drop git patches applied in new release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Tue, 14 Mar 2017 15:28:09 -0400
+
+gnome-shell (3.23.91-0ubuntu4) zesty; urgency=medium
+
+  * debian/control.in:
+    - Recommend chrome-gnome-shell
+    - Recommend xserver-xorg-legacy until all the proprietary NVIDIA
+      drivers depend on it (LP: #1559576)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 10 Mar 2017 20:08:00 -0500
+
+gnome-shell (3.23.91-0ubuntu3) zesty; urgency=medium
+
+  * debian/control.in:
+    - Depend on gir1.2-geoclue-2.0 (LP: #1671823)
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Fri, 10 Mar 2017 09:19:54 -0500
+
+gnome-shell (3.23.91-0ubuntu2) zesty; urgency=medium
+
+  * Add patches to backport some commits from git:
+    - git_fix_typos.patch
+    - git_fix_telepathy_notifications.patch
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Thu, 02 Mar 2017 15:50:41 -0500
+
+gnome-shell (3.23.91-0ubuntu1) zesty; urgency=medium
+
+  * Merge with Debian unstable. Remaining changes:
+    - debian/control.in:
+      + Update mutter dependencies
+      + Depend on gir1.2-rsvg-2.0
+      + Bump minimum gjs to 1.47.90
+      + Depend on gnome-session which due to package
+        split now contains only the session files
+    - debian/patches/ubuntu-lightdm-user-switching.patch:
+      + Allow user switching when using LightDM.
+    - debian/patches/ubuntu_lock_on_suspend.patch
+      + Respect Ubuntu's lock-on-suspend setting.
+    - debian/gnome-shell.gsettings-override:
+      + Replace firefox-esr with firefox
+    - Don't install the 3.14 process-working file since
+      we already did the 3.14 upgrade for 16.04 LTS
+  * New upstream release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Wed, 01 Mar 2017 13:06:59 -0500
+
 gnome-shell (3.22.3-3) unstable; urgency=medium
 
   * debian/patches/50-extension-reload-fail.patch:
diff -pruN 49.0-1/debian/control 49.0-1ubuntu1/debian/control
--- 49.0-1/debian/control	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/control	2025-09-24 16:23:55.000000000 +0000
@@ -1,7 +1,8 @@
 Source: gnome-shell
 Section: gnome
 Priority: optional
-Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
 Uploaders: Jeremy Bícha <jbicha@ubuntu.com>, Marco Trevisan (Treviño) <marco@ubuntu.com>
 Build-Depends: appstream,
                bash-completion,
@@ -64,14 +65,17 @@ Build-Depends: appstream,
                python3-docutils,
                sassc,
                systemd-dev [linux-any],
+               terser,
                xauth <!nocheck>,
                xvfb <!nocheck>,
                xwayland <!nocheck>
 Rules-Requires-Root: no
 Standards-Version: 4.7.2
 Homepage: https://gitlab.gnome.org/GNOME/gnome-shell
-Vcs-Git: https://salsa.debian.org/gnome-team/gnome-shell.git
-Vcs-Browser: https://salsa.debian.org/gnome-team/gnome-shell
+XS-Debian-Vcs-Git: https://salsa.debian.org/gnome-team/gnome-shell.git
+XS-Debian-Vcs-Browser: https://salsa.debian.org/gnome-team/gnome-shell
+Vcs-Git: https://salsa.debian.org/gnome-team/gnome-shell.git -b ubuntu/latest
+Vcs-Browser: https://salsa.debian.org/gnome-team/gnome-shell/tree/ubuntu/latest
 
 Package: gnome-shell
 Architecture: linux-any
@@ -111,13 +115,13 @@ Depends: gir1.2-accountsservice-1.0,
          libglib2.0-bin (>= 2.86.0),
          python3,
          tecla,
+         ubuntu-wallpapers,
          ${gir:Depends},
          ${misc:Depends},
          ${shlibs:Depends}
 Recommends: bolt,
             evolution-data-server (>= 3.45),
             gdm3,
-            gnome-browser-connector,
             gnome-remote-desktop,
             gnome-menus,
             gnome-user-docs,
@@ -125,10 +129,12 @@ Recommends: bolt,
             iio-sensor-proxy,
             power-profiles-daemon,
             switcheroo-control,
+            ubuntu-session | gnome-session,
             unzip
 Suggests: gir1.2-malcontent-0 [linux-any],
           gir1.2-telepathyglib-0.12,
           gir1.2-telepathylogger-0.2,
+          gnome-browser-connector,
           gnome-shell-extension-prefs,
 Breaks: gnome-shell-extension-appindicator (<< 50),
         gnome-shell-extension-dash-to-panel (<< 55),
@@ -140,6 +146,8 @@ Breaks: gnome-shell-extension-appindicat
         gnome-shell-extension-workspaces-to-dock (<< 54~),
         gnome-shell-extensions (<< 40.0~),
         gnome-session (<< 49~beta~),
+        ubuntu-session (<< 49~beta~),
+        authd (<< 0.4.2~),
 Provides: notification-daemon,
           polkit-1-auth-agent
 Description: graphical shell for the GNOME desktop
@@ -153,6 +161,7 @@ Description: graphical shell for the GNO
 Package: gnome-shell-common
 Architecture: all
 Depends: ${misc:Depends}
+Recommends: yaru-theme-gnome-shell (>= 25.04)
 Description: common files for the GNOME graphical shell
  This package contains translations and data files for the GNOME shell.
 
diff -pruN 49.0-1/debian/gbp.conf 49.0-1ubuntu1/debian/gbp.conf
--- 49.0-1/debian/gbp.conf	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/gbp.conf	2025-09-24 16:23:55.000000000 +0000
@@ -1,6 +1,7 @@
 [DEFAULT]
 pristine-tar = True
-debian-branch = debian/latest
+debian-branch = ubuntu/latest
+debian-tag=ubuntu/%(version)s
 upstream-branch = upstream/latest
 
 [buildpackage]
diff -pruN 49.0-1/debian/gnome-shell-common.alternatives 49.0-1ubuntu1/debian/gnome-shell-common.alternatives
--- 49.0-1/debian/gnome-shell-common.alternatives	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/gnome-shell-common.alternatives	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,4 @@
+Name: gdm-theme.gresource
+Link: /usr/share/gnome-shell/gdm-theme.gresource
+Alternative: /usr/share/gnome-shell/gnome-shell-theme.gresource
+Priority: 10
diff -pruN 49.0-1/debian/gnome-shell-common.install 49.0-1ubuntu1/debian/gnome-shell-common.install
--- 49.0-1/debian/gnome-shell-common.install	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/gnome-shell-common.install	2025-09-24 16:23:55.000000000 +0000
@@ -7,3 +7,4 @@ usr/share/gnome-control-center
 usr/share/gnome-shell
 usr/share/icons/hicolor/*/apps/*
 usr/share/locale
+debian/ubuntu-session-mods/ubuntu.json usr/share/gnome-shell/modes/
diff -pruN 49.0-1/debian/patches/global-make-possible-to-set-debug-flags-dynamically.patch 49.0-1ubuntu1/debian/patches/global-make-possible-to-set-debug-flags-dynamically.patch
--- 49.0-1/debian/patches/global-make-possible-to-set-debug-flags-dynamically.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/global-make-possible-to-set-debug-flags-dynamically.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,247 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Tue, 24 Oct 2017 02:15:13 +0200
+Subject: global: make possible to set debug-flags dynamically
+
+Adding {set,get}_debug_flags functions to the shell global object to
+make possible to set this easily from looking class, making it easier
+for developers and users to debug without having to restart the shell
+with environment variables.
+
+Debug flags in main are updated when the "debug-flags" property is
+changed. I'm keeping this as a string-list so that it's easier to update.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=789377
+
+Bug-GNOME: https://bugzilla.gnome.org/show_bug.cgi?id=789377
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/6
+---
+ src/main.c         | 61 +++++++++++++++++++++++++++++++++++++++++-------------
+ src/shell-global.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/shell-global.h |  4 ++++
+ 3 files changed, 103 insertions(+), 14 deletions(-)
+
+diff --git a/src/main.c b/src/main.c
+index ee86171..d0529b5 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -360,14 +360,14 @@ shell_a11y_init (void)
+ }
+ 
+ static void
+-shell_init_debug (const char *debug_env)
++shell_update_debug (const char *debug_string)
+ {
+   static const GDebugKey keys[] = {
+     { "backtrace-warnings", SHELL_DEBUG_BACKTRACE_WARNINGS },
+     { "backtrace-segfaults", SHELL_DEBUG_BACKTRACE_SEGFAULTS },
+   };
+ 
+-  _shell_debug = g_parse_debug_string (debug_env, keys,
++  _shell_debug = g_parse_debug_string (debug_string, keys,
+                                        G_N_ELEMENTS (keys));
+ }
+ 
+@@ -469,6 +469,42 @@ dump_gjs_stack_on_signal (int signo)
+   _tracked_signals[signo] = TRUE;
+ }
+ 
++static void
++reset_signal_handler_to_default (int signo)
++{
++  signal (signo, SIG_DFL);
++  _tracked_signals[signo] = FALSE;
++}
++
++static void
++setup_debug_signal_listners (void)
++{
++  dump_gjs_stack_on_signal (SIGABRT);
++  dump_gjs_stack_on_signal (SIGFPE);
++  dump_gjs_stack_on_signal (SIGIOT);
++  dump_gjs_stack_on_signal (SIGTRAP);
++
++  if ((_shell_debug & SHELL_DEBUG_BACKTRACE_SEGFAULTS))
++    {
++      dump_gjs_stack_on_signal (SIGBUS);
++      dump_gjs_stack_on_signal (SIGSEGV);
++    }
++  else
++    {
++      reset_signal_handler_to_default (SIGBUS);
++      reset_signal_handler_to_default (SIGSEGV);
++    }
++}
++
++static void
++global_notify_debug_flags (GObject    *gobject,
++                           GParamSpec *pspec,
++                           gpointer    data)
++{
++  shell_update_debug (shell_global_get_debug_flags (shell_global_get ()));
++  setup_debug_signal_listners ();
++}
++
+ static gboolean
+ list_modes (const char  *option_name,
+             const char  *value,
+@@ -601,6 +637,7 @@ main (int argc, char **argv)
+   g_autoptr (GFile) automation_script = NULL;
+   g_autofree char *cwd = NULL;
+   GError *error = NULL;
++  const char *debug_flags;
+   int ecode = EXIT_SUCCESS;
+ 
+   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+@@ -636,17 +673,6 @@ main (int argc, char **argv)
+   if (session_mode == NULL)
+     session_mode = is_gdm_mode ? (char *)"gdm" : (char *)"user";
+ 
+-  dump_gjs_stack_on_signal (SIGABRT);
+-  dump_gjs_stack_on_signal (SIGFPE);
+-  dump_gjs_stack_on_signal (SIGIOT);
+-  dump_gjs_stack_on_signal (SIGTRAP);
+-
+-  if ((_shell_debug & SHELL_DEBUG_BACKTRACE_SEGFAULTS))
+-    {
+-      dump_gjs_stack_on_signal (SIGBUS);
+-      dump_gjs_stack_on_signal (SIGSEGV);
+-    }
+-
+   if (script_path)
+     automation_script = g_file_new_for_commandline_arg_and_cwd (script_path, cwd);
+ 
+@@ -655,10 +681,16 @@ main (int argc, char **argv)
+    * resolve internal modules.
+    */
+   _shell_global_init ("session-mode", session_mode,
++                      "debug-flags", debug_flags,
+                       "force-animations", force_animations,
+                       "automation-script", automation_script,
+                       NULL);
+ 
++  g_signal_connect (shell_global_get (), "notify::debug-flags",
++                    G_CALLBACK (global_notify_debug_flags), NULL);
++
++  setup_debug_signal_listners ();
++
+   /* Setup Meta _after_ the Shell global to avoid GjsContext
+    * iterating on the main loop once Meta starts adding events
+    */
+@@ -668,7 +700,8 @@ main (int argc, char **argv)
+       return EXIT_FAILURE;
+     }
+ 
+-  shell_init_debug (g_getenv ("SHELL_DEBUG"));
++  debug_flags = g_getenv ("SHELL_DEBUG");
++  shell_update_debug (debug_flags);
+ 
+   shell_dbus_init (meta_context_is_replacing (context));
+   shell_a11y_init ();
+diff --git a/src/shell-global.c b/src/shell-global.c
+index 9a6de28..8a3fbce 100644
+--- a/src/shell-global.c
++++ b/src/shell-global.c
+@@ -61,6 +61,7 @@ struct _ShellGlobal {
+   MetaWorkspaceManager *workspace_manager;
+ 
+   char *session_mode;
++  char *debug_flags;
+ 
+   GjsContext *js_context;
+   MetaPlugin *plugin;
+@@ -119,6 +120,7 @@ enum {
+   PROP_SWITCHEROO_CONTROL,
+   PROP_FORCE_ANIMATIONS,
+   PROP_AUTOMATION_SCRIPT,
++  PROP_DEBUG_FLAGS,
+ 
+   N_PROPS
+ };
+@@ -235,6 +237,9 @@ shell_global_set_property(GObject         *object,
+     case PROP_AUTOMATION_SCRIPT:
+       g_set_object (&global->automation_script, g_value_get_object (value));
+       break;
++    case PROP_DEBUG_FLAGS:
++      shell_global_set_debug_flags (global, g_value_get_string (value));
++      break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       break;
+@@ -315,6 +320,9 @@ shell_global_get_property(GObject         *object,
+     case PROP_AUTOMATION_SCRIPT:
+       g_value_set_object (value, global->automation_script);
+       break;
++    case PROP_DEBUG_FLAGS:
++      g_value_set_string (value, global->debug_flags);
++      break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       break;
+@@ -623,6 +631,13 @@ shell_global_class_init (ShellGlobalClass *klass)
+                          G_TYPE_FILE,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ 
++  props[PROP_DEBUG_FLAGS] =
++    g_param_spec_string ("debug-flags",
++                         "Debug Flags",
++                         "The debugging flags",
++                         NULL,
++                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
++
+   g_object_class_install_properties (gobject_class, N_PROPS, props);
+ }
+ 
+@@ -2008,3 +2023,40 @@ shell_global_set_frame_finish_timestamp (ShellGlobal *global,
+       g_object_notify_by_pspec (G_OBJECT (global), props[PROP_FRAME_FINISH_TIMESTAMP]);
+     }
+ }
++
++/**
++ * shell_global_get_debug_flags:
++ * @global: a #ShellGlobal
++ *
++ * Returns: (transfer none): The current debug flags
++ */
++const gchar *
++shell_global_get_debug_flags (ShellGlobal *global)
++{
++  g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
++
++  return global->debug_flags;
++}
++
++/**
++ * shell_global_set_debug_flags:
++ * @global: a #ShellGlobal
++ * @debug_flags: (nullable): A string for debugging flags
++ *
++ * Updates the debugging flags at runtime as the one set using the SHELL_DEBUG
++ * environment variables. Currently we support 'backtrace-warnings' and
++ * 'backtrace-segfaults' keys.
++ */
++void
++shell_global_set_debug_flags (ShellGlobal  *global,
++                              const char   *debug_flags)
++{
++  g_return_if_fail (SHELL_IS_GLOBAL (global));
++
++  if (g_strcmp0 (global->debug_flags, debug_flags) != 0)
++    {
++      g_free (global->debug_flags);
++      global->debug_flags = g_strdup (debug_flags);
++      g_object_notify (G_OBJECT (global), "debug-flags");
++    }
++}
+diff --git a/src/shell-global.h b/src/shell-global.h
+index ef07996..330e0ff 100644
+--- a/src/shell-global.h
++++ b/src/shell-global.h
+@@ -125,4 +125,8 @@ gboolean shell_global_get_frame_finish_timestamp (ShellGlobal *global);
+ void shell_global_set_frame_finish_timestamp (ShellGlobal *global,
+                                               gboolean     enable);
+ 
++const char * shell_global_get_debug_flags       (ShellGlobal  *global);
++void         shell_global_set_debug_flags       (ShellGlobal  *global,
++                                                 const char   *debug_flags);
++
+ G_END_DECLS
diff -pruN 49.0-1/debian/patches/main-add-backtrace-crashes-all-and-backtrace-all.patch 49.0-1ubuntu1/debian/patches/main-add-backtrace-crashes-all-and-backtrace-all.patch
--- 49.0-1/debian/patches/main-add-backtrace-crashes-all-and-backtrace-all.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/main-add-backtrace-crashes-all-and-backtrace-all.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,47 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 2 Aug 2018 16:17:39 +0200
+Subject: main: add `backtrace-crashes-all` and `backtrace-all`
+
+These are just convenient aliases to not to have to list all the types,
+as having more granularity is cool, but it might also cause some annoyance.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=789377
+
+Bug-GNOME: https://bugzilla.gnome.org/show_bug.cgi?id=789377
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/6
+---
+ src/main.c         | 7 +++++++
+ src/shell-global.c | 2 ++
+ 2 files changed, 9 insertions(+)
+
+diff --git a/src/main.c b/src/main.c
+index 9cd5948..e58cb85 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -58,6 +58,13 @@ static const GDebugKey SHELL_DEBUG_KEYS[] = {
+   { "backtrace-segfaults",   SHELL_DEBUG_BACKTRACE_SEGFAULTS },
+   { "backtrace-aborts",      SHELL_DEBUG_BACKTRACE_ABORTS },
+   { "backtrace-math-errors", SHELL_DEBUG_BACKTRACE_FPE },
++  { "backtrace-crashes-all", SHELL_DEBUG_BACKTRACE_SEGFAULTS |
++                             SHELL_DEBUG_BACKTRACE_ABORTS |
++                             SHELL_DEBUG_BACKTRACE_FPE },
++  { "backtrace-all",         SHELL_DEBUG_BACKTRACE_WARNINGS |
++                             SHELL_DEBUG_BACKTRACE_SEGFAULTS |
++                             SHELL_DEBUG_BACKTRACE_ABORTS |
++                             SHELL_DEBUG_BACKTRACE_FPE },
+ };
+ static int _default_debug_flags = SHELL_DEBUG_BACKTRACE_ABORTS |
+                                   SHELL_DEBUG_BACKTRACE_FPE;
+diff --git a/src/shell-global.c b/src/shell-global.c
+index 8e08e56..b2ed81b 100644
+--- a/src/shell-global.c
++++ b/src/shell-global.c
+@@ -2049,6 +2049,8 @@ shell_global_get_debug_flags (ShellGlobal *global)
+  *  - 'backtrace-segfaults'
+  *  - 'backtrace-aborts'
+  *  - 'backtrace-math-errors'
++ *  - 'backtrace-crashes-all'
++ *  - 'backtrace-all'
+  *  - 'all'
+  */
+ void
diff -pruN 49.0-1/debian/patches/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch 49.0-1ubuntu1/debian/patches/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch
--- 49.0-1/debian/patches/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,169 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Tue, 24 Oct 2017 03:20:34 +0200
+Subject: main: increase the granularity of backtraces in SHELL_DEBUG
+
+Add support for multiple debug-keys for getting the backtraces,
+allowing more control using both SHELL_DEBUG and/or set_debug_flags
+
+https://bugzilla.gnome.org/show_bug.cgi?id=789377
+
+Bug-GNOME: https://bugzilla.gnome.org/show_bug.cgi?id=789377
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/6
+---
+ src/main.c         | 76 +++++++++++++++++++++++++++++++++++++++++-------------
+ src/shell-global.c | 10 ++++---
+ 2 files changed, 65 insertions(+), 21 deletions(-)
+
+diff --git a/src/main.c b/src/main.c
+index d0529b5..9cd5948 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -42,16 +42,26 @@ static char *session_mode = NULL;
+ static int caught_signal = 0;
+ static gboolean force_animations = FALSE;
+ static char *script_path = NULL;
++static gboolean _tracked_signals[NSIG] = { 0 };
+ 
+ #define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
+ #define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
+ 
+ enum {
+-  SHELL_DEBUG_BACKTRACE_WARNINGS = 1,
+-  SHELL_DEBUG_BACKTRACE_SEGFAULTS = 2,
++  SHELL_DEBUG_BACKTRACE_WARNINGS  = (1 << 0),
++  SHELL_DEBUG_BACKTRACE_SEGFAULTS = (1 << 1),
++  SHELL_DEBUG_BACKTRACE_ABORTS    = (1 << 2),
++  SHELL_DEBUG_BACKTRACE_FPE       = (1 << 3),
+ };
+-static int _shell_debug;
+-static gboolean _tracked_signals[NSIG] = { 0 };
++static const GDebugKey SHELL_DEBUG_KEYS[] = {
++  { "backtrace-warnings",    SHELL_DEBUG_BACKTRACE_WARNINGS },
++  { "backtrace-segfaults",   SHELL_DEBUG_BACKTRACE_SEGFAULTS },
++  { "backtrace-aborts",      SHELL_DEBUG_BACKTRACE_ABORTS },
++  { "backtrace-math-errors", SHELL_DEBUG_BACKTRACE_FPE },
++};
++static int _default_debug_flags = SHELL_DEBUG_BACKTRACE_ABORTS |
++                                  SHELL_DEBUG_BACKTRACE_FPE;
++static int _shell_debug = 0;
+ 
+ static void
+ shell_dbus_acquire_name (GDBusProxy  *bus,
+@@ -362,13 +372,23 @@ shell_a11y_init (void)
+ static void
+ shell_update_debug (const char *debug_string)
+ {
+-  static const GDebugKey keys[] = {
+-    { "backtrace-warnings", SHELL_DEBUG_BACKTRACE_WARNINGS },
+-    { "backtrace-segfaults", SHELL_DEBUG_BACKTRACE_SEGFAULTS },
+-  };
++  _shell_debug = g_parse_debug_string (debug_string, SHELL_DEBUG_KEYS,
++                                       G_N_ELEMENTS (SHELL_DEBUG_KEYS));
++}
++
++static char *
++debug_flags_to_string (void)
++{
++  gsize i, j;
++  const char *enabled_flags[G_N_ELEMENTS (SHELL_DEBUG_KEYS) + 1] = { 0 };
++
++  for (i = 0, j = 0; i < G_N_ELEMENTS (SHELL_DEBUG_KEYS); ++i)
++    {
++      if ((_shell_debug & SHELL_DEBUG_KEYS[i].value))
++        enabled_flags[j++] = SHELL_DEBUG_KEYS[i].key;
++    }
+ 
+-  _shell_debug = g_parse_debug_string (debug_string, keys,
+-                                       G_N_ELEMENTS (keys));
++  return g_strjoinv (":", (char**) enabled_flags);
+ }
+ 
+ static GLogWriterOutput
+@@ -479,10 +499,23 @@ reset_signal_handler_to_default (int signo)
+ static void
+ setup_debug_signal_listners (void)
+ {
+-  dump_gjs_stack_on_signal (SIGABRT);
+-  dump_gjs_stack_on_signal (SIGFPE);
+-  dump_gjs_stack_on_signal (SIGIOT);
+-  dump_gjs_stack_on_signal (SIGTRAP);
++  if ((_shell_debug & SHELL_DEBUG_BACKTRACE_ABORTS))
++    {
++      dump_gjs_stack_on_signal (SIGABRT);
++      dump_gjs_stack_on_signal (SIGIOT);
++      dump_gjs_stack_on_signal (SIGTRAP);
++    }
++  else
++    {
++      reset_signal_handler_to_default (SIGABRT);
++      reset_signal_handler_to_default (SIGIOT);
++      reset_signal_handler_to_default (SIGTRAP);
++    }
++
++  if ((_shell_debug & SHELL_DEBUG_BACKTRACE_FPE))
++    dump_gjs_stack_on_signal (SIGFPE);
++  else
++    reset_signal_handler_to_default (SIGFPE);
+ 
+   if ((_shell_debug & SHELL_DEBUG_BACKTRACE_SEGFAULTS))
+     {
+@@ -636,8 +669,9 @@ main (int argc, char **argv)
+   g_autoptr (MetaContext) context = NULL;
+   g_autoptr (GFile) automation_script = NULL;
+   g_autofree char *cwd = NULL;
++  g_autofree char *debug_flags_string = NULL;
+   GError *error = NULL;
+-  const char *debug_flags;
++  const char *shell_debug;
+   int ecode = EXIT_SUCCESS;
+ 
+   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+@@ -681,7 +715,7 @@ main (int argc, char **argv)
+    * resolve internal modules.
+    */
+   _shell_global_init ("session-mode", session_mode,
+-                      "debug-flags", debug_flags,
++                      "debug-flags", debug_flags_string,
+                       "force-animations", force_animations,
+                       "automation-script", automation_script,
+                       NULL);
+@@ -700,8 +734,14 @@ main (int argc, char **argv)
+       return EXIT_FAILURE;
+     }
+ 
+-  debug_flags = g_getenv ("SHELL_DEBUG");
+-  shell_update_debug (debug_flags);
++  shell_debug = g_getenv ("SHELL_DEBUG");
++
++  if (shell_debug)
++    shell_update_debug (shell_debug);
++  else
++    _shell_debug = _default_debug_flags;
++
++  debug_flags_string = debug_flags_to_string ();
+ 
+   shell_dbus_init (meta_context_is_replacing (context));
+   shell_a11y_init ();
+diff --git a/src/shell-global.c b/src/shell-global.c
+index 8a3fbce..8e08e56 100644
+--- a/src/shell-global.c
++++ b/src/shell-global.c
+@@ -2041,11 +2041,15 @@ shell_global_get_debug_flags (ShellGlobal *global)
+ /**
+  * shell_global_set_debug_flags:
+  * @global: a #ShellGlobal
+- * @debug_flags: (nullable): A string for debugging flags
++ * @debug_flags: (nullable): A comma-separated string of debugging flags
+  *
+  * Updates the debugging flags at runtime as the one set using the SHELL_DEBUG
+- * environment variables. Currently we support 'backtrace-warnings' and
+- * 'backtrace-segfaults' keys.
++ * environment variables. Currently we support these keys:
++ *  - 'backtrace-warnings'
++ *  - 'backtrace-segfaults'
++ *  - 'backtrace-aborts'
++ *  - 'backtrace-math-errors'
++ *  - 'all'
+  */
+ void
+ shell_global_set_debug_flags (ShellGlobal  *global,
diff -pruN 49.0-1/debian/patches/main-show-an-error-message-on-gnome-shell-crash.patch 49.0-1ubuntu1/debian/patches/main-show-an-error-message-on-gnome-shell-crash.patch
--- 49.0-1/debian/patches/main-show-an-error-message-on-gnome-shell-crash.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/main-show-an-error-message-on-gnome-shell-crash.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,30 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 2 Aug 2018 15:59:20 +0200
+Subject: main: show an error message on gnome-shell crash
+
+When we call the crash signal handler, write on log the reason of the
+crash, also to make easier to parse the logs later on, and being able
+to understand if a stacktrace is coming from a crash or a different
+gjs error.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=789377
+
+Bug-GNOME: https://bugzilla.gnome.org/show_bug.cgi?id=789377
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/6
+---
+ src/main.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/main.c b/src/main.c
+index 99eca3e..ee86171 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -430,6 +430,8 @@ dump_gjs_stack_on_signal_handler (int signo)
+   struct sigaction sa = { .sa_handler = dump_gjs_stack_alarm_sigaction };
+   gsize i;
+ 
++  g_printerr ("GNOME Shell crashed with signal %d\n", signo);
++
+   /* Ignore all the signals starting this point, a part the one we'll raise
+    * (which is implicitly ignored here through SA_RESETHAND), this is needed
+    * not to get this handler being called by other signals that we were
diff -pruN 49.0-1/debian/patches/series 49.0-1ubuntu1/debian/patches/series
--- 49.0-1/debian/patches/series	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/series	2025-09-24 16:23:55.000000000 +0000
@@ -1,3 +1,60 @@
+slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch
 debian/Revert-tests-Fail-on-warnings-too.patch
-shell-app-Warn-instead-of-crashing-if-disposed-before-sta.patch
 debian/data-Add-im-config.desktop-to-System-folder.patch
+shell-app-Warn-instead-of-crashing-if-disposed-before-sta.patch
+ubuntu/desktop_detect.patch
+ubuntu/lightdm-user-switching.patch
+ubuntu/lock_on_suspend.patch
+ubuntu/background_login.patch
+ubuntu/gdm_alternatives.patch
+ubuntu/chromium-snap-pwa.patch
+main-show-an-error-message-on-gnome-shell-crash.patch
+global-make-possible-to-set-debug-flags-dynamically.patch
+main-increase-the-granularity-of-backtraces-in-SHELL_DEBU.patch
+main-add-backtrace-crashes-all-and-backtrace-all.patch
+sessionMode-add-support-for-debugFlags-parameter.patch
+ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
+ubuntu/resolve_alternate_theme_path.patch
+ubuntu/secure_mode_extension.patch
+ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
+ubuntu/configure_login_screen.patch
+ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch
+ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch
+ubuntu/main-Support-ubuntu-color-scheme-and-yaru-variants.patch
+ubuntu/st-them-context-Use-yaru-colors-as-accents.patch
+ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch
+ubuntu/st-Add-support-for-brown-accent-color.patch
+ubuntu/st-spinner-design-and-animation.patch
+ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
+ubuntu/appMenu-Show-details-menu-item-for-snap-applications.patch
+ubuntu/main-Add-support-for-playing-a-startup-sound-if-enabled-i.patch
+ubuntu-authd/authPrompt-Right-align-cancel-button.patch
+ubuntu-authd/unlockDialog-Support-request-type-of-REUSE_USERNAME.patch
+ubuntu-authd/authPrompt-Parameterize-reset-function.patch
+ubuntu-authd/loginDialog-Move-authPrompt.begin-to-helper-function.patch
+ubuntu-authd/gdm-Add-new-AuthMenuButton-control.patch
+ubuntu-authd/loginDialog-Port-sessions-menu-over-to-AuthMenuButton.patch
+ubuntu-authd/loginDialog-Add-Login-Options-button.patch
+ubuntu-authd/unlockDialog-Add-Login-Options-menu-button.patch
+ubuntu-authd/gdm-Add-support-unified-authentication.patch
+ubuntu-authd/gdm-util-Figure-out-default-service-from-service-definiti.patch
+ubuntu-authd/gdm-util-Emit-service-request-signal-on-every-request.patch
+ubuntu-authd/src-Add-function-for-generating-QR-codes.patch
+ubuntu-authd/gdm-Add-Login-Failed-notification.patch
+ubuntu-authd/authPrompt-Add-support-for-Web-Login.patch
+ubuntu-authd/authPrompt-Fade-out-cancel-button-after-verification.patch
+ubuntu-authd/gdm-util-Add-new-background-message-type.patch
+ubuntu-authd/gdm-util-Catch-errors-on-re-authentication.patch
+ubuntu-authd/shell-qr-code-generator-Use-g-c-c-embedded-library-simpli.patch
+ubuntu-authd/start-abstracting-the-unified-mechanism-handler.patch
+ubuntu-authd/gdm-loginDialog-Bind-login-option-button-visibility-to-me.patch
+ubuntu-authd/gdm-loginDialog-Fix-cancel-button-visibility.patch
+ubuntu-authd/gdm-Improve-detection-of-unavailable-services.patch
+ubuntu-authd/gdm-Support-more-authentication-mechanism-UIs.patch
+ubuntu-authd/gdm-webLogin-Support-custom-buttons-and-auto-ack.patch
+ubuntu-authd/gdm-webLogin-Improve-label-rendering.patch
+ubuntu-authd/Implement-authd-support-via-the-unified-mechanism.patch
+ubuntu-authd/js-Minify-the-authd-protocol-during-build.patch
+ubuntu-authd/js-gdm-util-Add-a-gsettings-to-disable-the-authd-in-gdm.patch
+ubuntu-authd/gdm-authPrompt-Fix-key-focus-handling-on-choice-list.patch
+ubuntu/extensions-Include-snapd-prompting-extension.patch
diff -pruN 49.0-1/debian/patches/sessionMode-add-support-for-debugFlags-parameter.patch 49.0-1ubuntu1/debian/patches/sessionMode-add-support-for-debugFlags-parameter.patch
--- 49.0-1/debian/patches/sessionMode-add-support-for-debugFlags-parameter.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/sessionMode-add-support-for-debugFlags-parameter.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,46 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 2 Aug 2018 16:05:13 +0200
+Subject: sessionMode: add support for `debugFlags` parameter
+
+A session can now define `debugFlags` from a json file, still leaving priority
+to the environment variable.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=789377
+
+Bug-GNOME: https://bugzilla.gnome.org/show_bug.cgi?id=789377
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/6
+---
+ js/ui/main.js        | 7 +++++++
+ js/ui/sessionMode.js | 1 +
+ 2 files changed, 8 insertions(+)
+
+diff --git a/js/ui/main.js b/js/ui/main.js
+index 40898e1..a110e5b 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -147,6 +147,13 @@ function _sessionUpdated() {
+             _remoteAccessInhibited = true;
+         }
+     }
++
++    if (!GLib.getenv('SHELL_DEBUG')) {
++        if (typeof sessionMode.debugFlags === 'string')
++            global.set_debug_flags(sessionMode.debugFlags);
++        else if (Array.isArray(sessionMode.debugFlags))
++            global.set_debug_flags(sessionMode.debugFlags.join(':'))
++    }
+ }
+ 
+ /** @returns {void} */
+diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
+index dea9be9..d131d07 100644
+--- a/js/ui/sessionMode.js
++++ b/js/ui/sessionMode.js
+@@ -29,6 +29,7 @@ const _modes = {
+         showWelcomeDialog: false,
+         allowSettings: false,
+         allowScreencast: false,
++        debugFlags: [],
+         enabledExtensions: [],
+         hasRunDialog: false,
+         hasWorkspaces: false,
diff -pruN 49.0-1/debian/patches/slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch 49.0-1ubuntu1/debian/patches/slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch
--- 49.0-1/debian/patches/slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/slider-Use-the-sprite-of-the-touch-event-not-the-pointer.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,37 @@
+From: Daniel van Vugt <daniel.van.vugt@canonical.com>
+Date: Tue, 23 Sep 2025 14:12:56 +0800
+Subject: slider: Use the sprite of the touch event, not the pointer
+
+startDragging() starts with:
+
+  const sprite = backend.get_sprite(global.stage, event);
+  this._sprite = sprite;
+
+but then vfunc_touch_event() would only compare this._sprite to:
+
+  sprite = backend.get_pointer_sprite(global.stage);
+
+which never matched. And so _endDragging() was never called and
+the grab was never dismissed, effectively freezing the shell.
+
+Bug: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/8660
+Fixes: c206ec693 ("slider: Use Clutter.Sprite for pointing focus accounting")
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3887
+Origin: upstream, 49.1, commit:e54aa588c1604b63e2f051e20c1ecb3e601beb5c
+---
+ js/ui/slider.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/js/ui/slider.js b/js/ui/slider.js
+index 2cf5f14..05861b5 100644
+--- a/js/ui/slider.js
++++ b/js/ui/slider.js
+@@ -132,7 +132,7 @@ export const Slider = GObject.registerClass({
+ 
+     vfunc_touch_event(event) {
+         const backend = global.stage.get_context().get_backend();
+-        const sprite = backend.get_pointer_sprite(global.stage);
++        const sprite = backend.get_sprite(global.stage, event);
+ 
+         if (!this._dragging &&
+             event.type() === Clutter.EventType.TOUCH_BEGIN) {
diff -pruN 49.0-1/debian/patches/ubuntu/appMenu-Show-details-menu-item-for-snap-applications.patch 49.0-1ubuntu1/debian/patches/ubuntu/appMenu-Show-details-menu-item-for-snap-applications.patch
--- 49.0-1/debian/patches/ubuntu/appMenu-Show-details-menu-item-for-snap-applications.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/appMenu-Show-details-menu-item-for-snap-applications.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,78 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Mon, 16 Sep 2024 23:28:45 +0200
+Subject: appMenu: Show details menu item for snap applications
+
+If a snap application is installed we can still show the details menu
+pointing to the snap-store instead of to GNOME Software
+
+Forwarded: Not-needed
+---
+ js/ui/appMenu.js | 33 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 32 insertions(+), 1 deletion(-)
+
+diff --git a/js/ui/appMenu.js b/js/ui/appMenu.js
+index b8d8f64..547331b 100644
+--- a/js/ui/appMenu.js
++++ b/js/ui/appMenu.js
+@@ -9,6 +9,7 @@ import * as AppFavorites from './appFavorites.js';
+ import * as Main from './main.js';
+ import * as ParentalControlsManager from '../misc/parentalControlsManager.js';
+ import * as PopupMenu from './popupMenu.js';
++import * as Util from '../misc/util.js';
+ 
+ export class AppMenu extends PopupMenu.PopupMenu {
+     /**
+@@ -81,7 +82,23 @@ export class AppMenu extends PopupMenu.PopupMenu {
+ 
+         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ 
+-        this._detailsItem = this.addAction(_('App Details'), async () => {
++        this._detailsItem = this.addAction(_('App Details'), async event => {
++            const snapName = this._getSnapName();
++            if (snapName) {
++                const snapStore = this._getSnapStore();
++                if (!snapStore) {
++                    logError(new Error('Snap store is not installed!'))
++                    return;
++                }
++
++                snapStore.activate_full(-1, event.get_time());
++                Util.spawnApp([
++                    ...snapStore.appInfo.get_commandline().split(' '), snapName,
++                ]);
++                Main.overview.hide();
++                return;
++            }
++
+             const id = this._app.get_id();
+             const args = GLib.Variant.new('(ss)', [id, '']);
+             const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
+@@ -179,7 +196,20 @@ export class AppMenu extends PopupMenu.PopupMenu {
+             : _('Launch Using Discrete Graphics Card');
+     }
+ 
++    _getSnapStore() {
++        return this._appSystem.lookup_app('snap-store_snap-store.desktop');
++    }
++
++    _getSnapName() {
++        return this._app?.appInfo?.get_string('X-SnapInstanceName');
++    }
++
+     _updateDetailsVisibility() {
++        if (this._getSnapName()) {
++            this._detailsItem.visible = !!this._getSnapStore();
++            return;
++        }
++
+         const sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
+         this._detailsItem.visible = sw !== null;
+     }
+@@ -245,6 +275,7 @@ export class AppMenu extends PopupMenu.PopupMenu {
+         this._updateNewWindowItem();
+         this._updateFavoriteItem();
+         this._updateGpuItem();
++        this._updateDetailsVisibility();
+     }
+ 
+     _queueUpdateWindowsSection() {
diff -pruN 49.0-1/debian/patches/ubuntu/background_login.patch 49.0-1ubuntu1/debian/patches/ubuntu/background_login.patch
--- 49.0-1/debian/patches/ubuntu/background_login.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/background_login.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,50 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Tue, 10 Sep 2019 13:13:14 +0100
+Subject: js/ui/background.js: Match theme login screen color
+
+This way the login animation will appear to expand over the login
+screen (system background) instead of suddenly replacing it.
+
+Original author: Didier Roche <didrocks@ubuntu.com>
+Original author: Daniel van Vugt <daniel.van.vugt@canonical.com>
+Author: Marco Trevisan <marco.trevisan@canonical.com>
+
+Last-Update: 2022-08-30
+Forwarded: not-needed
+---
+ js/ui/background.js | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/js/ui/background.js b/js/ui/background.js
+index cee38f7..c320300 100644
+--- a/js/ui/background.js
++++ b/js/ui/background.js
+@@ -100,8 +100,10 @@ import GObject from 'gi://GObject';
+ import GnomeBG from 'gi://GnomeBG';
+ import GnomeDesktop from 'gi://GnomeDesktop';
+ import Meta from 'gi://Meta';
++import St from 'gi://St';
+ import * as Signals from '../misc/signals.js';
+ 
++import * as Desktop from '../misc/desktop.js';
+ import * as LoginManager from '../misc/loginManager.js';
+ import * as Main from './main.js';
+ import * as Params from '../misc/params.js';
+@@ -537,7 +539,16 @@ export const SystemBackground = GObject.registerClass({
+     _init() {
+         if (_systemBackground == null) {
+             _systemBackground = new Meta.Background({meta_display: global.display});
+-            _systemBackground.set_color(DEFAULT_BACKGROUND_COLOR);
++
++            let backgroundColor = DEFAULT_BACKGROUND_COLOR;
++            if (Desktop.is('ubuntu')) {
++                const dummyBgActor = new St.Widget({name: 'lockDialogGroup'});
++                Main.uiGroup.add_child(dummyBgActor);
++                backgroundColor = dummyBgActor.get_theme_node().get_background_color();
++                dummyBgActor.destroy();
++            }
++
++            _systemBackground.set_color(backgroundColor);
+         }
+ 
+         super._init({
diff -pruN 49.0-1/debian/patches/ubuntu/chromium-snap-pwa.patch 49.0-1ubuntu1/debian/patches/ubuntu/chromium-snap-pwa.patch
--- 49.0-1/debian/patches/ubuntu/chromium-snap-pwa.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/chromium-snap-pwa.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,64 @@
+From: Nathan Pratta Teodosio <nathan.teodosio@canonical.com>
+Date: Mon, 16 Sep 2024 11:19:04 +0200
+Subject: Fix Chromium's progressive web application tab and dock grouping.
+
+Bug-Ubuntu: https://launchpad.net/bugs/2007652
+Forwarded: not-needed
+---
+ src/shell-window-tracker.c | 32 ++++++++++++++++++++++++++++++--
+ 1 file changed, 30 insertions(+), 2 deletions(-)
+
+diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c
+index 839f524..675d74c 100644
+--- a/src/shell-window-tracker.c
++++ b/src/shell-window-tracker.c
+@@ -152,11 +152,21 @@ get_app_from_window_wmclass (MetaWindow  *window)
+   const char *wm_instance;
+   const char *sandbox_id;
+   g_autofree char *app_prefix = NULL;
++  gboolean is_sandboxed_chromium;
+ 
+   appsys = shell_app_system_get_default ();
+ 
+   sandbox_id = meta_window_get_sandboxed_app_id (window);
+-  if (sandbox_id)
++
++  /* Progressive web application tab and dock grouping needs
++   * to be excepted from the special sandboxed logic to work properly
++   * in the Chromium snap (LP:2007652).
++   */
++  is_sandboxed_chromium = sandbox_id &&
++                          g_str_has_prefix (sandbox_id, "chromium_") &&
++                          g_str_has_suffix (sandbox_id, "_chromium");
++
++  if (sandbox_id && !is_sandboxed_chromium)
+     app_prefix = g_strdup_printf ("%s.", sandbox_id);
+ 
+   /* Notes on the heuristics used here:
+@@ -193,7 +203,25 @@ get_app_from_window_wmclass (MetaWindow  *window)
+   wm_instance = meta_window_get_wm_class_instance (window);
+   app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance);
+   if (app != NULL && check_app_id_prefix (app, app_prefix))
+-    return g_object_ref (app);
++    {
++      if (is_sandboxed_chromium)
++        {
++          GDesktopAppInfo *app_info = shell_app_get_app_info (app);
++          const gchar *exe = NULL;
++
++          if (!G_IS_APP_INFO (app_info))
++            return NULL;
++
++          if (!(exe = g_app_info_get_executable (G_APP_INFO (app_info))))
++            return NULL;
++
++          if (g_strcmp0 (exe, "/snap/bin/chromium") != 0 &&
++              !g_str_has_prefix (exe, "/snap/bin/chromium_"))
++            return NULL;
++        }
++
++      return g_object_ref (app);
++    }
+ 
+   /* then try a match from WM_CLASS to StartupWMClass */
+   wm_class = meta_window_get_wm_class (window);
diff -pruN 49.0-1/debian/patches/ubuntu/configure_login_screen.patch 49.0-1ubuntu1/debian/patches/ubuntu/configure_login_screen.patch
--- 49.0-1/debian/patches/ubuntu/configure_login_screen.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/configure_login_screen.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,211 @@
+From: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Date: Thu, 11 Mar 2021 10:12:06 +0200
+Subject: Allow configuring login screen background
+
+Configuring login screen backgound and properties is a recurring request
+especially on corporate environment.
+Allows to override the default embedded style in the theme with specific
+gsettings keys.
+Forwarded: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/680
+Bug: https://bugs.launchpad.net/ubuntu/+source/gnome-shell/+bug/1918613
+Origin: ubuntu
+---
+ data/com.ubuntu.login-screen.gschema.xml.in | 70 +++++++++++++++++++++++++++++
+ data/meson.build                            |  8 +++-
+ js/ui/background.js                         |  4 ++
+ js/ui/screenShield.js                       | 42 +++++++++++++++++
+ 4 files changed, 123 insertions(+), 1 deletion(-)
+ create mode 100644 data/com.ubuntu.login-screen.gschema.xml.in
+
+diff --git a/data/com.ubuntu.login-screen.gschema.xml.in b/data/com.ubuntu.login-screen.gschema.xml.in
+new file mode 100644
+index 0000000..ee8b3a2
+--- /dev/null
++++ b/data/com.ubuntu.login-screen.gschema.xml.in
+@@ -0,0 +1,70 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<schemalist gettext-domain="@GETTEXT_PACKAGE@">
++  <enum id="com.ubuntu.login-screen.BackgroundRepeat">
++    <value value="1" nick="default"/>
++    <value value="2" nick="repeat"/>
++    <value value="3" nick="repeat-x"/>
++    <value value="4" nick="repeat-y"/>
++    <value value="5" nick="no-repeat"/>
++    <value value="6" nick="space"/>
++    <value value="7" nick="round"/>
++  </enum>
++  <enum id="com.ubuntu.login-screen.BackgroundSize">
++    <value value="1" nick="default"/>
++    <value value="2" nick="auto"/>
++    <value value="3" nick="cover"/>
++    <value value="4" nick="contain"/>
++  </enum>
++  <schema id="com.ubuntu.login-screen" path="/com/ubuntu/login-screen/">
++    <key name="background-picture-uri" type="s">
++      <default>''</default>
++      <summary>
++        Sets the background image for the login screen.
++      </summary>
++      <description>
++        URI to use for the background image. Note that the backend only
++        supports local (file://) URIs.
++        It overrides the value defined in the default style sheet.
++      </description>
++    </key>
++    <key name="background-color" type="s">
++      <default>''</default>
++      <summary>
++        The background-color property sets the background color.
++      </summary>
++      <description>
++        The background-color property sets the background color to use when
++        the background picture URI is missing or when it doesn't cover the whole background.
++        It overrides the value defined in the default style sheet.
++      </description>
++    </key>
++    <key name="background-repeat" enum="com.ubuntu.login-screen.BackgroundRepeat">
++      <default>'default'</default>
++      <summary>
++        The background-repeat property sets if/how the background image will be repeated.
++      </summary>
++      <description>
++        The background-repeat property sets if/how a background image will be repeated.
++        By default, a background-image is repeated both vertically and horizontally.
++
++        It overrides the value defined in the default style sheet.
++      </description>
++    </key>
++    <key name="background-size" enum="com.ubuntu.login-screen.BackgroundSize">
++      <default>'default'</default>
++      <summary>
++        The background-size property specifies the size of the background image.
++      </summary>
++      <description>
++        The background-size property specifies the size of the background images.
++
++        There are three keywords you can use with this property:
++        auto: The background image is displayed in its original size;
++        cover: Resize the background image to cover the entire container, even if it has to stretch the image or cut a little bit off one of the edges;
++        contain: Resize the background image to make sure the image is fully visible.
++
++        It overrides the value defined in the default style sheet.
++      </description>
++    </key>
++  </schema>
++</schemalist>
+diff --git a/data/meson.build b/data/meson.build
+index cc7b5e2..fcf1f34 100644
+--- a/data/meson.build
++++ b/data/meson.build
+@@ -109,6 +109,12 @@ schema = configure_file(
+   configuration: schemaconf,
+   install_dir: schemadir
+ )
++schema_ubuntu_login = configure_file(
++  input: 'com.ubuntu.login-screen.gschema.xml.in',
++  output: 'com.ubuntu.login-screen.gschema.xml',
++  configuration: schemaconf,
++  install_dir: schemadir
++)
+ install_data('00_org.gnome.shell.gschema.override', install_dir: schemadir)
+ 
+ if have_systemd
+@@ -152,7 +158,7 @@ endif
+ 
+ # for unit tests - gnome.compile_schemas() only looks in srcdir
+ compiled_schemas = custom_target('compile-schemas',
+-  input: schema,
++  input: [schema, schema_ubuntu_login],
+   output: 'gschemas.compiled',
+   command: [find_program('glib-compile-schemas'), '--strict', data_builddir],
+ )
+diff --git a/js/ui/background.js b/js/ui/background.js
+index c320300..0454c64 100644
+--- a/js/ui/background.js
++++ b/js/ui/background.js
+@@ -542,7 +542,11 @@ export const SystemBackground = GObject.registerClass({
+ 
+             let backgroundColor = DEFAULT_BACKGROUND_COLOR;
+             if (Desktop.is('ubuntu')) {
++                const loginSettings = new Gio.Settings({schema_id: 'com.ubuntu.login-screen'});
++                const bgColor = loginSettings.get_string('background-color');
+                 const dummyBgActor = new St.Widget({name: 'lockDialogGroup'});
++                if (bgColor)
++                    dummyBgActor.set_style(`background-color: ${bgColor};`);
+                 Main.uiGroup.add_child(dummyBgActor);
+                 backgroundColor = dummyBgActor.get_theme_node().get_background_color();
+                 dummyBgActor.destroy();
+diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
+index b3bbcac..720b98a 100644
+--- a/js/ui/screenShield.js
++++ b/js/ui/screenShield.js
+@@ -31,6 +31,12 @@ const DISABLE_LOCK_KEY = 'disable-lock-screen';
+ 
+ const LOCKED_STATE_STR = 'screenShield.locked';
+ 
++const LOGIN_SCREEN_SCHEMA = 'com.ubuntu.login-screen';
++const LOGIN_SCREEN_BACKGROUND_COLOR_KEY = 'background-color';
++const LOGIN_SCREEN_BACKGROUND_PICTURE_URI_KEY = 'background-picture-uri';
++const LOGIN_SCREEN_BACKGROUND_REPEAT_KEY = 'background-repeat';
++const LOGIN_SCREEN_BACKGROUND_SIZE_KEY = 'background-size';
++
+ // ScreenShield animation time
+ // - STANDARD_FADE_TIME is used when the session goes idle
+ // - MANUAL_FADE_TIME is used for lowering the shield when asked by the user,
+@@ -114,6 +120,16 @@ export class ScreenShield extends Signals.EventEmitter {
+         this._lockSettings = new Gio.Settings({schema_id: LOCKDOWN_SCHEMA});
+         this._lockSettings.connect(`changed::${DISABLE_LOCK_KEY}`, this._syncInhibitor.bind(this));
+ 
++        this._loginScreenSettings = new Gio.Settings({schema_id: LOGIN_SCREEN_SCHEMA});
++        [
++            LOGIN_SCREEN_BACKGROUND_COLOR_KEY,
++            LOGIN_SCREEN_BACKGROUND_PICTURE_URI_KEY,
++            LOGIN_SCREEN_BACKGROUND_REPEAT_KEY,
++            LOGIN_SCREEN_BACKGROUND_SIZE_KEY,
++        ].forEach(schema => this._loginScreenSettings.connect(`changed::${schema}`,
++            () => this._refreshBackground()));
++        this._refreshBackground();
++
+         this._isModal = false;
+         this._isGreeter = false;
+         this._isActive = false;
+@@ -214,6 +230,31 @@ export class ScreenShield extends Signals.EventEmitter {
+         return this._isModal;
+     }
+ 
++    _refreshBackground() {
++        const inlineStyle = [];
++
++        const getSetting = s => this._loginScreenSettings.get_string(s);
++        const backgroundColor = getSetting(LOGIN_SCREEN_BACKGROUND_COLOR_KEY);
++        const backgroundPictureUri = getSetting(LOGIN_SCREEN_BACKGROUND_PICTURE_URI_KEY);
++        const backgroundRepeat = getSetting(LOGIN_SCREEN_BACKGROUND_REPEAT_KEY);
++        const backgroundSize = getSetting(LOGIN_SCREEN_BACKGROUND_SIZE_KEY);
++
++        if (backgroundColor && !backgroundColor.includes('rgba'))
++            inlineStyle.push(`background-color: ${backgroundColor}`);
++        if (backgroundPictureUri)
++            inlineStyle.push(`background-image: url("${backgroundPictureUri}")`);
++        if (backgroundRepeat !== 'default')
++            inlineStyle.push(`background-repeat: ${backgroundRepeat}`);
++        if (backgroundSize !== 'default')
++            inlineStyle.push(`background-size: ${backgroundSize}`);
++
++        const lockDialogGroupStyle = inlineStyle.join('; ') || null;
++        this._lockDialogGroup.set_style(lockDialogGroupStyle);
++        this._dialog?.set_style(lockDialogGroupStyle ? `
++            background-image: none;
++            background-color: transparent;` : null);
++    }
++
+     async _syncInhibitor() {
+         const lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY) ||
+                             this._settings.get_boolean(SUSPEND_LOCK_ENABLED_KEY);
+@@ -442,6 +483,7 @@ export class ScreenShield extends Signals.EventEmitter {
+             }
+ 
+             this._dialog = new constructor(this._lockDialogGroup);
++            this._refreshBackground();
+ 
+             if (!this._dialog.open()) {
+                 // This is kind of an impossible error: we're already modal
diff -pruN 49.0-1/debian/patches/ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch 49.0-1ubuntu1/debian/patches/ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch
--- 49.0-1/debian/patches/ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/darkMode-Add-support-to-Yaru-theme-color-variants.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,71 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 24 Aug 2022 19:15:36 +0200
+Subject: darkMode: Add support to Yaru theme color variants
+
+Support switching to dark mode when using the Yaru theme color accents.
+---
+ js/ui/status/darkMode.js | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+diff --git a/js/ui/status/darkMode.js b/js/ui/status/darkMode.js
+index 4e68a74..6fc8f18 100644
+--- a/js/ui/status/darkMode.js
++++ b/js/ui/status/darkMode.js
+@@ -1,5 +1,6 @@
+ import Gio from 'gi://Gio';
+ import GObject from 'gi://GObject';
++import St from 'gi://St';
+ 
+ import * as Main from '../main.js';
+ 
+@@ -19,6 +20,9 @@ class DarkModeToggle extends QuickToggle {
+         this._changedId = this._settings.connect('changed::color-scheme',
+             () => this._sync());
+ 
++        St.Settings.get().connect('notify::gtk-theme', () => this._sync());
++        St.Settings.get().connect('notify::accent-color', () => this._sync());
++
+         this.connectObject(
+             'destroy', () => this._settings.run_dispose(),
+             'clicked', () => this._toggleMode(),
+@@ -28,8 +32,40 @@ class DarkModeToggle extends QuickToggle {
+ 
+     _toggleMode() {
+         Main.layoutManager.screenTransition.run();
++        const preferDark = !this.checked;
+         this._settings.set_string('color-scheme',
+             this.checked ? 'default' : 'prefer-dark');
++
++        if (St.Settings.get().gtkTheme === 'Yaru')
++            this._setYaruSettings(preferDark);
++    }
++
++    _setYaruSettings(preferDark) {
++        const currentlyDark =
++            this._settings.get_string('gtk-theme').endsWith('-dark') &&
++            this._settings.get_string('icon-theme').endsWith('-dark');
++
++        if (currentlyDark !== preferDark) {
++            const themeVariant = Main.getYaruVariantFromAccent();
++            const newTheme = `Yaru${
++                themeVariant !== 'default' ? `-${themeVariant}` : ''}${
++                preferDark ? '-dark' : ''}`;
++
++            this._settings.set_string('gtk-theme', newTheme);
++            this._settings.set_string('icon-theme', newTheme);
++        }
++
++        const schemaSource = Gio.SettingsSchemaSource.get_default();
++        const geditSchema = schemaSource.lookup('org.gnome.gedit.preferences.editor', true);
++
++        if (geditSchema) {
++            const geditSettings = Gio.Settings.new_full(geditSchema, null, null);
++            const geditScheme = geditSettings.get_user_value('scheme')?.unpack();
++
++            if (geditScheme?.startsWith('Yaru') &&
++                geditScheme.endsWith('-dark') !== preferDark)
++                geditSettings.set_string('scheme', `Yaru${preferDark ? '-dark' : ''}`);
++        }
+     }
+ 
+     _sync() {
diff -pruN 49.0-1/debian/patches/ubuntu/desktop_detect.patch 49.0-1ubuntu1/debian/patches/ubuntu/desktop_detect.patch
--- 49.0-1/debian/patches/ubuntu/desktop_detect.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/desktop_detect.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,65 @@
+From: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Date: Wed, 20 Jun 2018 19:22:06 +0200
+Subject: Add an helper to detect current desktop
+
+We will differentiate some behavior depending on current desktop. Add an
+helper to centralize the current desktop detection.
+Forwarded: not-needed
+Origin: ubuntu
+---
+ js/js-resources.gresource.xml |  1 +
+ js/misc/desktop.js            | 33 +++++++++++++++++++++++++++++++++
+ 2 files changed, 34 insertions(+)
+ create mode 100644 js/misc/desktop.js
+
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index e5e6167..8c2b908 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -22,6 +22,7 @@
+     <file>misc/dbusErrors.js</file>
+     <file>misc/dbusUtils.js</file>
+     <file>misc/dependencies.js</file>
++    <file>misc/desktop.js</file>
+     <file>misc/errorUtils.js</file>
+     <file>misc/extensionUtils.js</file>
+     <file>misc/fileUtils.js</file>
+diff --git a/js/misc/desktop.js b/js/misc/desktop.js
+new file mode 100644
+index 0000000..05044c1
+--- /dev/null
++++ b/js/misc/desktop.js
+@@ -0,0 +1,33 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++
++import GLib from 'gi://GLib';
++
++// current desktop doesn't change unless we restart the shell or control
++// the env variable. It's safe to cache matching result
++const _currentDesktopsMatches = new Map();
++
++// is:
++// @name: desktop string you want to assert if it matches the current desktop env
++//
++// The function examples XDG_CURRENT_DESKTOP and return if the current desktop
++// is part of that desktop string.
++//
++// Return value: if the environment isn't set or doesn't match, return False
++// otherwise, return True.
++export function is(name) {
++    if (!_currentDesktopsMatches.size) {
++        const desktopsEnv = GLib.getenv('XDG_CURRENT_DESKTOP');
++        if (!desktopsEnv) {
++            _currentDesktopsMatches.set(name, false);
++            return false;
++        }
++
++        const desktops = desktopsEnv.split(':');
++        desktops.forEach(d => _currentDesktopsMatches.set(d, true));
++
++        if (!_currentDesktopsMatches.size)
++            _currentDesktopsMatches.set(name, _currentDesktopsMatches.has(name));
++    }
++
++    return !!_currentDesktopsMatches.get(name);
++}
diff -pruN 49.0-1/debian/patches/ubuntu/extensions-Include-snapd-prompting-extension.patch 49.0-1ubuntu1/debian/patches/ubuntu/extensions-Include-snapd-prompting-extension.patch
--- 49.0-1/debian/patches/ubuntu/extensions-Include-snapd-prompting-extension.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/extensions-Include-snapd-prompting-extension.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,1245 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 14 Aug 2025 20:08:10 +0200
+Subject: extensions: Include snapd-prompting extension
+
+---
+ extensions/meson.build                             |   4 +
+ .../snapd-prompting@canonical.com/dbusServer.js    |  92 ++++
+ .../dependencies/gi.js                             |  12 +
+ .../dependencies/shell/extensions/extension.js     |   1 +
+ .../dependencies/shell/misc.js                     |   6 +
+ .../dependencies/shell/ui.js                       |  19 +
+ .../snapd-prompting@canonical.com/extension.js     |  46 ++
+ .../snapd-prompting@canonical.com/metadata.json    |   8 +
+ .../promptsHandler.js                              | 188 ++++++++
+ extensions/snapd-prompting@canonical.com/utils.js  | 513 +++++++++++++++++++++
+ .../snapd-prompting@canonical.com/windowsGroup.js  | 248 ++++++++++
+ meson.build                                        |   1 +
+ 12 files changed, 1138 insertions(+)
+ create mode 100644 extensions/meson.build
+ create mode 100644 extensions/snapd-prompting@canonical.com/dbusServer.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/dependencies/gi.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/dependencies/shell/extensions/extension.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/dependencies/shell/misc.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/dependencies/shell/ui.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/extension.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/metadata.json
+ create mode 100644 extensions/snapd-prompting@canonical.com/promptsHandler.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/utils.js
+ create mode 100644 extensions/snapd-prompting@canonical.com/windowsGroup.js
+
+diff --git a/extensions/meson.build b/extensions/meson.build
+new file mode 100644
+index 0000000..6ca7127
+--- /dev/null
++++ b/extensions/meson.build
+@@ -0,0 +1,4 @@
++install_subdir(
++    'snapd-prompting@canonical.com',
++    install_dir: datadir / 'gnome-shell' / 'extensions',
++)
+diff --git a/extensions/snapd-prompting@canonical.com/dbusServer.js b/extensions/snapd-prompting@canonical.com/dbusServer.js
+new file mode 100644
+index 0000000..ae75ad6
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/dbusServer.js
+@@ -0,0 +1,92 @@
++/* dbusServer.js
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Marco Trevisan <marco@ubuntu.com>
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ */
++
++import {
++    Gio,
++    GObject,
++} from './dependencies/gi.js';
++
++
++const SnapdPromptingInterface = 'com.canonical.Shell.PermissionPrompting';
++const SnapdPromptingObjectPath = `/${SnapdPromptingInterface.replaceAll('.', '/')}`;
++
++const SNAPD_PROMPTING_INTERFACE = `
++<node>
++    <interface name="${SnapdPromptingInterface}">
++        <method name="Prompt">
++            <arg name="snap_app_id" type="s" direction="in"/>
++            <arg name="app_pid" type="t" direction="in"/>
++        </method>
++    </interface>
++</node>
++`;
++
++export const DBusServer = GObject.registerClass({
++    Signals: {
++        'prompt-request': {
++            param_types: [GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_UINT64],
++        },
++    },
++}, class DBusServer extends GObject.Object {
++    constructor() {
++        super();
++
++        // TODO: Make all this asyc
++        this._dbusObject = Gio.DBusExportedObject.wrapJSObject(
++            SNAPD_PROMPTING_INTERFACE, this);
++        try {
++            this._dbusObject.export(Gio.DBus.session, SnapdPromptingObjectPath);
++        } catch (e) {
++            logError(e, `Failed to export ${SnapdPromptingObjectPath}`);
++        }
++
++        this._ownName = Gio.DBus.session.own_name(SnapdPromptingInterface,
++            Gio.BusNameOwnerFlags.NONE, null, () => this._lostName());
++    }
++
++    destroy() {
++        if (this._ownName)
++            Gio.DBus.session.unown_name(this._ownName);
++
++        try {
++            this._dbusObject.unexport();
++        } catch (e) {
++            logError(e, `Failed to unexport ${SnapdPromptingObjectPath}`);
++        }
++
++        this._dbusObject.run_dispose();
++        delete this._dbusObject;
++    }
++
++    _lostName(name) {
++        console.error(`Name "${name}" lost`);
++        delete this._ownName;
++    }
++
++    PromptAsync([snapAppID, appPid], invocation) {
++        this.emit('prompt-request', invocation.get_sender(), snapAppID, appPid);
++
++        try {
++            invocation.return_value(null);
++        } catch (e) {
++            invocation.return_gerror(e);
++        }
++    }
++});
+diff --git a/extensions/snapd-prompting@canonical.com/dependencies/gi.js b/extensions/snapd-prompting@canonical.com/dependencies/gi.js
+new file mode 100644
+index 0000000..3004550
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/dependencies/gi.js
+@@ -0,0 +1,12 @@
++export {default as Atk} from 'gi://Atk';
++export {default as Clutter} from 'gi://Clutter';
++export {default as Cogl} from 'gi://Cogl';
++export {default as GLib} from 'gi://GLib';
++export {default as GObject} from 'gi://GObject';
++export {default as GdkPixbuf} from 'gi://GdkPixbuf';
++export {default as Gio} from 'gi://Gio';
++export {default as Meta} from 'gi://Meta';
++export {default as Mtk} from 'gi://Mtk';
++export {default as Pango} from 'gi://Pango';
++export {default as Shell} from 'gi://Shell';
++export {default as St} from 'gi://St';
+diff --git a/extensions/snapd-prompting@canonical.com/dependencies/shell/extensions/extension.js b/extensions/snapd-prompting@canonical.com/dependencies/shell/extensions/extension.js
+new file mode 100644
+index 0000000..bd9db98
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/dependencies/shell/extensions/extension.js
+@@ -0,0 +1 @@
++export * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
+diff --git a/extensions/snapd-prompting@canonical.com/dependencies/shell/misc.js b/extensions/snapd-prompting@canonical.com/dependencies/shell/misc.js
+new file mode 100644
+index 0000000..bf587f5
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/dependencies/shell/misc.js
+@@ -0,0 +1,6 @@
++export * as AnimationUtils from 'resource:///org/gnome/shell/misc/animationUtils.js';
++export * as Config from 'resource:///org/gnome/shell/misc/config.js';
++export * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
++export * as ParentalControlsManager from 'resource:///org/gnome/shell/misc/parentalControlsManager.js';
++export * as SignalTracker from 'resource:///org/gnome/shell/misc/signalTracker.js';
++export * as Util from 'resource:///org/gnome/shell/misc/util.js';
+diff --git a/extensions/snapd-prompting@canonical.com/dependencies/shell/ui.js b/extensions/snapd-prompting@canonical.com/dependencies/shell/ui.js
+new file mode 100644
+index 0000000..9a772f9
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/dependencies/shell/ui.js
+@@ -0,0 +1,19 @@
++export * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
++export * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js';
++export * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js';
++export * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js';
++export * as CloseDialog from 'resource:///org/gnome/shell/ui/closeDialog.js';
++export * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
++export * as Dash from 'resource:///org/gnome/shell/ui/dash.js';
++export * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
++export * as Main from 'resource:///org/gnome/shell/ui/main.js';
++export * as Overview from 'resource:///org/gnome/shell/ui/overview.js';
++export * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
++export * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js';
++export * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
++export * as SearchController from 'resource:///org/gnome/shell/ui/searchController.js';
++export * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
++export * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
++export * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js';
++export * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js';
++export * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js';
+diff --git a/extensions/snapd-prompting@canonical.com/extension.js b/extensions/snapd-prompting@canonical.com/extension.js
+new file mode 100644
+index 0000000..c89f56d
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/extension.js
+@@ -0,0 +1,46 @@
++/* extension.js
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ */
++
++import {
++    GLib,
++    Shell,
++} from './dependencies/gi.js';
++
++// import { Extension } from './dependencies/shell/extensions/extension.js';
++import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
++import {DBusServer} from './dbusServer.js';
++import {PromptsHandler} from './promptsHandler.js';
++
++export default class SnapdPromptingDialog extends Extension {
++    enable() {
++        this._dbusServer = new DBusServer();
++        this._promptsHandler = new PromptsHandler(this._dbusServer);
++        // this._dbusServer.connect('prompt-request', )
++
++        Shell.util_spawn_async(
++            null, ['python3', `${this.path}/../tools/permission-dialog-launcher.py`], null,
++            GLib.SpawnFlags.SEARCH_PATH);
++    }
++
++    disable() {
++        this._dbusServer?.destroy();
++        this._dbusServer = null;
++        this._promptsHandler?.destroy();
++        this._promptsHandler = null;
++    }
++}
+diff --git a/extensions/snapd-prompting@canonical.com/metadata.json b/extensions/snapd-prompting@canonical.com/metadata.json
+new file mode 100644
+index 0000000..6c69393
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/metadata.json
+@@ -0,0 +1,8 @@
++{
++  "name": "SNAPD Permission Prompting",
++  "description": "A GNOME Shell extension to make the SNAPD prompts to be properly integrated",
++  "uuid": "snapd-prompting@canonical.com",
++  "shell-version": [
++    "49"
++  ]
++}
+diff --git a/extensions/snapd-prompting@canonical.com/promptsHandler.js b/extensions/snapd-prompting@canonical.com/promptsHandler.js
+new file mode 100644
+index 0000000..97dadb3
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/promptsHandler.js
+@@ -0,0 +1,188 @@
++/* promptsHandler.js
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Marco Trevisan <marco@ubuntu.com>
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ */
++
++import {
++    Gio,
++    GLib,
++    Shell,
++    GObject,
++} from './dependencies/gi.js';
++
++import * as Utils from './utils.js';
++import {WindowsGroup} from './windowsGroup.js';
++
++import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
++import {CloseDialog} from './dependencies/shell/ui.js';
++
++const DEFAULT_PROMPTING_DBUS_NAME = 'com.canonical.Snapd.AppArmor.PromptingClient.Dialog';
++
++export const PromptsHandler = GObject.registerClass(
++class PromptsHandler extends Utils.DestroyableGObject {
++    constructor(dbusServer) {
++        super();
++
++        this._requestIDs = 0;
++        this._pendingRequests = [];
++        this._windowsGroups = [];
++        this._injectionManager = new InjectionManager();
++
++        // FIXME: Drop the injection as soon we don't have more apps to track.
++        const self = this;
++        this._injectionManager.overrideMethod(CloseDialog.CloseDialog.prototype,
++            'vfunc_show', originalMethod => {
++                return function (...args) {
++                    // eslint-disable-next-line no-invalid-this
++                    if (self._windowsGroups.find(wg => wg.snapWindows.includes(this._window)))
++                        return;
++
++                    // eslint-disable-next-line no-invalid-this
++                    originalMethod.apply(this, ...args);
++                };
++            });
++
++        this._injectionManager.overrideMethod(Shell.AppSystem.prototype, 'get_running', originalMethod => {
++            return function (...args) {
++                const promptApps = self._windowsGroups.map(wg => wg.promptApp);
++                // eslint-disable-next-line no-invalid-this
++                const runningApps = originalMethod.call(this, ...args);
++                return runningApps.filter(a => !promptApps.includes(a));
++            };
++        });
++
++        this._windowsTracker = Shell.WindowTracker.get_default();
++
++        Gio.DBus.watch_name(Gio.BusType.SESSION,
++            DEFAULT_PROMPTING_DBUS_NAME,
++            Gio.BusNameWatcherFlags.NONE,
++            (_, _c, owner) => (this._promptDialogName = owner),
++            () => (this._promptDialogName = null));
++
++        dbusServer.connectObject('prompt-request', (_, ...args) =>
++            this._onPromptRequest(...args), this);
++
++        global.display.connectObject('window-created',
++            () => this._processRequests(), this);
++    }
++
++    _onPromptRequest(sender, snapAppID, snapAppPid) {
++        if (!this._checkSenderValidity(sender)) {
++            console.log(`Ignoring prompt request from ${sender}`);
++            return;
++        }
++
++        if (snapAppID.indexOf('_') < 0) {
++            // If no full security profile has been provided, we assume it's
++            // the main application, for now.
++            snapAppID = `${snapAppID}_${snapAppID}`;
++        }
++
++        const id = this._requestIDs++;
++        const request = {id, sender, snapAppID, snapAppPid};
++        this._pendingRequests.push(request);
++
++        request.timeoutID = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => {
++            const idx = this._pendingRequests.findIndex(r => r.id === id);
++            if (idx < 0)
++                return GLib.SOURCE_REMOVE;
++
++            this._pendingRequests.splice(idx, 1);
++            return GLib.SOURCE_REMOVE;
++        });
++
++        this._processRequests();
++    }
++
++    _processRequests() {
++        if (!this._pendingRequests?.length)
++            return;
++
++        const currentRequests = this._pendingRequests;
++        this._pendingRequests = [];
++
++        currentRequests.forEach(async req => {
++            try {
++                const {sender, snapAppID, snapAppPid, timeoutID} = req;
++                await this._processPromptRequest(sender, snapAppID, snapAppPid);
++                GLib.source_remove(timeoutID);
++            } catch (e) {
++                // Retry until timeout occurs.
++                console.warn(`Prompt request cannot be handled: ${e}`);
++                this._pendingRequests.push(req);
++            }
++        });
++    }
++
++    async _processPromptRequest(sender, snapAppID, snapAppPid) {
++        const promptWindow = await this._findPromptWindow(sender);
++
++        if (!promptWindow)
++            throw new Error(`Prompt Window for sender ${sender} not found`);
++
++        const snapApp = this._windowsTracker.get_app_from_pid(snapAppPid);
++        if (!snapApp)
++            throw new Error(`Snap application for PID ${snapAppPid} not found`);
++
++        const snapWindows = snapApp.get_windows().filter(
++            w => w.get_pid() === snapAppPid && w.get_sandboxed_app_id() === snapAppID);
++
++        if (!snapWindows?.length)
++            throw new Error(`Snap window(s) with App ID ${snapAppID} and PID ${snapAppPid} not found`);
++
++        const windowGroup = new WindowsGroup(promptWindow, snapApp, snapWindows);
++        this._windowsGroups.push(windowGroup);
++        promptWindow.connectObject('unmanaging', () => {
++            this._windowsGroups = this._windowsGroups.filter(wg => wg !== windowGroup);
++            windowGroup.destroy();
++        }, this);
++    }
++
++    async _findPromptWindow(sender) {
++        // FIXME: we need to have only one window in this way...
++        // const promptWindow = Utils.getWindowByBusName(sender);
++        // So, expose also the GTK Window object path or window ID in the API.
++        const allWindows = global.display.list_all_windows();
++        const promptWindow = allWindows.find(w =>
++            w.get_gtk_unique_bus_name() === sender);
++
++        if (promptWindow)
++            return promptWindow;
++
++        const senderPID = await Utils.getSenderPid(sender);
++        return allWindows.find(w => w.get_pid() === senderPID);
++    }
++
++    _checkSenderValidity(sender) {
++        // TODO:!
++        // We need to ensure also that this sender is matching a known unique name...
++        // So as first, set a name watcher, and only accept connections from that name.
++        console.log('_checkSenderValidity: Expecting', this._promptDialogName, 'got', sender);
++        // Maybe double-check that the PID of the prompting window matches the snap ID
++        return true;
++    }
++
++    destroy() {
++        this._windowsTracker = null;
++        this._injectionManager.clear();
++        this._injectionManager = null;
++        this._pendingRequests.forEach(req => GLib.source_remove(req.timeoutID));
++
++        super.destroy();
++    }
++});
+diff --git a/extensions/snapd-prompting@canonical.com/utils.js b/extensions/snapd-prompting@canonical.com/utils.js
+new file mode 100644
+index 0000000..2b6e465
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/utils.js
+@@ -0,0 +1,513 @@
++import {
++    GLib,
++    Gio,
++    GObject,
++    Shell,
++} from './dependencies/gi.js';
++
++import {SignalTracker} from './dependencies/shell/misc.js';
++
++const {_gi: Gi} = imports;
++
++export const SignalsHandlerFlags = Object.freeze({
++    NONE: 0,
++    CONNECT_AFTER: 1,
++});
++
++const GENERIC_KEY = Symbol('generic');
++
++export const DestroyableGObject = GObject.registerClass({
++    Signals: {'destroy': {}},
++}, class DestroyableGObject extends GObject.Object {
++    destroy() {
++        this.emit('destroy');
++    }
++});
++
++SignalTracker.registerDestroyableType(DestroyableGObject);
++
++/**
++ * Simplify global signals and function injections handling
++ * abstract class
++ */
++const BasicHandler = class DashToDockBasicHandler {
++    static get genericKey() {
++        return GENERIC_KEY;
++    }
++
++    constructor(parentObject) {
++        this._storage = Object.create(null);
++
++        if (parentObject) {
++            if (!(parentObject.connect instanceof Function))
++                throw new TypeError('Not a valid parent object');
++
++            if (!(parentObject instanceof GObject.Object) ||
++                GObject.signal_lookup('destroy', parentObject.constructor.$gtype)) {
++                this._parentObject = parentObject;
++                this._destroyId = parentObject.connect('destroy', () => this.destroy());
++            }
++        }
++    }
++
++    add(...args) {
++        // Convert arguments object to array, concatenate with generic
++        // Call addWithLabel with ags as if they were passed arguments
++        this.addWithLabel(GENERIC_KEY, ...args);
++    }
++
++    clear() {
++        Object.getOwnPropertySymbols(this._storage).forEach(label =>
++            this.removeWithLabel(label));
++    }
++
++    destroy() {
++        this._parentObject?.disconnect(this._destroyId);
++        this._parentObject = null;
++
++        this.clear();
++    }
++
++    block() {
++        Object.getOwnPropertySymbols(this._storage).forEach(label =>
++            this.blockWithLabel(label));
++    }
++
++    unblock() {
++        Object.getOwnPropertySymbols(this._storage).forEach(label =>
++            this.unblockWithLabel(label));
++    }
++
++    addWithLabel(label, ...args) {
++        if (typeof label !== 'symbol')
++            throw new Error(`Invalid label ${label}, must be a symbol`);
++
++        let argsArray = [...args];
++        if (argsArray.every(arg => !Array.isArray(arg)))
++            argsArray = [argsArray];
++
++        if (this._storage[label] === undefined)
++            this._storage[label] = [];
++
++        // Skip first element of the arguments
++        for (const argArray of argsArray) {
++            if (argArray.length < 3)
++                throw new Error('Unexpected number of arguments');
++            const item = this._storage[label];
++            try {
++                item.push(this._create(...argArray));
++            } catch (e) {
++                logError(e);
++            }
++        }
++    }
++
++    removeWithLabel(label) {
++        this._storage[label]?.reverse().forEach(item => this._remove(item));
++        delete this._storage[label];
++    }
++
++    blockWithLabel(label) {
++        (this._storage[label] || []).forEach(item => this._block(item));
++    }
++
++    unblockWithLabel(label) {
++        (this._storage[label] || []).forEach(item => this._unblock(item));
++    }
++
++    _removeByItem(item) {
++        Object.getOwnPropertySymbols(this._storage).forEach(label =>
++            (this._storage[label] = this._storage[label].filter(it => {
++                if (!this._itemsEqual(it, item))
++                    return true;
++                this._remove(item);
++                return false;
++            })));
++    }
++
++    // Virtual methods to be implemented by subclass
++
++    /**
++     * Create single element to be stored in the storage structure
++     *
++     * @param _object
++     * @param _element
++     * @param _callback
++     */
++    _create(_object, _element, _callback) {
++        throw new GObject.NotImplementedError(`_create in ${this.constructor.name}`);
++    }
++
++    /**
++     * Correctly delete single element
++     *
++     * @param _item
++     */
++    _remove(_item) {
++        throw new GObject.NotImplementedError(`_remove in ${this.constructor.name}`);
++    }
++
++    /**
++     * Block single element
++     *
++     * @param _item
++     */
++    _block(_item) {
++        throw new GObject.NotImplementedError(`_block in ${this.constructor.name}`);
++    }
++
++    /**
++     * Unblock single element
++     *
++     * @param _item
++     */
++    _unblock(_item) {
++        throw new GObject.NotImplementedError(`_unblock in ${this.constructor.name}`);
++    }
++
++    _itemsEqual(itemA, itemB) {
++        if (itemA === itemB)
++            return true;
++
++        if (itemA.length !== itemB.length)
++            return false;
++
++        return itemA.every((_, idx) => itemA[idx] === itemB[idx]);
++    }
++};
++
++/**
++ * Manage global signals
++ */
++export class GlobalSignalsHandler extends BasicHandler {
++    _create(object, event, callback, flags = SignalsHandlerFlags.NONE) {
++        if (!object)
++            throw new Error('Impossible to connect to an invalid object');
++
++        const after = flags === SignalsHandlerFlags.CONNECT_AFTER;
++        const connector = after ? object.connect_after : object.connect;
++
++        if (!connector) {
++            throw new Error(`Requested to connect to signal '${event}', ` +
++                `but no implementation for 'connect${after ? '_after' : ''}' ` +
++                `found in ${object.constructor.name}`);
++        }
++
++        const item = [object];
++        const isDestroy = event === 'destroy';
++        const isParentObject = object === this._parentObject;
++
++        if (isDestroy && !isParentObject) {
++            const originalCallback = callback;
++            callback = () => {
++                this._removeByItem(item);
++                originalCallback();
++            };
++        }
++        const id = connector.call(object, event, callback);
++        item.push(id);
++
++        if (isDestroy && isParentObject) {
++            this._parentObject.disconnect(this._destroyId);
++            this._destroyId =
++                this._parentObject.connect('destroy', () => this.destroy());
++        }
++
++        return item;
++    }
++
++    _remove(item) {
++        const [object, id] = item;
++        object.disconnect(id);
++    }
++
++    _block(item) {
++        const [object, id] = item;
++
++        if (object instanceof GObject.Object)
++            GObject.Object.prototype.block_signal_handler.call(object, id);
++    }
++
++    _unblock(item) {
++        const [object, id] = item;
++
++        if (object instanceof GObject.Object)
++            GObject.Object.prototype.unblock_signal_handler.call(object, id);
++    }
++}
++
++/**
++ * Manage function injection: both instances and prototype can be overridden
++ * and restored
++ */
++export class InjectionsHandler extends BasicHandler {
++    _create(object, name, injectedFunction) {
++        const original = object[name];
++
++        if (!(original instanceof Function))
++            throw new Error(`Function ${name}() is not available for ${object}`);
++
++        object[name] = function (...args) {
++            return injectedFunction.call(this, original, ...args);
++        };
++        return [object, name, original];
++    }
++
++    _remove(item) {
++        const [object, name, original] = item;
++        object[name] = original;
++    }
++}
++
++/**
++ * Manage vfunction injection: both instances and prototype can be overridden
++ * and restored
++ */
++export class VFuncInjectionsHandler extends BasicHandler {
++    _create(prototype, name, injectedFunction) {
++        const original = prototype[`vfunc_${name}`];
++        if (!(original instanceof Function))
++            throw new Error(`Virtual function ${name} is not available for ${prototype}`);
++        this._replaceVFunc(prototype, name, injectedFunction);
++        return [prototype, name];
++    }
++
++    _remove(item) {
++        const [prototype, name] = item;
++        const originalVFunc = prototype[`vfunc_${name}`];
++        try {
++            // This may fail if trying to reset to a never-overridden vfunc
++            // as gjs doesn't consider it a function, even if it's true that
++            // originalVFunc instanceof Function.
++            this._replaceVFunc(prototype, name, originalVFunc);
++        } catch {
++            try {
++                this._replaceVFunc(prototype, name, function (...args) {
++                    // eslint-disable-next-line no-invalid-this
++                    return originalVFunc.call(this, ...args);
++                });
++            } catch (e) {
++                logError(e, `Removing vfunc_${name}`);
++            }
++        }
++    }
++
++    _replaceVFunc(prototype, name, func) {
++        if (Gi.gobject_prototype_symbol && Gi.gobject_prototype_symbol in prototype)
++            prototype = prototype[Gi.gobject_prototype_symbol];
++
++        return prototype[Gi.hook_up_vfunc_symbol](name, func);
++    }
++}
++
++/**
++ * Manage properties injection: both instances and prototype can be overridden
++ * and restored
++ */
++export class PropertyInjectionsHandler extends BasicHandler {
++    constructor(parentObject, params) {
++        super(parentObject);
++        this._params = params;
++    }
++
++    _create(instance, name, injectedPropertyDescriptor) {
++        if (!this._params?.allowNewProperty && !(name in instance))
++            throw new Error(`Object ${instance} has no '${name}' property`);
++
++        const {prototype} = instance.constructor;
++        const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(prototype, name) ??
++            Object.getOwnPropertyDescriptor(instance, name);
++
++        Object.defineProperty(instance, name, {
++            ...originalPropertyDescriptor,
++            ...injectedPropertyDescriptor,
++            ...{configurable: true},
++        });
++        return [instance, name, originalPropertyDescriptor];
++    }
++
++    _remove(item) {
++        const [instance, name, originalPropertyDescriptor] = item;
++        if (originalPropertyDescriptor)
++            Object.defineProperty(instance, name, originalPropertyDescriptor);
++        else
++            delete instance[name];
++    }
++}
++
++/**
++ * Construct a map of gtk application window object paths to MetaWindows.
++ */
++export function getWindowsByObjectPath() {
++    const windowsByObjectPath = new Map();
++    const {workspaceManager} = global;
++    const workspaces = [...new Array(workspaceManager.nWorkspaces)].map(
++        (_c, i) => workspaceManager.get_workspace_by_index(i));
++
++    workspaces.forEach(ws => {
++        ws.list_windows().forEach(w => {
++            const path = w.get_gtk_window_object_path();
++            if (path)
++                windowsByObjectPath.set(path, w);
++        });
++    });
++
++    return windowsByObjectPath;
++}
++
++export function getWindowsByBusName() {
++    const windowsByObjectPath = new Map();
++    const {workspaceManager} = global;
++    const workspaces = [...new Array(workspaceManager.nWorkspaces)].map(
++        (_c, i) => workspaceManager.get_workspace_by_index(i));
++
++    workspaces.forEach(ws => {
++        ws.list_windows().forEach(w => {
++            const busName = w.get_gtk_unique_bus_name();
++            if (busName)
++                windowsByObjectPath.set(busName, w);
++        });
++    });
++
++    return windowsByObjectPath;
++}
++
++export function getWindowByBusName(busName) {
++    return global.display.list_all_windows().find(w =>
++        w.get_gtk_unique_bus_name() === busName);
++}
++
++/**
++ * Re-implements shell_app_compare so that can be used to resort running apps
++ *
++ * @param appA
++ * @param appB
++ */
++export function shellAppCompare(appA, appB) {
++    if (appA.state !== appB.state) {
++        if (appA.state === Shell.AppState.RUNNING)
++            return -1;
++        return 1;
++    }
++
++    const windowsA = appA.get_windows();
++    const windowsB = appB.get_windows();
++
++    const isMinimized = windows => !windows.some(w => w.showing_on_its_workspace());
++    const minimizedB = isMinimized(windowsB);
++    if (isMinimized(windowsA) !== minimizedB) {
++        if (minimizedB)
++            return -1;
++        return 1;
++    }
++
++    if (appA.state === Shell.AppState.RUNNING) {
++        if (windowsA.length && !windowsB.length)
++            return -1;
++        else if (!windowsA.length && windowsB.length)
++            return 1;
++
++        const lastUserTime = windows =>
++            Math.max(...windows.map(w => w.get_user_time()));
++        return lastUserTime(windowsB) - lastUserTime(windowsA);
++    }
++
++    return 0;
++}
++
++/**
++ * Re-implements shell_app_compare_windows
++ *
++ * @param winA
++ * @param winB
++ */
++export function shellWindowsCompare(winA, winB) {
++    const activeWorkspace = global.workspaceManager.get_active_workspace();
++    const wsA = winA.get_workspace() === activeWorkspace;
++    const wsB = winB.get_workspace() === activeWorkspace;
++
++    if (wsA && !wsB)
++        return -1;
++    else if (!wsA && wsB)
++        return 1;
++
++    const visA = winA.showing_on_its_workspace();
++    const visB = winB.showing_on_its_workspace();
++
++    if (visA && !visB)
++        return -1;
++    else if (!visA && visB)
++        return 1;
++
++    return winB.get_user_time() - winA.get_user_time();
++}
++
++export const CancellableChild = GObject.registerClass({
++    Properties: {
++        'parent': GObject.ParamSpec.object(
++            'parent', 'parent', 'parent',
++            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
++            Gio.Cancellable.$gtype),
++    },
++},
++class CancellableChild extends Gio.Cancellable {
++    _init(parent) {
++        if (parent && !(parent instanceof Gio.Cancellable))
++            throw TypeError('Not a valid cancellable');
++
++        super._init({parent});
++
++        if (parent?.is_cancelled()) {
++            this.cancel();
++            return;
++        }
++
++        this._connectToParent();
++    }
++
++    _connectToParent() {
++        this._connectId = this.parent?.connect(() => {
++            this._realCancel();
++
++            if (this._disconnectIdle)
++                return;
++
++            this._disconnectIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
++                delete this._disconnectIdle;
++                this._disconnectFromParent();
++                return GLib.SOURCE_REMOVE;
++            });
++        });
++    }
++
++    _disconnectFromParent() {
++        if (this._connectId && !this._disconnectIdle) {
++            this.parent.disconnect(this._connectId);
++            delete this._connectId;
++        }
++    }
++
++    _realCancel() {
++        Gio.Cancellable.prototype.cancel.call(this);
++    }
++
++    cancel() {
++        this._disconnectFromParent();
++        this._realCancel();
++    }
++});
++
++export async function getSenderPid(sender) {
++    const res = await Gio.DBus.session.call(
++        'org.freedesktop.DBus',
++        '/',
++        'org.freedesktop.DBus',
++        'GetConnectionUnixProcessID',
++        new GLib.Variant('(s)', [sender]),
++        new GLib.VariantType('(u)'),
++        Gio.DBusCallFlags.NONE,
++        -1,
++        null);
++    const [pid] = res.deepUnpack();
++    return pid;
++}
+diff --git a/extensions/snapd-prompting@canonical.com/windowsGroup.js b/extensions/snapd-prompting@canonical.com/windowsGroup.js
+new file mode 100644
+index 0000000..7421484
+--- /dev/null
++++ b/extensions/snapd-prompting@canonical.com/windowsGroup.js
+@@ -0,0 +1,248 @@
++/* promptsHandler.js
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Marco Trevisan <marco@ubuntu.com>
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ */
++
++import {
++    Meta,
++    Shell,
++    GObject,
++} from './dependencies/gi.js';
++
++import {
++    Main,
++} from './dependencies/shell/ui.js';
++
++import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
++
++import * as Utils from './utils.js';
++import {SignalTracker} from './dependencies/shell/misc.js';
++
++export const WindowsGroup = GObject.registerClass(
++class WindowsGroup extends Utils.DestroyableGObject {
++    constructor(promptWindow, snapApp, snapWindows) {
++        super();
++
++        this._injectionManager = new InjectionManager();
++
++        this._snapApp = snapApp;
++        this._snapWindows = snapWindows;
++
++        this._promptWindow = promptWindow;
++        this._promptApp = Shell.WindowTracker.get_default().get_window_app(promptWindow);
++
++        this._lastFocusedSnapWindowSignals = new SignalTracker.TransientSignalHolder(this);
++
++        this._injectionManager.overrideMethod(this._promptApp, 'launch', () => () => {});
++        this._injectionManager.overrideMethod(this._promptApp, 'launch_action', () => () => {});
++        this._injectionManager.overrideMethod(this._promptApp, 'open_new_window', () => () => {});
++        this._injectionManager.overrideMethod(this._promptApp, 'create_icon_texture', () => {
++            return function (...args) {
++                return snapApp.create_icon_texture(...args);
++            };
++        });
++        this._injectionManager.overrideMethod(this._promptApp, 'can_open_new-window', () => () => false);
++        this._injectionManager.overrideMethod(this._promptApp, 'get_windows', () => () => []);
++
++        this._injectionManager.overrideMethod(this._snapApp, 'get_windows', originalMethod => {
++            return function (...args) {
++                // eslint-disable-next-line no-invalid-this
++                return [...originalMethod.call(this, ...args) ?? [], promptWindow];
++            };
++        });
++
++        this._injectionManager.overrideMethod(this._snapApp, 'activate', () => () =>
++            Main.activateWindow(this._promptWindow));
++        this._injectionManager.overrideMethod(this._snapApp, 'activate_window', () => () =>
++            Main.activateWindow(this._promptWindow));
++        this._injectionManager.overrideMethod(this._snapApp, 'activate_full', () => () =>
++            Main.activateWindow(this._promptWindow));
++
++        this._snapWindows.forEach(w => {
++            this._injectionManager.overrideMethod(w, 'has_attached_dialogs', () => () => true);
++            Main.wm._checkDimming(w);
++
++            const replicatedMethods = [
++                'activate',
++                'activate_with_focus',
++                'activate_with_workspace',
++                'focus',
++                'make_above',
++                'raise',
++                'raise_and_make_recent_on_workspace',
++            ];
++
++            replicatedMethods.forEach(method => {
++                this._injectionManager.overrideMethod(w, method, originalMethod => (...args) => {
++                    originalMethod.call(w, ...args);
++                    this._promptWindow[method](...args);
++                });
++            });
++
++            const updatePromptWindowPosition = () => {
++                this._lastRect = null;
++                Main.activateWindow(this._promptWindow);
++                this._adjustPromptPosition();
++                Main.wm._checkDimming(w);
++            };
++
++            w.connectObject('focus', updatePromptWindowPosition,
++                GObject.ConnectFlags.AFTER, this);
++
++            w.connectObject('raised', updatePromptWindowPosition,
++                GObject.ConnectFlags.AFTER, this);
++        });
++
++        this._promptWindow.connectObject('focus', () => () => {
++            this._lastFocusedSnapWindow().focus(global.get_current_time());
++        }, GObject.ConnectFlags.AFTER, this._lastFocusedSnapWindowSignals);
++        this._promptWindow.connectObject('raised', () => () =>
++            this._lastFocusedSnapWindow().raise(),
++        GObject.ConnectFlags.AFTER, this._lastFocusedSnapWindowSignals);
++
++        this._promptWindowInjections = new InjectionManager();
++        this._promptWindowInjections.overrideMethod(this._promptWindow,
++            'is_attached_dialog', () => () => true);
++        this._promptWindowInjections.overrideMethod(this._promptWindow,
++            'get_transient_for', () => () => this._lastFocusedSnapWindow());
++
++        this._promptWindow.set_type(Meta.WindowType.MODAL_DIALOG);
++        this._promptWindow.hide_from_window_list();
++
++        this._snapApp.emit('windows-changed');
++
++        // track the prompt window for closed signal...
++        // And destroy everything...
++
++        // TODO: Track all the windows, although they should not change.
++        // Even though a snap could still open another window with different PID.
++        // But we show the dialog over all of them...
++
++        // TODO:
++        // -make urgent and demand attention!
++
++        this._promptWindow.set_demands_attention();
++
++        this._adjustPromptPosition();
++        this._monitorPromptWindowChanges();
++        this._monitorLastSnapdWindowChanges();
++    }
++
++    _monitorLastSnapdWindowChanges() {
++        this._lastFocusedSnapWindowChangesSignals?.destroy();
++        this._lastFocusedSnapWindowChangesSignals = new SignalTracker.TransientSignalHolder(
++            this._lastFocusedSnapWindowSignals);
++        this._monitorWindowChanges(this._lastFocusedSnapWindow(),
++            this._lastFocusedSnapWindowChangesSignals);
++    }
++
++    _monitorPromptWindowChanges() {
++        this._promptWindowChangesSignals?.destroy();
++        this._promptWindowChangesSignals = new SignalTracker.TransientSignalHolder(this);
++        this._monitorWindowChanges(this._promptWindow, this._promptWindowChangesSignals);
++    }
++
++    _monitorWindowChanges(window, signalsTracker) {
++        window.connectObject('position-changed',
++            () => this._adjustPromptPosition(), signalsTracker);
++        window.connectObject('size-changed',
++            () => this._adjustPromptPosition(), signalsTracker);
++    }
++
++    _adjustPromptPosition() {
++        const lastFocusedSnapWindow = this._lastFocusedSnapWindow();
++        const promptWindowFrameRect = this._promptWindow.get_frame_rect();
++        const frameRect = lastFocusedSnapWindow.get_frame_rect();
++
++        if (this._lastRect &&
++            this._lastRect.width === promptWindowFrameRect.width &&
++            this._lastRect.height === promptWindowFrameRect.height &&
++            !lastFocusedSnapWindow.is_maximized()) {
++            const newX = frameRect.x + (promptWindowFrameRect.x - this._lastRect.x);
++            const newY = frameRect.y + (promptWindowFrameRect.y - this._lastRect.y);
++
++            this._lastFocusedSnapWindowChangesSignals?.destroy();
++            lastFocusedSnapWindow.move_resize_frame(
++                true,
++                newX,
++                newY,
++                frameRect.width,
++                frameRect.height
++            );
++            this._monitorLastSnapdWindowChanges();
++
++            this._lastRect = promptWindowFrameRect;
++
++            return;
++        }
++
++        const centerX = frameRect.x + frameRect.width / 2;
++        const centerY = frameRect.y + frameRect.height / 2;
++
++        const newX = Math.round(centerX - promptWindowFrameRect.width / 2);
++        const newY = Math.round(centerY - promptWindowFrameRect.height / 2);
++
++        // CLAMP TO WORKSPACE!
++
++        this._promptWindowChangesSignals?.destroy();
++        this._promptWindow.move_resize_frame(
++            true,
++            newX,
++            newY,
++            promptWindowFrameRect.width,
++            promptWindowFrameRect.height
++        );
++        this._monitorPromptWindowChanges();
++
++        this._lastRect = {
++            x: newX,
++            y: newY,
++            width: promptWindowFrameRect.width,
++            height: promptWindowFrameRect.height,
++        };
++    }
++
++    _lastFocusedSnapWindow() {
++        const maxUserTime = Math.max(...this._snapWindows.map(w => w.get_user_time()));
++        return this._snapWindows.find(w => w.get_user_time() === maxUserTime);
++    }
++
++    get promptWindow() {
++        return this._promptWindow;
++    }
++
++    get promptApp() {
++        return this._promptApp;
++    }
++
++    get snapApp() {
++        return this._snapApp;
++    }
++
++    get snapWindows() {
++        return this._snapWindows;
++    }
++
++    destroy() {
++        this._injectionManager.clear();
++        this._injectionManager = null;
++        this._snapWindows.forEach(w => Main.wm._checkDimming(w));
++
++        super.destroy();
++    }
++});
+diff --git a/meson.build b/meson.build
+index df593c6..c5cef29 100644
+--- a/meson.build
++++ b/meson.build
+@@ -317,6 +317,7 @@ subdir('data')
+ subdir('js')
+ subdir('src')
+ subdir('po')
++subdir('extensions')
+ 
+ if get_option('tests') and have_x11_client
+   subdir('tests')
diff -pruN 49.0-1/debian/patches/ubuntu/gdm_alternatives.patch 49.0-1ubuntu1/debian/patches/ubuntu/gdm_alternatives.patch
--- 49.0-1/debian/patches/ubuntu/gdm_alternatives.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/gdm_alternatives.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,42 @@
+From: Jeremy Soller <jeremy@system76.com>
+Date: Wed, 20 Jun 2018 19:22:06 +0200
+Subject: Add support for GDM theme alternatives
+
+GNOME vanilla and systemd76 derivative ships their own GDM theme.
+
+This allows to provide alternative gresource file for gdm3 that must contain
+a `gdm3.css` stylesheet that will be applied.
+
+Bug: https://bugzilla.gnome.org/show_bug.cgi?id=787454
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gnome-shell/+bug/1715722
+Last-Update: 2017-09-08
+---
+ data/gnome-shell-theme.gresource.xml | 1 +
+ js/ui/sessionMode.js                 | 2 ++
+ 2 files changed, 3 insertions(+)
+
+diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml
+index 30d9dc4..544ce4b 100644
+--- a/data/gnome-shell-theme.gresource.xml
++++ b/data/gnome-shell-theme.gresource.xml
+@@ -3,6 +3,7 @@
+   <gresource prefix="/org/gnome/shell/theme">
+     <file>calendar-today.svg</file>
+     <file>calendar-today-light.svg</file>
++    <file alias="gdm.css">gnome-shell-dark.css</file>
+     <file>gnome-shell-dark.css</file>
+     <file>gnome-shell-light.css</file>
+     <file>gnome-shell-high-contrast.css</file>
+diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
+index e45fce0..dea9be9 100644
+--- a/js/ui/sessionMode.js
++++ b/js/ui/sessionMode.js
+@@ -50,6 +50,8 @@ const _modes = {
+ 
+     'gdm': {
+         hasNotifications: true,
++        stylesheetName: 'gdm.css',
++        themeResourceName: 'gdm-theme.gresource',
+         isGreeter: true,
+         isPrimary: true,
+         unlockDialog: LoginDialog,
diff -pruN 49.0-1/debian/patches/ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch 49.0-1ubuntu1/debian/patches/ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch
--- 49.0-1/debian/patches/ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/keep-ubuntu-logo-bright-lp1867133-v1.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,36 @@
+From: Daniel van Vugt <daniel.van.vugt@canonical.com>
+Date: Thu, 2 Apr 2020 17:16:27 +0800
+Subject: Keep the Ubuntu logo at full brightness during startup animation
+
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1867133
+Forwarded: not-needed
+Last-Update: 2020-03-18
+---
+ js/gdm/loginDialog.js | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index 26966b6..02fb16f 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -1549,7 +1549,11 @@ export const LoginDialog = GObject.registerClass({
+             {sortGroup: CtrlAltTab.SortGroup.MIDDLE});
+         this.activate();
+ 
+-        this.opacity = 0;
++        // Clutter doesn't yet fully support invisible parents with forced
++        // visible children and will make everything invisible (flicker) on
++        // the first frame if we start at 0. So we start at 1 instead...
++        this.opacity = 1;
++        this._logoBin.set_opacity_override(255);
+ 
+         this._grab = Main.pushModal(global.stage, {actionMode: Shell.ActionMode.LOGIN_SCREEN});
+ 
+@@ -1557,6 +1561,7 @@ export const LoginDialog = GObject.registerClass({
+             opacity: 255,
+             duration: 1000,
+             mode: Clutter.AnimationMode.EASE_IN_QUAD,
++            onComplete: () => { this._logoBin.set_opacity_override(-1); },
+         });
+ 
+         return true;
diff -pruN 49.0-1/debian/patches/ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch 49.0-1ubuntu1/debian/patches/ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch
--- 49.0-1/debian/patches/ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/layout-Try-to-allocate-before-getting-size-of-tracke.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,34 @@
+From: Daniel van Vugt <daniel.van.vugt@canonical.com>
+Date: Wed, 31 Mar 2021 17:59:09 +0800
+Subject: layout: Try to allocate before getting size of tracked actors
+
+Because we're about to `get_transformed_{position,size}` of each,
+which will return NaNs if not yet allocated. Those NaNs were finding
+their way into the workspace strut definitions on startup and not
+getting corrected until after the startup animation completed. This
+meant any extensions depending on the `workareas-changed` signal were
+getting an incorrect workarea (the whole workspace) and so were
+rendered out of place during the login animation. Now they're not.
+
+Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
+Origin: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1785
+Bug-Ubuntu: https://launchpad.net/bugs/1917939, https://launchpad.net/bugs/1919979
+Bug-GNOME: https://gitlab.gnome.org/GNOME/mutter/-/issues/1627
+Forwarded: yes
+Last-Update: 2021-04-07
+---
+ js/ui/layout.js | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/js/ui/layout.js b/js/ui/layout.js
+index 354ef60..270da55 100644
+--- a/js/ui/layout.js
++++ b/js/ui/layout.js
+@@ -1069,6 +1069,7 @@ export const LayoutManager = GObject.registerClass({
+             if (!(actorData.affectsInputRegion && wantsInputRegion) && !actorData.affectsStruts)
+                 continue;
+ 
++            actorData.actor.get_allocation_box();
+             let [x, y] = actorData.actor.get_transformed_position();
+             let [w, h] = actorData.actor.get_transformed_size();
+             x = Math.round(x);
diff -pruN 49.0-1/debian/patches/ubuntu/lightdm-user-switching.patch 49.0-1ubuntu1/debian/patches/ubuntu/lightdm-user-switching.patch
--- 49.0-1/debian/patches/ubuntu/lightdm-user-switching.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/lightdm-user-switching.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,93 @@
+From: Tim Lunn <tim@feathertop.org>
+Date: Tue, 9 Oct 2012 11:18:28 +0200
+Subject: [PATCH] userMenu: allow user switching when using lightdm
+
+When running lightdm and gnome-shell, its currently not possible to
+switch users via the usermenu. This commit adds a dbus call to
+switch to the lightdm greeter.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=685794
+---
+ js/misc/systemActions.js | 47 ++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 40 insertions(+), 7 deletions(-)
+
+diff --git a/js/misc/systemActions.js b/js/misc/systemActions.js
+index c24d254..9f2c143 100644
+--- a/js/misc/systemActions.js
++++ b/js/misc/systemActions.js
+@@ -237,6 +237,34 @@ const SystemActions = GObject.registerClass({
+         return this._actions.get(LOCK_ORIENTATION_ACTION_ID).iconName;
+     }
+ 
++    _lightdmLoginSession() {
++        try {
++            let seat = GLib.getenv("XDG_SEAT_PATH");
++            let result = Gio.DBus.system.call_sync('org.freedesktop.DisplayManager',
++                                                   seat,
++                                                   'org.freedesktop.DisplayManager.Seat',
++                                                   'SwitchToGreeter', null, null,
++                                                   Gio.DBusCallFlags.NONE,
++                                                   -1, null);
++            return result;
++        } catch(e) {
++            return false;
++        }
++    }
++
++    _sensorProxyAppeared() {
++        this._sensorProxy = new SensorProxy(Gio.DBus.system, SENSOR_BUS_NAME, SENSOR_OBJECT_PATH,
++            (proxy, error)  => {
++                if (error) {
++                    log(error.message);
++                    return;
++                }
++                this._sensorProxy.connect('g-properties-changed',
++                                          () => { this._updateOrientationLock(); });
++                this._updateOrientationLock();
++            });
++    }
++
+     _updateOrientationLock() {
+         const available = this._monitorManager.get_panel_orientation_managed();
+ 
+@@ -338,7 +366,7 @@ const SystemActions = GObject.registerClass({
+     _updateLockScreen() {
+         let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+         let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
+-        this._actions.get(LOCK_SCREEN_ACTION_ID).available = showLock && allowLockScreen && LoginManager.canLock();
++        this._actions.get(LOCK_SCREEN_ACTION_ID).available = showLock && allowLockScreen;
+         this.notify('can-lock-screen');
+     }
+ 
+@@ -427,20 +455,25 @@ const SystemActions = GObject.registerClass({
+         if (!this._actions.get(LOCK_SCREEN_ACTION_ID).available)
+             throw new Error('The lock-screen action is not available!');
+ 
+-        Main.screenShield.lock(true);
++        if (Main.screenShield)
++            Main.screenShield.lock(true);
++        else
++            this._lightdmLoginSession();
+     }
+ 
+     activateSwitchUser() {
+         if (!this._actions.get(SWITCH_USER_ACTION_ID).available)
+             throw new Error('The switch-user action is not available!');
+ 
+-        if (Main.screenShield)
++        if (Main.screenShield) {
+             Main.screenShield.lock(false);
+ 
+-        Clutter.threads_add_repaint_func(Clutter.RepaintFlags.POST_PAINT, () => {
+-            Gdm.goto_login_session_sync(null);
+-            return false;
+-        });
++            Clutter.threads_add_repaint_func(Clutter.RepaintFlags.POST_PAINT, () => {
++                Gdm.goto_login_session_sync(null);
++                return false;
++            });
++        } else
++            this._lightdmLoginSession();
+     }
+ 
+     activateLogout() {
diff -pruN 49.0-1/debian/patches/ubuntu/lock_on_suspend.patch 49.0-1ubuntu1/debian/patches/ubuntu/lock_on_suspend.patch
--- 49.0-1/debian/patches/ubuntu/lock_on_suspend.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/lock_on_suspend.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,48 @@
+From: Tim Lunn <tim@feathertop.org>
+Date: Wed, 20 Jun 2018 19:22:06 +0200
+Subject: add support for the ubuntu lock on suspend option
+
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1063110
+---
+ js/ui/screenShield.js | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
+index 2087c39..b3bbcac 100644
+--- a/js/ui/screenShield.js
++++ b/js/ui/screenShield.js
+@@ -24,6 +24,7 @@ import {adjustAnimationTime} from '../misc/animationUtils.js';
+ const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
+ const LOCK_ENABLED_KEY = 'lock-enabled';
+ const LOCK_DELAY_KEY = 'lock-delay';
++const SUSPEND_LOCK_ENABLED_KEY = 'ubuntu-lock-on-suspend';
+ 
+ const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
+ const DISABLE_LOCK_KEY = 'disable-lock-screen';
+@@ -108,6 +109,7 @@ export class ScreenShield extends Signals.EventEmitter {
+ 
+         this._settings = new Gio.Settings({schema_id: SCREENSAVER_SCHEMA});
+         this._settings.connect(`changed::${LOCK_ENABLED_KEY}`, this._syncInhibitor.bind(this));
++        this._settings.connect(`changed::${SUSPEND_LOCK_ENABLED_KEY}`, this._syncInhibitor.bind(this));
+ 
+         this._lockSettings = new Gio.Settings({schema_id: LOCKDOWN_SCHEMA});
+         this._lockSettings.connect(`changed::${DISABLE_LOCK_KEY}`, this._syncInhibitor.bind(this));
+@@ -213,7 +215,8 @@ export class ScreenShield extends Signals.EventEmitter {
+     }
+ 
+     async _syncInhibitor() {
+-        const lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
++        const lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY) ||
++                            this._settings.get_boolean(SUSPEND_LOCK_ENABLED_KEY);
+         const lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
+         const inhibit = !!this._loginSession && this._loginSession.Active &&
+                          !this._isActive && lockEnabled && !lockLocked &&
+@@ -244,7 +247,7 @@ export class ScreenShield extends Signals.EventEmitter {
+ 
+     _prepareForSleep(loginManager, aboutToSuspend) {
+         if (aboutToSuspend) {
+-            if (this._settings.get_boolean(LOCK_ENABLED_KEY))
++            if (this._settings.get_boolean(SUSPEND_LOCK_ENABLED_KEY))
+                 this.lock(true);
+         } else {
+             this._wakeUpScreen();
diff -pruN 49.0-1/debian/patches/ubuntu/main-Add-support-for-playing-a-startup-sound-if-enabled-i.patch 49.0-1ubuntu1/debian/patches/ubuntu/main-Add-support-for-playing-a-startup-sound-if-enabled-i.patch
--- 49.0-1/debian/patches/ubuntu/main-Add-support-for-playing-a-startup-sound-if-enabled-i.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/main-Add-support-for-playing-a-startup-sound-if-enabled-i.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,61 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 15 Aug 2024 05:56:11 -0400
+Subject: main: Add support for playing a startup sound if enabled in settings
+
+---
+ data/org.gnome.shell.ubuntu.gschema.xml.in |  7 +++++++
+ js/ui/main.js                              | 17 +++++++++++++++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/data/org.gnome.shell.ubuntu.gschema.xml.in b/data/org.gnome.shell.ubuntu.gschema.xml.in
+index 6f9cc62..10b7dbb 100644
+--- a/data/org.gnome.shell.ubuntu.gschema.xml.in
++++ b/data/org.gnome.shell.ubuntu.gschema.xml.in
+@@ -13,5 +13,12 @@
+         The preferred color scheme for the shell user interface. Valid values are “default”, “prefer-dark”, “prefer-light”.
+       </description>
+     </key>
++    <key name="startup-sound" type="s">
++      <default>''</default>
++      <summary>Startup sound</summary>
++      <description>
++        The startup sound file name for the current sound theme or a file path.
++      </description>
++    </key>
+   </schema>
+ </schemalist>
+diff --git a/js/ui/main.js b/js/ui/main.js
+index 208ffc9..f9d57a5 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -14,6 +14,7 @@ import * as BrightnessManager from '../misc/brightnessManager.js';
+ import * as Config from '../misc/config.js';
+ import * as Components from './components.js';
+ import * as CtrlAltTab from './ctrlAltTab.js';
++import * as Desktop from '../misc/desktop.js';
+ import * as EndSessionDialog from './endSessionDialog.js';
+ import * as ExtensionSystem from './extensionSystem.js';
+ import * as ExtensionDownloader from './extensionDownloader.js';
+@@ -395,6 +396,22 @@ async function _initializeUI() {
+             let perfOutput = GLib.getenv('SHELL_PERF_OUTPUT');
+             Scripting.runPerfScript(perfModule, perfOutput);
+         }
++
++        if (!screenShield?.locked && Desktop.is('ubuntu')) {
++            const settings = new Gio.Settings({
++                schema_id: 'org.gnome.shell.ubuntu',
++            });
++            const startupSound = settings.get_string('startup-sound');
++            if (startupSound?.length) {
++                const player = global.display.get_sound_player();
++                if (startupSound.startsWith('/')) {
++                    player.play_from_file(
++                        Gio.File.new_for_path(startupSound), _('Startup sound'), null);
++                } else {
++                    player.play_from_theme(startupSound, _('Startup sound'), null);
++                }
++            }
++        }
+     });
+ }
+ 
diff -pruN 49.0-1/debian/patches/ubuntu/main-Support-ubuntu-color-scheme-and-yaru-variants.patch 49.0-1ubuntu1/debian/patches/ubuntu/main-Support-ubuntu-color-scheme-and-yaru-variants.patch
--- 49.0-1/debian/patches/ubuntu/main-Support-ubuntu-color-scheme-and-yaru-variants.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/main-Support-ubuntu-color-scheme-and-yaru-variants.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,299 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Fri, 25 Feb 2022 01:07:12 +0100
+Subject: main: Support ubuntu color-scheme and yaru variants
+
+Make StSettings to compute the main theme and the chosen variant so that
+it can be used to adapt system-wide settings.
+
+Also support a specific shell color scheme to allow having mixed-colors
+shell.
+
+Forwarded: not-needed
+---
+ data/meson.build                           |  8 ++-
+ data/org.gnome.shell.ubuntu.gschema.xml.in | 17 +++++++
+ js/ui/main.js                              | 38 +++++++++++++-
+ src/st/st-settings.c                       | 81 ++++++++++++++++++++++++++++++
+ 4 files changed, 142 insertions(+), 2 deletions(-)
+ create mode 100644 data/org.gnome.shell.ubuntu.gschema.xml.in
+
+diff --git a/data/meson.build b/data/meson.build
+index fcf1f34..2098941 100644
+--- a/data/meson.build
++++ b/data/meson.build
+@@ -109,6 +109,12 @@ schema = configure_file(
+   configuration: schemaconf,
+   install_dir: schemadir
+ )
++schema_ubuntu = configure_file(
++  input: 'org.gnome.shell.ubuntu.gschema.xml.in',
++  output: 'org.gnome.shell.ubuntu.gschema.xml',
++  configuration: schemaconf,
++  install_dir: schemadir
++)
+ schema_ubuntu_login = configure_file(
+   input: 'com.ubuntu.login-screen.gschema.xml.in',
+   output: 'com.ubuntu.login-screen.gschema.xml',
+@@ -158,7 +164,7 @@ endif
+ 
+ # for unit tests - gnome.compile_schemas() only looks in srcdir
+ compiled_schemas = custom_target('compile-schemas',
+-  input: [schema, schema_ubuntu_login],
++  input: [schema, schema_ubuntu, schema_ubuntu_login],
+   output: 'gschemas.compiled',
+   command: [find_program('glib-compile-schemas'), '--strict', data_builddir],
+ )
+diff --git a/data/org.gnome.shell.ubuntu.gschema.xml.in b/data/org.gnome.shell.ubuntu.gschema.xml.in
+new file mode 100644
+index 0000000..6f9cc62
+--- /dev/null
++++ b/data/org.gnome.shell.ubuntu.gschema.xml.in
+@@ -0,0 +1,17 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<schemalist gettext-domain="@GETTEXT_PACKAGE@">
++  <enum id="org.gnome.shell.ubuntu.GDesktopColorScheme">
++    <value value="1" nick="default"/>
++    <value value="2" nick="prefer-dark"/>
++    <value value="3" nick="prefer-light"/>
++  </enum>
++  <schema id="org.gnome.shell.ubuntu" path="/org/gnome/shell/ubuntu/">
++    <key name="color-scheme" enum="org.gnome.shell.ubuntu.GDesktopColorScheme">
++      <default>'default'</default>
++      <summary>Color scheme</summary>
++      <description>
++        The preferred color scheme for the shell user interface. Valid values are “default”, “prefer-dark”, “prefer-light”.
++      </description>
++    </key>
++  </schema>
++</schemalist>
+diff --git a/js/ui/main.js b/js/ui/main.js
+index de9b716..d3ec8a1 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -184,6 +184,7 @@ export async function start() {
+ 
+     St.Settings.get().connect('notify::high-contrast', _loadDefaultStylesheet);
+     St.Settings.get().connect('notify::color-scheme', _loadDefaultStylesheet);
++    St.Settings.get().connect('notify::shell-color-scheme', _loadDefaultStylesheet);
+ 
+     // Initialize ParentalControlsManager before the UI
+     ParentalControlsManager.getDefault();
+@@ -463,7 +464,19 @@ function _getStylesheet(name) {
+ 
+ /** @returns {string} */
+ export function getStyleVariant() {
+-    const {colorScheme} = St.Settings.get();
++    const {colorScheme, shellColorScheme} = St.Settings.get();
++
++    switch (shellColorScheme) {
++    case 'prefer-light':
++        return getStyleVariantForColorScheme(St.SystemColorScheme.PREFER_LIGHT);
++    case 'prefer-dark':
++        return getStyleVariantForColorScheme(St.SystemColorScheme.PREFER_DARK);
++    }
++
++    return getStyleVariantForColorScheme(colorScheme);
++}
++
++function getStyleVariantForColorScheme(colorScheme) {
+     switch (sessionMode.colorScheme) {
+     case 'force-dark':
+         return 'dark';
+@@ -480,6 +493,29 @@ export function getStyleVariant() {
+     }
+ }
+ 
++export function getYaruVariantFromAccent() {
++    switch (St.Settings.get().accentColor) {
++    case St.SystemAccentColor.BLUE:
++      return "blue";
++    case St.SystemAccentColor.TEAL:
++      return "prussiangreen";
++    case St.SystemAccentColor.GREEN:
++      return "olive";
++    case St.SystemAccentColor.YELLOW:
++      return "yellow";
++    case St.SystemAccentColor.ORANGE:
++      return "default";
++    case St.SystemAccentColor.RED:
++      return "red";
++    case St.SystemAccentColor.PINK:
++      return "magenta";
++    case St.SystemAccentColor.PURPLE:
++      return "purple";
++    case St.SystemAccentColor.SLATE:
++      return "sage";
++  }
++}
++
+ function _getDefaultStylesheet() {
+     let stylesheet = null;
+     let name = sessionMode.stylesheetName;
+diff --git a/src/st/st-settings.c b/src/st/st-settings.c
+index 5fe1842..fe0a5c5 100644
+--- a/src/st/st-settings.c
++++ b/src/st/st-settings.c
+@@ -34,6 +34,7 @@
+ #define KEY_ACCENT_COLOR          "accent-color"
+ #define KEY_HIGH_CONTRAST         "high-contrast"
+ #define KEY_GTK_ICON_THEME        "icon-theme"
++#define KEY_GTK_THEME             "gtk-theme"
+ #define KEY_MAGNIFIER_ACTIVE      "screen-magnifier-enabled"
+ #define KEY_DISABLE_SHOW_PASSWORD "disable-show-password"
+ 
+@@ -46,7 +47,9 @@ enum {
+   PROP_COLOR_SCHEME,
+   PROP_ACCENT_COLOR,
+   PROP_HIGH_CONTRAST,
++  PROP_GTK_THEME,
+   PROP_GTK_ICON_THEME,
++  PROP_SHELL_COLOR_SCHEME,
+   PROP_MAGNIFIER_ACTIVE,
+   PROP_SLOW_DOWN_FACTOR,
+   PROP_DISABLE_SHOW_PASSWORD,
+@@ -63,10 +66,12 @@ struct _StSettings
+   GSettings *a11y_applications_settings;
+   GSettings *a11y_interface_settings;
+   GSettings *lockdown_settings;
++  GSettings *ubuntu_settings;
+ 
+   gchar *font_name;
+   gboolean high_contrast;
+   gchar *gtk_icon_theme;
++  gchar *gtk_theme;
+   int inhibit_animations_count;
+   gboolean enable_animations;
+   gboolean primary_paste;
+@@ -228,7 +233,9 @@ st_settings_finalize (GObject *object)
+   g_object_unref (settings->a11y_applications_settings);
+   g_object_unref (settings->a11y_interface_settings);
+   g_object_unref (settings->lockdown_settings);
++  g_object_unref (settings->ubuntu_settings);
+   g_free (settings->font_name);
++  g_free (settings->gtk_theme);
+   g_free (settings->gtk_icon_theme);
+ 
+   G_OBJECT_CLASS (st_settings_parent_class)->finalize (object);
+@@ -280,6 +287,19 @@ st_settings_get_property (GObject    *object,
+     case PROP_GTK_ICON_THEME:
+       g_value_set_string (value, settings->gtk_icon_theme);
+       break;
++    case PROP_GTK_THEME:
++      g_value_set_string (value, settings->gtk_theme);
++      break;
++    case PROP_SHELL_COLOR_SCHEME:
++      {
++        g_autoptr (GVariant) user_value = NULL;
++
++        user_value = g_settings_get_user_value (settings->ubuntu_settings,
++                                                KEY_COLOR_SCHEME);
++        g_value_set_string (value, user_value ?
++          g_variant_get_string (user_value, NULL) : NULL);
++      }
++      break;
+     case PROP_COLOR_SCHEME:
+       g_value_set_enum (value, settings->color_scheme);
+       break;
+@@ -383,6 +403,26 @@ st_settings_class_init (StSettingsClass *klass)
+                                                 ST_TYPE_SYSTEM_ACCENT_COLOR,
+                                                 ST_SYSTEM_ACCENT_COLOR_BLUE,
+                                                 ST_PARAM_READABLE);
++  /**
++   * StSettings:gtk-theme:
++   *
++   * The current GTK theme
++   */
++  props[PROP_GTK_THEME] = g_param_spec_string ("gtk-theme",
++                                               "GTK Theme",
++                                               "GTK Theme",
++                                               "",
++                                               ST_PARAM_READABLE);
++  /**
++   * StSettings:shell-color-scheme:
++   *
++   * The current GTK theme
++   */
++  props[PROP_SHELL_COLOR_SCHEME] = g_param_spec_string ("shell-color-scheme",
++                                                        "Shell Color Scheme",
++                                                        "Shell Color Scheme",
++                                                        "default",
++                                                        ST_PARAM_READABLE);
+ 
+   /**
+    * StSettings:magnifier-active:
+@@ -414,6 +454,25 @@ st_settings_class_init (StSettingsClass *klass)
+   g_object_class_install_properties (object_class, N_PROPS, props);
+ }
+ 
++static void
++update_theme_settings (StSettings *settings)
++{
++  g_auto(GStrv) parts = NULL;
++  g_autofree char *theme = NULL;
++
++  theme = g_settings_get_string (settings->interface_settings, KEY_GTK_THEME);
++  parts = g_strsplit (theme, "-", 2);
++
++  if (g_strv_length (parts) > 1)
++    g_set_str (&theme, parts[0]);
++
++  if (g_set_str (&settings->gtk_theme, theme))
++    {
++      g_object_notify_by_pspec (G_OBJECT (settings),
++                                props[PROP_GTK_THEME]);
++    }
++}
++
+ static void
+ on_interface_settings_changed (GSettings   *g_settings,
+                                const gchar *key,
+@@ -435,6 +494,10 @@ on_interface_settings_changed (GSettings   *g_settings,
+       settings->font_name = g_settings_get_string (g_settings, key);
+       g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_FONT_NAME]);
+     }
++  else if (g_str_equal (key, KEY_GTK_THEME))
++    {
++      update_theme_settings (settings);
++    }
+   else if (g_str_equal (key, KEY_GTK_ICON_THEME))
+     {
+       g_free (settings->gtk_icon_theme);
+@@ -455,6 +518,18 @@ on_interface_settings_changed (GSettings   *g_settings,
+     }
+ }
+ 
++static void
++on_ubuntu_settings_changed (GSettings   *g_settings,
++                            const gchar *key,
++                            StSettings  *settings)
++{
++  if (g_str_equal (key, KEY_COLOR_SCHEME))
++    {
++      g_object_notify_by_pspec (G_OBJECT (settings),
++                                props[PROP_SHELL_COLOR_SCHEME]);
++    }
++}
++
+ static void
+ on_mouse_settings_changed (GSettings   *g_settings,
+                            const gchar *key,
+@@ -510,6 +585,10 @@ st_settings_init (StSettings *settings)
+   g_signal_connect (settings->interface_settings, "changed",
+                     G_CALLBACK (on_interface_settings_changed), settings);
+ 
++  settings->ubuntu_settings = g_settings_new ("org.gnome.shell.ubuntu");
++  g_signal_connect (settings->ubuntu_settings, "changed",
++                    G_CALLBACK (on_ubuntu_settings_changed), settings);
++
+   settings->mouse_settings = g_settings_new ("org.gnome.desktop.peripherals.mouse");
+   g_signal_connect (settings->mouse_settings, "changed",
+                     G_CALLBACK (on_mouse_settings_changed), settings);
+@@ -526,6 +605,8 @@ st_settings_init (StSettings *settings)
+   g_signal_connect (settings->lockdown_settings, "changed",
+                     G_CALLBACK (on_lockdown_settings_changed), settings);
+ 
++  update_theme_settings (settings);
++
+   settings->enable_animations = g_settings_get_boolean (settings->interface_settings,
+                                                         KEY_ENABLE_ANIMATIONS);
+   settings->primary_paste = g_settings_get_boolean (settings->interface_settings,
diff -pruN 49.0-1/debian/patches/ubuntu/resolve_alternate_theme_path.patch 49.0-1ubuntu1/debian/patches/ubuntu/resolve_alternate_theme_path.patch
--- 49.0-1/debian/patches/ubuntu/resolve_alternate_theme_path.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/resolve_alternate_theme_path.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,50 @@
+From: Didier Roche <didrocks@ubuntu.com>
+Date: Tue, 22 Oct 2019 11:22:06 +0200
+Subject: Resolve real path name for theme file
+
+ We are using alternative theme paths. Some of them are symlinks like
+ gdm3.css. It points to a different directory and we need to ensure
+ assets are loaded from the real theme path then (assets path are
+ relative to it).
+ Resolve them symlinks to ensure we use the original file itself when
+ loading the stylesheet.
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gnome-shell/+bug/1798747
+Forwarded: Not-needed (upstream doesn't support officially theming)
+---
+ js/ui/main.js | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/js/ui/main.js b/js/ui/main.js
+index a110e5b..bbf9172 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -431,6 +431,14 @@ async function _handleLockScreenWarning() {
+     }
+ }
+ 
++function _realpath(path) {
++    try {
++        while (GLib.file_test(path, GLib.FileTest.IS_SYMLINK))
++            path = GLib.file_read_link(path);
++    } catch (e) { }
++    return path;
++}
++
+ function _getStylesheet(name) {
+     let stylesheet;
+ 
+@@ -441,12 +449,12 @@ function _getStylesheet(name) {
+     let dataDirs = GLib.get_system_data_dirs();
+     for (let i = 0; i < dataDirs.length; i++) {
+         let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'theme', name]);
+-        stylesheet = Gio.file_new_for_path(path);
++        stylesheet = Gio.file_new_for_path(_realpath(path));
+         if (stylesheet.query_exists(null))
+             return stylesheet;
+     }
+ 
+-    stylesheet = Gio.File.new_for_path(`${global.datadir}/theme/${name}`);
++    stylesheet = Gio.File.new_for_path(_realpath(`${global.datadir}/theme/${name}`));
+     if (stylesheet.query_exists(null))
+         return stylesheet;
+ 
diff -pruN 49.0-1/debian/patches/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch 49.0-1ubuntu1/debian/patches/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch
--- 49.0-1/debian/patches/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/search-call-XUbuntuCancel-method-on-providers-when-no-dat.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,163 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 23 Aug 2018 20:00:57 +0200
+Subject: search: call XUbuntuCancel method on providers when no data is
+ needed
+
+Add XUbuntuCancel method to search providers and call it when a search provider
+is still doing operations.
+Ignore the result when the method does not exist or is cancelled.
+
+This will allow to stop operations on providers.
+
+Fixes LP: #1756826
+
+Bug-GNOME: https://gitlab.gnome.org/GNOME/gnome-shell/issues/183
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/bionic/+source/gnome-shell/+bug/1756826
+Forwarded: not-needed
+---
+ .../org.gnome.ShellSearchProvider.xml              |  6 ++++
+ .../org.gnome.ShellSearchProvider2.xml             |  6 ++++
+ js/ui/remoteSearch.js                              | 12 ++++++++
+ js/ui/search.js                                    | 33 ++++++++++++++++++++++
+ 4 files changed, 57 insertions(+)
+
+diff --git a/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml b/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml
+index 78ad305..393cb01 100644
+--- a/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml
++++ b/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml
+@@ -69,5 +69,11 @@
+     <method name="ActivateResult">
+       <arg type="s" name="identifier" direction="in" />
+     </method>
++
++    <!--
++        XUbuntuCancel:
++        Cancel the current search operation
++    -->
++    <method name="XUbuntuCancel" />
+   </interface>
+ </node>
+diff --git a/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml b/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml
+index 9502340..8141bc0 100644
+--- a/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml
++++ b/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml
+@@ -83,5 +83,11 @@
+       <arg type="as" name="terms" direction="in" />
+       <arg type="u" name="timestamp" direction="in" />
+     </method>
++
++    <!--
++        XUbuntuCancel:
++        Cancel the current search operation
++    -->
++    <method name="XUbuntuCancel" />
+   </interface>
+ </node>
+diff --git a/js/ui/remoteSearch.js b/js/ui/remoteSearch.js
+index b7247ca..f20c876 100644
+--- a/js/ui/remoteSearch.js
++++ b/js/ui/remoteSearch.js
+@@ -28,6 +28,7 @@ const SearchProviderIface = `
+ <method name="ActivateResult">
+     <arg type="s" direction="in" />
+ </method>
++<method name="XUbuntuCancel" />
+ </interface>
+ </node>`;
+ 
+@@ -56,6 +57,7 @@ const SearchProvider2Iface = `
+     <arg type="as" direction="in" />
+     <arg type="u" direction="in" />
+ </method>
++<method name="XUbuntuCancel" />
+ </interface>
+ </node>`;
+ 
+@@ -306,6 +308,16 @@ class RemoteSearchProvider {
+         return resultMetas;
+     }
+ 
++    async XUbuntuCancel(cancellable) {
++        try {
++            await this.proxy.XUbuntuCancelAsync(cancellable);
++        } catch (error) {
++            if (!error.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD) &&
++                !error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
++                log(`Received error from D-Bus search provider ${this.id} during XUbuntuCancel: ${error}`);
++        }
++    }
++
+     activateResult(id) {
+         this.proxy.ActivateResultAsync(id).catch(logError);
+     }
+diff --git a/js/ui/search.js b/js/ui/search.js
+index d7f1df3..1c1a936 100644
+--- a/js/ui/search.js
++++ b/js/ui/search.js
+@@ -227,7 +227,9 @@ const SearchResultsBase = GObject.registerClass({
+         this._cancellable.cancel();
+         this._cancellable.reset();
+ 
++        this.provider.resultsMetasInProgress = true;
+         const metas = await this.provider.getResultMetas(metasNeeded, this._cancellable);
++        this.provider.resultsMetasInProgress = this._cancellable.is_cancelled();
+ 
+         if (this._cancellable.is_cancelled()) {
+             if (metas.length > 0)
+@@ -630,6 +632,10 @@ export const SearchResultsView = GObject.registerClass({
+ 
+         this._searchTimeoutId = 0;
+         this._cancellable = new Gio.Cancellable();
++        this._searchCancelCancellable = new Gio.Cancellable();
++        const cancellableCancelledId = this._cancellable.connect(() =>
++            this._cancelSearchProviderRequest());
++        this.connect('destroy', () => this._cancellable.disconnect(cancellableCancelledId));
+ 
+         this._registerProvider(new AppDisplay.AppSearchProvider());
+ 
+@@ -678,11 +684,31 @@ export const SearchResultsView = GObject.registerClass({
+         }
+     }
+ 
++    _cancelSearchProviderRequest() {
++        if (this._terms.length !== 0 || this._searchCancelTimeoutId)
++            return;
++
++        this._searchCancelTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
++            Promise.all(this._providers.map(async provider => {
++                if (provider.isRemoteProvider &&
++                    (provider.searchInProgress || provider.resultsMetasInProgress)) {
++                    await provider.XUbuntuCancel(this._searchCancelCancellable);
++                    provider.searchInProgress = false;
++                    provider.resultsMetasInProgress = false;
++                }
++            })).catch(logError);
++
++            delete this._searchCancelTimeoutId;
++            return GLib.SOURCE_REMOVE;
++        });
++    }
++
+     _reset() {
+         this._terms = [];
+         this._results = {};
+         this._clearDisplay();
+         this._clearSearchTimeout();
++        this._cancelSearchProviderRequest();
+         this._defaultResult = null;
+         this._startingSearch = false;
+ 
+@@ -754,6 +780,13 @@ export const SearchResultsView = GObject.registerClass({
+         if (this._terms.length > 0)
+             isSubSearch = searchString.indexOf(previousSearchString) === 0;
+ 
++        this._searchCancelCancellable.cancel();
++        this._searchCancelCancellable.reset();
++        if (this._searchCancelTimeoutId) {
++            GLib.source_remove(this._searchCancelTimeoutId);
++            delete this._searchCancelTimeoutId;
++        }
++
+         this._terms = terms;
+         this._isSubSearch = isSubSearch;
+         this._updateSearchProgress();
diff -pruN 49.0-1/debian/patches/ubuntu/secure_mode_extension.patch 49.0-1ubuntu1/debian/patches/ubuntu/secure_mode_extension.patch
--- 49.0-1/debian/patches/ubuntu/secure_mode_extension.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/secure_mode_extension.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,89 @@
+From: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Date: Wed, 20 Jun 2018 19:22:06 +0200
+Subject: Don't allow ubuntu mode extension to update
+
+Ensure that no update is proposed or loaded if sideloaded (always
+prefer system version) on the ubuntu session.
+We want to ensure that the default code running is going through
+our QA and security team process than being loaded from a 3rd
+party website.
+Also, that will enable us to upload newer versions on GNOME
+extension website while still letting older ubuntu release versions
+running expected extension version.
+Origin: ubuntu
+Forwarded: https://bugzilla.gnome.org/show_bug.cgi?id=789852
+---
+ js/ui/extensionDownloader.js | 11 +++++++++++
+ js/ui/extensionSystem.js     |  9 +++++++++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/js/ui/extensionDownloader.js b/js/ui/extensionDownloader.js
+index fe687cb..1bb709c 100644
+--- a/js/ui/extensionDownloader.js
++++ b/js/ui/extensionDownloader.js
+@@ -5,6 +5,7 @@ import GObject from 'gi://GObject';
+ import Soup from 'gi://Soup';
+ 
+ import * as Config from '../misc/config.js';
++import * as Desktop from '../misc/desktop.js';
+ import * as Dialog from './dialog.js';
+ import * as ExtensionUtils from '../misc/extensionUtils.js';
+ import * as FileUtils from '../misc/fileUtils.js';
+@@ -42,6 +43,13 @@ export async function installExtension(uuid, invocation) {
+         shell_version: Config.PACKAGE_VERSION,
+     };
+ 
++    if (Desktop.is('ubuntu') && Main.extensionManager.isModeExtension(uuid)) {
++        const msg = _("This is an extension enabled by your current mode, you can’t install manually any update in that session.");
++        Main.notifyError(_("Can't install “%s”:").format(uuid), msg);
++        invocation.return_dbus_error('org.gnome.Shell.ExtensionError', msg);
++        return;
++    }
++
+     const message = Soup.Message.new_from_encoded_form('GET',
+         REPOSITORY_URL_INFO,
+         Soup.form_encode_hash(params));
+@@ -202,6 +210,9 @@ export async function checkForUpdates() {
+             return;
+         if (extension.hasUpdate)
+             return;
++        // don't updates out of repository mode extension
++        if (Desktop.is('ubuntu') && Main.extensionManager.isModeExtension(uuid))
++            return;
+         metadatas[uuid] = {
+             version: extension.metadata.version,
+         };
+diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js
+index 22121c4..bec5baf 100644
+--- a/js/ui/extensionSystem.js
++++ b/js/ui/extensionSystem.js
+@@ -6,6 +6,7 @@ import Shell from 'gi://Shell';
+ import * as Signals from '../misc/signals.js';
+ 
+ import * as Config from '../misc/config.js';
++import * as Desktop from '../misc/desktop.js';
+ import * as ExtensionDownloader from './extensionDownloader.js';
+ import {formatError} from '../misc/errorUtils.js';
+ import {
+@@ -481,6 +482,10 @@ export class ExtensionManager extends Signals.EventEmitter {
+         await this.loadExtension(newExtension);
+     }
+ 
++    isModeExtension(uuid) {
++        return this._getModeExtensions().indexOf(uuid) !== -1;
++    }
++
+     async _callExtensionInit(uuid) {
+         if (!this._extensionSupportsSessionMode(uuid))
+             return false;
+@@ -726,6 +731,10 @@ export class ExtensionManager extends Signals.EventEmitter {
+             let type = dir.has_prefix(perUserDir)
+                 ? ExtensionType.PER_USER
+                 : ExtensionType.SYSTEM;
++            if (Desktop.is('ubuntu') && this.isModeExtension(uuid) && type === ExtensionType.PER_USER) {
++                log(`Found user extension ${uuid}, but not loading from ${dir.get_path()} directory as part of session mode.`);
++                return null;
++            }
+             try {
+                 extension = this.createExtensionObject(uuid, dir, type);
+             } catch (error) {
diff -pruN 49.0-1/debian/patches/ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch 49.0-1ubuntu1/debian/patches/ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch
--- 49.0-1/debian/patches/ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/sessionMode-Add-support-for-configuring-an-icons-resource.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,38 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 24 Feb 2022 04:22:32 +0100
+Subject: sessionMode: Add support for configuring an icons resource name
+
+Similar to what we did (and upstreamed) for the theme, in this case
+however there's very likely no interest from upstream for this.
+
+Forwarded: not-needed
+---
+ js/ui/main.js        | 2 +-
+ js/ui/sessionMode.js | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/js/ui/main.js b/js/ui/main.js
+index bbf9172..de9b716 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -622,7 +622,7 @@ export function reloadThemeResource() {
+ 
+ /** @private */
+ function _loadIcons() {
+-    _iconResource = Gio.Resource.load(`${global.datadir}/gnome-shell-icons.gresource`);
++    _iconResource = Gio.Resource.load(`${global.datadir}/${sessionMode.iconsResourceName}`);
+     _iconResource._register();
+ }
+ 
+diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
+index d131d07..16bbfd6 100644
+--- a/js/ui/sessionMode.js
++++ b/js/ui/sessionMode.js
+@@ -24,6 +24,7 @@ const _modes = {
+         stylesheetName: 'gnome-shell.css',
+         colorScheme: 'prefer-dark',
+         themeResourceName: 'gnome-shell-theme.gresource',
++        iconsResourceName: 'gnome-shell-icons.gresource',
+         hasOverview: false,
+         showCalendarEvents: false,
+         showWelcomeDialog: false,
diff -pruN 49.0-1/debian/patches/ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch 49.0-1ubuntu1/debian/patches/ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch
--- 49.0-1/debian/patches/ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/shell-global-util-Do-not-move-snap-apps-to-gnome-apps-sco.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,75 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Mon, 27 Mar 2023 21:02:02 +0200
+Subject: shell-global, util: Do not move snap apps to gnome-apps scope
+
+Snap applications already have their own scope so we must not move them.
+
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gnome-shell/+bug/2011806
+---
+ js/misc/util.js    | 20 ++++++++++++++++++--
+ src/shell-global.c |  7 +++++++
+ 2 files changed, 25 insertions(+), 2 deletions(-)
+
+diff --git a/js/misc/util.js b/js/misc/util.js
+index 0101ecf..19479da 100644
+--- a/js/misc/util.js
++++ b/js/misc/util.js
+@@ -117,6 +117,20 @@ export function spawnApp(argv) {
+  */
+ export function trySpawn(argv) {
+     let pid;
++    let path = argv[0];
++
++    if (!path?.includes('/')) {
++        path = GLib.find_program_in_path(argv[0]);
++        if (!path) {
++            throw new GLib.SpawnError({
++                code: GLib.SpawnError.NOENT,
++                message: _('Command not found'),
++            });
++        }
++
++        argv = [path, ...argv.slice(1)];
++    }
++
+     try {
+         const launchContext = global.create_app_launch_context(0, -1);
+         pid = Shell.util_spawn_async(
+@@ -142,8 +156,10 @@ export function trySpawn(argv) {
+         }
+     }
+ 
+-    // Async call, we don't need the reply though
+-    GnomeDesktop.start_systemd_scope(argv[0], pid, null, null, null, () => {});
++    if (!path?.includes('/snap/bin') && !argv?.join(' ').includes('snap run')) {
++        // Async call, we don't need the reply though
++        GnomeDesktop.start_systemd_scope(argv[0], pid, null, null, null, () => {});
++    }
+ 
+     // Dummy child watch; we don't want to double-fork internally
+     // because then we lose the parent-child relationship, which
+diff --git a/src/shell-global.c b/src/shell-global.c
+index b2ed81b..98e8ee0 100644
+--- a/src/shell-global.c
++++ b/src/shell-global.c
+@@ -1432,6 +1432,7 @@ shell_global_app_launched_cb (GAppLaunchContext *context,
+ {
+   gint32 pid;
+   const gchar *app_name;
++  const gchar *command_line;
+ 
+   if (!g_variant_lookup (platform_data, "pid", "i", &pid))
+     return;
+@@ -1445,6 +1446,12 @@ shell_global_app_launched_cb (GAppLaunchContext *context,
+   if (app_name == NULL)
+     app_name = g_app_info_get_executable (info);
+ 
++  command_line = g_app_info_get_commandline (info);
++  if (command_line &&
++      (strstr (command_line, "/snap/bin") ||
++       strstr (command_line, "snap run")))
++    return;
++
+   /* Start async request; we don't care about the result */
+   gnome_start_systemd_scope (app_name,
+                              pid,
diff -pruN 49.0-1/debian/patches/ubuntu/st-Add-support-for-brown-accent-color.patch 49.0-1ubuntu1/debian/patches/ubuntu/st-Add-support-for-brown-accent-color.patch
--- 49.0-1/debian/patches/ubuntu/st-Add-support-for-brown-accent-color.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/st-Add-support-for-brown-accent-color.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,87 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 15 Aug 2024 07:36:01 -0400
+Subject: st: Add support for brown accent color
+
+---
+ js/ui/main.js             |  2 ++
+ src/st/st-settings.h      |  1 +
+ src/st/st-theme-context.c | 13 +++++++++++++
+ 3 files changed, 16 insertions(+)
+
+diff --git a/js/ui/main.js b/js/ui/main.js
+index d3ec8a1..208ffc9 100644
+--- a/js/ui/main.js
++++ b/js/ui/main.js
+@@ -513,6 +513,8 @@ export function getYaruVariantFromAccent() {
+       return "purple";
+     case St.SystemAccentColor.SLATE:
+       return "sage";
++    case St.SystemAccentColor.BROWN:
++      return "wartybrown";
+   }
+ }
+ 
+diff --git a/src/st/st-settings.h b/src/st/st-settings.h
+index 4c00188..796502a 100644
+--- a/src/st/st-settings.h
++++ b/src/st/st-settings.h
+@@ -44,6 +44,7 @@ typedef enum {
+   ST_SYSTEM_ACCENT_COLOR_PINK = G_DESKTOP_ACCENT_COLOR_PINK,
+   ST_SYSTEM_ACCENT_COLOR_PURPLE = G_DESKTOP_ACCENT_COLOR_PURPLE,
+   ST_SYSTEM_ACCENT_COLOR_SLATE = G_DESKTOP_ACCENT_COLOR_SLATE,
++  ST_SYSTEM_ACCENT_COLOR_BROWN = G_DESKTOP_ACCENT_COLOR_BROWN,
+ } StSystemAccentColor;
+ 
+ #define ST_TYPE_SETTINGS (st_settings_get_type ())
+diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c
+index 55220da..e2de219 100644
+--- a/src/st/st-theme-context.c
++++ b/src/st/st-theme-context.c
+@@ -37,6 +37,7 @@
+ #define ACCENT_COLOR_PINK   "#d56199"
+ #define ACCENT_COLOR_PURPLE "#9141ac"
+ #define ACCENT_COLOR_SLATE  "#6f8396"
++#define ACCENT_COLOR_BROWN  "#b39169"
+ 
+ #define ACCENT_FG_COLOR     "#ffffff"
+ 
+@@ -44,6 +45,7 @@
+  * the automated yaru machinery.
+  */
+ #define YARU_ACCENT_COLOR_BLUE_LIGHT "#0070de"
++#define YARU_ACCENT_COLOR_BROWN_LIGHT "#8c6c47"
+ #define YARU_ACCENT_COLOR_GREEN_LIGHT "#488001"
+ #define YARU_ACCENT_COLOR_ORANGE_LIGHT "#cb4314"
+ #define YARU_ACCENT_COLOR_PINK_LIGHT "#ae4aae"
+@@ -57,6 +59,7 @@
+  * the automated yaru machinery.
+  */
+ #define YARU_ACCENT_COLOR_BLUE_DARK "#0073E5"
++#define YARU_ACCENT_COLOR_BROWN_DARK "#92714a"
+ #define YARU_ACCENT_COLOR_GREEN_DARK "#4B8501"
+ #define YARU_ACCENT_COLOR_ORANGE_DARK "#d34615"
+ #define YARU_ACCENT_COLOR_PINK_DARK "#B34CB3"
+@@ -350,6 +353,12 @@ update_accent_colors (StThemeContext *context)
+           cogl_color_from_string (&context->accent_color, color);
+           break;
+ 
++        case ST_SYSTEM_ACCENT_COLOR_BROWN:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_BROWN_DARK : YARU_ACCENT_COLOR_BROWN_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
+         default:
+           g_assert_not_reached ();
+         }
+@@ -397,6 +406,10 @@ update_accent_colors (StThemeContext *context)
+       cogl_color_from_string (&context->accent_color, ACCENT_COLOR_SLATE);
+       break;
+ 
++    case ST_SYSTEM_ACCENT_COLOR_BROWN:
++      cogl_color_from_string (&context->accent_color, ACCENT_COLOR_BROWN);
++      break;
++
+     default:
+       g_assert_not_reached ();
+     }
diff -pruN 49.0-1/debian/patches/ubuntu/st-spinner-design-and-animation.patch 49.0-1ubuntu1/debian/patches/ubuntu/st-spinner-design-and-animation.patch
--- 49.0-1/debian/patches/ubuntu/st-spinner-design-and-animation.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/st-spinner-design-and-animation.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,207 @@
+From: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Date: Tue, 8 Jul 2025 05:37:21 +0200
+Subject: Align StSpinnerContent Design and Animation to
+ YaruCircularProgressIndicator
+
+Forwarded: no, ubuntu only.
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/2114899
+Last-Update: 2025-06-28
+---
+ src/st/st-spinner-content.c | 138 +++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 137 insertions(+), 1 deletion(-)
+
+diff --git a/src/st/st-spinner-content.c b/src/st/st-spinner-content.c
+index 925667c..ae4ef76 100644
+--- a/src/st/st-spinner-content.c
++++ b/src/st/st-spinner-content.c
+@@ -20,7 +20,9 @@
+ #include <cairo.h>
+ 
+ #include <st-widget.h>
++#include <st-settings.h>
+ 
++/* Upstream Definitions */
+ #define MIN_RADIUS 8
+ #define NAT_RADIUS 48
+ #define SMALL_WIDTH 2.5
+@@ -39,6 +41,34 @@
+  * where k is an integer */
+ #define N_CYCLES 53
+ 
++/* Yaru Definitions */
++/*
++ * AdwSpinnerPaintable's MAX_RADIUS has similar function to NAT_RADIUS
++ * but NAT_RADIUS = 48 while MAX_RADIUS = 32.
++ * to calculate new LARGE_WIDTH, we apply LERP
++ * where t = (NAT_RADIUS - 8) / (MAX_RADIUS - 8) = 1.67
++ * AdwSpinnerPaintable's LARGE_WIDTH = 6
++ * new LARGE_WIDTH = LERP (2, 6, 1.67) = 8.67
++ */
++#define YARU_LARGE_WIDTH 8.67
++#define YARU_SMALL_WIDTH 2
++#define YARU_START_ANGLE (-G_PI * 0.5)
++#define YARU_SEGMENT_ANGLE (2 * G_PI / 3) /* 120 degress per segment */
++#define YARU_SPIN_DURATION_MS 8000
++#define YARU_N_CYCLES 6
++
++/*
++ * AdwSpinnerPaintable UPPER_MIN_GAP_ANGLE = 0.2 at MAX_RADIUS = 32
++ * LOWER_MIN_GAP_ANGLE =  0.1 at MIN_RADIUS = 8.
++ * note: MAX_RADIUS != NAT_RADIUS.
++ * to calculate the new UPPER_MIN_GAP_ANGLE, we apply LERP
++ * where t = (NAT_RADIUS - 8) / (MAX_RADIUS - 8) = 1.67
++ * new UPPER_MIN_GAP_ANGLE = LERP (0.1, 0.2, 1.67) = 0.267
++ */
++#define UPPER_MIN_GAP_ANGLE 0.267
++#define LOWER_MIN_GAP_ANGLE 0.1
++#define MAX_GAP_ANGLE 0.5
++
+ /**
+  * StSpinnerContent:
+  *
+@@ -62,6 +92,7 @@ struct _StSpinnerContent
+ 
+   CoglTexture *texture;
+   gboolean dirty;
++  gboolean yaru_mode;
+ 
+   CoglBitmap *buffer;
+ };
+@@ -72,9 +103,26 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (StSpinnerContent, st_spinner_content, G_TYPE_OBJE
+                                G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+                                                       st_spinner_content_iface_init))
+ 
++static void
++update_yaru_mode (StSpinnerContent *spinner,
++                      StSettings       *settings)
++{
++  g_autofree char *gtk_theme = NULL;
++
++  g_object_get (settings, "gtk-theme", &gtk_theme, NULL);
++
++  spinner->yaru_mode = g_strcmp0 (gtk_theme, "Yaru") == 0;
++}
++
+ static void
+ st_spinner_content_init (StSpinnerContent *spinner)
+ {
++  StSettings *settings = st_settings_get ();
++
++  update_yaru_mode (spinner, settings);
++  g_signal_connect_object (settings, "notify::gtk-theme",
++                           G_CALLBACK (update_yaru_mode),
++                           spinner, G_CONNECT_SWAPPED);
+ }
+ 
+ static void
+@@ -131,6 +179,12 @@ st_spinner_content_set_actor (StSpinnerContent *spinner,
+       clutter_timeline_set_repeat_count (spinner->timeline, -1);
+       clutter_timeline_set_progress_mode (spinner->timeline, CLUTTER_LINEAR);
+ 
++      if (spinner->yaru_mode)
++        {
++          clutter_timeline_set_progress_mode (spinner->timeline, CLUTTER_EASE_IN_OUT_SINE);
++          clutter_timeline_set_duration (spinner->timeline, YARU_SPIN_DURATION_MS);
++        }
++
+       g_signal_connect_object (spinner->timeline, "new-frame", G_CALLBACK (new_frame_cb), spinner, 0);
+ 
+       if (clutter_actor_is_mapped (actor))
+@@ -315,6 +369,85 @@ st_spinner_content_draw_spinner (StSpinnerContent *spinner,
+   cairo_restore (cr);
+ }
+ 
++static void
++st_spinner_content_draw_yaru_spinner (StSpinnerContent *spinner,
++                                      cairo_t          *cr,
++                                      int               width,
++                                      int               height)
++{
++  CoglColor color;
++  double radius, radius_t, line_width;
++  double progress, start_angle, end_angle;
++  float gap_angle, min_gap_angle, gap_angle_t_actual, gap_angle_t;
++  uint8_t segment_counter;
++
++  g_assert (spinner->actor);
++
++  if (ST_IS_WIDGET (spinner->actor))
++    {
++      StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (spinner->actor));
++      st_theme_node_get_foreground_color (theme_node, &color);
++    }
++  else
++    {
++      cogl_color_init_from_4f (&color, 0., 0., 0., 1.);
++    }
++
++  radius = MIN (floorf (MIN (width, height) / 2), NAT_RADIUS);
++  radius_t = INVERSE_LERP (MIN_RADIUS, NAT_RADIUS, radius);
++  line_width = LERP (YARU_SMALL_WIDTH, YARU_LARGE_WIDTH,
++                     INVERSE_LERP (MIN_RADIUS, NAT_RADIUS, radius));
++  radius -= roundf (line_width / 2.);
++
++  if (radius < 0)
++    return;
++
++  cairo_translate (cr, roundf (width / 2), roundf(height / 2));
++  cairo_set_line_width (cr, line_width);
++
++  /* Moving part */
++
++  segment_counter = 0;
++
++  if (spinner->timeline)
++    progress = clutter_timeline_get_progress (spinner->timeline) * YARU_N_CYCLES * M_PI * 2;
++  else
++    progress = START_ANGLE;
++
++
++  min_gap_angle = LERP (UPPER_MIN_GAP_ANGLE, LOWER_MIN_GAP_ANGLE, radius_t);
++  gap_angle_t_actual = INVERSE_LERP (0, 2 * M_PI * YARU_N_CYCLES, progress);
++  /* pinpong behavior: 0->1 is transformed to 0->1->0 */
++  gap_angle_t = 1 - fabs (2 * gap_angle_t_actual - 1);
++  gap_angle = LERP (min_gap_angle, MAX_GAP_ANGLE, gap_angle_t);
++
++  end_angle = progress + START_ANGLE - YARU_SEGMENT_ANGLE;
++  start_angle = progress + START_ANGLE - gap_angle;
++
++  do {
++    cairo_save (cr);
++
++    start_angle = normalize_angle (start_angle);
++    end_angle = normalize_angle (end_angle);
++
++    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
++    cairo_set_source_rgba (cr,
++                           color.red / 255.,
++                           color.green / 255.,
++                           color.blue / 255.,
++                           color.alpha / 255.);
++
++    cairo_arc (cr, 0, 0, radius, end_angle, start_angle);
++    cairo_stroke (cr);
++
++    segment_counter++;
++    start_angle += YARU_SEGMENT_ANGLE;
++    end_angle += YARU_SEGMENT_ANGLE;
++
++    cairo_restore (cr);
++  } while (segment_counter < 3);
++}
++
+ static void
+ st_spinner_content_redraw (StSpinnerContent *spinner)
+ {
+@@ -398,7 +531,10 @@ st_spinner_content_redraw (StSpinnerContent *spinner)
+   cairo_paint (cr);
+   cairo_restore (cr);
+ 
+-  st_spinner_content_draw_spinner (spinner, cr, width, height);
++  if (G_LIKELY (spinner->yaru_mode))
++    st_spinner_content_draw_yaru_spinner (spinner, cr, width, height);
++  else
++    st_spinner_content_draw_spinner (spinner, cr, width, height);
+ 
+   cairo_destroy (cr);
+ 
diff -pruN 49.0-1/debian/patches/ubuntu/st-them-context-Use-yaru-colors-as-accents.patch 49.0-1ubuntu1/debian/patches/ubuntu/st-them-context-Use-yaru-colors-as-accents.patch
--- 49.0-1/debian/patches/ubuntu/st-them-context-Use-yaru-colors-as-accents.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu/st-them-context-Use-yaru-colors-as-accents.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,147 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Fri, 14 Mar 2025 04:23:17 +0100
+Subject: st/them-context: Use yaru colors as accents
+
+While they're very close to the GNOME ones, yaru has some variations of
+the accent colors, so let's use them instead of the default ones
+
+Colors are computed as the $yaru_accent_bg_color in yaru.
+---
+ src/st/st-theme-context.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 108 insertions(+)
+
+diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c
+index 5b066d2..55220da 100644
+--- a/src/st/st-theme-context.c
++++ b/src/st/st-theme-context.c
+@@ -40,6 +40,32 @@
+ 
+ #define ACCENT_FG_COLOR     "#ffffff"
+ 
++/* These colors are optimized versions with a light contrasting content using
++ * the automated yaru machinery.
++ */
++#define YARU_ACCENT_COLOR_BLUE_LIGHT "#0070de"
++#define YARU_ACCENT_COLOR_GREEN_LIGHT "#488001"
++#define YARU_ACCENT_COLOR_ORANGE_LIGHT "#cb4314"
++#define YARU_ACCENT_COLOR_PINK_LIGHT "#ae4aae"
++#define YARU_ACCENT_COLOR_PURPLE_LIGHT "#7360d7"
++#define YARU_ACCENT_COLOR_RED_LIGHT "#d82b48"
++#define YARU_ACCENT_COLOR_SLATE_LIGHT "#627766"
++#define YARU_ACCENT_COLOR_TEAL_LIGHT "#2e7e7c"
++#define YARU_ACCENT_COLOR_YELLOW_LIGHT "#9a6800"
++
++/* These colors are optimized versions with a dark contrasting content using
++ * the automated yaru machinery.
++ */
++#define YARU_ACCENT_COLOR_BLUE_DARK "#0073E5"
++#define YARU_ACCENT_COLOR_GREEN_DARK "#4B8501"
++#define YARU_ACCENT_COLOR_ORANGE_DARK "#d34615"
++#define YARU_ACCENT_COLOR_PINK_DARK "#B34CB3"
++#define YARU_ACCENT_COLOR_PURPLE_DARK "#7764D8"
++#define YARU_ACCENT_COLOR_RED_DARK "#DA3450"
++#define YARU_ACCENT_COLOR_SLATE_DARK "#657B69"
++#define YARU_ACCENT_COLOR_TEAL_DARK "#308280"
++#define YARU_ACCENT_COLOR_YELLOW_DARK "#9f6c00"
++
+ struct _StThemeContext {
+   GObject parent;
+ 
+@@ -182,6 +208,14 @@ st_theme_context_init (StThemeContext *context)
+                             "notify::accent-color",
+                             G_CALLBACK (update_accent_colors),
+                             context);
++  g_signal_connect_swapped (st_settings_get (),
++                            "notify::color-scheme",
++                            G_CALLBACK (update_accent_colors),
++                            context);
++  g_signal_connect_swapped (st_settings_get (),
++                            "notify::gtk-theme",
++                            G_CALLBACK (update_accent_colors),
++                            context);
+   g_signal_connect (st_texture_cache_get_default (),
+                     "icon-theme-changed",
+                     G_CALLBACK (on_icon_theme_changed),
+@@ -248,8 +282,82 @@ update_accent_colors (StThemeContext *context)
+ {
+   StSettings *settings = st_settings_get ();
+   StSystemAccentColor accent_color;
++  g_autofree char *gtk_theme = NULL;
+ 
+   g_object_get (settings, "accent-color", &accent_color, NULL);
++  g_object_get (settings, "gtk-theme", &gtk_theme, NULL);
++
++  if (G_LIKELY (gtk_theme && g_str_has_prefix (gtk_theme, "Yaru")))
++    {
++      StSystemColorScheme color_scheme;
++      const char *color;
++
++      g_object_get (settings, "color-scheme", &color_scheme, NULL);
++
++      switch (accent_color)
++        {
++        case ST_SYSTEM_ACCENT_COLOR_BLUE:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_BLUE_DARK : YARU_ACCENT_COLOR_BLUE_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_TEAL:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_TEAL_DARK : YARU_ACCENT_COLOR_TEAL_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_GREEN:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_GREEN_DARK : YARU_ACCENT_COLOR_GREEN_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_YELLOW:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_YELLOW_DARK : YARU_ACCENT_COLOR_YELLOW_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_ORANGE:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_ORANGE_DARK : YARU_ACCENT_COLOR_ORANGE_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_RED:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_RED_DARK : YARU_ACCENT_COLOR_RED_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_PINK:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_PINK_DARK : YARU_ACCENT_COLOR_PINK_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_PURPLE:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_PURPLE_DARK : YARU_ACCENT_COLOR_PURPLE_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        case ST_SYSTEM_ACCENT_COLOR_SLATE:
++          color = color_scheme == ST_SYSTEM_COLOR_SCHEME_PREFER_DARK ?
++            YARU_ACCENT_COLOR_SLATE_DARK : YARU_ACCENT_COLOR_SLATE_LIGHT;
++          cogl_color_from_string (&context->accent_color, color);
++          break;
++
++        default:
++          g_assert_not_reached ();
++        }
++
++      cogl_color_from_string (&context->accent_fg_color, ACCENT_FG_COLOR);
++      st_theme_context_changed (context);
++      return;
++    }
+ 
+   switch (accent_color)
+     {
diff -pruN 49.0-1/debian/patches/ubuntu-authd/Implement-authd-support-via-the-unified-mechanism.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/Implement-authd-support-via-the-unified-mechanism.patch
--- 49.0-1/debian/patches/ubuntu-authd/Implement-authd-support-via-the-unified-mechanism.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/Implement-authd-support-via-the-unified-mechanism.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,6577 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 29 Feb 2024 21:30:36 +0100
+Subject: Implement authd support via the unified mechanism
+
+---
+ js/gdm/authd.js               |  815 ++++++
+ js/gdm/authdProtocol.js       | 5666 +++++++++++++++++++++++++++++++++++++++++
+ js/gdm/const.js               |    3 +
+ js/gdm/util.js                |    4 +-
+ js/js-resources.gresource.xml |    3 +
+ po/POTFILES.in                |    1 +
+ 6 files changed, 6491 insertions(+), 1 deletion(-)
+ create mode 100644 js/gdm/authd.js
+ create mode 100644 js/gdm/authdProtocol.js
+
+diff --git a/js/gdm/authd.js b/js/gdm/authd.js
+new file mode 100644
+index 0000000..3395bb3
+--- /dev/null
++++ b/js/gdm/authd.js
+@@ -0,0 +1,815 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++import GLib from 'gi://GLib';
++
++import {MessageType} from './util.js';
++import {authd as Authd, gdm as AuthdGdm, pam as AuthdPam} from './authdProtocol.js';
++import {UnifiedAuthService} from './unifiedMechanism.js';
++import * as Const from './const.js';
++
++export const SERVICE_NAME = 'gdm-authd';
++
++const STRING_PROTOCOL_NAME = 'com.ubuntu.authd.gdm';
++const STRING_PROTOCOL_VERSION_ = 1;
++
++const AUTO_SELECTION_MAX_WAIT = 1000;
++
++const AuthMechanismIDs = Object.freeze({
++    BrokerSelection: 'authd-broker-selection',
++    AuthModeSelection: 'authd-auth-mode-selection',
++});
++
++
++// FIXME: Add to proto!
++const AuthResult = Object.freeze({
++    Granted: 'granted',
++    Denied: 'denied',
++    Cancelled: 'cancelled',
++    Retry: 'retry',
++    Next: 'next',
++});
++
++// MOVE To proto?!
++const UILayoutTypes = Object.freeze({
++    Form: 'form',
++    NewPassword: 'newpassword',
++    QrCode: 'qrcode',
++});
++
++const EntryTypes = Object.freeze({
++    Chars: 'chars',
++    CharsPassword: 'chars_password',
++    Digits: 'digits',
++    DigitsPassword: 'digits_password',
++});
++
++function isEntryPassword(entryType) {
++    switch (entryType) {
++    case EntryTypes.Chars:
++    case EntryTypes.Digits:
++        return false;
++    default:
++        return true;
++    }
++}
++
++function isEntryPIN(entryType) {
++    switch (entryType) {
++    case EntryTypes.Digits:
++    case EntryTypes.DigitsPassword:
++        return true;
++    default:
++        return false;
++    }
++}
++
++export class AuthdSwitchableHandler extends UnifiedAuthService {
++    get protocolName() {
++        return STRING_PROTOCOL_NAME;
++    }
++
++    getSupportedRoles() {
++        return [
++            Const.PASSWORD_ROLE_NAME,
++            Const.WEB_LOGIN_ROLE_NAME,
++            Const.CHOICE_LIST_ROLE_NAME,
++            Const.PLAIN_TEXT_ROLE_NAME,
++            Const.MESSAGE_ROLE_NAME,
++        ];
++    }
++
++    constructor() {
++        super();
++        this._brokers = {};
++        this._authModes = {};
++        this._authMechanisms = {};
++        this._pendingEvents = [];
++        this._stage = undefined;
++        this._pendingStage = undefined;
++        this._pendingNewChallenge = null;
++    }
++
++    handleProtocolRequest(protocol, version, json) {
++        const authdData = AuthdGdm.Data.fromObject(JSON.parse(json));
++        const reply = this._handleAuthdProtocol(authdData);
++        this.sendProtocolResponse(reply.toJSON(), {ignoreMessageWait: true});
++    }
++
++    handleProblem(problem) {
++        console.log(`authd: Got a problem: ${problem}`)
++
++        this.emit('queue-priority-message', problem, MessageType.ERROR);
++        this.reset();
++
++        return true
++    }
++
++    getProtocolResponse(_mechanism, _role, _response) {
++        throw new Error('This should not be called!');
++    }
++
++    _handleAuthdProtocol(authdData) {
++        switch (authdData.type) {
++        case AuthdGdm.DataType.hello:
++            console.log('authd: Starting authd protocol');
++            return new AuthdGdm.Data({
++                type: AuthdGdm.DataType.hello,
++                hello: new AuthdGdm.HelloData({version: 1}),
++            });
++        case AuthdGdm.DataType.poll: {
++            /* gather the events happened meanwhile awaiting... here... */
++            const pendingEvents = this._pendingEvents;
++            this._pendingEvents = [];
++            return new AuthdGdm.Data({
++                type: AuthdGdm.DataType.pollResponse,
++                pollResponse: pendingEvents,
++            });
++        }
++        case AuthdGdm.DataType.request:
++            return new AuthdGdm.Data({
++                type: AuthdGdm.DataType.response,
++                response: new AuthdGdm.ResponseData({
++                    type: authdData.request.type,
++                    ...this._handleAuthdRequest(authdData.request),
++                }),
++            });
++        case AuthdGdm.DataType.event:
++            this._handleAuthdEvent(authdData.event);
++            return new AuthdGdm.Data({
++                type: AuthdGdm.DataType.eventAck,
++            });
++        default:
++            throw new Error(`Unhandled type ${authdData.type}`);
++        }
++    }
++
++    _handleAuthdRequest(request) {
++        switch (request.type) {
++        case AuthdGdm.RequestType.uiLayoutCapabilities: {
++            const supportedEntries = Object.values(EntryTypes).join(',');
++            const supportedWait = [true, false].join(',');
++            return {
++                uiLayoutCapabilities: new AuthdGdm.Responses.UiLayoutCapabilities({
++                    supportedUiLayouts: [
++                        new Authd.UILayout({
++                            type: UILayoutTypes.Form,
++                            label: 'required',
++                            wait: `optional:${supportedWait}`,
++                            entry: `optional:${supportedEntries}`,
++                        }),
++                        new Authd.UILayout({
++                            type: UILayoutTypes.NewPassword,
++                            label: 'required',
++                            entry: `optional:${supportedEntries}`,
++                        }),
++                        new Authd.UILayout({
++                            type: UILayoutTypes.QrCode,
++                            content: 'required',
++                            wait: `required:${supportedWait}`,
++                            label: 'required',
++                            button: 'optional',
++                            code: 'optional',
++                        }),
++                    ],
++                }),
++            };
++        }
++
++        case AuthdGdm.RequestType.changeStage: {
++            // FIXME: Check is valid value of AuthdPam.Stage
++            this._onStageChanged(request.changeStage.stage);
++            return {ack: new AuthdGdm.Responses.Ack()};
++        }
++
++        default:
++            throw new Error(`Unhandled request type ${request.type}`);
++        }
++    }
++
++    setForegroundMechanism(mechanism) {
++        if (this._selectedAuthMode === mechanism.id)
++            return true;
++        if (!this._authMechanisms[mechanism.id])
++            return true;
++
++        const maybeSwitchMechanism = () => {
++            if (this._stage !== AuthdPam.Stage.challenge)
++                return
++
++            this._pendingStage = AuthdGdm.EventType.challenge;
++            this._selectAuthMode(mechanism.id);
++        }
++
++        if (this._authenticationStarted && this._isWaitingLayout()) {
++            this._authCancelledAction = maybeSwitchMechanism;
++            this._pendingEvents.push(
++                new AuthdGdm.EventData({
++                type: AuthdGdm.EventType.isAuthenticatedCancelled,
++                isAuthenticatedCancelled: new AuthdGdm.Events.IsAuthenticatedCancelled(),
++            }));
++
++            return true;
++        }
++
++        maybeSwitchMechanism();
++        return true;
++    }
++
++    handleMechanism(mechanism) {
++        if (!this._selectedAuthMode || this._stage !== AuthdPam.Stage.challenge)
++            return mechanism.role !== Const.CHOICE_LIST_ROLE_NAME;
++
++        if (this._selectedAuthMode !== mechanism.id)
++            return true;
++
++        return false;
++    }
++
++    _newPasswordConfirmed(newPassword) {
++        if (this._pendingNewChallenge === newPassword)
++            return true;
++
++        if (!this._pendingNewChallenge?.length) {
++            let msg = _('Please, type the new passphrase again');
++            if (isEntryPIN(this._uiLayout.entry))
++                msg = _('Please, type the new PIN again');
++            this.emit('queue-priority-message', msg, MessageType.INFO);
++            this._pendingNewChallenge = newPassword;
++        } else {
++            let msg = _('The provided passphrases do not match, please try again');
++            if (isEntryPIN(this._uiLayout.entry))
++                msg = _('The provided PIN numbers do not match, please try again');
++            this.emit('queue-priority-message', msg, MessageType.ERROR);
++            this._pendingNewChallenge = null;
++        }
++
++        return false;
++    }
++
++    handleAuthSelectionResponse(mechanism, role, response) {
++        if (role !== Const.CHOICE_LIST_ROLE_NAME &&
++            role !== Const.MESSAGE_ROLE_NAME) {
++            this._pendingEvents = this._pendingEvents.filter(ev =>
++                ev.type !== AuthdGdm.EventType.isAuthenticatedRequested);
++        }
++
++        switch (role) {
++        case Const.CHOICE_LIST_ROLE_NAME:
++            this._onChoiceSelected(mechanism, response);
++            break;
++
++        case Const.PASSWORD_ROLE_NAME:
++            if (this._uiLayout.type === UILayoutTypes.NewPassword &&
++                !this._newPasswordConfirmed(response.password)) {
++                this._showChallenge();
++                break;
++            }
++
++            this._pendingEvents.push(
++                new AuthdGdm.EventData({
++                    type: AuthdGdm.EventType.isAuthenticatedRequested,
++                    isAuthenticatedRequested: new AuthdGdm.Events.IsAuthenticatedRequested({
++                        authenticationData: new Authd.IARequest.AuthenticationData({
++                            secret: response.password,
++                        }),
++                    }),
++                }));
++            break;
++
++        case Const.PLAIN_TEXT_ROLE_NAME:
++            if (this._uiLayout.type === UILayoutTypes.NewPassword &&
++                !this._newPasswordConfirmed(response.text)) {
++                this._showChallenge();
++                break;
++            }
++
++            this._pendingEvents.push(
++                new AuthdGdm.EventData({
++                    type: AuthdGdm.EventType.isAuthenticatedRequested,
++                    isAuthenticatedRequested: new AuthdGdm.Events.IsAuthenticatedRequested({
++                        authenticationData: new Authd.IARequest.AuthenticationData({
++                            secret: response.text,
++                        }),
++                    }),
++                }));
++            break;
++
++        case Const.WEB_LOGIN_ROLE_NAME:
++            this._pendingEvents.push(
++                new AuthdGdm.EventData({
++                    type: AuthdGdm.EventType.isAuthenticatedRequested,
++                    isAuthenticatedRequested: new AuthdGdm.Events.IsAuthenticatedRequested({
++                        authenticationData: new Authd.IARequest.AuthenticationData({
++                            wait: 'true',
++                        }),
++                    }),
++                }));
++            break;
++        }
++
++
++        return true;
++    }
++
++    _doStageChange(stage) {
++        this._onStageChanged(stage);
++        this._requestStageChange(stage);
++    }
++
++    _requestStageChange(stage) {
++        this._pendingEvents.push(new AuthdGdm.EventData({
++            type: AuthdGdm.EventType.stageChanged,
++            stageChanged: new AuthdGdm.Events.StageChanged({stage}),
++        }));
++    }
++
++    _maybeStartBrokerSelection() {
++        if (this._pendingStage !== AuthdPam.Stage.brokerSelection)
++            return;
++
++        if (this._stage === AuthdPam.Stage.brokerSelection)
++            return;
++
++        this._stage = this._pendingStage;
++
++        const brokerIDs = Object.keys(this._brokers);
++        switch (brokerIDs.length) {
++            case 0:
++                return;
++            case 1:
++                if (this._selectedBroker !== brokerIDs[0])
++                    this._selectBroker(brokerIDs[0]);
++                return;
++        }
++
++        this._maybeShowChoiceList(AuthMechanismIDs.BrokerSelection,
++            _('Select the broker'), this._brokers);
++    }
++
++    _selectBroker(brokerId) {
++        this._pendingEvents.push(new AuthdGdm.EventData({
++            type: AuthdGdm.EventType.brokerSelected,
++            brokerSelected: new AuthdGdm.Events.BrokerSelected({
++                brokerId,
++            }),
++        }))
++    }
++
++    _maybeStartAuthModeSelection() {
++        if (this._pendingStage !== AuthdPam.Stage.authModeSelection)
++            return;
++
++        if (this._stage === AuthdPam.Stage.authModeSelection)
++            return;
++
++        if (this._authenticationStarted)
++            return;
++
++        this._stage = this._pendingStage;
++
++        const authModesIDs = Object.keys(this._authModes);
++        switch (authModesIDs.length) {
++            case 0:
++                return;
++            case 1:
++                if (this._selectedAuthMode !== authModesIDs[0])
++                    this._selectAuthMode(authModesIDs[0]);
++                return;
++        }
++
++        this._maybeShowChoiceList(AuthMechanismIDs.AuthModeSelection,
++            _('Select the authentication mode'), this._authModes);
++    }
++
++    _selectAuthMode(authModeId) {
++        this._pendingEvents.push(new AuthdGdm.EventData({
++            type: AuthdGdm.EventType.authModeSelected,
++            authModeSelected: new AuthdGdm.Events.AuthModeSelected({
++                authModeId,
++            }),
++        }));
++    }
++
++    _maybeCancelPendingAutoSelection() {
++        if (!this._autoSelectTimeoutID)
++            return;
++
++        GLib.source_remove(this._autoSelectTimeoutID);
++        delete this._autoSelectTimeoutID;
++    }
++
++    _maybeShowChoiceList(id, prompt, choices) {
++        // This is a workaround to handle the cases in which authd is now fast
++        // enough to provide us a choice together with the list.
++        // So let's wait a bit, before asking the user for a choice.
++
++        this._maybeCancelPendingAutoSelection();
++
++        if (this._skipNextAutoSelection) {
++            this._showChoiceList(id, prompt, choices);
++            delete this._skipNextAutoSelection;
++            return;
++        }
++
++        this._autoSelectTimeoutID = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
++            AUTO_SELECTION_MAX_WAIT, () => {
++                delete this._autoSelectTimeoutID;
++                this._showChoiceList(id, prompt, choices);
++                return GLib.SOURCE_REMOVE;
++            });
++    }
++
++    _showChoiceList(id, prompt, choices) {
++        const choiceListMechanism = {
++            name: 'Choices list',
++            selectable: false,
++            role: Const.CHOICE_LIST_ROLE_NAME,
++            serviceName: SERVICE_NAME,
++            protocol: this.protocolName,
++            id,
++            prompt,
++            choices,
++        };
++
++        this.emit('mechanisms-changed', {[id]: choiceListMechanism});
++    }
++
++    _onChoiceSelected(mechanism, key) {
++        switch (mechanism.id) {
++        case AuthMechanismIDs.BrokerSelection:
++            this._selectBroker(key);
++            break;
++
++        case AuthMechanismIDs.AuthModeSelection:
++            this._selectAuthMode(key);
++            break;
++        }
++    }
++
++    cancelRequested() {
++        let prevStage = this._stage - 1;
++
++        switch (this._stage) {
++            case AuthdPam.Stage.challenge:
++                if (Object.keys(this._authModes).length > 1) {
++                    this._skipNextAutoSelection = true;
++                    break;
++                }
++
++                prevStage--;
++                // fallthrough
++
++            case AuthdPam.Stage.authModeSelection:
++                if (Object.keys(this._brokers).length > 1) {
++                    this._skipNextAutoSelection = true;
++                    break;
++                }
++
++                prevStage--;
++                // fallthrough
++
++            default:
++                // Mark the verification as failed, as we do not to want
++                // to rely on the default allowedFailures value.
++                this.emit('verification-failed', /* should retry */ false);
++                return false;
++        }
++
++        this._requestStageChange(prevStage);
++        return true;
++    }
++
++    _onStageChanged(stage) {
++        if (this._pendingStage !== stage) {
++            this.emit('clear-message-queue');
++            this._pendingStage = stage;
++
++            this._maybeCancelPendingAutoSelection();
++        }
++
++        switch (stage) {
++        case AuthdPam.Stage.userSelection:
++            this._cancelAndReset();
++            break;
++
++        case AuthdPam.Stage.brokerSelection:
++            this._authModes = {};
++            this._uiLayout = null;
++            this._authenticationStarted = false;
++            this._selectedAuthMode = null;
++            this._maybeStartBrokerSelection();
++            break;
++
++        case AuthdPam.Stage.authModeSelection:
++            this._uiLayout = null;
++            this._authenticationStarted = false;
++            this._selectedAuthMode = null;
++            this._maybeStartAuthModeSelection();
++            break;
++
++        case AuthdPam.Stage.challenge:
++            this._maybeStartChallenge();
++            break;
++        }
++    }
++
++    _handleAuthdEvent(event) {
++        switch (event.type) {
++        case AuthdGdm.EventType.userSelected:
++            break;
++
++        case AuthdGdm.EventType.brokersReceived:
++            this._brokers = {};
++            event.brokersReceived.brokersInfos.forEach(brokerInfo => {
++                this._brokers[brokerInfo.id] = brokerInfo.name;
++            });
++            this._maybeStartBrokerSelection();
++
++            break;
++
++        case AuthdGdm.EventType.brokerSelected:
++            this._maybeCancelPendingAutoSelection();
++
++            this._selectedBroker = event.brokerSelected.brokerId;
++            if (this._stage === AuthdPam.Stage.brokerSelection)
++                this.emit('choice-list-selected', this._selectedBroker);
++
++            console.log('authd: Broker selected', this._selectedBroker);
++            break;
++
++        case AuthdGdm.EventType.authModesReceived:
++            this._authMechanisms = {};
++            this._authModes = {};
++            event.authModesReceived.authModes.forEach(authMode => {
++                this._authModes[authMode.id] = authMode.label;
++                this._authMechanisms[authMode.id] = {
++                    name: authMode.label,
++                    selectable: true,
++                    /* FIXME: we can't really define the real role until the mechanism is selected */
++                    role: Const.PASSWORD_ROLE_NAME,
++                    id: authMode.id,
++                    prompt: authMode.label,
++                    serviceName: SERVICE_NAME,
++                    protocol: this.protocolName,
++                };
++            });
++
++            this._maybeStartAuthModeSelection();
++            break;
++
++        case AuthdGdm.EventType.authModeSelected:
++            this._maybeCancelPendingAutoSelection();
++
++            if (this._selectedAuthMode === event.authModeSelected.authModeId)
++                return;
++
++            this._selectedAuthMode = event.authModeSelected.authModeId;
++            this.emit('clear-message-queue');
++
++            if (this._stage >= AuthdPam.Stage.brokerSelection)
++                this._maybeStartChallenge();
++            break;
++
++        case AuthdGdm.EventType.uiLayoutReceived:
++            this._uiLayout = event.uiLayoutReceived.uiLayout;
++            this._maybeStartChallenge();
++            break;
++
++        case AuthdGdm.EventType.startAuthentication:
++            this._authenticationStarted = true;
++            this._pendingNewChallenge = null;
++            this._maybeStartChallenge();
++
++            break;
++
++        case AuthdGdm.EventType.authEvent:
++            if (!this._authenticationStarted)
++                return;
++
++            this._authenticationStarted = false;
++            this._handleAuthResponse(event.authEvent.response);
++            break;
++
++        default:
++            throw new Error(`Unhandled event type ${event.type}`);
++        }
++    }
++
++    reset() {
++        this.cancel();
++    }
++
++    cancel() {
++        if (this._stage !== undefined)
++            console.log("authd: Cancelling authentication")
++
++        this._maybeCancelPendingAutoSelection();
++
++        this._stage = undefined;
++        this._uiLayout = null;
++        this._pendingNewChallenge = null;
++        this._selectedAuthMode = null;
++        this._skipNextAutoSelection = false;
++        this._authenticationStarted = false;
++        this._authMechanisms = {};
++        this._authModes = {};
++        this._authCancelledAction = null;
++        this.emit('mechanisms-changed', {});
++    }
++
++    _maybeStartChallenge() {
++        if (this._pendingStage !== AuthdPam.Stage.challenge)
++            return;
++
++        if (!this._selectedAuthMode)
++            return;
++
++        if (!this._authenticationStarted)
++            return;
++
++        if (!this._uiLayout)
++            return;
++
++        if (!this._authModes[this._selectedAuthMode])
++            return;
++
++        this._stage = this._pendingStage;
++        this._showChallenge();
++    }
++
++    sortMechanisms(mA, mB) {
++        if (mA.id === this._selectedAuthMode)
++            return -1;
++        if (mB.id === this._selectedAuthMode)
++            return 1;
++        return 0;
++    }
++
++    _isWaitingLayout() {
++        return this._uiLayout?.wait === 'true'
++    }
++
++    _showChallenge() {
++        if (!this._selectedAuthMode)
++            throw new Error('No authentication mode selected');
++        if (!this._authModes[this._selectedAuthMode])
++            throw new Error(`Selected authentication mode '${this._selectedAuthMode}' is not supported`);
++        if (!this._authMechanisms[this._selectedAuthMode])
++            throw new Error(`Selected authentication mode '${this._selectedAuthMode}' is not supported mechanism`);
++        if (!this._uiLayout)
++            throw new Error('No ui layouts defined');
++
++        console.log('authd: Starting challenge request', this._uiLayout.type, this._uiLayout.label);
++
++        switch (this._uiLayout.type) {
++        case UILayoutTypes.Form:
++        case UILayoutTypes.NewPassword: {
++            const label = this._uiLayout.label;
++            let infoMsg;
++            let prompt;
++            const mechanism = this._authMechanisms[this._selectedAuthMode];
++            if (this._isWaitingLayout())
++                infoMsg = label;
++
++            if (this._uiLayout.entry?.length) {
++                prompt = label;
++
++                if (!prompt?.length) {
++                    switch (this._uiLayout.entry) {
++                    case EntryTypes.CharsPassword:
++                        prompt = _('Password');
++                        break;
++                    case EntryTypes.Chars:
++                        prompt = _('Input value');
++                        break;
++                    case EntryTypes.DigitsPassword:
++                        // FIXME: Add another role for validating them
++                        prompt = _('Input numbers');
++                        break;
++                    case EntryTypes.Digits:
++                        // FIXME: Add another role for validating them
++                        prompt = _('Input numbers');
++                        break;
++                    }
++                }
++
++                if (!isEntryPassword(this._uiLayout.entry))
++                    mechanism.role = Const.PLAIN_TEXT_ROLE_NAME;
++            } else {
++                // FIXME: We need to add other roles to show wait actions.
++                // mechanism.role = Const.PLAIN_TEXT_ROLE_NAME;
++                // delete mechanism.role;
++                mechanism.role = Const.MESSAGE_ROLE_NAME;
++                prompt = infoMsg;
++            }
++
++            mechanism.prompt = prompt ?? '';
++            this.emit('mechanisms-changed', this._authMechanisms);
++
++            if (infoMsg?.length && mechanism.prompt !== infoMsg)
++                this.emit('queue-message', infoMsg, MessageType.INFO);
++
++            if (!this._isWaitingLayout())
++                break;
++
++            this._pendingEvents = this._pendingEvents.filter(ev =>
++                ev.type !== AuthdGdm.EventType.isAuthenticatedRequested);
++
++            this._pendingEvents.push(new AuthdGdm.EventData({
++                type: AuthdGdm.EventType.isAuthenticatedRequested,
++                isAuthenticatedRequested: new AuthdGdm.Events.IsAuthenticatedRequested({
++                    authenticationData: new Authd.IARequest.AuthenticationData({
++                        wait: 'true',
++                    }),
++                }),
++            }));
++            break;
++        }
++
++        case UILayoutTypes.QrCode: {
++            const mechanism = this._authMechanisms[this._selectedAuthMode];
++            mechanism.role = Const.WEB_LOGIN_ROLE_NAME;
++            mechanism.link_prompt = this._uiLayout.label;
++            mechanism.init_prompt = null;
++            mechanism.uri = this._uiLayout.content;
++            mechanism.code = this._uiLayout.code;
++            mechanism.autoAck = true;
++
++            if (this._uiLayout.button) {
++                mechanism.customButton = {
++                    default: true,
++                    label: this._uiLayout.button,
++                    action: () => {
++                        this._pendingEvents.push(
++                            new AuthdGdm.EventData({
++                                type: AuthdGdm.EventType.reselectAuthMode,
++                                reselectAuthMode: new AuthdGdm.Events.ReselectAuthMode(),
++                            }))
++                    },
++                };
++            }
++
++            this.emit('mechanisms-changed', this._authMechanisms);
++            break;
++        }
++
++        default:
++            throw new Error(`UI Layout ${this._uiLayout.type} is not handled`);
++        }
++    }
++
++    _handleAuthResponse(response) {
++        console.log("authd: Access response:", response.access)
++
++        this._pendingNewChallenge = null;
++        const authCancelledAction = this._authCancelledAction;
++        this._authCancelledAction = null;
++
++        if (response.access !== AuthResult.Retry)
++            this._uiLayout = null;
++
++        this.emit('service-request', {authResponse: response.access});
++
++        switch (response.access) {
++        case AuthResult.Granted:
++            break;
++
++        case AuthResult.Retry:
++        case AuthResult.Denied: {
++            if (response?.msg)
++                this.emit('queue-priority-message', response.msg, MessageType.ERROR);
++
++            if (response.access === AuthResult.Denied) {
++                this._failVerification();
++            } else if (response.access === AuthResult.Retry) {
++                this._maybeStartAuthModeSelection();
++            }
++            break;
++        }
++
++        case AuthResult.Cancelled: {
++            authCancelledAction?.();
++            break;
++        }
++
++        case AuthResult.Next: {
++            this.emit('clear-message-queue');
++            this.emit('queue-priority-message', response.msg, MessageType.INFO);
++            this._authModes = {};
++            break;
++        }
++        }
++    }
++
++    _failVerification() {
++        this._authMechanisms = {};
++        this._authModes = {};
++        this.emit('mechanisms-changed', {});
++
++        this.emit('verification-failed', /* should retry */ false);
++    }
++
++    _cancelAndReset() {
++        this.emit('cancel');
++        this.emit('reset');
++    }
++}
+diff --git a/js/gdm/authdProtocol.js b/js/gdm/authdProtocol.js
+new file mode 100644
+index 0000000..a371827
+--- /dev/null
++++ b/js/gdm/authdProtocol.js
+@@ -0,0 +1,5666 @@
++var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
++
++var indexMinimal = {};
++
++var minimal$1 = {};
++
++var aspromise;
++var hasRequiredAspromise;
++
++function requireAspromise () {
++	if (hasRequiredAspromise) return aspromise;
++	hasRequiredAspromise = 1;
++	aspromise = asPromise;
++
++	/**
++	 * Callback as used by {@link util.asPromise}.
++	 * @typedef asPromiseCallback
++	 * @type {function}
++	 * @param {Error|null} error Error, if any
++	 * @param {...*} params Additional arguments
++	 * @returns {undefined}
++	 */
++
++	/**
++	 * Returns a promise from a node-style callback function.
++	 * @memberof util
++	 * @param {asPromiseCallback} fn Function to call
++	 * @param {*} ctx Function context
++	 * @param {...*} params Function arguments
++	 * @returns {Promise<*>} Promisified function
++	 */
++	function asPromise(fn, ctx/*, varargs */) {
++	    var params  = new Array(arguments.length - 1),
++	        offset  = 0,
++	        index   = 2,
++	        pending = true;
++	    while (index < arguments.length)
++	        params[offset++] = arguments[index++];
++	    return new Promise(function executor(resolve, reject) {
++	        params[offset] = function callback(err/*, varargs */) {
++	            if (pending) {
++	                pending = false;
++	                if (err)
++	                    reject(err);
++	                else {
++	                    var params = new Array(arguments.length - 1),
++	                        offset = 0;
++	                    while (offset < params.length)
++	                        params[offset++] = arguments[offset];
++	                    resolve.apply(null, params);
++	                }
++	            }
++	        };
++	        try {
++	            fn.apply(ctx || null, params);
++	        } catch (err) {
++	            if (pending) {
++	                pending = false;
++	                reject(err);
++	            }
++	        }
++	    });
++	}
++	return aspromise;
++}
++
++var base64$1 = {};
++
++var hasRequiredBase64;
++
++function requireBase64 () {
++	if (hasRequiredBase64) return base64$1;
++	hasRequiredBase64 = 1;
++	(function (exports) {
++
++		/**
++		 * A minimal base64 implementation for number arrays.
++		 * @memberof util
++		 * @namespace
++		 */
++		var base64 = exports;
++
++		/**
++		 * Calculates the byte length of a base64 encoded string.
++		 * @param {string} string Base64 encoded string
++		 * @returns {number} Byte length
++		 */
++		base64.length = function length(string) {
++		    var p = string.length;
++		    if (!p)
++		        return 0;
++		    var n = 0;
++		    while (--p % 4 > 1 && string.charAt(p) === "=")
++		        ++n;
++		    return Math.ceil(string.length * 3) / 4 - n;
++		};
++
++		// Base64 encoding table
++		var b64 = new Array(64);
++
++		// Base64 decoding table
++		var s64 = new Array(123);
++
++		// 65..90, 97..122, 48..57, 43, 47
++		for (var i = 0; i < 64;)
++		    s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++;
++
++		/**
++		 * Encodes a buffer to a base64 encoded string.
++		 * @param {Uint8Array} buffer Source buffer
++		 * @param {number} start Source start
++		 * @param {number} end Source end
++		 * @returns {string} Base64 encoded string
++		 */
++		base64.encode = function encode(buffer, start, end) {
++		    var parts = null,
++		        chunk = [];
++		    var i = 0, // output index
++		        j = 0, // goto index
++		        t;     // temporary
++		    while (start < end) {
++		        var b = buffer[start++];
++		        switch (j) {
++		            case 0:
++		                chunk[i++] = b64[b >> 2];
++		                t = (b & 3) << 4;
++		                j = 1;
++		                break;
++		            case 1:
++		                chunk[i++] = b64[t | b >> 4];
++		                t = (b & 15) << 2;
++		                j = 2;
++		                break;
++		            case 2:
++		                chunk[i++] = b64[t | b >> 6];
++		                chunk[i++] = b64[b & 63];
++		                j = 0;
++		                break;
++		        }
++		        if (i > 8191) {
++		            (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));
++		            i = 0;
++		        }
++		    }
++		    if (j) {
++		        chunk[i++] = b64[t];
++		        chunk[i++] = 61;
++		        if (j === 1)
++		            chunk[i++] = 61;
++		    }
++		    if (parts) {
++		        if (i)
++		            parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));
++		        return parts.join("");
++		    }
++		    return String.fromCharCode.apply(String, chunk.slice(0, i));
++		};
++
++		var invalidEncoding = "invalid encoding";
++
++		/**
++		 * Decodes a base64 encoded string to a buffer.
++		 * @param {string} string Source string
++		 * @param {Uint8Array} buffer Destination buffer
++		 * @param {number} offset Destination offset
++		 * @returns {number} Number of bytes written
++		 * @throws {Error} If encoding is invalid
++		 */
++		base64.decode = function decode(string, buffer, offset) {
++		    var start = offset;
++		    var j = 0, // goto index
++		        t;     // temporary
++		    for (var i = 0; i < string.length;) {
++		        var c = string.charCodeAt(i++);
++		        if (c === 61 && j > 1)
++		            break;
++		        if ((c = s64[c]) === undefined)
++		            throw Error(invalidEncoding);
++		        switch (j) {
++		            case 0:
++		                t = c;
++		                j = 1;
++		                break;
++		            case 1:
++		                buffer[offset++] = t << 2 | (c & 48) >> 4;
++		                t = c;
++		                j = 2;
++		                break;
++		            case 2:
++		                buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2;
++		                t = c;
++		                j = 3;
++		                break;
++		            case 3:
++		                buffer[offset++] = (t & 3) << 6 | c;
++		                j = 0;
++		                break;
++		        }
++		    }
++		    if (j === 1)
++		        throw Error(invalidEncoding);
++		    return offset - start;
++		};
++
++		/**
++		 * Tests if the specified string appears to be base64 encoded.
++		 * @param {string} string String to test
++		 * @returns {boolean} `true` if probably base64 encoded, otherwise false
++		 */
++		base64.test = function test(string) {
++		    return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string);
++		}; 
++	} (base64$1));
++	return base64$1;
++}
++
++var eventemitter;
++var hasRequiredEventemitter;
++
++function requireEventemitter () {
++	if (hasRequiredEventemitter) return eventemitter;
++	hasRequiredEventemitter = 1;
++	eventemitter = EventEmitter;
++
++	/**
++	 * Constructs a new event emitter instance.
++	 * @classdesc A minimal event emitter.
++	 * @memberof util
++	 * @constructor
++	 */
++	function EventEmitter() {
++
++	    /**
++	     * Registered listeners.
++	     * @type {Object.<string,*>}
++	     * @private
++	     */
++	    this._listeners = {};
++	}
++
++	/**
++	 * Registers an event listener.
++	 * @param {string} evt Event name
++	 * @param {function} fn Listener
++	 * @param {*} [ctx] Listener context
++	 * @returns {util.EventEmitter} `this`
++	 */
++	EventEmitter.prototype.on = function on(evt, fn, ctx) {
++	    (this._listeners[evt] || (this._listeners[evt] = [])).push({
++	        fn  : fn,
++	        ctx : ctx || this
++	    });
++	    return this;
++	};
++
++	/**
++	 * Removes an event listener or any matching listeners if arguments are omitted.
++	 * @param {string} [evt] Event name. Removes all listeners if omitted.
++	 * @param {function} [fn] Listener to remove. Removes all listeners of `evt` if omitted.
++	 * @returns {util.EventEmitter} `this`
++	 */
++	EventEmitter.prototype.off = function off(evt, fn) {
++	    if (evt === undefined)
++	        this._listeners = {};
++	    else {
++	        if (fn === undefined)
++	            this._listeners[evt] = [];
++	        else {
++	            var listeners = this._listeners[evt];
++	            for (var i = 0; i < listeners.length;)
++	                if (listeners[i].fn === fn)
++	                    listeners.splice(i, 1);
++	                else
++	                    ++i;
++	        }
++	    }
++	    return this;
++	};
++
++	/**
++	 * Emits an event by calling its listeners with the specified arguments.
++	 * @param {string} evt Event name
++	 * @param {...*} args Arguments
++	 * @returns {util.EventEmitter} `this`
++	 */
++	EventEmitter.prototype.emit = function emit(evt) {
++	    var listeners = this._listeners[evt];
++	    if (listeners) {
++	        var args = [],
++	            i = 1;
++	        for (; i < arguments.length;)
++	            args.push(arguments[i++]);
++	        for (i = 0; i < listeners.length;)
++	            listeners[i].fn.apply(listeners[i++].ctx, args);
++	    }
++	    return this;
++	};
++	return eventemitter;
++}
++
++var float;
++var hasRequiredFloat;
++
++function requireFloat () {
++	if (hasRequiredFloat) return float;
++	hasRequiredFloat = 1;
++
++	float = factory(factory);
++
++	/**
++	 * Reads / writes floats / doubles from / to buffers.
++	 * @name util.float
++	 * @namespace
++	 */
++
++	/**
++	 * Writes a 32 bit float to a buffer using little endian byte order.
++	 * @name util.float.writeFloatLE
++	 * @function
++	 * @param {number} val Value to write
++	 * @param {Uint8Array} buf Target buffer
++	 * @param {number} pos Target buffer offset
++	 * @returns {undefined}
++	 */
++
++	/**
++	 * Writes a 32 bit float to a buffer using big endian byte order.
++	 * @name util.float.writeFloatBE
++	 * @function
++	 * @param {number} val Value to write
++	 * @param {Uint8Array} buf Target buffer
++	 * @param {number} pos Target buffer offset
++	 * @returns {undefined}
++	 */
++
++	/**
++	 * Reads a 32 bit float from a buffer using little endian byte order.
++	 * @name util.float.readFloatLE
++	 * @function
++	 * @param {Uint8Array} buf Source buffer
++	 * @param {number} pos Source buffer offset
++	 * @returns {number} Value read
++	 */
++
++	/**
++	 * Reads a 32 bit float from a buffer using big endian byte order.
++	 * @name util.float.readFloatBE
++	 * @function
++	 * @param {Uint8Array} buf Source buffer
++	 * @param {number} pos Source buffer offset
++	 * @returns {number} Value read
++	 */
++
++	/**
++	 * Writes a 64 bit double to a buffer using little endian byte order.
++	 * @name util.float.writeDoubleLE
++	 * @function
++	 * @param {number} val Value to write
++	 * @param {Uint8Array} buf Target buffer
++	 * @param {number} pos Target buffer offset
++	 * @returns {undefined}
++	 */
++
++	/**
++	 * Writes a 64 bit double to a buffer using big endian byte order.
++	 * @name util.float.writeDoubleBE
++	 * @function
++	 * @param {number} val Value to write
++	 * @param {Uint8Array} buf Target buffer
++	 * @param {number} pos Target buffer offset
++	 * @returns {undefined}
++	 */
++
++	/**
++	 * Reads a 64 bit double from a buffer using little endian byte order.
++	 * @name util.float.readDoubleLE
++	 * @function
++	 * @param {Uint8Array} buf Source buffer
++	 * @param {number} pos Source buffer offset
++	 * @returns {number} Value read
++	 */
++
++	/**
++	 * Reads a 64 bit double from a buffer using big endian byte order.
++	 * @name util.float.readDoubleBE
++	 * @function
++	 * @param {Uint8Array} buf Source buffer
++	 * @param {number} pos Source buffer offset
++	 * @returns {number} Value read
++	 */
++
++	// Factory function for the purpose of node-based testing in modified global environments
++	function factory(exports) {
++
++	    // float: typed array
++	    if (typeof Float32Array !== "undefined") (function() {
++
++	        var f32 = new Float32Array([ -0 ]),
++	            f8b = new Uint8Array(f32.buffer),
++	            le  = f8b[3] === 128;
++
++	        function writeFloat_f32_cpy(val, buf, pos) {
++	            f32[0] = val;
++	            buf[pos    ] = f8b[0];
++	            buf[pos + 1] = f8b[1];
++	            buf[pos + 2] = f8b[2];
++	            buf[pos + 3] = f8b[3];
++	        }
++
++	        function writeFloat_f32_rev(val, buf, pos) {
++	            f32[0] = val;
++	            buf[pos    ] = f8b[3];
++	            buf[pos + 1] = f8b[2];
++	            buf[pos + 2] = f8b[1];
++	            buf[pos + 3] = f8b[0];
++	        }
++
++	        /* istanbul ignore next */
++	        exports.writeFloatLE = le ? writeFloat_f32_cpy : writeFloat_f32_rev;
++	        /* istanbul ignore next */
++	        exports.writeFloatBE = le ? writeFloat_f32_rev : writeFloat_f32_cpy;
++
++	        function readFloat_f32_cpy(buf, pos) {
++	            f8b[0] = buf[pos    ];
++	            f8b[1] = buf[pos + 1];
++	            f8b[2] = buf[pos + 2];
++	            f8b[3] = buf[pos + 3];
++	            return f32[0];
++	        }
++
++	        function readFloat_f32_rev(buf, pos) {
++	            f8b[3] = buf[pos    ];
++	            f8b[2] = buf[pos + 1];
++	            f8b[1] = buf[pos + 2];
++	            f8b[0] = buf[pos + 3];
++	            return f32[0];
++	        }
++
++	        /* istanbul ignore next */
++	        exports.readFloatLE = le ? readFloat_f32_cpy : readFloat_f32_rev;
++	        /* istanbul ignore next */
++	        exports.readFloatBE = le ? readFloat_f32_rev : readFloat_f32_cpy;
++
++	    // float: ieee754
++	    })(); else (function() {
++
++	        function writeFloat_ieee754(writeUint, val, buf, pos) {
++	            var sign = val < 0 ? 1 : 0;
++	            if (sign)
++	                val = -val;
++	            if (val === 0)
++	                writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos);
++	            else if (isNaN(val))
++	                writeUint(2143289344, buf, pos);
++	            else if (val > 3.4028234663852886e+38) // +-Infinity
++	                writeUint((sign << 31 | 2139095040) >>> 0, buf, pos);
++	            else if (val < 1.1754943508222875e-38) // denormal
++	                writeUint((sign << 31 | Math.round(val / 1.401298464324817e-45)) >>> 0, buf, pos);
++	            else {
++	                var exponent = Math.floor(Math.log(val) / Math.LN2),
++	                    mantissa = Math.round(val * Math.pow(2, -exponent) * 8388608) & 8388607;
++	                writeUint((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos);
++	            }
++	        }
++
++	        exports.writeFloatLE = writeFloat_ieee754.bind(null, writeUintLE);
++	        exports.writeFloatBE = writeFloat_ieee754.bind(null, writeUintBE);
++
++	        function readFloat_ieee754(readUint, buf, pos) {
++	            var uint = readUint(buf, pos),
++	                sign = (uint >> 31) * 2 + 1,
++	                exponent = uint >>> 23 & 255,
++	                mantissa = uint & 8388607;
++	            return exponent === 255
++	                ? mantissa
++	                ? NaN
++	                : sign * Infinity
++	                : exponent === 0 // denormal
++	                ? sign * 1.401298464324817e-45 * mantissa
++	                : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608);
++	        }
++
++	        exports.readFloatLE = readFloat_ieee754.bind(null, readUintLE);
++	        exports.readFloatBE = readFloat_ieee754.bind(null, readUintBE);
++
++	    })();
++
++	    // double: typed array
++	    if (typeof Float64Array !== "undefined") (function() {
++
++	        var f64 = new Float64Array([-0]),
++	            f8b = new Uint8Array(f64.buffer),
++	            le  = f8b[7] === 128;
++
++	        function writeDouble_f64_cpy(val, buf, pos) {
++	            f64[0] = val;
++	            buf[pos    ] = f8b[0];
++	            buf[pos + 1] = f8b[1];
++	            buf[pos + 2] = f8b[2];
++	            buf[pos + 3] = f8b[3];
++	            buf[pos + 4] = f8b[4];
++	            buf[pos + 5] = f8b[5];
++	            buf[pos + 6] = f8b[6];
++	            buf[pos + 7] = f8b[7];
++	        }
++
++	        function writeDouble_f64_rev(val, buf, pos) {
++	            f64[0] = val;
++	            buf[pos    ] = f8b[7];
++	            buf[pos + 1] = f8b[6];
++	            buf[pos + 2] = f8b[5];
++	            buf[pos + 3] = f8b[4];
++	            buf[pos + 4] = f8b[3];
++	            buf[pos + 5] = f8b[2];
++	            buf[pos + 6] = f8b[1];
++	            buf[pos + 7] = f8b[0];
++	        }
++
++	        /* istanbul ignore next */
++	        exports.writeDoubleLE = le ? writeDouble_f64_cpy : writeDouble_f64_rev;
++	        /* istanbul ignore next */
++	        exports.writeDoubleBE = le ? writeDouble_f64_rev : writeDouble_f64_cpy;
++
++	        function readDouble_f64_cpy(buf, pos) {
++	            f8b[0] = buf[pos    ];
++	            f8b[1] = buf[pos + 1];
++	            f8b[2] = buf[pos + 2];
++	            f8b[3] = buf[pos + 3];
++	            f8b[4] = buf[pos + 4];
++	            f8b[5] = buf[pos + 5];
++	            f8b[6] = buf[pos + 6];
++	            f8b[7] = buf[pos + 7];
++	            return f64[0];
++	        }
++
++	        function readDouble_f64_rev(buf, pos) {
++	            f8b[7] = buf[pos    ];
++	            f8b[6] = buf[pos + 1];
++	            f8b[5] = buf[pos + 2];
++	            f8b[4] = buf[pos + 3];
++	            f8b[3] = buf[pos + 4];
++	            f8b[2] = buf[pos + 5];
++	            f8b[1] = buf[pos + 6];
++	            f8b[0] = buf[pos + 7];
++	            return f64[0];
++	        }
++
++	        /* istanbul ignore next */
++	        exports.readDoubleLE = le ? readDouble_f64_cpy : readDouble_f64_rev;
++	        /* istanbul ignore next */
++	        exports.readDoubleBE = le ? readDouble_f64_rev : readDouble_f64_cpy;
++
++	    // double: ieee754
++	    })(); else (function() {
++
++	        function writeDouble_ieee754(writeUint, off0, off1, val, buf, pos) {
++	            var sign = val < 0 ? 1 : 0;
++	            if (sign)
++	                val = -val;
++	            if (val === 0) {
++	                writeUint(0, buf, pos + off0);
++	                writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos + off1);
++	            } else if (isNaN(val)) {
++	                writeUint(0, buf, pos + off0);
++	                writeUint(2146959360, buf, pos + off1);
++	            } else if (val > 1.7976931348623157e+308) { // +-Infinity
++	                writeUint(0, buf, pos + off0);
++	                writeUint((sign << 31 | 2146435072) >>> 0, buf, pos + off1);
++	            } else {
++	                var mantissa;
++	                if (val < 2.2250738585072014e-308) { // denormal
++	                    mantissa = val / 5e-324;
++	                    writeUint(mantissa >>> 0, buf, pos + off0);
++	                    writeUint((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + off1);
++	                } else {
++	                    var exponent = Math.floor(Math.log(val) / Math.LN2);
++	                    if (exponent === 1024)
++	                        exponent = 1023;
++	                    mantissa = val * Math.pow(2, -exponent);
++	                    writeUint(mantissa * 4503599627370496 >>> 0, buf, pos + off0);
++	                    writeUint((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + off1);
++	                }
++	            }
++	        }
++
++	        exports.writeDoubleLE = writeDouble_ieee754.bind(null, writeUintLE, 0, 4);
++	        exports.writeDoubleBE = writeDouble_ieee754.bind(null, writeUintBE, 4, 0);
++
++	        function readDouble_ieee754(readUint, off0, off1, buf, pos) {
++	            var lo = readUint(buf, pos + off0),
++	                hi = readUint(buf, pos + off1);
++	            var sign = (hi >> 31) * 2 + 1,
++	                exponent = hi >>> 20 & 2047,
++	                mantissa = 4294967296 * (hi & 1048575) + lo;
++	            return exponent === 2047
++	                ? mantissa
++	                ? NaN
++	                : sign * Infinity
++	                : exponent === 0 // denormal
++	                ? sign * 5e-324 * mantissa
++	                : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496);
++	        }
++
++	        exports.readDoubleLE = readDouble_ieee754.bind(null, readUintLE, 0, 4);
++	        exports.readDoubleBE = readDouble_ieee754.bind(null, readUintBE, 4, 0);
++
++	    })();
++
++	    return exports;
++	}
++
++	// uint helpers
++
++	function writeUintLE(val, buf, pos) {
++	    buf[pos    ] =  val        & 255;
++	    buf[pos + 1] =  val >>> 8  & 255;
++	    buf[pos + 2] =  val >>> 16 & 255;
++	    buf[pos + 3] =  val >>> 24;
++	}
++
++	function writeUintBE(val, buf, pos) {
++	    buf[pos    ] =  val >>> 24;
++	    buf[pos + 1] =  val >>> 16 & 255;
++	    buf[pos + 2] =  val >>> 8  & 255;
++	    buf[pos + 3] =  val        & 255;
++	}
++
++	function readUintLE(buf, pos) {
++	    return (buf[pos    ]
++	          | buf[pos + 1] << 8
++	          | buf[pos + 2] << 16
++	          | buf[pos + 3] << 24) >>> 0;
++	}
++
++	function readUintBE(buf, pos) {
++	    return (buf[pos    ] << 24
++	          | buf[pos + 1] << 16
++	          | buf[pos + 2] << 8
++	          | buf[pos + 3]) >>> 0;
++	}
++	return float;
++}
++
++var inquire_1;
++var hasRequiredInquire;
++
++function requireInquire () {
++	if (hasRequiredInquire) return inquire_1;
++	hasRequiredInquire = 1;
++	inquire_1 = inquire;
++
++	/**
++	 * Requires a module only if available.
++	 * @memberof util
++	 * @param {string} moduleName Module to require
++	 * @returns {?Object} Required module if available and not empty, otherwise `null`
++	 */
++	function inquire(moduleName) {
++	    try {
++	        var mod = eval("quire".replace(/^/,"re"))(moduleName); // eslint-disable-line no-eval
++	        if (mod && (mod.length || Object.keys(mod).length))
++	            return mod;
++	    } catch (e) {} // eslint-disable-line no-empty
++	    return null;
++	}
++	return inquire_1;
++}
++
++var utf8$2 = {};
++
++var hasRequiredUtf8;
++
++function requireUtf8 () {
++	if (hasRequiredUtf8) return utf8$2;
++	hasRequiredUtf8 = 1;
++	(function (exports) {
++
++		/**
++		 * A minimal UTF8 implementation for number arrays.
++		 * @memberof util
++		 * @namespace
++		 */
++		var utf8 = exports;
++
++		/**
++		 * Calculates the UTF8 byte length of a string.
++		 * @param {string} string String
++		 * @returns {number} Byte length
++		 */
++		utf8.length = function utf8_length(string) {
++		    var len = 0,
++		        c = 0;
++		    for (var i = 0; i < string.length; ++i) {
++		        c = string.charCodeAt(i);
++		        if (c < 128)
++		            len += 1;
++		        else if (c < 2048)
++		            len += 2;
++		        else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) {
++		            ++i;
++		            len += 4;
++		        } else
++		            len += 3;
++		    }
++		    return len;
++		};
++
++		/**
++		 * Reads UTF8 bytes as a string.
++		 * @param {Uint8Array} buffer Source buffer
++		 * @param {number} start Source start
++		 * @param {number} end Source end
++		 * @returns {string} String read
++		 */
++		utf8.read = function utf8_read(buffer, start, end) {
++		    var len = end - start;
++		    if (len < 1)
++		        return "";
++		    var parts = null,
++		        chunk = [],
++		        i = 0, // char offset
++		        t;     // temporary
++		    while (start < end) {
++		        t = buffer[start++];
++		        if (t < 128)
++		            chunk[i++] = t;
++		        else if (t > 191 && t < 224)
++		            chunk[i++] = (t & 31) << 6 | buffer[start++] & 63;
++		        else if (t > 239 && t < 365) {
++		            t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000;
++		            chunk[i++] = 0xD800 + (t >> 10);
++		            chunk[i++] = 0xDC00 + (t & 1023);
++		        } else
++		            chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63;
++		        if (i > 8191) {
++		            (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));
++		            i = 0;
++		        }
++		    }
++		    if (parts) {
++		        if (i)
++		            parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));
++		        return parts.join("");
++		    }
++		    return String.fromCharCode.apply(String, chunk.slice(0, i));
++		};
++
++		/**
++		 * Writes a string as UTF8 bytes.
++		 * @param {string} string Source string
++		 * @param {Uint8Array} buffer Destination buffer
++		 * @param {number} offset Destination offset
++		 * @returns {number} Bytes written
++		 */
++		utf8.write = function utf8_write(string, buffer, offset) {
++		    var start = offset,
++		        c1, // character 1
++		        c2; // character 2
++		    for (var i = 0; i < string.length; ++i) {
++		        c1 = string.charCodeAt(i);
++		        if (c1 < 128) {
++		            buffer[offset++] = c1;
++		        } else if (c1 < 2048) {
++		            buffer[offset++] = c1 >> 6       | 192;
++		            buffer[offset++] = c1       & 63 | 128;
++		        } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) {
++		            c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF);
++		            ++i;
++		            buffer[offset++] = c1 >> 18      | 240;
++		            buffer[offset++] = c1 >> 12 & 63 | 128;
++		            buffer[offset++] = c1 >> 6  & 63 | 128;
++		            buffer[offset++] = c1       & 63 | 128;
++		        } else {
++		            buffer[offset++] = c1 >> 12      | 224;
++		            buffer[offset++] = c1 >> 6  & 63 | 128;
++		            buffer[offset++] = c1       & 63 | 128;
++		        }
++		    }
++		    return offset - start;
++		}; 
++	} (utf8$2));
++	return utf8$2;
++}
++
++var pool_1;
++var hasRequiredPool;
++
++function requirePool () {
++	if (hasRequiredPool) return pool_1;
++	hasRequiredPool = 1;
++	pool_1 = pool;
++
++	/**
++	 * An allocator as used by {@link util.pool}.
++	 * @typedef PoolAllocator
++	 * @type {function}
++	 * @param {number} size Buffer size
++	 * @returns {Uint8Array} Buffer
++	 */
++
++	/**
++	 * A slicer as used by {@link util.pool}.
++	 * @typedef PoolSlicer
++	 * @type {function}
++	 * @param {number} start Start offset
++	 * @param {number} end End offset
++	 * @returns {Uint8Array} Buffer slice
++	 * @this {Uint8Array}
++	 */
++
++	/**
++	 * A general purpose buffer pool.
++	 * @memberof util
++	 * @function
++	 * @param {PoolAllocator} alloc Allocator
++	 * @param {PoolSlicer} slice Slicer
++	 * @param {number} [size=8192] Slab size
++	 * @returns {PoolAllocator} Pooled allocator
++	 */
++	function pool(alloc, slice, size) {
++	    var SIZE   = size || 8192;
++	    var MAX    = SIZE >>> 1;
++	    var slab   = null;
++	    var offset = SIZE;
++	    return function pool_alloc(size) {
++	        if (size < 1 || size > MAX)
++	            return alloc(size);
++	        if (offset + size > SIZE) {
++	            slab = alloc(SIZE);
++	            offset = 0;
++	        }
++	        var buf = slice.call(slab, offset, offset += size);
++	        if (offset & 7) // align to 32 bit
++	            offset = (offset | 7) + 1;
++	        return buf;
++	    };
++	}
++	return pool_1;
++}
++
++var longbits;
++var hasRequiredLongbits;
++
++function requireLongbits () {
++	if (hasRequiredLongbits) return longbits;
++	hasRequiredLongbits = 1;
++	longbits = LongBits;
++
++	var util = requireMinimal();
++
++	/**
++	 * Constructs new long bits.
++	 * @classdesc Helper class for working with the low and high bits of a 64 bit value.
++	 * @memberof util
++	 * @constructor
++	 * @param {number} lo Low 32 bits, unsigned
++	 * @param {number} hi High 32 bits, unsigned
++	 */
++	function LongBits(lo, hi) {
++
++	    // note that the casts below are theoretically unnecessary as of today, but older statically
++	    // generated converter code might still call the ctor with signed 32bits. kept for compat.
++
++	    /**
++	     * Low bits.
++	     * @type {number}
++	     */
++	    this.lo = lo >>> 0;
++
++	    /**
++	     * High bits.
++	     * @type {number}
++	     */
++	    this.hi = hi >>> 0;
++	}
++
++	/**
++	 * Zero bits.
++	 * @memberof util.LongBits
++	 * @type {util.LongBits}
++	 */
++	var zero = LongBits.zero = new LongBits(0, 0);
++
++	zero.toNumber = function() { return 0; };
++	zero.zzEncode = zero.zzDecode = function() { return this; };
++	zero.length = function() { return 1; };
++
++	/**
++	 * Zero hash.
++	 * @memberof util.LongBits
++	 * @type {string}
++	 */
++	var zeroHash = LongBits.zeroHash = "\0\0\0\0\0\0\0\0";
++
++	/**
++	 * Constructs new long bits from the specified number.
++	 * @param {number} value Value
++	 * @returns {util.LongBits} Instance
++	 */
++	LongBits.fromNumber = function fromNumber(value) {
++	    if (value === 0)
++	        return zero;
++	    var sign = value < 0;
++	    if (sign)
++	        value = -value;
++	    var lo = value >>> 0,
++	        hi = (value - lo) / 4294967296 >>> 0;
++	    if (sign) {
++	        hi = ~hi >>> 0;
++	        lo = ~lo >>> 0;
++	        if (++lo > 4294967295) {
++	            lo = 0;
++	            if (++hi > 4294967295)
++	                hi = 0;
++	        }
++	    }
++	    return new LongBits(lo, hi);
++	};
++
++	/**
++	 * Constructs new long bits from a number, long or string.
++	 * @param {Long|number|string} value Value
++	 * @returns {util.LongBits} Instance
++	 */
++	LongBits.from = function from(value) {
++	    if (typeof value === "number")
++	        return LongBits.fromNumber(value);
++	    if (util.isString(value)) {
++	        /* istanbul ignore else */
++	        if (util.Long)
++	            value = util.Long.fromString(value);
++	        else
++	            return LongBits.fromNumber(parseInt(value, 10));
++	    }
++	    return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero;
++	};
++
++	/**
++	 * Converts this long bits to a possibly unsafe JavaScript number.
++	 * @param {boolean} [unsigned=false] Whether unsigned or not
++	 * @returns {number} Possibly unsafe number
++	 */
++	LongBits.prototype.toNumber = function toNumber(unsigned) {
++	    if (!unsigned && this.hi >>> 31) {
++	        var lo = ~this.lo + 1 >>> 0,
++	            hi = ~this.hi     >>> 0;
++	        if (!lo)
++	            hi = hi + 1 >>> 0;
++	        return -(lo + hi * 4294967296);
++	    }
++	    return this.lo + this.hi * 4294967296;
++	};
++
++	/**
++	 * Converts this long bits to a long.
++	 * @param {boolean} [unsigned=false] Whether unsigned or not
++	 * @returns {Long} Long
++	 */
++	LongBits.prototype.toLong = function toLong(unsigned) {
++	    return util.Long
++	        ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned))
++	        /* istanbul ignore next */
++	        : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) };
++	};
++
++	var charCodeAt = String.prototype.charCodeAt;
++
++	/**
++	 * Constructs new long bits from the specified 8 characters long hash.
++	 * @param {string} hash Hash
++	 * @returns {util.LongBits} Bits
++	 */
++	LongBits.fromHash = function fromHash(hash) {
++	    if (hash === zeroHash)
++	        return zero;
++	    return new LongBits(
++	        ( charCodeAt.call(hash, 0)
++	        | charCodeAt.call(hash, 1) << 8
++	        | charCodeAt.call(hash, 2) << 16
++	        | charCodeAt.call(hash, 3) << 24) >>> 0
++	    ,
++	        ( charCodeAt.call(hash, 4)
++	        | charCodeAt.call(hash, 5) << 8
++	        | charCodeAt.call(hash, 6) << 16
++	        | charCodeAt.call(hash, 7) << 24) >>> 0
++	    );
++	};
++
++	/**
++	 * Converts this long bits to a 8 characters long hash.
++	 * @returns {string} Hash
++	 */
++	LongBits.prototype.toHash = function toHash() {
++	    return String.fromCharCode(
++	        this.lo        & 255,
++	        this.lo >>> 8  & 255,
++	        this.lo >>> 16 & 255,
++	        this.lo >>> 24      ,
++	        this.hi        & 255,
++	        this.hi >>> 8  & 255,
++	        this.hi >>> 16 & 255,
++	        this.hi >>> 24
++	    );
++	};
++
++	/**
++	 * Zig-zag encodes this long bits.
++	 * @returns {util.LongBits} `this`
++	 */
++	LongBits.prototype.zzEncode = function zzEncode() {
++	    var mask =   this.hi >> 31;
++	    this.hi  = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
++	    this.lo  = ( this.lo << 1                   ^ mask) >>> 0;
++	    return this;
++	};
++
++	/**
++	 * Zig-zag decodes this long bits.
++	 * @returns {util.LongBits} `this`
++	 */
++	LongBits.prototype.zzDecode = function zzDecode() {
++	    var mask = -(this.lo & 1);
++	    this.lo  = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
++	    this.hi  = ( this.hi >>> 1                  ^ mask) >>> 0;
++	    return this;
++	};
++
++	/**
++	 * Calculates the length of this longbits when encoded as a varint.
++	 * @returns {number} Length
++	 */
++	LongBits.prototype.length = function length() {
++	    var part0 =  this.lo,
++	        part1 = (this.lo >>> 28 | this.hi << 4) >>> 0,
++	        part2 =  this.hi >>> 24;
++	    return part2 === 0
++	         ? part1 === 0
++	           ? part0 < 16384
++	             ? part0 < 128 ? 1 : 2
++	             : part0 < 2097152 ? 3 : 4
++	           : part1 < 16384
++	             ? part1 < 128 ? 5 : 6
++	             : part1 < 2097152 ? 7 : 8
++	         : part2 < 128 ? 9 : 10;
++	};
++	return longbits;
++}
++
++var hasRequiredMinimal;
++
++function requireMinimal () {
++	if (hasRequiredMinimal) return minimal$1;
++	hasRequiredMinimal = 1;
++	(function (exports) {
++		var util = exports;
++
++		// used to return a Promise where callback is omitted
++		util.asPromise = requireAspromise();
++
++		// converts to / from base64 encoded strings
++		util.base64 = requireBase64();
++
++		// base class of rpc.Service
++		util.EventEmitter = requireEventemitter();
++
++		// float handling accross browsers
++		util.float = requireFloat();
++
++		// requires modules optionally and hides the call from bundlers
++		util.inquire = requireInquire();
++
++		// converts to / from utf8 encoded strings
++		util.utf8 = requireUtf8();
++
++		// provides a node-like buffer pool in the browser
++		util.pool = requirePool();
++
++		// utility to work with the low and high bits of a 64 bit value
++		util.LongBits = requireLongbits();
++
++		/**
++		 * Whether running within node or not.
++		 * @memberof util
++		 * @type {boolean}
++		 */
++		util.isNode = Boolean(typeof commonjsGlobal !== "undefined"
++		                   && commonjsGlobal
++		                   && commonjsGlobal.process
++		                   && commonjsGlobal.process.versions
++		                   && commonjsGlobal.process.versions.node);
++
++		/**
++		 * Global object reference.
++		 * @memberof util
++		 * @type {Object}
++		 */
++		util.global = util.isNode && commonjsGlobal
++		           || typeof window !== "undefined" && window
++		           || typeof self   !== "undefined" && self
++		           || commonjsGlobal; // eslint-disable-line no-invalid-this
++
++		/**
++		 * An immuable empty array.
++		 * @memberof util
++		 * @type {Array.<*>}
++		 * @const
++		 */
++		util.emptyArray = Object.freeze ? Object.freeze([]) : /* istanbul ignore next */ []; // used on prototypes
++
++		/**
++		 * An immutable empty object.
++		 * @type {Object}
++		 * @const
++		 */
++		util.emptyObject = Object.freeze ? Object.freeze({}) : /* istanbul ignore next */ {}; // used on prototypes
++
++		/**
++		 * Tests if the specified value is an integer.
++		 * @function
++		 * @param {*} value Value to test
++		 * @returns {boolean} `true` if the value is an integer
++		 */
++		util.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) {
++		    return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
++		};
++
++		/**
++		 * Tests if the specified value is a string.
++		 * @param {*} value Value to test
++		 * @returns {boolean} `true` if the value is a string
++		 */
++		util.isString = function isString(value) {
++		    return typeof value === "string" || value instanceof String;
++		};
++
++		/**
++		 * Tests if the specified value is a non-null object.
++		 * @param {*} value Value to test
++		 * @returns {boolean} `true` if the value is a non-null object
++		 */
++		util.isObject = function isObject(value) {
++		    return value && typeof value === "object";
++		};
++
++		/**
++		 * Checks if a property on a message is considered to be present.
++		 * This is an alias of {@link util.isSet}.
++		 * @function
++		 * @param {Object} obj Plain object or message instance
++		 * @param {string} prop Property name
++		 * @returns {boolean} `true` if considered to be present, otherwise `false`
++		 */
++		util.isset =
++
++		/**
++		 * Checks if a property on a message is considered to be present.
++		 * @param {Object} obj Plain object or message instance
++		 * @param {string} prop Property name
++		 * @returns {boolean} `true` if considered to be present, otherwise `false`
++		 */
++		util.isSet = function isSet(obj, prop) {
++		    var value = obj[prop];
++		    if (value != null && obj.hasOwnProperty(prop)) // eslint-disable-line eqeqeq, no-prototype-builtins
++		        return typeof value !== "object" || (Array.isArray(value) ? value.length : Object.keys(value).length) > 0;
++		    return false;
++		};
++
++		/**
++		 * Any compatible Buffer instance.
++		 * This is a minimal stand-alone definition of a Buffer instance. The actual type is that exported by node's typings.
++		 * @interface Buffer
++		 * @extends Uint8Array
++		 */
++
++		/**
++		 * Node's Buffer class if available.
++		 * @type {Constructor<Buffer>}
++		 */
++		util.Buffer = (function() {
++		    try {
++		        var Buffer = util.inquire("buffer").Buffer;
++		        // refuse to use non-node buffers if not explicitly assigned (perf reasons):
++		        return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null;
++		    } catch (e) {
++		        /* istanbul ignore next */
++		        return null;
++		    }
++		})();
++
++		// Internal alias of or polyfull for Buffer.from.
++		util._Buffer_from = null;
++
++		// Internal alias of or polyfill for Buffer.allocUnsafe.
++		util._Buffer_allocUnsafe = null;
++
++		/**
++		 * Creates a new buffer of whatever type supported by the environment.
++		 * @param {number|number[]} [sizeOrArray=0] Buffer size or number array
++		 * @returns {Uint8Array|Buffer} Buffer
++		 */
++		util.newBuffer = function newBuffer(sizeOrArray) {
++		    /* istanbul ignore next */
++		    return typeof sizeOrArray === "number"
++		        ? util.Buffer
++		            ? util._Buffer_allocUnsafe(sizeOrArray)
++		            : new util.Array(sizeOrArray)
++		        : util.Buffer
++		            ? util._Buffer_from(sizeOrArray)
++		            : typeof Uint8Array === "undefined"
++		                ? sizeOrArray
++		                : new Uint8Array(sizeOrArray);
++		};
++
++		/**
++		 * Array implementation used in the browser. `Uint8Array` if supported, otherwise `Array`.
++		 * @type {Constructor<Uint8Array>}
++		 */
++		util.Array = typeof Uint8Array !== "undefined" ? Uint8Array /* istanbul ignore next */ : Array;
++
++		/**
++		 * Any compatible Long instance.
++		 * This is a minimal stand-alone definition of a Long instance. The actual type is that exported by long.js.
++		 * @interface Long
++		 * @property {number} low Low bits
++		 * @property {number} high High bits
++		 * @property {boolean} unsigned Whether unsigned or not
++		 */
++
++		/**
++		 * Long.js's Long class if available.
++		 * @type {Constructor<Long>}
++		 */
++		util.Long = /* istanbul ignore next */ util.global.dcodeIO && /* istanbul ignore next */ util.global.dcodeIO.Long
++		         || /* istanbul ignore next */ util.global.Long
++		         || util.inquire("long");
++
++		/**
++		 * Regular expression used to verify 2 bit (`bool`) map keys.
++		 * @type {RegExp}
++		 * @const
++		 */
++		util.key2Re = /^true|false|0|1$/;
++
++		/**
++		 * Regular expression used to verify 32 bit (`int32` etc.) map keys.
++		 * @type {RegExp}
++		 * @const
++		 */
++		util.key32Re = /^-?(?:0|[1-9][0-9]*)$/;
++
++		/**
++		 * Regular expression used to verify 64 bit (`int64` etc.) map keys.
++		 * @type {RegExp}
++		 * @const
++		 */
++		util.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/;
++
++		/**
++		 * Converts a number or long to an 8 characters long hash string.
++		 * @param {Long|number} value Value to convert
++		 * @returns {string} Hash
++		 */
++		util.longToHash = function longToHash(value) {
++		    return value
++		        ? util.LongBits.from(value).toHash()
++		        : util.LongBits.zeroHash;
++		};
++
++		/**
++		 * Converts an 8 characters long hash string to a long or number.
++		 * @param {string} hash Hash
++		 * @param {boolean} [unsigned=false] Whether unsigned or not
++		 * @returns {Long|number} Original value
++		 */
++		util.longFromHash = function longFromHash(hash, unsigned) {
++		    var bits = util.LongBits.fromHash(hash);
++		    if (util.Long)
++		        return util.Long.fromBits(bits.lo, bits.hi, unsigned);
++		    return bits.toNumber(Boolean(unsigned));
++		};
++
++		/**
++		 * Merges the properties of the source object into the destination object.
++		 * @memberof util
++		 * @param {Object.<string,*>} dst Destination object
++		 * @param {Object.<string,*>} src Source object
++		 * @param {boolean} [ifNotSet=false] Merges only if the key is not already set
++		 * @returns {Object.<string,*>} Destination object
++		 */
++		function merge(dst, src, ifNotSet) { // used by converters
++		    for (var keys = Object.keys(src), i = 0; i < keys.length; ++i)
++		        if (dst[keys[i]] === undefined || !ifNotSet)
++		            dst[keys[i]] = src[keys[i]];
++		    return dst;
++		}
++
++		util.merge = merge;
++
++		/**
++		 * Converts the first character of a string to lower case.
++		 * @param {string} str String to convert
++		 * @returns {string} Converted string
++		 */
++		util.lcFirst = function lcFirst(str) {
++		    return str.charAt(0).toLowerCase() + str.substring(1);
++		};
++
++		/**
++		 * Creates a custom error constructor.
++		 * @memberof util
++		 * @param {string} name Error name
++		 * @returns {Constructor<Error>} Custom error constructor
++		 */
++		function newError(name) {
++
++		    function CustomError(message, properties) {
++
++		        if (!(this instanceof CustomError))
++		            return new CustomError(message, properties);
++
++		        // Error.call(this, message);
++		        // ^ just returns a new error instance because the ctor can be called as a function
++
++		        Object.defineProperty(this, "message", { get: function() { return message; } });
++
++		        /* istanbul ignore next */
++		        if (Error.captureStackTrace) // node
++		            Error.captureStackTrace(this, CustomError);
++		        else
++		            Object.defineProperty(this, "stack", { value: new Error().stack || "" });
++
++		        if (properties)
++		            merge(this, properties);
++		    }
++
++		    CustomError.prototype = Object.create(Error.prototype, {
++		        constructor: {
++		            value: CustomError,
++		            writable: true,
++		            enumerable: false,
++		            configurable: true,
++		        },
++		        name: {
++		            get: function get() { return name; },
++		            set: undefined,
++		            enumerable: false,
++		            // configurable: false would accurately preserve the behavior of
++		            // the original, but I'm guessing that was not intentional.
++		            // For an actual error subclass, this property would
++		            // be configurable.
++		            configurable: true,
++		        },
++		        toString: {
++		            value: function value() { return this.name + ": " + this.message; },
++		            writable: true,
++		            enumerable: false,
++		            configurable: true,
++		        },
++		    });
++
++		    return CustomError;
++		}
++
++		util.newError = newError;
++
++		/**
++		 * Constructs a new protocol error.
++		 * @classdesc Error subclass indicating a protocol specifc error.
++		 * @memberof util
++		 * @extends Error
++		 * @template T extends Message<T>
++		 * @constructor
++		 * @param {string} message Error message
++		 * @param {Object.<string,*>} [properties] Additional properties
++		 * @example
++		 * try {
++		 *     MyMessage.decode(someBuffer); // throws if required fields are missing
++		 * } catch (e) {
++		 *     if (e instanceof ProtocolError && e.instance)
++		 *         console.log("decoded so far: " + JSON.stringify(e.instance));
++		 * }
++		 */
++		util.ProtocolError = newError("ProtocolError");
++
++		/**
++		 * So far decoded message instance.
++		 * @name util.ProtocolError#instance
++		 * @type {Message<T>}
++		 */
++
++		/**
++		 * A OneOf getter as returned by {@link util.oneOfGetter}.
++		 * @typedef OneOfGetter
++		 * @type {function}
++		 * @returns {string|undefined} Set field name, if any
++		 */
++
++		/**
++		 * Builds a getter for a oneof's present field name.
++		 * @param {string[]} fieldNames Field names
++		 * @returns {OneOfGetter} Unbound getter
++		 */
++		util.oneOfGetter = function getOneOf(fieldNames) {
++		    var fieldMap = {};
++		    for (var i = 0; i < fieldNames.length; ++i)
++		        fieldMap[fieldNames[i]] = 1;
++
++		    /**
++		     * @returns {string|undefined} Set field name, if any
++		     * @this Object
++		     * @ignore
++		     */
++		    return function() { // eslint-disable-line consistent-return
++		        for (var keys = Object.keys(this), i = keys.length - 1; i > -1; --i)
++		            if (fieldMap[keys[i]] === 1 && this[keys[i]] !== undefined && this[keys[i]] !== null)
++		                return keys[i];
++		    };
++		};
++
++		/**
++		 * A OneOf setter as returned by {@link util.oneOfSetter}.
++		 * @typedef OneOfSetter
++		 * @type {function}
++		 * @param {string|undefined} value Field name
++		 * @returns {undefined}
++		 */
++
++		/**
++		 * Builds a setter for a oneof's present field name.
++		 * @param {string[]} fieldNames Field names
++		 * @returns {OneOfSetter} Unbound setter
++		 */
++		util.oneOfSetter = function setOneOf(fieldNames) {
++
++		    /**
++		     * @param {string} name Field name
++		     * @returns {undefined}
++		     * @this Object
++		     * @ignore
++		     */
++		    return function(name) {
++		        for (var i = 0; i < fieldNames.length; ++i)
++		            if (fieldNames[i] !== name)
++		                delete this[fieldNames[i]];
++		    };
++		};
++
++		/**
++		 * Default conversion options used for {@link Message#toJSON} implementations.
++		 *
++		 * These options are close to proto3's JSON mapping with the exception that internal types like Any are handled just like messages. More precisely:
++		 *
++		 * - Longs become strings
++		 * - Enums become string keys
++		 * - Bytes become base64 encoded strings
++		 * - (Sub-)Messages become plain objects
++		 * - Maps become plain objects with all string keys
++		 * - Repeated fields become arrays
++		 * - NaN and Infinity for float and double fields become strings
++		 *
++		 * @type {IConversionOptions}
++		 * @see https://developers.google.com/protocol-buffers/docs/proto3?hl=en#json
++		 */
++		util.toJSONOptions = {
++		    longs: String,
++		    enums: String,
++		    bytes: String,
++		    json: true
++		};
++
++		// Sets up buffer utility according to the environment (called in index-minimal)
++		util._configure = function() {
++		    var Buffer = util.Buffer;
++		    /* istanbul ignore if */
++		    if (!Buffer) {
++		        util._Buffer_from = util._Buffer_allocUnsafe = null;
++		        return;
++		    }
++		    // because node 4.x buffers are incompatible & immutable
++		    // see: https://github.com/dcodeIO/protobuf.js/pull/665
++		    util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from ||
++		        /* istanbul ignore next */
++		        function Buffer_from(value, encoding) {
++		            return new Buffer(value, encoding);
++		        };
++		    util._Buffer_allocUnsafe = Buffer.allocUnsafe ||
++		        /* istanbul ignore next */
++		        function Buffer_allocUnsafe(size) {
++		            return new Buffer(size);
++		        };
++		}; 
++	} (minimal$1));
++	return minimal$1;
++}
++
++var writer = Writer$1;
++
++var util$4      = requireMinimal();
++
++var BufferWriter$1; // cyclic
++
++var LongBits$1  = util$4.LongBits,
++    base64    = util$4.base64,
++    utf8$1      = util$4.utf8;
++
++/**
++ * Constructs a new writer operation instance.
++ * @classdesc Scheduled writer operation.
++ * @constructor
++ * @param {function(*, Uint8Array, number)} fn Function to call
++ * @param {number} len Value byte length
++ * @param {*} val Value to write
++ * @ignore
++ */
++function Op(fn, len, val) {
++
++    /**
++     * Function to call.
++     * @type {function(Uint8Array, number, *)}
++     */
++    this.fn = fn;
++
++    /**
++     * Value byte length.
++     * @type {number}
++     */
++    this.len = len;
++
++    /**
++     * Next operation.
++     * @type {Writer.Op|undefined}
++     */
++    this.next = undefined;
++
++    /**
++     * Value to write.
++     * @type {*}
++     */
++    this.val = val; // type varies
++}
++
++/* istanbul ignore next */
++function noop() {} // eslint-disable-line no-empty-function
++
++/**
++ * Constructs a new writer state instance.
++ * @classdesc Copied writer state.
++ * @memberof Writer
++ * @constructor
++ * @param {Writer} writer Writer to copy state from
++ * @ignore
++ */
++function State(writer) {
++
++    /**
++     * Current head.
++     * @type {Writer.Op}
++     */
++    this.head = writer.head;
++
++    /**
++     * Current tail.
++     * @type {Writer.Op}
++     */
++    this.tail = writer.tail;
++
++    /**
++     * Current buffer length.
++     * @type {number}
++     */
++    this.len = writer.len;
++
++    /**
++     * Next state.
++     * @type {State|null}
++     */
++    this.next = writer.states;
++}
++
++/**
++ * Constructs a new writer instance.
++ * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`.
++ * @constructor
++ */
++function Writer$1() {
++
++    /**
++     * Current length.
++     * @type {number}
++     */
++    this.len = 0;
++
++    /**
++     * Operations head.
++     * @type {Object}
++     */
++    this.head = new Op(noop, 0, 0);
++
++    /**
++     * Operations tail
++     * @type {Object}
++     */
++    this.tail = this.head;
++
++    /**
++     * Linked forked states.
++     * @type {Object|null}
++     */
++    this.states = null;
++
++    // When a value is written, the writer calculates its byte length and puts it into a linked
++    // list of operations to perform when finish() is called. This both allows us to allocate
++    // buffers of the exact required size and reduces the amount of work we have to do compared
++    // to first calculating over objects and then encoding over objects. In our case, the encoding
++    // part is just a linked list walk calling operations with already prepared values.
++}
++
++var create$1 = function create() {
++    return util$4.Buffer
++        ? function create_buffer_setup() {
++            return (Writer$1.create = function create_buffer() {
++                return new BufferWriter$1();
++            })();
++        }
++        /* istanbul ignore next */
++        : function create_array() {
++            return new Writer$1();
++        };
++};
++
++/**
++ * Creates a new writer.
++ * @function
++ * @returns {BufferWriter|Writer} A {@link BufferWriter} when Buffers are supported, otherwise a {@link Writer}
++ */
++Writer$1.create = create$1();
++
++/**
++ * Allocates a buffer of the specified size.
++ * @param {number} size Buffer size
++ * @returns {Uint8Array} Buffer
++ */
++Writer$1.alloc = function alloc(size) {
++    return new util$4.Array(size);
++};
++
++// Use Uint8Array buffer pool in the browser, just like node does with buffers
++/* istanbul ignore else */
++if (util$4.Array !== Array)
++    Writer$1.alloc = util$4.pool(Writer$1.alloc, util$4.Array.prototype.subarray);
++
++/**
++ * Pushes a new operation to the queue.
++ * @param {function(Uint8Array, number, *)} fn Function to call
++ * @param {number} len Value byte length
++ * @param {number} val Value to write
++ * @returns {Writer} `this`
++ * @private
++ */
++Writer$1.prototype._push = function push(fn, len, val) {
++    this.tail = this.tail.next = new Op(fn, len, val);
++    this.len += len;
++    return this;
++};
++
++function writeByte(val, buf, pos) {
++    buf[pos] = val & 255;
++}
++
++function writeVarint32(val, buf, pos) {
++    while (val > 127) {
++        buf[pos++] = val & 127 | 128;
++        val >>>= 7;
++    }
++    buf[pos] = val;
++}
++
++/**
++ * Constructs a new varint writer operation instance.
++ * @classdesc Scheduled varint writer operation.
++ * @extends Op
++ * @constructor
++ * @param {number} len Value byte length
++ * @param {number} val Value to write
++ * @ignore
++ */
++function VarintOp(len, val) {
++    this.len = len;
++    this.next = undefined;
++    this.val = val;
++}
++
++VarintOp.prototype = Object.create(Op.prototype);
++VarintOp.prototype.fn = writeVarint32;
++
++/**
++ * Writes an unsigned 32 bit value as a varint.
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.uint32 = function write_uint32(value) {
++    // here, the call to this.push has been inlined and a varint specific Op subclass is used.
++    // uint32 is by far the most frequently used operation and benefits significantly from this.
++    this.len += (this.tail = this.tail.next = new VarintOp(
++        (value = value >>> 0)
++                < 128       ? 1
++        : value < 16384     ? 2
++        : value < 2097152   ? 3
++        : value < 268435456 ? 4
++        :                     5,
++    value)).len;
++    return this;
++};
++
++/**
++ * Writes a signed 32 bit value as a varint.
++ * @function
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.int32 = function write_int32(value) {
++    return value < 0
++        ? this._push(writeVarint64, 10, LongBits$1.fromNumber(value)) // 10 bytes per spec
++        : this.uint32(value);
++};
++
++/**
++ * Writes a 32 bit value as a varint, zig-zag encoded.
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.sint32 = function write_sint32(value) {
++    return this.uint32((value << 1 ^ value >> 31) >>> 0);
++};
++
++function writeVarint64(val, buf, pos) {
++    while (val.hi) {
++        buf[pos++] = val.lo & 127 | 128;
++        val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0;
++        val.hi >>>= 7;
++    }
++    while (val.lo > 127) {
++        buf[pos++] = val.lo & 127 | 128;
++        val.lo = val.lo >>> 7;
++    }
++    buf[pos++] = val.lo;
++}
++
++/**
++ * Writes an unsigned 64 bit value as a varint.
++ * @param {Long|number|string} value Value to write
++ * @returns {Writer} `this`
++ * @throws {TypeError} If `value` is a string and no long library is present.
++ */
++Writer$1.prototype.uint64 = function write_uint64(value) {
++    var bits = LongBits$1.from(value);
++    return this._push(writeVarint64, bits.length(), bits);
++};
++
++/**
++ * Writes a signed 64 bit value as a varint.
++ * @function
++ * @param {Long|number|string} value Value to write
++ * @returns {Writer} `this`
++ * @throws {TypeError} If `value` is a string and no long library is present.
++ */
++Writer$1.prototype.int64 = Writer$1.prototype.uint64;
++
++/**
++ * Writes a signed 64 bit value as a varint, zig-zag encoded.
++ * @param {Long|number|string} value Value to write
++ * @returns {Writer} `this`
++ * @throws {TypeError} If `value` is a string and no long library is present.
++ */
++Writer$1.prototype.sint64 = function write_sint64(value) {
++    var bits = LongBits$1.from(value).zzEncode();
++    return this._push(writeVarint64, bits.length(), bits);
++};
++
++/**
++ * Writes a boolish value as a varint.
++ * @param {boolean} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.bool = function write_bool(value) {
++    return this._push(writeByte, 1, value ? 1 : 0);
++};
++
++function writeFixed32(val, buf, pos) {
++    buf[pos    ] =  val         & 255;
++    buf[pos + 1] =  val >>> 8   & 255;
++    buf[pos + 2] =  val >>> 16  & 255;
++    buf[pos + 3] =  val >>> 24;
++}
++
++/**
++ * Writes an unsigned 32 bit value as fixed 32 bits.
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.fixed32 = function write_fixed32(value) {
++    return this._push(writeFixed32, 4, value >>> 0);
++};
++
++/**
++ * Writes a signed 32 bit value as fixed 32 bits.
++ * @function
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.sfixed32 = Writer$1.prototype.fixed32;
++
++/**
++ * Writes an unsigned 64 bit value as fixed 64 bits.
++ * @param {Long|number|string} value Value to write
++ * @returns {Writer} `this`
++ * @throws {TypeError} If `value` is a string and no long library is present.
++ */
++Writer$1.prototype.fixed64 = function write_fixed64(value) {
++    var bits = LongBits$1.from(value);
++    return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi);
++};
++
++/**
++ * Writes a signed 64 bit value as fixed 64 bits.
++ * @function
++ * @param {Long|number|string} value Value to write
++ * @returns {Writer} `this`
++ * @throws {TypeError} If `value` is a string and no long library is present.
++ */
++Writer$1.prototype.sfixed64 = Writer$1.prototype.fixed64;
++
++/**
++ * Writes a float (32 bit).
++ * @function
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.float = function write_float(value) {
++    return this._push(util$4.float.writeFloatLE, 4, value);
++};
++
++/**
++ * Writes a double (64 bit float).
++ * @function
++ * @param {number} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.double = function write_double(value) {
++    return this._push(util$4.float.writeDoubleLE, 8, value);
++};
++
++var writeBytes = util$4.Array.prototype.set
++    ? function writeBytes_set(val, buf, pos) {
++        buf.set(val, pos); // also works for plain array values
++    }
++    /* istanbul ignore next */
++    : function writeBytes_for(val, buf, pos) {
++        for (var i = 0; i < val.length; ++i)
++            buf[pos + i] = val[i];
++    };
++
++/**
++ * Writes a sequence of bytes.
++ * @param {Uint8Array|string} value Buffer or base64 encoded string to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.bytes = function write_bytes(value) {
++    var len = value.length >>> 0;
++    if (!len)
++        return this._push(writeByte, 1, 0);
++    if (util$4.isString(value)) {
++        var buf = Writer$1.alloc(len = base64.length(value));
++        base64.decode(value, buf, 0);
++        value = buf;
++    }
++    return this.uint32(len)._push(writeBytes, len, value);
++};
++
++/**
++ * Writes a string.
++ * @param {string} value Value to write
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.string = function write_string(value) {
++    var len = utf8$1.length(value);
++    return len
++        ? this.uint32(len)._push(utf8$1.write, len, value)
++        : this._push(writeByte, 1, 0);
++};
++
++/**
++ * Forks this writer's state by pushing it to a stack.
++ * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state.
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.fork = function fork() {
++    this.states = new State(this);
++    this.head = this.tail = new Op(noop, 0, 0);
++    this.len = 0;
++    return this;
++};
++
++/**
++ * Resets this instance to the last state.
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.reset = function reset() {
++    if (this.states) {
++        this.head   = this.states.head;
++        this.tail   = this.states.tail;
++        this.len    = this.states.len;
++        this.states = this.states.next;
++    } else {
++        this.head = this.tail = new Op(noop, 0, 0);
++        this.len  = 0;
++    }
++    return this;
++};
++
++/**
++ * Resets to the last state and appends the fork state's current write length as a varint followed by its operations.
++ * @returns {Writer} `this`
++ */
++Writer$1.prototype.ldelim = function ldelim() {
++    var head = this.head,
++        tail = this.tail,
++        len  = this.len;
++    this.reset().uint32(len);
++    if (len) {
++        this.tail.next = head.next; // skip noop
++        this.tail = tail;
++        this.len += len;
++    }
++    return this;
++};
++
++/**
++ * Finishes the write operation.
++ * @returns {Uint8Array} Finished buffer
++ */
++Writer$1.prototype.finish = function finish() {
++    var head = this.head.next, // skip noop
++        buf  = this.constructor.alloc(this.len),
++        pos  = 0;
++    while (head) {
++        head.fn(head.val, buf, pos);
++        pos += head.len;
++        head = head.next;
++    }
++    // this.head = this.tail = null;
++    return buf;
++};
++
++Writer$1._configure = function(BufferWriter_) {
++    BufferWriter$1 = BufferWriter_;
++    Writer$1.create = create$1();
++    BufferWriter$1._configure();
++};
++
++var writer_buffer = BufferWriter;
++
++// extends Writer
++var Writer = writer;
++(BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter;
++
++var util$3 = requireMinimal();
++
++/**
++ * Constructs a new buffer writer instance.
++ * @classdesc Wire format writer using node buffers.
++ * @extends Writer
++ * @constructor
++ */
++function BufferWriter() {
++    Writer.call(this);
++}
++
++BufferWriter._configure = function () {
++    /**
++     * Allocates a buffer of the specified size.
++     * @function
++     * @param {number} size Buffer size
++     * @returns {Buffer} Buffer
++     */
++    BufferWriter.alloc = util$3._Buffer_allocUnsafe;
++
++    BufferWriter.writeBytesBuffer = util$3.Buffer && util$3.Buffer.prototype instanceof Uint8Array && util$3.Buffer.prototype.set.name === "set"
++        ? function writeBytesBuffer_set(val, buf, pos) {
++          buf.set(val, pos); // faster than copy (requires node >= 4 where Buffers extend Uint8Array and set is properly inherited)
++          // also works for plain array values
++        }
++        /* istanbul ignore next */
++        : function writeBytesBuffer_copy(val, buf, pos) {
++          if (val.copy) // Buffer values
++            val.copy(buf, pos, 0, val.length);
++          else for (var i = 0; i < val.length;) // plain array values
++            buf[pos++] = val[i++];
++        };
++};
++
++
++/**
++ * @override
++ */
++BufferWriter.prototype.bytes = function write_bytes_buffer(value) {
++    if (util$3.isString(value))
++        value = util$3._Buffer_from(value, "base64");
++    var len = value.length >>> 0;
++    this.uint32(len);
++    if (len)
++        this._push(BufferWriter.writeBytesBuffer, len, value);
++    return this;
++};
++
++function writeStringBuffer(val, buf, pos) {
++    if (val.length < 40) // plain js is faster for short strings (probably due to redundant assertions)
++        util$3.utf8.write(val, buf, pos);
++    else if (buf.utf8Write)
++        buf.utf8Write(val, pos);
++    else
++        buf.write(val, pos);
++}
++
++/**
++ * @override
++ */
++BufferWriter.prototype.string = function write_string_buffer(value) {
++    var len = util$3.Buffer.byteLength(value);
++    this.uint32(len);
++    if (len)
++        this._push(writeStringBuffer, len, value);
++    return this;
++};
++
++
++/**
++ * Finishes the write operation.
++ * @name BufferWriter#finish
++ * @function
++ * @returns {Buffer} Finished buffer
++ */
++
++BufferWriter._configure();
++
++var reader = Reader$1;
++
++var util$2      = requireMinimal();
++
++var BufferReader$1; // cyclic
++
++var LongBits  = util$2.LongBits,
++    utf8      = util$2.utf8;
++
++/* istanbul ignore next */
++function indexOutOfRange(reader, writeLength) {
++    return RangeError("index out of range: " + reader.pos + " + " + (writeLength || 1) + " > " + reader.len);
++}
++
++/**
++ * Constructs a new reader instance using the specified buffer.
++ * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`.
++ * @constructor
++ * @param {Uint8Array} buffer Buffer to read from
++ */
++function Reader$1(buffer) {
++
++    /**
++     * Read buffer.
++     * @type {Uint8Array}
++     */
++    this.buf = buffer;
++
++    /**
++     * Read buffer position.
++     * @type {number}
++     */
++    this.pos = 0;
++
++    /**
++     * Read buffer length.
++     * @type {number}
++     */
++    this.len = buffer.length;
++}
++
++var create_array = typeof Uint8Array !== "undefined"
++    ? function create_typed_array(buffer) {
++        if (buffer instanceof Uint8Array || Array.isArray(buffer))
++            return new Reader$1(buffer);
++        throw Error("illegal buffer");
++    }
++    /* istanbul ignore next */
++    : function create_array(buffer) {
++        if (Array.isArray(buffer))
++            return new Reader$1(buffer);
++        throw Error("illegal buffer");
++    };
++
++var create = function create() {
++    return util$2.Buffer
++        ? function create_buffer_setup(buffer) {
++            return (Reader$1.create = function create_buffer(buffer) {
++                return util$2.Buffer.isBuffer(buffer)
++                    ? new BufferReader$1(buffer)
++                    /* istanbul ignore next */
++                    : create_array(buffer);
++            })(buffer);
++        }
++        /* istanbul ignore next */
++        : create_array;
++};
++
++/**
++ * Creates a new reader using the specified buffer.
++ * @function
++ * @param {Uint8Array|Buffer} buffer Buffer to read from
++ * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader}
++ * @throws {Error} If `buffer` is not a valid buffer
++ */
++Reader$1.create = create();
++
++Reader$1.prototype._slice = util$2.Array.prototype.subarray || /* istanbul ignore next */ util$2.Array.prototype.slice;
++
++/**
++ * Reads a varint as an unsigned 32 bit value.
++ * @function
++ * @returns {number} Value read
++ */
++Reader$1.prototype.uint32 = (function read_uint32_setup() {
++    var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)
++    return function read_uint32() {
++        value = (         this.buf[this.pos] & 127       ) >>> 0; if (this.buf[this.pos++] < 128) return value;
++        value = (value | (this.buf[this.pos] & 127) <<  7) >>> 0; if (this.buf[this.pos++] < 128) return value;
++        value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value;
++        value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value;
++        value = (value | (this.buf[this.pos] &  15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value;
++
++        /* istanbul ignore if */
++        if ((this.pos += 5) > this.len) {
++            this.pos = this.len;
++            throw indexOutOfRange(this, 10);
++        }
++        return value;
++    };
++})();
++
++/**
++ * Reads a varint as a signed 32 bit value.
++ * @returns {number} Value read
++ */
++Reader$1.prototype.int32 = function read_int32() {
++    return this.uint32() | 0;
++};
++
++/**
++ * Reads a zig-zag encoded varint as a signed 32 bit value.
++ * @returns {number} Value read
++ */
++Reader$1.prototype.sint32 = function read_sint32() {
++    var value = this.uint32();
++    return value >>> 1 ^ -(value & 1) | 0;
++};
++
++/* eslint-disable no-invalid-this */
++
++function readLongVarint() {
++    // tends to deopt with local vars for octet etc.
++    var bits = new LongBits(0, 0);
++    var i = 0;
++    if (this.len - this.pos > 4) { // fast route (lo)
++        for (; i < 4; ++i) {
++            // 1st..4th
++            bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;
++            if (this.buf[this.pos++] < 128)
++                return bits;
++        }
++        // 5th
++        bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0;
++        bits.hi = (bits.hi | (this.buf[this.pos] & 127) >>  4) >>> 0;
++        if (this.buf[this.pos++] < 128)
++            return bits;
++        i = 0;
++    } else {
++        for (; i < 3; ++i) {
++            /* istanbul ignore if */
++            if (this.pos >= this.len)
++                throw indexOutOfRange(this);
++            // 1st..3th
++            bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;
++            if (this.buf[this.pos++] < 128)
++                return bits;
++        }
++        // 4th
++        bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0;
++        return bits;
++    }
++    if (this.len - this.pos > 4) { // fast route (hi)
++        for (; i < 5; ++i) {
++            // 6th..10th
++            bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;
++            if (this.buf[this.pos++] < 128)
++                return bits;
++        }
++    } else {
++        for (; i < 5; ++i) {
++            /* istanbul ignore if */
++            if (this.pos >= this.len)
++                throw indexOutOfRange(this);
++            // 6th..10th
++            bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;
++            if (this.buf[this.pos++] < 128)
++                return bits;
++        }
++    }
++    /* istanbul ignore next */
++    throw Error("invalid varint encoding");
++}
++
++/* eslint-enable no-invalid-this */
++
++/**
++ * Reads a varint as a signed 64 bit value.
++ * @name Reader#int64
++ * @function
++ * @returns {Long} Value read
++ */
++
++/**
++ * Reads a varint as an unsigned 64 bit value.
++ * @name Reader#uint64
++ * @function
++ * @returns {Long} Value read
++ */
++
++/**
++ * Reads a zig-zag encoded varint as a signed 64 bit value.
++ * @name Reader#sint64
++ * @function
++ * @returns {Long} Value read
++ */
++
++/**
++ * Reads a varint as a boolean.
++ * @returns {boolean} Value read
++ */
++Reader$1.prototype.bool = function read_bool() {
++    return this.uint32() !== 0;
++};
++
++function readFixed32_end(buf, end) { // note that this uses `end`, not `pos`
++    return (buf[end - 4]
++          | buf[end - 3] << 8
++          | buf[end - 2] << 16
++          | buf[end - 1] << 24) >>> 0;
++}
++
++/**
++ * Reads fixed 32 bits as an unsigned 32 bit integer.
++ * @returns {number} Value read
++ */
++Reader$1.prototype.fixed32 = function read_fixed32() {
++
++    /* istanbul ignore if */
++    if (this.pos + 4 > this.len)
++        throw indexOutOfRange(this, 4);
++
++    return readFixed32_end(this.buf, this.pos += 4);
++};
++
++/**
++ * Reads fixed 32 bits as a signed 32 bit integer.
++ * @returns {number} Value read
++ */
++Reader$1.prototype.sfixed32 = function read_sfixed32() {
++
++    /* istanbul ignore if */
++    if (this.pos + 4 > this.len)
++        throw indexOutOfRange(this, 4);
++
++    return readFixed32_end(this.buf, this.pos += 4) | 0;
++};
++
++/* eslint-disable no-invalid-this */
++
++function readFixed64(/* this: Reader */) {
++
++    /* istanbul ignore if */
++    if (this.pos + 8 > this.len)
++        throw indexOutOfRange(this, 8);
++
++    return new LongBits(readFixed32_end(this.buf, this.pos += 4), readFixed32_end(this.buf, this.pos += 4));
++}
++
++/* eslint-enable no-invalid-this */
++
++/**
++ * Reads fixed 64 bits.
++ * @name Reader#fixed64
++ * @function
++ * @returns {Long} Value read
++ */
++
++/**
++ * Reads zig-zag encoded fixed 64 bits.
++ * @name Reader#sfixed64
++ * @function
++ * @returns {Long} Value read
++ */
++
++/**
++ * Reads a float (32 bit) as a number.
++ * @function
++ * @returns {number} Value read
++ */
++Reader$1.prototype.float = function read_float() {
++
++    /* istanbul ignore if */
++    if (this.pos + 4 > this.len)
++        throw indexOutOfRange(this, 4);
++
++    var value = util$2.float.readFloatLE(this.buf, this.pos);
++    this.pos += 4;
++    return value;
++};
++
++/**
++ * Reads a double (64 bit float) as a number.
++ * @function
++ * @returns {number} Value read
++ */
++Reader$1.prototype.double = function read_double() {
++
++    /* istanbul ignore if */
++    if (this.pos + 8 > this.len)
++        throw indexOutOfRange(this, 4);
++
++    var value = util$2.float.readDoubleLE(this.buf, this.pos);
++    this.pos += 8;
++    return value;
++};
++
++/**
++ * Reads a sequence of bytes preceeded by its length as a varint.
++ * @returns {Uint8Array} Value read
++ */
++Reader$1.prototype.bytes = function read_bytes() {
++    var length = this.uint32(),
++        start  = this.pos,
++        end    = this.pos + length;
++
++    /* istanbul ignore if */
++    if (end > this.len)
++        throw indexOutOfRange(this, length);
++
++    this.pos += length;
++    if (Array.isArray(this.buf)) // plain array
++        return this.buf.slice(start, end);
++
++    if (start === end) { // fix for IE 10/Win8 and others' subarray returning array of size 1
++        var nativeBuffer = util$2.Buffer;
++        return nativeBuffer
++            ? nativeBuffer.alloc(0)
++            : new this.buf.constructor(0);
++    }
++    return this._slice.call(this.buf, start, end);
++};
++
++/**
++ * Reads a string preceeded by its byte length as a varint.
++ * @returns {string} Value read
++ */
++Reader$1.prototype.string = function read_string() {
++    var bytes = this.bytes();
++    return utf8.read(bytes, 0, bytes.length);
++};
++
++/**
++ * Skips the specified number of bytes if specified, otherwise skips a varint.
++ * @param {number} [length] Length if known, otherwise a varint is assumed
++ * @returns {Reader} `this`
++ */
++Reader$1.prototype.skip = function skip(length) {
++    if (typeof length === "number") {
++        /* istanbul ignore if */
++        if (this.pos + length > this.len)
++            throw indexOutOfRange(this, length);
++        this.pos += length;
++    } else {
++        do {
++            /* istanbul ignore if */
++            if (this.pos >= this.len)
++                throw indexOutOfRange(this);
++        } while (this.buf[this.pos++] & 128);
++    }
++    return this;
++};
++
++/**
++ * Skips the next element of the specified wire type.
++ * @param {number} wireType Wire type received
++ * @returns {Reader} `this`
++ */
++Reader$1.prototype.skipType = function(wireType) {
++    switch (wireType) {
++        case 0:
++            this.skip();
++            break;
++        case 1:
++            this.skip(8);
++            break;
++        case 2:
++            this.skip(this.uint32());
++            break;
++        case 3:
++            while ((wireType = this.uint32() & 7) !== 4) {
++                this.skipType(wireType);
++            }
++            break;
++        case 5:
++            this.skip(4);
++            break;
++
++        /* istanbul ignore next */
++        default:
++            throw Error("invalid wire type " + wireType + " at offset " + this.pos);
++    }
++    return this;
++};
++
++Reader$1._configure = function(BufferReader_) {
++    BufferReader$1 = BufferReader_;
++    Reader$1.create = create();
++    BufferReader$1._configure();
++
++    var fn = util$2.Long ? "toLong" : /* istanbul ignore next */ "toNumber";
++    util$2.merge(Reader$1.prototype, {
++
++        int64: function read_int64() {
++            return readLongVarint.call(this)[fn](false);
++        },
++
++        uint64: function read_uint64() {
++            return readLongVarint.call(this)[fn](true);
++        },
++
++        sint64: function read_sint64() {
++            return readLongVarint.call(this).zzDecode()[fn](false);
++        },
++
++        fixed64: function read_fixed64() {
++            return readFixed64.call(this)[fn](true);
++        },
++
++        sfixed64: function read_sfixed64() {
++            return readFixed64.call(this)[fn](false);
++        }
++
++    });
++};
++
++var reader_buffer = BufferReader;
++
++// extends Reader
++var Reader = reader;
++(BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader;
++
++var util$1 = requireMinimal();
++
++/**
++ * Constructs a new buffer reader instance.
++ * @classdesc Wire format reader using node buffers.
++ * @extends Reader
++ * @constructor
++ * @param {Buffer} buffer Buffer to read from
++ */
++function BufferReader(buffer) {
++    Reader.call(this, buffer);
++
++    /**
++     * Read buffer.
++     * @name BufferReader#buf
++     * @type {Buffer}
++     */
++}
++
++BufferReader._configure = function () {
++    /* istanbul ignore else */
++    if (util$1.Buffer)
++        BufferReader.prototype._slice = util$1.Buffer.prototype.slice;
++};
++
++
++/**
++ * @override
++ */
++BufferReader.prototype.string = function read_string_buffer() {
++    var len = this.uint32(); // modifies pos
++    return this.buf.utf8Slice
++        ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len))
++        : this.buf.toString("utf-8", this.pos, this.pos = Math.min(this.pos + len, this.len));
++};
++
++/**
++ * Reads a sequence of bytes preceeded by its length as a varint.
++ * @name BufferReader#bytes
++ * @function
++ * @returns {Buffer} Value read
++ */
++
++BufferReader._configure();
++
++var rpc = {};
++
++var service = Service;
++
++var util = requireMinimal();
++
++// Extends EventEmitter
++(Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service;
++
++/**
++ * A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}.
++ *
++ * Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`.
++ * @typedef rpc.ServiceMethodCallback
++ * @template TRes extends Message<TRes>
++ * @type {function}
++ * @param {Error|null} error Error, if any
++ * @param {TRes} [response] Response message
++ * @returns {undefined}
++ */
++
++/**
++ * A service method part of a {@link rpc.Service} as created by {@link Service.create}.
++ * @typedef rpc.ServiceMethod
++ * @template TReq extends Message<TReq>
++ * @template TRes extends Message<TRes>
++ * @type {function}
++ * @param {TReq|Properties<TReq>} request Request message or plain object
++ * @param {rpc.ServiceMethodCallback<TRes>} [callback] Node-style callback called with the error, if any, and the response message
++ * @returns {Promise<Message<TRes>>} Promise if `callback` has been omitted, otherwise `undefined`
++ */
++
++/**
++ * Constructs a new RPC service instance.
++ * @classdesc An RPC service as returned by {@link Service#create}.
++ * @exports rpc.Service
++ * @extends util.EventEmitter
++ * @constructor
++ * @param {RPCImpl} rpcImpl RPC implementation
++ * @param {boolean} [requestDelimited=false] Whether requests are length-delimited
++ * @param {boolean} [responseDelimited=false] Whether responses are length-delimited
++ */
++function Service(rpcImpl, requestDelimited, responseDelimited) {
++
++    if (typeof rpcImpl !== "function")
++        throw TypeError("rpcImpl must be a function");
++
++    util.EventEmitter.call(this);
++
++    /**
++     * RPC implementation. Becomes `null` once the service is ended.
++     * @type {RPCImpl|null}
++     */
++    this.rpcImpl = rpcImpl;
++
++    /**
++     * Whether requests are length-delimited.
++     * @type {boolean}
++     */
++    this.requestDelimited = Boolean(requestDelimited);
++
++    /**
++     * Whether responses are length-delimited.
++     * @type {boolean}
++     */
++    this.responseDelimited = Boolean(responseDelimited);
++}
++
++/**
++ * Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}.
++ * @param {Method|rpc.ServiceMethod<TReq,TRes>} method Reflected or static method
++ * @param {Constructor<TReq>} requestCtor Request constructor
++ * @param {Constructor<TRes>} responseCtor Response constructor
++ * @param {TReq|Properties<TReq>} request Request message or plain object
++ * @param {rpc.ServiceMethodCallback<TRes>} callback Service callback
++ * @returns {undefined}
++ * @template TReq extends Message<TReq>
++ * @template TRes extends Message<TRes>
++ */
++Service.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) {
++
++    if (!request)
++        throw TypeError("request must be specified");
++
++    var self = this;
++    if (!callback)
++        return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request);
++
++    if (!self.rpcImpl) {
++        setTimeout(function() { callback(Error("already ended")); }, 0);
++        return undefined;
++    }
++
++    try {
++        return self.rpcImpl(
++            method,
++            requestCtor[self.requestDelimited ? "encodeDelimited" : "encode"](request).finish(),
++            function rpcCallback(err, response) {
++
++                if (err) {
++                    self.emit("error", err, method);
++                    return callback(err);
++                }
++
++                if (response === null) {
++                    self.end(/* endedByRPC */ true);
++                    return undefined;
++                }
++
++                if (!(response instanceof responseCtor)) {
++                    try {
++                        response = responseCtor[self.responseDelimited ? "decodeDelimited" : "decode"](response);
++                    } catch (err) {
++                        self.emit("error", err, method);
++                        return callback(err);
++                    }
++                }
++
++                self.emit("data", response, method);
++                return callback(null, response);
++            }
++        );
++    } catch (err) {
++        self.emit("error", err, method);
++        setTimeout(function() { callback(err); }, 0);
++        return undefined;
++    }
++};
++
++/**
++ * Ends this service and emits the `end` event.
++ * @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation.
++ * @returns {rpc.Service} `this`
++ */
++Service.prototype.end = function end(endedByRPC) {
++    if (this.rpcImpl) {
++        if (!endedByRPC) // signal end to rpcImpl
++            this.rpcImpl(null, null, null);
++        this.rpcImpl = null;
++        this.emit("end").off();
++    }
++    return this;
++};
++
++(function (exports) {
++
++	/**
++	 * Streaming RPC helpers.
++	 * @namespace
++	 */
++	var rpc = exports;
++
++	/**
++	 * RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets.
++	 * @typedef RPCImpl
++	 * @type {function}
++	 * @param {Method|rpc.ServiceMethod<Message<{}>,Message<{}>>} method Reflected or static method being called
++	 * @param {Uint8Array} requestData Request data
++	 * @param {RPCImplCallback} callback Callback function
++	 * @returns {undefined}
++	 * @example
++	 * function rpcImpl(method, requestData, callback) {
++	 *     if (protobuf.util.lcFirst(method.name) !== "myMethod") // compatible with static code
++	 *         throw Error("no such method");
++	 *     asynchronouslyObtainAResponse(requestData, function(err, responseData) {
++	 *         callback(err, responseData);
++	 *     });
++	 * }
++	 */
++
++	/**
++	 * Node-style callback as used by {@link RPCImpl}.
++	 * @typedef RPCImplCallback
++	 * @type {function}
++	 * @param {Error|null} error Error, if any, otherwise `null`
++	 * @param {Uint8Array|null} [response] Response data or `null` to signal end of stream, if there hasn't been an error
++	 * @returns {undefined}
++	 */
++
++	rpc.Service = service; 
++} (rpc));
++
++var roots = {};
++
++(function (exports) {
++	var protobuf = exports;
++
++	/**
++	 * Build type, one of `"full"`, `"light"` or `"minimal"`.
++	 * @name build
++	 * @type {string}
++	 * @const
++	 */
++	protobuf.build = "minimal";
++
++	// Serialization
++	protobuf.Writer       = writer;
++	protobuf.BufferWriter = writer_buffer;
++	protobuf.Reader       = reader;
++	protobuf.BufferReader = reader_buffer;
++
++	// Utility
++	protobuf.util         = requireMinimal();
++	protobuf.rpc          = rpc;
++	protobuf.roots        = roots;
++	protobuf.configure    = configure;
++
++	/* istanbul ignore next */
++	/**
++	 * Reconfigures the library according to the environment.
++	 * @returns {undefined}
++	 */
++	function configure() {
++	    protobuf.util._configure();
++	    protobuf.Writer._configure(protobuf.BufferWriter);
++	    protobuf.Reader._configure(protobuf.BufferReader);
++	}
++
++	// Set up buffer utility according to the environment
++	configure(); 
++} (indexMinimal));
++
++var minimal = indexMinimal;
++
++/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
++
++const $util = minimal.util;
++
++const $root = minimal.roots["default"] || (minimal.roots["default"] = {});
++
++const gdm = $root.gdm = (() => {
++
++    const gdm = {};
++
++    gdm.DataType = (function() {
++        const valuesById = {}, values = Object.create(valuesById);
++        values[valuesById[0] = "unknownType"] = 0;
++        values[valuesById[1] = "hello"] = 1;
++        values[valuesById[2] = "event"] = 2;
++        values[valuesById[3] = "eventAck"] = 3;
++        values[valuesById[4] = "request"] = 4;
++        values[valuesById[5] = "response"] = 5;
++        values[valuesById[6] = "poll"] = 6;
++        values[valuesById[7] = "pollResponse"] = 7;
++        return values;
++    })();
++
++    gdm.Data = (function() {
++
++        function Data(properties) {
++            this.pollResponse = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        Data.prototype.type = 0;
++        Data.prototype.hello = null;
++        Data.prototype.request = null;
++        Data.prototype.response = null;
++        Data.prototype.event = null;
++        Data.prototype.pollResponse = $util.emptyArray;
++
++        let $oneOfFields;
++
++        Object.defineProperty(Data.prototype, "_hello", {
++            get: $util.oneOfGetter($oneOfFields = ["hello"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(Data.prototype, "_request", {
++            get: $util.oneOfGetter($oneOfFields = ["request"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(Data.prototype, "_response", {
++            get: $util.oneOfGetter($oneOfFields = ["response"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(Data.prototype, "_event", {
++            get: $util.oneOfGetter($oneOfFields = ["event"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Data.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.Data)
++                return object;
++            let message = new $root.gdm.Data();
++            switch (object.type) {
++            default:
++                if (typeof object.type === "number") {
++                    message.type = object.type;
++                    break;
++                }
++                break;
++            case "unknownType":
++            case 0:
++                message.type = 0;
++                break;
++            case "hello":
++            case 1:
++                message.type = 1;
++                break;
++            case "event":
++            case 2:
++                message.type = 2;
++                break;
++            case "eventAck":
++            case 3:
++                message.type = 3;
++                break;
++            case "request":
++            case 4:
++                message.type = 4;
++                break;
++            case "response":
++            case 5:
++                message.type = 5;
++                break;
++            case "poll":
++            case 6:
++                message.type = 6;
++                break;
++            case "pollResponse":
++            case 7:
++                message.type = 7;
++                break;
++            }
++            if (object.hello != null) {
++                if (typeof object.hello !== "object")
++                    throw TypeError(".gdm.Data.hello: object expected");
++                message.hello = $root.gdm.HelloData.fromObject(object.hello);
++            }
++            if (object.request != null) {
++                if (typeof object.request !== "object")
++                    throw TypeError(".gdm.Data.request: object expected");
++                message.request = $root.gdm.RequestData.fromObject(object.request);
++            }
++            if (object.response != null) {
++                if (typeof object.response !== "object")
++                    throw TypeError(".gdm.Data.response: object expected");
++                message.response = $root.gdm.ResponseData.fromObject(object.response);
++            }
++            if (object.event != null) {
++                if (typeof object.event !== "object")
++                    throw TypeError(".gdm.Data.event: object expected");
++                message.event = $root.gdm.EventData.fromObject(object.event);
++            }
++            if (object.pollResponse) {
++                if (!Array.isArray(object.pollResponse))
++                    throw TypeError(".gdm.Data.pollResponse: array expected");
++                message.pollResponse = [];
++                for (let i = 0; i < object.pollResponse.length; ++i) {
++                    if (typeof object.pollResponse[i] !== "object")
++                        throw TypeError(".gdm.Data.pollResponse: object expected");
++                    message.pollResponse[i] = $root.gdm.EventData.fromObject(object.pollResponse[i]);
++                }
++            }
++            return message;
++        };
++
++        Data.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.pollResponse = [];
++            if (options.defaults)
++                object.type = options.enums === String ? "unknownType" : 0;
++            if (message.type != null && message.hasOwnProperty("type"))
++                object.type = options.enums === String ? $root.gdm.DataType[message.type] === undefined ? message.type : $root.gdm.DataType[message.type] : message.type;
++            if (message.hello != null && message.hasOwnProperty("hello")) {
++                object.hello = $root.gdm.HelloData.toObject(message.hello, options);
++                if (options.oneofs)
++                    object._hello = "hello";
++            }
++            if (message.request != null && message.hasOwnProperty("request")) {
++                object.request = $root.gdm.RequestData.toObject(message.request, options);
++                if (options.oneofs)
++                    object._request = "request";
++            }
++            if (message.response != null && message.hasOwnProperty("response")) {
++                object.response = $root.gdm.ResponseData.toObject(message.response, options);
++                if (options.oneofs)
++                    object._response = "response";
++            }
++            if (message.event != null && message.hasOwnProperty("event")) {
++                object.event = $root.gdm.EventData.toObject(message.event, options);
++                if (options.oneofs)
++                    object._event = "event";
++            }
++            if (message.pollResponse && message.pollResponse.length) {
++                object.pollResponse = [];
++                for (let j = 0; j < message.pollResponse.length; ++j)
++                    object.pollResponse[j] = $root.gdm.EventData.toObject(message.pollResponse[j], options);
++            }
++            return object;
++        };
++
++        Data.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return Data;
++    })();
++
++    gdm.HelloData = (function() {
++
++        function HelloData(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        HelloData.prototype.version = 0;
++
++        HelloData.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.HelloData)
++                return object;
++            let message = new $root.gdm.HelloData();
++            if (object.version != null)
++                message.version = object.version >>> 0;
++            return message;
++        };
++
++        HelloData.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.version = 0;
++            if (message.version != null && message.hasOwnProperty("version"))
++                object.version = message.version;
++            return object;
++        };
++
++        HelloData.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return HelloData;
++    })();
++
++    gdm.RequestType = (function() {
++        const valuesById = {}, values = Object.create(valuesById);
++        values[valuesById[0] = "unknownRequest"] = 0;
++        values[valuesById[1] = "updateBrokersList"] = 1;
++        values[valuesById[2] = "composeAuthenticationView"] = 2;
++        values[valuesById[3] = "uiLayoutCapabilities"] = 3;
++        values[valuesById[4] = "changeStage"] = 4;
++        return values;
++    })();
++
++    gdm.Requests = (function() {
++
++        function Requests(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        Requests.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.Requests)
++                return object;
++            return new $root.gdm.Requests();
++        };
++
++        Requests.toObject = function toObject() {
++            return {};
++        };
++
++        Requests.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        Requests.UiLayoutCapabilities = (function() {
++
++            function UiLayoutCapabilities(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            UiLayoutCapabilities.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Requests.UiLayoutCapabilities)
++                    return object;
++                return new $root.gdm.Requests.UiLayoutCapabilities();
++            };
++
++            UiLayoutCapabilities.toObject = function toObject() {
++                return {};
++            };
++
++            UiLayoutCapabilities.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return UiLayoutCapabilities;
++        })();
++
++        Requests.ChangeStage = (function() {
++
++            function ChangeStage(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            ChangeStage.prototype.stage = 0;
++
++            ChangeStage.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Requests.ChangeStage)
++                    return object;
++                let message = new $root.gdm.Requests.ChangeStage();
++                switch (object.stage) {
++                default:
++                    if (typeof object.stage === "number") {
++                        message.stage = object.stage;
++                        break;
++                    }
++                    break;
++                case "userSelection":
++                case 0:
++                    message.stage = 0;
++                    break;
++                case "brokerSelection":
++                case 1:
++                    message.stage = 1;
++                    break;
++                case "authModeSelection":
++                case 2:
++                    message.stage = 2;
++                    break;
++                case "challenge":
++                case 3:
++                    message.stage = 3;
++                    break;
++                }
++                return message;
++            };
++
++            ChangeStage.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.stage = options.enums === String ? "userSelection" : 0;
++                if (message.stage != null && message.hasOwnProperty("stage"))
++                    object.stage = options.enums === String ? $root.pam.Stage[message.stage] === undefined ? message.stage : $root.pam.Stage[message.stage] : message.stage;
++                return object;
++            };
++
++            ChangeStage.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return ChangeStage;
++        })();
++
++        return Requests;
++    })();
++
++    gdm.RequestData = (function() {
++
++        function RequestData(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        RequestData.prototype.type = 0;
++        RequestData.prototype.uiLayoutCapabilities = null;
++        RequestData.prototype.changeStage = null;
++
++        let $oneOfFields;
++
++        Object.defineProperty(RequestData.prototype, "data", {
++            get: $util.oneOfGetter($oneOfFields = ["uiLayoutCapabilities", "changeStage"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        RequestData.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.RequestData)
++                return object;
++            let message = new $root.gdm.RequestData();
++            switch (object.type) {
++            default:
++                if (typeof object.type === "number") {
++                    message.type = object.type;
++                    break;
++                }
++                break;
++            case "unknownRequest":
++            case 0:
++                message.type = 0;
++                break;
++            case "updateBrokersList":
++            case 1:
++                message.type = 1;
++                break;
++            case "composeAuthenticationView":
++            case 2:
++                message.type = 2;
++                break;
++            case "uiLayoutCapabilities":
++            case 3:
++                message.type = 3;
++                break;
++            case "changeStage":
++            case 4:
++                message.type = 4;
++                break;
++            }
++            if (object.uiLayoutCapabilities != null) {
++                if (typeof object.uiLayoutCapabilities !== "object")
++                    throw TypeError(".gdm.RequestData.uiLayoutCapabilities: object expected");
++                message.uiLayoutCapabilities = $root.gdm.Requests.UiLayoutCapabilities.fromObject(object.uiLayoutCapabilities);
++            }
++            if (object.changeStage != null) {
++                if (typeof object.changeStage !== "object")
++                    throw TypeError(".gdm.RequestData.changeStage: object expected");
++                message.changeStage = $root.gdm.Requests.ChangeStage.fromObject(object.changeStage);
++            }
++            return message;
++        };
++
++        RequestData.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.type = options.enums === String ? "unknownRequest" : 0;
++            if (message.type != null && message.hasOwnProperty("type"))
++                object.type = options.enums === String ? $root.gdm.RequestType[message.type] === undefined ? message.type : $root.gdm.RequestType[message.type] : message.type;
++            if (message.uiLayoutCapabilities != null && message.hasOwnProperty("uiLayoutCapabilities")) {
++                object.uiLayoutCapabilities = $root.gdm.Requests.UiLayoutCapabilities.toObject(message.uiLayoutCapabilities, options);
++                if (options.oneofs)
++                    object.data = "uiLayoutCapabilities";
++            }
++            if (message.changeStage != null && message.hasOwnProperty("changeStage")) {
++                object.changeStage = $root.gdm.Requests.ChangeStage.toObject(message.changeStage, options);
++                if (options.oneofs)
++                    object.data = "changeStage";
++            }
++            return object;
++        };
++
++        RequestData.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return RequestData;
++    })();
++
++    gdm.Responses = (function() {
++
++        function Responses(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        Responses.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.Responses)
++                return object;
++            return new $root.gdm.Responses();
++        };
++
++        Responses.toObject = function toObject() {
++            return {};
++        };
++
++        Responses.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        Responses.Ack = (function() {
++
++            function Ack(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            Ack.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Responses.Ack)
++                    return object;
++                return new $root.gdm.Responses.Ack();
++            };
++
++            Ack.toObject = function toObject() {
++                return {};
++            };
++
++            Ack.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return Ack;
++        })();
++
++        Responses.UiLayoutCapabilities = (function() {
++
++            function UiLayoutCapabilities(properties) {
++                this.supportedUiLayouts = [];
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            UiLayoutCapabilities.prototype.supportedUiLayouts = $util.emptyArray;
++
++            UiLayoutCapabilities.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Responses.UiLayoutCapabilities)
++                    return object;
++                let message = new $root.gdm.Responses.UiLayoutCapabilities();
++                if (object.supportedUiLayouts) {
++                    if (!Array.isArray(object.supportedUiLayouts))
++                        throw TypeError(".gdm.Responses.UiLayoutCapabilities.supportedUiLayouts: array expected");
++                    message.supportedUiLayouts = [];
++                    for (let i = 0; i < object.supportedUiLayouts.length; ++i) {
++                        if (typeof object.supportedUiLayouts[i] !== "object")
++                            throw TypeError(".gdm.Responses.UiLayoutCapabilities.supportedUiLayouts: object expected");
++                        message.supportedUiLayouts[i] = $root.authd.UILayout.fromObject(object.supportedUiLayouts[i]);
++                    }
++                }
++                return message;
++            };
++
++            UiLayoutCapabilities.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.arrays || options.defaults)
++                    object.supportedUiLayouts = [];
++                if (message.supportedUiLayouts && message.supportedUiLayouts.length) {
++                    object.supportedUiLayouts = [];
++                    for (let j = 0; j < message.supportedUiLayouts.length; ++j)
++                        object.supportedUiLayouts[j] = $root.authd.UILayout.toObject(message.supportedUiLayouts[j], options);
++                }
++                return object;
++            };
++
++            UiLayoutCapabilities.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return UiLayoutCapabilities;
++        })();
++
++        return Responses;
++    })();
++
++    gdm.ResponseData = (function() {
++
++        function ResponseData(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        ResponseData.prototype.type = 0;
++        ResponseData.prototype.ack = null;
++        ResponseData.prototype.uiLayoutCapabilities = null;
++
++        let $oneOfFields;
++
++        Object.defineProperty(ResponseData.prototype, "data", {
++            get: $util.oneOfGetter($oneOfFields = ["ack", "uiLayoutCapabilities"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        ResponseData.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.ResponseData)
++                return object;
++            let message = new $root.gdm.ResponseData();
++            switch (object.type) {
++            default:
++                if (typeof object.type === "number") {
++                    message.type = object.type;
++                    break;
++                }
++                break;
++            case "unknownRequest":
++            case 0:
++                message.type = 0;
++                break;
++            case "updateBrokersList":
++            case 1:
++                message.type = 1;
++                break;
++            case "composeAuthenticationView":
++            case 2:
++                message.type = 2;
++                break;
++            case "uiLayoutCapabilities":
++            case 3:
++                message.type = 3;
++                break;
++            case "changeStage":
++            case 4:
++                message.type = 4;
++                break;
++            }
++            if (object.ack != null) {
++                if (typeof object.ack !== "object")
++                    throw TypeError(".gdm.ResponseData.ack: object expected");
++                message.ack = $root.gdm.Responses.Ack.fromObject(object.ack);
++            }
++            if (object.uiLayoutCapabilities != null) {
++                if (typeof object.uiLayoutCapabilities !== "object")
++                    throw TypeError(".gdm.ResponseData.uiLayoutCapabilities: object expected");
++                message.uiLayoutCapabilities = $root.gdm.Responses.UiLayoutCapabilities.fromObject(object.uiLayoutCapabilities);
++            }
++            return message;
++        };
++
++        ResponseData.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.type = options.enums === String ? "unknownRequest" : 0;
++            if (message.type != null && message.hasOwnProperty("type"))
++                object.type = options.enums === String ? $root.gdm.RequestType[message.type] === undefined ? message.type : $root.gdm.RequestType[message.type] : message.type;
++            if (message.ack != null && message.hasOwnProperty("ack")) {
++                object.ack = $root.gdm.Responses.Ack.toObject(message.ack, options);
++                if (options.oneofs)
++                    object.data = "ack";
++            }
++            if (message.uiLayoutCapabilities != null && message.hasOwnProperty("uiLayoutCapabilities")) {
++                object.uiLayoutCapabilities = $root.gdm.Responses.UiLayoutCapabilities.toObject(message.uiLayoutCapabilities, options);
++                if (options.oneofs)
++                    object.data = "uiLayoutCapabilities";
++            }
++            return object;
++        };
++
++        ResponseData.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return ResponseData;
++    })();
++
++    gdm.EventType = (function() {
++        const valuesById = {}, values = Object.create(valuesById);
++        values[valuesById[0] = "unknownEvent"] = 0;
++        values[valuesById[1] = "userSelected"] = 1;
++        values[valuesById[2] = "brokersReceived"] = 2;
++        values[valuesById[3] = "brokerSelected"] = 3;
++        values[valuesById[4] = "authModesReceived"] = 4;
++        values[valuesById[5] = "authModeSelected"] = 5;
++        values[valuesById[6] = "reselectAuthMode"] = 6;
++        values[valuesById[7] = "authEvent"] = 7;
++        values[valuesById[8] = "uiLayoutReceived"] = 8;
++        values[valuesById[9] = "startAuthentication"] = 9;
++        values[valuesById[10] = "isAuthenticatedRequested"] = 10;
++        values[valuesById[11] = "isAuthenticatedCancelled"] = 11;
++        values[valuesById[12] = "stageChanged"] = 12;
++        return values;
++    })();
++
++    gdm.Events = (function() {
++
++        function Events(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        Events.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.Events)
++                return object;
++            return new $root.gdm.Events();
++        };
++
++        Events.toObject = function toObject() {
++            return {};
++        };
++
++        Events.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        Events.BrokersReceived = (function() {
++
++            function BrokersReceived(properties) {
++                this.brokersInfos = [];
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            BrokersReceived.prototype.brokersInfos = $util.emptyArray;
++
++            BrokersReceived.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.BrokersReceived)
++                    return object;
++                let message = new $root.gdm.Events.BrokersReceived();
++                if (object.brokersInfos) {
++                    if (!Array.isArray(object.brokersInfos))
++                        throw TypeError(".gdm.Events.BrokersReceived.brokersInfos: array expected");
++                    message.brokersInfos = [];
++                    for (let i = 0; i < object.brokersInfos.length; ++i) {
++                        if (typeof object.brokersInfos[i] !== "object")
++                            throw TypeError(".gdm.Events.BrokersReceived.brokersInfos: object expected");
++                        message.brokersInfos[i] = $root.authd.ABResponse.BrokerInfo.fromObject(object.brokersInfos[i]);
++                    }
++                }
++                return message;
++            };
++
++            BrokersReceived.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.arrays || options.defaults)
++                    object.brokersInfos = [];
++                if (message.brokersInfos && message.brokersInfos.length) {
++                    object.brokersInfos = [];
++                    for (let j = 0; j < message.brokersInfos.length; ++j)
++                        object.brokersInfos[j] = $root.authd.ABResponse.BrokerInfo.toObject(message.brokersInfos[j], options);
++                }
++                return object;
++            };
++
++            BrokersReceived.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return BrokersReceived;
++        })();
++
++        Events.BrokerSelected = (function() {
++
++            function BrokerSelected(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            BrokerSelected.prototype.brokerId = "";
++
++            BrokerSelected.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.BrokerSelected)
++                    return object;
++                let message = new $root.gdm.Events.BrokerSelected();
++                if (object.brokerId != null)
++                    message.brokerId = String(object.brokerId);
++                return message;
++            };
++
++            BrokerSelected.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.brokerId = "";
++                if (message.brokerId != null && message.hasOwnProperty("brokerId"))
++                    object.brokerId = message.brokerId;
++                return object;
++            };
++
++            BrokerSelected.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return BrokerSelected;
++        })();
++
++        Events.UserSelected = (function() {
++
++            function UserSelected(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            UserSelected.prototype.userId = "";
++
++            UserSelected.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.UserSelected)
++                    return object;
++                let message = new $root.gdm.Events.UserSelected();
++                if (object.userId != null)
++                    message.userId = String(object.userId);
++                return message;
++            };
++
++            UserSelected.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.userId = "";
++                if (message.userId != null && message.hasOwnProperty("userId"))
++                    object.userId = message.userId;
++                return object;
++            };
++
++            UserSelected.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return UserSelected;
++        })();
++
++        Events.StartAuthentication = (function() {
++
++            function StartAuthentication(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            StartAuthentication.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.StartAuthentication)
++                    return object;
++                return new $root.gdm.Events.StartAuthentication();
++            };
++
++            StartAuthentication.toObject = function toObject() {
++                return {};
++            };
++
++            StartAuthentication.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return StartAuthentication;
++        })();
++
++        Events.AuthModesReceived = (function() {
++
++            function AuthModesReceived(properties) {
++                this.authModes = [];
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            AuthModesReceived.prototype.authModes = $util.emptyArray;
++
++            AuthModesReceived.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.AuthModesReceived)
++                    return object;
++                let message = new $root.gdm.Events.AuthModesReceived();
++                if (object.authModes) {
++                    if (!Array.isArray(object.authModes))
++                        throw TypeError(".gdm.Events.AuthModesReceived.authModes: array expected");
++                    message.authModes = [];
++                    for (let i = 0; i < object.authModes.length; ++i) {
++                        if (typeof object.authModes[i] !== "object")
++                            throw TypeError(".gdm.Events.AuthModesReceived.authModes: object expected");
++                        message.authModes[i] = $root.authd.GAMResponse.AuthenticationMode.fromObject(object.authModes[i]);
++                    }
++                }
++                return message;
++            };
++
++            AuthModesReceived.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.arrays || options.defaults)
++                    object.authModes = [];
++                if (message.authModes && message.authModes.length) {
++                    object.authModes = [];
++                    for (let j = 0; j < message.authModes.length; ++j)
++                        object.authModes[j] = $root.authd.GAMResponse.AuthenticationMode.toObject(message.authModes[j], options);
++                }
++                return object;
++            };
++
++            AuthModesReceived.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return AuthModesReceived;
++        })();
++
++        Events.AuthModeSelected = (function() {
++
++            function AuthModeSelected(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            AuthModeSelected.prototype.authModeId = "";
++
++            AuthModeSelected.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.AuthModeSelected)
++                    return object;
++                let message = new $root.gdm.Events.AuthModeSelected();
++                if (object.authModeId != null)
++                    message.authModeId = String(object.authModeId);
++                return message;
++            };
++
++            AuthModeSelected.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.authModeId = "";
++                if (message.authModeId != null && message.hasOwnProperty("authModeId"))
++                    object.authModeId = message.authModeId;
++                return object;
++            };
++
++            AuthModeSelected.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return AuthModeSelected;
++        })();
++
++        Events.AuthEvent = (function() {
++
++            function AuthEvent(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            AuthEvent.prototype.response = null;
++
++            AuthEvent.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.AuthEvent)
++                    return object;
++                let message = new $root.gdm.Events.AuthEvent();
++                if (object.response != null) {
++                    if (typeof object.response !== "object")
++                        throw TypeError(".gdm.Events.AuthEvent.response: object expected");
++                    message.response = $root.authd.IAResponse.fromObject(object.response);
++                }
++                return message;
++            };
++
++            AuthEvent.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.response = null;
++                if (message.response != null && message.hasOwnProperty("response"))
++                    object.response = $root.authd.IAResponse.toObject(message.response, options);
++                return object;
++            };
++
++            AuthEvent.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return AuthEvent;
++        })();
++
++        Events.ReselectAuthMode = (function() {
++
++            function ReselectAuthMode(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            ReselectAuthMode.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.ReselectAuthMode)
++                    return object;
++                return new $root.gdm.Events.ReselectAuthMode();
++            };
++
++            ReselectAuthMode.toObject = function toObject() {
++                return {};
++            };
++
++            ReselectAuthMode.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return ReselectAuthMode;
++        })();
++
++        Events.IsAuthenticatedRequested = (function() {
++
++            function IsAuthenticatedRequested(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            IsAuthenticatedRequested.prototype.authenticationData = null;
++
++            IsAuthenticatedRequested.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.IsAuthenticatedRequested)
++                    return object;
++                let message = new $root.gdm.Events.IsAuthenticatedRequested();
++                if (object.authenticationData != null) {
++                    if (typeof object.authenticationData !== "object")
++                        throw TypeError(".gdm.Events.IsAuthenticatedRequested.authenticationData: object expected");
++                    message.authenticationData = $root.authd.IARequest.AuthenticationData.fromObject(object.authenticationData);
++                }
++                return message;
++            };
++
++            IsAuthenticatedRequested.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.authenticationData = null;
++                if (message.authenticationData != null && message.hasOwnProperty("authenticationData"))
++                    object.authenticationData = $root.authd.IARequest.AuthenticationData.toObject(message.authenticationData, options);
++                return object;
++            };
++
++            IsAuthenticatedRequested.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return IsAuthenticatedRequested;
++        })();
++
++        Events.IsAuthenticatedCancelled = (function() {
++
++            function IsAuthenticatedCancelled(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            IsAuthenticatedCancelled.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.IsAuthenticatedCancelled)
++                    return object;
++                return new $root.gdm.Events.IsAuthenticatedCancelled();
++            };
++
++            IsAuthenticatedCancelled.toObject = function toObject() {
++                return {};
++            };
++
++            IsAuthenticatedCancelled.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return IsAuthenticatedCancelled;
++        })();
++
++        Events.StageChanged = (function() {
++
++            function StageChanged(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            StageChanged.prototype.stage = 0;
++
++            StageChanged.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.StageChanged)
++                    return object;
++                let message = new $root.gdm.Events.StageChanged();
++                switch (object.stage) {
++                default:
++                    if (typeof object.stage === "number") {
++                        message.stage = object.stage;
++                        break;
++                    }
++                    break;
++                case "userSelection":
++                case 0:
++                    message.stage = 0;
++                    break;
++                case "brokerSelection":
++                case 1:
++                    message.stage = 1;
++                    break;
++                case "authModeSelection":
++                case 2:
++                    message.stage = 2;
++                    break;
++                case "challenge":
++                case 3:
++                    message.stage = 3;
++                    break;
++                }
++                return message;
++            };
++
++            StageChanged.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.stage = options.enums === String ? "userSelection" : 0;
++                if (message.stage != null && message.hasOwnProperty("stage"))
++                    object.stage = options.enums === String ? $root.pam.Stage[message.stage] === undefined ? message.stage : $root.pam.Stage[message.stage] : message.stage;
++                return object;
++            };
++
++            StageChanged.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return StageChanged;
++        })();
++
++        Events.UiLayoutReceived = (function() {
++
++            function UiLayoutReceived(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            UiLayoutReceived.prototype.uiLayout = null;
++
++            UiLayoutReceived.fromObject = function fromObject(object) {
++                if (object instanceof $root.gdm.Events.UiLayoutReceived)
++                    return object;
++                let message = new $root.gdm.Events.UiLayoutReceived();
++                if (object.uiLayout != null) {
++                    if (typeof object.uiLayout !== "object")
++                        throw TypeError(".gdm.Events.UiLayoutReceived.uiLayout: object expected");
++                    message.uiLayout = $root.authd.UILayout.fromObject(object.uiLayout);
++                }
++                return message;
++            };
++
++            UiLayoutReceived.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults)
++                    object.uiLayout = null;
++                if (message.uiLayout != null && message.hasOwnProperty("uiLayout"))
++                    object.uiLayout = $root.authd.UILayout.toObject(message.uiLayout, options);
++                return object;
++            };
++
++            UiLayoutReceived.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return UiLayoutReceived;
++        })();
++
++        return Events;
++    })();
++
++    gdm.EventData = (function() {
++
++        function EventData(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        EventData.prototype.type = 0;
++        EventData.prototype.brokersReceived = null;
++        EventData.prototype.brokerSelected = null;
++        EventData.prototype.authModesReceived = null;
++        EventData.prototype.authModeSelected = null;
++        EventData.prototype.isAuthenticatedRequested = null;
++        EventData.prototype.stageChanged = null;
++        EventData.prototype.uiLayoutReceived = null;
++        EventData.prototype.authEvent = null;
++        EventData.prototype.reselectAuthMode = null;
++        EventData.prototype.startAuthentication = null;
++        EventData.prototype.userSelected = null;
++        EventData.prototype.isAuthenticatedCancelled = null;
++
++        let $oneOfFields;
++
++        Object.defineProperty(EventData.prototype, "data", {
++            get: $util.oneOfGetter($oneOfFields = ["brokersReceived", "brokerSelected", "authModesReceived", "authModeSelected", "isAuthenticatedRequested", "stageChanged", "uiLayoutReceived", "authEvent", "reselectAuthMode", "startAuthentication", "userSelected", "isAuthenticatedCancelled"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        EventData.fromObject = function fromObject(object) {
++            if (object instanceof $root.gdm.EventData)
++                return object;
++            let message = new $root.gdm.EventData();
++            switch (object.type) {
++            default:
++                if (typeof object.type === "number") {
++                    message.type = object.type;
++                    break;
++                }
++                break;
++            case "unknownEvent":
++            case 0:
++                message.type = 0;
++                break;
++            case "userSelected":
++            case 1:
++                message.type = 1;
++                break;
++            case "brokersReceived":
++            case 2:
++                message.type = 2;
++                break;
++            case "brokerSelected":
++            case 3:
++                message.type = 3;
++                break;
++            case "authModesReceived":
++            case 4:
++                message.type = 4;
++                break;
++            case "authModeSelected":
++            case 5:
++                message.type = 5;
++                break;
++            case "reselectAuthMode":
++            case 6:
++                message.type = 6;
++                break;
++            case "authEvent":
++            case 7:
++                message.type = 7;
++                break;
++            case "uiLayoutReceived":
++            case 8:
++                message.type = 8;
++                break;
++            case "startAuthentication":
++            case 9:
++                message.type = 9;
++                break;
++            case "isAuthenticatedRequested":
++            case 10:
++                message.type = 10;
++                break;
++            case "isAuthenticatedCancelled":
++            case 11:
++                message.type = 11;
++                break;
++            case "stageChanged":
++            case 12:
++                message.type = 12;
++                break;
++            }
++            if (object.brokersReceived != null) {
++                if (typeof object.brokersReceived !== "object")
++                    throw TypeError(".gdm.EventData.brokersReceived: object expected");
++                message.brokersReceived = $root.gdm.Events.BrokersReceived.fromObject(object.brokersReceived);
++            }
++            if (object.brokerSelected != null) {
++                if (typeof object.brokerSelected !== "object")
++                    throw TypeError(".gdm.EventData.brokerSelected: object expected");
++                message.brokerSelected = $root.gdm.Events.BrokerSelected.fromObject(object.brokerSelected);
++            }
++            if (object.authModesReceived != null) {
++                if (typeof object.authModesReceived !== "object")
++                    throw TypeError(".gdm.EventData.authModesReceived: object expected");
++                message.authModesReceived = $root.gdm.Events.AuthModesReceived.fromObject(object.authModesReceived);
++            }
++            if (object.authModeSelected != null) {
++                if (typeof object.authModeSelected !== "object")
++                    throw TypeError(".gdm.EventData.authModeSelected: object expected");
++                message.authModeSelected = $root.gdm.Events.AuthModeSelected.fromObject(object.authModeSelected);
++            }
++            if (object.isAuthenticatedRequested != null) {
++                if (typeof object.isAuthenticatedRequested !== "object")
++                    throw TypeError(".gdm.EventData.isAuthenticatedRequested: object expected");
++                message.isAuthenticatedRequested = $root.gdm.Events.IsAuthenticatedRequested.fromObject(object.isAuthenticatedRequested);
++            }
++            if (object.stageChanged != null) {
++                if (typeof object.stageChanged !== "object")
++                    throw TypeError(".gdm.EventData.stageChanged: object expected");
++                message.stageChanged = $root.gdm.Events.StageChanged.fromObject(object.stageChanged);
++            }
++            if (object.uiLayoutReceived != null) {
++                if (typeof object.uiLayoutReceived !== "object")
++                    throw TypeError(".gdm.EventData.uiLayoutReceived: object expected");
++                message.uiLayoutReceived = $root.gdm.Events.UiLayoutReceived.fromObject(object.uiLayoutReceived);
++            }
++            if (object.authEvent != null) {
++                if (typeof object.authEvent !== "object")
++                    throw TypeError(".gdm.EventData.authEvent: object expected");
++                message.authEvent = $root.gdm.Events.AuthEvent.fromObject(object.authEvent);
++            }
++            if (object.reselectAuthMode != null) {
++                if (typeof object.reselectAuthMode !== "object")
++                    throw TypeError(".gdm.EventData.reselectAuthMode: object expected");
++                message.reselectAuthMode = $root.gdm.Events.ReselectAuthMode.fromObject(object.reselectAuthMode);
++            }
++            if (object.startAuthentication != null) {
++                if (typeof object.startAuthentication !== "object")
++                    throw TypeError(".gdm.EventData.startAuthentication: object expected");
++                message.startAuthentication = $root.gdm.Events.StartAuthentication.fromObject(object.startAuthentication);
++            }
++            if (object.userSelected != null) {
++                if (typeof object.userSelected !== "object")
++                    throw TypeError(".gdm.EventData.userSelected: object expected");
++                message.userSelected = $root.gdm.Events.UserSelected.fromObject(object.userSelected);
++            }
++            if (object.isAuthenticatedCancelled != null) {
++                if (typeof object.isAuthenticatedCancelled !== "object")
++                    throw TypeError(".gdm.EventData.isAuthenticatedCancelled: object expected");
++                message.isAuthenticatedCancelled = $root.gdm.Events.IsAuthenticatedCancelled.fromObject(object.isAuthenticatedCancelled);
++            }
++            return message;
++        };
++
++        EventData.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.type = options.enums === String ? "unknownEvent" : 0;
++            if (message.type != null && message.hasOwnProperty("type"))
++                object.type = options.enums === String ? $root.gdm.EventType[message.type] === undefined ? message.type : $root.gdm.EventType[message.type] : message.type;
++            if (message.brokersReceived != null && message.hasOwnProperty("brokersReceived")) {
++                object.brokersReceived = $root.gdm.Events.BrokersReceived.toObject(message.brokersReceived, options);
++                if (options.oneofs)
++                    object.data = "brokersReceived";
++            }
++            if (message.brokerSelected != null && message.hasOwnProperty("brokerSelected")) {
++                object.brokerSelected = $root.gdm.Events.BrokerSelected.toObject(message.brokerSelected, options);
++                if (options.oneofs)
++                    object.data = "brokerSelected";
++            }
++            if (message.authModesReceived != null && message.hasOwnProperty("authModesReceived")) {
++                object.authModesReceived = $root.gdm.Events.AuthModesReceived.toObject(message.authModesReceived, options);
++                if (options.oneofs)
++                    object.data = "authModesReceived";
++            }
++            if (message.authModeSelected != null && message.hasOwnProperty("authModeSelected")) {
++                object.authModeSelected = $root.gdm.Events.AuthModeSelected.toObject(message.authModeSelected, options);
++                if (options.oneofs)
++                    object.data = "authModeSelected";
++            }
++            if (message.isAuthenticatedRequested != null && message.hasOwnProperty("isAuthenticatedRequested")) {
++                object.isAuthenticatedRequested = $root.gdm.Events.IsAuthenticatedRequested.toObject(message.isAuthenticatedRequested, options);
++                if (options.oneofs)
++                    object.data = "isAuthenticatedRequested";
++            }
++            if (message.stageChanged != null && message.hasOwnProperty("stageChanged")) {
++                object.stageChanged = $root.gdm.Events.StageChanged.toObject(message.stageChanged, options);
++                if (options.oneofs)
++                    object.data = "stageChanged";
++            }
++            if (message.uiLayoutReceived != null && message.hasOwnProperty("uiLayoutReceived")) {
++                object.uiLayoutReceived = $root.gdm.Events.UiLayoutReceived.toObject(message.uiLayoutReceived, options);
++                if (options.oneofs)
++                    object.data = "uiLayoutReceived";
++            }
++            if (message.authEvent != null && message.hasOwnProperty("authEvent")) {
++                object.authEvent = $root.gdm.Events.AuthEvent.toObject(message.authEvent, options);
++                if (options.oneofs)
++                    object.data = "authEvent";
++            }
++            if (message.reselectAuthMode != null && message.hasOwnProperty("reselectAuthMode")) {
++                object.reselectAuthMode = $root.gdm.Events.ReselectAuthMode.toObject(message.reselectAuthMode, options);
++                if (options.oneofs)
++                    object.data = "reselectAuthMode";
++            }
++            if (message.startAuthentication != null && message.hasOwnProperty("startAuthentication")) {
++                object.startAuthentication = $root.gdm.Events.StartAuthentication.toObject(message.startAuthentication, options);
++                if (options.oneofs)
++                    object.data = "startAuthentication";
++            }
++            if (message.userSelected != null && message.hasOwnProperty("userSelected")) {
++                object.userSelected = $root.gdm.Events.UserSelected.toObject(message.userSelected, options);
++                if (options.oneofs)
++                    object.data = "userSelected";
++            }
++            if (message.isAuthenticatedCancelled != null && message.hasOwnProperty("isAuthenticatedCancelled")) {
++                object.isAuthenticatedCancelled = $root.gdm.Events.IsAuthenticatedCancelled.toObject(message.isAuthenticatedCancelled, options);
++                if (options.oneofs)
++                    object.data = "isAuthenticatedCancelled";
++            }
++            return object;
++        };
++
++        EventData.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return EventData;
++    })();
++
++    return gdm;
++})();
++
++const authd = $root.authd = (() => {
++
++    const authd = {};
++
++    authd.Empty = (function() {
++
++        function Empty(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        Empty.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.Empty)
++                return object;
++            return new $root.authd.Empty();
++        };
++
++        Empty.toObject = function toObject() {
++            return {};
++        };
++
++        Empty.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return Empty;
++    })();
++
++    authd.GPBRequest = (function() {
++
++        function GPBRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GPBRequest.prototype.username = "";
++
++        GPBRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GPBRequest)
++                return object;
++            let message = new $root.authd.GPBRequest();
++            if (object.username != null)
++                message.username = String(object.username);
++            return message;
++        };
++
++        GPBRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.username = "";
++            if (message.username != null && message.hasOwnProperty("username"))
++                object.username = message.username;
++            return object;
++        };
++
++        GPBRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GPBRequest;
++    })();
++
++    authd.GPBResponse = (function() {
++
++        function GPBResponse(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GPBResponse.prototype.previousBroker = "";
++
++        GPBResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GPBResponse)
++                return object;
++            let message = new $root.authd.GPBResponse();
++            if (object.previousBroker != null)
++                message.previousBroker = String(object.previousBroker);
++            return message;
++        };
++
++        GPBResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.previousBroker = "";
++            if (message.previousBroker != null && message.hasOwnProperty("previousBroker"))
++                object.previousBroker = message.previousBroker;
++            return object;
++        };
++
++        GPBResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GPBResponse;
++    })();
++
++    authd.ABResponse = (function() {
++
++        function ABResponse(properties) {
++            this.brokersInfos = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        ABResponse.prototype.brokersInfos = $util.emptyArray;
++
++        ABResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.ABResponse)
++                return object;
++            let message = new $root.authd.ABResponse();
++            if (object.brokersInfos) {
++                if (!Array.isArray(object.brokersInfos))
++                    throw TypeError(".authd.ABResponse.brokersInfos: array expected");
++                message.brokersInfos = [];
++                for (let i = 0; i < object.brokersInfos.length; ++i) {
++                    if (typeof object.brokersInfos[i] !== "object")
++                        throw TypeError(".authd.ABResponse.brokersInfos: object expected");
++                    message.brokersInfos[i] = $root.authd.ABResponse.BrokerInfo.fromObject(object.brokersInfos[i]);
++                }
++            }
++            return message;
++        };
++
++        ABResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.brokersInfos = [];
++            if (message.brokersInfos && message.brokersInfos.length) {
++                object.brokersInfos = [];
++                for (let j = 0; j < message.brokersInfos.length; ++j)
++                    object.brokersInfos[j] = $root.authd.ABResponse.BrokerInfo.toObject(message.brokersInfos[j], options);
++            }
++            return object;
++        };
++
++        ABResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        ABResponse.BrokerInfo = (function() {
++
++            function BrokerInfo(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            BrokerInfo.prototype.id = "";
++            BrokerInfo.prototype.name = "";
++            BrokerInfo.prototype.brandIcon = null;
++
++            let $oneOfFields;
++
++            Object.defineProperty(BrokerInfo.prototype, "_brandIcon", {
++                get: $util.oneOfGetter($oneOfFields = ["brandIcon"]),
++                set: $util.oneOfSetter($oneOfFields)
++            });
++
++            BrokerInfo.fromObject = function fromObject(object) {
++                if (object instanceof $root.authd.ABResponse.BrokerInfo)
++                    return object;
++                let message = new $root.authd.ABResponse.BrokerInfo();
++                if (object.id != null)
++                    message.id = String(object.id);
++                if (object.name != null)
++                    message.name = String(object.name);
++                if (object.brandIcon != null)
++                    message.brandIcon = String(object.brandIcon);
++                return message;
++            };
++
++            BrokerInfo.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults) {
++                    object.id = "";
++                    object.name = "";
++                }
++                if (message.id != null && message.hasOwnProperty("id"))
++                    object.id = message.id;
++                if (message.name != null && message.hasOwnProperty("name"))
++                    object.name = message.name;
++                if (message.brandIcon != null && message.hasOwnProperty("brandIcon")) {
++                    object.brandIcon = message.brandIcon;
++                    if (options.oneofs)
++                        object._brandIcon = "brandIcon";
++                }
++                return object;
++            };
++
++            BrokerInfo.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return BrokerInfo;
++        })();
++
++        return ABResponse;
++    })();
++
++    authd.StringResponse = (function() {
++
++        function StringResponse(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        StringResponse.prototype.msg = "";
++
++        StringResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.StringResponse)
++                return object;
++            let message = new $root.authd.StringResponse();
++            if (object.msg != null)
++                message.msg = String(object.msg);
++            return message;
++        };
++
++        StringResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.msg = "";
++            if (message.msg != null && message.hasOwnProperty("msg"))
++                object.msg = message.msg;
++            return object;
++        };
++
++        StringResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return StringResponse;
++    })();
++
++    authd.SessionMode = (function() {
++        const valuesById = {}, values = Object.create(valuesById);
++        values[valuesById[0] = "UNDEFINED"] = 0;
++        values[valuesById[1] = "LOGIN"] = 1;
++        values[valuesById[2] = "CHANGE_PASSWORD"] = 2;
++        return values;
++    })();
++
++    authd.SBRequest = (function() {
++
++        function SBRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        SBRequest.prototype.brokerId = "";
++        SBRequest.prototype.username = "";
++        SBRequest.prototype.lang = "";
++        SBRequest.prototype.mode = 0;
++
++        SBRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.SBRequest)
++                return object;
++            let message = new $root.authd.SBRequest();
++            if (object.brokerId != null)
++                message.brokerId = String(object.brokerId);
++            if (object.username != null)
++                message.username = String(object.username);
++            if (object.lang != null)
++                message.lang = String(object.lang);
++            switch (object.mode) {
++            default:
++                if (typeof object.mode === "number") {
++                    message.mode = object.mode;
++                    break;
++                }
++                break;
++            case "UNDEFINED":
++            case 0:
++                message.mode = 0;
++                break;
++            case "LOGIN":
++            case 1:
++                message.mode = 1;
++                break;
++            case "CHANGE_PASSWORD":
++            case 2:
++                message.mode = 2;
++                break;
++            }
++            return message;
++        };
++
++        SBRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.brokerId = "";
++                object.username = "";
++                object.lang = "";
++                object.mode = options.enums === String ? "UNDEFINED" : 0;
++            }
++            if (message.brokerId != null && message.hasOwnProperty("brokerId"))
++                object.brokerId = message.brokerId;
++            if (message.username != null && message.hasOwnProperty("username"))
++                object.username = message.username;
++            if (message.lang != null && message.hasOwnProperty("lang"))
++                object.lang = message.lang;
++            if (message.mode != null && message.hasOwnProperty("mode"))
++                object.mode = options.enums === String ? $root.authd.SessionMode[message.mode] === undefined ? message.mode : $root.authd.SessionMode[message.mode] : message.mode;
++            return object;
++        };
++
++        SBRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return SBRequest;
++    })();
++
++    authd.SBResponse = (function() {
++
++        function SBResponse(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        SBResponse.prototype.sessionId = "";
++        SBResponse.prototype.encryptionKey = "";
++
++        SBResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.SBResponse)
++                return object;
++            let message = new $root.authd.SBResponse();
++            if (object.sessionId != null)
++                message.sessionId = String(object.sessionId);
++            if (object.encryptionKey != null)
++                message.encryptionKey = String(object.encryptionKey);
++            return message;
++        };
++
++        SBResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.sessionId = "";
++                object.encryptionKey = "";
++            }
++            if (message.sessionId != null && message.hasOwnProperty("sessionId"))
++                object.sessionId = message.sessionId;
++            if (message.encryptionKey != null && message.hasOwnProperty("encryptionKey"))
++                object.encryptionKey = message.encryptionKey;
++            return object;
++        };
++
++        SBResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return SBResponse;
++    })();
++
++    authd.GAMRequest = (function() {
++
++        function GAMRequest(properties) {
++            this.supportedUiLayouts = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GAMRequest.prototype.sessionId = "";
++        GAMRequest.prototype.supportedUiLayouts = $util.emptyArray;
++
++        GAMRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GAMRequest)
++                return object;
++            let message = new $root.authd.GAMRequest();
++            if (object.sessionId != null)
++                message.sessionId = String(object.sessionId);
++            if (object.supportedUiLayouts) {
++                if (!Array.isArray(object.supportedUiLayouts))
++                    throw TypeError(".authd.GAMRequest.supportedUiLayouts: array expected");
++                message.supportedUiLayouts = [];
++                for (let i = 0; i < object.supportedUiLayouts.length; ++i) {
++                    if (typeof object.supportedUiLayouts[i] !== "object")
++                        throw TypeError(".authd.GAMRequest.supportedUiLayouts: object expected");
++                    message.supportedUiLayouts[i] = $root.authd.UILayout.fromObject(object.supportedUiLayouts[i]);
++                }
++            }
++            return message;
++        };
++
++        GAMRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.supportedUiLayouts = [];
++            if (options.defaults)
++                object.sessionId = "";
++            if (message.sessionId != null && message.hasOwnProperty("sessionId"))
++                object.sessionId = message.sessionId;
++            if (message.supportedUiLayouts && message.supportedUiLayouts.length) {
++                object.supportedUiLayouts = [];
++                for (let j = 0; j < message.supportedUiLayouts.length; ++j)
++                    object.supportedUiLayouts[j] = $root.authd.UILayout.toObject(message.supportedUiLayouts[j], options);
++            }
++            return object;
++        };
++
++        GAMRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GAMRequest;
++    })();
++
++    authd.UILayout = (function() {
++
++        function UILayout(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        UILayout.prototype.type = "";
++        UILayout.prototype.label = null;
++        UILayout.prototype.button = null;
++        UILayout.prototype.wait = null;
++        UILayout.prototype.entry = null;
++        UILayout.prototype.content = null;
++        UILayout.prototype.code = null;
++        UILayout.prototype.rendersQrcode = null;
++
++        let $oneOfFields;
++
++        Object.defineProperty(UILayout.prototype, "_label", {
++            get: $util.oneOfGetter($oneOfFields = ["label"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_button", {
++            get: $util.oneOfGetter($oneOfFields = ["button"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_wait", {
++            get: $util.oneOfGetter($oneOfFields = ["wait"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_entry", {
++            get: $util.oneOfGetter($oneOfFields = ["entry"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_content", {
++            get: $util.oneOfGetter($oneOfFields = ["content"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_code", {
++            get: $util.oneOfGetter($oneOfFields = ["code"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        Object.defineProperty(UILayout.prototype, "_rendersQrcode", {
++            get: $util.oneOfGetter($oneOfFields = ["rendersQrcode"]),
++            set: $util.oneOfSetter($oneOfFields)
++        });
++
++        UILayout.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.UILayout)
++                return object;
++            let message = new $root.authd.UILayout();
++            if (object.type != null)
++                message.type = String(object.type);
++            if (object.label != null)
++                message.label = String(object.label);
++            if (object.button != null)
++                message.button = String(object.button);
++            if (object.wait != null)
++                message.wait = String(object.wait);
++            if (object.entry != null)
++                message.entry = String(object.entry);
++            if (object.content != null)
++                message.content = String(object.content);
++            if (object.code != null)
++                message.code = String(object.code);
++            if (object.rendersQrcode != null)
++                message.rendersQrcode = Boolean(object.rendersQrcode);
++            return message;
++        };
++
++        UILayout.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.type = "";
++            if (message.type != null && message.hasOwnProperty("type"))
++                object.type = message.type;
++            if (message.label != null && message.hasOwnProperty("label")) {
++                object.label = message.label;
++                if (options.oneofs)
++                    object._label = "label";
++            }
++            if (message.button != null && message.hasOwnProperty("button")) {
++                object.button = message.button;
++                if (options.oneofs)
++                    object._button = "button";
++            }
++            if (message.wait != null && message.hasOwnProperty("wait")) {
++                object.wait = message.wait;
++                if (options.oneofs)
++                    object._wait = "wait";
++            }
++            if (message.entry != null && message.hasOwnProperty("entry")) {
++                object.entry = message.entry;
++                if (options.oneofs)
++                    object._entry = "entry";
++            }
++            if (message.content != null && message.hasOwnProperty("content")) {
++                object.content = message.content;
++                if (options.oneofs)
++                    object._content = "content";
++            }
++            if (message.code != null && message.hasOwnProperty("code")) {
++                object.code = message.code;
++                if (options.oneofs)
++                    object._code = "code";
++            }
++            if (message.rendersQrcode != null && message.hasOwnProperty("rendersQrcode")) {
++                object.rendersQrcode = message.rendersQrcode;
++                if (options.oneofs)
++                    object._rendersQrcode = "rendersQrcode";
++            }
++            return object;
++        };
++
++        UILayout.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return UILayout;
++    })();
++
++    authd.GAMResponse = (function() {
++
++        function GAMResponse(properties) {
++            this.authenticationModes = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GAMResponse.prototype.authenticationModes = $util.emptyArray;
++
++        GAMResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GAMResponse)
++                return object;
++            let message = new $root.authd.GAMResponse();
++            if (object.authenticationModes) {
++                if (!Array.isArray(object.authenticationModes))
++                    throw TypeError(".authd.GAMResponse.authenticationModes: array expected");
++                message.authenticationModes = [];
++                for (let i = 0; i < object.authenticationModes.length; ++i) {
++                    if (typeof object.authenticationModes[i] !== "object")
++                        throw TypeError(".authd.GAMResponse.authenticationModes: object expected");
++                    message.authenticationModes[i] = $root.authd.GAMResponse.AuthenticationMode.fromObject(object.authenticationModes[i]);
++                }
++            }
++            return message;
++        };
++
++        GAMResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.authenticationModes = [];
++            if (message.authenticationModes && message.authenticationModes.length) {
++                object.authenticationModes = [];
++                for (let j = 0; j < message.authenticationModes.length; ++j)
++                    object.authenticationModes[j] = $root.authd.GAMResponse.AuthenticationMode.toObject(message.authenticationModes[j], options);
++            }
++            return object;
++        };
++
++        GAMResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        GAMResponse.AuthenticationMode = (function() {
++
++            function AuthenticationMode(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            AuthenticationMode.prototype.id = "";
++            AuthenticationMode.prototype.label = "";
++
++            AuthenticationMode.fromObject = function fromObject(object) {
++                if (object instanceof $root.authd.GAMResponse.AuthenticationMode)
++                    return object;
++                let message = new $root.authd.GAMResponse.AuthenticationMode();
++                if (object.id != null)
++                    message.id = String(object.id);
++                if (object.label != null)
++                    message.label = String(object.label);
++                return message;
++            };
++
++            AuthenticationMode.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (options.defaults) {
++                    object.id = "";
++                    object.label = "";
++                }
++                if (message.id != null && message.hasOwnProperty("id"))
++                    object.id = message.id;
++                if (message.label != null && message.hasOwnProperty("label"))
++                    object.label = message.label;
++                return object;
++            };
++
++            AuthenticationMode.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return AuthenticationMode;
++        })();
++
++        return GAMResponse;
++    })();
++
++    authd.SAMRequest = (function() {
++
++        function SAMRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        SAMRequest.prototype.sessionId = "";
++        SAMRequest.prototype.authenticationModeId = "";
++
++        SAMRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.SAMRequest)
++                return object;
++            let message = new $root.authd.SAMRequest();
++            if (object.sessionId != null)
++                message.sessionId = String(object.sessionId);
++            if (object.authenticationModeId != null)
++                message.authenticationModeId = String(object.authenticationModeId);
++            return message;
++        };
++
++        SAMRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.sessionId = "";
++                object.authenticationModeId = "";
++            }
++            if (message.sessionId != null && message.hasOwnProperty("sessionId"))
++                object.sessionId = message.sessionId;
++            if (message.authenticationModeId != null && message.hasOwnProperty("authenticationModeId"))
++                object.authenticationModeId = message.authenticationModeId;
++            return object;
++        };
++
++        SAMRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return SAMRequest;
++    })();
++
++    authd.SAMResponse = (function() {
++
++        function SAMResponse(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        SAMResponse.prototype.uiLayoutInfo = null;
++
++        SAMResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.SAMResponse)
++                return object;
++            let message = new $root.authd.SAMResponse();
++            if (object.uiLayoutInfo != null) {
++                if (typeof object.uiLayoutInfo !== "object")
++                    throw TypeError(".authd.SAMResponse.uiLayoutInfo: object expected");
++                message.uiLayoutInfo = $root.authd.UILayout.fromObject(object.uiLayoutInfo);
++            }
++            return message;
++        };
++
++        SAMResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.uiLayoutInfo = null;
++            if (message.uiLayoutInfo != null && message.hasOwnProperty("uiLayoutInfo"))
++                object.uiLayoutInfo = $root.authd.UILayout.toObject(message.uiLayoutInfo, options);
++            return object;
++        };
++
++        SAMResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return SAMResponse;
++    })();
++
++    authd.IARequest = (function() {
++
++        function IARequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        IARequest.prototype.sessionId = "";
++        IARequest.prototype.authenticationData = null;
++
++        IARequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.IARequest)
++                return object;
++            let message = new $root.authd.IARequest();
++            if (object.sessionId != null)
++                message.sessionId = String(object.sessionId);
++            if (object.authenticationData != null) {
++                if (typeof object.authenticationData !== "object")
++                    throw TypeError(".authd.IARequest.authenticationData: object expected");
++                message.authenticationData = $root.authd.IARequest.AuthenticationData.fromObject(object.authenticationData);
++            }
++            return message;
++        };
++
++        IARequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.sessionId = "";
++                object.authenticationData = null;
++            }
++            if (message.sessionId != null && message.hasOwnProperty("sessionId"))
++                object.sessionId = message.sessionId;
++            if (message.authenticationData != null && message.hasOwnProperty("authenticationData"))
++                object.authenticationData = $root.authd.IARequest.AuthenticationData.toObject(message.authenticationData, options);
++            return object;
++        };
++
++        IARequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        IARequest.AuthenticationData = (function() {
++
++            function AuthenticationData(properties) {
++                if (properties)
++                    for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                        if (properties[keys[i]] != null)
++                            this[keys[i]] = properties[keys[i]];
++            }
++
++            AuthenticationData.prototype.secret = null;
++            AuthenticationData.prototype.wait = null;
++            AuthenticationData.prototype.skip = null;
++
++            let $oneOfFields;
++
++            Object.defineProperty(AuthenticationData.prototype, "item", {
++                get: $util.oneOfGetter($oneOfFields = ["secret", "wait", "skip"]),
++                set: $util.oneOfSetter($oneOfFields)
++            });
++
++            AuthenticationData.fromObject = function fromObject(object) {
++                if (object instanceof $root.authd.IARequest.AuthenticationData)
++                    return object;
++                let message = new $root.authd.IARequest.AuthenticationData();
++                if (object.secret != null)
++                    message.secret = String(object.secret);
++                if (object.wait != null)
++                    message.wait = String(object.wait);
++                if (object.skip != null)
++                    message.skip = String(object.skip);
++                return message;
++            };
++
++            AuthenticationData.toObject = function toObject(message, options) {
++                if (!options)
++                    options = {};
++                let object = {};
++                if (message.secret != null && message.hasOwnProperty("secret")) {
++                    object.secret = message.secret;
++                    if (options.oneofs)
++                        object.item = "secret";
++                }
++                if (message.wait != null && message.hasOwnProperty("wait")) {
++                    object.wait = message.wait;
++                    if (options.oneofs)
++                        object.item = "wait";
++                }
++                if (message.skip != null && message.hasOwnProperty("skip")) {
++                    object.skip = message.skip;
++                    if (options.oneofs)
++                        object.item = "skip";
++                }
++                return object;
++            };
++
++            AuthenticationData.prototype.toJSON = function toJSON() {
++                return this.constructor.toObject(this, minimal.util.toJSONOptions);
++            };
++
++            return AuthenticationData;
++        })();
++
++        return IARequest;
++    })();
++
++    authd.IAResponse = (function() {
++
++        function IAResponse(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        IAResponse.prototype.access = "";
++        IAResponse.prototype.msg = "";
++
++        IAResponse.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.IAResponse)
++                return object;
++            let message = new $root.authd.IAResponse();
++            if (object.access != null)
++                message.access = String(object.access);
++            if (object.msg != null)
++                message.msg = String(object.msg);
++            return message;
++        };
++
++        IAResponse.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.access = "";
++                object.msg = "";
++            }
++            if (message.access != null && message.hasOwnProperty("access"))
++                object.access = message.access;
++            if (message.msg != null && message.hasOwnProperty("msg"))
++                object.msg = message.msg;
++            return object;
++        };
++
++        IAResponse.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return IAResponse;
++    })();
++
++    authd.SDBFURequest = (function() {
++
++        function SDBFURequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        SDBFURequest.prototype.brokerId = "";
++        SDBFURequest.prototype.username = "";
++
++        SDBFURequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.SDBFURequest)
++                return object;
++            let message = new $root.authd.SDBFURequest();
++            if (object.brokerId != null)
++                message.brokerId = String(object.brokerId);
++            if (object.username != null)
++                message.username = String(object.username);
++            return message;
++        };
++
++        SDBFURequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.brokerId = "";
++                object.username = "";
++            }
++            if (message.brokerId != null && message.hasOwnProperty("brokerId"))
++                object.brokerId = message.brokerId;
++            if (message.username != null && message.hasOwnProperty("username"))
++                object.username = message.username;
++            return object;
++        };
++
++        SDBFURequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return SDBFURequest;
++    })();
++
++    authd.ESRequest = (function() {
++
++        function ESRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        ESRequest.prototype.sessionId = "";
++
++        ESRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.ESRequest)
++                return object;
++            let message = new $root.authd.ESRequest();
++            if (object.sessionId != null)
++                message.sessionId = String(object.sessionId);
++            return message;
++        };
++
++        ESRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.sessionId = "";
++            if (message.sessionId != null && message.hasOwnProperty("sessionId"))
++                object.sessionId = message.sessionId;
++            return object;
++        };
++
++        ESRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return ESRequest;
++    })();
++
++    authd.GetPasswdByNameRequest = (function() {
++
++        function GetPasswdByNameRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GetPasswdByNameRequest.prototype.name = "";
++        GetPasswdByNameRequest.prototype.shouldPreCheck = false;
++
++        GetPasswdByNameRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GetPasswdByNameRequest)
++                return object;
++            let message = new $root.authd.GetPasswdByNameRequest();
++            if (object.name != null)
++                message.name = String(object.name);
++            if (object.shouldPreCheck != null)
++                message.shouldPreCheck = Boolean(object.shouldPreCheck);
++            return message;
++        };
++
++        GetPasswdByNameRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.name = "";
++                object.shouldPreCheck = false;
++            }
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            if (message.shouldPreCheck != null && message.hasOwnProperty("shouldPreCheck"))
++                object.shouldPreCheck = message.shouldPreCheck;
++            return object;
++        };
++
++        GetPasswdByNameRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GetPasswdByNameRequest;
++    })();
++
++    authd.GetGroupByNameRequest = (function() {
++
++        function GetGroupByNameRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GetGroupByNameRequest.prototype.name = "";
++
++        GetGroupByNameRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GetGroupByNameRequest)
++                return object;
++            let message = new $root.authd.GetGroupByNameRequest();
++            if (object.name != null)
++                message.name = String(object.name);
++            return message;
++        };
++
++        GetGroupByNameRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.name = "";
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            return object;
++        };
++
++        GetGroupByNameRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GetGroupByNameRequest;
++    })();
++
++    authd.GetShadowByNameRequest = (function() {
++
++        function GetShadowByNameRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GetShadowByNameRequest.prototype.name = "";
++
++        GetShadowByNameRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GetShadowByNameRequest)
++                return object;
++            let message = new $root.authd.GetShadowByNameRequest();
++            if (object.name != null)
++                message.name = String(object.name);
++            return message;
++        };
++
++        GetShadowByNameRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.name = "";
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            return object;
++        };
++
++        GetShadowByNameRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GetShadowByNameRequest;
++    })();
++
++    authd.GetByIDRequest = (function() {
++
++        function GetByIDRequest(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GetByIDRequest.prototype.id = 0;
++
++        GetByIDRequest.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GetByIDRequest)
++                return object;
++            let message = new $root.authd.GetByIDRequest();
++            if (object.id != null)
++                message.id = object.id >>> 0;
++            return message;
++        };
++
++        GetByIDRequest.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults)
++                object.id = 0;
++            if (message.id != null && message.hasOwnProperty("id"))
++                object.id = message.id;
++            return object;
++        };
++
++        GetByIDRequest.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GetByIDRequest;
++    })();
++
++    authd.PasswdEntry = (function() {
++
++        function PasswdEntry(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        PasswdEntry.prototype.name = "";
++        PasswdEntry.prototype.passwd = "";
++        PasswdEntry.prototype.uid = 0;
++        PasswdEntry.prototype.gid = 0;
++        PasswdEntry.prototype.gecos = "";
++        PasswdEntry.prototype.homedir = "";
++        PasswdEntry.prototype.shell = "";
++
++        PasswdEntry.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.PasswdEntry)
++                return object;
++            let message = new $root.authd.PasswdEntry();
++            if (object.name != null)
++                message.name = String(object.name);
++            if (object.passwd != null)
++                message.passwd = String(object.passwd);
++            if (object.uid != null)
++                message.uid = object.uid >>> 0;
++            if (object.gid != null)
++                message.gid = object.gid >>> 0;
++            if (object.gecos != null)
++                message.gecos = String(object.gecos);
++            if (object.homedir != null)
++                message.homedir = String(object.homedir);
++            if (object.shell != null)
++                message.shell = String(object.shell);
++            return message;
++        };
++
++        PasswdEntry.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.name = "";
++                object.passwd = "";
++                object.uid = 0;
++                object.gid = 0;
++                object.gecos = "";
++                object.homedir = "";
++                object.shell = "";
++            }
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            if (message.passwd != null && message.hasOwnProperty("passwd"))
++                object.passwd = message.passwd;
++            if (message.uid != null && message.hasOwnProperty("uid"))
++                object.uid = message.uid;
++            if (message.gid != null && message.hasOwnProperty("gid"))
++                object.gid = message.gid;
++            if (message.gecos != null && message.hasOwnProperty("gecos"))
++                object.gecos = message.gecos;
++            if (message.homedir != null && message.hasOwnProperty("homedir"))
++                object.homedir = message.homedir;
++            if (message.shell != null && message.hasOwnProperty("shell"))
++                object.shell = message.shell;
++            return object;
++        };
++
++        PasswdEntry.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return PasswdEntry;
++    })();
++
++    authd.PasswdEntries = (function() {
++
++        function PasswdEntries(properties) {
++            this.entries = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        PasswdEntries.prototype.entries = $util.emptyArray;
++
++        PasswdEntries.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.PasswdEntries)
++                return object;
++            let message = new $root.authd.PasswdEntries();
++            if (object.entries) {
++                if (!Array.isArray(object.entries))
++                    throw TypeError(".authd.PasswdEntries.entries: array expected");
++                message.entries = [];
++                for (let i = 0; i < object.entries.length; ++i) {
++                    if (typeof object.entries[i] !== "object")
++                        throw TypeError(".authd.PasswdEntries.entries: object expected");
++                    message.entries[i] = $root.authd.PasswdEntry.fromObject(object.entries[i]);
++                }
++            }
++            return message;
++        };
++
++        PasswdEntries.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.entries = [];
++            if (message.entries && message.entries.length) {
++                object.entries = [];
++                for (let j = 0; j < message.entries.length; ++j)
++                    object.entries[j] = $root.authd.PasswdEntry.toObject(message.entries[j], options);
++            }
++            return object;
++        };
++
++        PasswdEntries.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return PasswdEntries;
++    })();
++
++    authd.GroupEntry = (function() {
++
++        function GroupEntry(properties) {
++            this.members = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GroupEntry.prototype.name = "";
++        GroupEntry.prototype.passwd = "";
++        GroupEntry.prototype.gid = 0;
++        GroupEntry.prototype.members = $util.emptyArray;
++
++        GroupEntry.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GroupEntry)
++                return object;
++            let message = new $root.authd.GroupEntry();
++            if (object.name != null)
++                message.name = String(object.name);
++            if (object.passwd != null)
++                message.passwd = String(object.passwd);
++            if (object.gid != null)
++                message.gid = object.gid >>> 0;
++            if (object.members) {
++                if (!Array.isArray(object.members))
++                    throw TypeError(".authd.GroupEntry.members: array expected");
++                message.members = [];
++                for (let i = 0; i < object.members.length; ++i)
++                    message.members[i] = String(object.members[i]);
++            }
++            return message;
++        };
++
++        GroupEntry.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.members = [];
++            if (options.defaults) {
++                object.name = "";
++                object.passwd = "";
++                object.gid = 0;
++            }
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            if (message.passwd != null && message.hasOwnProperty("passwd"))
++                object.passwd = message.passwd;
++            if (message.gid != null && message.hasOwnProperty("gid"))
++                object.gid = message.gid;
++            if (message.members && message.members.length) {
++                object.members = [];
++                for (let j = 0; j < message.members.length; ++j)
++                    object.members[j] = message.members[j];
++            }
++            return object;
++        };
++
++        GroupEntry.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GroupEntry;
++    })();
++
++    authd.GroupEntries = (function() {
++
++        function GroupEntries(properties) {
++            this.entries = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        GroupEntries.prototype.entries = $util.emptyArray;
++
++        GroupEntries.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.GroupEntries)
++                return object;
++            let message = new $root.authd.GroupEntries();
++            if (object.entries) {
++                if (!Array.isArray(object.entries))
++                    throw TypeError(".authd.GroupEntries.entries: array expected");
++                message.entries = [];
++                for (let i = 0; i < object.entries.length; ++i) {
++                    if (typeof object.entries[i] !== "object")
++                        throw TypeError(".authd.GroupEntries.entries: object expected");
++                    message.entries[i] = $root.authd.GroupEntry.fromObject(object.entries[i]);
++                }
++            }
++            return message;
++        };
++
++        GroupEntries.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.entries = [];
++            if (message.entries && message.entries.length) {
++                object.entries = [];
++                for (let j = 0; j < message.entries.length; ++j)
++                    object.entries[j] = $root.authd.GroupEntry.toObject(message.entries[j], options);
++            }
++            return object;
++        };
++
++        GroupEntries.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return GroupEntries;
++    })();
++
++    authd.ShadowEntry = (function() {
++
++        function ShadowEntry(properties) {
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        ShadowEntry.prototype.name = "";
++        ShadowEntry.prototype.passwd = "";
++        ShadowEntry.prototype.lastChange = 0;
++        ShadowEntry.prototype.changeMinDays = 0;
++        ShadowEntry.prototype.changeMaxDays = 0;
++        ShadowEntry.prototype.changeWarnDays = 0;
++        ShadowEntry.prototype.changeInactiveDays = 0;
++        ShadowEntry.prototype.expireDate = 0;
++
++        ShadowEntry.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.ShadowEntry)
++                return object;
++            let message = new $root.authd.ShadowEntry();
++            if (object.name != null)
++                message.name = String(object.name);
++            if (object.passwd != null)
++                message.passwd = String(object.passwd);
++            if (object.lastChange != null)
++                message.lastChange = object.lastChange | 0;
++            if (object.changeMinDays != null)
++                message.changeMinDays = object.changeMinDays | 0;
++            if (object.changeMaxDays != null)
++                message.changeMaxDays = object.changeMaxDays | 0;
++            if (object.changeWarnDays != null)
++                message.changeWarnDays = object.changeWarnDays | 0;
++            if (object.changeInactiveDays != null)
++                message.changeInactiveDays = object.changeInactiveDays | 0;
++            if (object.expireDate != null)
++                message.expireDate = object.expireDate | 0;
++            return message;
++        };
++
++        ShadowEntry.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.defaults) {
++                object.name = "";
++                object.passwd = "";
++                object.lastChange = 0;
++                object.changeMinDays = 0;
++                object.changeMaxDays = 0;
++                object.changeWarnDays = 0;
++                object.changeInactiveDays = 0;
++                object.expireDate = 0;
++            }
++            if (message.name != null && message.hasOwnProperty("name"))
++                object.name = message.name;
++            if (message.passwd != null && message.hasOwnProperty("passwd"))
++                object.passwd = message.passwd;
++            if (message.lastChange != null && message.hasOwnProperty("lastChange"))
++                object.lastChange = message.lastChange;
++            if (message.changeMinDays != null && message.hasOwnProperty("changeMinDays"))
++                object.changeMinDays = message.changeMinDays;
++            if (message.changeMaxDays != null && message.hasOwnProperty("changeMaxDays"))
++                object.changeMaxDays = message.changeMaxDays;
++            if (message.changeWarnDays != null && message.hasOwnProperty("changeWarnDays"))
++                object.changeWarnDays = message.changeWarnDays;
++            if (message.changeInactiveDays != null && message.hasOwnProperty("changeInactiveDays"))
++                object.changeInactiveDays = message.changeInactiveDays;
++            if (message.expireDate != null && message.hasOwnProperty("expireDate"))
++                object.expireDate = message.expireDate;
++            return object;
++        };
++
++        ShadowEntry.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return ShadowEntry;
++    })();
++
++    authd.ShadowEntries = (function() {
++
++        function ShadowEntries(properties) {
++            this.entries = [];
++            if (properties)
++                for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
++                    if (properties[keys[i]] != null)
++                        this[keys[i]] = properties[keys[i]];
++        }
++
++        ShadowEntries.prototype.entries = $util.emptyArray;
++
++        ShadowEntries.fromObject = function fromObject(object) {
++            if (object instanceof $root.authd.ShadowEntries)
++                return object;
++            let message = new $root.authd.ShadowEntries();
++            if (object.entries) {
++                if (!Array.isArray(object.entries))
++                    throw TypeError(".authd.ShadowEntries.entries: array expected");
++                message.entries = [];
++                for (let i = 0; i < object.entries.length; ++i) {
++                    if (typeof object.entries[i] !== "object")
++                        throw TypeError(".authd.ShadowEntries.entries: object expected");
++                    message.entries[i] = $root.authd.ShadowEntry.fromObject(object.entries[i]);
++                }
++            }
++            return message;
++        };
++
++        ShadowEntries.toObject = function toObject(message, options) {
++            if (!options)
++                options = {};
++            let object = {};
++            if (options.arrays || options.defaults)
++                object.entries = [];
++            if (message.entries && message.entries.length) {
++                object.entries = [];
++                for (let j = 0; j < message.entries.length; ++j)
++                    object.entries[j] = $root.authd.ShadowEntry.toObject(message.entries[j], options);
++            }
++            return object;
++        };
++
++        ShadowEntries.prototype.toJSON = function toJSON() {
++            return this.constructor.toObject(this, minimal.util.toJSONOptions);
++        };
++
++        return ShadowEntries;
++    })();
++
++    return authd;
++})();
++
++const pam = $root.pam = (() => {
++
++    const pam = {};
++
++    pam.Stage = (function() {
++        const valuesById = {}, values = Object.create(valuesById);
++        values[valuesById[0] = "userSelection"] = 0;
++        values[valuesById[1] = "brokerSelection"] = 1;
++        values[valuesById[2] = "authModeSelection"] = 2;
++        values[valuesById[3] = "challenge"] = 3;
++        return values;
++    })();
++
++    return pam;
++})();
++
++export { authd, $root as default, gdm, pam };
+diff --git a/js/gdm/const.js b/js/gdm/const.js
+index ad854ca..b6ed4cd 100644
+--- a/js/gdm/const.js
++++ b/js/gdm/const.js
+@@ -1,6 +1,9 @@
+ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+ 
+ export const WEB_LOGIN_ROLE_NAME = 'eidp';
++export const PLAIN_TEXT_ROLE_NAME = 'text';
++export const MESSAGE_ROLE_NAME = 'message';
+ export const PASSWORD_ROLE_NAME = 'password';
+ export const SMARTCARD_ROLE_NAME = 'smartcard';
+ export const FINGERPRINT_ROLE_NAME = 'fingerprint';
++export const CHOICE_LIST_ROLE_NAME = 'choice-list';
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 43fe656..0fd61ca 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -4,6 +4,7 @@ import Gio from 'gi://Gio';
+ import GLib from 'gi://GLib';
+ import * as Signals from '../misc/signals.js';
+ 
++import * as Authd from './authd.js';
+ import * as Batch from './batch.js';
+ import * as OVirt from './oVirt.js';
+ import * as Vmware from './vmware.js';
+@@ -25,7 +26,7 @@ Gio._promisify(Gdm.UserVerifierProxy.prototype,
+     'call_begin_verification_for_user');
+ Gio._promisify(Gdm.UserVerifierProxy.prototype, 'call_begin_verification');
+ 
+-export const UNIFIED_AUTH_SERVICE_NAME = 'gdm-unified-auth';
++export const UNIFIED_AUTH_SERVICE_NAME = Authd.SERVICE_NAME;
+ export const PASSWORD_SERVICE_NAME = 'gdm-password';
+ export const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+ export const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+@@ -167,6 +168,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+         this._unifiedAuthServices = new Map();
+         this.addUnifiedAuthService(new UnifiedMechanism.UnifiedMechanismProtocolHandler());
++        this.addUnifiedAuthService(new Authd.AuthdSwitchableHandler());
+ 
+         this._credentialManagers = {};
+ 
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index 684af12..d57b68e 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -15,6 +15,9 @@
+     <file>gdm/unifiedMechanism.js</file>
+     <file>gdm/webLogin.js</file>
+ 
++    <file>gdm/authd.js</file>
++    <file>gdm/authdProtocol.js</file>
++
+     <file>extensions/extension.js</file>
+     <file>extensions/sharedInternals.js</file>
+ 
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 5ec7365..8ca4aac 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -10,6 +10,7 @@ data/X-GNOME-Shell-System.directory.desktop.in
+ data/X-GNOME-Shell-Utilities.directory.desktop.in
+ js/dbusServices/extensions/extensionPrefsDialog.js
+ js/dbusServices/extensions/ui/extension-error-page.ui
++js/gdm/authd.js
+ js/gdm/authPrompt.js
+ js/gdm/loginDialog.js
+ js/gdm/util.js
diff -pruN 49.0-1/debian/patches/ubuntu-authd/authPrompt-Add-support-for-Web-Login.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Add-support-for-Web-Login.patch
--- 49.0-1/debian/patches/ubuntu-authd/authPrompt-Add-support-for-Web-Login.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Add-support-for-Web-Login.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,682 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 14:18:24 -0500
+Subject: authPrompt: Add support for Web Login
+
+SSSD supports Web Login via a url on an external device. It
+advertises this support over the auth-selection json protocol
+we now have limited support for.
+
+This commit extends that support Web Login by way of showing a
+QR code as proposed in the design here:
+
+https://gitlab.gnome.org/Teams/Design/os-mockups/-/blob/master/lock-login/web-login.png.
+---
+ .../gnome-shell-sass/widgets/_login-lock.scss      |  75 +++++-
+ js/gdm/authPrompt.js                               | 125 ++++++++++
+ js/gdm/util.js                                     |  46 ++++
+ js/gdm/webLogin.js                                 | 259 +++++++++++++++++++++
+ js/js-resources.gresource.xml                      |   1 +
+ po/POTFILES.in                                     |   1 +
+ 6 files changed, 506 insertions(+), 1 deletion(-)
+ create mode 100644 js/gdm/webLogin.js
+
+diff --git a/data/theme/gnome-shell-sass/widgets/_login-lock.scss b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+index c7411d0..65e2a29 100644
+--- a/data/theme/gnome-shell-sass/widgets/_login-lock.scss
++++ b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+@@ -38,7 +38,8 @@ $_gdm_dialog_width: 25em;
+   &.cancel-button,
+   &.switch-user-button,
+   &.login-dialog-auth-menu-button,
+-  &.login-dialog-session-list-button {
++  &.login-dialog-session-list-button,
++  &.web-login-intro-button {
+     @extend .icon-button;
+     @extend %system_button;
+ 
+@@ -233,8 +234,80 @@ $_gdm_dialog_width: 25em;
+   }
+ }
+ 
++.login-dialog-prompt-layout {
++  spacing: $base_padding * 2;
++  min-width: 30em;
++}
++
++.login-dialog-prompt-entry {
++  @extend %system_entry;
++}
++
++.web-login-dialog-content-overlay {
++  background-color: transparentize($bg_color, 0.3);
++  border-radius: $modal_radius;
++  margin-bottom: 3em;
++}
++
++.web-login-spinner {
++  color: $osd_fg_color;
++  background-color: transparentize($osd_bg_color, 0.5);
++  border: 5px transparent;
++  border-radius: 50px;
++}
++
++.web-login-title-label {
++  @include fontsize($base_font_size);
++  color: if($variant == 'dark', darken($fg_color,30%), lighten($fg_color,20%));
++}
++
++.web-login-url-label {
++  @include fontsize($base_font_size);
++  @extend %monospace;
++  color: $fg_color;
++  text-align: center;
++}
++
++.web-login-code-title-label {
++  @include fontsize($base_font_size);
++  @include fontsize($base_font_size);
++  color: $fg_color;
++}
++
++.web-login-code-label {
++  @include fontsize($base_font_size);
++  color: $fg_color;
++  font-weight: bold;
++}
++
++.web-login-prompt {
++  padding-top: $base_padding;
++  padding-bottom: $base_padding;
++  padding-left: $base_padding * 4.5;
++  padding-right: $base_padding * 4.5;
++  spacing: 1.75em;
++  border-radius: $base_border_radius * 2;
++}
++
++.web-login-intro-button-label {
++  @include fontsize($base_font_size + 5);
++  color: $fg_color;
++  height: 3em;
++  text-align: center;
++  font-weight: bold;
++}
++
++.web-login-intro-button {
++  @include fontsize($base_font_size);
++  color: $fg_color;
++  height: 3em;
++  text-align: center;
++  border-radius: $base_border_radius * 4;
++}
++
+ // Screen Shield
+ // a.k.a. the lockscreen, uses transparent styles
++
+ .unlock-dialog {
+   background-color: transparent;
+ 
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 3c8e1f6..5053ece 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -14,6 +14,7 @@ import * as MessageTray from '../ui/messageTray.js';
+ import * as Params from '../misc/params.js';
+ import * as ShellEntry from '../ui/shellEntry.js';
+ import * as UserWidget from '../ui/userWidget.js';
++import * as WebLogin from './webLogin.js';
+ import {wiggle} from '../misc/animationUtils.js';
+ 
+ const DEFAULT_BUTTON_WELL_ICON_SIZE = 16;
+@@ -84,6 +85,8 @@ export const AuthPrompt = GObject.registerClass({
+         this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this));
+         this._userVerifier.connect('mechanisms-list-changed', this._onAuthMechanismsListChanged.bind(this));
+         this._userVerifier.connect('foreground-mechanism-changed', this._onForegroundMechanismChanged.bind(this));
++        this._userVerifier.connect('web-login', this._onWebLogin.bind(this));
++        this._userVerifier.connect('web-login-time-out', this._onWebLoginTimeOut.bind(this));
+         this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this));
+         this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
+         this._userVerifier.connect('reset', this._onReset.bind(this));
+@@ -173,6 +176,13 @@ export const AuthPrompt = GObject.registerClass({
+             this.cancelButton.opacity = 0;
+         this._mainBox.add_child(this.cancelButton);
+ 
++        this._webLoginPromptWell = new St.Widget({
++            layout_manager: new Clutter.BinLayout(),
++            x_expand: true,
++            y_align: Clutter.ActorAlign.CENTER,
++        });
++        this._mainBox.add_child(this._webLoginPromptWell);
++
+         this._authList = new AuthList.AuthList();
+         this._authList.set({
+             visible: false,
+@@ -318,6 +328,9 @@ export const AuthPrompt = GObject.registerClass({
+         }
+ 
+         this._capsLockWarningLabel.visible = secret;
++
++        this._webLoginPromptWell.visible = false;
++        this._entry.visible = true;
+     }
+ 
+     _onAskQuestion(verifier, serviceName, question, secret) {
+@@ -364,6 +377,110 @@ export const AuthPrompt = GObject.registerClass({
+         this.emit('mechanisms-changed', serviceName);
+     }
+ 
++    _onWebLogin(userVerifier, serviceName, introMessage, linkMessage, uri, code) {
++        const introAlreadyUp = this._queryingService === serviceName;
++
++        if (this._queryingService)
++            this.clear();
++
++        this._entry.visible = false;
++
++        if (this._preemptiveAnswer)
++            this._preemptiveAnswer = null;
++
++        this._webLoginPromptWell.remove_all_children();
++
++        if (this._spinner)
++            this._spinner.stop();
++
++        if (!introAlreadyUp && introMessage) {
++            this._webLoginIntro = new WebLogin.WebLoginIntro({message: introMessage});
++            this._webLoginIntro.set({
++                x_expand: true,
++                y_align: Clutter.ActorAlign.START,
++            });
++            this._webLoginIntro.connect('clicked', () => {
++                this._queryingService = serviceName;
++
++                if (this._webLoginTimedOut)
++                    this._refreshWebLogin(serviceName);
++                else
++                    this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code);
++            });
++            this._webLoginPromptWell.add_child(this._webLoginIntro);
++            this._webLoginPromptWell.visible = true;
++
++            this.updateSensitivity(true);
++        } else {
++            this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code);
++        }
++
++        this._webLoginTimedOut = false;
++        this.emit('prompted');
++    }
++
++    _refreshWebLogin(serviceName) {
++        this.reset({
++            beginRequestType: BeginRequestType.REUSE_USERNAME,
++            queryingService: serviceName,
++        });
++    }
++
++    _onWebLoginTimeOut(userVerifier, serviceName) {
++        this._webLoginTimedOut = true;
++
++        if (this._queryingService !== serviceName)
++            return;
++
++        this._refreshWebLogin(serviceName);
++    }
++
++    _closeWebLoginDialog() {
++        if (!this._webLoginDialog)
++            return;
++
++        this._webLoginDialog.close();
++        this._webLoginDialog = null;
++    }
++
++    _openWebLoginDialog(userVerifier, serviceName, message, url, code) {
++        if (this._queryingService)
++            this.clear();
++
++        this._closeWebLoginDialog();
++
++        this._queryingService = serviceName;
++
++        this._webLoginPromptWell.remove_all_children();
++
++        this._webLoginDialog = new WebLogin.WebLoginDialog({message, url, code});
++        this._webLoginDialog.open(global.get_current_time());
++        this._webLoginDialog.connect('cancel', () => {
++            if (this.verificationStatus !== AuthPromptStatus.VERIFICATION_SUCCEEDED)
++                this.reset();
++        });
++        this._webLoginDialog.connect('done', () => {
++            userVerifier.connectObject(
++                `service-request::${serviceName}`, this._closeWebLoginDialog.bind(this),
++
++                'verification-complete', this._closeWebLoginDialog.bind(this),
++
++                'verification-failed', () => {
++                    this._closeWebLoginDialog();
++                    this.showLoginFailedNotification();
++                    this.reset();
++                },
++                this);
++            userVerifier.webLoginDone(serviceName);
++        });
++
++        if (this._spinner)
++            this._spinner.stop();
++
++        this.updateSensitivity(true);
++        this.emit('prompted');
++    }
++
+     _onCredentialManagerAuthenticated() {
+         if (this.verificationStatus !== AuthPromptStatus.VERIFICATION_SUCCEEDED)
+             this.reset();
+@@ -521,12 +638,16 @@ export const AuthPrompt = GObject.registerClass({
+         this.stopSpinning();
+         this._authList.clear();
+         this._authList.hide();
++        this._webLoginPromptWell.remove_all_children();
++        this._webLoginPromptWell.visible = false;
+     }
+ 
+     setQuestion(question) {
+         this._entry.hint_text = question;
+ 
+         this._authList.hide();
++        this._closeWebLoginDialog();
++
+         this._entry.show();
+         this._entry.grab_key_focus();
+     }
+@@ -608,6 +729,9 @@ export const AuthPrompt = GObject.registerClass({
+     }
+ 
+     updateSensitivity(sensitive) {
++        if (this._webLoginDialog)
++            return;
++
+         if (this._entry.reactive === sensitive)
+             return;
+ 
+@@ -737,6 +861,7 @@ export const AuthPrompt = GObject.registerClass({
+             hold: null,
+         });
+ 
++        this.hideLoginFailedNotification();
+         this.updateSensitivity(false);
+ 
+         let hold = params.hold;
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index e5b469b..e28445d 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -39,6 +39,7 @@ export const AUTH_MECHANISM_PROTOCOL = 'auth-mechanisms';
+ export const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
+ export const UNIFIED_AUTHENTICATION_KEY = 'enable-unified-authentication';
+ export const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
++export const WEB_LOGIN_AUTHENTICATION_KEY = 'enable-web-authentication';
+ export const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
+ export const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
+ export const BANNER_MESSAGE_KEY = 'banner-message-enable';
+@@ -266,6 +267,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             this._cancellable = null;
+         }
+ 
++        this._clearWebLoginTimeout();
+         this._clearUserVerifier();
+         this._clearMessageQueue();
+         this._activeServices.clear();
+@@ -847,6 +849,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+     _startMechanismFromUnifiedService(mechanism) {
+         const roleHandlers = {
++            [WEB_LOGIN_ROLE_NAME]: this._startWebLogin.bind(this),
+             [PASSWORD_ROLE_NAME]: this._startPasswordLogin.bind(this),
+         };
+ 
+@@ -950,6 +953,45 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         await this.replyWithJSON(serviceName, JSON.stringify(reply));
+     }
+ 
++    _startWebLogin(serviceName, mechanismId) {
++        const mechanisms = this._publishedMechanisms.get(serviceName);
++
++        if (!mechanisms)
++            return;
++
++        if (!mechanisms[mechanismId])
++            return;
++
++        const {init_prompt: initPrompt, link_prompt: linkPrompt, uri, code, role, timeout} = mechanisms[mechanismId];
++
++        if (!linkPrompt || !uri)
++            return;
++
++        if (timeout) {
++            this._webLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout * 1000,
++                () => {
++                    this.emit('web-login-time-out', serviceName);
++                    this._webLoginTimeoutId = 0;
++                    return GLib.SOURCE_REMOVE;
++                });
++        }
++
++        this._pendingMechanisms.set(role, mechanismId);
++        this.emit('web-login', serviceName, initPrompt, linkPrompt, uri, code);
++    }
++
++    _clearWebLoginTimeout() {
++        if (this._webLoginTimeoutId) {
++            GLib.source_remove(this._webLoginTimeoutId);
++            this._webLoginTimeoutId = 0;
++        }
++    }
++
++    webLoginDone(serviceName) {
++        this._clearWebLoginTimeout();
++        this._replyWithAuthSelectionResponse(serviceName, WEB_LOGIN_ROLE_NAME, {});
++    }
++
+     _filterAuthMechanisms(mechanismsList, filterFunc) {
+         return mechanismsList.filter(mechanism => {
+             const mapping = DiscreteServiceMechanismDefinitions.find(m =>
+@@ -1211,6 +1253,10 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         const doneTrying = !shouldRetry || !this._canRetry();
+ 
+         this.emit('verification-failed', serviceName, !doneTrying);
++
++        if (!this._userVerifier)
++            return;
++
+         try {
+             if (doneTrying) {
+                 this._disconnectSignals();
+diff --git a/js/gdm/webLogin.js b/js/gdm/webLogin.js
+new file mode 100644
+index 0000000..30697ae
+--- /dev/null
++++ b/js/gdm/webLogin.js
+@@ -0,0 +1,259 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++//
++// A widget showing a URL for web login
++/* exported WebLoginPrompt */
++
++import Clutter from 'gi://Clutter';
++import GObject from 'gi://GObject';
++import Gio from 'gi://Gio';
++import Shell from 'gi://Shell';
++import St from 'gi://St';
++import {Spinner} from '../ui/animation.js';
++import * as ModalDialog from '../ui/modalDialog.js';
++import * as Params from '../misc/params.js';
++
++const QR_CODE_SIZE = 150;
++const WEB_LOGIN_SPINNER_SIZE = 35;
++
++Gio._promisify(Shell.QrCodeGenerator.prototype, 'generate_qr_code');
++
++export const QrCode = GObject.registerClass(
++class QrCode extends St.Bin {
++    _init(params) {
++        const themeContext = St.ThemeContext.get_for_stage(global.stage);
++        const {iconSize, url} = Params.parse(params, {
++            iconSize: QR_CODE_SIZE,
++            url: null,
++        });
++
++        super._init({
++            width: QR_CODE_SIZE,
++            height: QR_CODE_SIZE,
++            x_align: Clutter.ActorAlign.CENTER,
++        });
++
++        this._qrCodeGenerator = new Shell.QrCodeGenerator();
++        this._iconSize = iconSize;
++        this._url = url;
++        this.child = new St.Icon({
++            icon_size: this._iconSize,
++            style_class: 'qr-code',
++        });
++
++        themeContext.connectObject('notify::scale-factor', this.update.bind(this), this);
++
++        this.update();
++    }
++
++    vfunc_style_changed() {
++        super.vfunc_style_changed();
++
++        let node = this.get_theme_node();
++        let [found, iconSize] = node.lookup_length('icon-size', false);
++
++        if (!found)
++            return;
++
++        let themeContext = St.ThemeContext.get_for_stage(global.stage);
++
++        this._iconSize = iconSize / themeContext.scaleFactor;
++        this.update().catch(logError);
++    }
++
++    async update() {
++        let {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
++        this.set_size(
++            this._iconSize * scaleFactor,
++            this._iconSize * scaleFactor);
++
++        this.child.gicon = await this._qrCodeGenerator.generate_qr_code(
++            this._url, this._iconSize, this._iconSize);
++
++        this.style = null;
++    }
++});
++
++export const WebLoginPrompt = GObject.registerClass(
++class WebLoginPrompt extends St.BoxLayout {
++    _init(params) {
++        const {iconSize, message, url, code} = Params.parse(params, {
++            iconSize: QR_CODE_SIZE,
++            message: null,
++            url: null,
++            code: null,
++        });
++
++        super._init({
++            styleClass: 'web-login-prompt',
++            orientation: Clutter.Orientation.VERTICAL,
++        });
++
++        this._urlTitleLabel = new St.Label({
++            text: message,
++            style_class: 'web-login-title-label',
++        });
++        this.add_child(this._urlTitleLabel);
++
++        this._qrCode = new QrCode({iconSize, url});
++        this.add_child(this._qrCode);
++
++        this._urlLabel = new St.Label({
++            style_class: 'web-login-url-label',
++            text: this._formatURLForDisplay(url),
++            x_expand: true,
++        });
++        this.add_child(this._urlLabel);
++
++        if (code) {
++            this._codeBox = new St.BoxLayout({
++                x_align: Clutter.ActorAlign.CENTER,
++                x_expand: true,
++            });
++            this.add_child(this._codeBox);
++
++            this._codeTitleLabel = new St.Label({
++                text: _('Login code: '),
++                style_class: 'web-login-code-title-label',
++            });
++            this._codeBox.add_child(this._codeTitleLabel);
++
++            this._codeLabel = new St.Label({
++                text: code,
++                style_class: 'web-login-code-label',
++            });
++            this._codeBox.add_child(this._codeLabel);
++        }
++    }
++
++    _formatURLForDisplay(url) {
++        const http = 'http://';
++        const https = 'https://';
++
++        if (url.startsWith(http))
++            return url.substring(http.length);
++
++        if (url.startsWith(https))
++            return url.substring(https.length);
++
++        return url;
++    }
++});
++
++export const WebLoginDialog = GObject.registerClass({
++    Signals: {
++        'cancel': {},
++        'done': {},
++    },
++}, class WebLoginDialog extends ModalDialog.ModalDialog {
++    _init(params) {
++        const {message, url, code} = Params.parse(params, {
++            message: null,
++            url: null,
++            code: null,
++        });
++
++        super._init({
++            shouldFadeOut: false,
++            styleClass: 'web-login-dialog',
++        });
++
++        this._webLoginPrompt = new WebLoginPrompt({code, message, url});
++        this._webLoginPrompt.set({
++            y_align: Clutter.ActorAlign.CENTER,
++        });
++
++        this.contentLayout.reactive = false;
++        this.contentLayout.can_focus = false;
++        this.contentLayout.add_child(this._webLoginPrompt);
++        this._updateButtons();
++
++        this._contentOverlay = new St.Widget({
++            layout_manager: new Clutter.BinLayout(),
++            style_class: 'web-login-dialog-content-overlay',
++        });
++        this._contentOverlay.hide();
++
++        this.backgroundStack.add_child(this._contentOverlay);
++
++        const constraint = new Clutter.BindConstraint({
++            source: this.dialogLayout,
++            coordinate: Clutter.BindCoordinate.ALL,
++        });
++
++        this._contentOverlay.add_constraint(constraint);
++
++        this._spinnerFrame = new St.Widget({
++            layout_manager: new Clutter.BinLayout(),
++            styleClass: 'web-login-spinner',
++        });
++        this._contentOverlay.add_child(this._spinnerFrame);
++
++        this._spinner = new Spinner(WEB_LOGIN_SPINNER_SIZE, {
++            hideOnStop: true,
++        });
++        this._spinnerFrame.add_child(this._spinner);
++    }
++
++    done() {
++        this._doneButton.reactive = false;
++        this._doneButton.can_focus = false;
++
++        this._contentOverlay.show();
++        global.stage.set_key_focus(this.dialogLayout);
++        this._spinner.play();
++
++        this.emit('done');
++    }
++
++    _updateButtons() {
++        this.clearButtons();
++
++        this._cancelButton = this.addButton({
++            action: this.cancel.bind(this),
++            label: _('Cancel'),
++            key: Clutter.KEY_Escape,
++        });
++
++        this._doneButton = this.addButton({
++            action: this.done.bind(this),
++            default: true,
++            label: _('Done'),
++        });
++    }
++
++    cancel() {
++        this.emit('cancel');
++        this.close();
++    }
++});
++
++export var WebLoginIntro = GObject.registerClass(
++class WebLoginIntro extends St.Button {
++    _init(params) {
++        const {message} = Params.parse(params, {
++            message: null,
++        });
++
++        const label = new St.Label({
++            text: message,
++            style_class: 'web-login-intro-button-label',
++            x_align: Clutter.ActorAlign.CENTER,
++            y_align: Clutter.ActorAlign.CENTER,
++            x_expand: true,
++            y_expand: false,
++        });
++
++        label.clutter_text.line_wrap = true;
++        label.clutter_text.y_align = Clutter.ActorAlign.CENTER;
++        label.clutter_text.x_align = Clutter.ActorAlign.CENTER;
++
++        super._init({
++            style_class: 'web-login-prompt login-dialog-button web-login-intro-button',
++            accessible_name: message,
++            button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
++            reactive: true,
++            can_focus: true,
++            child: label,
++        });
++    }
++});
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index b1c59f3..26d5f35 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -11,6 +11,7 @@
+     <file>gdm/realmd.js</file>
+     <file>gdm/util.js</file>
+     <file>gdm/vmware.js</file>
++    <file>gdm/webLogin.js</file>
+ 
+     <file>extensions/extension.js</file>
+     <file>extensions/sharedInternals.js</file>
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index ee0829c..5ec7365 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -13,6 +13,7 @@ js/dbusServices/extensions/ui/extension-error-page.ui
+ js/gdm/authPrompt.js
+ js/gdm/loginDialog.js
+ js/gdm/util.js
++js/gdm/webLogin.js
+ js/misc/breakManager.js
+ js/misc/brightnessManager.js
+ js/misc/systemActions.js
diff -pruN 49.0-1/debian/patches/ubuntu-authd/authPrompt-Fade-out-cancel-button-after-verification.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Fade-out-cancel-button-after-verification.patch
--- 49.0-1/debian/patches/ubuntu-authd/authPrompt-Fade-out-cancel-button-after-verification.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Fade-out-cancel-button-after-verification.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,30 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Fri, 9 Feb 2024 09:02:25 -0500
+Subject: authPrompt: Fade out cancel button after verification
+
+Right now we make the cancel button non-reactive when
+verification is complete, but it still disrupts the
+login transition because in many cases most of the other
+screen elements have been faded out.
+
+This commit fades it out as well.
+---
+ js/gdm/authPrompt.js | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 5053ece..3634e3c 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -552,6 +552,11 @@ export const AuthPrompt = GObject.registerClass({
+         this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
+         this.cancelButton.reactive = false;
+         this.cancelButton.can_focus = false;
++        this.cancelButton.ease({
++            opacity: 0,
++            duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
++            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
++        });
+     }
+ 
+     _onReset() {
diff -pruN 49.0-1/debian/patches/ubuntu-authd/authPrompt-Parameterize-reset-function.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Parameterize-reset-function.patch
--- 49.0-1/debian/patches/ubuntu-authd/authPrompt-Parameterize-reset-function.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Parameterize-reset-function.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,83 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 13:06:32 -0500
+Subject: authPrompt: Parameterize reset function
+
+In the future, the code will need to do a partial reset where
+some state is carried over or explicitly specified.
+
+This commit prepares for that by allowing the request type and
+querying service to be specified at reset time.
+---
+ js/gdm/authPrompt.js | 45 +++++++++++++++++++++++++--------------------
+ 1 file changed, 25 insertions(+), 20 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index d737179..833b35b 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -631,7 +631,12 @@ export const AuthPrompt = GObject.registerClass({
+             this._updateEntry(false);
+     }
+ 
+-    reset() {
++    reset(params) {
++        let {beginRequestType, queryingService} = Params.parse(params, {
++            beginRequestType: null,
++            queryingService: null,
++        });
++
+         let oldStatus = this.verificationStatus;
+         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
+         this.cancelButton.reactive = this._hasCancelButton;
+@@ -641,7 +646,7 @@ export const AuthPrompt = GObject.registerClass({
+         if (this._userVerifier)
+             this._userVerifier.cancel();
+ 
+-        this._queryingService = null;
++        this._queryingService = queryingService;
+         this.clear();
+         this._message.opacity = 0;
+         this.setUser(null);
+@@ -653,24 +658,24 @@ export const AuthPrompt = GObject.registerClass({
+         else if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
+             this.emit('cancelled');
+ 
+-        let beginRequestType;
+-
+-        if (this._mode === AuthPromptMode.UNLOCK_ONLY) {
+-            // The user is constant at the unlock screen, so it will immediately
+-            // respond to the request with the username
+-            if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
+-                return;
+-            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
+-        } else if (this._userVerifier.foregroundServiceDeterminesUsername()) {
+-            // We don't need to know the username if the user preempted the login screen
+-            // with a smartcard or with preauthenticated oVirt credentials
+-            beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
+-        } else if (oldStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
+-            // We're going back to retry with current user
+-            beginRequestType = BeginRequestType.REUSE_USERNAME;
+-        } else {
+-            // In all other cases, we should get the username up front.
+-            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
++        if (beginRequestType === null) {
++            if (this._mode === AuthPromptMode.UNLOCK_ONLY) {
++                // The user is constant at the unlock screen, so it will immediately
++                // respond to the request with the username
++                if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
++                    return;
++                beginRequestType = BeginRequestType.PROVIDE_USERNAME;
++            } else if (this._userVerifier.foregroundServiceDeterminesUsername()) {
++                // We don't need to know the username if the user preempted the login screen
++                // with a smartcard or with preauthenticated oVirt credentials
++                beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
++            } else if (oldStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
++                // We're going back to retry with current user
++                beginRequestType = BeginRequestType.REUSE_USERNAME;
++            } else {
++                // In all other cases, we should get the username up front.
++                beginRequestType = BeginRequestType.PROVIDE_USERNAME;
++            }
+         }
+ 
+         this.emit('reset', beginRequestType);
diff -pruN 49.0-1/debian/patches/ubuntu-authd/authPrompt-Right-align-cancel-button.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Right-align-cancel-button.patch
--- 49.0-1/debian/patches/ubuntu-authd/authPrompt-Right-align-cancel-button.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/authPrompt-Right-align-cancel-button.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,25 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 13:48:26 -0500
+Subject: authPrompt: Right align cancel button
+
+The cancel button should always hug the input controls
+to the right of it.
+
+This commit fixes that.
+---
+ js/gdm/authPrompt.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index b9d3535..d737179 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -158,7 +158,7 @@ export const AuthPrompt = GObject.registerClass({
+             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+             reactive: this._hasCancelButton,
+             can_focus: this._hasCancelButton,
+-            x_align: Clutter.ActorAlign.START,
++            x_align: Clutter.ActorAlign.END,
+             y_align: Clutter.ActorAlign.CENTER,
+             icon_name: 'go-previous-symbolic',
+         });
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-Add-Login-Failed-notification.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-Login-Failed-notification.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-Add-Login-Failed-notification.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-Login-Failed-notification.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,84 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 7 Feb 2024 11:47:32 -0500
+Subject: gdm: Add "Login Failed" notification
+
+One step toward implementing Web Login is adding the ability to
+show "Login Failed" as a notification banner.
+
+This is because with Web Login, the auth prompt is occluded so can't
+be used to show an error message itself.
+
+This commit adds the necessary code to show the Login Failed
+notification, but doesn't actually call that code yet.
+
+It will be used later when Web Login support is added.
+---
+ .../gnome-shell-sass/widgets/_login-lock.scss      | 13 ++++++++++++
+ js/gdm/authPrompt.js                               | 23 ++++++++++++++++++++++
+ 2 files changed, 36 insertions(+)
+
+diff --git a/data/theme/gnome-shell-sass/widgets/_login-lock.scss b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+index f9183f1..c7411d0 100644
+--- a/data/theme/gnome-shell-sass/widgets/_login-lock.scss
++++ b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+@@ -393,6 +393,19 @@ $_gdm_dialog_width: 25em;
+   }
+ }
+ 
++// Login Failed banner
++.auth-notification-banner {
++  .message-header {
++    width: 0px;
++    height: 0px;
++
++    .message-close-button {
++      border: none;
++      background-color: rgba(0, 0, 0, 0.0);
++    }
++  }
++}
++
+ // QR Code
+ .qr-code {
+   background: black;
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index eb28eb0..3c8e1f6 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -10,6 +10,7 @@ import * as Animation from '../ui/animation.js';
+ import * as AuthList from './authList.js';
+ import * as Batch from './batch.js';
+ import * as GdmUtil from './util.js';
++import * as MessageTray from '../ui/messageTray.js';
+ import * as Params from '../misc/params.js';
+ import * as ShellEntry from '../ui/shellEntry.js';
+ import * as UserWidget from '../ui/userWidget.js';
+@@ -650,6 +651,28 @@ export const AuthPrompt = GObject.registerClass({
+         this._userVerifier.setForegroundMechanism(mechanism);
+     }
+ 
++    showLoginFailedNotification() {
++        const source = new MessageTray.getSystemSource();
++
++        this._loginFailedNotification = new MessageTray.Notification({
++            source,
++            iconName: 'dialog-password-symbolic',
++            title: _('Login Failed'),
++            body: _('Please try again'),
++            isTransient: true,
++        });
++
++        source.addNotification(this._loginFailedNotification);
++    }
++
++    hideLoginFailedNotification() {
++        if (!this._loginFailedNotification)
++            return;
++
++        this._loginFailedNotification.destroy();
++        this._loginFailedNotification = null;
++    }
++
+     reset(params) {
+         let {beginRequestType, queryingService} = Params.parse(params, {
+             beginRequestType: null,
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-Add-new-AuthMenuButton-control.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-new-AuthMenuButton-control.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-Add-new-AuthMenuButton-control.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-new-AuthMenuButton-control.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,255 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 11:04:20 -0500
+Subject: gdm: Add new AuthMenuButton control
+
+The latest login screen designs here:
+
+https://gitlab.gnome.org/Teams/Design/os-mockups/-/blob/master/lock-login/login-authentication.png
+
+shows a new "Login Options" menu in the corner.
+
+As a first step toward acheiving that goal, this commit adds a new
+AuthMenuButton class. It's a fairly generic control derived from
+the code used to show the sessions menu button. One key difference
+is it allows a multi-valued, partial key for its entries. This provides
+flexibility that we'll need to leverage later.
+
+Nothing uses this new class yet. A subsequent commit will change the
+sessions menu button code over to use it, and a commit after that will
+use it for Login Options.
+---
+ js/gdm/authMenuButton.js      | 212 ++++++++++++++++++++++++++++++++++++++++++
+ js/js-resources.gresource.xml |   1 +
+ 2 files changed, 213 insertions(+)
+ create mode 100644 js/gdm/authMenuButton.js
+
+diff --git a/js/gdm/authMenuButton.js b/js/gdm/authMenuButton.js
+new file mode 100644
+index 0000000..ea462ac
+--- /dev/null
++++ b/js/gdm/authMenuButton.js
+@@ -0,0 +1,212 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++/*
++ * Copyright 2024 Red Hat, Inc
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++import Atk from 'gi://Atk';
++import Clutter from 'gi://Clutter';
++import GObject from 'gi://GObject';
++import Shell from 'gi://Shell';
++import St from 'gi://St';
++
++import * as BoxPointer from '../ui/boxpointer.js';
++import * as Main from '../ui/main.js';
++import * as Params from '../misc/params.js';
++import * as PopupMenu from '../ui/popupMenu.js';
++
++export const AuthMenuButton = GObject.registerClass({
++    Signals: {'active-item-changed': {}},
++}, class AuthMenuButton extends St.Bin {
++    _init(params) {
++        params = Params.parse(params, {
++            title: '',
++            iconName: '',
++        });
++
++        let button = new St.Button({
++            style_class: 'login-dialog-button login-dialog-auth-menu-button',
++            icon_name: params.iconName,
++            reactive: true,
++            track_hover: true,
++            can_focus: true,
++            accessible_name: params.title,
++            accessible_role: Atk.Role.MENU,
++            x_align: Clutter.ActorAlign.CENTER,
++            y_align: Clutter.ActorAlign.CENTER,
++        });
++
++        super._init({child: button});
++        this._button = button;
++
++        this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.BOTTOM);
++        this._menu.box.add_style_class_name('login-dialog-auth-menu-button-popup-menu-box');
++        Main.uiGroup.add_child(this._menu.actor);
++        this._menu.actor.hide();
++
++        this._header = new St.Label({
++            text: params.title,
++            style_class: 'login-dialog-auth-menu-button-title',
++            y_align: Clutter.ActorAlign.START,
++            y_expand: true,
++        });
++        this._menu.box.add_child(this._header);
++
++        this._menu.connect('open-state-changed', (menu, isOpen) => {
++            if (isOpen)
++                this._button.add_style_pseudo_class('active');
++            else
++                this._button.remove_style_pseudo_class('active');
++        });
++
++        this._manager = new PopupMenu.PopupMenuManager(this._button,
++            {actionMode: Shell.ActionMode.NONE});
++        this._manager.addMenu(this._menu);
++
++        this._button.connect('clicked', () => this._menu.toggle());
++
++        this._items = new Map();
++        this._activeItem = null;
++        this.updateSensitivity(true);
++    }
++
++    _getMenuItem(item) {
++        if (!item)
++            return null;
++
++        return this._items.get(JSON.stringify(item));
++    }
++
++    updateSensitivity(sensitive) {
++        this._sensitive = sensitive;
++
++        if (this._items.size <= 1)
++            sensitive = false;
++
++        this._button.reactive = sensitive;
++        this._button.can_focus = sensitive;
++        this.opacity = sensitive ? 255 : 0;
++        this._menu.close(BoxPointer.PopupAnimation.NONE);
++    }
++
++    _updateOrnament() {
++        for (const menuItem of this._items.values())
++            menuItem.setOrnament(PopupMenu.Ornament.NO_DOT);
++
++        const activeMenuItem = this._getMenuItem(this._activeItem);
++
++        if (activeMenuItem)
++            activeMenuItem.setOrnament(PopupMenu.Ornament.DOT);
++    }
++
++    _findItems(searchCriteria) {
++        let items = [];
++        for (const itemKey of this._items.keys()) {
++            const item = JSON.parse(itemKey);
++
++            let criteriaMismatch = false;
++            for (const key of Object.keys(searchCriteria)) {
++                if (!searchCriteria[key])
++                    continue;
++
++                if (item[key] === searchCriteria[key])
++                    continue;
++
++                criteriaMismatch = true;
++                break;
++            }
++
++            if (criteriaMismatch)
++                continue;
++
++            items.push(itemKey);
++        }
++
++        return items;
++    }
++
++    clearItems(searchCriteria) {
++        if (!searchCriteria)
++            searchCriteria = {};
++
++        const activeMenuItem = this._getMenuItem(this._activeItem);
++        const itemKeys = this._findItems(searchCriteria);
++        for (const itemKey of itemKeys) {
++            const menuItem = this._items.get(itemKey);
++
++            if (activeMenuItem === menuItem)
++                this._activeItem = null;
++
++            menuItem.destroy();
++            this._items.delete(itemKey);
++        }
++    }
++
++    addItem(item) {
++        let menuItem = this._getMenuItem(item);
++
++        if (menuItem)
++            throw new Error(`Duplicate item ${JSON.stringify(item)}`);
++
++        if (!item.name)
++            throw new Error(`item ${JSON.stringify(item)} lacks name`);
++
++        menuItem = new PopupMenu.PopupMenuItem(item.name, {
++            style_class: 'login-dialog-auth-menu-button-item',
++        });
++        menuItem.setOrnament(PopupMenu.Ornament.NO_DOT);
++        menuItem.connect('activate', () => {
++            this.setActiveItem(item);
++        });
++
++        this._menu.addMenuItem(menuItem);
++        this._items.set(JSON.stringify(item), menuItem);
++        this.updateSensitivity(this._sensitive);
++    }
++
++    _resolveItem(searchCriteria) {
++        const itemKeys = this._findItems(searchCriteria);
++
++        if (!itemKeys.length)
++            throw new Error(`Unknown item ${JSON.stringify(searchCriteria)}`);
++
++        if (itemKeys.length > 1)
++            throw new Error(`Matched multiple items with criteria ${JSON.stringify(searchCriteria)}`);
++
++        const item = JSON.parse(itemKeys[0]);
++        const menuItem = this._items.get(itemKeys[0]);
++        return {item, menuItem};
++    }
++
++    setActiveItem(searchCriteria) {
++        const {item, menuItem} = this._resolveItem(searchCriteria);
++        const activeMenuItem = this._getMenuItem(this._activeItem);
++
++        if (menuItem === activeMenuItem)
++            return;
++
++        this._activeItem = item;
++        this._updateOrnament();
++        this.emit('active-item-changed');
++    }
++
++    getActiveItem() {
++        return this._activeItem;
++    }
++
++    close() {
++        this._menu.close();
++    }
++});
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index 8c2b908..b1c59f3 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -2,6 +2,7 @@
+ <gresources>
+   <gresource prefix="/org/gnome/shell">
+     <file>gdm/authList.js</file>
++    <file>gdm/authMenuButton.js</file>
+     <file>gdm/authPrompt.js</file>
+     <file>gdm/batch.js</file>
+     <file>gdm/credentialManager.js</file>
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-Add-support-unified-authentication.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-support-unified-authentication.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-Add-support-unified-authentication.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Add-support-unified-authentication.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,442 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 14:09:34 -0500
+Subject: gdm: Add support unified authentication
+
+SSSD wants the ability to control all authentication mechanisms
+it can support from a single pam conversation.
+
+This commit adds support for a new "unified" authentication service
+that allows for that. It leverages GDM's new JSON extension with a
+new "auth-selection" protocol provided by pam_sss.
+
+This initial commit just supports password like mechanisms. Future
+commits will add support for mechanisms that support other authentication
+ roles.
+---
+ js/gdm/loginDialog.js |   5 +-
+ js/gdm/util.js        | 262 +++++++++++++++++++++++++++++++++++++++++++++++---
+ js/ui/unlockDialog.js |   1 +
+ 3 files changed, 255 insertions(+), 13 deletions(-)
+
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index b3a4a3f..c9d6fbf 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -544,7 +544,10 @@ export const LoginDialog = GObject.registerClass({
+         this._gdmClient = new Gdm.Client();
+ 
+         try {
+-            this._gdmClient.set_enabled_extensions([Gdm.UserVerifierChoiceList.interface_info().name]);
++            this._gdmClient.set_enabled_extensions([
++                Gdm.UserVerifierChoiceList.interface_info().name,
++                Gdm.UserVerifierCustomJSON.interface_info().name,
++            ]);
+         } catch {
+         }
+ 
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index b0d775e..ae085349 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -23,6 +23,7 @@ Gio._promisify(Gdm.UserVerifierProxy.prototype,
+     'call_begin_verification_for_user');
+ Gio._promisify(Gdm.UserVerifierProxy.prototype, 'call_begin_verification');
+ 
++export const UNIFIED_AUTH_SERVICE_NAME = 'gdm-unified-auth';
+ export const PASSWORD_SERVICE_NAME = 'gdm-password';
+ export const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+ export const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+@@ -31,8 +32,12 @@ const CLONE_FADE_ANIMATION_TIME = 250;
+ export const PASSWORD_ROLE_NAME = 'password';
+ export const SMARTCARD_ROLE_NAME = 'smartcard';
+ export const FINGERPRINT_ROLE_NAME = 'fingerprint';
++export const WEB_LOGIN_ROLE_NAME = 'eidp';
++
++export const AUTH_MECHANISM_PROTOCOL = 'auth-mechanisms';
+ 
+ export const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
++export const UNIFIED_AUTHENTICATION_KEY = 'enable-unified-authentication';
+ export const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
+ export const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
+ export const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
+@@ -45,6 +50,13 @@ export const ALLOWED_FAILURES_KEY = 'allowed-failures';
+ export const LOGO_KEY = 'logo';
+ export const DISABLE_USER_LIST_KEY = 'disable-user-list';
+ 
++const AUTH_SELECTION_COMPLETION_STATUS = 'Ok';
++
++const UNIFIED_AUTH_SUPPORTED_ROLES = [
++    PASSWORD_ROLE_NAME,
++    WEB_LOGIN_ROLE_NAME,
++];
++
+ // Give user 48ms to read each character of a PAM message
+ const USER_READ_TIME = 48;
+ const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
+@@ -151,6 +163,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._activeServices = new Set();
+         this._unavailableServices = new Set();
+         this._activeServices = new Set();
++        this._publishedMechanisms = new Map();
+         this._pendingMechanisms = new Map();
+ 
+         this._credentialManagers = {};
+@@ -239,6 +252,10 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+                 this._userVerifierChoiceList.run_dispose();
+                 this._userVerifierChoiceList = null;
+             }
++            if (this._userVerifierCustomJSON) {
++                this._userVerifierCustomJSON.run_dispose();
++                this._userVerifierCustomJSON = null;
++            }
+         }
+     }
+ 
+@@ -275,7 +292,20 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     async answerQuery(serviceName, answer) {
+         try {
+             await this._handlePendingMessages();
+-            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
++            if (this._pendingMechanisms.has(PASSWORD_ROLE_NAME))
++                this._replyWithAuthSelectionResponse(serviceName, PASSWORD_ROLE_NAME, {password: answer});
++            else
++                this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
++        } catch (e) {
++            if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
++                logError(e);
++        }
++    }
++
++    async replyWithJSON(serviceName, json) {
++        try {
++            await this._handlePendingMessages();
++            this._userVerifierCustomJSON.call_reply(serviceName, json, this._cancellable, null);
+         } catch (e) {
+             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                 logError(e);
+@@ -537,6 +567,18 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._verificationFailed(serviceName, false);
+     }
+ 
++    _getClientExtensionProxies() {
++        if (this._client.get_user_verifier_choice_list)
++            this._userVerifierChoiceList = this._client.get_user_verifier_choice_list();
++        else
++            this._userVerifierChoiceList = null;
++
++        if (this._client.get_user_verifier_custom_json)
++            this._userVerifierCustomJSON = this._client.get_user_verifier_custom_json();
++        else
++            this._userVerifierCustomJSON = null;
++    }
++
+     async _openReauthenticationChannel(userName) {
+         try {
+             this._clearUserVerifier();
+@@ -558,10 +600,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             return;
+         }
+ 
+-        if (this._client.get_user_verifier_choice_list)
+-            this._userVerifierChoiceList = this._client.get_user_verifier_choice_list();
+-        else
+-            this._userVerifierChoiceList = null;
++        this._getClientExtensionProxies();
+ 
+         this.reauthenticating = true;
+         this._connectSignals();
+@@ -581,10 +620,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             return;
+         }
+ 
+-        if (this._client.get_user_verifier_choice_list)
+-            this._userVerifierChoiceList = this._client.get_user_verifier_choice_list();
+-        else
+-            this._userVerifierChoiceList = null;
++        this._getClientExtensionProxies();
+ 
+         this._connectSignals();
+         this._beginVerification();
+@@ -610,11 +646,17 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             this._userVerifierChoiceList.connectObject('choice-query',
+                 this._onChoiceListQuery.bind(this), this);
+         }
++
++        if (this._userVerifierCustomJSON) {
++            this._userVerifierCustomJSON.connectObject('request',
++                this._onCustomJSONRequest.bind(this), this);
++        }
+     }
+ 
+     _disconnectSignals() {
+         this._userVerifier?.disconnectObject(this);
+         this._userVerifierChoiceList?.disconnectObject(this);
++        this._userVerifierCustomJSON?.disconnectObject(this);
+     }
+ 
+     _getForegroundService() {
+@@ -723,7 +765,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _getDetectedDefaultService() {
+-        if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
++        if (this._settings.get_boolean(UNIFIED_AUTHENTICATION_KEY))
++            return UNIFIED_AUTH_SERVICE_NAME;
++        else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
+             return PASSWORD_SERVICE_NAME;
+         else if (this._smartcardManager)
+             return SMARTCARD_SERVICE_NAME;
+@@ -790,11 +834,71 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._maybeStartFingerprintVerification().catch(logError);
+     }
+ 
++    _startMechanismFromUnifiedService(mechanism) {
++        const roleHandlers = {
++            [PASSWORD_ROLE_NAME]: this._startPasswordLogin.bind(this),
++        };
++
++        let handler = roleHandlers[mechanism.role];
++
++        if (handler)
++            handler(mechanism.serviceName, mechanism.id);
++    }
++
++    _shouldStartBackgroundService(serviceName) {
++        if (!this._userName)
++            return false;
++
++        if (this.serviceIsForeground(serviceName))
++            return false;
++
++        if (this._activeServices.has(serviceName))
++            return false;
++
++        if (this._unavailableServices.has(serviceName))
++            return false;
++
++        if (serviceName === FINGERPRINT_SERVICE_NAME)
++            return this._fingerprintReaderType !== FingerprintReaderType.NONE;
++
++        return true;
++    }
++
++    _onMechanismsListChanged(serviceName, mechanismsList) {
++        /* Start all non-selectable (background) mechanisms and the foreground
++         * mechanism. Note for discrete auth (e.g. not the non-unified services),
++         * the foreground mechanism is already started explicitly in beginVerification
++         */
++        if (this._foregroundMechanism?.serviceName === UNIFIED_AUTH_SERVICE_NAME)
++            this._startMechanismFromUnifiedService(this._foregroundMechanism);
++
++        if (this._unavailableServices.has(serviceName))
++            return;
++
++        for (const mechanism of mechanismsList) {
++            /* If it's selectable we wait until it's selected to start it
++             */
++            if (mechanism.selectable)
++                continue;
++
++            if (this._shouldStartBackgroundService(serviceName))
++                this._startService(serviceName);
++
++            if (serviceName === UNIFIED_AUTH_SERVICE_NAME)
++                this._startMechanismFromUnifiedService(mechanism);
++        }
++    }
++
+     _beginVerification() {
+         const foregroundService = this._getForegroundService();
+ 
++        this._mechanismsListChangedSignalId =
++            this.connect('mechanisms-list-changed',
++                (_, ...args) => this._onMechanismsListChanged(...args));
++
+         this._startService(foregroundService);
+         this._startBackgroundServices();
++        this._generateMechanismsFromDiscreteServices();
+     }
+ 
+     _onChoiceListQuery(client, serviceName, promptMessage, list) {
+@@ -804,6 +908,128 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this.emit('show-choice-list', serviceName, promptMessage, list.deepUnpack());
+     }
+ 
++    _startPasswordLogin(serviceName, mechanismId) {
++        const mechanisms = this._publishedMechanisms.get(serviceName);
++
++        if (!mechanisms)
++            return;
++
++        if (!mechanisms[mechanismId])
++            return;
++
++        const {prompt, role} = mechanisms[mechanismId];
++
++        this._pendingMechanisms.set(role, mechanismId);
++        this.emit('ask-question', serviceName, prompt, true);
++    }
++
++    async _replyWithAuthSelectionResponse(serviceName, role, response) {
++        const mechanismId = this._pendingMechanisms.get(role);
++        this._pendingMechanisms.delete(role);
++
++        const reply = {
++            'auth-selection': {
++                'status': AUTH_SELECTION_COMPLETION_STATUS,
++                [mechanismId]: response,
++            },
++        };
++
++        await this.replyWithJSON(serviceName, JSON.stringify(reply));
++    }
++
++    _filterAuthMechanisms(mechanismsList, filterFunc) {
++        return mechanismsList.filter(mechanism => {
++            const mapping = DiscreteServiceMechanismDefinitions.find(m =>
++                m.role === mechanism.role);
++
++            if (!mapping)
++                return true;
++
++            if (!mapping.selectable) {
++                if (filterFunc)
++                    filterFunc(mechanism);
++                return false;
++            }
++
++            const enabled = this._settings.get_boolean(mapping.setting);
++
++            if (!enabled && filterFunc)
++                filterFunc(mechanism);
++
++            return enabled;
++        });
++    }
++
++    _handleAuthSelection(serviceName, authSelection) {
++        let mechanisms = authSelection.mechanisms;
++        const priorityList = authSelection.priority;
++
++        if (!mechanisms)
++            return;
++
++        const ids = Object.keys(mechanisms);
++        ids.sort((a, b) => {
++            const priorityA = priorityList.indexOf(a);
++            const priorityB = priorityList.indexOf(b);
++
++            if (priorityA !== -1 && priorityB !== -1)
++                return priorityA - priorityB;
++
++            if (priorityA !== -1)
++                return -1;
++
++            if (priorityB !== -1)
++                return 1;
++
++            return 0;
++        });
++
++        let mechanismsList = [];
++        for (const id of ids) {
++            const name = mechanisms[id].name;
++            const role = mechanisms[id].role;
++
++            if (!name || !role)
++                continue;
++
++            const selectable = mechanisms[id].selectable;
++            mechanismsList.push({id, name, role, serviceName, selectable});
++        }
++
++        mechanismsList = this._filterAuthMechanisms(mechanismsList, mechanism => {
++            delete mechanisms[mechanism.id];
++        });
++
++        log(`newly published mechanisms: ${JSON.stringify(mechanisms)}`);
++        this._publishedMechanisms.set(serviceName, mechanisms);
++
++        this.emit('mechanisms-list-changed', serviceName, mechanismsList);
++    }
++
++    _handleAuthMechanismsRequest(serviceName, requestObject) {
++        const authSelection = requestObject['auth-selection'];
++
++        if (authSelection)
++            this._handleAuthSelection(serviceName, authSelection);
++    }
++
++    _onCustomJSONRequest(client, serviceName, protocol, version, json) {
++        this.emit(`service-request::${serviceName}`);
++
++        if (protocol === AUTH_MECHANISM_PROTOCOL) {
++            let requestObject;
++
++            try {
++                requestObject = JSON.parse(json);
++            } catch (e) {
++                logError(e);
++                return;
++            }
++
++            this._handleAuthMechanismsRequest(serviceName, requestObject);
++        }
++    }
++
+     _onInfo(client, serviceName, info) {
+         if (this.serviceIsForeground(serviceName)) {
+             this._queueMessage(serviceName, info, MessageType.INFO);
+@@ -885,12 +1111,17 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _generateMechanismsFromDiscreteServices() {
++        // Unified auth doesn't support all authentication mechanisms (e.g. fingerprint) and also sometimes unified auth
++        // isn't available at all. Fill in the gaps in coverage with mechanisms synthesized from non-unified authentication
++        // services.
++        const unifiedAuthAvailable = this._activeServices.has(UNIFIED_AUTH_SERVICE_NAME) && !this._unavailableServices.has(UNIFIED_AUTH_SERVICE_NAME);
+         for (const definition of DiscreteServiceMechanismDefinitions) {
+             const enabled = this._isDiscreteServiceEnabled(definition);
+             const available = this._activeServices.has(definition.serviceName) && !this._unavailableServices.has(definition.serviceName);
++            const supportedByUnifiedAuth = unifiedAuthAvailable && UNIFIED_AUTH_SUPPORTED_ROLES.includes(definition.role);
+ 
+-            let mechanismsList = [];
+-            if (enabled && available) {
++            const mechanismsList = [];
++            if (enabled && available && !supportedByUnifiedAuth) {
+                 mechanismsList.push({
+                     id: definition.mechanismId,
+                     name: definition.mechanismName,
+@@ -908,6 +1139,10 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._activeServices.clear();
+         this._unavailableServices.clear();
+         this._activeServices.clear();
++        if (this._mechanismsListChangedSignalId) {
++            this.disconnect(this._mechanismsListChangedSignalId);
++            this._mechanismsListChangedSignalId = 0;
++        }
+         this._updateDefaultService();
+ 
+         this.emit('reset');
+@@ -989,6 +1224,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     _onServiceUnavailable(_client, serviceName, errorMessage) {
+         this._unavailableServices.add(serviceName);
+ 
++        if (serviceName === UNIFIED_AUTH_SERVICE_NAME)
++            this._generateMechanismsFromDiscreteServices();
++
+         if (!errorMessage)
+             return;
+ 
+diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
+index dde6bd3..8f1791f 100644
+--- a/js/ui/unlockDialog.js
++++ b/js/ui/unlockDialog.js
+@@ -529,6 +529,7 @@ export const UnlockDialog = GObject.registerClass({
+         try {
+             this._gdmClient.set_enabled_extensions([
+                 Gdm.UserVerifierChoiceList.interface_info().name,
++                Gdm.UserVerifierCustomJSON.interface_info().name,
+             ]);
+         } catch {
+         }
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-Improve-detection-of-unavailable-services.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Improve-detection-of-unavailable-services.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-Improve-detection-of-unavailable-services.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Improve-detection-of-unavailable-services.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,54 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:15:14 +0200
+Subject: gdm: Improve detection of unavailable services
+
+---
+ js/gdm/util.js | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 1ccc57b..dcef546 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -878,8 +878,15 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         }
+     }
+ 
++    _serviceIsAvailable(serviceName) {
++        return GLib.file_test(`/etc/pam.d/${serviceName}`, GLib.FileTest.EXISTS) ||
++            GLib.file_test(`/usr/lib/pam.d/${serviceName}`, GLib.FileTest.EXISTS)
++    }
++
+     _getDetectedDefaultService() {
+-        if (this._settings.get_boolean(UNIFIED_AUTHENTICATION_KEY))
++        if ((!this._settings.settingsSchema.has_key(UNIFIED_AUTHENTICATION_KEY) ||
++             this._settings.get_boolean(UNIFIED_AUTHENTICATION_KEY)) &&
++             this._serviceIsAvailable(UNIFIED_AUTH_SERVICE_NAME))
+             return UNIFIED_AUTH_SERVICE_NAME;
+ 
+         const definition = DiscreteServiceMechanismDefinitions.find(
+@@ -891,6 +898,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         const oldDefaultService = this._defaultService;
+         this._defaultService = this._getDetectedDefaultService();
+ 
++        if (this._unavailableServices.has(this._defaultService))
++            this._defaultService = null;
++
+         if (!this._defaultService) {
+             log('no authentication service is enabled, using password authentication');
+             this._defaultService = PASSWORD_SERVICE_NAME;
+@@ -916,6 +926,14 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             }
+         } catch (e) {
+             this._activeServices.delete(serviceName);
++            if (this.serviceIsForeground(serviceName) &&
++                Gio.DBusError.is_remote_error(e) &&
++                Gio.DBusError.get_remote_error(e) === 'org.gnome.DisplayManager.SessionWorker.Error.ServiceUnavailable') {
++                this._unavailableServices.add(serviceName);
++                this._updateDefaultService();
++                this._beginVerification();
++                return;
++            }
+             if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                 return;
+             if (!this.serviceIsForeground(serviceName)) {
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-Support-more-authentication-mechanism-UIs.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Support-more-authentication-mechanism-UIs.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-Support-more-authentication-mechanism-UIs.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-Support-more-authentication-mechanism-UIs.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,166 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:23:15 +0200
+Subject: gdm: Support more authentication mechanism UIs
+
+---
+ js/gdm/authPrompt.js | 21 ++++++++++++++++++
+ js/gdm/util.js       | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 79 insertions(+), 2 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 7359c84..3d3c5bc 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -88,9 +88,11 @@ export const AuthPrompt = GObject.registerClass({
+ 
+         this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this));
+         this._userVerifier.connect('show-message', this._onShowMessage.bind(this));
++        this._userVerifier.connect('show-waiting-message', this._onWaitingMessage.bind(this));
+         this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this));
+         this._userVerifier.connect('mechanisms-list-changed', this._onAuthMechanismsListChanged.bind(this));
+         this._userVerifier.connect('foreground-mechanism-changed', this._onForegroundMechanismChanged.bind(this));
++        this._userVerifier.connect('choice-list-selected', (_, ...args) => this._onChoiceListSelected(...args));
+         this._userVerifier.connect('web-login', this._onWebLogin.bind(this));
+         this._userVerifier.connect('web-login-time-out', this._onWebLoginTimeOut.bind(this));
+         this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this));
+@@ -371,6 +373,20 @@ export const AuthPrompt = GObject.registerClass({
+         this.emit('prompted');
+     }
+ 
++    _onWaitingMessage(verifier, serviceName, info) {
++        if (this._queryingService)
++            this.clear();
++
++        this._queryingService = serviceName;
++        this._entry.visible = false;
++        this._message.grab_key_focus();
++        this.setMessage(info, GdmUtil.MessageType.INFO);
++        this.startSpinning();
++        this.emit('prompted');
++
++        this._userVerifier.waitMessageShown(serviceName);
++    }
++
+     _onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) {
+         if (this._queryingService)
+             this.clear();
+@@ -390,6 +406,11 @@ export const AuthPrompt = GObject.registerClass({
+         this.emit('mechanisms-changed', serviceName);
+     }
+ 
++    _onChoiceListSelected(service, key) {
++        if (this._authList?.visible)
++            this._authList.emit('activate', key);
++    }
++
+     _onWebLogin(userVerifier, serviceName, introMessage, linkMessage, uri, code) {
+         const introAlreadyUp = this._queryingService === serviceName;
+ 
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index dcef546..435226c 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -365,6 +365,12 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     selectChoice(serviceName, key) {
++        if (serviceName === UNIFIED_AUTH_SERVICE_NAME &&
++            this._pendingMechanisms.has(Const.CHOICE_LIST_ROLE_NAME)) {
++            this._replyWithAuthSelectionResponse(serviceName, Const.CHOICE_LIST_ROLE_NAME, key);
++            return;
++        }
++
+         this._userVerifierChoiceList.call_select_choice(serviceName, key, this._cancellable, null);
+     }
+ 
+@@ -373,6 +379,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             await this._handlePendingMessages();
+             if (this._pendingMechanisms.has(PASSWORD_ROLE_NAME))
+                 this._replyWithAuthSelectionResponse(serviceName, PASSWORD_ROLE_NAME, {password: answer});
++            else if (this._pendingMechanisms.has(Const.PLAIN_TEXT_ROLE_NAME))
++                this._replyWithAuthSelectionResponse(serviceName, Const.PLAIN_TEXT_ROLE_NAME, {text: answer});
+             else
+                 this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
+         } catch (e) {
+@@ -976,6 +984,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         const roleHandlers = {
+             [WEB_LOGIN_ROLE_NAME]: this._startWebLogin,
+             [PASSWORD_ROLE_NAME]: this._startQuestionLogin,
++            [Const.PLAIN_TEXT_ROLE_NAME]: this._startQuestionLogin,
++            [Const.CHOICE_LIST_ROLE_NAME]: this._startChoiceListSelection,
++            [Const.MESSAGE_ROLE_NAME]: this._startMessageLogin,
+         };
+ 
+         const handler = roleHandlers[mechanism.role];
+@@ -1050,7 +1061,27 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this.emit('show-choice-list', serviceName, promptMessage, list.deepUnpack());
+     }
+ 
+-    _startPasswordLogin(serviceName, mechanismId) {
++    _startChoiceListSelection(serviceName, mechanismId) {
++        const mechanisms = this._publishedMechanisms.get(serviceName);
++
++        if (!mechanisms) {
++            logError(new Error(`Impossible to find mechanisms for ${serviceName}`));
++            return;
++        }
++
++        const mechanism = mechanisms[mechanismId];
++        if (!mechanism) {
++            logError(new Error(`Impossible to find mechanism for ${mechanismId}`));
++            return;
++        }
++
++        const {prompt, choices, role} = mechanism;
++
++        this._pendingMechanisms.set(role, mechanismId);
++        this.emit('show-choice-list', serviceName, prompt, choices);
++    }
++
++    _startQuestionLogin(serviceName, mechanismId) {
+         const mechanisms = this._publishedMechanisms.get(serviceName);
+ 
+         if (!mechanisms)
+@@ -1060,9 +1091,25 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             return;
+ 
+         const {prompt, role} = mechanisms[mechanismId];
++        const secret = role !== Const.PLAIN_TEXT_ROLE_NAME;
+ 
+         this._pendingMechanisms.set(role, mechanismId);
+-        this.emit('ask-question', serviceName, prompt, true);
++        this.emit('ask-question', serviceName, prompt, secret);
++    }
++
++    _startMessageLogin(serviceName, mechanismId) {
++        const mechanisms = this._publishedMechanisms.get(serviceName);
++
++        if (!mechanisms)
++            return;
++
++        if (!mechanisms[mechanismId])
++            return;
++
++        const {prompt, role} = mechanisms[mechanismId];
++
++        this._pendingMechanisms.set(role, mechanismId);
++        this.emit('show-waiting-message', serviceName, prompt);
+     }
+ 
+     _replyWithAuthSelectionResponse(serviceName, role, response) {
+@@ -1140,6 +1187,15 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._replyWithAuthSelectionResponse(serviceName, WEB_LOGIN_ROLE_NAME, {});
+     }
+ 
++    waitMessageShown(serviceName) {
++        const mechanism = this._getPendingMechanismForRole(Const.MESSAGE_ROLE_NAME);
++        const authService = this._unifiedAuthServices.get(mechanism?.protocol);
++        if (!authService)
++            return;
++
++        this._replyWithAuthSelectionResponse(serviceName, Const.MESSAGE_ROLE_NAME, {});
++    }
++
+     _filterAuthMechanisms(mechanismsList, filterFunc) {
+         return mechanismsList.filter(mechanism => {
+             const mapping = DiscreteServiceMechanismDefinitions.find(m =>
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-authPrompt-Fix-key-focus-handling-on-choice-list.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-authPrompt-Fix-key-focus-handling-on-choice-list.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-authPrompt-Fix-key-focus-handling-on-choice-list.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-authPrompt-Fix-key-focus-handling-on-choice-list.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,72 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Fri, 22 Aug 2025 15:08:23 +0200
+Subject: gdm/authPrompt: Fix key focus handling on choice list
+
+When a choice list widget is prompted in GDM we call updateSensitivity()
+but this does not act on the currently visible authentication widget but
+rather on the text entry all the times.
+
+And this causes that when a choice list is shown we always set the key
+focus to the (hidden) text entry.
+
+This implies that when the list is finally shown, moving the arrow keys
+leads makes the entry context menu to be shown instead of being able to
+navigate through the entries.
+
+So, make updateSensitivity use the currently visible authorization
+widget instead and use it to control the sensitivity of the choice list
+
+Origin: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3849
+---
+ js/gdm/authPrompt.js | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 28b0d4c..293abd5 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -700,13 +700,13 @@ export const AuthPrompt = GObject.registerClass({
+         this._authList.set({
+             opacity: 0,
+             visible: true,
+-            reactive: false,
+         });
++        this.updateSensitivity(false);
+         this._authList.ease({
+             opacity: 255,
+             duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
+             transition: Clutter.AnimationMode.EASE_OUT_QUAD,
+-            onComplete: () => (this._authList.reactive = true),
++            onComplete: () => this.updateSensitivity(true),
+         });
+     }
+ 
+@@ -776,18 +776,23 @@ export const AuthPrompt = GObject.registerClass({
+         if (this._webLoginDialog)
+             return;
+ 
+-        if (this._entry.reactive === sensitive)
++        let authWidget = this._entry;
++
++        if (this._authList.visible)
++            authWidget = this._authList;
++
++        if (authWidget.reactive === sensitive)
+             return;
+ 
+-        this._entry.reactive = sensitive;
++        authWidget.reactive = sensitive;
+ 
+         if (sensitive) {
+-            this._entry.grab_key_focus();
++            authWidget.grab_key_focus();
+         } else {
+             this.grab_key_focus();
+ 
+-            if (this._entry === this._passwordEntry)
+-                this._entry.password_visible = false;
++            if (authWidget === this._passwordEntry)
++                authWidget.password_visible = false;
+         }
+     }
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-loginDialog-Bind-login-option-button-visibility-to-me.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-loginDialog-Bind-login-option-button-visibility-to-me.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-loginDialog-Bind-login-option-button-visibility-to-me.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-loginDialog-Bind-login-option-button-visibility-to-me.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,33 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:09:30 +0200
+Subject: gdm/loginDialog: Bind login option button visibility to mechanisms
+
+---
+ js/gdm/loginDialog.js | 1 +
+ js/ui/unlockDialog.js | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index 24c2538..486b281 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -1130,6 +1130,7 @@ export const LoginDialog = GObject.registerClass({
+         const activeMechanism = this._loginOptionsButton.getActiveItem();
+ 
+         this._loginOptionsButton.clearItems({serviceName});
++        this._loginOptionsButton.updateSensitivity(mechanisms.length !== 0);
+         if (mechanisms.length === 0)
+             return;
+ 
+diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
+index 9e0827f..76301e7 100644
+--- a/js/ui/unlockDialog.js
++++ b/js/ui/unlockDialog.js
+@@ -878,6 +878,7 @@ export const UnlockDialog = GObject.registerClass({
+         const activeMechanism = this._loginOptionsButton.getActiveItem();
+ 
+         this._loginOptionsButton.clearItems({serviceName});
++        this._loginOptionsButton.updateSensitivity(mechanisms.length !== 0);
+         if (mechanisms.length === 0)
+             return;
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-loginDialog-Fix-cancel-button-visibility.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-loginDialog-Fix-cancel-button-visibility.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-loginDialog-Fix-cancel-button-visibility.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-loginDialog-Fix-cancel-button-visibility.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,57 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:09:57 +0200
+Subject: gdm/loginDialog: Fix cancel button visibility
+
+---
+ js/gdm/authPrompt.js  | 7 +++++++
+ js/gdm/loginDialog.js | 4 ++--
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index e61bbb9..7359c84 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -55,6 +55,12 @@ export const AuthPrompt = GObject.registerClass({
+         'mechanisms-changed': {param_types: [GObject.TYPE_STRING]},
+         'reset': {param_types: [GObject.TYPE_UINT]},
+     },
++    Properties: {
++        'verification-status': GObject.ParamSpec.uint(
++            'verification-status', 'verification-status', 'verification-status',
++            GObject.ParamFlags.READWRITE,
++            AuthPromptStatus.NOT_VERIFYING, AuthPromptStatus.VERIFICATION_IN_PROGRESS, 0),
++    },
+ }, class AuthPrompt extends St.BoxLayout {
+     _init(gdmClient, mode) {
+         super._init({
+@@ -820,6 +826,7 @@ export const AuthPrompt = GObject.registerClass({
+         this.cancelButton.reactive = this._hasCancelButton;
+         this.cancelButton.can_focus = this._hasCancelButton;
+         this._preemptiveAnswer = null;
++        this.cancelButton.opacity = this._hasCancelButton ? 255 : 0;
+ 
+         if (this._userVerifier)
+             this._userVerifier.cancel();
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index 486b281..a16f0e5 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -592,6 +592,8 @@ export const LoginDialog = GObject.registerClass({
+         this._authPrompt.connect('prompted', this._onPrompted.bind(this));
+         this._authPrompt.connect('reset', this._onReset.bind(this));
+         this._authPrompt.connect('mechanisms-changed', this._onMechanismsChanged.bind(this));
++        this._authPrompt.connectObject('notify::verification-status',
++            () => this._updateCancelButton(), this);
+         this._authPrompt.hide();
+         this.add_child(this._authPrompt);
+ 
+@@ -1215,9 +1217,7 @@ export const LoginDialog = GObject.registerClass({
+                 this._user = this._userManager.get_user(answer);
+                 this._authPrompt.clear();
+                 this._beginVerification({userName: answer});
+-                this._updateCancelButton();
+             });
+-        this._updateCancelButton();
+ 
+         this._sessionMenuButton.updateSensitivity(false);
+         this._authPrompt.updateSensitivity(true);
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-util-Add-new-background-message-type.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Add-new-background-message-type.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-util-Add-new-background-message-type.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Add-new-background-message-type.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,22 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Fri, 9 Feb 2024 16:39:13 -0500
+Subject: gdm/util: Add new "background" message type
+
+This commit adds a new "background" message type for messages that
+should appear in the footer.
+---
+ js/gdm/util.js | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index e28445d..569ece1 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -103,6 +103,7 @@ export const MessageType = {
+     HINT: 1,
+     INFO: 2,
+     ERROR: 3,
++    BACKGROUND: 4,
+ };
+ 
+ const FingerprintReaderType = {
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-util-Catch-errors-on-re-authentication.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Catch-errors-on-re-authentication.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-util-Catch-errors-on-re-authentication.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Catch-errors-on-re-authentication.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,21 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Tue, 27 Feb 2024 23:34:13 +0100
+Subject: gdm/util: Catch errors on re-authentication
+
+---
+ js/gdm/util.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 569ece1..e76055e 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -230,7 +230,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         // If possible, reauthenticate an already running session,
+         // so any session specific credentials get updated appropriately
+         if (userName)
+-            this._openReauthenticationChannel(userName);
++            this._openReauthenticationChannel(userName).catch(logError);
+         else
+             this._getUserVerifier();
+     }
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-util-Emit-service-request-signal-on-every-request.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Emit-service-request-signal-on-every-request.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-util-Emit-service-request-signal-on-every-request.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Emit-service-request-signal-on-every-request.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,66 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 14:15:12 -0500
+Subject: gdm/util: Emit "service-request" signal on every request
+
+GDM doesn't provide any sort of "ack" when a users input
+has been acknowledged by the underlying PAM conversation.
+
+We know when a bit of input has been processed when the next
+bit of input or output gets requested (or authentication succeeds).
+
+This commit emits a signal any time there is such a request so it
+can be connected to (which will later be used to know when to close
+a dialog used for Web Login)
+---
+ js/gdm/util.js | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 296d2ac..e5b469b 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -913,6 +913,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onChoiceListQuery(client, serviceName, promptMessage, list) {
++        this.emit(`service-request::${serviceName}`);
++
+         if (!this.serviceIsForeground(serviceName))
+             return;
+ 
+@@ -1042,6 +1044,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onInfo(client, serviceName, info) {
++        this.emit(`service-request::${serviceName}`);
++
+         if (this.serviceIsForeground(serviceName)) {
+             this._queueMessage(serviceName, info, MessageType.INFO);
+         } else if (this.serviceIsFingerprint(serviceName)) {
+@@ -1063,6 +1067,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onProblem(client, serviceName, problem) {
++        this.emit(`service-request::${serviceName}`);
++
+         const isFingerprint = this.serviceIsFingerprint(serviceName);
+ 
+         if (!this.serviceIsForeground(serviceName) && !isFingerprint)
+@@ -1099,6 +1105,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onInfoQuery(client, serviceName, question) {
++        this.emit(`service-request::${serviceName}`);
++
+         if (!this.serviceIsForeground(serviceName))
+             return;
+ 
+@@ -1106,6 +1114,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onSecretInfoQuery(client, serviceName, secretQuestion) {
++        this.emit(`service-request::${serviceName}`);
++
+         if (!this.serviceIsForeground(serviceName))
+             return;
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-util-Figure-out-default-service-from-service-definiti.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Figure-out-default-service-from-service-definiti.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-util-Figure-out-default-service-from-service-definiti.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-util-Figure-out-default-service-from-service-definiti.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,61 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Mon, 12 Feb 2024 14:57:44 -0500
+Subject: gdm/util: Figure out default service from service definitions
+
+Now that we have a list of service definitions mapped to what
+sort of authentication they can do, conveniently already in
+the order of preferred default services, there's no reason
+we shouldn't use it for updating the default service.
+
+This commit changes the code to do that instead of the
+long if/else if code that was there before
+---
+ js/gdm/util.js | 25 ++++++++++++++++++-------
+ 1 file changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index ae085349..296d2ac 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -62,6 +62,7 @@ const USER_READ_TIME = 48;
+ const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
+ const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
+ 
++// Note these are specified in order of preference
+ const DiscreteServiceMechanismDefinitions = [
+     {
+         serviceName: PASSWORD_SERVICE_NAME,
+@@ -764,16 +765,26 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             this._cancelAndReset();
+     }
+ 
++    _isDiscreteServiceEnabled(definition) {
++        switch (definition.serviceName) {
++        case PASSWORD_SERVICE_NAME:
++            return this._settings.get_boolean(definition.setting);
++        case FINGERPRINT_SERVICE_NAME:
++            return this._fingerprintReaderType !== FingerprintReaderType.NONE;
++        case SMARTCARD_SERVICE_NAME:
++            return !!this._smartcardManager;
++        default:
++            return false;
++        }
++    }
++
+     _getDetectedDefaultService() {
+         if (this._settings.get_boolean(UNIFIED_AUTHENTICATION_KEY))
+             return UNIFIED_AUTH_SERVICE_NAME;
+-        else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
+-            return PASSWORD_SERVICE_NAME;
+-        else if (this._smartcardManager)
+-            return SMARTCARD_SERVICE_NAME;
+-        else if (this._fingerprintReaderType !== FingerprintReaderType.NONE)
+-            return FINGERPRINT_SERVICE_NAME;
+-        return null;
++
++        const definition = DiscreteServiceMechanismDefinitions.find(
++            def => this._isDiscreteServiceEnabled(def));
++        return definition?.serviceName ?? null;
+     }
+ 
+     _updateDefaultService() {
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-webLogin-Improve-label-rendering.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-webLogin-Improve-label-rendering.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-webLogin-Improve-label-rendering.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-webLogin-Improve-label-rendering.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,41 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:26:23 +0200
+Subject: gdm/webLogin: Improve label rendering
+
+---
+ js/gdm/webLogin.js | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/js/gdm/webLogin.js b/js/gdm/webLogin.js
+index 65b10e6..19f3ac2 100644
+--- a/js/gdm/webLogin.js
++++ b/js/gdm/webLogin.js
+@@ -7,6 +7,7 @@ import Clutter from 'gi://Clutter';
+ import GObject from 'gi://GObject';
+ import Gio from 'gi://Gio';
+ import Shell from 'gi://Shell';
++import Pango from 'gi://Pango';
+ import St from 'gi://St';
+ import {Spinner} from '../ui/animation.js';
+ import * as ModalDialog from '../ui/modalDialog.js';
+@@ -120,6 +121,10 @@ class WebLoginPrompt extends St.BoxLayout {
+             text: message,
+             style_class: 'web-login-title-label',
+         });
++        this._urlTitleLabel.clutterText.set({
++            lineWrap: true,
++            ellipsize: Pango.EllipsizeMode.NONE,
++        });
+         this.add_child(this._urlTitleLabel);
+ 
+         this._qrCode = new QrCode({iconSize, url});
+@@ -157,6 +162,9 @@ class WebLoginPrompt extends St.BoxLayout {
+         const http = 'http://';
+         const https = 'https://';
+ 
++        if (url.endsWith('/'))
++            url = url.substring(0, url.length-1);
++
+         if (url.startsWith(http))
+             return url.substring(http.length);
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/gdm-webLogin-Support-custom-buttons-and-auto-ack.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-webLogin-Support-custom-buttons-and-auto-ack.patch
--- 49.0-1/debian/patches/ubuntu-authd/gdm-webLogin-Support-custom-buttons-and-auto-ack.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/gdm-webLogin-Support-custom-buttons-and-auto-ack.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,196 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 11 Sep 2024 02:25:28 +0200
+Subject: gdm/webLogin: Support custom buttons and auto-ack
+
+---
+ js/gdm/authPrompt.js | 41 +++++++++++++++++++++++------------------
+ js/gdm/util.js       | 21 +++++++++++++++++++--
+ js/gdm/webLogin.js   | 17 +++++++++++++----
+ 3 files changed, 55 insertions(+), 24 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 3d3c5bc..28b0d4c 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -411,7 +411,7 @@ export const AuthPrompt = GObject.registerClass({
+             this._authList.emit('activate', key);
+     }
+ 
+-    _onWebLogin(userVerifier, serviceName, introMessage, linkMessage, uri, code) {
++    _onWebLogin(userVerifier, serviceName, introMessage, linkMessage, uri, code, autoAck, customButton) {
+         const introAlreadyUp = this._queryingService === serviceName;
+ 
+         if (this._queryingService)
+@@ -439,14 +439,14 @@ export const AuthPrompt = GObject.registerClass({
+                 if (this._webLoginTimedOut)
+                     this._refreshWebLogin(serviceName);
+                 else
+-                    this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code);
++                    this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code, autoAck, customButton);
+             });
+             this._webLoginPromptWell.add_child(this._webLoginIntro);
+             this._webLoginPromptWell.visible = true;
+ 
+             this.updateSensitivity(true);
+         } else {
+-            this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code);
++            this._openWebLoginDialog(userVerifier, serviceName, linkMessage, uri, code, autoAck, customButton);
+         }
+ 
+         this._webLoginTimedOut = false;
+@@ -477,7 +477,7 @@ export const AuthPrompt = GObject.registerClass({
+         this._webLoginDialog = null;
+     }
+ 
+-    _openWebLoginDialog(userVerifier, serviceName, message, url, code) {
++    _openWebLoginDialog(userVerifier, serviceName, message, url, code, autoAck, customButton) {
+         if (this._queryingService)
+             this.clear();
+ 
+@@ -487,26 +487,31 @@ export const AuthPrompt = GObject.registerClass({
+ 
+         this._webLoginPromptWell.remove_all_children();
+ 
+-        this._webLoginDialog = new WebLogin.WebLoginDialog({message, url, code});
+-        this._webLoginDialog.open(global.get_current_time());
++        this._webLoginDialog = new WebLogin.WebLoginDialog({message, url, code, autoAck, customButton});
+         this._webLoginDialog.connect('cancel', () => {
++            if (userVerifier.cancelRequested()) {
++                this._closeWebLoginDialog();
++                return;
++            }
+             if (this.verificationStatus !== AuthPromptStatus.VERIFICATION_SUCCEEDED)
+                 this.reset();
+         });
+-        this._webLoginDialog.connect('done', () => {
+-            userVerifier.connectObject(
+-                `service-request::${serviceName}`, this._closeWebLoginDialog.bind(this),
++        this._webLoginDialog.connect('done', () =>
++            userVerifier.webLoginDone(serviceName))
++        this._webLoginDialog.connect('opened', () =>
++            userVerifier.webLoginOpened(serviceName))
+ 
+-                'verification-complete', this._closeWebLoginDialog.bind(this),
++        this._webLoginDialog.open(global.get_current_time());
+ 
+-                'verification-failed', () => {
+-                    this._closeWebLoginDialog();
+-                    this.showLoginFailedNotification();
+-                    this.reset();
+-                },
+-                this);
+-            userVerifier.webLoginDone(serviceName);
+-        });
++        userVerifier.connectObject(
++            `service-request::${serviceName}`, this._closeWebLoginDialog.bind(this),
++            'verification-complete', this._closeWebLoginDialog.bind(this),
++            'verification-failed', () => {
++                this._closeWebLoginDialog();
++                this.showLoginFailedNotification();
++                this.reset();
++            },
++            this._webLoginDialog);
+ 
+         if (this._spinner)
+             this._spinner.stop();
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 435226c..43fe656 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -1143,7 +1143,11 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             return;
+         }
+ 
+-        const {init_prompt: initPrompt, link_prompt: linkPrompt, uri, code, role, timeout} = mechanisms[mechanismId];
++        const {
++            init_prompt: initPrompt,
++            link_prompt: linkPrompt,
++            uri, code, role, timeout, autoAck, customButton,
++        } = mechanisms[mechanismId];
+ 
+         if (!linkPrompt || !uri)
+             return;
+@@ -1158,7 +1162,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         }
+ 
+         this._pendingMechanisms.set(role, mechanismId);
+-        this.emit('web-login', serviceName, initPrompt, linkPrompt, uri, code);
++        this.emit('web-login', serviceName, initPrompt, linkPrompt, uri, code, autoAck, customButton);
+     }
+ 
+     _clearWebLoginTimeout() {
+@@ -1180,6 +1184,19 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._clearWebLoginTimeout();
+ 
+         const mechanism = this._getPendingMechanismForRole(WEB_LOGIN_ROLE_NAME);
++        if (mechanism?.autoAck)
++            return;
++        const authService = this._unifiedAuthServices.get(mechanism?.protocol);
++        if (!authService)
++            return;
++
++        this._replyWithAuthSelectionResponse(serviceName, WEB_LOGIN_ROLE_NAME, {});
++    }
++
++    webLoginOpened(serviceName) {
++        const mechanism = this._getPendingMechanismForRole(WEB_LOGIN_ROLE_NAME);
++        if (!mechanism?.autoAck)
++            return;
+         const authService = this._unifiedAuthServices.get(mechanism?.protocol);
+         if (!authService)
+             return;
+diff --git a/js/gdm/webLogin.js b/js/gdm/webLogin.js
+index 2795c3d..65b10e6 100644
+--- a/js/gdm/webLogin.js
++++ b/js/gdm/webLogin.js
+@@ -174,10 +174,12 @@ export const WebLoginDialog = GObject.registerClass({
+     },
+ }, class WebLoginDialog extends ModalDialog.ModalDialog {
+     _init(params) {
+-        const {message, url, code} = Params.parse(params, {
++        const {message, url, code, autoAck, customButton} = Params.parse(params, {
+             message: null,
+             url: null,
+             code: null,
++            autoAck: false,
++            customButton: null,
+         });
+ 
+         super._init({
+@@ -193,7 +195,7 @@ export const WebLoginDialog = GObject.registerClass({
+         this.contentLayout.reactive = false;
+         this.contentLayout.can_focus = false;
+         this.contentLayout.add_child(this._webLoginPrompt);
+-        this._updateButtons();
++        this._updateButtons(autoAck, customButton);
+ 
+         this._contentOverlay = new St.Widget({
+             layout_manager: new Clutter.BinLayout(),
+@@ -233,18 +235,25 @@ export const WebLoginDialog = GObject.registerClass({
+         this.emit('done');
+     }
+ 
+-    _updateButtons() {
++    _updateButtons(autoAck, customButton) {
+         this.clearButtons();
+ 
+         this._cancelButton = this.addButton({
+             action: this.cancel.bind(this),
+             label: _('Cancel'),
+             key: Clutter.KEY_Escape,
++            default: !autoAck && !customButton?.default,
+         });
+ 
++        if (customButton)
++            this.addButton(customButton);
++
++        if (autoAck)
++            return;
++
+         this._doneButton = this.addButton({
+             action: this.done.bind(this),
+-            default: true,
++            default: !customButton?.default,
+             label: _('Done'),
+         });
+     }
diff -pruN 49.0-1/debian/patches/ubuntu-authd/js-Minify-the-authd-protocol-during-build.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/js-Minify-the-authd-protocol-during-build.patch
--- 49.0-1/debian/patches/ubuntu-authd/js-Minify-the-authd-protocol-during-build.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/js-Minify-the-authd-protocol-during-build.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,74 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 29 Feb 2024 22:38:26 +0100
+Subject: js: Minify the authd protocol during build
+
+---
+ js/gdm/authd.js               |  2 +-
+ js/gdm/meson.build            | 12 ++++++++++++
+ js/js-resources.gresource.xml |  2 +-
+ js/meson.build                |  3 ++-
+ 4 files changed, 16 insertions(+), 3 deletions(-)
+ create mode 100644 js/gdm/meson.build
+
+diff --git a/js/gdm/authd.js b/js/gdm/authd.js
+index 3395bb3..24e3e58 100644
+--- a/js/gdm/authd.js
++++ b/js/gdm/authd.js
+@@ -2,7 +2,7 @@
+ import GLib from 'gi://GLib';
+ 
+ import {MessageType} from './util.js';
+-import {authd as Authd, gdm as AuthdGdm, pam as AuthdPam} from './authdProtocol.js';
++import {authd as Authd, gdm as AuthdGdm, pam as AuthdPam} from './authdProtocol.min.js';
+ import {UnifiedAuthService} from './unifiedMechanism.js';
+ import * as Const from './const.js';
+ 
+diff --git a/js/gdm/meson.build b/js/gdm/meson.build
+new file mode 100644
+index 0000000..858cc16
+--- /dev/null
++++ b/js/gdm/meson.build
+@@ -0,0 +1,12 @@
++minified_authd_proto_js =   custom_target('authd-min-proto',
++  input: files('authdProtocol.js'),
++  output: '@BASENAME@.min.js',
++  command: [
++    find_program('terser', required: true),
++    '--compress',
++    '--mangle',
++    '--',
++    '@INPUT@',
++  ],
++  capture: true,
++)
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index d57b68e..17f9451 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -16,7 +16,7 @@
+     <file>gdm/webLogin.js</file>
+ 
+     <file>gdm/authd.js</file>
+-    <file>gdm/authdProtocol.js</file>
++    <file>gdm/authdProtocol.min.js</file>
+ 
+     <file>extensions/extension.js</file>
+     <file>extensions/sharedInternals.js</file>
+diff --git a/js/meson.build b/js/meson.build
+index e594e23..5dcd50b 100644
+--- a/js/meson.build
++++ b/js/meson.build
+@@ -1,11 +1,12 @@
+ subdir('misc')
+ subdir('dbusServices')
++subdir('gdm')
+ 
+ js_resources = gnome.compile_resources(
+   'js-resources', 'js-resources.gresource.xml',
+   source_dir: ['.', meson.current_build_dir()],
+   c_name: 'shell_js_resources',
+-  dependencies: [config_js]
++  dependencies: [config_js, minified_authd_proto_js]
+ )
+ 
+ if have_portal_helper
diff -pruN 49.0-1/debian/patches/ubuntu-authd/js-gdm-util-Add-a-gsettings-to-disable-the-authd-in-gdm.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/js-gdm-util-Add-a-gsettings-to-disable-the-authd-in-gdm.patch
--- 49.0-1/debian/patches/ubuntu-authd/js-gdm-util-Add-a-gsettings-to-disable-the-authd-in-gdm.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/js-gdm-util-Add-a-gsettings-to-disable-the-authd-in-gdm.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,63 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Thu, 29 Feb 2024 23:33:23 +0100
+Subject: js/gdm/util: Add a gsettings to disable the authd in gdm
+
+---
+ data/com.ubuntu.login-screen.gschema.xml.in | 10 ++++++++++
+ js/gdm/util.js                              |  9 +++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/data/com.ubuntu.login-screen.gschema.xml.in b/data/com.ubuntu.login-screen.gschema.xml.in
+index ee8b3a2..136d67a 100644
+--- a/data/com.ubuntu.login-screen.gschema.xml.in
++++ b/data/com.ubuntu.login-screen.gschema.xml.in
+@@ -66,5 +66,15 @@
+         It overrides the value defined in the default style sheet.
+       </description>
+     </key>
++    <key name="enable-authd-authentication" type="b">
++      <default>true</default>
++      <summary>
++        Enable AuthD authentication.
++      </summary>
++      <description>
++        A pluggable authentication system for external brokers.
++        This key allows to toggle its support in gdm.
++      </description>
++    </key>
+   </schema>
+ </schemalist>
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index 0fd61ca..d1f1386 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -49,6 +49,9 @@ export const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
+ export const BANNER_MESSAGE_PATH_KEY = 'banner-message-path';
+ export const ALLOWED_FAILURES_KEY = 'allowed-failures';
+ 
++export const UBUNTU_LOGIN_SCREEN_SCHEMA = 'com.ubuntu.login-screen';
++export const UBUNTU_AUTHD_AUTHENTICATION_KEY = 'enable-authd-authentication';
++
+ export const LOGO_KEY = 'logo';
+ export const DISABLE_USER_LIST_KEY = 'disable-user-list';
+ 
+@@ -177,6 +180,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+         this._settings = new Gio.Settings({schema_id: LOGIN_SCREEN_SCHEMA});
+         this._settings.connect('changed', () => this._onSettingsChanged());
++        this._ubuntuSettings = new Gio.Settings({schema_id: UBUNTU_LOGIN_SCREEN_SCHEMA});
++        this._ubuntuSettings.connectObject('changed', () => this._onSettingsChanged(), this);
+         this._updateEnabledServices();
+         this._updateDefaultService();
+ 
+@@ -899,6 +904,10 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+              this._serviceIsAvailable(UNIFIED_AUTH_SERVICE_NAME))
+             return UNIFIED_AUTH_SERVICE_NAME;
+ 
++        if (this._ubuntuSettings.get_boolean(UBUNTU_AUTHD_AUTHENTICATION_KEY) &&
++            this._serviceIsAvailable(UNIFIED_AUTH_SERVICE_NAME))
++            return UNIFIED_AUTH_SERVICE_NAME;
++
+         const definition = DiscreteServiceMechanismDefinitions.find(
+             def => this._isDiscreteServiceEnabled(def));
+         return definition?.serviceName ?? null;
diff -pruN 49.0-1/debian/patches/ubuntu-authd/loginDialog-Add-Login-Options-button.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Add-Login-Options-button.patch
--- 49.0-1/debian/patches/ubuntu-authd/loginDialog-Add-Login-Options-button.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Add-Login-Options-button.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,418 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 13:40:26 -0500
+Subject: loginDialog: Add Login Options button
+
+This commit maps the various auth services to their roles, and
+calls this mapping an "auth mechanism". This list of auth
+mechanisms is used to populate a new Login Options menu, as
+speced out in the design here:
+
+https://gitlab.gnome.org/Teams/Design/os-mockups/-/blob/master/lock-login/login-authentication.png
+---
+ js/gdm/authPrompt.js  |  19 ++++++++
+ js/gdm/loginDialog.js |  50 ++++++++++++++++++++
+ js/gdm/util.js        | 126 ++++++++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 186 insertions(+), 9 deletions(-)
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 833b35b..eb28eb0 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -50,6 +50,7 @@ export const AuthPrompt = GObject.registerClass({
+         'failed': {},
+         'next': {},
+         'prompted': {},
++        'mechanisms-changed': {param_types: [GObject.TYPE_STRING]},
+         'reset': {param_types: [GObject.TYPE_UINT]},
+     },
+ }, class AuthPrompt extends St.BoxLayout {
+@@ -80,6 +81,8 @@ export const AuthPrompt = GObject.registerClass({
+         this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this));
+         this._userVerifier.connect('show-message', this._onShowMessage.bind(this));
+         this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this));
++        this._userVerifier.connect('mechanisms-list-changed', this._onAuthMechanismsListChanged.bind(this));
++        this._userVerifier.connect('foreground-mechanism-changed', this._onForegroundMechanismChanged.bind(this));
+         this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this));
+         this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
+         this._userVerifier.connect('reset', this._onReset.bind(this));
+@@ -88,6 +91,7 @@ export const AuthPrompt = GObject.registerClass({
+         this.smartcardDetected = this._userVerifier.smartcardDetected;
+ 
+         this.connect('destroy', this._onDestroy.bind(this));
++        this.mechanisms = new Map();
+ 
+         this._userWell = new St.Bin({
+             x_expand: true,
+@@ -354,6 +358,11 @@ export const AuthPrompt = GObject.registerClass({
+         this.emit('prompted');
+     }
+ 
++    _onAuthMechanismsListChanged(userVerifier, serviceName, mechanismsList) {
++        this.mechanisms.set(serviceName, mechanismsList);
++        this.emit('mechanisms-changed', serviceName);
++    }
++
+     _onCredentialManagerAuthenticated() {
+         if (this.verificationStatus !== AuthPromptStatus.VERIFICATION_SUCCEEDED)
+             this.reset();
+@@ -379,6 +388,11 @@ export const AuthPrompt = GObject.registerClass({
+             this.reset();
+     }
+ 
++    _onForegroundMechanismChanged() {
++        if (this.verificationStatus !== AuthPromptStatus.VERIFICATION_SUCCEEDED)
++            this.reset({beginRequestType: BeginRequestType.REUSE_USERNAME});
++    }
++
+     _onShowMessage(_userVerifier, serviceName, message, type) {
+         let wiggleParameters = {duration: 0};
+ 
+@@ -631,6 +645,11 @@ export const AuthPrompt = GObject.registerClass({
+             this._updateEntry(false);
+     }
+ 
++    setForegroundMechanism(mechanism) {
++        this._userVerifier.setForegroundService(mechanism.serviceName);
++        this._userVerifier.setForegroundMechanism(mechanism);
++    }
++
+     reset(params) {
+         let {beginRequestType, queryingService} = Params.parse(params, {
+             beginRequestType: null,
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index ca76132..b3a4a3f 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -588,6 +588,7 @@ export const LoginDialog = GObject.registerClass({
+         this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
+         this._authPrompt.connect('prompted', this._onPrompted.bind(this));
+         this._authPrompt.connect('reset', this._onReset.bind(this));
++        this._authPrompt.connect('mechanisms-changed', this._onMechanismsChanged.bind(this));
+         this._authPrompt.hide();
+         this.add_child(this._authPrompt);
+ 
+@@ -641,6 +642,7 @@ export const LoginDialog = GObject.registerClass({
+         });
+         this.add_child(this._bottomButtonGroup);
+ 
++        this._createLoginOptionsButton();
+         this._createSessionMenuButton();
+ 
+         this._a11yMenuButton = new A11yMenuButton();
+@@ -677,6 +679,21 @@ export const LoginDialog = GObject.registerClass({
+             this._updateDisableUserList.bind(this), this);
+     }
+ 
++    _createLoginOptionsButton() {
++        this._loginOptionsButton = new AuthMenuButton.AuthMenuButton({
++            title: _('Login Options'),
++            iconName: 'dialog-password-symbolic',
++        });
++        this._loginOptionsButton.connect('active-item-changed',
++            () => {
++                const activeMechanism = this._loginOptionsButton.getActiveItem();
++                if (activeMechanism)
++                    this._authPrompt.setForegroundMechanism(activeMechanism);
++            });
++        this._loginOptionsButton.show();
++        this._bottomButtonGroup.add_child(this._loginOptionsButton);
++    }
++
+     _createSessionMenuButton() {
+         this._sessionMenuButton = new AuthMenuButton.AuthMenuButton({
+             title: _('Session'),
+@@ -1104,6 +1121,34 @@ export const LoginDialog = GObject.registerClass({
+         }
+     }
+ 
++    _onMechanismsChanged(authPrompt, serviceName) {
++        const mechanisms = Array.from(authPrompt.mechanisms.get(serviceName));
++
++        const activeMechanism = this._loginOptionsButton.getActiveItem();
++
++        this._loginOptionsButton.clearItems({serviceName});
++        if (mechanisms.length === 0)
++            return;
++
++        const defaultId = mechanisms[0].id;
++
++        mechanisms.sort((a, b) => a.name.localeCompare(b.name));
++
++        for (const {role, id, name, selectable} of mechanisms) {
++            if (!selectable)
++                continue;
++
++            const mechanism = {serviceName, id, name, role};
++            this._loginOptionsButton.addItem(mechanism);
++
++            const wasActive = activeMechanism?.id === id;
++            const isDefault = id === defaultId;
++
++            if (wasActive || (!activeMechanism && isDefault))
++                this._loginOptionsButton.setActiveItem(mechanism);
++        }
++    }
++
+     _onDefaultSessionChanged(client, sessionId) {
+         this._sessionMenuButton.setActiveItem({id: sessionId});
+     }
+@@ -1466,6 +1511,8 @@ export const LoginDialog = GObject.registerClass({
+         });
+ 
+         this._authPrompt.begin(params);
++
++        this._loginOptionsButton.updateSensitivity(true);
+     }
+ 
+     _setUserListExpanded(expanded) {
+@@ -1474,6 +1521,7 @@ export const LoginDialog = GObject.registerClass({
+     }
+ 
+     _hideUserList() {
++        this._loginOptionsButton.clearItems();
+         this._setUserListExpanded(false);
+         if (this._userSelectionBox.visible)
+             GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
+@@ -1493,6 +1541,8 @@ export const LoginDialog = GObject.registerClass({
+         this._ensureUserListLoaded();
+         this._authPrompt.hide();
+         this._hideBannerView();
++        this._loginOptionsButton.clearItems();
++        this._loginOptionsButton.updateSensitivity(false);
+         this._sessionMenuButton.updateSensitivity(false);
+         this._setUserListExpanded(true);
+         this._notListedButton.show();
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index f6b797c..dd8f4b0 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -28,6 +28,10 @@ export const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+ export const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+ const CLONE_FADE_ANIMATION_TIME = 250;
+ 
++export const PASSWORD_ROLE_NAME = 'password';
++export const SMARTCARD_ROLE_NAME = 'smartcard';
++export const FINGERPRINT_ROLE_NAME = 'fingerprint';
++
+ export const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
+ export const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
+ export const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
+@@ -46,6 +50,35 @@ const USER_READ_TIME = 48;
+ const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
+ const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
+ 
++const DiscreteServiceMechanismDefinitions = [
++    {
++        serviceName: PASSWORD_SERVICE_NAME,
++        mechanismId: 'password',
++        mechanismName: _('Password'),
++        setting: PASSWORD_AUTHENTICATION_KEY,
++        role: PASSWORD_ROLE_NAME,
++        selectable: true,
++    },
++
++    {
++        serviceName: SMARTCARD_SERVICE_NAME,
++        mechanismId: 'smartcard',
++        mechanismName: _('Smartcard'),
++        setting: SMARTCARD_AUTHENTICATION_KEY,
++        role: SMARTCARD_ROLE_NAME,
++        selectable: true,
++    },
++
++    {
++        serviceName: FINGERPRINT_SERVICE_NAME,
++        mechanismId: 'fingerprint',
++        mechanismName: _('Fingerprint'),
++        setting: FINGERPRINT_AUTHENTICATION_KEY,
++        role: FINGERPRINT_ROLE_NAME,
++        selectable: false,
++    },
++];
++
+ /**
+  * Keep messages in order by priority
+  *
+@@ -108,6 +141,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+         this._defaultService = null;
+         this._preemptingService = null;
++        this._foregroundMechanism = null;
+         this._fingerprintReaderType = FingerprintReaderType.NONE;
+ 
+         this._messageQueue = [];
+@@ -116,6 +150,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._failCounter = 0;
+         this._activeServices = new Set();
+         this._unavailableServices = new Set();
++        this._activeServices = new Set();
++        this._pendingMechanisms = new Map();
+ 
+         this._credentialManagers = {};
+ 
+@@ -194,6 +230,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _clearUserVerifier() {
++        this._pendingMechanisms.clear();
+         if (this._userVerifier) {
+             this._disconnectSignals();
+             this._userVerifier.run_dispose();
+@@ -447,7 +484,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _onCredentialManagerAuthenticated(credentialManager, _token) {
+-        this._preemptingService = credentialManager.service;
++        this.setForegroundService(credentialManager.service);
+         this.emit('credential-manager-authenticated');
+     }
+ 
+@@ -483,9 +520,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             this.smartcardDetected = smartcardDetected;
+ 
+             if (this.smartcardDetected)
+-                this._preemptingService = SMARTCARD_SERVICE_NAME;
++                this.setForegroundService(SMARTCARD_SERVICE_NAME);
+             else if (this._preemptingService === SMARTCARD_SERVICE_NAME)
+-                this._preemptingService = null;
++                this.setForegroundService(null);
+ 
+             this.emit('smartcard-status-changed');
+         }
+@@ -587,6 +624,50 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         return this._defaultService;
+     }
+ 
++    setForegroundService(serviceName) {
++        if (this._getForegroundService() === serviceName)
++            return;
++
++        this._preemptingService = serviceName;
++        this.emit('foreground-service-changed');
++    }
++
++    _getForegroundMechanism() {
++        if (this._foregroundMechanism)
++            return this._foregroundMechanism;
++
++        const foregroundService = this._getForegroundService();
++        if (foregroundService === this._defaultService) {
++            const definition = DiscreteServiceMechanismDefinitions.find(
++                def => def.serviceName === foregroundService);
++
++            if (!definition)
++                return null;
++
++            const {mechanismName, mechanismId, role, serviceName} = definition;
++
++            return {name: mechanismName, id: mechanismId, role, serviceName};
++        }
++
++        return null;
++    }
++
++    setForegroundMechanism(mechanism) {
++        const foregroundMechanism = this._getForegroundMechanism();
++
++        this._foregroundMechanism = mechanism;
++
++        if (foregroundMechanism === mechanism)
++            return;
++
++        if (foregroundMechanism?.role === mechanism?.role &&
++            foregroundMechanism?.mechanismName === mechanism?.mechanismName &&
++            foregroundMechanism?.serviceName === mechanism?.serviceName)
++            return;
++
++        this.emit('foreground-mechanism-changed');
++    }
++
+     serviceIsForeground(serviceName) {
+         return serviceName === this._getForegroundService();
+     }
+@@ -667,6 +748,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     async _startService(serviceName) {
++        this._activeServices.add(serviceName);
+         this._hold.acquire();
+         try {
+             this._activeServices.add(serviceName);
+@@ -697,11 +779,6 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._hold.release();
+     }
+ 
+-    _beginVerification() {
+-        this._startService(this._getForegroundService());
+-        this._maybeStartFingerprintVerification().catch(logError);
+-    }
+-
+     async _maybeStartFingerprintVerification() {
+         if (this._userName &&
+             this._fingerprintReaderType !== FingerprintReaderType.NONE &&
+@@ -709,6 +786,17 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             await this._startService(FINGERPRINT_SERVICE_NAME);
+     }
+ 
++    _startBackgroundServices() {
++        this._maybeStartFingerprintVerification().catch(logError);
++    }
++
++    _beginVerification() {
++        const foregroundService = this._getForegroundService();
++
++        this._startService(foregroundService);
++        this._startBackgroundServices();
++    }
++
+     _onChoiceListQuery(client, serviceName, promptMessage, list) {
+         if (!this.serviceIsForeground(serviceName))
+             return;
+@@ -796,11 +884,30 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this.emit('ask-question', serviceName, secretQuestion, true);
+     }
+ 
++    _generateMechanismsFromDiscreteServices() {
++        for (const definition of DiscreteServiceMechanismDefinitions) {
++            const enabled = this._settings.get_boolean(definition.setting);
++            const available = this._activeServices.has(definition.serviceName) && !this._unavailableServices.has(definition.serviceName);
++
++            let mechanismsList = [];
++            if (enabled && available) {
++                mechanismsList.push({
++                    id: definition.mechanismId,
++                    name: definition.mechanismName,
++                    role: definition.role,
++                    selectable: definition.selectable,
++                });
++            }
++            this.emit('mechanisms-list-changed', definition.serviceName, mechanismsList);
++        }
++    }
++
+     _onReset() {
+         // Clear previous attempts to authenticate
+         this._failCounter = 0;
+         this._activeServices.clear();
+         this._unavailableServices.clear();
++        this._activeServices.clear();
+         this._updateDefaultService();
+ 
+         this.emit('reset');
+@@ -895,6 +1002,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+     _onConversationStopped(client, serviceName) {
+         this._activeServices.delete(serviceName);
++        this.emit('mechanisms-list-changed', serviceName, []);
+ 
+         // If the login failed with the preauthenticated oVirt credentials
+         // then discard the credentials and revert to default authentication
+@@ -903,7 +1011,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         const isForeground = this.serviceIsForeground(serviceName);
+         if (isCredentialManager && isForeground) {
+             this._credentialManagers[serviceName].token = null;
+-            this._preemptingService = null;
++            this.setForegroundService(null);
+             this._verificationFailed(serviceName, false);
+             return;
+         }
diff -pruN 49.0-1/debian/patches/ubuntu-authd/loginDialog-Move-authPrompt.begin-to-helper-function.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Move-authPrompt.begin-to-helper-function.patch
--- 49.0-1/debian/patches/ubuntu-authd/loginDialog-Move-authPrompt.begin-to-helper-function.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Move-authPrompt.begin-to-helper-function.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,78 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 12:59:56 -0500
+Subject: loginDialog: Move authPrompt.begin to helper function
+
+Soon, we're going to need to do some other things than firing
+off the auth prompt when beginning verification.
+
+In prepration for that, this commit creates a wrapper function,
+_beginVerification, that can be easily augmented later.
+---
+ js/gdm/loginDialog.js | 19 +++++++++++++++----
+ 1 file changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index 02fb16f..1d203ab 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -37,6 +37,7 @@ import * as LoginManager from '../misc/loginManager.js';
+ import * as Main from '../ui/main.js';
+ import * as MessageTray from '../ui/messageTray.js';
+ import * as ModalDialog from '../ui/modalDialog.js';
++import * as Params from '../misc/params.js';
+ import * as PopupMenu from '../ui/popupMenu.js';
+ import * as Realmd from './realmd.js';
+ import * as UserWidget from '../ui/userWidget.js';
+@@ -1070,7 +1071,7 @@ export const LoginDialog = GObject.registerClass({
+         if (previousUser && beginRequest === AuthPrompt.BeginRequestType.REUSE_USERNAME) {
+             this._user = previousUser;
+             this._authPrompt.setUser(this._user);
+-            this._authPrompt.begin({userName: previousUser.get_user_name()});
++            this._beginVerification({userName: previousUser.get_user_name()});
+         } else if (beginRequest === AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
+             if (!this._disableUserList)
+                 this._showUserList();
+@@ -1142,7 +1143,7 @@ export const LoginDialog = GObject.registerClass({
+                 let answer = this._authPrompt.getAnswer();
+                 this._user = this._userManager.get_user(answer);
+                 this._authPrompt.clear();
+-                this._authPrompt.begin({userName: answer});
++                this._beginVerification({userName: answer});
+                 this._updateCancelButton();
+             });
+         this._updateCancelButton();
+@@ -1436,6 +1437,15 @@ export const LoginDialog = GObject.registerClass({
+         });
+     }
+ 
++    _beginVerification(params) {
++        params = Params.parse(params, {
++            userName: null,
++            hold: null,
++        });
++
++        this._authPrompt.begin(params);
++    }
++
+     _setUserListExpanded(expanded) {
+         this._userList.updateStyle(expanded);
+         this._userSelectionBox.visible = expanded;
+@@ -1454,7 +1464,7 @@ export const LoginDialog = GObject.registerClass({
+ 
+     _hideUserListAndBeginVerification() {
+         this._hideUserList();
+-        this._authPrompt.begin();
++        this._beginVerification();
+     }
+ 
+     _showUserList() {
+@@ -1474,7 +1484,8 @@ export const LoginDialog = GObject.registerClass({
+         let userName = item.user.get_user_name();
+         let hold = new Batch.Hold();
+ 
+-        this._authPrompt.begin({userName, hold});
++        this._beginVerification({userName, hold});
++
+         return hold;
+     }
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/loginDialog-Port-sessions-menu-over-to-AuthMenuButton.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Port-sessions-menu-over-to-AuthMenuButton.patch
--- 49.0-1/debian/patches/ubuntu-authd/loginDialog-Port-sessions-menu-over-to-AuthMenuButton.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/loginDialog-Port-sessions-menu-over-to-AuthMenuButton.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,151 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 11:11:32 -0500
+Subject: loginDialog: Port sessions menu over to AuthMenuButton
+
+Now that AuthMenuButton exists, we should use it.
+
+This commit changes the session menu over to use the new
+control.
+---
+ .../gnome-shell-sass/widgets/_login-lock.scss      | 27 +++++++++++++
+ js/gdm/loginDialog.js                              | 45 ++++++++++++++++------
+ 2 files changed, 60 insertions(+), 12 deletions(-)
+
+diff --git a/data/theme/gnome-shell-sass/widgets/_login-lock.scss b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+index 4ac7c48..1484e98 100644
+--- a/data/theme/gnome-shell-sass/widgets/_login-lock.scss
++++ b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+@@ -37,6 +37,7 @@ $_gdm_dialog_width: 25em;
+   &.a11y-button,
+   &.cancel-button,
+   &.switch-user-button,
++  &.login-dialog-auth-menu-button,
+   &.login-dialog-session-list-button {
+     @extend .icon-button;
+     @extend %system_button;
+@@ -49,6 +50,25 @@ $_gdm_dialog_width: 25em;
+   }
+ }
+ 
++.login-dialog-auth-menu-button-title {
++  @include fontsize($base_font_size - 1);
++  color: darken($_gdm_fg,40%);
++  font-weight: bold;
++  padding-top: $base_padding;
++  padding-bottom: $base_padding * 2;
++  padding-left: $base_padding * 2;
++  padding-right: $base_padding * 2;
++}
++
++.login-dialog-auth-menu-button-popup-menu-box {
++  padding: $base_padding * 3;
++}
++
++.login-dialog-auth-menu-button-item {
++  padding-left: $base_padding * 2;
++  padding-right: $base_padding * 2;
++}
++
+ .login-dialog-button-box {
+   spacing: $base_padding * 2;
+ }
+@@ -72,6 +92,13 @@ $_gdm_dialog_width: 25em;
+   }
+ }
+ 
++.login-dialog-menu-button-box {
++  spacing: $base_padding * 2;
++  padding-left: $base_padding * 2;
++  padding-right: $base_padding * 2;
++  padding-bottom: $base_padding * 2;
++}
++
+ .login-dialog-logo-bin {
+   margin: 3em 0;
+ }
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index 1d203ab..ca76132 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -27,9 +27,9 @@ import Pango from 'gi://Pango';
+ import Shell from 'gi://Shell';
+ import St from 'gi://St';
+ 
++import * as AuthMenuButton from './authMenuButton.js';
+ import * as AuthPrompt from './authPrompt.js';
+ import * as Batch from './batch.js';
+-import * as BoxPointer from '../ui/boxpointer.js';
+ import * as CtrlAltTab from '../ui/ctrlAltTab.js';
+ import * as GdmUtil from './util.js';
+ import * as Layout from '../ui/layout.js';
+@@ -641,14 +641,7 @@ export const LoginDialog = GObject.registerClass({
+         });
+         this.add_child(this._bottomButtonGroup);
+ 
+-        this._sessionMenuButton = new SessionMenuButton();
+-        this._sessionMenuButton.connect('session-activated',
+-            (list, sessionId) => {
+-                this._greeter.call_select_session_sync(sessionId, null);
+-            });
+-        this._sessionMenuButton.opacity = 0;
+-        this._sessionMenuButton.show();
+-        this._bottomButtonGroup.add_child(this._sessionMenuButton);
++        this._createSessionMenuButton();
+ 
+         this._a11yMenuButton = new A11yMenuButton();
+         this._bottomButtonGroup.add_child(this._a11yMenuButton);
+@@ -684,6 +677,35 @@ export const LoginDialog = GObject.registerClass({
+             this._updateDisableUserList.bind(this), this);
+     }
+ 
++    _createSessionMenuButton() {
++        this._sessionMenuButton = new AuthMenuButton.AuthMenuButton({
++            title: _('Session'),
++            iconName: 'emblem-system-symbolic',
++        });
++
++        let ids = Gdm.get_session_ids();
++        ids.sort();
++
++        for (const id of ids) {
++            let [sessionName, sessionDescription_] = Gdm.get_session_name_and_description(id);
++
++            this._sessionMenuButton.addItem({name: sessionName, id});
++        }
++
++        if (ids.length <= 1) {
++            this._sessionMenuButton.hide();
++            return;
++        }
++
++        this._sessionMenuButton.connect('active-item-changed', () => {
++            const session = this._sessionMenuButton.getActiveItem();
++
++            if (session)
++                this._greeter.call_select_session_sync(session.id, null);
++        });
++        this._bottomButtonGroup.add_child(this._sessionMenuButton);
++    }
++
+     _getBannerAllocation(dialogBox) {
+         let actorBox = new Clutter.ActorBox();
+ 
+@@ -1083,7 +1105,7 @@ export const LoginDialog = GObject.registerClass({
+     }
+ 
+     _onDefaultSessionChanged(client, sessionId) {
+-        this._sessionMenuButton.setActiveSession(sessionId);
++        this._sessionMenuButton.setActiveItem({id: sessionId});
+     }
+ 
+     _shouldShowSessionMenuButton() {
+@@ -1471,8 +1493,7 @@ export const LoginDialog = GObject.registerClass({
+         this._ensureUserListLoaded();
+         this._authPrompt.hide();
+         this._hideBannerView();
+-        this._sessionMenuButton.close();
+-        this._sessionMenuButton.hide();
++        this._sessionMenuButton.updateSensitivity(false);
+         this._setUserListExpanded(true);
+         this._notListedButton.show();
+         this._userList.grab_key_focus();
diff -pruN 49.0-1/debian/patches/ubuntu-authd/shell-qr-code-generator-Use-g-c-c-embedded-library-simpli.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/shell-qr-code-generator-Use-g-c-c-embedded-library-simpli.patch
--- 49.0-1/debian/patches/ubuntu-authd/shell-qr-code-generator-Use-g-c-c-embedded-library-simpli.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/shell-qr-code-generator-Use-g-c-c-embedded-library-simpli.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,2042 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Sat, 22 Feb 2025 03:31:47 +0100
+Subject: shell-qr-code-generator: Use g-c-c embedded library,
+ simplifying dependencies
+
+---
+ .../gnome-shell-sass/widgets/_login-lock.scss      |   12 +-
+ js/gdm/webLogin.js                                 |   56 +-
+ meson.build                                        |    2 -
+ src/meson.build                                    |    5 +-
+ src/qrcodegen.c                                    | 1028 ++++++++++++++++++++
+ src/qrcodegen.h                                    |  385 ++++++++
+ src/shell-qr-code-generator.c                      |  266 +++--
+ src/shell-qr-code-generator.h                      |    7 +-
+ 8 files changed, 1662 insertions(+), 99 deletions(-)
+ create mode 100644 src/qrcodegen.c
+ create mode 100644 src/qrcodegen.h
+
+diff --git a/data/theme/gnome-shell-sass/widgets/_login-lock.scss b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+index 65e2a29..d8e2298 100644
+--- a/data/theme/gnome-shell-sass/widgets/_login-lock.scss
++++ b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+@@ -481,8 +481,16 @@ $_gdm_dialog_width: 25em;
+ 
+ // QR Code
+ .qr-code {
+-  background: black;
++  @if ($variant == 'light') {
++    $qrcode_bg_color: mix($fg_color, $bg_color, 8%);
++    background-color: $qrcode_bg_color;
++    border-color: $qrcode_bg_color;
++    color: $fg_color;
++  } @else {
++    background-color: $_gdm_fg;
++    border-color: $_gdm_fg;
++    color: $_gdm_bg;
++  }
+   border-radius: $base_border_radius * .5;
+-  border-color: white;
+   border-width: 1em;
+ }
+diff --git a/js/gdm/webLogin.js b/js/gdm/webLogin.js
+index 30697ae..2795c3d 100644
+--- a/js/gdm/webLogin.js
++++ b/js/gdm/webLogin.js
+@@ -17,7 +17,7 @@ const WEB_LOGIN_SPINNER_SIZE = 35;
+ 
+ Gio._promisify(Shell.QrCodeGenerator.prototype, 'generate_qr_code');
+ 
+-export const QrCode = GObject.registerClass(
++const QrCode = GObject.registerClass(
+ class QrCode extends St.Bin {
+     _init(params) {
+         const themeContext = St.ThemeContext.get_for_stage(global.stage);
+@@ -32,7 +32,6 @@ class QrCode extends St.Bin {
+             x_align: Clutter.ActorAlign.CENTER,
+         });
+ 
+-        this._qrCodeGenerator = new Shell.QrCodeGenerator();
+         this._iconSize = iconSize;
+         this._url = url;
+         this.child = new St.Icon({
+@@ -40,23 +39,44 @@ class QrCode extends St.Bin {
+             style_class: 'qr-code',
+         });
+ 
+-        themeContext.connectObject('notify::scale-factor', this.update.bind(this), this);
++        this.connect('destroy', () => this._cancellable?.cancel());
+ 
+-        this.update();
++        themeContext.connectObject('notify::scale-factor',
++            () => this.update().catch(logError), this);
+     }
+ 
+     vfunc_style_changed() {
+         super.vfunc_style_changed();
+ 
+-        let node = this.get_theme_node();
+-        let [found, iconSize] = node.lookup_length('icon-size', false);
++        let changed = false;
++        const node = this.child.get_theme_node();
++        const [found, iconSize] = node.lookup_length('icon-size', false);
++
++        if (found) {
++            const themeContext = St.ThemeContext.get_for_stage(global.stage);
++            const newIconSize = iconSize / themeContext.scaleFactor;
++            if (this._iconSize !== newIconSize) {
++                this._iconSize = newIconSize;
++                changed = true;
++            }
++        }
+ 
+-        if (!found)
+-            return;
++        const bgColor = node.get_background_color();
++        const fgColor = node.get_foreground_color();
+ 
+-        let themeContext = St.ThemeContext.get_for_stage(global.stage);
++        if (!this._bgColor?.equal(bgColor)) {
++            this._bgColor = bgColor;
++            changed = true;
++        }
++
++        if (!this._fgColor?.equal(fgColor)) {
++            this._fgColor = fgColor;
++            changed = true;
++        }
++
++        if (!changed)
++            return;
+ 
+-        this._iconSize = iconSize / themeContext.scaleFactor;
+         this.update().catch(logError);
+     }
+ 
+@@ -66,10 +86,18 @@ class QrCode extends St.Bin {
+             this._iconSize * scaleFactor,
+             this._iconSize * scaleFactor);
+ 
+-        this.child.gicon = await this._qrCodeGenerator.generate_qr_code(
+-            this._url, this._iconSize, this._iconSize);
+-
+-        this.style = null;
++        this._cancellable?.cancel();
++        this._cancellable = new Gio.Cancellable();
++        const qrCodeGenerator = new Shell.QrCodeGenerator();
++
++        try {
++            this.child.gicon = await qrCodeGenerator.generate_qr_code(
++                this._url, this._iconSize, this._iconSize,
++                this._bgColor, this._fgColor, this._cancellable);
++        } catch (e) {
++            if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
++                logError(e);
++        }
+     }
+ });
+ 
+diff --git a/meson.build b/meson.build
+index 6c1d1ad..df593c6 100644
+--- a/meson.build
++++ b/meson.build
+@@ -31,7 +31,6 @@ systemd_req = '>= 246'
+ gnome_desktop_req = '>= 40'
+ pipewire_req = '>= 0.3.49'
+ pango_req = '>= 1.46.0'
+-qrencode_req = '>= 4.1.1'
+ 
+ nm_req = '>= 1.10.4'
+ secret_req = '>= 0.18'
+@@ -87,7 +86,6 @@ polkit_dep = dependency('polkit-agent-1', version: polkit_req)
+ schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
+ gnome_desktop_dep = dependency('gnome-desktop-4', version: gnome_desktop_req)
+ pango_dep = dependency('pango', version: pango_req)
+-qrencode_dep = dependency('libqrencode', version: qrencode_req)
+ 
+ have_fonts = mutter_dep.get_variable('have_fonts') == 'true'
+ have_x11 = mutter_dep.get_variable('have_x11') == 'true'
+diff --git a/src/meson.build b/src/meson.build
+index f7d9bff..8b8c473 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -75,7 +75,6 @@ gnome_shell_deps = [
+   libsystemd_dep,
+   libpipewire_dep,
+   pango_dep,
+-  qrencode_dep,
+ ]
+ 
+ if have_x11_client
+@@ -199,6 +198,10 @@ if have_x11_client
+   ]
+ endif
+ 
++libshell_sources += [
++  'qrcodegen.c',
++]
++
+ if have_networkmanager
+   libshell_sources += 'shell-network-agent.c'
+ endif
+diff --git a/src/qrcodegen.c b/src/qrcodegen.c
+new file mode 100644
+index 0000000..604eede
+--- /dev/null
++++ b/src/qrcodegen.c
+@@ -0,0 +1,1028 @@
++/*
++ * QR Code generator library (C)
++ *
++ * Copyright (c) Project Nayuki. (MIT License)
++ * https://www.nayuki.io/page/qr-code-generator-library
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy of
++ * this software and associated documentation files (the "Software"), to deal in
++ * the Software without restriction, including without limitation the rights to
++ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
++ * the Software, and to permit persons to whom the Software is furnished to do so,
++ * subject to the following conditions:
++ * - The above copyright notice and this permission notice shall be included in
++ *   all copies or substantial portions of the Software.
++ * - The Software is provided "as is", without warranty of any kind, express or
++ *   implied, including but not limited to the warranties of merchantability,
++ *   fitness for a particular purpose and noninfringement. In no event shall the
++ *   authors or copyright holders be liable for any claim, damages or other
++ *   liability, whether in an action of contract, tort or otherwise, arising from,
++ *   out of or in connection with the Software or the use or other dealings in the
++ *   Software.
++ */
++
++#include <assert.h>
++#include <limits.h>
++#include <stdlib.h>
++#include <string.h>
++#include "qrcodegen.h"
++
++#ifndef QRCODEGEN_TEST
++	#define testable static  // Keep functions private
++#else
++	#define testable  // Expose private functions
++#endif
++
++
++/*---- Forward declarations for private functions ----*/
++
++// Regarding all public and private functions defined in this source file:
++// - They require all pointer/array arguments to be not null unless the array length is zero.
++// - They only read input scalar/array arguments, write to output pointer/array
++//   arguments, and return scalar values; they are "pure" functions.
++// - They don't read mutable global variables or write to any global variables.
++// - They don't perform I/O, read the clock, print to console, etc.
++// - They allocate a small and constant amount of stack memory.
++// - They don't allocate or free any memory on the heap.
++// - They don't recurse or mutually recurse. All the code
++//   could be inlined into the top-level public functions.
++// - They run in at most quadratic time with respect to input arguments.
++//   Most functions run in linear time, and some in constant time.
++//   There are no unbounded loops or non-obvious termination conditions.
++// - They are completely thread-safe if the caller does not give the
++//   same writable buffer to concurrent calls to these functions.
++
++testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen);
++
++testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]);
++testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl);
++testable int getNumRawDataModules(int ver);
++
++testable void reedSolomonComputeDivisor(int degree, uint8_t result[]);
++testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen,
++	const uint8_t generator[], int degree, uint8_t result[]);
++testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y);
++
++testable void initializeFunctionModules(int version, uint8_t qrcode[]);
++static void drawLightFunctionModules(uint8_t qrcode[], int version);
++static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]);
++testable int getAlignmentPatternPositions(int version, uint8_t result[7]);
++static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]);
++
++static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]);
++static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask);
++static long getPenaltyScore(const uint8_t qrcode[]);
++static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize);
++static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize);
++static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize);
++
++testable bool getModuleBounded(const uint8_t qrcode[], int x, int y);
++testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark);
++testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark);
++static bool getBit(int x, int i);
++
++testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars);
++testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version);
++static int numCharCountBits(enum qrcodegen_Mode mode, int version);
++
++
++
++/*---- Private tables of constants ----*/
++
++// The set of all legal characters in alphanumeric mode, where each character
++// value maps to the index in the string. For checking text and encoding segments.
++static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
++
++// Sentinel value for use in only some functions.
++#define LENGTH_OVERFLOW -1
++
++// For generating error correction codes.
++testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = {
++	// Version: (note that index 0 is for padding, and is set to an illegal value)
++	//0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
++	{-1,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Low
++	{-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},  // Medium
++	{-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Quartile
++	{-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // High
++};
++
++#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30  // Based on the table above
++
++// For generating error correction codes.
++testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = {
++	// Version: (note that index 0 is for padding, and is set to an illegal value)
++	//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
++	{-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25},  // Low
++	{-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49},  // Medium
++	{-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68},  // Quartile
++	{-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81},  // High
++};
++
++// For automatic mask pattern selection.
++static const int PENALTY_N1 =  3;
++static const int PENALTY_N2 =  3;
++static const int PENALTY_N3 = 40;
++static const int PENALTY_N4 = 10;
++
++
++
++/*---- High-level QR Code encoding functions ----*/
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
++		enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) {
++
++	size_t textLen = strlen(text);
++	if (textLen == 0)
++		return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode);
++	size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion);
++
++	struct qrcodegen_Segment seg;
++	if (qrcodegen_isNumeric(text)) {
++		if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen)
++			goto fail;
++		seg = qrcodegen_makeNumeric(text, tempBuffer);
++	} else if (qrcodegen_isAlphanumeric(text)) {
++		if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen)
++			goto fail;
++		seg = qrcodegen_makeAlphanumeric(text, tempBuffer);
++	} else {
++		if (textLen > bufLen)
++			goto fail;
++		for (size_t i = 0; i < textLen; i++)
++			tempBuffer[i] = (uint8_t)text[i];
++		seg.mode = qrcodegen_Mode_BYTE;
++		seg.bitLength = calcSegmentBitLength(seg.mode, textLen);
++		if (seg.bitLength == LENGTH_OVERFLOW)
++			goto fail;
++		seg.numChars = (int)textLen;
++		seg.data = tempBuffer;
++	}
++	return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode);
++
++fail:
++	qrcode[0] = 0;  // Set size to invalid value for safety
++	return false;
++}
++
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
++		enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) {
++
++	struct qrcodegen_Segment seg;
++	seg.mode = qrcodegen_Mode_BYTE;
++	seg.bitLength = calcSegmentBitLength(seg.mode, dataLen);
++	if (seg.bitLength == LENGTH_OVERFLOW) {
++		qrcode[0] = 0;  // Set size to invalid value for safety
++		return false;
++	}
++	seg.numChars = (int)dataLen;
++	seg.data = dataAndTemp;
++	return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode);
++}
++
++
++// Appends the given number of low-order bits of the given value to the given byte-based
++// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits.
++testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) {
++	assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0);
++	for (int i = numBits - 1; i >= 0; i--, (*bitLen)++)
++		buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7));
++}
++
++
++
++/*---- Low-level QR Code encoding functions ----*/
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
++		enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) {
++	return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl,
++		qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode);
++}
++
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
++		int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) {
++	assert(segs != NULL || len == 0);
++	assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX);
++	assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7);
++
++	// Find the minimal version number to use
++	int version, dataUsedBits;
++	for (version = minVersion; ; version++) {
++		int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;  // Number of data bits available
++		dataUsedBits = getTotalBits(segs, len, version);
++		if (dataUsedBits != LENGTH_OVERFLOW && dataUsedBits <= dataCapacityBits)
++			break;  // This version number is found to be suitable
++		if (version >= maxVersion) {  // All versions in the range could not fit the given data
++			qrcode[0] = 0;  // Set size to invalid value for safety
++			return false;
++		}
++	}
++	assert(dataUsedBits != LENGTH_OVERFLOW);
++
++	// Increase the error correction level while the data still fits in the current version number
++	for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) {  // From low to high
++		if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8)
++			ecl = (enum qrcodegen_Ecc)i;
++	}
++
++	// Concatenate all segments to create the data bit string
++	memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0]));
++	int bitLen = 0;
++	for (size_t i = 0; i < len; i++) {
++		const struct qrcodegen_Segment *seg = &segs[i];
++		appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen);
++		appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen);
++		for (int j = 0; j < seg->bitLength; j++) {
++			int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1;
++			appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen);
++		}
++	}
++	assert(bitLen == dataUsedBits);
++
++	// Add terminator and pad up to a byte if applicable
++	int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;
++	assert(bitLen <= dataCapacityBits);
++	int terminatorBits = dataCapacityBits - bitLen;
++	if (terminatorBits > 4)
++		terminatorBits = 4;
++	appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen);
++	appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen);
++	assert(bitLen % 8 == 0);
++
++	// Pad with alternating bytes until data capacity is reached
++	for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
++		appendBitsToBuffer(padByte, 8, qrcode, &bitLen);
++
++	// Compute ECC, draw modules
++	addEccAndInterleave(qrcode, version, ecl, tempBuffer);
++	initializeFunctionModules(version, qrcode);
++	drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode);
++	drawLightFunctionModules(qrcode, version);
++	initializeFunctionModules(version, tempBuffer);
++
++	// Do masking
++	if (mask == qrcodegen_Mask_AUTO) {  // Automatically choose best mask
++		long minPenalty = LONG_MAX;
++		for (int i = 0; i < 8; i++) {
++			enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i;
++			applyMask(tempBuffer, qrcode, msk);
++			drawFormatBits(ecl, msk, qrcode);
++			long penalty = getPenaltyScore(qrcode);
++			if (penalty < minPenalty) {
++				mask = msk;
++				minPenalty = penalty;
++			}
++			applyMask(tempBuffer, qrcode, msk);  // Undoes the mask due to XOR
++		}
++	}
++	assert(0 <= (int)mask && (int)mask <= 7);
++	applyMask(tempBuffer, qrcode, mask);  // Apply the final choice of mask
++	drawFormatBits(ecl, mask, qrcode);  // Overwrite old format bits
++	return true;
++}
++
++
++
++/*---- Error correction code generation functions ----*/
++
++// Appends error correction bytes to each block of the given data array, then interleaves
++// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains
++// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will
++// be clobbered by this function. The final answer is stored in result[0 : rawCodewords].
++testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) {
++	// Calculate parameter numbers
++	assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX);
++	int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version];
++	int blockEccLen = ECC_CODEWORDS_PER_BLOCK  [(int)ecl][version];
++	int rawCodewords = getNumRawDataModules(version) / 8;
++	int dataLen = getNumDataCodewords(version, ecl);
++	int numShortBlocks = numBlocks - rawCodewords % numBlocks;
++	int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen;
++
++	// Split data into blocks, calculate ECC, and interleave
++	// (not concatenate) the bytes into a single sequence
++	uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX];
++	reedSolomonComputeDivisor(blockEccLen, rsdiv);
++	const uint8_t *dat = data;
++	for (int i = 0; i < numBlocks; i++) {
++		int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1);
++		uint8_t *ecc = &data[dataLen];  // Temporary storage
++		reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc);
++		for (int j = 0, k = i; j < datLen; j++, k += numBlocks) {  // Copy data
++			if (j == shortBlockDataLen)
++				k -= numShortBlocks;
++			result[k] = dat[j];
++		}
++		for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks)  // Copy ECC
++			result[k] = ecc[j];
++		dat += datLen;
++	}
++}
++
++
++// Returns the number of 8-bit codewords that can be used for storing data (not ECC),
++// for the given version number and error correction level. The result is in the range [9, 2956].
++testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) {
++	int v = version, e = (int)ecl;
++	assert(0 <= e && e < 4);
++	return getNumRawDataModules(v) / 8
++		- ECC_CODEWORDS_PER_BLOCK    [e][v]
++		* NUM_ERROR_CORRECTION_BLOCKS[e][v];
++}
++
++
++// Returns the number of data bits that can be stored in a QR Code of the given version number, after
++// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
++// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
++testable int getNumRawDataModules(int ver) {
++	assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX);
++	int result = (16 * ver + 128) * ver + 64;
++	if (ver >= 2) {
++		int numAlign = ver / 7 + 2;
++		result -= (25 * numAlign - 10) * numAlign - 55;
++		if (ver >= 7)
++			result -= 36;
++	}
++	assert(208 <= result && result <= 29648);
++	return result;
++}
++
++
++
++/*---- Reed-Solomon ECC generator functions ----*/
++
++// Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree].
++// This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm.
++testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) {
++	assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX);
++	// Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
++	// For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
++	memset(result, 0, (size_t)degree * sizeof(result[0]));
++	result[degree - 1] = 1;  // Start off with the monomial x^0
++
++	// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
++	// drop the highest monomial term which is always 1x^degree.
++	// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
++	uint8_t root = 1;
++	for (int i = 0; i < degree; i++) {
++		// Multiply the current product by (x - r^i)
++		for (int j = 0; j < degree; j++) {
++			result[j] = reedSolomonMultiply(result[j], root);
++			if (j + 1 < degree)
++				result[j] ^= result[j + 1];
++		}
++		root = reedSolomonMultiply(root, 0x02);
++	}
++}
++
++
++// Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials.
++// The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree].
++// All polynomials are in big endian, and the generator has an implicit leading 1 term.
++testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen,
++		const uint8_t generator[], int degree, uint8_t result[]) {
++	assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX);
++	memset(result, 0, (size_t)degree * sizeof(result[0]));
++	for (int i = 0; i < dataLen; i++) {  // Polynomial division
++		uint8_t factor = data[i] ^ result[0];
++		memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0]));
++		result[degree - 1] = 0;
++		for (int j = 0; j < degree; j++)
++			result[j] ^= reedSolomonMultiply(generator[j], factor);
++	}
++}
++
++#undef qrcodegen_REED_SOLOMON_DEGREE_MAX
++
++
++// Returns the product of the two given field elements modulo GF(2^8/0x11D).
++// All inputs are valid. This could be implemented as a 256*256 lookup table.
++testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) {
++	// Russian peasant multiplication
++	uint8_t z = 0;
++	for (int i = 7; i >= 0; i--) {
++		z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D));
++		z ^= ((y >> i) & 1) * x;
++	}
++	return z;
++}
++
++
++
++/*---- Drawing function modules ----*/
++
++// Clears the given QR Code grid with light modules for the given
++// version's size, then marks every function module as dark.
++testable void initializeFunctionModules(int version, uint8_t qrcode[]) {
++	// Initialize QR Code
++	int qrsize = version * 4 + 17;
++	memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0]));
++	qrcode[0] = (uint8_t)qrsize;
++
++	// Fill horizontal and vertical timing patterns
++	fillRectangle(6, 0, 1, qrsize, qrcode);
++	fillRectangle(0, 6, qrsize, 1, qrcode);
++
++	// Fill 3 finder patterns (all corners except bottom right) and format bits
++	fillRectangle(0, 0, 9, 9, qrcode);
++	fillRectangle(qrsize - 8, 0, 8, 9, qrcode);
++	fillRectangle(0, qrsize - 8, 9, 8, qrcode);
++
++	// Fill numerous alignment patterns
++	uint8_t alignPatPos[7];
++	int numAlign = getAlignmentPatternPositions(version, alignPatPos);
++	for (int i = 0; i < numAlign; i++) {
++		for (int j = 0; j < numAlign; j++) {
++			// Don't draw on the three finder corners
++			if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)))
++				fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode);
++		}
++	}
++
++	// Fill version blocks
++	if (version >= 7) {
++		fillRectangle(qrsize - 11, 0, 3, 6, qrcode);
++		fillRectangle(0, qrsize - 11, 6, 3, qrcode);
++	}
++}
++
++
++// Draws light function modules and possibly some dark modules onto the given QR Code, without changing
++// non-function modules. This does not draw the format bits. This requires all function modules to be previously
++// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules.
++static void drawLightFunctionModules(uint8_t qrcode[], int version) {
++	// Draw horizontal and vertical timing patterns
++	int qrsize = qrcodegen_getSize(qrcode);
++	for (int i = 7; i < qrsize - 7; i += 2) {
++		setModuleBounded(qrcode, 6, i, false);
++		setModuleBounded(qrcode, i, 6, false);
++	}
++
++	// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
++	for (int dy = -4; dy <= 4; dy++) {
++		for (int dx = -4; dx <= 4; dx++) {
++			int dist = abs(dx);
++			if (abs(dy) > dist)
++				dist = abs(dy);
++			if (dist == 2 || dist == 4) {
++				setModuleUnbounded(qrcode, 3 + dx, 3 + dy, false);
++				setModuleUnbounded(qrcode, qrsize - 4 + dx, 3 + dy, false);
++				setModuleUnbounded(qrcode, 3 + dx, qrsize - 4 + dy, false);
++			}
++		}
++	}
++
++	// Draw numerous alignment patterns
++	uint8_t alignPatPos[7];
++	int numAlign = getAlignmentPatternPositions(version, alignPatPos);
++	for (int i = 0; i < numAlign; i++) {
++		for (int j = 0; j < numAlign; j++) {
++			if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))
++				continue;  // Don't draw on the three finder corners
++			for (int dy = -1; dy <= 1; dy++) {
++				for (int dx = -1; dx <= 1; dx++)
++					setModuleBounded(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0);
++			}
++		}
++	}
++
++	// Draw version blocks
++	if (version >= 7) {
++		// Calculate error correction code and pack bits
++		int rem = version;  // version is uint6, in the range [7, 40]
++		for (int i = 0; i < 12; i++)
++			rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
++		long bits = (long)version << 12 | rem;  // uint18
++		assert(bits >> 18 == 0);
++
++		// Draw two copies
++		for (int i = 0; i < 6; i++) {
++			for (int j = 0; j < 3; j++) {
++				int k = qrsize - 11 + j;
++				setModuleBounded(qrcode, k, i, (bits & 1) != 0);
++				setModuleBounded(qrcode, i, k, (bits & 1) != 0);
++				bits >>= 1;
++			}
++		}
++	}
++}
++
++
++// Draws two copies of the format bits (with its own error correction code) based
++// on the given mask and error correction level. This always draws all modules of
++// the format bits, unlike drawLightFunctionModules() which might skip dark modules.
++static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) {
++	// Calculate error correction code and pack bits
++	assert(0 <= (int)mask && (int)mask <= 7);
++	static const int table[] = {1, 0, 3, 2};
++	int data = table[(int)ecl] << 3 | (int)mask;  // errCorrLvl is uint2, mask is uint3
++	int rem = data;
++	for (int i = 0; i < 10; i++)
++		rem = (rem << 1) ^ ((rem >> 9) * 0x537);
++	int bits = (data << 10 | rem) ^ 0x5412;  // uint15
++	assert(bits >> 15 == 0);
++
++	// Draw first copy
++	for (int i = 0; i <= 5; i++)
++		setModuleBounded(qrcode, 8, i, getBit(bits, i));
++	setModuleBounded(qrcode, 8, 7, getBit(bits, 6));
++	setModuleBounded(qrcode, 8, 8, getBit(bits, 7));
++	setModuleBounded(qrcode, 7, 8, getBit(bits, 8));
++	for (int i = 9; i < 15; i++)
++		setModuleBounded(qrcode, 14 - i, 8, getBit(bits, i));
++
++	// Draw second copy
++	int qrsize = qrcodegen_getSize(qrcode);
++	for (int i = 0; i < 8; i++)
++		setModuleBounded(qrcode, qrsize - 1 - i, 8, getBit(bits, i));
++	for (int i = 8; i < 15; i++)
++		setModuleBounded(qrcode, 8, qrsize - 15 + i, getBit(bits, i));
++	setModuleBounded(qrcode, 8, qrsize - 8, true);  // Always dark
++}
++
++
++// Calculates and stores an ascending list of positions of alignment patterns
++// for this version number, returning the length of the list (in the range [0,7]).
++// Each position is in the range [0,177), and are used on both the x and y axes.
++// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
++testable int getAlignmentPatternPositions(int version, uint8_t result[7]) {
++	if (version == 1)
++		return 0;
++	int numAlign = version / 7 + 2;
++	int step = (version == 32) ? 26 :
++		(version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2;
++	for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step)
++		result[i] = (uint8_t)pos;
++	result[0] = 6;
++	return numAlign;
++}
++
++
++// Sets every module in the range [left : left + width] * [top : top + height] to dark.
++static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) {
++	for (int dy = 0; dy < height; dy++) {
++		for (int dx = 0; dx < width; dx++)
++			setModuleBounded(qrcode, left + dx, top + dy, true);
++	}
++}
++
++
++
++/*---- Drawing data modules and masking ----*/
++
++// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of
++// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits).
++static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) {
++	int qrsize = qrcodegen_getSize(qrcode);
++	int i = 0;  // Bit index into the data
++	// Do the funny zigzag scan
++	for (int right = qrsize - 1; right >= 1; right -= 2) {  // Index of right column in each column pair
++		if (right == 6)
++			right = 5;
++		for (int vert = 0; vert < qrsize; vert++) {  // Vertical counter
++			for (int j = 0; j < 2; j++) {
++				int x = right - j;  // Actual x coordinate
++				bool upward = ((right + 1) & 2) == 0;
++				int y = upward ? qrsize - 1 - vert : vert;  // Actual y coordinate
++				if (!getModuleBounded(qrcode, x, y) && i < dataLen * 8) {
++					bool dark = getBit(data[i >> 3], 7 - (i & 7));
++					setModuleBounded(qrcode, x, y, dark);
++					i++;
++				}
++				// If this QR Code has any remainder bits (0 to 7), they were assigned as
++				// 0/false/light by the constructor and are left unchanged by this method
++			}
++		}
++	}
++	assert(i == dataLen * 8);
++}
++
++
++// XORs the codeword modules in this QR Code with the given mask pattern
++// and given pattern of function modules. The codeword bits must be drawn
++// before masking. Due to the arithmetic of XOR, calling applyMask() with
++// the same mask value a second time will undo the mask. A final well-formed
++// QR Code needs exactly one (not zero, two, etc.) mask applied.
++static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) {
++	assert(0 <= (int)mask && (int)mask <= 7);  // Disallows qrcodegen_Mask_AUTO
++	int qrsize = qrcodegen_getSize(qrcode);
++	for (int y = 0; y < qrsize; y++) {
++		for (int x = 0; x < qrsize; x++) {
++			if (getModuleBounded(functionModules, x, y))
++				continue;
++			bool invert;
++			switch ((int)mask) {
++				case 0:  invert = (x + y) % 2 == 0;                    break;
++				case 1:  invert = y % 2 == 0;                          break;
++				case 2:  invert = x % 3 == 0;                          break;
++				case 3:  invert = (x + y) % 3 == 0;                    break;
++				case 4:  invert = (x / 3 + y / 2) % 2 == 0;            break;
++				case 5:  invert = x * y % 2 + x * y % 3 == 0;          break;
++				case 6:  invert = (x * y % 2 + x * y % 3) % 2 == 0;    break;
++				case 7:  invert = ((x + y) % 2 + x * y % 3) % 2 == 0;  break;
++				default:  assert(false);  return;
++			}
++			bool val = getModuleBounded(qrcode, x, y);
++			setModuleBounded(qrcode, x, y, val ^ invert);
++		}
++	}
++}
++
++
++// Calculates and returns the penalty score based on state of the given QR Code's current modules.
++// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
++static long getPenaltyScore(const uint8_t qrcode[]) {
++	int qrsize = qrcodegen_getSize(qrcode);
++	long result = 0;
++
++	// Adjacent modules in row having same color, and finder-like patterns
++	for (int y = 0; y < qrsize; y++) {
++		bool runColor = false;
++		int runX = 0;
++		int runHistory[7] = {0};
++		for (int x = 0; x < qrsize; x++) {
++			if (getModuleBounded(qrcode, x, y) == runColor) {
++				runX++;
++				if (runX == 5)
++					result += PENALTY_N1;
++				else if (runX > 5)
++					result++;
++			} else {
++				finderPenaltyAddHistory(runX, runHistory, qrsize);
++				if (!runColor)
++					result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3;
++				runColor = getModuleBounded(qrcode, x, y);
++				runX = 1;
++			}
++		}
++		result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3;
++	}
++	// Adjacent modules in column having same color, and finder-like patterns
++	for (int x = 0; x < qrsize; x++) {
++		bool runColor = false;
++		int runY = 0;
++		int runHistory[7] = {0};
++		for (int y = 0; y < qrsize; y++) {
++			if (getModuleBounded(qrcode, x, y) == runColor) {
++				runY++;
++				if (runY == 5)
++					result += PENALTY_N1;
++				else if (runY > 5)
++					result++;
++			} else {
++				finderPenaltyAddHistory(runY, runHistory, qrsize);
++				if (!runColor)
++					result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3;
++				runColor = getModuleBounded(qrcode, x, y);
++				runY = 1;
++			}
++		}
++		result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3;
++	}
++
++	// 2*2 blocks of modules having same color
++	for (int y = 0; y < qrsize - 1; y++) {
++		for (int x = 0; x < qrsize - 1; x++) {
++			bool  color = getModuleBounded(qrcode, x, y);
++			if (  color == getModuleBounded(qrcode, x + 1, y) &&
++			      color == getModuleBounded(qrcode, x, y + 1) &&
++			      color == getModuleBounded(qrcode, x + 1, y + 1))
++				result += PENALTY_N2;
++		}
++	}
++
++	// Balance of dark and light modules
++	int dark = 0;
++	for (int y = 0; y < qrsize; y++) {
++		for (int x = 0; x < qrsize; x++) {
++			if (getModuleBounded(qrcode, x, y))
++				dark++;
++		}
++	}
++	int total = qrsize * qrsize;  // Note that size is odd, so dark/total != 1/2
++	// Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
++	int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1;
++	assert(0 <= k && k <= 9);
++	result += k * PENALTY_N4;
++	assert(0 <= result && result <= 2568888L);  // Non-tight upper bound based on default values of PENALTY_N1, ..., N4
++	return result;
++}
++
++
++// Can only be called immediately after a light run is added, and
++// returns either 0, 1, or 2. A helper function for getPenaltyScore().
++static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) {
++	int n = runHistory[1];
++	assert(n <= qrsize * 3);  (void)qrsize;
++	bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
++	// The maximum QR Code size is 177, hence the dark run length n <= 177.
++	// Arithmetic is promoted to int, so n*4 will not overflow.
++	return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
++	     + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
++}
++
++
++// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
++static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) {
++	if (currentRunColor) {  // Terminate dark run
++		finderPenaltyAddHistory(currentRunLength, runHistory, qrsize);
++		currentRunLength = 0;
++	}
++	currentRunLength += qrsize;  // Add light border to final run
++	finderPenaltyAddHistory(currentRunLength, runHistory, qrsize);
++	return finderPenaltyCountPatterns(runHistory, qrsize);
++}
++
++
++// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
++static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) {
++	if (runHistory[0] == 0)
++		currentRunLength += qrsize;  // Add light border to initial run
++	memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0]));
++	runHistory[0] = currentRunLength;
++}
++
++
++
++/*---- Basic QR Code information ----*/
++
++// Public function - see documentation comment in header file.
++int qrcodegen_getSize(const uint8_t qrcode[]) {
++	assert(qrcode != NULL);
++	int result = qrcode[0];
++	assert((qrcodegen_VERSION_MIN * 4 + 17) <= result
++		&& result <= (qrcodegen_VERSION_MAX * 4 + 17));
++	return result;
++}
++
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) {
++	assert(qrcode != NULL);
++	int qrsize = qrcode[0];
++	return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModuleBounded(qrcode, x, y);
++}
++
++
++// Returns the color of the module at the given coordinates, which must be in bounds.
++testable bool getModuleBounded(const uint8_t qrcode[], int x, int y) {
++	int qrsize = qrcode[0];
++	assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize);
++	int index = y * qrsize + x;
++	return getBit(qrcode[(index >> 3) + 1], index & 7);
++}
++
++
++// Sets the color of the module at the given coordinates, which must be in bounds.
++testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) {
++	int qrsize = qrcode[0];
++	assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize);
++	int index = y * qrsize + x;
++	int bitIndex = index & 7;
++	int byteIndex = (index >> 3) + 1;
++	if (isDark)
++		qrcode[byteIndex] |= 1 << bitIndex;
++	else
++		qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF;
++}
++
++
++// Sets the color of the module at the given coordinates, doing nothing if out of bounds.
++testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark) {
++	int qrsize = qrcode[0];
++	if (0 <= x && x < qrsize && 0 <= y && y < qrsize)
++		setModuleBounded(qrcode, x, y, isDark);
++}
++
++
++// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14.
++static bool getBit(int x, int i) {
++	return ((x >> i) & 1) != 0;
++}
++
++
++
++/*---- Segment handling ----*/
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_isNumeric(const char *text) {
++	assert(text != NULL);
++	for (; *text != '\0'; text++) {
++		if (*text < '0' || *text > '9')
++			return false;
++	}
++	return true;
++}
++
++
++// Public function - see documentation comment in header file.
++bool qrcodegen_isAlphanumeric(const char *text) {
++	assert(text != NULL);
++	for (; *text != '\0'; text++) {
++		if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL)
++			return false;
++	}
++	return true;
++}
++
++
++// Public function - see documentation comment in header file.
++size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) {
++	int temp = calcSegmentBitLength(mode, numChars);
++	if (temp == LENGTH_OVERFLOW)
++		return SIZE_MAX;
++	assert(0 <= temp && temp <= INT16_MAX);
++	return ((size_t)temp + 7) / 8;
++}
++
++
++// Returns the number of data bits needed to represent a segment
++// containing the given number of characters using the given mode. Notes:
++// - Returns LENGTH_OVERFLOW on failure, i.e. numChars > INT16_MAX
++//   or the number of needed bits exceeds INT16_MAX (i.e. 32767).
++// - Otherwise, all valid results are in the range [0, INT16_MAX].
++// - For byte mode, numChars measures the number of bytes, not Unicode code points.
++// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned.
++//   An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
++testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) {
++	// All calculations are designed to avoid overflow on all platforms
++	if (numChars > (unsigned int)INT16_MAX)
++		return LENGTH_OVERFLOW;
++	long result = (long)numChars;
++	if (mode == qrcodegen_Mode_NUMERIC)
++		result = (result * 10 + 2) / 3;  // ceil(10/3 * n)
++	else if (mode == qrcodegen_Mode_ALPHANUMERIC)
++		result = (result * 11 + 1) / 2;  // ceil(11/2 * n)
++	else if (mode == qrcodegen_Mode_BYTE)
++		result *= 8;
++	else if (mode == qrcodegen_Mode_KANJI)
++		result *= 13;
++	else if (mode == qrcodegen_Mode_ECI && numChars == 0)
++		result = 3 * 8;
++	else {  // Invalid argument
++		assert(false);
++		return LENGTH_OVERFLOW;
++	}
++	assert(result >= 0);
++	if (result > INT16_MAX)
++		return LENGTH_OVERFLOW;
++	return (int)result;
++}
++
++
++// Public function - see documentation comment in header file.
++struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) {
++	assert(data != NULL || len == 0);
++	struct qrcodegen_Segment result;
++	result.mode = qrcodegen_Mode_BYTE;
++	result.bitLength = calcSegmentBitLength(result.mode, len);
++	assert(result.bitLength != LENGTH_OVERFLOW);
++	result.numChars = (int)len;
++	if (len > 0)
++		memcpy(buf, data, len * sizeof(buf[0]));
++	result.data = buf;
++	return result;
++}
++
++
++// Public function - see documentation comment in header file.
++struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) {
++	assert(digits != NULL);
++	struct qrcodegen_Segment result;
++	size_t len = strlen(digits);
++	result.mode = qrcodegen_Mode_NUMERIC;
++	int bitLen = calcSegmentBitLength(result.mode, len);
++	assert(bitLen != LENGTH_OVERFLOW);
++	result.numChars = (int)len;
++	if (bitLen > 0)
++		memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0]));
++	result.bitLength = 0;
++
++	unsigned int accumData = 0;
++	int accumCount = 0;
++	for (; *digits != '\0'; digits++) {
++		char c = *digits;
++		assert('0' <= c && c <= '9');
++		accumData = accumData * 10 + (unsigned int)(c - '0');
++		accumCount++;
++		if (accumCount == 3) {
++			appendBitsToBuffer(accumData, 10, buf, &result.bitLength);
++			accumData = 0;
++			accumCount = 0;
++		}
++	}
++	if (accumCount > 0)  // 1 or 2 digits remaining
++		appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength);
++	assert(result.bitLength == bitLen);
++	result.data = buf;
++	return result;
++}
++
++
++// Public function - see documentation comment in header file.
++struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) {
++	assert(text != NULL);
++	struct qrcodegen_Segment result;
++	size_t len = strlen(text);
++	result.mode = qrcodegen_Mode_ALPHANUMERIC;
++	int bitLen = calcSegmentBitLength(result.mode, len);
++	assert(bitLen != LENGTH_OVERFLOW);
++	result.numChars = (int)len;
++	if (bitLen > 0)
++		memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0]));
++	result.bitLength = 0;
++
++	unsigned int accumData = 0;
++	int accumCount = 0;
++	for (; *text != '\0'; text++) {
++		const char *temp = strchr(ALPHANUMERIC_CHARSET, *text);
++		assert(temp != NULL);
++		accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET);
++		accumCount++;
++		if (accumCount == 2) {
++			appendBitsToBuffer(accumData, 11, buf, &result.bitLength);
++			accumData = 0;
++			accumCount = 0;
++		}
++	}
++	if (accumCount > 0)  // 1 character remaining
++		appendBitsToBuffer(accumData, 6, buf, &result.bitLength);
++	assert(result.bitLength == bitLen);
++	result.data = buf;
++	return result;
++}
++
++
++// Public function - see documentation comment in header file.
++struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) {
++	struct qrcodegen_Segment result;
++	result.mode = qrcodegen_Mode_ECI;
++	result.numChars = 0;
++	result.bitLength = 0;
++	if (assignVal < 0)
++		assert(false);
++	else if (assignVal < (1 << 7)) {
++		memset(buf, 0, 1 * sizeof(buf[0]));
++		appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength);
++	} else if (assignVal < (1 << 14)) {
++		memset(buf, 0, 2 * sizeof(buf[0]));
++		appendBitsToBuffer(2, 2, buf, &result.bitLength);
++		appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength);
++	} else if (assignVal < 1000000L) {
++		memset(buf, 0, 3 * sizeof(buf[0]));
++		appendBitsToBuffer(6, 3, buf, &result.bitLength);
++		appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength);
++		appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength);
++	} else
++		assert(false);
++	result.data = buf;
++	return result;
++}
++
++
++// Calculates the number of bits needed to encode the given segments at the given version.
++// Returns a non-negative number if successful. Otherwise returns LENGTH_OVERFLOW if a segment
++// has too many characters to fit its length field, or the total bits exceeds INT16_MAX.
++testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) {
++	assert(segs != NULL || len == 0);
++	long result = 0;
++	for (size_t i = 0; i < len; i++) {
++		int numChars  = segs[i].numChars;
++		int bitLength = segs[i].bitLength;
++		assert(0 <= numChars  && numChars  <= INT16_MAX);
++		assert(0 <= bitLength && bitLength <= INT16_MAX);
++		int ccbits = numCharCountBits(segs[i].mode, version);
++		assert(0 <= ccbits && ccbits <= 16);
++		if (numChars >= (1L << ccbits))
++			return LENGTH_OVERFLOW;  // The segment's length doesn't fit the field's bit width
++		result += 4L + ccbits + bitLength;
++		if (result > INT16_MAX)
++			return LENGTH_OVERFLOW;  // The sum might overflow an int type
++	}
++	assert(0 <= result && result <= INT16_MAX);
++	return (int)result;
++}
++
++
++// Returns the bit width of the character count field for a segment in the given mode
++// in a QR Code at the given version number. The result is in the range [0, 16].
++static int numCharCountBits(enum qrcodegen_Mode mode, int version) {
++	assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX);
++	int i = (version + 7) / 17;
++	switch (mode) {
++		case qrcodegen_Mode_NUMERIC     : { static const int temp[] = {10, 12, 14}; return temp[i]; }
++		case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; }
++		case qrcodegen_Mode_BYTE        : { static const int temp[] = { 8, 16, 16}; return temp[i]; }
++		case qrcodegen_Mode_KANJI       : { static const int temp[] = { 8, 10, 12}; return temp[i]; }
++		case qrcodegen_Mode_ECI         : return 0;
++		default:  assert(false);  return -1;  // Dummy value
++	}
++}
++
++
++#undef LENGTH_OVERFLOW
+diff --git a/src/qrcodegen.h b/src/qrcodegen.h
+new file mode 100644
+index 0000000..edbb6c0
+--- /dev/null
++++ b/src/qrcodegen.h
+@@ -0,0 +1,385 @@
++/*
++ * QR Code generator library (C)
++ *
++ * Copyright (c) Project Nayuki. (MIT License)
++ * https://www.nayuki.io/page/qr-code-generator-library
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy of
++ * this software and associated documentation files (the "Software"), to deal in
++ * the Software without restriction, including without limitation the rights to
++ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
++ * the Software, and to permit persons to whom the Software is furnished to do so,
++ * subject to the following conditions:
++ * - The above copyright notice and this permission notice shall be included in
++ *   all copies or substantial portions of the Software.
++ * - The Software is provided "as is", without warranty of any kind, express or
++ *   implied, including but not limited to the warranties of merchantability,
++ *   fitness for a particular purpose and noninfringement. In no event shall the
++ *   authors or copyright holders be liable for any claim, damages or other
++ *   liability, whether in an action of contract, tort or otherwise, arising from,
++ *   out of or in connection with the Software or the use or other dealings in the
++ *   Software.
++ */
++
++#pragma once
++
++#include <stdbool.h>
++#include <stddef.h>
++#include <stdint.h>
++
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++/*
++ * This library creates QR Code symbols, which is a type of two-dimension barcode.
++ * Invented by Denso Wave and described in the ISO/IEC 18004 standard.
++ * A QR Code structure is an immutable square grid of dark and light cells.
++ * The library provides functions to create a QR Code from text or binary data.
++ * The library covers the QR Code Model 2 specification, supporting all versions (sizes)
++ * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
++ *
++ * Ways to create a QR Code object:
++ * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
++ * - Low level: Custom-make the list of segments and call
++ *   qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
++ * (Note that all ways require supplying the desired error correction level and various byte buffers.)
++ */
++
++
++/*---- Enum and struct types----*/
++
++/*
++ * The error correction level in a QR Code symbol.
++ */
++enum qrcodegen_Ecc {
++	// Must be declared in ascending order of error protection
++	// so that an internal qrcodegen function works properly
++	qrcodegen_Ecc_LOW = 0 ,  // The QR Code can tolerate about  7% erroneous codewords
++	qrcodegen_Ecc_MEDIUM  ,  // The QR Code can tolerate about 15% erroneous codewords
++	qrcodegen_Ecc_QUARTILE,  // The QR Code can tolerate about 25% erroneous codewords
++	qrcodegen_Ecc_HIGH    ,  // The QR Code can tolerate about 30% erroneous codewords
++};
++
++
++/*
++ * The mask pattern used in a QR Code symbol.
++ */
++enum qrcodegen_Mask {
++	// A special value to tell the QR Code encoder to
++	// automatically select an appropriate mask pattern
++	qrcodegen_Mask_AUTO = -1,
++	// The eight actual mask patterns
++	qrcodegen_Mask_0 = 0,
++	qrcodegen_Mask_1,
++	qrcodegen_Mask_2,
++	qrcodegen_Mask_3,
++	qrcodegen_Mask_4,
++	qrcodegen_Mask_5,
++	qrcodegen_Mask_6,
++	qrcodegen_Mask_7,
++};
++
++
++/*
++ * Describes how a segment's data bits are interpreted.
++ */
++enum qrcodegen_Mode {
++	qrcodegen_Mode_NUMERIC      = 0x1,
++	qrcodegen_Mode_ALPHANUMERIC = 0x2,
++	qrcodegen_Mode_BYTE         = 0x4,
++	qrcodegen_Mode_KANJI        = 0x8,
++	qrcodegen_Mode_ECI          = 0x7,
++};
++
++
++/*
++ * A segment of character/binary/control data in a QR Code symbol.
++ * The mid-level way to create a segment is to take the payload data
++ * and call a factory function such as qrcodegen_makeNumeric().
++ * The low-level way to create a segment is to custom-make the bit buffer
++ * and initialize a qrcodegen_Segment struct with appropriate values.
++ * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
++ * Any segment longer than this is meaningless for the purpose of generating QR Codes.
++ * Moreover, the maximum allowed bit length is 32767 because
++ * the largest QR Code (version 40) has 31329 modules.
++ */
++struct qrcodegen_Segment {
++	// The mode indicator of this segment.
++	enum qrcodegen_Mode mode;
++
++	// The length of this segment's unencoded data. Measured in characters for
++	// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
++	// Always zero or positive. Not the same as the data's bit length.
++	int numChars;
++
++	// The data bits of this segment, packed in bitwise big endian.
++	// Can be null if the bit length is zero.
++	uint8_t *data;
++
++	// The number of valid data bits used in the buffer. Requires
++	// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
++	// The character count (numChars) must agree with the mode and the bit buffer length.
++	int bitLength;
++};
++
++
++
++/*---- Macro constants and functions ----*/
++
++#define qrcodegen_VERSION_MIN   1  // The minimum version number supported in the QR Code Model 2 standard
++#define qrcodegen_VERSION_MAX  40  // The maximum version number supported in the QR Code Model 2 standard
++
++// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
++// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
++// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
++// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
++#define qrcodegen_BUFFER_LEN_FOR_VERSION(n)  ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
++
++// The worst-case number of bytes needed to store one QR Code, up to and including
++// version 40. This value equals 3918, which is just under 4 kilobytes.
++// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
++#define qrcodegen_BUFFER_LEN_MAX  qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
++
++
++
++/*---- Functions (high level) to generate QR Codes ----*/
++
++/*
++ * Encodes the given text string to a QR Code, returning true if successful.
++ * If the data is too long to fit in any version in the given range
++ * at the given ECC level, then false is returned.
++ *
++ * The input text must be encoded in UTF-8 and contain no NULs.
++ * Requires 1 <= minVersion <= maxVersion <= 40.
++ *
++ * The smallest possible QR Code version within the given range is automatically
++ * chosen for the output. Iff boostEcl is true, then the ECC level of the result
++ * may be higher than the ecl argument if it can be done without increasing the
++ * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
++ * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
++ *
++ * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
++ * - Before calling the function:
++ *   - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
++ *     reading and writing; hence each array must have a length of at least len.
++ *   - The two ranges must not overlap (aliasing).
++ *   - The initial state of both ranges can be uninitialized
++ *     because the function always writes before reading.
++ * - After the function returns:
++ *   - Both ranges have no guarantee on which elements are initialized and what values are stored.
++ *   - tempBuffer contains no useful data and should be treated as entirely uninitialized.
++ *   - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
++ *
++ * If successful, the resulting QR Code may use numeric,
++ * alphanumeric, or byte mode to encode the text.
++ *
++ * In the most optimistic case, a QR Code at version 40 with low ECC
++ * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
++ * up to 4296 characters, or any digit string up to 7089 characters.
++ * These numbers represent the hard upper limit of the QR Code standard.
++ *
++ * Please consult the QR Code specification for information on
++ * data capacities per version, ECC level, and text encoding mode.
++ */
++bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
++	enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
++
++
++/*
++ * Encodes the given binary data to a QR Code, returning true if successful.
++ * If the data is too long to fit in any version in the given range
++ * at the given ECC level, then false is returned.
++ *
++ * Requires 1 <= minVersion <= maxVersion <= 40.
++ *
++ * The smallest possible QR Code version within the given range is automatically
++ * chosen for the output. Iff boostEcl is true, then the ECC level of the result
++ * may be higher than the ecl argument if it can be done without increasing the
++ * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
++ * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
++ *
++ * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
++ * - Before calling the function:
++ *   - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow
++ *     reading and writing; hence each array must have a length of at least len.
++ *   - The two ranges must not overlap (aliasing).
++ *   - The input array range dataAndTemp[0 : dataLen] should normally be
++ *     valid UTF-8 text, but is not required by the QR Code standard.
++ *   - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len]
++ *     can be uninitialized because the function always writes before reading.
++ * - After the function returns:
++ *   - Both ranges have no guarantee on which elements are initialized and what values are stored.
++ *   - dataAndTemp contains no useful data and should be treated as entirely uninitialized.
++ *   - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
++ *
++ * If successful, the resulting QR Code will use byte mode to encode the data.
++ *
++ * In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
++ * sequence up to length 2953. This is the hard upper limit of the QR Code standard.
++ *
++ * Please consult the QR Code specification for information on
++ * data capacities per version, ECC level, and text encoding mode.
++ */
++bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
++	enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
++
++
++/*---- Functions (low level) to generate QR Codes ----*/
++
++/*
++ * Encodes the given segments to a QR Code, returning true if successful.
++ * If the data is too long to fit in any version at the given ECC level,
++ * then false is returned.
++ *
++ * The smallest possible QR Code version is automatically chosen for
++ * the output. The ECC level of the result may be higher than the
++ * ecl argument if it can be done without increasing the version.
++ *
++ * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
++ * - Before calling the function:
++ *   - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
++ *     reading and writing; hence each array must have a length of at least len.
++ *   - The two ranges must not overlap (aliasing).
++ *   - The initial state of both ranges can be uninitialized
++ *     because the function always writes before reading.
++ *   - The input array segs can contain segments whose data buffers overlap with tempBuffer.
++ * - After the function returns:
++ *   - Both ranges have no guarantee on which elements are initialized and what values are stored.
++ *   - tempBuffer contains no useful data and should be treated as entirely uninitialized.
++ *   - Any segment whose data buffer overlaps with tempBuffer[0 : len]
++ *     must be treated as having invalid values in that array.
++ *   - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
++ *
++ * Please consult the QR Code specification for information on
++ * data capacities per version, ECC level, and text encoding mode.
++ *
++ * This function allows the user to create a custom sequence of segments that switches
++ * between modes (such as alphanumeric and byte) to encode text in less space.
++ * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
++ */
++bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
++	enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
++
++
++/*
++ * Encodes the given segments to a QR Code, returning true if successful.
++ * If the data is too long to fit in any version in the given range
++ * at the given ECC level, then false is returned.
++ *
++ * Requires 1 <= minVersion <= maxVersion <= 40.
++ *
++ * The smallest possible QR Code version within the given range is automatically
++ * chosen for the output. Iff boostEcl is true, then the ECC level of the result
++ * may be higher than the ecl argument if it can be done without increasing the
++ * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
++ * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
++ *
++ * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
++ * - Before calling the function:
++ *   - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
++ *     reading and writing; hence each array must have a length of at least len.
++ *   - The two ranges must not overlap (aliasing).
++ *   - The initial state of both ranges can be uninitialized
++ *     because the function always writes before reading.
++ *   - The input array segs can contain segments whose data buffers overlap with tempBuffer.
++ * - After the function returns:
++ *   - Both ranges have no guarantee on which elements are initialized and what values are stored.
++ *   - tempBuffer contains no useful data and should be treated as entirely uninitialized.
++ *   - Any segment whose data buffer overlaps with tempBuffer[0 : len]
++ *     must be treated as having invalid values in that array.
++ *   - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
++ *
++ * Please consult the QR Code specification for information on
++ * data capacities per version, ECC level, and text encoding mode.
++ *
++ * This function allows the user to create a custom sequence of segments that switches
++ * between modes (such as alphanumeric and byte) to encode text in less space.
++ * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
++ */
++bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
++	int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]);
++
++
++/*
++ * Tests whether the given string can be encoded as a segment in numeric mode.
++ * A string is encodable iff each character is in the range 0 to 9.
++ */
++bool qrcodegen_isNumeric(const char *text);
++
++
++/*
++ * Tests whether the given string can be encoded as a segment in alphanumeric mode.
++ * A string is encodable iff each character is in the following set: 0 to 9, A to Z
++ * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
++ */
++bool qrcodegen_isAlphanumeric(const char *text);
++
++
++/*
++ * Returns the number of bytes (uint8_t) needed for the data buffer of a segment
++ * containing the given number of characters using the given mode. Notes:
++ * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal
++ *   calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767).
++ * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
++ * - It is okay for the user to allocate more bytes for the buffer than needed.
++ * - For byte mode, numChars measures the number of bytes, not Unicode code points.
++ * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
++ *   An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
++ */
++size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
++
++
++/*
++ * Returns a segment representing the given binary data encoded in
++ * byte mode. All input byte arrays are acceptable. Any text string
++ * can be converted to UTF-8 bytes and encoded as a byte mode segment.
++ */
++struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
++
++
++/*
++ * Returns a segment representing the given string of decimal digits encoded in numeric mode.
++ */
++struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
++
++
++/*
++ * Returns a segment representing the given text string encoded in alphanumeric mode.
++ * The characters allowed are: 0 to 9, A to Z (uppercase only), space,
++ * dollar, percent, asterisk, plus, hyphen, period, slash, colon.
++ */
++struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
++
++
++/*
++ * Returns a segment representing an Extended Channel Interpretation
++ * (ECI) designator with the given assignment value.
++ */
++struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
++
++
++/*---- Functions to extract raw data from QR Codes ----*/
++
++/*
++ * Returns the side length of the given QR Code, assuming that encoding succeeded.
++ * The result is in the range [21, 177]. Note that the length of the array buffer
++ * is related to the side length - every 'uint8_t qrcode[]' must have length at least
++ * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
++ */
++int qrcodegen_getSize(const uint8_t qrcode[]);
++
++
++/*
++ * Returns the color of the module (pixel) at the given coordinates, which is false
++ * for light or true for dark. The top left corner has the coordinates (x=0, y=0).
++ * If the given coordinates are out of bounds, then false (light) is returned.
++ */
++bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
++
++
++#ifdef __cplusplus
++}
++#endif
+diff --git a/src/shell-qr-code-generator.c b/src/shell-qr-code-generator.c
+index 6c7da8c..52531e9 100644
+--- a/src/shell-qr-code-generator.c
++++ b/src/shell-qr-code-generator.c
+@@ -7,15 +7,18 @@
+ #include <meta/meta-plugin.h>
+ #include <st/st.h>
+ 
+-#include <qrencode.h>
++#include "qrcodegen.h"
+ 
+ #include "shell-global.h"
+ #include "shell-qr-code-generator.h"
+ 
+-#define BYTES_PER_RGB_888 3
+-
+ typedef struct _ShellQrCodeGeneratorPrivate  ShellQrCodeGeneratorPrivate;
+ 
++#define BYTES_PER_R8G8B8 3
++#define BYTES_PER_R8G8B8A8 4
++#define BYTES_PER_FORMAT(format) \
++  ((format) == COGL_PIXEL_FORMAT_RGB_888 ? BYTES_PER_R8G8B8 : BYTES_PER_R8G8B8A8)
++
+ struct _ShellQrCodeGenerator
+ {
+   GObject parent_instance;
+@@ -28,6 +31,8 @@ struct _ShellQrCodeGeneratorPrivate
+   char *url;
+   size_t width;
+   size_t height;
++  CoglColor *bg_color;
++  CoglColor *fg_color;
+   GTask *image_task;
+   GTask *icon_task;
+ };
+@@ -39,6 +44,8 @@ shell_qr_code_generator_dispose (GObject *object)
+ {
+   ShellQrCodeGenerator *self = SHELL_QR_CODE_GENERATOR (object);
+   g_clear_pointer (&self->priv->url, g_free);
++  g_clear_pointer (&self->priv->bg_color, cogl_color_free);
++  g_clear_pointer (&self->priv->fg_color, cogl_color_free);
+ }
+ 
+ static void
+@@ -54,32 +61,56 @@ shell_qr_code_generator_init (ShellQrCodeGenerator *qr_code_generator)
+   qr_code_generator->priv = shell_qr_code_generator_get_instance_private (qr_code_generator);
+ }
+ 
++static void
++fill_pixel (GByteArray         *array,
++            const CoglColor *color,
++            CoglPixelFormat     pixel_format,
++            int                 pixel_size)
++{
++  guint i;
++
++  for (i = 0; i < pixel_size; i++)
++    {
++      g_byte_array_append (array, &color->red, 1);
++      g_byte_array_append (array, &color->green, 1);
++      g_byte_array_append (array, &color->blue, 1);
++
++      if (pixel_format == COGL_PIXEL_FORMAT_RGBA_8888)
++        g_byte_array_append (array, &color->alpha, 1);
++    }
++}
++
++
+ static guint8 *
+-generate_icon (const char   *url,
+-               size_t        width,
+-               size_t        height,
+-               GError      **error)
++generate_icon (const char          *url,
++               size_t               size,
++               const CoglColor  *bg_color,
++               const CoglColor  *fg_color,
++               size_t              *out_size,
++               CoglPixelFormat     *out_pixel_format,
++               GCancellable        *cancellable,
++               GError             **error)
+ {
+-  QRcode *qrcode;
+-  g_autofree guint8 *pixel_data = NULL;
+-  guint8 white_pixel[BYTES_PER_RGB_888] = {255, 255, 255};
+-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+-  guint8 black_pixel[BYTES_PER_RGB_888] = {0, 0, 0};
+-#else
+-  guint8 black_pixel[BYTES_PER_RGB_888] = {255, 0, 0};
+-#endif
+-  size_t pixel_size = sizeof (white_pixel);
+-  size_t symbol_size;
+-  size_t symbols_per_row, number_of_rows;
+-  size_t code_width;
+-  size_t code_height;
+-  size_t offset_x;
+-  size_t offset_y;
+-  size_t row, symbol, symbol_x, symbol_y;
+-
+-  qrcode = QRcode_encodeString (url, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
+-
+-  if (!qrcode)
++  g_autoptr (GByteArray) qr_matrix = NULL;
++  uint8_t qr_code[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)];
++  uint8_t temp_buf[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)];
++  static const CoglColor white_color = COGL_COLOR_INIT (255, 255, 255, 255);
++  static const CoglColor black_color = COGL_COLOR_INIT (0, 0, 0, 255);
++  CoglPixelFormat pixel_format;
++  gint pixel_size, qr_size, total_size;
++  gint column, row, i;
++
++  if (g_cancellable_set_error_if_cancelled (cancellable, error))
++    return FALSE;
++
++  if (!qrcodegen_encodeText (url,
++                             temp_buf,
++                             qr_code,
++                             qrcodegen_Ecc_MEDIUM,
++                             qrcodegen_VERSION_MIN,
++                             qrcodegen_VERSION_MAX,
++                             qrcodegen_Mask_AUTO,
++                             FALSE))
+     {
+       g_set_error (error,
+                    G_IO_ERROR,
+@@ -89,47 +120,69 @@ generate_icon (const char   *url,
+       return NULL;
+     }
+ 
+-  symbols_per_row = qrcode->width;
+-  number_of_rows = qrcode->width;
++  if (g_cancellable_set_error_if_cancelled (cancellable, error))
++    return FALSE;
+ 
+-  symbol_size = MIN (width, height) / symbols_per_row;
+-  code_width = symbol_size * symbols_per_row;
+-  code_height = symbol_size * number_of_rows;
+-  offset_x = (width - code_width) / 2;
+-  offset_y = (height - code_height) / 2;
++  qr_size = qrcodegen_getSize (qr_code);
++  pixel_size = MAX (1, size / qr_size);
++  total_size = qr_size * pixel_size;
+ 
+-  pixel_data = calloc (height, width * BYTES_PER_RGB_888);
++  if (!bg_color)
++    bg_color = &white_color;
+ 
+-  for (row = 0; row < number_of_rows; row++)
+-    {
+-      for (symbol = 0; symbol < symbols_per_row; symbol++)
+-        {
+-          guint8 *pixel;
++  if (!fg_color)
++    fg_color = &black_color;
++
++
++  if (bg_color->alpha == 255 && fg_color->alpha == 255)
++    pixel_format = COGL_PIXEL_FORMAT_RGB_888;
++  else
++    pixel_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ 
+-          if (qrcode->data[row * symbols_per_row + symbol] & 1)
+-            pixel = black_pixel;
+-          else
+-            pixel = white_pixel;
++  qr_matrix = g_byte_array_sized_new (total_size * total_size * pixel_size *
++                                      BYTES_PER_FORMAT (pixel_format));
+ 
+-          for (symbol_y = 0; symbol_y < symbol_size; symbol_y++)
++  for (column = 0; column < total_size; column++)
++    {
++      for (i = 0; i < pixel_size; i++)
++        {
++          for (row = 0; row < qr_size; row++)
+             {
+-              for (symbol_x = 0; symbol_x < symbol_size; symbol_x++)
+-                {
+-                  size_t x, y;
+-                  y = offset_y + (row * symbol_size) + symbol_y;
+-                  x = offset_x + (symbol * symbol_size) + symbol_x;
+-
+-                  memcpy (&pixel_data[(y * width + x) * pixel_size],
+-                          pixel,
+-                          pixel_size);
+-                }
++              if (qrcodegen_getModule (qr_code, column, row))
++                fill_pixel (qr_matrix, fg_color, pixel_format, pixel_size);
++              else
++                fill_pixel (qr_matrix, bg_color, pixel_format, pixel_size);
+             }
+         }
++
++      if (g_cancellable_set_error_if_cancelled (cancellable, error))
++        return FALSE;
+     }
+ 
+-  return g_steal_pointer (&pixel_data);
++  if (out_size)
++    *out_size = total_size;
++  if (pixel_format)
++    *out_pixel_format = pixel_format;
++
++  return g_byte_array_steal (qr_matrix, NULL);
++}
++
++typedef struct
++{
++  CoglPixelFormat pixel_format;
++  guint8 *pixel_data;
++  size_t  size;
++} QrCodeData;
++
++static void
++qr_code_data_free (QrCodeData *qrcode_data)
++{
++  g_free (qrcode_data->pixel_data);
++  g_free (qrcode_data);
+ }
+ 
++G_DEFINE_AUTOPTR_CLEANUP_FUNC (QrCodeData, qr_code_data_free);
++
+ static void
+ qr_code_generator_thread (GTask        *task,
+                           gpointer      source,
+@@ -139,13 +192,37 @@ qr_code_generator_thread (GTask        *task,
+   GError *error = NULL;
+   ShellQrCodeGenerator *self = task_data;
+   g_autofree guint8 *pixel_data = NULL;
++  g_autoptr (QrCodeData) qrcode_data = NULL;
++  CoglPixelFormat pixel_format;
++  size_t actual_size;
+ 
+-  pixel_data = generate_icon (self->priv->url, self->priv->width, self->priv->height, &error);
++  if (g_task_return_error_if_cancelled (task))
++    return;
++
++  pixel_data = generate_icon (self->priv->url,
++                              MAX (self->priv->width, self->priv->height),
++                              self->priv->bg_color,
++                              self->priv->fg_color,
++                              &actual_size,
++                              &pixel_format,
++                              cancellable,
++                              &error);
++
++  if (g_task_return_error_if_cancelled (task))
++    return;
+ 
+   if (error != NULL)
+-    g_task_return_error (task, error);
+-  else
+-    g_task_return_pointer (task, g_steal_pointer (&pixel_data), NULL);
++    {
++      g_task_return_error (task, error);
++      return;
++    }
++
++  qrcode_data = g_new (QrCodeData, 1);
++  qrcode_data->pixel_data = g_steal_pointer (&pixel_data);
++  qrcode_data->size = actual_size;
++  qrcode_data->pixel_format = pixel_format;
++  g_task_return_pointer (task, g_steal_pointer (&qrcode_data),
++                         (GDestroyNotify) qr_code_data_free);
+ }
+ 
+ static void
+@@ -160,16 +237,16 @@ on_image_task_complete (ShellQrCodeGenerator *self,
+   ClutterBackend *backend =
+     clutter_context_get_backend (clutter_context);
+   CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+-  guint8 *pixel_data;
++  g_autoptr (QrCodeData) qrcode_data = NULL;
+   g_autoptr (ClutterContent) content = NULL;
+   g_autoptr (GError) error = NULL;
+   gboolean data_set;
+ 
+-  pixel_data = g_task_propagate_pointer (G_TASK (result), &error);
++  qrcode_data = g_task_propagate_pointer (G_TASK (result), &error);
+ 
+   if (error != NULL)
+     {
+-      g_task_return_error (self->priv->icon_task, error);
++      g_task_return_error (self->priv->icon_task, g_steal_pointer (&error));
+       return;
+     }
+ 
+@@ -177,16 +254,17 @@ on_image_task_complete (ShellQrCodeGenerator *self,
+                                                       self->priv->height);
+   data_set = st_image_content_set_data (ST_IMAGE_CONTENT (content),
+                                         ctx,
+-                                        pixel_data,
+-                                        COGL_PIXEL_FORMAT_RGB_888,
+-                                        self->priv->width,
+-                                        self->priv->height,
+-                                        self->priv->width * BYTES_PER_RGB_888,
++                                        qrcode_data->pixel_data,
++                                        qrcode_data->pixel_format,
++                                        qrcode_data->size,
++                                        qrcode_data->size,
++                                        qrcode_data->size *
++                                        BYTES_PER_FORMAT (qrcode_data->pixel_format),
+                                         &error);
+ 
+   if (!data_set)
+     {
+-      g_task_return_error (self->priv->icon_task, error);
++      g_task_return_error (self->priv->icon_task, g_steal_pointer (&error));
+       return;
+     }
+ 
+@@ -195,19 +273,30 @@ on_image_task_complete (ShellQrCodeGenerator *self,
+ }
+ 
+ /**
+- * shell_qr_code_generator_generator_qr_code:
+- * @qr_code_generator: the #ShellQrCodeGenerator
+- * @stream: The stream for the QR code generator
++ * shell_qr_code_generator_generate_qr_code:
++ * @self: the #ShellQrCodeGenerator
++ * @url: the URL of which generate the qr code
++ * @width: The width of the qrcode
++ * @height: The height of the qrcode
++ * @bg_color: (nullable): The background color of the code
++ * @fg_color: (nullable): The foreground color of the code
++ * @cancellable: (nullable): A #GCancellable to cancel teh operation
+  * @callback: (scope async): function to call returning success or failure
+- * of the async grabbing
++ *   of the async grabbing
+  * @user_data: the data to pass to callback function
+  *
++ * Generates the QrCode asynchronously.
++ *
++ * Use shell_qr_code_generator_generate_qr_code_finish() to complete it.
+  */
+ void
+ shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
+                                           const char           *url,
+                                           size_t                width,
+                                           size_t                height,
++                                          const CoglColor   *bg_color,
++                                          const CoglColor   *fg_color,
++                                          GCancellable         *cancellable,
+                                           GAsyncReadyCallback   callback,
+                                           gpointer              user_data)
+ {
+@@ -226,7 +315,20 @@ shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
+                                  shell_qr_code_generator_generate_qr_code,
+                                  G_IO_ERROR,
+                                  G_IO_ERROR_INVALID_DATA,
+-                                 "No valid QR code URI provided");
++                                 "No valid QR code uri is provided");
++      return;
++    }
++
++  if (width != height)
++    {
++      if (callback)
++        g_task_report_new_error (self,
++                                 callback,
++                                 user_data,
++                                 shell_qr_code_generator_generate_qr_code,
++                                 G_IO_ERROR,
++                                 G_IO_ERROR_INVALID_DATA,
++                                 "Qr code size mismatch");
+       return;
+     }
+ 
+@@ -247,12 +349,15 @@ shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
+   priv->url = g_strdup (url);
+   priv->width = width;
+   priv->height = height;
++  priv->bg_color = cogl_color_copy (bg_color);
++  priv->fg_color = cogl_color_copy (fg_color);
+ 
+-  priv->icon_task = g_task_new (self, NULL, callback, user_data);
++  priv->icon_task = g_task_new (self, cancellable, callback, user_data);
+   g_task_set_source_tag (priv->icon_task, shell_qr_code_generator_generate_qr_code);
+   g_task_set_task_data (priv->icon_task, self, NULL);
+ 
+-  priv->image_task = g_task_new (self, NULL, (GAsyncReadyCallback) on_image_task_complete, NULL);
++  priv->image_task = g_task_new (self, cancellable,
++                                 (GAsyncReadyCallback) on_image_task_complete, NULL);
+   g_task_set_source_tag (priv->image_task, on_image_task_complete);
+   g_task_set_task_data (priv->image_task, self, NULL);
+   g_task_run_in_thread (priv->image_task, qr_code_generator_thread);
+@@ -260,13 +365,15 @@ shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
+ 
+ /**
+  * shell_qr_code_generator_generate_qr_code_finish:
++ * @self: the #ShellQrCodeGenerator
+  * @result: the #GAsyncResult that was provided to the callback
+  * @error: #GError for error reporting
+  *
+- * Finish the asynchronous operation started by shell_qr_code_generator_generate_qr_code()
++ * Finish the asynchronous operation started by
++ * shell_qr_code_generator_generate_qr_code()
+  * and obtain its result.
+  *
+- * Returns: (transfer full):  a #GIcon of the QR code
++ * Returns: (transfer full): a #GIcon of the QR code
+  *
+  */
+ GIcon *
+@@ -279,6 +386,9 @@ shell_qr_code_generator_generate_qr_code_finish (ShellQrCodeGenerator   *self,
+   g_return_val_if_fail (g_async_result_is_tagged (result,
+                                                   shell_qr_code_generator_generate_qr_code),
+                         NULL);
++  g_clear_pointer (&self->priv->url, g_free);
++  g_clear_pointer (&self->priv->bg_color, cogl_color_free);
++  g_clear_pointer (&self->priv->fg_color, cogl_color_free);
+   return g_task_propagate_pointer (G_TASK (result), error);
+ }
+ 
+diff --git a/src/shell-qr-code-generator.h b/src/shell-qr-code-generator.h
+index a12ce22..0aed569 100644
+--- a/src/shell-qr-code-generator.h
++++ b/src/shell-qr-code-generator.h
+@@ -15,13 +15,16 @@ G_DECLARE_FINAL_TYPE (ShellQrCodeGenerator, shell_qr_code_generator,
+ 
+ ShellQrCodeGenerator *shell_qr_code_generator_new (void);
+ 
+-void    shell_qr_code_generator_generate_qr_code           (ShellQrCodeGenerator   *qr_code_generator,
++void    shell_qr_code_generator_generate_qr_code           (ShellQrCodeGenerator   *self,
+                                                             const char             *url,
+                                                             size_t                  width,
+                                                             size_t                  height,
++                                                            const CoglColor        *bg_color,
++                                                            const CoglColor        *fg_color,
++                                                            GCancellable           *cancellable,
+                                                             GAsyncReadyCallback     callback,
+                                                             gpointer                user_data);
+-GIcon *shell_qr_code_generator_generate_qr_code_finish   (ShellQrCodeGenerator   *qr_code_generator,
++GIcon *shell_qr_code_generator_generate_qr_code_finish   (ShellQrCodeGenerator   *self,
+                                                           GAsyncResult           *result,
+                                                           GError                **error);
+ 
diff -pruN 49.0-1/debian/patches/ubuntu-authd/src-Add-function-for-generating-QR-codes.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/src-Add-function-for-generating-QR-codes.patch
--- 49.0-1/debian/patches/ubuntu-authd/src-Add-function-for-generating-QR-codes.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/src-Add-function-for-generating-QR-codes.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,406 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 11:31:33 -0500
+Subject: src: Add function for generating QR codes
+
+In order to implement Web Login in GDM we're going to need to be
+able to generate QR codes. It's possible we'll need QR codes for
+other future features as well.
+
+As a first step, this commit adds API for outputting a GIcon
+of a QR code for a specified URL.
+---
+ .../gnome-shell-sass/widgets/_login-lock.scss      |   8 +
+ meson.build                                        |   2 +
+ src/meson.build                                    |   3 +
+ src/shell-qr-code-generator.c                      | 289 +++++++++++++++++++++
+ src/shell-qr-code-generator.h                      |  28 ++
+ 5 files changed, 330 insertions(+)
+ create mode 100644 src/shell-qr-code-generator.c
+ create mode 100644 src/shell-qr-code-generator.h
+
+diff --git a/data/theme/gnome-shell-sass/widgets/_login-lock.scss b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+index 1484e98..f9183f1 100644
+--- a/data/theme/gnome-shell-sass/widgets/_login-lock.scss
++++ b/data/theme/gnome-shell-sass/widgets/_login-lock.scss
+@@ -392,3 +392,11 @@ $_gdm_dialog_width: 25em;
+     }
+   }
+ }
++
++// QR Code
++.qr-code {
++  background: black;
++  border-radius: $base_border_radius * .5;
++  border-color: white;
++  border-width: 1em;
++}
+diff --git a/meson.build b/meson.build
+index df593c6..6c1d1ad 100644
+--- a/meson.build
++++ b/meson.build
+@@ -31,6 +31,7 @@ systemd_req = '>= 246'
+ gnome_desktop_req = '>= 40'
+ pipewire_req = '>= 0.3.49'
+ pango_req = '>= 1.46.0'
++qrencode_req = '>= 4.1.1'
+ 
+ nm_req = '>= 1.10.4'
+ secret_req = '>= 0.18'
+@@ -86,6 +87,7 @@ polkit_dep = dependency('polkit-agent-1', version: polkit_req)
+ schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
+ gnome_desktop_dep = dependency('gnome-desktop-4', version: gnome_desktop_req)
+ pango_dep = dependency('pango', version: pango_req)
++qrencode_dep = dependency('libqrencode', version: qrencode_req)
+ 
+ have_fonts = mutter_dep.get_variable('have_fonts') == 'true'
+ have_x11 = mutter_dep.get_variable('have_x11') == 'true'
+diff --git a/src/meson.build b/src/meson.build
+index a4ce889..f7d9bff 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -75,6 +75,7 @@ gnome_shell_deps = [
+   libsystemd_dep,
+   libpipewire_dep,
+   pango_dep,
++  qrencode_dep,
+ ]
+ 
+ if have_x11_client
+@@ -175,6 +176,8 @@ libshell_sources = [
+   'shell-perf-log.c',
+   'shell-polkit-authentication-agent.c',
+   'shell-polkit-authentication-agent.h',
++  'shell-qr-code-generator.c',
++  'shell-qr-code-generator.h',
+   'shell-screenshot.c',
+   'shell-secure-text-buffer.c',
+   'shell-secure-text-buffer.h',
+diff --git a/src/shell-qr-code-generator.c b/src/shell-qr-code-generator.c
+new file mode 100644
+index 0000000..6c7da8c
+--- /dev/null
++++ b/src/shell-qr-code-generator.c
+@@ -0,0 +1,289 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++
++#include <clutter/clutter.h>
++#include <cogl/cogl.h>
++#include <meta/display.h>
++#include <meta/util.h>
++#include <meta/meta-plugin.h>
++#include <st/st.h>
++
++#include <qrencode.h>
++
++#include "shell-global.h"
++#include "shell-qr-code-generator.h"
++
++#define BYTES_PER_RGB_888 3
++
++typedef struct _ShellQrCodeGeneratorPrivate  ShellQrCodeGeneratorPrivate;
++
++struct _ShellQrCodeGenerator
++{
++  GObject parent_instance;
++
++  ShellQrCodeGeneratorPrivate *priv;
++};
++
++struct _ShellQrCodeGeneratorPrivate
++{
++  char *url;
++  size_t width;
++  size_t height;
++  GTask *image_task;
++  GTask *icon_task;
++};
++
++G_DEFINE_TYPE_WITH_PRIVATE (ShellQrCodeGenerator, shell_qr_code_generator, G_TYPE_OBJECT);
++
++static void
++shell_qr_code_generator_dispose (GObject *object)
++{
++  ShellQrCodeGenerator *self = SHELL_QR_CODE_GENERATOR (object);
++  g_clear_pointer (&self->priv->url, g_free);
++}
++
++static void
++shell_qr_code_generator_class_init (ShellQrCodeGeneratorClass *qr_code_generator_class)
++{
++  GObjectClass *gobject_class = (GObjectClass *) qr_code_generator_class;
++  gobject_class->dispose = shell_qr_code_generator_dispose;
++}
++
++static void
++shell_qr_code_generator_init (ShellQrCodeGenerator *qr_code_generator)
++{
++  qr_code_generator->priv = shell_qr_code_generator_get_instance_private (qr_code_generator);
++}
++
++static guint8 *
++generate_icon (const char   *url,
++               size_t        width,
++               size_t        height,
++               GError      **error)
++{
++  QRcode *qrcode;
++  g_autofree guint8 *pixel_data = NULL;
++  guint8 white_pixel[BYTES_PER_RGB_888] = {255, 255, 255};
++#if G_BYTE_ORDER == G_LITTLE_ENDIAN
++  guint8 black_pixel[BYTES_PER_RGB_888] = {0, 0, 0};
++#else
++  guint8 black_pixel[BYTES_PER_RGB_888] = {255, 0, 0};
++#endif
++  size_t pixel_size = sizeof (white_pixel);
++  size_t symbol_size;
++  size_t symbols_per_row, number_of_rows;
++  size_t code_width;
++  size_t code_height;
++  size_t offset_x;
++  size_t offset_y;
++  size_t row, symbol, symbol_x, symbol_y;
++
++  qrcode = QRcode_encodeString (url, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
++
++  if (!qrcode)
++    {
++      g_set_error (error,
++                   G_IO_ERROR,
++                   G_IO_ERROR_FAILED,
++                   "QRCode generation failed for url %s",
++                   url);
++      return NULL;
++    }
++
++  symbols_per_row = qrcode->width;
++  number_of_rows = qrcode->width;
++
++  symbol_size = MIN (width, height) / symbols_per_row;
++  code_width = symbol_size * symbols_per_row;
++  code_height = symbol_size * number_of_rows;
++  offset_x = (width - code_width) / 2;
++  offset_y = (height - code_height) / 2;
++
++  pixel_data = calloc (height, width * BYTES_PER_RGB_888);
++
++  for (row = 0; row < number_of_rows; row++)
++    {
++      for (symbol = 0; symbol < symbols_per_row; symbol++)
++        {
++          guint8 *pixel;
++
++          if (qrcode->data[row * symbols_per_row + symbol] & 1)
++            pixel = black_pixel;
++          else
++            pixel = white_pixel;
++
++          for (symbol_y = 0; symbol_y < symbol_size; symbol_y++)
++            {
++              for (symbol_x = 0; symbol_x < symbol_size; symbol_x++)
++                {
++                  size_t x, y;
++                  y = offset_y + (row * symbol_size) + symbol_y;
++                  x = offset_x + (symbol * symbol_size) + symbol_x;
++
++                  memcpy (&pixel_data[(y * width + x) * pixel_size],
++                          pixel,
++                          pixel_size);
++                }
++            }
++        }
++    }
++
++  return g_steal_pointer (&pixel_data);
++}
++
++static void
++qr_code_generator_thread (GTask        *task,
++                          gpointer      source,
++                          gpointer      task_data,
++                          GCancellable *cancellable)
++{
++  GError *error = NULL;
++  ShellQrCodeGenerator *self = task_data;
++  g_autofree guint8 *pixel_data = NULL;
++
++  pixel_data = generate_icon (self->priv->url, self->priv->width, self->priv->height, &error);
++
++  if (error != NULL)
++    g_task_return_error (task, error);
++  else
++    g_task_return_pointer (task, g_steal_pointer (&pixel_data), NULL);
++}
++
++static void
++on_image_task_complete (ShellQrCodeGenerator *self,
++                        GAsyncResult         *result,
++                        gpointer              user_data)
++{
++  ShellGlobal *global = shell_global_get ();
++  ClutterStage *stage = shell_global_get_stage (global);
++  ClutterContext *clutter_context =
++    clutter_actor_get_context (CLUTTER_ACTOR (stage));
++  ClutterBackend *backend =
++    clutter_context_get_backend (clutter_context);
++  CoglContext *ctx = clutter_backend_get_cogl_context (backend);
++  guint8 *pixel_data;
++  g_autoptr (ClutterContent) content = NULL;
++  g_autoptr (GError) error = NULL;
++  gboolean data_set;
++
++  pixel_data = g_task_propagate_pointer (G_TASK (result), &error);
++
++  if (error != NULL)
++    {
++      g_task_return_error (self->priv->icon_task, error);
++      return;
++    }
++
++  content = st_image_content_new_with_preferred_size (self->priv->width,
++                                                      self->priv->height);
++  data_set = st_image_content_set_data (ST_IMAGE_CONTENT (content),
++                                        ctx,
++                                        pixel_data,
++                                        COGL_PIXEL_FORMAT_RGB_888,
++                                        self->priv->width,
++                                        self->priv->height,
++                                        self->priv->width * BYTES_PER_RGB_888,
++                                        &error);
++
++  if (!data_set)
++    {
++      g_task_return_error (self->priv->icon_task, error);
++      return;
++    }
++
++  g_task_return_pointer (self->priv->icon_task, g_steal_pointer (&content), g_object_unref);
++  g_clear_object (&self->priv->image_task);
++}
++
++/**
++ * shell_qr_code_generator_generator_qr_code:
++ * @qr_code_generator: the #ShellQrCodeGenerator
++ * @stream: The stream for the QR code generator
++ * @callback: (scope async): function to call returning success or failure
++ * of the async grabbing
++ * @user_data: the data to pass to callback function
++ *
++ */
++void
++shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
++                                          const char           *url,
++                                          size_t                width,
++                                          size_t                height,
++                                          GAsyncReadyCallback   callback,
++                                          gpointer              user_data)
++{
++  ShellQrCodeGeneratorPrivate *priv;
++
++  g_return_if_fail (SHELL_IS_QR_CODE_GENERATOR (self));
++
++  priv = self->priv;
++
++  if (!url || *url == '\0')
++    {
++      if (callback)
++        g_task_report_new_error (self,
++                                 callback,
++                                 user_data,
++                                 shell_qr_code_generator_generate_qr_code,
++                                 G_IO_ERROR,
++                                 G_IO_ERROR_INVALID_DATA,
++                                 "No valid QR code URI provided");
++      return;
++    }
++
++  if (priv->url != NULL)
++    {
++      if (callback)
++        g_task_report_new_error (self,
++                                 callback,
++                                 user_data,
++                                 shell_qr_code_generator_generate_qr_code,
++                                 G_IO_ERROR,
++                                 G_IO_ERROR_PENDING,
++                                 "Only one QR code generator operation at a time "
++                                 "is permitted");
++      return;
++    }
++
++  priv->url = g_strdup (url);
++  priv->width = width;
++  priv->height = height;
++
++  priv->icon_task = g_task_new (self, NULL, callback, user_data);
++  g_task_set_source_tag (priv->icon_task, shell_qr_code_generator_generate_qr_code);
++  g_task_set_task_data (priv->icon_task, self, NULL);
++
++  priv->image_task = g_task_new (self, NULL, (GAsyncReadyCallback) on_image_task_complete, NULL);
++  g_task_set_source_tag (priv->image_task, on_image_task_complete);
++  g_task_set_task_data (priv->image_task, self, NULL);
++  g_task_run_in_thread (priv->image_task, qr_code_generator_thread);
++}
++
++/**
++ * shell_qr_code_generator_generate_qr_code_finish:
++ * @result: the #GAsyncResult that was provided to the callback
++ * @error: #GError for error reporting
++ *
++ * Finish the asynchronous operation started by shell_qr_code_generator_generate_qr_code()
++ * and obtain its result.
++ *
++ * Returns: (transfer full):  a #GIcon of the QR code
++ *
++ */
++GIcon *
++shell_qr_code_generator_generate_qr_code_finish (ShellQrCodeGenerator   *self,
++                                                 GAsyncResult           *result,
++                                                 GError                **error)
++{
++  g_return_val_if_fail (SHELL_IS_QR_CODE_GENERATOR (self), FALSE);
++  g_return_val_if_fail (G_IS_TASK (result), FALSE);
++  g_return_val_if_fail (g_async_result_is_tagged (result,
++                                                  shell_qr_code_generator_generate_qr_code),
++                        NULL);
++  return g_task_propagate_pointer (G_TASK (result), error);
++}
++
++ShellQrCodeGenerator *
++shell_qr_code_generator_new (void)
++{
++  return g_object_new (SHELL_TYPE_QR_CODE_GENERATOR, NULL);
++}
+diff --git a/src/shell-qr-code-generator.h b/src/shell-qr-code-generator.h
+new file mode 100644
+index 0000000..a12ce22
+--- /dev/null
++++ b/src/shell-qr-code-generator.h
+@@ -0,0 +1,28 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++#ifndef __SHELL_QR_CODE_GENERATOR_H__
++#define __SHELL_QR_CODE_GENERATOR_H__
++
++/**
++ * SECTION:shell-qr-code-generator
++ * @short_description: Generates a QR code for a given url.
++ *
++ * The #ShellQrCodeGenerator object is used to generate QR codes for URLs
++ *
++ */
++#define SHELL_TYPE_QR_CODE_GENERATOR (shell_qr_code_generator_get_type ())
++G_DECLARE_FINAL_TYPE (ShellQrCodeGenerator, shell_qr_code_generator,
++                      SHELL, QR_CODE_GENERATOR, GObject)
++
++ShellQrCodeGenerator *shell_qr_code_generator_new (void);
++
++void    shell_qr_code_generator_generate_qr_code           (ShellQrCodeGenerator   *qr_code_generator,
++                                                            const char             *url,
++                                                            size_t                  width,
++                                                            size_t                  height,
++                                                            GAsyncReadyCallback     callback,
++                                                            gpointer                user_data);
++GIcon *shell_qr_code_generator_generate_qr_code_finish   (ShellQrCodeGenerator   *qr_code_generator,
++                                                          GAsyncResult           *result,
++                                                          GError                **error);
++
++#endif /* ___SHELL_QR_CODE_GENERATOR_H__ */
diff -pruN 49.0-1/debian/patches/ubuntu-authd/start-abstracting-the-unified-mechanism-handler.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/start-abstracting-the-unified-mechanism-handler.patch
--- 49.0-1/debian/patches/ubuntu-authd/start-abstracting-the-unified-mechanism-handler.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/start-abstracting-the-unified-mechanism-handler.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,741 @@
+From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
+Date: Wed, 28 Feb 2024 20:30:13 +0100
+Subject: start abstracting the unified mechanism handler
+
+As we may have different protocols under the hood.
+---
+ js/gdm/authPrompt.js          |  11 +-
+ js/gdm/const.js               |   6 +
+ js/gdm/loginDialog.js         |   4 +-
+ js/gdm/unifiedMechanism.js    | 145 +++++++++++++++++++++
+ js/gdm/util.js                | 286 ++++++++++++++++++++++++++++--------------
+ js/js-resources.gresource.xml |   2 +
+ js/ui/unlockDialog.js         |   4 +-
+ 7 files changed, 356 insertions(+), 102 deletions(-)
+ create mode 100644 js/gdm/const.js
+ create mode 100644 js/gdm/unifiedMechanism.js
+
+diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
+index 3634e3c..e61bbb9 100644
+--- a/js/gdm/authPrompt.js
++++ b/js/gdm/authPrompt.js
+@@ -147,12 +147,19 @@ export const AuthPrompt = GObject.registerClass({
+ 
+     on_key_press_event(event) {
+         if (event.get_key_symbol() === Clutter.KEY_Escape) {
+-            this.cancel();
++            this._handleCancel();
+             return Clutter.EVENT_STOP;
+         }
+         return Clutter.EVENT_PROPAGATE;
+     }
+ 
++    _handleCancel() {
++        if (this._userVerifier.cancelRequested())
++            return;
++
++        this.cancel();
++    }
++
+     _initInputRow() {
+         this._mainBox = new St.BoxLayout({
+             style_class: 'login-dialog-button-box',
+@@ -171,7 +178,7 @@ export const AuthPrompt = GObject.registerClass({
+             icon_name: 'go-previous-symbolic',
+         });
+         if (this._hasCancelButton)
+-            this.cancelButton.connect('clicked', () => this.cancel());
++            this.cancelButton.connect('clicked', () => this._handleCancel());
+         else
+             this.cancelButton.opacity = 0;
+         this._mainBox.add_child(this.cancelButton);
+diff --git a/js/gdm/const.js b/js/gdm/const.js
+new file mode 100644
+index 0000000..ad854ca
+--- /dev/null
++++ b/js/gdm/const.js
+@@ -0,0 +1,6 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++
++export const WEB_LOGIN_ROLE_NAME = 'eidp';
++export const PASSWORD_ROLE_NAME = 'password';
++export const SMARTCARD_ROLE_NAME = 'smartcard';
++export const FINGERPRINT_ROLE_NAME = 'fingerprint';
+diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
+index c9d6fbf..24c2538 100644
+--- a/js/gdm/loginDialog.js
++++ b/js/gdm/loginDialog.js
+@@ -1137,11 +1137,11 @@ export const LoginDialog = GObject.registerClass({
+ 
+         mechanisms.sort((a, b) => a.name.localeCompare(b.name));
+ 
+-        for (const {role, id, name, selectable} of mechanisms) {
++        for (const {role, id, name, selectable, protocol} of mechanisms) {
+             if (!selectable)
+                 continue;
+ 
+-            const mechanism = {serviceName, id, name, role};
++            const mechanism = {serviceName, id, name, role, protocol};
+             this._loginOptionsButton.addItem(mechanism);
+ 
+             const wasActive = activeMechanism?.id === id;
+diff --git a/js/gdm/unifiedMechanism.js b/js/gdm/unifiedMechanism.js
+new file mode 100644
+index 0000000..77ba705
+--- /dev/null
++++ b/js/gdm/unifiedMechanism.js
+@@ -0,0 +1,145 @@
++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
++
++import GObject from 'gi://GObject';
++import * as Signals from '../misc/signals.js';
++import {
++    PASSWORD_ROLE_NAME,
++    WEB_LOGIN_ROLE_NAME
++} from './const.js';
++
++export const MECHANISM_PROTOCOL = 'auth-mechanisms';
++
++const AUTH_SELECTION_COMPLETION_STATUS = 'Ok';
++
++export const SUPPORTED_ROLES = [
++    PASSWORD_ROLE_NAME,
++    WEB_LOGIN_ROLE_NAME,
++];
++
++export class UnifiedAuthService extends Signals.EventEmitter {
++    // FIXME USE GObject so we have proper signal definitions
++    // signals:
++    // : 'mechanisms-changed'
++    // : 'proto-request-handled'
++    // : 'clear-message-queue'
++    // : 'queue-message'
++    // : 'reset'
++    // : 'service-request'
++    // : 'cancel'
++
++    get protocolName() {
++        throw new GObject.NotImplementedError(
++            `protocolName in ${this.constructor.name}`);
++    }
++
++    getSupportedRoles() {
++        throw new GObject.NotImplementedError(
++            `getSupportedRoles in ${this.constructor.name}`);
++    }
++
++    getMechanisms() {
++        throw new GObject.NotImplementedError(
++            `getMechanisms in ${this.constructor.name}`);
++    }
++
++    handleProtocolRequest(_protocol, _version, _json) {
++        throw new GObject.NotImplementedError(
++            `getMechanisms in ${this.constructor.name}`);
++    }
++
++    handleMechanism(_mechanism) {
++        return false;
++    }
++
++    handleAuthSelectionResponse(_mechanism, _role, _response) {
++        return false;
++    }
++
++    handleProblem(problem) {
++        return false;
++    }
++
++    sendProtocolResponse(reply, params={}) {
++        this.emit('protocol-request-handled', reply, params);
++    }
++
++    getProtocolResponse(_mechanism, _role, _response) {
++        throw new GObject.NotImplementedError(
++            `getMechanisms in ${this.constructor.name}`);
++    }
++
++    setForegroundMechanism(_mechanism) {
++        return false;
++    }
++
++    cancelRequested() {
++        return false;
++    }
++
++    cancel() {}
++
++    reset() {}
++}
++
++export class UnifiedMechanismProtocolHandler extends  UnifiedAuthService/* Signals.EventEmitter */ {
++    get protocolName() {
++        return MECHANISM_PROTOCOL;
++    }
++
++    getSupportedRoles() {
++        return SUPPORTED_ROLES;
++    }
++
++    handleProtocolRequest(_protocol, _version, json) {
++        this.emit(`service-request`);
++        let requestObject;
++
++        try {
++            requestObject = JSON.parse(json);
++        } catch (e) {
++            logError(e);
++            return;
++        }
++
++        const authSelection = requestObject['auth-selection'];
++
++        if (authSelection)
++            this._handleAuthSelection(authSelection);
++    }
++
++    _handleAuthSelection(authSelection) {
++        const mechanisms = authSelection.mechanisms;
++        const priorityList = authSelection.priority;
++
++        if (!mechanisms)
++            return;
++
++        const ids = Object.keys(mechanisms);
++        ids.sort((a, b) => {
++            const priorityA = priorityList.indexOf(a);
++            const priorityB = priorityList.indexOf(b);
++
++            if (priorityA !== -1 && priorityB !== -1)
++                return priorityA - priorityB;
++
++            if (priorityA !== -1)
++                return -1;
++
++            if (priorityB !== -1)
++                return 1;
++
++            return 0;
++        });
++
++        this.emit('mechanisms-changed', mechanisms);
++    }
++
++    getProtocolResponse(_mechanism, role, response) {
++        return {
++            'auth-selection': {
++                status: AUTH_SELECTION_COMPLETION_STATUS,
++                mechanismId: response,
++            },
++        };
++    }
++}
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index e76055e..1ccc57b 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -11,6 +11,8 @@ import * as Main from '../ui/main.js';
+ import {loadInterfaceXML} from '../misc/fileUtils.js';
+ import * as Params from '../misc/params.js';
+ import * as SmartcardManager from '../misc/smartcardManager.js';
++import * as UnifiedMechanism from './unifiedMechanism.js';
++import * as Const from './const.js';
+ 
+ const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(
+     loadInterfaceXML('net.reactivated.Fprint.Manager'));
+@@ -29,12 +31,10 @@ export const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+ export const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+ const CLONE_FADE_ANIMATION_TIME = 250;
+ 
+-export const PASSWORD_ROLE_NAME = 'password';
+-export const SMARTCARD_ROLE_NAME = 'smartcard';
+-export const FINGERPRINT_ROLE_NAME = 'fingerprint';
+-export const WEB_LOGIN_ROLE_NAME = 'eidp';
+-
+-export const AUTH_MECHANISM_PROTOCOL = 'auth-mechanisms';
++export const PASSWORD_ROLE_NAME = Const.PASSWORD_ROLE_NAME;
++export const SMARTCARD_ROLE_NAME = Const.SMARTCARD_ROLE_NAME;
++export const FINGERPRINT_ROLE_NAME = Const.FINGERPRINT_ROLE_NAME;
++export const WEB_LOGIN_ROLE_NAME = Const.WEB_LOGIN_ROLE_NAME;
+ 
+ export const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
+ export const UNIFIED_AUTHENTICATION_KEY = 'enable-unified-authentication';
+@@ -51,13 +51,6 @@ export const ALLOWED_FAILURES_KEY = 'allowed-failures';
+ export const LOGO_KEY = 'logo';
+ export const DISABLE_USER_LIST_KEY = 'disable-user-list';
+ 
+-const AUTH_SELECTION_COMPLETION_STATUS = 'Ok';
+-
+-const UNIFIED_AUTH_SUPPORTED_ROLES = [
+-    PASSWORD_ROLE_NAME,
+-    WEB_LOGIN_ROLE_NAME,
+-];
+-
+ // Give user 48ms to read each character of a PAM message
+ const USER_READ_TIME = 48;
+ const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
+@@ -67,6 +60,7 @@ const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
+ const DiscreteServiceMechanismDefinitions = [
+     {
+         serviceName: PASSWORD_SERVICE_NAME,
++        protocol: 'discrete',
+         mechanismId: 'password',
+         mechanismName: _('Password'),
+         setting: PASSWORD_AUTHENTICATION_KEY,
+@@ -76,6 +70,7 @@ const DiscreteServiceMechanismDefinitions = [
+ 
+     {
+         serviceName: SMARTCARD_SERVICE_NAME,
++        protocol: 'discrete',
+         mechanismId: 'smartcard',
+         mechanismName: _('Smartcard'),
+         setting: SMARTCARD_AUTHENTICATION_KEY,
+@@ -85,6 +80,7 @@ const DiscreteServiceMechanismDefinitions = [
+ 
+     {
+         serviceName: FINGERPRINT_SERVICE_NAME,
++        protocol: 'discrete',
+         mechanismId: 'fingerprint',
+         mechanismName: _('Fingerprint'),
+         setting: FINGERPRINT_AUTHENTICATION_KEY,
+@@ -169,6 +165,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._publishedMechanisms = new Map();
+         this._pendingMechanisms = new Map();
+ 
++        this._unifiedAuthServices = new Map();
++        this.addUnifiedAuthService(new UnifiedMechanism.UnifiedMechanismProtocolHandler());
++
+         this._credentialManagers = {};
+ 
+         this.reauthenticating = false;
+@@ -183,6 +182,69 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this.addCredentialManager(Vmware.SERVICE_NAME, Vmware.getVmwareCredentialsManager());
+     }
+ 
++    addUnifiedAuthService(unifiedService) {
++        /* TODO: check if not implements too */
++        if (!unifiedService || !unifiedService.protocolName)
++            throw new Error('Invalid unified AuthService');
++
++        const handler = this._unifiedAuthServices.get(unifiedService.protocolName);
++        if (handler && handler !== unifiedService)
++            throw new Error(`Protocol ${unifiedService.protocolName} is already handled`);
++
++        this._unifiedAuthServices.set(unifiedService.protocolName, unifiedService);
++        this._monitorUnifiedServiceMechanisms(unifiedService);
++    }
++
++    _monitorUnifiedServiceMechanisms(unifiedService) {
++        const serviceName = UNIFIED_AUTH_SERVICE_NAME;
++        unifiedService.connectObject('mechanisms-changed', (_, mechanisms) => {
++            let mechanismsList = [];
++            for (const id of Object.keys(mechanisms)) {
++                const name = mechanisms[id].name;
++                const role = mechanisms[id].role;
++
++                if (!name || !role)
++                    continue;
++
++                const protocol = unifiedService.protocolName;
++                const selectable = mechanisms[id].selectable ?? true;
++                mechanisms[id].protocol = protocol;
++                mechanismsList.push({id, name, role, serviceName, protocol, selectable});
++            }
++
++            // FIXME: invert the logic, using mechanismsList.filter() instead.
++            mechanismsList = this._filterAuthMechanisms(mechanismsList, m => {
++                delete mechanisms[m.id];
++            });
++
++            if (unifiedService.sortMechanisms)
++                mechanismsList = mechanismsList.sort((a, b) =>
++                    unifiedService.sortMechanisms(a, b));
++
++            this._publishedMechanisms.set(serviceName, mechanisms);
++            this.emit('mechanisms-list-changed', serviceName, mechanismsList);
++        }, this);
++
++        unifiedService.connectObject('start-mechanism',
++            (_, ...args) => this._startMechanismFromUnifiedService(...args), this);
++        unifiedService.connectObject('choice-list-selected',
++            (_, ...args) => this.emit('choice-list-selected', serviceName, ...args), this);
++        unifiedService.connectObject('queue-message',
++            (_, ...args) => this._queueMessage(serviceName, ...args), this);
++        unifiedService.connectObject('queue-priority-message',
++            (_, ...args) => this._queuePriorityMessage(serviceName, ...args), this);
++        unifiedService.connectObject('clear-message-queue',
++            (_, ...args) => this._clearMessageQueue(...args), this);
++        unifiedService.connectObject('verification-complete',
++            () => this._onVerificationComplete(serviceName), this);
++        unifiedService.connectObject('verification-failed',
++            (_, shouldRetry) => this._verificationFailed(serviceName, shouldRetry).catch(logError), this);
++        unifiedService.connectObject('service-request',
++            (_, ...args) => this.emit(`service-request::${serviceName}`, ...args), this);
++        unifiedService.connectObject('reset', () => this.emit('reset'), this);
++        unifiedService.connectObject('cancel', () => this.cancel(), this);
++    }
++
+     addCredentialManager(serviceName, credentialManager) {
+         if (this._credentialManagers[serviceName])
+             return;
+@@ -239,6 +301,12 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         if (this._cancellable)
+             this._cancellable.cancel();
+ 
++        [...this._unifiedAuthServices.values()].forEach(s => s.cancel());
++        if (this._mechanismsListChangedSignalId) {
++            this.disconnect(this._mechanismsListChangedSignalId);
++            this._mechanismsListChangedSignalId = 0;
++        }
++
+         if (this._userVerifier) {
+             this._userVerifier.call_cancel_sync(null);
+             this.clear();
+@@ -284,11 +352,18 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._smartcardManager = null;
+ 
+         this._fingerprintManager = null;
++        this._unifiedAuthServices.forEach(s => s.disconnectObject(this));
++        this._unifiedAuthServices.clear();
+ 
+         for (let service in this._credentialManagers)
+             this.removeCredentialManager(service);
+     }
+ 
++    cancelRequested() {
++        return [...this._unifiedAuthServices.values()].some(service =>
++            service.cancelRequested());
++    }
++
+     selectChoice(serviceName, key) {
+         this._userVerifierChoiceList.call_select_choice(serviceName, key, this._cancellable, null);
+     }
+@@ -306,10 +381,23 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         }
+     }
+ 
+-    async replyWithJSON(serviceName, json) {
++    async _replyWithJSON(serviceName, json, params) {
++        const cancellable = this._cancellable;
+         try {
+-            await this._handlePendingMessages();
+-            this._userVerifierCustomJSON.call_reply(serviceName, json, this._cancellable, null);
++            if (!params.ignoreMessageWait)
++                await this._handlePendingMessages();
++        } catch (e) {
++            if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
++                return;
++
++            logError(e);
++            this._userVerifierCustomJSON.call_report_error(serviceName,
++                `Unexpected error: ${e}`, cancellable, null);
++            return;
++        }
++
++        try {
++            this._userVerifierCustomJSON.call_reply(serviceName, json, cancellable, null);
+         } catch (e) {
+             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                 logError(e);
+@@ -690,9 +778,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             if (!definition)
+                 return null;
+ 
+-            const {mechanismName, mechanismId, role, serviceName} = definition;
++            const {mechanismName, mechanismId, role, serviceName, protocol} = definition;
+ 
+-            return {name: mechanismName, id: mechanismId, role, serviceName};
++            return {name: mechanismName, id: mechanismId, role, serviceName, protocol};
+         }
+ 
+         return null;
+@@ -707,10 +795,19 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+             return;
+ 
+         if (foregroundMechanism?.role === mechanism?.role &&
++            foregroundMechanism?.id === mechanism?.id &&
+             foregroundMechanism?.mechanismName === mechanism?.mechanismName &&
+             foregroundMechanism?.serviceName === mechanism?.serviceName)
+             return;
+ 
++        if (mechanism.serviceName === UNIFIED_AUTH_SERVICE_NAME) {
++            const unifiedService = this._unifiedAuthServices.get(mechanism.protocol);
++            if (unifiedService.setForegroundMechanism(mechanism)) {
++                this._foregroundMechanism = mechanism;
++                return;
++            }
++        }
++
+         this.emit('foreground-mechanism-changed');
+     }
+ 
+@@ -849,15 +946,23 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     }
+ 
+     _startMechanismFromUnifiedService(mechanism) {
++        const unifiedService = this._unifiedAuthServices.get(mechanism.protocol);
++        if (!unifiedService) {
++            logError(new Error(`No unified services found for protocol ${mechanism.protocol}`));
++            return;
++        }
++
++        if (unifiedService.handleMechanism(mechanism))
++            return;
++
+         const roleHandlers = {
+-            [WEB_LOGIN_ROLE_NAME]: this._startWebLogin.bind(this),
+-            [PASSWORD_ROLE_NAME]: this._startPasswordLogin.bind(this),
++            [WEB_LOGIN_ROLE_NAME]: this._startWebLogin,
++            [PASSWORD_ROLE_NAME]: this._startQuestionLogin,
+         };
+ 
+-        let handler = roleHandlers[mechanism.role];
+-
++        const handler = roleHandlers[mechanism.role];
+         if (handler)
+-            handler(mechanism.serviceName, mechanism.id);
++            handler.call(this, mechanism.serviceName, mechanism.id);
+     }
+ 
+     _shouldStartBackgroundService(serviceName) {
+@@ -907,6 +1012,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     _beginVerification() {
+         const foregroundService = this._getForegroundService();
+ 
++        if (this._mechanismsListChangedSignalId)
++            this.disconnect(this._mechanismsListChangedSignalId);
+         this._mechanismsListChangedSignalId =
+             this.connect('mechanisms-list-changed',
+                 (_, ...args) => this._onMechanismsListChanged(...args));
+@@ -940,28 +1047,36 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this.emit('ask-question', serviceName, prompt, true);
+     }
+ 
+-    async _replyWithAuthSelectionResponse(serviceName, role, response) {
+-        const mechanismId = this._pendingMechanisms.get(role);
++    _replyWithAuthSelectionResponse(serviceName, role, response) {
++        const mechanism = this._getPendingMechanismForRole(role);
++        if (!mechanism)
++            return;
++
+         this._pendingMechanisms.delete(role);
++        const authService = this._unifiedAuthServices.get(mechanism.protocol);
+ 
+-        const reply = {
+-            'auth-selection': {
+-                'status': AUTH_SELECTION_COMPLETION_STATUS,
+-                [mechanismId]: response,
+-            },
+-        };
++        if (!authService)
++            throw new Error(`No authentication service for protocol ${mechanism.protocol}`);
+ 
+-        await this.replyWithJSON(serviceName, JSON.stringify(reply));
++        if (authService.handleAuthSelectionResponse(mechanism, role, response))
++            return;
++
++        const reply = authService.getProtocolResponse(mechanism, role, response);
++        authService.sendProtocolResponse(reply);
+     }
+ 
+     _startWebLogin(serviceName, mechanismId) {
+         const mechanisms = this._publishedMechanisms.get(serviceName);
+ 
+-        if (!mechanisms)
++        if (!mechanisms) {
++            logError(new Error(`No mechanisms found for ${serviceName}`));
+             return;
++        }
+ 
+-        if (!mechanisms[mechanismId])
++        if (!mechanisms[mechanismId]) {
++            logError(new Error(`No mechanisms found for ID ${mechanismId}`));
+             return;
++        }
+ 
+         const {init_prompt: initPrompt, link_prompt: linkPrompt, uri, code, role, timeout} = mechanisms[mechanismId];
+ 
+@@ -988,8 +1103,22 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         }
+     }
+ 
++    _getPendingMechanismForRole(role) {
++        const mechanismId = this._pendingMechanisms.get(role);
++        if (!mechanismId)
++            return null;
++
++        return [...this._publishedMechanisms.values()].find(m => m[mechanismId])?.[mechanismId];
++    }
++
+     webLoginDone(serviceName) {
+         this._clearWebLoginTimeout();
++
++        const mechanism = this._getPendingMechanismForRole(WEB_LOGIN_ROLE_NAME);
++        const authService = this._unifiedAuthServices.get(mechanism?.protocol);
++        if (!authService)
++            return;
++
+         this._replyWithAuthSelectionResponse(serviceName, WEB_LOGIN_ROLE_NAME, {});
+     }
+ 
+@@ -1016,73 +1145,29 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         });
+     }
+ 
+-    _handleAuthSelection(serviceName, authSelection) {
+-        let mechanisms = authSelection.mechanisms;
+-        const priorityList = authSelection.priority;
++    _onCustomJSONRequest(client, serviceName, protocol, version, json) {
++        const authService = this._unifiedAuthServices.get(protocol);
++        if (!authService) {
++            console.log(`Got a request using protocol ${protocol} v${version} but it was ignored`);
+ 
+-        if (!mechanisms)
++            this._userVerifierCustomJSON.call_report_error(serviceName,
++                `Unsupported Protocol: ${protocol} ${version}`, this._cancellable, null);
+             return;
+-
+-        const ids = Object.keys(mechanisms);
+-        ids.sort((a, b) => {
+-            const priorityA = priorityList.indexOf(a);
+-            const priorityB = priorityList.indexOf(b);
+-
+-            if (priorityA !== -1 && priorityB !== -1)
+-                return priorityA - priorityB;
+-
+-            if (priorityA !== -1)
+-                return -1;
+-
+-            if (priorityB !== -1)
+-                return 1;
+-
+-            return 0;
+-        });
+-
+-        let mechanismsList = [];
+-        for (const id of ids) {
+-            const name = mechanisms[id].name;
+-            const role = mechanisms[id].role;
+-
+-            if (!name || !role)
+-                continue;
+-
+-            const selectable = mechanisms[id].selectable;
+-            mechanismsList.push({id, name, role, serviceName, selectable});
+         }
+ 
+-        mechanismsList = this._filterAuthMechanisms(mechanismsList, mechanism => {
+-            delete mechanisms[mechanism.id];
++        const id = authService.connect('protocol-request-handled', (_, reply, params) => {
++            authService.disconnect(id);
++            this._replyWithJSON(serviceName, JSON.stringify(reply), params).catch(
++                logError);
+         });
+ 
+-        log(`newly published mechanisms: ${JSON.stringify(mechanisms)}`);
+-        this._publishedMechanisms.set(serviceName, mechanisms);
+-
+-        this.emit('mechanisms-list-changed', serviceName, mechanismsList);
+-    }
+-
+-    _handleAuthMechanismsRequest(serviceName, requestObject) {
+-        const authSelection = requestObject['auth-selection'];
+-
+-        if (authSelection)
+-            this._handleAuthSelection(serviceName, authSelection);
+-    }
+-
+-    _onCustomJSONRequest(client, serviceName, protocol, version, json) {
+-        this.emit(`service-request::${serviceName}`);
+-
+-        if (protocol === AUTH_MECHANISM_PROTOCOL) {
+-            let requestObject;
+-
+-            try {
+-                requestObject = JSON.parse(json);
+-            } catch (e) {
+-                logError(e);
+-                return;
+-            }
++        try {
++            authService.handleProtocolRequest(protocol, version, json);
++        } catch (e) {
++            logError(e);
+ 
+-            this._handleAuthMechanismsRequest(serviceName, requestObject);
++            this._userVerifierCustomJSON?.call_report_error(serviceName,
++                e.toString(), this._cancellable, null);
+         }
+     }
+ 
+@@ -1112,6 +1197,12 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+     _onProblem(client, serviceName, problem) {
+         this.emit(`service-request::${serviceName}`);
+ 
++        if (serviceName === UNIFIED_AUTH_SERVICE_NAME) {
++            if ([...this._unifiedAuthServices.values()].some(service =>
++                service.handleProblem(problem)))
++                return
++        }
++
+         const isFingerprint = this.serviceIsFingerprint(serviceName);
+ 
+         if (!this.serviceIsForeground(serviceName) && !isFingerprint)
+@@ -1181,8 +1272,10 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         const unifiedAuthAvailable = this._activeServices.has(UNIFIED_AUTH_SERVICE_NAME) && !this._unavailableServices.has(UNIFIED_AUTH_SERVICE_NAME);
+         for (const definition of DiscreteServiceMechanismDefinitions) {
+             const enabled = this._isDiscreteServiceEnabled(definition);
+-            const available = this._activeServices.has(definition.serviceName) && !this._unavailableServices.has(definition.serviceName);
+-            const supportedByUnifiedAuth = unifiedAuthAvailable && UNIFIED_AUTH_SUPPORTED_ROLES.includes(definition.role);
++            const available = this._activeServices.has(definition.serviceName) &&
++                !this._unavailableServices.has(definition.serviceName);
++            const supportedByUnifiedAuth = unifiedAuthAvailable &&
++                UnifiedMechanism.SUPPORTED_ROLES.includes(definition.role);
+ 
+             const mechanismsList = [];
+             if (enabled && available && !supportedByUnifiedAuth) {
+@@ -1203,6 +1296,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+         this._activeServices.clear();
+         this._unavailableServices.clear();
+         this._activeServices.clear();
++        [...this._unifiedAuthServices.values()].forEach(s => s.reset());
+         if (this._mechanismsListChangedSignalId) {
+             this.disconnect(this._mechanismsListChangedSignalId);
+             this._mechanismsListChangedSignalId = 0;
+diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
+index 26d5f35..684af12 100644
+--- a/js/js-resources.gresource.xml
++++ b/js/js-resources.gresource.xml
+@@ -5,12 +5,14 @@
+     <file>gdm/authMenuButton.js</file>
+     <file>gdm/authPrompt.js</file>
+     <file>gdm/batch.js</file>
++    <file>gdm/const.js</file>
+     <file>gdm/credentialManager.js</file>
+     <file>gdm/loginDialog.js</file>
+     <file>gdm/oVirt.js</file>
+     <file>gdm/realmd.js</file>
+     <file>gdm/util.js</file>
+     <file>gdm/vmware.js</file>
++    <file>gdm/unifiedMechanism.js</file>
+     <file>gdm/webLogin.js</file>
+ 
+     <file>extensions/extension.js</file>
+diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
+index 8f1791f..9e0827f 100644
+--- a/js/ui/unlockDialog.js
++++ b/js/ui/unlockDialog.js
+@@ -885,11 +885,11 @@ export const UnlockDialog = GObject.registerClass({
+ 
+         mechanisms.sort((a, b) => a.name.localeCompare(b.name));
+ 
+-        for (const {role, id, name, selectable} of mechanisms) {
++        for (const {role, id, name, selectable, protocol} of mechanisms) {
+             if (!selectable)
+                 continue;
+ 
+-            const mechanism = {serviceName, id, name, role};
++            const mechanism = {serviceName, id, name, role, protocol};
+             this._loginOptionsButton.addItem(mechanism);
+ 
+             const wasActive = activeMechanism?.id === id;
diff -pruN 49.0-1/debian/patches/ubuntu-authd/unlockDialog-Add-Login-Options-menu-button.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/unlockDialog-Add-Login-Options-menu-button.patch
--- 49.0-1/debian/patches/ubuntu-authd/unlockDialog-Add-Login-Options-menu-button.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/unlockDialog-Add-Login-Options-menu-button.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,187 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 13:41:39 -0500
+Subject: unlockDialog: Add Login Options menu button
+
+---
+ js/gdm/util.js        |  2 +-
+ js/ui/unlockDialog.js | 77 ++++++++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 65 insertions(+), 14 deletions(-)
+
+diff --git a/js/gdm/util.js b/js/gdm/util.js
+index dd8f4b0..b0d775e 100644
+--- a/js/gdm/util.js
++++ b/js/gdm/util.js
+@@ -886,7 +886,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
+ 
+     _generateMechanismsFromDiscreteServices() {
+         for (const definition of DiscreteServiceMechanismDefinitions) {
+-            const enabled = this._settings.get_boolean(definition.setting);
++            const enabled = this._isDiscreteServiceEnabled(definition);
+             const available = this._activeServices.has(definition.serviceName) && !this._unavailableServices.has(definition.serviceName);
+ 
+             let mechanismsList = [];
+diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
+index ec57eb9..dde6bd3 100644
+--- a/js/ui/unlockDialog.js
++++ b/js/ui/unlockDialog.js
+@@ -15,6 +15,7 @@ import * as Main from './main.js';
+ import * as MessageTray from './messageTray.js';
+ import * as SwipeTracker from './swipeTracker.js';
+ import {formatDateWithCFormatString} from '../misc/dateUtils.js';
++import * as AuthMenuButton from '../gdm/authMenuButton.js';
+ import * as AuthPrompt from '../gdm/authPrompt.js';
+ import {AuthPromptStatus} from '../gdm/authPrompt.js';
+ import {MprisSource} from './mpris.js';
+@@ -430,12 +431,12 @@ class UnlockDialogClock extends St.BoxLayout {
+ 
+ const UnlockDialogLayout = GObject.registerClass(
+ class UnlockDialogLayout extends Clutter.LayoutManager {
+-    _init(stack, notifications, switchUserButton) {
++    _init(stack, notifications, menuButtons) {
+         super._init();
+ 
+         this._stack = stack;
+         this._notifications = notifications;
+-        this._switchUserButton = switchUserButton;
++        this._menuButtons = menuButtons;
+     }
+ 
+     vfunc_get_preferred_width(container, forHeight) {
+@@ -487,22 +488,22 @@ class UnlockDialogLayout extends Clutter.LayoutManager {
+ 
+         this._stack.allocate(actorBox);
+ 
+-        // Switch User button
+-        if (this._switchUserButton.visible) {
++        // Switch User and Login Options buttons
++        if (this._menuButtons.visible) {
+             let [, , natWidth, natHeight] =
+-                this._switchUserButton.get_preferred_size();
++                this._menuButtons.get_preferred_size();
+ 
+-            const textDirection = this._switchUserButton.get_text_direction();
++            const textDirection = this._menuButtons.get_text_direction();
+             if (textDirection === Clutter.TextDirection.RTL)
+                 actorBox.x1 = box.x1 + natWidth;
+             else
+-                actorBox.x1 = box.x2 - (natWidth * 2);
++                actorBox.x1 = box.x2 - natWidth;
+ 
+-            actorBox.y1 = box.y2 - (natHeight * 2);
++            actorBox.y1 = box.y2 - natHeight;
+             actorBox.x2 = actorBox.x1 + natWidth;
+             actorBox.y2 = actorBox.y1 + natHeight;
+ 
+-            this._switchUserButton.allocate(actorBox);
++            this._menuButtons.allocate(actorBox);
+         }
+     }
+ });
+@@ -612,19 +613,40 @@ export const UnlockDialog = GObject.registerClass({
+         this._notificationsBox = new NotificationsBox();
+         this._notificationsBox.connect('wake-up-screen', () => this.emit('wake-up-screen'));
+ 
++        this._menuButtons = new St.BoxLayout({
++            opacity: 0,
++            style_class: 'login-dialog-menu-button-box',
++            x_align: Clutter.ActorAlign.END,
++            y_align: Clutter.ActorAlign.END,
++        });
++
++        // Login Options button
++        this._loginOptionsButton = new AuthMenuButton.AuthMenuButton({
++            title: _('Login Options'),
++            iconName: 'dialog-password-symbolic',
++        });
++        this._loginOptionsButton.connect('active-item-changed',
++            () => {
++                const activeMechanism = this._loginOptionsButton.getActiveItem();
++                if (activeMechanism)
++                    this._authPrompt.setForegroundMechanism(activeMechanism);
++            });
++        this._loginOptionsButton.updateSensitivity(true);
++        this._menuButtons.add_child(this._loginOptionsButton);
++
+         // Switch User button
+         this._otherUserButton = new St.Button({
+             style_class: 'login-dialog-button switch-user-button',
+             accessible_name: _('Log in as another user'),
+             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+             reactive: false,
+-            opacity: 0,
+             x_align: Clutter.ActorAlign.END,
+             y_align: Clutter.ActorAlign.END,
+             icon_name: 'system-users-symbolic',
+         });
+         this._otherUserButton.set_pivot_point(0.5, 0.5);
+         this._otherUserButton.connect('clicked', this._otherUserClicked.bind(this));
++        this._menuButtons.add_child(this._otherUserButton);
+ 
+         this._screenSaverSettings = new Gio.Settings({schema_id: 'org.gnome.desktop.screensaver'});
+ 
+@@ -647,11 +669,11 @@ export const UnlockDialog = GObject.registerClass({
+         mainBox.add_constraint(new Layout.MonitorConstraint({primary: true}));
+         mainBox.add_child(this._stack);
+         mainBox.add_child(this._notificationsBox);
+-        mainBox.add_child(this._otherUserButton);
++        mainBox.add_child(this._menuButtons);
+         mainBox.layout_manager = new UnlockDialogLayout(
+             this._stack,
+             this._notificationsBox,
+-            this._otherUserButton);
++            this._menuButtons);
+         this.add_child(mainBox);
+ 
+         this._idleMonitor = global.backend.get_core_idle_monitor();
+@@ -745,6 +767,7 @@ export const UnlockDialog = GObject.registerClass({
+             this._authPrompt.connect('failed', this._fail.bind(this));
+             this._authPrompt.connect('cancelled', this._fail.bind(this));
+             this._authPrompt.connect('reset', this._onReset.bind(this));
++            this._authPrompt.connect('mechanisms-changed', this._onMechanismsChanged.bind(this));
+             this._promptBox.add_child(this._authPrompt);
+         }
+ 
+@@ -824,7 +847,7 @@ export const UnlockDialog = GObject.registerClass({
+             translation_y: -FADE_OUT_TRANSLATION * progress * scaleFactor,
+         });
+ 
+-        this._otherUserButton.set({
++        this._menuButtons.set({
+             opacity: 255 * progress,
+             scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+             scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+@@ -848,6 +871,34 @@ export const UnlockDialog = GObject.registerClass({
+         this._authPrompt.begin({userName});
+     }
+ 
++    _onMechanismsChanged(authPrompt, serviceName) {
++        const mechanisms = Array.from(authPrompt.mechanisms.get(serviceName));
++
++        const activeMechanism = this._loginOptionsButton.getActiveItem();
++
++        this._loginOptionsButton.clearItems({serviceName});
++        if (mechanisms.length === 0)
++            return;
++
++        const defaultId = mechanisms[0].id;
++
++        mechanisms.sort((a, b) => a.name.localeCompare(b.name));
++
++        for (const {role, id, name, selectable} of mechanisms) {
++            if (!selectable)
++                continue;
++
++            const mechanism = {serviceName, id, name, role};
++            this._loginOptionsButton.addItem(mechanism);
++
++            const wasActive = activeMechanism?.id === id;
++            const isDefault = id === defaultId;
++
++            if (wasActive || (!activeMechanism && isDefault))
++                this._loginOptionsButton.setActiveItem(mechanism);
++        }
++    }
++
+     _escape() {
+         if (this._authPrompt && this.allowCancel)
+             this._authPrompt.cancel();
diff -pruN 49.0-1/debian/patches/ubuntu-authd/unlockDialog-Support-request-type-of-REUSE_USERNAME.patch 49.0-1ubuntu1/debian/patches/ubuntu-authd/unlockDialog-Support-request-type-of-REUSE_USERNAME.patch
--- 49.0-1/debian/patches/ubuntu-authd/unlockDialog-Support-request-type-of-REUSE_USERNAME.patch	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/patches/ubuntu-authd/unlockDialog-Support-request-type-of-REUSE_USERNAME.patch	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,23 @@
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 6 Feb 2024 13:41:39 -0500
+Subject: unlockDialog: Support request type of REUSE_USERNAME
+
+The unlock dialog currently fails to reuse the username, when requested
+to do so. This commit fixes that.
+---
+ js/ui/unlockDialog.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
+index 63ba591..ec57eb9 100644
+--- a/js/ui/unlockDialog.js
++++ b/js/ui/unlockDialog.js
+@@ -838,7 +838,7 @@ export const UnlockDialog = GObject.registerClass({
+ 
+     _onReset(authPrompt, beginRequest) {
+         let userName;
+-        if (beginRequest === AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
++        if (beginRequest !== AuthPrompt.BeginRequestType.DONT_PROVIDE_USERNAME) {
+             this._authPrompt.setUser(this._user);
+             userName = this._userName;
+         } else {
diff -pruN 49.0-1/debian/rules 49.0-1ubuntu1/debian/rules
--- 49.0-1/debian/rules	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/rules	2025-09-24 16:23:55.000000000 +0000
@@ -31,6 +31,11 @@ override_dh_auto_configure:
 		-Dextensions-tool:bash_completion=enabled \
 		$(CONFFLAGS)
 
+execute_before_dh_auto_build:
+	# Regenerate the css files before building
+	env MESON_SOURCE_ROOT=$(CURDIR) MESON_DIST_ROOT=$(CURDIR) \
+		python3 build-aux/generate-stylesheets.py
+
 override_dh_makeshlibs:
 	dh_makeshlibs -X/usr/lib/gnome-shell/
 
@@ -63,7 +68,9 @@ endif
 override_dh_auto_test:
 ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
 	env XDG_CACHE_HOME="$(shell mktemp -d -t cache-XXXXXXXX)" \
-	dbus-run-session xvfb-run -a dh_auto_test -- $(meson_test_options)
+	dbus-run-session -- \
+	xvfb-run -a -e /proc/self/fd/2 -s '-noreset -screen 0 1280x1024x24' \
+	dh_auto_test -- $(meson_test_options)
 endif
 
 override_dh_gnome_clean:
diff -pruN 49.0-1/debian/salsa-ci.yml 49.0-1ubuntu1/debian/salsa-ci.yml
--- 49.0-1/debian/salsa-ci.yml	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/salsa-ci.yml	2025-09-24 16:23:55.000000000 +0000
@@ -4,4 +4,4 @@ variables:
   SALSA_CI_DISABLE_AUTOPKGTEST_I386: 0
 
 include:
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/ubuntu.yml
diff -pruN 49.0-1/debian/tests/control 49.0-1ubuntu1/debian/tests/control
--- 49.0-1/debian/tests/control	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/tests/control	2025-09-24 16:23:55.000000000 +0000
@@ -1,12 +1,15 @@
 Tests: build-and-test
 Depends: @builddeps@
+Architecture: !i386
 Restrictions: allow-stderr
 
 Tests: shell-tests
 Classes: desktop
+Architecture: !i386
 # FIXME: We only actually depend on fewer components, but
 # until upstream does not support properly installed tests, we need
 # to generate few tools first.
 Depends: @builddeps@,
          gnome-shell,
+         gnome-shell-ubuntu-extensions,
 Restrictions: allow-stderr skippable
diff -pruN 49.0-1/debian/tests/shell-tests 49.0-1ubuntu1/debian/tests/shell-tests
--- 49.0-1/debian/tests/shell-tests	2025-09-17 00:49:15.000000000 +0000
+++ 49.0-1ubuntu1/debian/tests/shell-tests	2025-09-24 16:23:55.000000000 +0000
@@ -50,11 +50,11 @@ tests[headlessStart]="--hotplug"
 tests[fittsy]=""
 
 for i in "${!tests[@]}"; do
-	echo "##### Running test $i"
-	dbus-run-session \
-	xvfb-run -a \
-	"${GNOME_SHELL_BUILDDIR}/gnome-shell-test-tool" \
-	--headless \
-	"$PWD/tests/shell/$i.js" \
-	${tests[$i]}
+    echo "##### Running test $i"
+    dbus-run-session -- \
+        xvfb-run -a -e /proc/self/fd/2 -s '-noreset -screen 0 1280x1024x24' \
+            "${GNOME_SHELL_BUILDDIR}/gnome-shell-test-tool" \
+                --headless \
+                "$PWD/tests/shell/$i.js" \
+                ${tests[$i]}
 done
diff -pruN 49.0-1/debian/ubuntu-session-mods/ubuntu.json 49.0-1ubuntu1/debian/ubuntu-session-mods/ubuntu.json
--- 49.0-1/debian/ubuntu-session-mods/ubuntu.json	1970-01-01 00:00:00.000000000 +0000
+++ 49.0-1ubuntu1/debian/ubuntu-session-mods/ubuntu.json	2025-09-24 16:23:55.000000000 +0000
@@ -0,0 +1,15 @@
+{
+    "parentMode": "user",
+    "stylesheetName": "Yaru/gnome-shell.css",
+    "colorScheme": "prefer-light",
+    "themeResourceName": "theme/Yaru/gnome-shell-theme.gresource",
+    "iconsResourceName": "theme/Yaru/gnome-shell-icons.gresource",
+    "debugFlags": ["backtrace-crashes-all"],
+    "enabledExtensions": [
+        "ubuntu-dock@ubuntu.com",
+        "ubuntu-appindicators@ubuntu.com",
+        "ding@rastersoft.com",
+        "tiling-assistant@ubuntu.com",
+        "snapd-prompting@canonical.com"
+    ]
+}
