Skip to content

Error window box #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
BrikeX opened this issue Nov 12, 2024 · 14 comments
Closed

Error window box #95

BrikeX opened this issue Nov 12, 2024 · 14 comments

Comments

@BrikeX
Copy link

BrikeX commented Nov 12, 2024

I got negative window box parameters when I tested PyWinCtl on Ubuntu 18.04. Additionally, the area I captured had a margin around the window.

Test code:

import pywinctl
import sys


def capture_window(window_title):
    windows = pywinctl.getWindowsWithTitle(window_title)

    if not windows:
        print(f"No window found with title: '{window_title}'")
        sys.exit(1)

    window = windows[0]

    print("Window Properties")
    print(f"Title: {window.title}")
    print(f"Is Minimized: {window.isMinimized}")
    print(f"Is Maximized: {window.isMaximized}")
    print(f"Is Active: {window.isActive}")
    print(f"Geometry: {window.box}")
    print(
        f"Left: {window.left}, Top: {window.top}, Width: {window.width}, Height: {window.height}"
    )


if __name__ == "__main__":
    window_title = "System Monitor"

    capture_window(window_title)

Terminal output:

Window Properties
Title: System Monitor
Is Minimized: False
Is Maximized: False
Is Active: False
Geometry: Box(left=-26, top=-23, width=1012, height=1132)
Left: -26, Top: -23, Width: 1012, Height: 1132
@BrikeX
Copy link
Author

BrikeX commented Nov 12, 2024

The left and top parameters are both expected to be 0. Whenever I get negative box parameters, other code cannot work because the bounding box exceeds the screen’s boundaries. When I take a screenshot based on the bounding box, there’s always a margin around the window.

@BrikeX
Copy link
Author

BrikeX commented Nov 12, 2024

image

@BrikeX
Copy link
Author

BrikeX commented Nov 12, 2024

Here is the test code to capture a window:

import pywinctl
import mss
import time
import sys


def capture_window(window_title, output_path):
    # Retrieve all windows matching the title
    windows = pywinctl.getWindowsWithTitle(window_title)

    if not windows:
        print(f"No window found with title: '{window_title}'")
        sys.exit(1)

    window = windows[0]  # Select the first matching window

    if window.isMinimized:
        print("Window is minimized. Restoring...")
        window.restore()
        time.sleep(0.5)  # Wait a moment for the window to restore

    print("Activating window...")
    window.activate()
    time.sleep(0.5)

    bbox = {
        "left": window.left,
        "top": window.top,
        "width": window.width,
        "height": window.height,
    }

    with mss.mss() as sct:
        monitor = {
            "top": bbox["top"],
            "left": bbox["left"],
            "width": bbox["width"],
            "height": bbox["height"],
        }
        screenshot = sct.grab(monitor)
        mss.tools.to_png(screenshot.rgb, screenshot.size, output=output_path)
        print(f"Screenshot saved to {output_path}")


if __name__ == "__main__":
    window_title = "System Monitor"
    output_file = "window_capture.png"
    capture_window(window_title, output_file)

@Kalmat
Copy link
Owner

Kalmat commented Nov 12, 2024

Hi! Thank you for your interest and feedback!

Let me please provide some context regarding my findings about window position in GNOME.

GNOME reserves some space around the windows in order to leave enough space for some decorations (e.g. shadows). Unlike most other Desktop/Window Managers, when you ask GNOME for the window position, it will include this additional space, so it doesn't match what you are actually seeing on screen (If you place a window at (0, 0), it is actually at a negative position for GNOME).

Again unlike most other Desktop/Window Managers, most apps in GNOME (not all) do not set the standard property _NET_FRAME_EXTENTS, but a custom one named _GTK_FRAME_EXTENTS. This property, in the case of GNOME, contains the size of the additional space AROUND the window I was mentioning before.

Sorry if this is confusing or too detailed, but it is important because PyWinBox (the module internally used by PyWinCtl to manage windows dimensions and positions) tries to calculate the right position of the window in GNOME using these properties. It retrieves the position returned by GNOME (which includes the EXTRA space), and then it tries to substract the _GTK_FRAME_EXTENT values from it. In the case that there is no _GTK_FRAME_EXTENT values, or they are wrong... the position will be wrong and, unfortunately, there is not any reliable solution.

This all is a possible answer to your problem... Although it is also possible that PyWinBox is not working as expected (I remember this issue drove me crazy for weeks one year ago)

Apart from that, you're totally right: XLib will not accept negative values. This should be properly handled by PyWinBox.

In short, let me prepare a script you can run (if you agree, of course) to test all this, so we can check where can be the problem in your case.

I will come back to you as soon as possible.

@BrikeX
Copy link
Author

BrikeX commented Nov 12, 2024

I understand, and thanks a lot. I'm okay with doing some tests.

@Kalmat
Copy link
Owner

Kalmat commented Nov 13, 2024

Hi again!

I think I found a possible solution by using an alternative method to retrieve Xlib's window geometry. It returns promissing values for those applications not setting GTK_FRAME_EXTENTS, but _NET_FRAME_EXTENTS, Not sure if this will work Ok for all applications and conditions, but it seems worth to give it a try.

Anyway, bear in mind that in GNOME, as a general rule despite any workaround we can try, it will not be possible to programmatically move a window to position (0, 0), since it means using negative values that are not allowed by Xlib (you can, nevertheless, using the mouse, but not programmatically as far as I could find). If your desktop has a taskbar and an activity bar, it will be visually OK, taking position (0, 0) as (72, 27).

Please, uninstall PyWinBox module, install this new wheel (version 0.8) I am attaching to this message and try again.

Let me know the results and whatever thoughts or comments you may have. Thanks again for your help!!!

PyWinBox-0.8-py3-none-any.whl.zip

@BrikeX
Copy link
Author

BrikeX commented Nov 14, 2024

Hi again!

I think I found a possible solution by using an alternative method to retrieve Xlib's window geometry. It returns promissing values for those applications not setting GTK_FRAME_EXTENTS, but _NET_FRAME_EXTENTS, Not sure if this will work Ok for all applications and conditions, but it seems worth to give it a try.

Anyway, bear in mind that in GNOME, as a general rule despite any workaround we can try, it will not be possible to programmatically move a window to position (0, 0), since it means using negative values that are not allowed by Xlib (you can, nevertheless, using the mouse, but not programmatically as far as I could find). If your desktop has a taskbar and an activity bar, it will be visually OK, taking position (0, 0) as (72, 27).

Please, uninstall PyWinBox module, install this new wheel (version 0.8) I am attaching to this message and try again.

Let me know the results and whatever thoughts or comments you may have. Thanks again for your help!!!

PyWinBox-0.8-py3-none-any.whl.zip

image
I uninstalled pywinbox 0.7 and installed version 0.8. Sadly, it still doesn’t work. I’m encountering the same issues as before.

@Kalmat
Copy link
Owner

Kalmat commented Nov 14, 2024

So sad to hear that, in deed.

Testing it in my Ubuntu 20.04 VM (It's the closest version I have to your 18.04 install), it works perfectly well now, and the module returns the right values of every window position, despite it sets _NET or _GTK properties. However, I didn't test in position (0, 0) since I can not place any window at that position (how did you manage to do that?)

Using this very simple script:

import time

import ewmhlib as E
import pywinctl as pwc

windows = pwc.getAllWindows()

screenSize = pwc.getScreenSize()

for win in windows:
    print(win.title, win.position, win.size)
    x, y = win.position
    print("PWC/PWB:", win.position, win.size)    # PyWinCtl uses PyWinBox so the returned values are the same
    eWin = E.EwmhWindow(win.getHandle())         # EWMH-lib object to access other methods (which rely on Xlib anyway)
    xWin = eWin.xWindow                          # X-Window object as required by Xlib
    print("XLIB", xWin.get_geometry())           # position and size returned by Xlib itself
    print("EWMHLIB", E._ewmhlib._getWindowGeom(xWin, xWin.id))  # position and size returned by Xlib using alternative method

    print("NET_EXTENTS", eWin._getNetFrameExtents()) # values INSIDE the window (e.g. title, status and borders) as [left, right, top, bottom]
    print("GTK_EXTENTS", eWin._getGtkFrameExtents())  # values AROUND the window (GNOME Only) as [left, right, top, bottom]
    print("EXTENTS", eWin.getFrameExtents())         # public method which returns one or the other for general purpose
    print()

    win.moveTo(0, 0)
    print("MOVE 1", win.position, win.size)
    while True:
        try:
            time.sleep(0.3)
        except KeyboardInterrupt:
            break

    win.moveTo(screenSize.width - win.width, screenSize.height - win.height)
    print("MOVE 2", win.position, win.size)
    while True:
        try:
            time.sleep(0.3)
        except KeyboardInterrupt:
            break

    win.moveTo(x, y)
    print("MOVE 3", x, y, win.position, win.size)

The output is totally correct for all windows, including when the window is moved:

Captura

@BrikeX
Copy link
Author

BrikeX commented Nov 15, 2024

So sad to hear that, in deed.

Testing it in my Ubuntu 20.04 VM (It's the closest version I have to your 18.04 install), it works perfectly well now, and the module returns the right values of every window position, despite it sets _NET or _GTK properties. However, I didn't test in position (0, 0) since I can not place any window at that position (how did you manage to do that?)

Using this very simple script:

import time

import ewmhlib as E
import pywinctl as pwc

windows = pwc.getAllWindows()

screenSize = pwc.getScreenSize()

for win in windows:
    print(win.title, win.position, win.size)
    x, y = win.position
    print("PWC/PWB:", win.position, win.size)    # PyWinCtl uses PyWinBox so the returned values are the same
    eWin = E.EwmhWindow(win.getHandle())         # EWMH-lib object to access other methods (which rely on Xlib anyway)
    xWin = eWin.xWindow                          # X-Window object as required by Xlib
    print("XLIB", xWin.get_geometry())           # position and size returned by Xlib itself
    print("EWMHLIB", E._ewmhlib._getWindowGeom(xWin, xWin.id))  # position and size returned by Xlib using alternative method

    print("NET_EXTENTS", eWin._getNetFrameExtents()) # values INSIDE the window (e.g. title, status and borders) as [left, right, top, bottom]
    print("GTK_EXTENTS", eWin._getGtkFrameExtents())  # values AROUND the window (GNOME Only) as [left, right, top, bottom]
    print("EXTENTS", eWin.getFrameExtents())         # public method which returns one or the other for general purpose
    print()

    win.moveTo(0, 0)
    print("MOVE 1", win.position, win.size)
    while True:
        try:
            time.sleep(0.3)
        except KeyboardInterrupt:
            break

    win.moveTo(screenSize.width - win.width, screenSize.height - win.height)
    print("MOVE 2", win.position, win.size)
    while True:
        try:
            time.sleep(0.3)
        except KeyboardInterrupt:
            break

    win.moveTo(x, y)
    print("MOVE 3", x, y, win.position, win.size)

The output is totally correct for all windows, including when the window is moved:

Captura

You can auto hide the dock in Settings.
image
By the way, is there a margin around the captured window?

@Kalmat
Copy link
Owner

Kalmat commented Nov 16, 2024

Hi!

You can auto hide the dock in Settings.

Yes, but not the activity bar, which forces minimum height to 27.

By the way, is there a margin around the captured window?

Sorry, I'm affraid I don't understand the question. Can you please explain?

Did you have the chance to test the script I provided in my previous reply in your own system? If so, can you please paste the output and visually check if all windows move to the right position?

@BrikeX
Copy link
Author

BrikeX commented Nov 16, 2024

The left and top parameters are both expected to be 0.

Sorry, I mean that if you hide the menu bar, the top parameter could be 0, and you can hide the Dock to make the left parameter 0. The top-left corner of the screen is (0, 0).

Regarding the question:

By the way, is there a margin around the captured window?

, you can use the code I have presented at #95 (comment) to check the saved figure.

I will provide feedback if I have time to test your code.

@Kalmat
Copy link
Owner

Kalmat commented Nov 17, 2024

Hi again!

Running your sample script to capture a window, there is no margin around the window. I tested it in position (72, 27), as well as in position (0, 27) (I couldn't remove the activity bar, so I can not move the window to position (0, 0).

In both cases, the saved image is this one (no margins, as you can see here):

window_capture

@BrikeX
Copy link
Author

BrikeX commented Nov 17, 2024

Hi,
I also tested my code on Linux Mint 21.3, and here is the output:
image

It seems that there is a smaller margin than on Ubuntu 18.04(#95 (comment)). So I guess it has compatibility issues on different systems, and I can solve my own issue by tracking the mouse's position. Thanks a lot for your patience.

@Kalmat
Copy link
Owner

Kalmat commented Nov 17, 2024

Good news!

Still I don't understand why that difference between my captured image and yours but, anyway, if you can work it out, it's fine. So happy to know my module is useful!

Thank you again because I am able now to solve the initial issue: position and size not properly working for some applications in GNOME. I will upload to PyPi this new version (0.8) as soon as possible.

@Kalmat Kalmat closed this as completed Nov 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants