From cc1c3abf2dbf7c982b924a551c3ac17b1ca3ee62 Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 18:20:24 -0400 Subject: [PATCH 1/7] wayland gets custom cursors (#182) * wayland gets custom cursors * lint * lint * tests, lint * lint --- setup.py | 1 + src/gscreenshot/__init__.py | 33 ++++++-- src/gscreenshot/cursor_locator/__init__.py | 24 ++++++ src/gscreenshot/cursor_locator/factory.py | 49 ++++++++++++ .../cursor_locator/gtk_cursor_locator.py | 80 +++++++++++++++++++ .../cursor_locator/x11_cursor_locator.py | 43 ++++++++++ src/gscreenshot/frontend/gtk/__init__.py | 5 +- src/gscreenshot/frontend/gtk/view.py | 2 + src/gscreenshot/screenshooter/__init__.py | 40 +++++----- .../gscreenshot/screenshot/test_screenshot.py | 7 +- test/gscreenshot/test_gscreenshot.py | 12 ++- 11 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 src/gscreenshot/cursor_locator/__init__.py create mode 100644 src/gscreenshot/cursor_locator/factory.py create mode 100644 src/gscreenshot/cursor_locator/gtk_cursor_locator.py create mode 100644 src/gscreenshot/cursor_locator/x11_cursor_locator.py diff --git a/setup.py b/setup.py index 82f47f0..a62bd76 100644 --- a/setup.py +++ b/setup.py @@ -190,6 +190,7 @@ def compile_manpage(): 'gscreenshot', 'gscreenshot.frontend', 'gscreenshot.frontend.gtk', + 'gscreenshot.cursor_locator', 'gscreenshot.screenshooter', 'gscreenshot.screenshot', 'gscreenshot.screenshot.effects', diff --git a/src/gscreenshot/__init__.py b/src/gscreenshot/__init__.py index b8769e5..c2f2517 100755 --- a/src/gscreenshot/__init__.py +++ b/src/gscreenshot/__init__.py @@ -143,9 +143,27 @@ def get_available_cursors(self) -> typing.Dict[str, typing.Optional[Image.Image] ) } + if session_is_wayland(): + del available['theme'] + available.update(self._stamps) return available + def get_cursor_by_name(self, name: typing.Optional[str]): + ''' + Get the cursor glyph that goes by the given name. This is + safe to call with None or bad names and will return a default + value. + ''' + cursors = self.get_available_cursors() + #pylint: disable=consider-iterating-dictionary + default = list(cursors.keys())[0] + + if name and name in cursors.keys(): + return cursors[name] + + return cursors[default] + def show_screenshot_notification(self) -> bool: ''' Show a notification that a screenshot was taken. @@ -209,7 +227,8 @@ def get_screenshooter_name(self) -> str: #pylint: disable=too-many-arguments def screenshot_full_display(self, delay: int=0, capture_cursor: bool=False, - cursor_name: str='theme', overwrite: bool=False, count: int=1 + cursor_name: typing.Optional[str]=None, + overwrite: bool=False, count: int=1 ) -> typing.Optional[Image.Image]: """ Takes a screenshot of the full display with a @@ -224,7 +243,7 @@ def screenshot_full_display(self, delay: int=0, capture_cursor: bool=False, if not capture_cursor: use_cursor = None else: - use_cursor = self.get_available_cursors()[cursor_name] + use_cursor = self.get_cursor_by_name(cursor_name) for _ in range(0, count): self.screenshooter.grab_fullscreen_( @@ -245,7 +264,8 @@ def screenshot_full_display(self, delay: int=0, capture_cursor: bool=False, #pylint: disable=too-many-arguments def screenshot_selected(self, delay: int=0, capture_cursor: bool=False, - cursor_name: str='theme', overwrite: bool=False, count: int=1, + cursor_name: typing.Optional[str]=None, + overwrite: bool=False, count: int=1, region: typing.Optional[typing.Tuple[int, int, int, int]]=None ) -> typing.Optional[Image.Image]: """ @@ -261,7 +281,7 @@ def screenshot_selected(self, delay: int=0, capture_cursor: bool=False, if not capture_cursor: use_cursor = None else: - use_cursor = self.get_available_cursors()[cursor_name] + use_cursor = self.get_cursor_by_name(cursor_name) for _ in range(0, count): self.screenshooter.grab_selection_( @@ -283,7 +303,8 @@ def screenshot_selected(self, delay: int=0, capture_cursor: bool=False, #pylint: disable=too-many-arguments def screenshot_window(self, delay: int=0, capture_cursor: bool=False, - cursor_name: str='theme', overwrite: bool=False, count: int=1 + cursor_name: typing.Optional[str]=None, + overwrite: bool=False, count: int=1 ) -> typing.Optional[Image.Image]: """ Interactively takes a screenshot of a selected window @@ -298,7 +319,7 @@ def screenshot_window(self, delay: int=0, capture_cursor: bool=False, if not capture_cursor: use_cursor = None else: - use_cursor = self.get_available_cursors()[cursor_name] + use_cursor = self.get_cursor_by_name(cursor_name) for _ in range(0, count): self.screenshooter.grab_window_( diff --git a/src/gscreenshot/cursor_locator/__init__.py b/src/gscreenshot/cursor_locator/__init__.py new file mode 100644 index 0000000..e6b1854 --- /dev/null +++ b/src/gscreenshot/cursor_locator/__init__.py @@ -0,0 +1,24 @@ +''' +Interface class for integrating cursor locators +''' +import typing + + +class CursorLocator(): + '''Parent class for cursor locator strategies''' + + __utilityname__: str = "default" + + def __init__(self): + """constructor""" + + def get_cursor_position(self) -> typing.Optional[typing.Tuple[int, int]]: + '''Return the cursor position as a tuple of (x, y)''' + raise NotImplementedError() + + @staticmethod + def can_run() -> bool: + """ + Whether this cursor locator can run + """ + return True diff --git a/src/gscreenshot/cursor_locator/factory.py b/src/gscreenshot/cursor_locator/factory.py new file mode 100644 index 0000000..190cb84 --- /dev/null +++ b/src/gscreenshot/cursor_locator/factory.py @@ -0,0 +1,49 @@ +''' +Utilities for selecting a cursor locator utility +''' + +import typing +from gscreenshot.cursor_locator import CursorLocator +from gscreenshot.cursor_locator.gtk_cursor_locator import GtkCursorLocator +from gscreenshot.cursor_locator.x11_cursor_locator import X11CursorLocator +from gscreenshot.util import session_is_wayland + + +class NoSupportedCursorLocatorError(Exception): + """NoSupportedCursorLocatorError""" + + +class CursorLocatorFactory(object): + '''Selects and instantiates a usable cursor finder''' + + def __init__(self, cursor_locator=None): + self.cursor_locator:typing.Optional[CursorLocator] = cursor_locator + self.xorg_locators = [ + X11CursorLocator, + GtkCursorLocator + ] + + self.wayland_locators = [ + GtkCursorLocator + ] + + self.locators:list = [] + + if session_is_wayland(): + self.locators = self.wayland_locators + else: + self.locators = self.xorg_locators + + def create(self) -> CursorLocator: + '''Returns a locator instance''' + if self.cursor_locator is not None: + return self.cursor_locator + + for locator in self.locators: + if locator.can_run(): + return locator() + + raise NoSupportedCursorLocatorError( + "No supported cursor locator available", + [x.__utilityname__ for x in self.locators if x.__utilityname__ is not None] + ) diff --git a/src/gscreenshot/cursor_locator/gtk_cursor_locator.py b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py new file mode 100644 index 0000000..cdf08a7 --- /dev/null +++ b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py @@ -0,0 +1,80 @@ +''' +Classes for capturing the cursor position using Gtk +''' +#pylint: disable=wrong-import-order +#pylint: disable=wrong-import-position +#pylint: disable=ungrouped-imports +import typing +import pygtkcompat +pygtkcompat.enable() +pygtkcompat.enable_gtk(version='3.0') + +from gi.repository import Gtk, Gdk +from gscreenshot.cursor_locator import CursorLocator + + +class GtkCursorLocator(CursorLocator): + ''' + Interactive cursor locator driving class + ''' + + __utilityname__: str = "gscreenshot" + + def get_cursor_position(self) -> typing.Optional[typing.Tuple[int, int]]: + """ + Gets the current position of the mouse cursor, if able. + Returns (x, y) or None. + """ + locator = GtkCursorLocatorWindow() + locator.show_all() + Gtk.main() + while Gtk.events_pending(): + Gtk.main_iteration() + + return locator.position + @staticmethod + def can_run() -> bool: + return True + + +class GtkCursorLocatorWindow(Gtk.Window): + ''' + GTK window for capturing the cursor position + ''' + def __init__(self): + '''constructor''' + self.position = None + super().__init__() + self.set_title("gscreenshot") + self.set_position(Gtk.WindowPosition.CENTER) + self.fullscreen() + self.set_opacity(.25) + self.screen = self.get_screen() + + box: Gtk.Grid = Gtk.Grid( + vexpand=False, + halign = Gtk.Align.CENTER, + valign = Gtk.Align.CENTER + ) + + help_text = Gtk.Label() + help_text.set_text( + "Move your cursor to the desired position then click to capture" + ) + help_subtext = Gtk.Label() + help_subtext.set_text( + "This extra step is required on Wayland. On X11, install Xlib to skip this." + ) + box.attach(help_text, 0, 0, 1, 1) + box.attach(help_subtext, 0, 1, 1, 1) + self.add(box) + self.connect("button_press_event", self.on_button_press) + + self.set_events(Gdk.POINTER_MOTION_MASK + | Gdk.BUTTON_PRESS_MASK) + + def on_button_press(self, _widget, event): + '''handle button press''' + self.position = (int(event.x), int(event.y)) + self.destroy() + Gtk.main_quit() diff --git a/src/gscreenshot/cursor_locator/x11_cursor_locator.py b/src/gscreenshot/cursor_locator/x11_cursor_locator.py new file mode 100644 index 0000000..668e555 --- /dev/null +++ b/src/gscreenshot/cursor_locator/x11_cursor_locator.py @@ -0,0 +1,43 @@ +""" +Xlib cursor locator classes +""" +import typing +try: + from Xlib import display +except ImportError: + display = None + +from gscreenshot.cursor_locator import CursorLocator + + +class X11CursorLocator(CursorLocator): + '''Xlib-based cursor locator''' + + __utilityname__: str = "python-xlib" + + def get_cursor_position(self) -> typing.Optional[typing.Tuple[int, int]]: + """ + Gets the current position of the mouse cursor, if able. + Returns (x, y) or None. + """ + if display is None: + return None + + try: + # This is a ctype + # pylint: disable=protected-access + mouse_data = display.Display().screen().root.query_pointer()._data + if 'root_x' not in mouse_data or 'root_y' not in mouse_data: + return None + # pylint: disable=bare-except + except: + # We don't really care about the specific error here. If we can't + # get the pointer, then just move on. + return None + + return (mouse_data["root_x"], mouse_data["root_y"]) + + @staticmethod + def can_run() -> bool: + '''can_run''' + return display is not None diff --git a/src/gscreenshot/frontend/gtk/__init__.py b/src/gscreenshot/frontend/gtk/__init__.py index 3d898b0..df0a55c 100644 --- a/src/gscreenshot/frontend/gtk/__init__.py +++ b/src/gscreenshot/frontend/gtk/__init__.py @@ -54,19 +54,20 @@ def __init__(self, application: Gscreenshot, view: View): self._hide = True self._capture_cursor = False self._show_preview() - self._view.show_cursor_options(self._capture_cursor) self._keymappings = {} self._overwrite_mode = True cursors = self._app.get_available_cursors() cursors[i18n("custom")] = None - self._cursor_selection = 'theme' + self._cursor_selection = list(cursors.keys())[0] self._view.update_available_cursors( cursors ) + self._view.show_cursor_options(self._capture_cursor) + def _begin_take_screenshot(self, app_method, **args): app_method(delay=self._delay, capture_cursor=self._capture_cursor, diff --git a/src/gscreenshot/frontend/gtk/view.py b/src/gscreenshot/frontend/gtk/view.py index 777785e..a211eb0 100644 --- a/src/gscreenshot/frontend/gtk/view.py +++ b/src/gscreenshot/frontend/gtk/view.py @@ -257,6 +257,8 @@ def show_cursor_options(self, show: bool): if show and GSCapabilities.ALTERNATE_CURSOR in self._capabilities: self._enable_and_show(self._cursor_selection_dropdown) self._enable_and_show(self._cursor_selection_label) + if self._cursor_selection_dropdown.get_active() < 0: + self._cursor_selection_dropdown.set_active(0) else: self._disable_and_hide(self._cursor_selection_dropdown) self._disable_and_hide(self._cursor_selection_label) diff --git a/src/gscreenshot/screenshooter/__init__.py b/src/gscreenshot/screenshooter/__init__.py index c99c35b..e526d4b 100644 --- a/src/gscreenshot/screenshooter/__init__.py +++ b/src/gscreenshot/screenshooter/__init__.py @@ -6,6 +6,7 @@ import tempfile import typing import PIL.Image +from gscreenshot.cursor_locator.factory import CursorLocatorFactory from gscreenshot.screenshot import Screenshot from gscreenshot.screenshot.effects.crop import CropEffect @@ -16,11 +17,6 @@ from gscreenshot.selector.factory import SelectorFactory from gscreenshot.util import session_is_wayland, GSCapabilities -try: - from Xlib import display -except ImportError: - display = None - class Screenshooter(object): """ @@ -93,9 +89,15 @@ def get_capabilities_(self) -> typing.Dict[str, str]: # If we're running, this is the bare minimum capabilities[GSCapabilities.CAPTURE_FULLSCREEN] = self.__utilityname__ - if display is not None and not session_is_wayland(): - capabilities[GSCapabilities.ALTERNATE_CURSOR] = "python-xlib" - capabilities[GSCapabilities.CURSOR_CAPTURE] = "python-xlib" + try: + cursor_locator = CursorLocatorFactory().create() + # pylint: disable=bare-except + except: + cursor_locator = None + + if cursor_locator is not None: + capabilities[GSCapabilities.ALTERNATE_CURSOR] = cursor_locator.__utilityname__ + capabilities[GSCapabilities.CURSOR_CAPTURE] = cursor_locator.__utilityname__ if self._selector is not None: capabilities.update(self._selector.get_capabilities()) @@ -112,11 +114,12 @@ def grab_fullscreen_(self, delay: int=0, capture_cursor: bool=False, self.grab_fullscreen(delay, capture_cursor) else: self.grab_fullscreen(delay, capture_cursor=False) - cursor_position = self.get_cursor_position() - if capture_cursor and use_cursor and self._screenshot and cursor_position: - stamp = StampEffect(use_cursor, cursor_position) - stamp.set_alias("cursor") - self._screenshot.add_effect(stamp) + if capture_cursor and use_cursor and self._screenshot: + cursor_position = self.get_cursor_position() + if cursor_position is not None: + stamp = StampEffect(use_cursor, cursor_position) + stamp.set_alias("cursor") + self._screenshot.add_effect(stamp) def grab_fullscreen(self, delay: int=0, capture_cursor: bool=False): """ @@ -213,23 +216,16 @@ def get_cursor_position(self) -> typing.Optional[typing.Tuple[int, int]]: Gets the current position of the mouse cursor, if able. Returns (x, y) or None. """ - if display is None: - return None try: - # This is a ctype - # pylint: disable=protected-access - mouse_data = display.Display().screen().root.query_pointer()._data - if 'root_x' not in mouse_data or 'root_y' not in mouse_data: - return None + cursor_locator = CursorLocatorFactory().create() + return cursor_locator.get_cursor_position() # pylint: disable=bare-except except: # We don't really care about the specific error here. If we can't # get the pointer, then just move on. return None - return (mouse_data["root_x"], mouse_data["root_y"]) - def _grab_selection_fallback(self, delay: int=0, capture_cursor: bool=False): """ Fallback for grabbing the selection, in case the selection tool fails to diff --git a/test/gscreenshot/screenshot/test_screenshot.py b/test/gscreenshot/screenshot/test_screenshot.py index 8914380..f9e9fd5 100644 --- a/test/gscreenshot/screenshot/test_screenshot.py +++ b/test/gscreenshot/screenshot/test_screenshot.py @@ -16,10 +16,7 @@ def setUp(self): self.image = Mock() self.screenshot = Screenshot(self.image) - @mock.patch('src.gscreenshot.screenshooter.display') - def test_add_fake_cursor(self, mock_xlib): - mock_xlib.Display().screen().root.query_pointer()._data = {'root_x': 20, 'root_y': 40} - + def test_add_fake_cursor(self): original_img = Image.open( resource_filename('gscreenshot.resources.pixmaps', 'gscreenshot.png') ) @@ -41,5 +38,5 @@ def test_add_fake_cursor(self, mock_xlib): ) self.assertLess( len(set(ImageChops.difference(expected_img, self.screenshot.get_image()).getdata())), # type: ignore - 100, + 2, "cursor was not stamped onto the test image correctly") diff --git a/test/gscreenshot/test_gscreenshot.py b/test/gscreenshot/test_gscreenshot.py index c4dc6a4..1e3d335 100644 --- a/test/gscreenshot/test_gscreenshot.py +++ b/test/gscreenshot/test_gscreenshot.py @@ -40,7 +40,9 @@ def test_screenshot_full_display_delay(self): self.assertEqual(self.fake_image, actual) - def test_screenshot_full_display_cursor(self): + @mock.patch('src.gscreenshot.session_is_wayland') + def test_screenshot_full_display_cursor(self, is_wayland): + is_wayland.return_value = False actual = self.gscreenshot.screenshot_full_display(capture_cursor=True) self.fake_screenshooter.grab_fullscreen_.assert_called_once_with( @@ -76,7 +78,9 @@ def test_screenshot_selected_delay(self): self.assertEqual(self.fake_image, actual) - def test_screenshot_selection_cursor(self): + @mock.patch('src.gscreenshot.session_is_wayland') + def test_screenshot_selection_cursor(self, is_wayland): + is_wayland.return_value = False actual = self.gscreenshot.screenshot_selected(capture_cursor=True) self.fake_screenshooter.grab_selection_.assert_called_once_with( @@ -111,7 +115,9 @@ def test_screenshot_window_delay(self): self.assertEqual(self.fake_image, actual) - def test_screenshot_window_cursor(self): + @mock.patch('src.gscreenshot.session_is_wayland') + def test_screenshot_window_cursor(self, is_wayland): + is_wayland.return_value = False actual = self.gscreenshot.screenshot_window(capture_cursor=True) self.fake_screenshooter.grab_window_.assert_called_once_with( From d045897a8583f90473df0bfed63a9f656e5e1ebb Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 18:49:22 -0400 Subject: [PATCH 2/7] fix custom cursor loop --- src/gscreenshot/frontend/gtk/view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gscreenshot/frontend/gtk/view.py b/src/gscreenshot/frontend/gtk/view.py index a211eb0..b7a9f17 100644 --- a/src/gscreenshot/frontend/gtk/view.py +++ b/src/gscreenshot/frontend/gtk/view.py @@ -304,11 +304,11 @@ def update_available_cursors(self, cursors: dict, selected: typing.Optional[str] if selected is not None and selected in cursors: self._cursor_selection_dropdown.set_active( - selected_idx + selected_idx-1 ) elif cursor_name == "theme": self._cursor_selection_dropdown.set_active( - len(self._cursor_selection_items)-1 + 0 ) def run(self): From afc63a599e603885df52d47396c9bf748df07dc5 Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 21:09:54 -0400 Subject: [PATCH 3/7] cancel cursor locate on keypress --- src/gscreenshot/cursor_locator/gtk_cursor_locator.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gscreenshot/cursor_locator/gtk_cursor_locator.py b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py index cdf08a7..6ef5f28 100644 --- a/src/gscreenshot/cursor_locator/gtk_cursor_locator.py +++ b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py @@ -48,7 +48,7 @@ def __init__(self): self.set_title("gscreenshot") self.set_position(Gtk.WindowPosition.CENTER) self.fullscreen() - self.set_opacity(.25) + self.set_opacity(.65) self.screen = self.get_screen() box: Gtk.Grid = Gtk.Grid( @@ -69,6 +69,7 @@ def __init__(self): box.attach(help_subtext, 0, 1, 1, 1) self.add(box) self.connect("button_press_event", self.on_button_press) + self.connect("key-press-event", self.on_keypress) self.set_events(Gdk.POINTER_MOTION_MASK | Gdk.BUTTON_PRESS_MASK) @@ -78,3 +79,8 @@ def on_button_press(self, _widget, event): self.position = (int(event.x), int(event.y)) self.destroy() Gtk.main_quit() + + def on_keypress(self, _widget, event): + '''handle keypress''' + self.destroy() + Gtk.main_quit() From 535d399436b65e0246abcb1bbb3c3e62e3faae9a Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 21:13:20 -0400 Subject: [PATCH 4/7] lint --- src/gscreenshot/cursor_locator/gtk_cursor_locator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gscreenshot/cursor_locator/gtk_cursor_locator.py b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py index 6ef5f28..c47813a 100644 --- a/src/gscreenshot/cursor_locator/gtk_cursor_locator.py +++ b/src/gscreenshot/cursor_locator/gtk_cursor_locator.py @@ -80,7 +80,7 @@ def on_button_press(self, _widget, event): self.destroy() Gtk.main_quit() - def on_keypress(self, _widget, event): + def on_keypress(self, _widget, _event): '''handle keypress''' self.destroy() Gtk.main_quit() From 200f9dc711208c4f122836485946a8aebf78437e Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 21:23:59 -0400 Subject: [PATCH 5/7] specfile and readme --- README.md | 7 +++++-- specs/gscreenshot.spec | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 800ec6e..047bd16 100644 --- a/README.md +++ b/README.md @@ -235,16 +235,19 @@ At this point, you can install gscreenshot itself by running * PIL/python-pillow + slop + python-xlib * Scrot only (any version) (cursor capture will not work in some scenarios, region selection may be glitchy due to scrot issues) +Feel free to mix and match, though some combinations may work better than others. +As of gscreenshot 3.5.0 python-xlib is _optional_ for full cursor capture functionality but will require an extra click. + ## For full functionality on Wayland, the recommended packages are: -* grim (for screenshots) +* xdg-desktop-portal for your environment (for screenshots) * slurp (for region selection) * xdg-open (for opening screenshots in your image viewer) * wl-clipboard (for copy to clipboard) ## For alternate Wayland configurations, choose from one of the following combinations: -* xdg-desktop-portal + slurp + python-dbus +* grim + slurp + python-dbus You can install X11 and Wayland package configurations in parallel - gscreenshot will detect if your session is Wayland or X11. diff --git a/specs/gscreenshot.spec b/specs/gscreenshot.spec index 0ffdfd8..82aaa6f 100644 --- a/specs/gscreenshot.spec +++ b/specs/gscreenshot.spec @@ -1,6 +1,6 @@ %define name gscreenshot -%define version 3.4.3 -%define unmangled_version 3.4.3 +%define version 3.5.0 +%define unmangled_version 3.5.0 %define release 1 Summary: A simple screenshot tool From 1a98dd2449ec440fc88a0e22fd945b18a1518b90 Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 22:33:11 -0400 Subject: [PATCH 6/7] fix saving to nontransparent formats --- src/gscreenshot/screenshot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gscreenshot/screenshot/__init__.py b/src/gscreenshot/screenshot/__init__.py index 8b3ac0b..79c39fa 100644 --- a/src/gscreenshot/screenshot/__init__.py +++ b/src/gscreenshot/screenshot/__init__.py @@ -53,7 +53,7 @@ def get_image(self) -> Image.Image: if effect.enabled: image = effect.apply_to(image) - return image + return image.convert("RGB") def get_preview(self, width: int, height: int, with_border=False) -> Image.Image: ''' From 1498c6bff09dc4ce0e9c463a18b20db488022f34 Mon Sep 17 00:00:00 2001 From: Nate Levesque Date: Sat, 16 Mar 2024 22:34:54 -0400 Subject: [PATCH 7/7] test --- test/gscreenshot/screenshot/test_screenshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gscreenshot/screenshot/test_screenshot.py b/test/gscreenshot/screenshot/test_screenshot.py index f9e9fd5..53c6b12 100644 --- a/test/gscreenshot/screenshot/test_screenshot.py +++ b/test/gscreenshot/screenshot/test_screenshot.py @@ -27,7 +27,7 @@ def test_add_fake_cursor(self): ) ) - expected_img = Image.open("../test/gscreenshot/screenshot/cursor_overlay_expected.png") + expected_img = Image.open("../test/gscreenshot/screenshot/cursor_overlay_expected.png").convert("RGB") self.screenshot = Screenshot(original_img)