Skip to content
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

Error with pymonctl.getAllMonitorsDict() on Linux Mint and Arch Linux #86

Closed
recluzegeek opened this issue Mar 21, 2024 · 11 comments
Closed

Comments

@recluzegeek
Copy link

recluzegeek commented Mar 21, 2024

Currently, I'm playing with this wonderful cross-platform library to track my application usage across systems and encountered the errors on the way described below:

  • The following error occurs when running the test_pywinctl.py under test directory on Linux Mint with Python 3.10. Works fine in window 10 under virtualbox.
Exception has occurred: AttributeError
module 'Xlib.ext.randr' has no attribute 'get_monitors'
  File "/home/osboxes/active_windows.py", line 19, in test_basic
    monitors = pymonctl.getAllMonitorsDict()
  File "/home/osboxes/active_windows.py", line 630, in main
    test_basic()
  File "/home/osboxes/active_windows.py", line 634, in <module>
    main()
AttributeError: module 'Xlib.ext.randr' has no attribute 'get_monitors'
  • Similar kind of issue appeared on the Arch Linux with Python 3.11.8. This time was the deprecation warning

DeprecationWarning: getAllScreens() is deprecated. Use getAllMonitorsDict() from PyMonCtl module instead monitors = pywinctl.getAllScreens()

Following the warning, refactored the code to use pymonctl.getAllMonitorsDict() and got this error,

  PLATFORM: linux

  MONITORS:
  Traceback (most recent call last):
  File "/src/active_window.py", line 19, in test_basic
    monitors = pymonctl.getAllMonitorsDict()
  File "/.venv/lib/python3.11/site-packages/pymonctl/_main.py", line 75, in getAllMonitorsDict
    return _getAllMonitorsDict()
           ^^^^^^^^^^^^^^^^^^^^^
  File "/.venv/lib/python3.11/site-packages/pymonctl/_pymonctl_linux.py", line 54, in _getAllMonitorsDict
    wx, wy, wr, wb = wa[0], wa[1], wa[2], wa[3]
                     ~~^^^
TypeError: 'NoneType' object is not subscriptable

From what I've read in the pymonctl README.md, properties may be None if can't be read/obtained. Also the key thing to note is that it doesn't work on Linux Mint with XLib installed

I'm using dual monitor and managing them using ARandR — Screen Layout Editor for Linux.

@recluzegeek recluzegeek changed the title module 'Xlib.ext.randr' has no attribute 'get_monitors' on Linux Mint & getting TypeError: 'NoneType' object is not subscriptable on Arch linux when using pymonctl.getAllMonitorsDict() Error with pymonctl.getAllMonitorsDict() on Linux Mint and Arch Linux Mar 21, 2024
@Kalmat
Copy link
Owner

Kalmat commented Mar 21, 2024

Hi! Thanks A LOT for your feedback!!!

Sorry I've been extremely busy, so I had no time to finish and fully test next version of all involved modules (PyWinBox, PyMonCtl and PyWinCtl)... I hope to be able to test everything soon, so I can eventually upload all these new versions which fix tones of issues and improves some other features.

In your case, this is how that part of the code looks now:

# Thanks to odknt (https://github.com/odknt) for his HELP!!!
if isinstance(wa, list) and len(wa) >= 4:
    wx, wy, wr, wb = wa[0], wa[1], wa[2], wa[3]
else:
    wx, wy, wr, wb = x, y, w, h

Linux and all possible combinations is particularly complex (well, it's sort of a nightmare, to be hones), so testing everything takes a lot. I hope you can wait!!!!

In the meantime, do not hesitate to arise any comment, issue or suggestion you may have. Thanks again!!!

EDIT: Did you fix the other issue regarding DWM? Is awesome an alternative you may consider? Just curious

@recluzegeek
Copy link
Author

recluzegeek commented Mar 21, 2024

Thanks for the quick reply. I'll look into your provided code snippet and come back to you once I test it. Shifting to awesome from dwm doesn't seem feasible at the moment because dwm is deeply integrated into my daily workflow. I rely heavily on it, and my muscle memory is attuned to its shortcuts, minimizing my use of the mouse.

EDIT: I tested these lines of code in windows, linux mint and arch running dwm and all of them passed, except on arch empty list and dictionary were returned

import pywinctl

print(pywinctl.getAllTitles())
print(pywinctl.getAllAppsWindowsTitles())

# ------------------------
# RESULT ON ARCH

[]
{}

Process finished with exit code 0

@Kalmat
Copy link
Owner

Kalmat commented Mar 21, 2024

HAHAHAHA! Sorry. SInce it says awesome is a fork of dwm I thought it would be more straightforward.

I guess you mean that you have tested Mint and Arch together with dwm, right? If so, since PyWinCtl requires EMWH, it is likely not properly working. In case it is not dwm, please let me know which desktop/window managers you are using in those cases.

Thanks!

@recluzegeek
Copy link
Author

No worries, I too was confused in my early linux days. It happens and IMO its normal :) I've tested arch with dwm and used Linux Mint 21.3 x86_64 with Cinnamon 6.0.4 as desktop environment.

@Kalmat
Copy link
Owner

Kalmat commented Mar 21, 2024

Ah, ok! That's why Mint works, whilst Arch doesn't... Arch with other desktop/window managers works if those managers are EWMH-compliant. I already have actual installs of Ubuntu/GNOME (Wayland is not working since it's not EMWH-compliant, Linux Mint/Cinnamon and Manjaro/KDE; but not Arch yet, sorry.

@recluzegeek
Copy link
Author

recluzegeek commented Mar 21, 2024

Manjaro actually is derived from Arch, as Mint is derived of Ubuntu. If it works on Manjaro, then it also should be working in arch. Issue itself is not in the arch, rather in the desktop/windows manager. In this case, dwm doesn't seems to be EWMH-compliant.

I created this git issue for mint on this issue, AttributeError: module 'Xlib.ext.randr' has no attribute 'get_monitors', since Xlib should be performing well across linux distros. Surprisingly, this issue arose during my Mint testing and not on Arch :)

Arch with dwm is my main development machine, yet I think I've to work in mint for sometime till new release.

@Kalmat
Copy link
Owner

Kalmat commented Mar 21, 2024

Yes, that's correct. EMWH-compliance depends on the Desktop/Windows Manager, not the OS. E.g. in "standard" Ubuntu, GNOME is EWMH-compliant, but Wayland is not (furthermore, there is no way in Wayland to get the list of windows nor the active one... I mean, that feature is not supported at all, even using any other protocol to communicate with it)

I hope to be able to start all tests soon, which include Mint/Cinnamon amongst many other combinations. I will keep you posted on any progress. Hopefully I will have a complete new version ready to be uploaded so all these issues are fixed.

Please, bear in mind that non-EWMH window managers (like dwm) will still not work with the new version, since it will inevitably continue to rely on EWMH to handle windows.

Back to the initial issue, and just if you are curious, this is a snippet of the new version, which includes a workaround specifically for Cinnamon:

def _getMonitorsData(handle: Optional[int] = None) -> (
                        List[Tuple[Xlib.display.Display, Struct, XWindow, randr.GetScreenResourcesCurrent,
                        randr.MonitorInfo, str, int, randr.GetOutputInfo, int, randr.GetCrtcInfo]]):
    monitors: List[Tuple[Xlib.display.Display, Struct, XWindow, randr.GetScreenResourcesCurrent,
                         randr.MonitorInfo, str, int, randr.GetOutputInfo, int, randr.GetCrtcInfo]] = []
    stopSearching = False
    for rootData in getRoots():
        display, screen, root = rootData
        try:
            mons = randr.get_monitors(root).monitors
        except:
            # In Cinnamon randr extension has no get_monitors() method (?!?!?!?)
            mons = _RgetAllMonitors()
            stopSearching = True
        for monitor in mons:
            if isinstance(monitor.name, int):
                monitor.name = display.get_atom_name(monitor.name)
            res = randr.get_screen_resources_current(root)
            output = monitor.crtcs[0]
            outputInfo = randr.get_output_info(display, output, res.config_timestamp)
            if outputInfo.crtc:
                if handle:
                    if handle == output:
                        crtcInfo = randr.get_crtc_info(display, outputInfo.crtc, res.config_timestamp)
                        return [(display, screen, root, res, monitor, monitor.name, output, outputInfo, outputInfo.crtc, crtcInfo)]
                else:
                    crtcInfo = randr.get_crtc_info(display, outputInfo.crtc, res.config_timestamp)
                    monitors.append((display, screen, root, res, monitor, monitor.name, output, outputInfo, outputInfo.crtc, crtcInfo))
        if stopSearching:
            break
    return monitors

def _RgetAllMonitors():
    # Check if this works in actual Cinnamon
    monitors: List[_Monitor] = []
    outputDict = {}
    for outputData in _XgetAllOutputs():
        display, screen, root, output, outputInfo = outputData
        outputDict[outputInfo.name] = {"outputData": outputData}

    namesData = _RgetMonitorsInfo()
    for item in namesData:
        monName, primary, x, y, w, h = item
        if monName in outputDict.keys():
            display, screen, root, output, outputInfo = outputDict[monName]["outputData"]
            wm, hm = outputInfo.mm_width, outputInfo.mm_height
            crtcs = [output]
            monitors.append(_Monitor(monName, primary, x, y, w, h, wm, hm, crtcs))
    return monitors

@recluzegeek
Copy link
Author

Hi there again. This time I'm not creating a new issue, 😄 just for clarification and enhancements. Does pywinctl provides any interface or method to return the currently running app name along with its windows title.

Like the new_window = pywinctl.getActiveWindow() returns the currently focused windows title,

Error with pymonctl.getAllMonitorsDict() on Linux Mint and Arch Linux · Issue #86 · Kalmat/PyWinCtl — Mozilla Firefox

I want a method to return Mozilla Firefox or firefox.exe (container in which its running) or something like that. Maybe the method exists, I'm unaware of it.

Also the docs didn't provided any help either, there are just bunch of methods name, we should be documenting them properly, what they returns or what they do along with some code examples.

@Kalmat
Copy link
Owner

Kalmat commented Mar 25, 2024

Hi again! I will try to clarify and summarize.

PYWINCTL:
PyWinCtl returns the active window and the name of the app it belongs to, by using:

    new_window = pywinctl.getActiveWindow()
    if new_window is not None:
        app_name = new_window.getAppName()
        print(app_name)
    else:
        print("NOT FOUND")

But ONLY IF the desktop/window manager is EWMH-compliant (e.g. GNOME, Cinnamon, KDE, awesome, ...). PyWinCtl will not work with non-EWMH environments (e.g. DWM, Wayland, ...)

PYMONCTL

  • Mint/Cinnamon: The module fails (it was working OK in a virtual machine, but not in an actual install). Fixed in next version.
  • Arch: Current version was never tested in this distro, sorry. Nevertheless, it works ok in Manjaro/KDE, but the watchdog freezes. Fixed in next version.

In short, you can use PyWinCtl to get the active app name, but not together with DWM.
If you also need PyMonCtl, I can provide you with a wheel that fixes the Cinnamon issue so you can work with it whilst I finish all tests and upload the new version.

I hope this helps to clarify.

@recluzegeek
Copy link
Author

Thanks for the clarification. I'm now working on mint in virtual box and was asking a general question about getting app name. It was my bad for not understanding the method naming convention. Again thanks for the clarification. 😶‍🌫️

@Kalmat
Copy link
Owner

Kalmat commented Mar 26, 2024

No worries at all! Happy to hear this has been helpful. Thank you so much for your feedback. As you suggested, I have improved the module documentation (again, it will be available in next version).

Just let me know if you need anything else, you find any other issue or you have any suggestion!

@Kalmat Kalmat closed this as completed Sep 23, 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