Repository: qtile/qtile

Issue:
mpv window doesn't spawn when launched via webtorrent or play-with-mpv apps
When I manually run `mpv` it opens up normally, while when I run applications like `webtorrent` or `play-with-mpv` the window of mpv won't spawn and it keeps running in background (while I can hear the audio). I don't have any issues when the same application launched with `vlc` option.


```
[farid@unknown ~/.config/qtile]$ ls -la ~/.config/mpv/
total 8
drwx------  2 farid farid 4096 Jan  2 00:20 .
drwxr-xr-x 49 farid farid 4096 Jan  2 00:20 ..
[farid@unknown ~]$
```
qtile version:
```
[farid@unknown ~]$ qtile -v
0.19.1.dev10+gda78ea40
[farid@unknown ~]$ pacman -Qi qtile-git
Name            : qtile-git
Version         : 0.19.0.r10.gda78ea40-1
Description     : A full-featured, pure-Python tiling window manager. (git version)
Architecture    : x86_64
URL             : http://www.qtile.org
Licenses        : MIT
Groups          : None
Provides        : qtile
Depends On      : gdk-pixbuf2  glibc  pango  python-cairocffi  python-cffi  python-xcffib
Optional Deps   : alsa-utils: volume widget [installed]
                  canto-daemon: canto widget
                  cmus: cmus widget
                  jupyter_console: interaction with qtile via Jupyter
                  khal: khal_calendar widget
                  libpulse: for pulse_volume and pulseaudio_ffi widget [installed]
                  librsvg: for SVG support in some widgets widgets or wallpapers [installed]
                  lm_sensors: sensors widget [installed]
                  moc: moc widget
                  python-dbus-next: for utils, notifications and several widgets [installed]
                  python-iwlib: wlan widget [installed]
                  python-keyring: imapwidget widget
                  python-mpd2: mpd2widget widget
                  python-psutil: graph, net and memory widget [installed]
                  python-pywlroots: Wayland backend [installed]
                  python-setproctitle: change process name to qtile
                  python-xdg: launchbar widget [installed]
Required By     : None
Optional For    : None
Conflicts With  : qtile
Replaces        : None
Installed Size  : 3.59 MiB
Packager        : Unknown Packager
Build Date      : Thu 30 Dec 2021 04:17:57 PM +04
Install Date    : Thu 30 Dec 2021 04:18:08 PM +04
Install Reason  : Explicitly installed
Install Script  : Yes
Validated By    : None
```

config.py:
```

from typing import List  # noqa: F401

from libqtile import bar, layout, widget
from libqtile import qtile, hook
from libqtile.config import Click, Drag, Group, Key, Match, Screen
from libqtile.lazy import lazy
from libqtile.utils import guess_terminal
import subprocess
import os


mod = "mod4"
terminal = "alacritty"

keys = [
    # Switch between windows
    Key([mod], "Left", lazy.layout.left(), desc="Move focus to left"),
    Key([mod], "Right", lazy.layout.right(), desc="Move focus to right"),
    Key([mod], "Down", lazy.layout.down(), desc="Move focus down"),
    Key([mod], "Up", lazy.layout.up(), desc="Move focus up"),
    Key([mod], "space", lazy.layout.next(),
        desc="Move window focus to other window"),

    # Move windows between left/right columns or move up/down in current stack.
    # Moving out of range in Columns layout will create new column.
    Key([mod, "shift"], "Left", lazy.layout.shuffle_left(),
        desc="Move window to the left"),
    Key([mod, "shift"], "Right", lazy.layout.shuffle_right(),
        desc="Move window to the right"),
    Key([mod, "shift"], "Down", lazy.layout.shuffle_down(),
        desc="Move window down"),
    Key([mod, "shift"], "Up", lazy.layout.shuffle_up(), desc="Move window up"),

    # Grow windows. If current window is on the edge of screen and direction
    # will be to screen edge - window would shrink.
    Key([mod, "control"], "Left", lazy.layout.grow_left(),
        desc="Grow window to the left"),
    Key([mod, "control"], "Right", lazy.layout.grow_right(),
        desc="Grow window to the right"),
    Key([mod, "control"], "Down", lazy.layout.grow_down(),
        desc="Grow window down"),
    Key([mod, "control"], "Up", lazy.layout.grow_up(), desc="Grow window up"),
    Key([mod], "n", lazy.layout.normalize(), desc="Reset all window sizes"),

    Key([mod, "mod1"], "Left", lazy.layout.grow(),
        desc="Grow window"),
    Key([mod, "mod1"], "Right", lazy.layout.shrink(),
        desc="Shrink window"),
    Key([mod, "mod1"], "Up", lazy.layout.maximize(),
        desc="Maximize window"),
    Key([mod, "mod1"], "Down", lazy.layout.normalize(),
        desc="Normalize window"),

    # Toggle between split and unsplit sides of stack.
    # Split = all windows displayed
    # Unsplit = 1 window displayed, like Max layout, but still with
    # multiple stack panes
    Key([mod, "shift"], "Return", lazy.layout.toggle_split(),
        desc="Toggle between split and unsplit sides of stack"),
    Key([mod], "Return", lazy.spawn(terminal), desc="Launch terminal"),

    # Toggle between different layouts as defined below
    Key([mod], "Tab", lazy.next_layout(), desc="Toggle between layouts"),
    Key([mod], "w", lazy.window.kill(), desc="Kill focused window"),
    Key([mod, "shift"], "f", lazy.window.toggle_floating(), desc="Toggle floating window mode"),
    Key([mod], "f", lazy.window.toggle_fullscreen(), desc="Toggle fullscreen window mode"),

    Key([mod, "control"], "r", lazy.restart(), desc="Restart Qtile"),
    Key([mod, "control"], "q", lazy.shutdown(), desc="Shutdown Qtile"),
    Key(["mod1", "control"], "r", lazy.spawn("reboot"), desc="Reboot host"),
    Key(["mod1", "control"], "p", lazy.spawn("poweroff"), desc="Shutdown host"),
    Key(["mod1", "control"], "s", lazy.spawn("systemctl suspend"), desc="Shutdown host"),
    Key([], "Print", lazy.spawn('scrot -s "%Y-%m-%d_%H%M%S_$wx$h_scrot.png" -e "mv $f ~/Screenshots/ -f"'), desc="Take Screnshot"),
    Key([mod], "s", lazy.spawn("switch-audio-source gui"), desc="Spawn a GUI for audio source selection"),
    Key([mod], "h", lazy.hide_show_bar("top")),
    Key([mod], "d", lazy.spawn("dpms"), desc="Spawn a GUI for DPMS setup"),
    Key([mod], "r", lazy.spawn("rofi -show combi"), desc="Spawn a command using a prompt widget"),
]

groups = [Group(i) for i in "123456789"]

for i in groups:
    keys.extend([
        # mod1 + letter of group = switch to group
        Key([mod], i.name, lazy.group[i.name].toscreen(),
            desc="Switch to group {}".format(i.name)),

        # mod1 + shift + letter of group = switch to & move focused window to group
        #Key([mod, "shift"], i.name, lazy.window.togroup(i.name, switch_group=True),
        #    desc="Switch to & move focused window to group {}".format(i.name)),
        # Or, use below if you prefer not to switch to that group.
        # # mod1 + shift + letter of group = move focused window to group
         Key([mod, "shift"], i.name, lazy.window.togroup(i.name), desc="move focused window to group {}".format(i.name)),
    ])

layout_theme = {"border_width": 2,
                "border_focus": "#1793d1",
                "margin": 2,
                "border_normal": "#312e30",
               }

layouts = [
    layout.Columns(**layout_theme, insert_position = 1, fair = True, margin_on_single = -1),
    layout.MonadTall(**layout_theme, single_border_width=0, single_margin=0, new_client_position='bottom'),
    ##layout.Bsp(**layout_theme),
    #layout.Max(**layout_theme),
    # layout.Floating(**layout_theme),
    # Try more layouts by unleashing below layouts.
    # layout.Stack(num_stacks=2),
    # layout.Bsp(),
    # layout.Matrix(),
    # layout.MonadTall(),
    # layout.MonadWide(),
    # layout.RatioTile(),
    # layout.Tile(),
    # layout.TreeTab(),
    # layout.VerticalTile(),
    # layout.Zoomy(),
]

colors = [
          '#dddfff',
          '#202124',
          '#444444',
          '#999999',
          '#ff6673',
          '#bd2c40',
          '#dddddd'
]


widget_defaults = dict(
    font='FiraCode Nerd Font',
    fontsize = 14,
    padding = 2,
    background = colors[1],
    foreground = colors[6],
)


extension_defaults = widget_defaults.copy()

screens = [
    Screen(
        top=bar.Bar(
            [
                widget.GroupBox(
                       margin_y = 3,
                       margin_x = 0,
                       padding_y = 5,
                       padding_x = 5,
                       borderwidth = 3,
                       rounded = False,
                       disable_drag = True,
                       highlight_method = "line",
                       active = colors[0],
                       background = colors[1],
                       highlight_color = colors[2],
                       inactive = colors[3],
                       invert_mouse_wheel = True,
                       ),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.CurrentLayoutIcon(scale = 0.7),
                #widget.WindowName(),
                #widget.TaskList(),
                widget.Spacer(),
                widget.Net(format=' {down}    {up}'),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.CPU(format='CPU: {load_percent}%'),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.Memory(format='RAM:{MemUsed: .0f} MB'),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.NvidiaSensors(format='GPU: {temp}°C', threshold=70),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.GenPollText(update_interval=1, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/network.sh")).decode(), mouse_callbacks = {'Button1': lambda: qtile.cmd_spawn(os.path.expanduser('~/.config/qtile/statusbar/network.sh ShowInfo')), 'Button3': lambda: qtile.cmd_spawn('nm-connection-editor')}),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.Clock(format=' %a %Y.%m.%d %H:%M', **widget_defaults),
                widget.Sep(padding=8, foreground=colors[3]),
                widget.GenPollText(update_interval=0.1, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/keyboard_layout.sh")).decode(), mouse_callbacks = {'Button1': lambda: qtile.cmd_spawn(os.path.expanduser('~/.config/qtile/statusbar/keyboard_layout.sh next'))}),
                widget.Systray(),
            ],
            size = 20,
        ),
    ),
]

# Drag floating layouts.
mouse = [
    Drag([mod], "Button1", lazy.window.set_position_floating(),
         start=lazy.window.get_position()),
    Drag([mod], "Button3", lazy.window.set_size_floating(),
         start=lazy.window.get_size()),
    Click([mod], "Button2", lazy.window.bring_to_front())
]

dgroups_key_binder = None
dgroups_app_rules = []  # type: List
main = None  # WARNING: this is deprecated and will be removed soon
follow_mouse_focus = True
bring_front_click = True
cursor_warp = False
floating_layout = layout.Floating(float_rules=[
    # Run the utility of `xprop` to see the wm class and name of an X client.
    *layout.Floating.default_float_rules,
    Match(wm_class='confirmreset'),  # gitk
    Match(wm_class='makebranch'),  # gitk
    Match(wm_class='maketag'),  # gitk
    Match(wm_class='ssh-askpass'),  # ssh-askpass
    Match(title='branchdialog'),  # gitk
    Match(title='pinentry'),  # GPG key password entry
])
auto_fullscreen = True
#focus_on_window_activation = "focus"
focus_on_window_activation = "smart"
auto_minimize = True

@hook.subscribe.startup_once
def autostart():
    home = os.path.expanduser('~/.config/qtile/autostart.sh')
    subprocess.call([home])

# XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
# string besides java UI toolkits; you can see several discussions on the
# mailing lists, GitHub issues, and other WM documentation that suggest setting
# this string if your java app doesn't work correctly. We may as well just lie
# and say that we're a working one by default.
#
# We choose LG3D to maximize irony: it is a 3D non-reparenting WM written in
# java that happens to be on java's whitelist.
wmname = "LG3D"
```



https://user-images.githubusercontent.com/35117097/147859940-c39beb62-cdc4-451a-99b5-33a908755f89.mp4


[qtile.log](https://github.com/qtile/qtile/files/7798212/qtile.log)




Discussion:
Thanks for reporting and the video.

I'll take a proper look later but one question for you. If you try running mpv manually with those same options and file, does a window open for you? I've tried it on my system and it does.
@elParaguayo yes, I've tried running the same command manually from terminal and it successfully launches mpv window in floating window mode

Manual run (which succeeds and launches floating window):
```
[farid@unknown ~]$ mpv --ontop --really-quiet --force-window --loop=no ~/Videos/simplescreenrecorder-2022-01-02_00.39.31.mp4
```

Play-with-mpv:
```
[farid@unknown ~]$ ps -ef | grep -i mpv
farid        841       1  0 13:08 tty1     00:00:00 /usr/bin/python /usr/bin/play-with-mpv
farid       3982    3902  1 13:09 tty1     00:00:00 mpv --ontop --really-quiet --loop=no --force-window http://localhost:8888/
farid       4424    4007  0 13:09 pts/0    00:00:00 grep --color -i mpv
```
Webtorrent:
```
[farid@unknown ~]$ ps -ef | grep -i mpv
farid        841       1  0 13:08 tty1     00:00:00 /usr/bin/python /usr/bin/play-with-mpv
farid       8827    8754  7 13:10 pts/1    00:00:00 mpv --really-quiet --loop=no --ontop http://localhost:8000/0/%D0%9D%D0%B5%20%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B8%D1%82%D0%B5%20%D0%BD%D0%B0%D0%B2%D0%B5%D1%80%D1%85_2021_WEB-DLRip.avi
farid       9457    4007  0 13:10 pts/0    00:00:00 grep --color -i mpv

OK. Is mpv listed in qtile's windows map when it's hidden? `qtile cmd-obj -o cmd -f eval -a self.windows_map`
I've found that the issue happen with not every torrent link. Some of them launch mpv window successfully (that's weird...). Both files are *.avi (just in case)

**Success case:**
```
[farid@unknown ~]$ webtorrent "magnet:?xt=urn:btih:CBDC4180866502F226D4D410B0FE0053B0A101F1&tr=http%3A%2F%2Fbt2.t-ru.org%2Fann%3Fmagnet&dn=%D0%94%D1%8E%D0%BD%D0%B0%20%2F%20Dune%20(%D0%94%D0%B5%D0%BD%D0%B8%20%D0%92%D0%B8%D0%BB%D1%8C%D0%BD%D1%91%D0%B2%20%2F%20Denis%20Villeneuve)%20%5B2021%2C%20%D0%A1%D0%A8%D0%90%2C%20%D1%84%D0%B0%D0%BD%D1%82%D0%B0%D1%81%D1%82%D0%B8%D0%BA%D0%B0%2C%20%D0%B1%D0%BE%D0%B5%D0%B2%D0%B8%D0%BA%2C%20%D0%B4%D1%80%D0%B0%D0%BC%D0%B0%2C%20%D0%BF%D1%80%D0%B8%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%2C%20WEB-DLRip%5D%20Dub" --mpv
```
Output of command:
```
[farid@unknown ~]$ qtile cmd-obj -o cmd -f eval -a self.windows_map
(True,
 '{4194592: Internal(wid=4194592), 4194596: <libqtile.widget.systray.Systray '
 'object at 0x7fdf04c1fbe0>, 6291458: <libqtile.widget.systray.Icon object at '
 '0x7fdf05040c10>, 8388610: <libqtile.widget.systray.Icon object at '
 "0x7fdf04acd9f0>, 29360130: Window(name='farid@unknown:~', wid=29360130), "
 '44040198: <libqtile.widget.systray.Icon object at 0x7fdefc5b7400>, 41943042: '
 "Window(name='farid@unknown:~', wid=41943042), 14680066: "
 "Window(name='Dune_2021_WEB-DLRip_by_Dalemake.avi - mpv', wid=14680066)}")
```

**Failure case:**

```
[farid@unknown ~]$ webtorrent "magnet:?xt=urn:btih:9B484CD18CE388176D6BF23591D29075B2F430ED&tr=http%3A%2F%2Fbt2.t-ru.org%2Fann%3Fmagnet&dn=%D0%9C%D0%B0%D0%B6%D0%BE%D1%80%D1%8B%20%D0%BD%D0%B0%20%D0%BC%D0%B5%D0%BB%D0%B8%20%2F%20Spoiled%20Brats%20%2F%20Pourris%20g%C3%A2t%C3%A9s%20(%D0%9D%D0%B8%D0%BA%D0%BE%D0%BB%D1%8F%20%D0%9A%D1%8E%D1%88%20%2F%20Nicolas%20Cuche)%20%5B2021%2C%20%D0%A4%D1%80%D0%B0%D0%BD%D1%86%D0%B8%D1%8F%2C%20%D0%BA%D0%BE%D0%BC%D0%B5%D0%B4%D0%B8%D1%8F%2C%20WEB-DLRip%5D%20Dub%20(iTunes)" --mpv
```
Output of command:
```
[farid@unknown ~]$ qtile cmd-obj -o cmd -f eval -a self.windows_map
(True,
 '{4194592: Internal(wid=4194592), 4194596: <libqtile.widget.systray.Systray '
 'object at 0x7fdf04c1fbe0>, 6291458: <libqtile.widget.systray.Icon object at '
 '0x7fdf05040c10>, 8388610: <libqtile.widget.systray.Icon object at '
 "0x7fdf04acd9f0>, 29360130: Window(name='farid@unknown:~', wid=29360130), "
 '44040198: <libqtile.widget.systray.Icon object at 0x7fdefc5b7400>, 41943042: '
 "Window(name='farid@unknown:~', wid=41943042)}")
```
Ah. Well I wonder if it's a video issue then. If there's only audio mpv doesn't spawn a new window

Having said that, mpv does create a window if started with `--force-window` which is what `play-with-mpv` seems to be using.

I wonder if that error in your video (regarding the window name) could be stopping the window from appearing.
> I wonder if that error in your video (regarding the window name) could be stopping the window from appearing.

Yeah. This is it.

I tested this by forcing mpv window to raise an error. The process is still running so I can hear audio but I get no window.

Looks like we need some better error handling.

I'll post a fix for this.


Resolving patch:
diff --git a/libqtile/backend/x11/window.py b/libqtile/backend/x11/window.py
--- a/libqtile/backend/x11/window.py
+++ b/libqtile/backend/x11/window.py
@@ -126,7 +126,10 @@ def _property_string(self, r):
         return r.value.to_string()
 
     def _property_utf8(self, r):
-        return r.value.to_utf8()
+        try:
+            return r.value.to_utf8()
+        except UnicodeDecodeError:
+            return r.value.to_string()
 
     def send_event(self, synthevent, mask=EventMask.NoEvent):
         self.conn.conn.core.SendEvent(False, self.wid, mask, synthevent.pack())


Test patch: