Skip to content

window.get_wm_name() is empty but not in window.get_property() equivalent #212

Open

Description

@HMaker

I am trying to interact with chrome browser windows using X11 APIs but python-xlib wrapper always returns empty window names. Here is what xprop shows about chrome windows:

WM_STATE(WM_STATE):
		window state: Normal
		icon window: 0x0
_NET_WM_DESKTOP(CARDINAL) = 0
_GTK_EDGE_CONSTRAINTS(CARDINAL) = 85
_NET_WM_ALLOWED_ACTIONS(ATOM) = _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_MINIMIZE, _NET_WM_ACTION_MAXIMIZE_HORZ, _NET_WM_ACTION_MAXIMIZE_VERT, _NET_WM_ACTION_CHANGE_DESKTOP, _NET_WM_ACTION_CLOSE, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_BELOW
_NET_WM_STATE(ATOM) = _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT
_NET_WM_USER_TIME(CARDINAL) = 19822488
WM_NORMAL_HINTS(WM_SIZE_HINTS):
		program specified location: 65, 24
		program specified minimum size: 500 by 72
_MOTIF_WM_HINTS(_MOTIF_WM_HINTS) = 0x2, 0x0, 0x0, 0x0, 0x0
WM_NAME(UTF8_STRING) = "New Issue · python-xlib/python-xlib - Google Chrome"
_NET_WM_NAME(UTF8_STRING) = "New Issue · python-xlib/python-xlib - Google Chrome"
XdndAware(ATOM) = BITMAP
_NET_WM_BYPASS_COMPOSITOR(CARDINAL) = 2
_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED(CARDINAL) = 1
WM_WINDOW_ROLE(STRING) = "browser"
WM_CLASS(STRING) = "google-chrome", "Google-chrome"
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_PID(CARDINAL) = 5700
WM_CLIENT_MACHINE(STRING) = "ubuntu"
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW, _NET_WM_PING, _NET_WM_SYNC_REQUEST

there WM_NAME property is not empty, so I tried the following which worked:

window.get_property(display.get_atom("WM_NAME"), display.get_atom("UTF8_STRING"), 0, 1024)

I guess xlib is using wrong property type, it must be UTF8_STRING, otherwise empty string is returned.

I am running python-xlib 0.31 on Python 3.8 on Ubuntu 20.04.

Activity

  1. benoit-pierre commented on Dec 2, 2021

    @benoit-pierre
    Member

    I can reproduce with Chromium.

    From looking at XGetWindowProperty's documentation:

    If the specified property exists but its type does not match the specified
    type, XGetWindowProperty returns the actual property type to actual_type_return,
    the actual property format (never zero) to actual_format_return, and the property
    length in bytes (even if the actual_format_return is 16 or 32) to bytes_after_return.
    It also ignores the delete argument. The nitems_return argument is empty.

    …it looks like our implementation of Window.get_full_property is wrong and should
    call get_property again with the correct type if the previous attempt returned
    a different type than asked for and bytes_after is not zero:

     Xlib/xobject/drawable.py | 22 +++++++++++++---------
     1 file changed, 13 insertions(+), 9 deletions(-)
    
    diff --git i/Xlib/xobject/drawable.py w/Xlib/xobject/drawable.py
    index c36a973..f79a7dc 100644
    --- i/Xlib/xobject/drawable.py
    +++ w/Xlib/xobject/drawable.py
    @@ -470,17 +470,21 @@ def get_property(self, property, property_type, offset, length, delete = 0):
     
         def get_full_property(self, property, property_type, sizehint = 10):
             prop = self.get_property(property, property_type, 0, sizehint)
    -        if prop:
    -            val = prop.value
    -            if prop.bytes_after:
    -                prop = self.get_property(property, property_type, sizehint,
    -                                         prop.bytes_after // 4 + 1)
    -                val = val + prop.value
    -
    -            prop.value = val
    +        if not prop:
    +            return None
    +        if not prop.bytes_after:
                 return prop
    +        val = prop.value
    +        if prop.property_type == property_type:
    +            offset = sizehint
    +            sizehint = prop.bytes_after // 4 + 1
             else:
    -            return None
    +            assert not val
    +            property_type = prop.property_type
    +            offset = 0
    +        prop = self.get_property(property, property_type, offset, sizehint)
    +        prop.value = val + prop.value
    +        return prop
     
         def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10):
             prop = self.get_full_property(property, property_type,
  2. MestreLion commented on Jan 23, 2023

    @MestreLion

    If the property_type parameter in Python is meant as a sort of "filter" to specify what the returned property should be, then I believe repeating the request with a different type would be the wrong approach to the problem.

    A better fix, IMHO, is to drop the Xatom.STRING argument when calling get_full_property in Window.get_wm_name() (and similar methods such as get_wm_icon_name). I wonder why it is using it instead of the default X.AnyPropertyType.

  3. MestreLion commented on Jan 23, 2023

    @MestreLion

    get_full_property() should not change a caller's argument: in case of an inconsistency between the requested and the obtained type it should either raise an exception or just return the obtained property as-is (bytes_left included) and let the caller handle the inconsistency. Otherwise, what's the point of the property_type parameter?

  4. MestreLion commented on Jan 23, 2023

    @MestreLion

    In other words: the meaning (and usefulness) of property_type in both Window.get_property() and Window.get_full_property() is debatable (IMHO both should be hard-coded to X.AnyPropertyType (= 0)), but in this case the bug is in Windows.get_wm_name() and friends for specifying the wrong type (or for specifying a type at all).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions