normcap icon indicating copy to clipboard operation
normcap copied to clipboard

Captured image is offset when using a single monitor not located at 0,0 (wlroots)

Open arthomnix opened this issue 7 months ago • 7 comments

What happened?

When using a single monitor not located at (0,0), the screenshot taken by NormCap is offset (as shown below). This can happen for example if you plug in a second monitor, set the second monitor to be located at (0,0) and the main monitor to be offset, and later unplug the second monitor.

Image

This image is the raw screenshot from /tmp/normcap, taken on a 2560x1600 display located at (0,1080).

Steps to reproduce:

  • Make sure you are using a wlroots-based Wayland compositor with wlr-randr installed
  • Run wlr-randr and note the ID of the display
  • Run wlr-randr --output <display ID> --pos 0,1080
  • Run normcap

How did you install NormCap?

AUR (Linux)

Operating System + Version?

Arch Linux (up to date as of 2024-04-11, kernel 6.14.1-zen1-1-zen)

[Linux only] Display Server (DS) + Desktop environment (DE)?

DS: Wayland, DE: river (wlroots)

Debug log output?*

18:26:56 - INFO    - normcap:50 - Start NormCap v0.5.9
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::systempalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::toolbuttonpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::buttonpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::checkboxpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::radiobuttonpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::headerpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::itemviewpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::messageboxlabelpelette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::tabbarpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::labelpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::groupboxpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::menupalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::menubarpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::texteditpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::texteditpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::textlineeditpalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::tooltippalette
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual qvariant qt6ctplatformtheme::themehint(qplatformtheme::themehint) const
18:26:56 - DEBUG   - normcap:183 - [QT] qtdebugmsg - virtual const qpalette* qt6ctplatformtheme::palette(qplatformtheme::palette) const qplatformtheme::systempalette
18:26:56 - DEBUG   - normcap.gui.tray:70 - System info:
{'normcap_version': '0.5.9', 'python_version': '3.13.2', 'cli_args': '/usr/bin/normcap -v debug', 'is_briefcase_package': False, 'is_flatpak_package': False, 'is_appimage_package': False, 'platform': 'linux', 'desktop_environment': <DesktopEnvironment.OTHER: 1>, 'display_manager_is_wayland': True, 'pyside6_version': '6.9.0', 'qt_version': '6.9.0', 'qt_library_path': '/usr/lib/qt6/plugins, /usr/bin', 'locale': 'DEFAULT', 'config_directory': PosixPath('/home/arthomnix/.config/normcap'), 'resources_path': PosixPath('/usr/lib/python3.13/site-packages/normcap/resources'), 'tesseract_path': PosixPath('/usr/bin/tesseract'), 'tessdata_path': None, 'envs': {'TESSDATA_PREFIX': None, 'LD_LIBRARY_PATH': None}, 'screens': [Screen(left=0, top=1080, right=2559, bottom=2679, device_pixel_ratio=1.0, index=0, screenshot=None)]}
18:26:56 - DEBUG   - normcap.gui.settings:167 - Skip update of non existing setting (show_introduction: None)
18:26:56 - DEBUG   - normcap.gui.settings:167 - Skip update of non existing setting (cli_mode: False)
18:26:56 - DEBUG   - normcap.gui.settings:167 - Skip update of non existing setting (background_mode: False)
18:26:56 - DEBUG   - normcap.gui.settings:167 - Skip update of non existing setting (clipboard_handler: None)
18:26:56 - DEBUG   - normcap.gui.tray:384 - Listen on local socket v0.5.9-normcap.
18:26:56 - DEBUG   - normcap.screengrab.main:21 - Compatible capture handlers: ['GRIM', 'DBUS_PORTAL', 'DBUS_SHELL']
18:26:56 - DEBUG   - normcap.screengrab.main:28 - Available capture handlers: ['GRIM', 'DBUS_PORTAL', 'DBUS_SHELL']
18:26:57 - DEBUG   - normcap.screengrab.post_processing:17 - Virtual geometry width: 2560
18:26:57 - DEBUG   - normcap.screengrab.post_processing:18 - Image width: 2560
18:26:57 - DEBUG   - normcap.screengrab.post_processing:19 - Resize ratio: 1.0
18:26:57 - INFO    - normcap.screengrab.main:67 - Screen captured using GRIM
18:26:57 - DEBUG   - normcap.gui.utils:22 - Save debug image as /tmp/normcap/2025-04-12_17-26-57_raw_screen0.png
18:26:57 - DEBUG   - normcap.gui.window:51 - Create window for screen 0
18:26:57 - DEBUG   - normcap.gui.window:141 - Set window of screen 0 to fullscreen
18:26:57 - DEBUG   - normcap:183 - [QT] qtwarningmsg - qsystemtrayicon::setvisible: no icon set
18:26:57 - DEBUG   - normcap.ocr.tesseract:24 - Executing '/usr/bin/tesseract --list-langs'
18:26:57 - DEBUG   - normcap.ocr.tesseract:37 - Tesseract command output: List of available languages in "/usr/share/tessdata/" (2): ¬ eng ¬ osd ¬
18:26:57 - WARNING - normcap.gui.window:129 - No window move method for 1
18:27:04 - DEBUG   - normcap.gui.tray:350 - Hide 1 window
18:27:04 - INFO    - normcap.gui.tray:606 - Exit normcap
18:27:04 - DEBUG   - normcap.gui.tray:607 - Debug images saved in /tmp/normcap

arthomnix avatar Apr 12 '25 17:04 arthomnix

Thanks for reporting, @arthomnix. Could you please check, if this issue is already present when you take a screenshot by running plain grim in that scenario? How does the resulting screenshot look in that case?

dynobo avatar Apr 17 '25 14:04 dynobo

Using plain grim the screenshot looks normal:

Image

arthomnix avatar Apr 17 '25 17:04 arthomnix

It looks like the issue is that the post-processing (https://github.com/dynobo/normcap/blob/main/normcap/screenshot/post_processing.py#L8) assumes that (0,0) in image coordinates will always be the same point as (0,0) in virtual geometry coordinates, however this is not the case for grim screenshots in this situation.

arthomnix avatar Apr 17 '25 17:04 arthomnix

One solution would be to get the virtual geometry size before capturing the image, and pass this to grim with the corner of the image fixed to 0,0 - so in this case the command would be grim -g "0,0 2560x2680"

arthomnix avatar Apr 17 '25 17:04 arthomnix

It looks like the issue is that the post-processing (https://github.com/dynobo/normcap/blob/main/normcap/screenshot/post_processing.py#L8) assumes that (0,0) in image coordinates will always be the same point as (0,0) in virtual geometry coordinates, however this is not the case for grim screenshots in this situation.

May I ask, where exactly do you see this assumption?

Because the intention is, to exactly not assume this but just rely on the screen coordinates report by Qt.

It would be interesting to know, if the coordinated reported by Qt are correct in this case, though. It would help a lot, if you could check that, by debugging into it or just printing the raw coordinates or something.

PS: Do you know a distro which offers a LiveCD with River (or maybe another wlroots based DE)? It's quite cumbersome to set up VM for various configurations, and my time is quite limited.

dynobo avatar Apr 21 '25 14:04 dynobo

May I ask, where exactly do you see this assumption?

The assumption is that the captured screenshot will cover the entire virtual geometry (including (0,0)). It can handle if the screenshot is scaled compared to the virtual geometry coordinates, but can't handle if the screenshot is translated (i.e. the top left of the screenshot doesn't correspond to (0,0) in the virtual geometry).

The issue is that grim automatically crops the screenshot so it contains only your displays and nothing extra, so if your display is offset from (0,0) the top left of the image will not correspond to (0,0) in virtual geometry coordinates as the post-processing code expects. If you don't want grim to do this, you have to use the -g option to specify the geometry of the screenshot manually with (0,0) as the origin.

PS: Do you know a distro which offers a LiveCD with River (or maybe another wlroots based DE)? It's quite cumbersome to set up VM for various configurations, and my time is quite limited.

Try Fedora Sway Spin (Sway is also wlroots-based)

arthomnix avatar Apr 21 '25 19:04 arthomnix

Thanks, @arthomnix , will try to check this out!

dynobo avatar Apr 21 '25 20:04 dynobo