Setting terminal window's title: wmctrl versus xdotool

Edit: I am not looking for other ways or better ways to change a window's title or to add titles to tabs in terminals. The answers in the proposed duplicate do not in any way come close to addressing the specific issue of why xdotool search … does not pick up window titles when the title is set by wmctrl as described above.


OS: Ubuntu 18.04

When I open a gnome-terminal window, the title is dkb@dkb:~

I can change it using

wmctrl -r :ACTIVE: -N "NewName"

but xdotool search …, doesn't "see" this title:

dkb@dkb:~$ xdotool search --name NewName
dkb@dkb:~$ 

I just get back the prompt instead of being provided the corresponding window identifier.

On the other hand, I can use xdotool itself to set the title and then xdotool search … provides the window identifier:

dkb@dkb:~$ xdotool getactivewindow set_window --name NewName
dkb@dkb:~$ xdotool search --name NewName
39845894
dkb@dkb:~$ 

In other words, if the title is set by xdotool, xdotool search works as it should:

From man xdotool

search [options] pattern Search for windows with titles, names, or classes with a regular expression pattern. The output is line-delimited list of X window identifiers.

So while it's not a big deal, I'd like to know what the reason is for xdotool not "seeing" the window title set by wmctrl.


Solution 1:

We can see that at the very least xdotool getactivewindow getwindowname works as intended.

$ wmctrl -r :ACTIVE: -N "Test1"
$ xdotool getactivewindow getwindowname
Test1
$ xdotool getactivewindow set_window --name Test2
$ xdotool getactivewindow getwindowname
Test2

So let's go deeper. We can list properties of window using xprop -id <id> command. For window with name set by wmctrl it gives:

_NET_WM_OPAQUE_REGION(CARDINAL) = 0, 0, 1920, 995
_NET_STARTUP_ID(UTF8_STRING) = "brisk-menu-2514-mariusz-HP-Pavilion-Notebook-mate-terminal-1_TIME1314631"
WM_WINDOW_ROLE(STRING) = "mate-terminal-window-4442-66103825-1563868224"
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_SYNC_REQUEST_COUNTER(CARDINAL) = 79691784, 79691785
_NET_WM_USER_TIME_WINDOW(WINDOW): window id # 0x4c00007
WM_CLIENT_LEADER(WINDOW): window id # 0x4c00001
_NET_WM_PID(CARDINAL) = 4442
WM_LOCALE_NAME(STRING) = "en_US.UTF-8"
WM_CLIENT_MACHINE(STRING) = "mariusz-HP-Pavilion-Notebook"
WM_NORMAL_HINTS(WM_SIZE_HINTS):
        program specified minimum size: 345 by 141
        program specified resize increment: 9 by 20
        program specified base size: 16 by 30
        window gravity: NorthWest
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING, _NET_WM_SYNC_REQUEST
WM_CLASS(STRING) = "mate-terminal", "Mate-terminal"
_NET_WM_ICON_NAME(UTF8_STRING) = "Terminal"
_NET_WM_NAME(UTF8_STRING) = "Test1"

And for for window with name set by xdotool it gives:

_NET_WM_OPAQUE_REGION(CARDINAL) = 0, 0, 1920, 995
_NET_STARTUP_ID(UTF8_STRING) = "brisk-menu-2514-mariusz-HP-Pavilion-Notebook-mate-terminal-1_TIME1314631"
WM_WINDOW_ROLE(STRING) = "mate-terminal-window-4442-66103825-1563868224"
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_SYNC_REQUEST_COUNTER(CARDINAL) = 79691784, 79691785
_NET_WM_USER_TIME_WINDOW(WINDOW): window id # 0x4c00007
WM_CLIENT_LEADER(WINDOW): window id # 0x4c00001
_NET_WM_PID(CARDINAL) = 4442
WM_LOCALE_NAME(STRING) = "en_US.UTF-8"
WM_CLIENT_MACHINE(STRING) = "mariusz-HP-Pavilion-Notebook"
WM_NORMAL_HINTS(WM_SIZE_HINTS):
        program specified minimum size: 345 by 141
        program specified resize increment: 9 by 20
        program specified base size: 16 by 30
        window gravity: NorthWest
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING, _NET_WM_SYNC_REQUEST
WM_CLASS(STRING) = "mate-terminal", "Mate-terminal"
_NET_WM_ICON_NAME(UTF8_STRING) = "Terminal"
_NET_WM_NAME(STRING) = "Test2"

And we can see that in both cases _NET_WM_NAME is set correctly. What is _NET_WM_NAME? It's an extension for x11 (link, link)

_NET_WM_NAME

_NET_WM_NAME, UTF8_STRING

The Client SHOULD set this to the title of the window in UTF-8 encoding. If set, the Window Manager should use this in preference to WM_NAME.

So x11 applications should prefer this property over WM_NAME. Let's display both of these properties. I've taken C code from here:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXSTR 1000

Display *display;
unsigned long window;
unsigned char *prop;

void check_status(int status, unsigned long window)
{
    if (status == BadWindow) {
        printf("window id # 0x%lx does not exists!", window);
        exit(1);
    }

    if (status != Success) {
        printf("XGetWindowProperty failed!");
        exit(2);
    }
}

unsigned char* get_string_property(char* property_name)
{
    Atom actual_type, filter_atom;
    int actual_format, status;
    unsigned long nitems, bytes_after;

    filter_atom = XInternAtom(display, property_name, True);
    status = XGetWindowProperty(display, window, filter_atom, 0, MAXSTR, False, AnyPropertyType,
                                &actual_type, &actual_format, &nitems, &bytes_after, &prop);
    check_status(status, window);
    return prop;
}

unsigned long get_long_property(char* property_name)
{
    get_string_property(property_name);
    unsigned long long_property = prop[0] + (prop[1]<<8) + (prop[2]<<16) + (prop[3]<<24);
    return long_property;
}

int main(int argc, char** argv)
{
    char *display_name = NULL;  // could be the value of $DISPLAY

    display = XOpenDisplay(display_name);
    if (display == NULL) {
        fprintf (stderr, "%s:  unable to open display '%s'\n", argv[0], XDisplayName (display_name));
    }
    int screen = XDefaultScreen(display);
    window = RootWindow(display, screen);

    window = get_long_property("_NET_ACTIVE_WINDOW");

    printf("_NET_WM_PID: %lu\n", get_long_property("_NET_WM_PID"));
    printf("WM_CLASS: %s\n", get_string_property("WM_CLASS"));
    printf("_NET_WM_NAME: %s\n", get_string_property("_NET_WM_NAME"));
    printf("WM_NAME: %s\n", get_string_property("WM_NAME"));

    XCloseDisplay(display);
    return 0;
}

Only added a printf("WM_NAME: %s\n", get_string_property("WM_NAME")); line. Running:

$ gcc test.c -o test -lX11
$ wmctrl -r :ACTIVE: -N "Test1"
$ ./test 
_NET_WM_PID: 4442
WM_CLASS: mate-terminal
_NET_WM_NAME: Test1
WM_NAME: (null)
$ xdotool getactivewindow set_window --name Test2
$ ./test 
_NET_WM_PID: 4442
WM_CLASS: mate-terminal
_NET_WM_NAME: Test2
WM_NAME: Test2

So the conclusion is: xdotool sets both _NET_WM_NAME and WN_NAME but only search by WM_NAME and wmctrl sets only _NET_WM_NAME.