AlmaLinux changes: Use AlmaLinux icon as activities button

Use unbranded illustrations

Fix Firefox desktop filename in favorites
This commit is contained in:
Eduard Abdullin 2025-07-29 17:16:48 +00:00 committed by root
commit 15ccd9462d
2 changed files with 489 additions and 1 deletions

View File

@ -47,10 +47,11 @@ Patch: 0001-data-Update-generated-stylesheets.patch
%if 0%{!?almalinux}
Patch: 0001-theme-Welcome-Illustration.patch
%endif
Patch: screenshot-tool.patch
# AlmaLinux Patch
Patch: 0001-panel-Bring-the-upstream-workspace-dots-back.patch
%define eds_version 3.45.1
%define gnome_desktop_version 44.0-7
%define glib2_version 2.79.2
@ -91,6 +92,7 @@ BuildRequires: pkgconfig(libpipewire-0.3) >= %{pipewire_version}
BuildRequires: pkgconfig(gtk4) >= %{gtk4_version}
BuildRequires: gettext >= 0.19.6
BuildRequires: python3
BuildRequires: python3-argcomplete
# for rst2man
BuildRequires: python3-docutils
@ -235,6 +237,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.gnome.Shell.Porta
%doc NEWS README.md
%{_bindir}/gnome-shell
%{_bindir}/gnome-extensions
%{_bindir}/gnome-screenshot-tool
%{_bindir}/gnome-shell-extension-tool
%{_bindir}/gnome-shell-test-tool
%{_datadir}/glib-2.0/schemas/*.xml
@ -242,6 +245,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.gnome.Shell.Porta
%{_datadir}/applications/org.gnome.Shell.Extensions.desktop
%{_datadir}/applications/org.gnome.Shell.desktop
%{_datadir}/bash-completion/completions/gnome-extensions
%{_datadir}/bash-completion/completions/gnome-screenshot-tool
%{_datadir}/gnome-control-center/keybindings/50-gnome-shell-launchers.xml
%{_datadir}/gnome-control-center/keybindings/50-gnome-shell-screenshots.xml
%{_datadir}/gnome-control-center/keybindings/50-gnome-shell-system.xml
@ -270,6 +274,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.gnome.Shell.Porta
%{_libexecdir}/gnome-shell-perf-helper
%{_libexecdir}/gnome-shell-hotplug-sniffer
%{_mandir}/man1/gnome-extensions.1*
%{_mandir}/man1/gnome-screenshot-tool.1*
%{_mandir}/man1/gnome-shell.1*
%if %{portal_helper}

483
screenshot-tool.patch Normal file
View File

@ -0,0 +1,483 @@
From c51ee10056e148e7eec74f3b67f3e5168373b563 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sat, 19 Jul 2025 14:12:46 +0200
Subject: [PATCH 1/2] screenshot: Fix taking interactive screenshots via D-Bus
The D-Bus method relies on the `screenshot-taken` and `closed` signals
to differentiate successful operations from canceled/failed ones.
However as the `screenshot-taken` signal is emitted asynchronously,
it may end up being emitted after the `closed` signal even in case
of success. Await the result to ensure correct ordering.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/8499
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3803>
---
js/ui/screenshot.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index 762848c287..962459214f 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -1350,7 +1350,7 @@ export const ScreenshotUI = GObject.registerClass({
visible: false,
}));
this._captureButton.connect('clicked',
- this._onCaptureButtonClicked.bind(this));
+ () => this._onCaptureButtonClicked().catch(logError));
this._bottomRowContainer.add_child(this._captureButton);
this._showPointerButtonContainer = new St.BoxLayout({
@@ -1872,9 +1872,13 @@ export const ScreenshotUI = GObject.registerClass({
return [x, y, w, h];
}
- _onCaptureButtonClicked() {
+ async _onCaptureButtonClicked() {
if (this._shotButton.checked) {
- this._saveScreenshot().catch(logError);
+ try {
+ await this._saveScreenshot();
+ } catch (e) {
+ logError(e);
+ }
this.close();
} else {
// Screencast closes the UI on its own.
@@ -2139,7 +2143,7 @@ export const ScreenshotUI = GObject.registerClass({
symbol === Clutter.KEY_KP_Enter || symbol === Clutter.KEY_ISO_Enter ||
((event.get_state() & Clutter.ModifierType.CONTROL_MASK) &&
(symbol === Clutter.KEY_c || symbol === Clutter.KEY_C))) {
- this._onCaptureButtonClicked();
+ this._onCaptureButtonClicked().catch(logError);
return Clutter.EVENT_STOP;
}
--
2.50.1
From f61170d09c728144ecf9c44b4b0a0392237fadd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 18 Jul 2025 21:34:36 +0200
Subject: [PATCH 2/2] Add screenshot CLI tool
The tool provides the same API as gnome-screenshot, but leverages
GNOME's built-in screenshot UI instead of implementing its own
interactive dialog.
---
man/gnome-screenshot-tool.rst | 49 +++++++
man/meson.build | 23 ++-
meson.build | 2 +
src/gnome-screenshot-tool | 266 ++++++++++++++++++++++++++++++++++
src/meson.build | 26 ++++
5 files changed, 358 insertions(+), 8 deletions(-)
create mode 100644 man/gnome-screenshot-tool.rst
create mode 100755 src/gnome-screenshot-tool
diff --git a/man/gnome-screenshot-tool.rst b/man/gnome-screenshot-tool.rst
new file mode 100644
index 0000000000..a471738394
--- /dev/null
+++ b/man/gnome-screenshot-tool.rst
@@ -0,0 +1,49 @@
+=====================
+gnome-screenshot-tool
+=====================
+
+-----------------------------------------
+Screenshot CLI tool for the GNOME desktop
+-----------------------------------------
+
+:Manual section: 1
+:Manual group: User Commands
+
+SYNOPSIS
+--------
+**gnome-screenshot-tool** [*OPTION*...]
+
+DESCRIPTION
+-----------
+**gnome-screenshot-tool** is a GNOME utility for taking screenshots of
+the entire screen, a window or a user-defined area of the screen.
+
+OPTIONS
+-------
+``-w``, ``--window``
+
+ Grab the currently active window instead of the entire screen
+
+``-a``, ``--area``
+
+ Grab an area of the screen instead of the entire screen
+
+``-i``, ``--interactive``
+
+ Bring up GNOME's native screenshot dialog
+
+``-p``, ``--include-pointer``
+
+ Include the pointer with the screenshot
+
+``-d``, ``--delay``\ =\ *SECONDS*
+
+ Take the screenshot after the specified delay [in seconds]
+
+``-f``, ``--file``\ =\ *FILENAME*
+
+ Save the screenshot directly to this file
+
+``-h``, ``--help``
+
+ Show a summary of the available options
diff --git a/man/meson.build b/man/meson.build
index c61bf51513..cfb9c8e07f 100644
--- a/man/meson.build
+++ b/man/meson.build
@@ -1,8 +1,15 @@
-custom_target('man page',
- input: 'gnome-shell.rst',
- output: 'gnome-shell.1',
- command: [rst2man, '--syntax-highlight=none', '@INPUT@'],
- capture: true,
- install_dir: mandir + '/man1',
- install: true
-)
+man_pages = [
+ 'gnome-shell.rst',
+ 'gnome-screenshot-tool.rst',
+]
+
+foreach page : man_pages
+ custom_target(f'@page@ man page',
+ input: page,
+ output: '@BASENAME@.1',
+ command: [rst2man, '--syntax-highlight=none', '@INPUT@'],
+ capture: true,
+ install_dir: mandir + '/man1',
+ install: true
+ )
+endforeach
diff --git a/meson.build b/meson.build
index 6f10a366bf..14b793db6a 100644
--- a/meson.build
+++ b/meson.build
@@ -134,6 +134,8 @@ if get_option('man')
subdir('man')
endif
+bash_completion = dependency('bash-completion', required: false)
+
mutter_typelibdir = mutter_dep.get_variable('typelibdir')
python = find_program('python3')
gjs = find_program('gjs')
diff --git a/src/gnome-screenshot-tool b/src/gnome-screenshot-tool
new file mode 100755
index 0000000000..e29650abfe
--- /dev/null
+++ b/src/gnome-screenshot-tool
@@ -0,0 +1,266 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import gi
+
+gi.require_version('Gdk', '4.0')
+gi.require_version('Gtk', '4.0')
+gi.require_version('GdkPixbuf', '2.0')
+from gi.repository import GLib, Gio, GdkPixbuf, Gdk, Gtk # type: ignore
+
+try:
+ import argcomplete
+except ModuleNotFoundError:
+ pass
+
+NAME = "org.gnome.Shell.Screenshot"
+OBJECT_PATH = "/org/gnome/Shell/Screenshot"
+INTERFACE = "org.gnome.Shell.Screenshot"
+
+class Screenshot:
+ def __init__(self):
+ self._proxy = Gio.DBusProxy.new_for_bus_sync(
+ bus_type = Gio.BusType.SESSION,
+ flags = Gio.DBusProxyFlags.NONE,
+ info = None,
+ name = NAME,
+ object_path = OBJECT_PATH,
+ interface_name = INTERFACE,
+ cancellable = None,
+ )
+
+ def screenshot(self, include_cursor) -> GdkPixbuf.Pixbuf:
+ variant = self._proxy.call_sync(
+ method_name = "Screenshot",
+ parameters = GLib.Variant(
+ "(bbs)",
+ (
+ include_cursor,
+ True,
+ self._get_tmp_filename(),
+ ),
+ ),
+ flags = Gio.DBusCallFlags.NO_AUTO_START,
+ timeout_msec = -1,
+ cancellable = None,
+ )
+ return self._get_screenshot_from_result(variant)
+
+ def screenshot_window(self, include_cursor) -> GdkPixbuf.Pixbuf:
+ variant = self._proxy.call_sync(
+ method_name = "ScreenshotWindow",
+ parameters = GLib.Variant(
+ "(bbbs)",
+ (
+ True,
+ include_cursor,
+ True,
+ self._get_tmp_filename(),
+ ),
+ ),
+ flags = Gio.DBusCallFlags.NO_AUTO_START,
+ timeout_msec = -1,
+ cancellable = None,
+ )
+ return self._get_screenshot_from_result(variant)
+
+ def screenshot_area(self, x, y, width, height) -> GdkPixbuf.Pixbuf:
+ variant = self._proxy.call_sync(
+ method_name = "ScreenshotArea",
+ parameters = GLib.Variant(
+ "(iiiibs)",
+ (
+ x,
+ y,
+ width,
+ height,
+ True,
+ self._get_tmp_filename(),
+ ),
+ ),
+ flags = Gio.DBusCallFlags.NO_AUTO_START,
+ timeout_msec = -1,
+ cancellable = None,
+ )
+ return self._get_screenshot_from_result(variant)
+
+ def interactive_screenshot(self) -> GdkPixbuf.Pixbuf:
+ variant = self._proxy.call_sync(
+ method_name = "InteractiveScreenshot",
+ parameters = None,
+ flags = Gio.DBusCallFlags.NO_AUTO_START,
+ timeout_msec = GLib.MAXINT,
+ cancellable = None,
+ )
+ [success, uri] = variant
+ file = Gio.File.new_for_uri(uri)
+ return self._get_screenshot_from_result((success, file.get_path()))
+
+ def select_area(self) -> tuple[int, int, int, int]:
+ variant = self._proxy.call_sync(
+ method_name = "SelectArea",
+ parameters = None,
+ flags = Gio.DBusCallFlags.NO_AUTO_START,
+ timeout_msec = -1,
+ cancellable = None,
+ )
+ [x, y, width, height] = variant
+ return (x, y, width, height)
+
+ def _get_screenshot_from_result(self, variant):
+ [success, filename] = variant
+ assert success
+ screenshot = GdkPixbuf.Pixbuf.new_from_file(filename)
+ GLib.unlink(filename)
+ return screenshot
+
+ def _get_tmp_filename(self):
+ path = GLib.build_filenamev([
+ GLib.get_user_cache_dir(),
+ "gnome-screenshot-tool",
+ ])
+ GLib.mkdir_with_parents(path, 0o700)
+ return GLib.build_filenamev([path, f'scr-{GLib.random_int()}'])
+
+class ScreenshotApp(Gtk.Application):
+ def __init__(self, config):
+ super().__init__(application_id="org.gnome.Screenshot")
+
+ self._config = config
+
+ def do_startup(self):
+ Gtk.Application.do_startup(self)
+ self._proxy = Screenshot()
+
+ def do_activate(self):
+ self.hold()
+ if self._config.area:
+ self._selected_area = self._proxy.select_area()
+ self._start_screenshot_timeout()
+
+ def _start_screenshot_timeout(self):
+ GLib.timeout_add_seconds(self._config.delay, self._take_screenshot)
+
+ def _take_screenshot(self):
+ include_pointer = self._config.include_pointer
+
+ try:
+ if self._config.interactive:
+ screenshot = self._proxy.interactive_screenshot()
+ elif self._config.area:
+ [x, y, width, height] = self._selected_area
+ screenshot = self._proxy.screenshot_area(x, y, width, height)
+ elif self._config.window:
+ screenshot = self._proxy.screenshot_window(include_pointer)
+ else:
+ screenshot = self._proxy.screenshot(include_pointer)
+
+ # if self._config.clipboard:
+ # self._save_screenshot_to_clipboard(screenshot)
+
+ if self._config.file:
+ target_file = Gio.File.new_for_commandline_arg(self._config.file)
+ override = True
+ else:
+ target_file = self._get_default_target_file()
+ override = False
+
+ self._save_screenshot_to_file(screenshot, target_file, override)
+ except Exception as e:
+ print(f'Failed to take screenshot: {e}')
+ finally:
+ self.release()
+
+ return GLib.SOURCE_REMOVE
+
+ def _save_screenshot_to_clipboard(self, screenshot):
+ dpy = Gdk.Display.get_default()
+ clipboard = dpy.get_clipboard()
+ clipboard.set(screenshot)
+
+ def _save_screenshot_to_file(self, screenshot, file, override):
+ if override:
+ ostream = file.replace(None, False, Gio.FileCreateFlags.NONE, None)
+ else:
+ ostream = file.create(Gio.FileCreateFlags.NONE, None)
+
+ [_, ext] = os.path.splitext(file.get_basename())
+ if not ext:
+ ext = 'png'
+ else:
+ ext = ext[1:]
+
+ def find_writable_format(formats, ext):
+ for f in formats:
+ for e in f.get_extensions():
+ if e == ext and f.is_writable():
+ return f.get_name()
+ return None
+
+ formats = GdkPixbuf.Pixbuf.get_formats()
+ format = find_writable_format(formats, ext)
+
+ if format == 'png':
+ keys = ["tEXt::Software"]
+ values = ["gnome-screenshot-tool"]
+ else:
+ keys = None
+ values = None
+
+ screenshot.save_to_streamv(ostream, format, keys, values, None)
+
+ def _get_default_target_file(self):
+ path = GLib.build_filenamev([
+ GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES) or GLib.get_home_dir(),
+ "Screenshots",
+ ])
+ dir = Gio.File.new_for_path(path)
+ try:
+ dir.make_directory_with_parents(None)
+ except GLib.Error as e:
+ if not e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.EXISTS):
+ raise e
+
+ timestamp = GLib.DateTime.new_now_local().format('%Y-%m-%d %H-%M-%S')
+ name = "Screenshot From %s" % (timestamp)
+
+ def suffixes() -> str:
+ yield ''
+
+ i = 1
+ while True:
+ yield f'-{i}'
+ i = i + 1
+
+ for suffix in suffixes():
+ file = dir.get_child(f'{name}{suffix}.png')
+ if not file.query_exists(None):
+ return file
+
+def main():
+ parser = argparse.ArgumentParser()
+
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("-w", "--window", action="store_true", help="Grab a window instead of the entire screen")
+ group.add_argument("-a", "--area", action="store_true", help="Grab an area of the screen instead of the entire screen")
+ group.add_argument("-i", "--interactive", action="store_true", help="Interactively set options")
+
+ # parser.add_argument("-c", "--clipboard", action="store_true", help="Send the grab directly to the clipboard")
+ parser.add_argument("-p", "--include-pointer", action="store_true", help="Include the pointer with the screenshot")
+ parser.add_argument("-d", "--delay", type=int, default=0, help="Take a screenshot after the specified delay (in seconds)")
+ parser.add_argument("-f", "--file", type=str, help="Save screenshot directly to this file")
+
+ if argcomplete:
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+
+ if args.interactive:
+ if args.include_pointer:
+ print("Option --include-pointer is ignored in interactive mode.")
+
+ app = ScreenshotApp(args)
+ app.run(None)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/meson.build b/src/meson.build
index 23d23b4f38..4ccdd3b055 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -310,3 +310,29 @@ run_test = executable('run-js-test', 'run-js-test.c',
include_directories: [conf_inc],
build_rpath: mutter_typelibdir,
)
+
+install_data(
+ 'gnome-screenshot-tool',
+ install_dir: bindir
+)
+
+if bash_completion.found()
+ bash_completion_dir = bash_completion.get_variable(
+ pkgconfig: 'completionsdir',
+ pkgconfig_define: ['datadir', datadir],
+ )
+
+ register_python_argcomplete = find_program('register-python-argcomplete')
+
+ custom_target(
+ 'screenshot-tool-bash-completion',
+ output: 'gnome-screenshot-tool',
+ command: [
+ register_python_argcomplete,
+ 'gnome-screenshot-tool',
+ ],
+ capture: true,
+ install_dir: bash_completion_dir,
+ install: true,
+ )
+endif
--
2.50.1