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

Xwayland apps on wayland #5101

Closed
2 tasks done
Gurjaka opened this issue Nov 26, 2024 · 6 comments · Fixed by #5102
Closed
2 tasks done

Xwayland apps on wayland #5101

Gurjaka opened this issue Nov 26, 2024 · 6 comments · Fixed by #5102

Comments

@Gurjaka
Copy link
Contributor

Gurjaka commented Nov 26, 2024

Issue description

image
First I discovered this issue when turning on Steam, later discovered that none Xwayland app works, even after running in terminal I get no output about error. This is all I got:

steam                                                                               
steam.sh[9848]: Running Steam on nixos 25.05 64-bit
steam.sh[9848]: STEAM_RUNTIME is enabled automatically
setup.sh[9896]: Steam runtime environment up-to-date!
steam.sh[9848]: Steam client's requirements are satisfied
CProcessEnvironmentManager is ready, 6 preallocated environment variables.
[2024-11-26 21:52:42] Startup - updater built Nov 12 2024 17:09:08
[2024-11-26 21:52:42] Startup - Steam Client launched with: '/home/gurami/.local/share/Steam/ubuntu12_32/steam' '-srt-logger-opened'
11/26 21:52:42 minidumps folder is set to /tmp/dumps
11/26 21:52:42 Init: Installing breakpad exception handler for appid(steam)/version(1731433018)/tid(9958)
src/steamexe/updateui_xwin.cpp (341) : Could not open connection to X
src/steamexe/updateui_xwin.cpp (341) : Could not open connection to X
11/26 21:52:42 Init: Installing breakpad exception handler for appid(steam)/version(1731433018)/tid(9958)
Error: Check your DISPLAY environment variable and make sure that you have enabled X.
If you are running remotely, make sure that you have a remote connection which will allow an X connection.

For more information visit https://support.steampowered.com/kb_article.php?ref=4050-WOJB-0608
Using host zenity for message
assert_20241126215242_4.dmp[9963]: Uploading dump (out-of-process)
/tmp/dumps/assert_20241126215242_4.dmp
src/steamexe/main.cpp (1328) : failed to initialize update status ui, or create initial window
src/steamexe/main.cpp (1328) : failed to initialize update status ui, or create initial window
11/26 21:52:42 Init: Installing breakpad exception handler for appid(steam)/version(1731433018)/tid(9958)
assert_20241126215242_7.dmp[9967]: Uploading dump (out-of-process)
/tmp/dumps/assert_20241126215242_7.dmp
~ 
❯ assert_20241126215242_7.dmp[9967]: Finished uploading minidump (out-of-process): success = yes
assert_20241126215242_7.dmp[9967]: response: CrashID=bp-d24466c0-21b4-45ce-a0d9-3cf602241126
assert_20241126215242_7.dmp[9967]: file ''/tmp/dumps/assert_20241126215242_7.dmp'', upload yes: ''CrashID=bp-d24466c0-21b4-45ce-a0d9-3cf602241126''
assert_20241126215242_4.dmp[9963]: Finished uploading minidump (out-of-process): success = yes
assert_20241126215242_4.dmp[9963]: response: CrashID=bp-38149775-6e7b-4400-a972-a50db2241126
assert_20241126215242_4.dmp[9963]: file ''/tmp/dumps/assert_20241126215242_4.dmp'', upload yes: ''CrashID=bp-38149775-6e7b-4400-a972-a50db2241126''

Version

0.29.0+1dacffb.flake

Backend

Wayland (experimental)

Config

import os
import subprocess
import socket
from libqtile import hook, qtile
from libqtile import bar, layout, qtile, widget
from libqtile.config import Click, Drag, Group, ScratchPad, DropDown, Key, Match, Screen
from libqtile.lazy import lazy
# from libqtile.utils import guess_terminal
from qtile_extras import widget
from libqtile.backend.wayland import InputConfig
from qtile_extras.widget.decorations import BorderDecoration
from libqtile.config import Key, KeyChord
from theme import colors

# Set backend
if qtile.core.name == "wayland":
    os.environ["XDG_SESSION_DESKTOP"] = "qtile:wlroots"
    os.environ["XDG_CURRENT_DESKTOP"] = "qtile:wlroots"

# Startup
@hook.subscribe.startup_once
def autostart():
    home = os.path.expanduser('~/Dotfiles/nixos/modules/qtile/autostart.sh')
    subprocess.Popen([home])

# OnReload
def onreload():
    reload = os.path.expanduser('~/Dotfiles/nixos/modules/qtile/reload.sh')
    subprocess.Popen([reload])

onreload()

# Variables

mod = "mod4"
host = socket.gethostname()
terminal = "kitty" 
browser = "firefox"
launcher = "rofi -show drun"
fileManager = "thunar"
editor = "code"
ntCenter = "swaync-client -t -sw"

keys = [
    # A list of available commands that can be bound to keys can be found
    # at https://docs.qtile.org/en/latest/manual/config/lazy.html
    # Switch between windows
    Key([mod], "h", lazy.layout.left(), desc="Move focus to left"),
    Key([mod], "l", lazy.layout.right(), desc="Move focus to right"),
    Key([mod], "j", lazy.layout.down(), desc="Move focus down"),
    Key([mod], "k", 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"], "h", lazy.layout.shuffle_left(), desc="Move window to the left"),
    Key([mod, "shift"], "l", lazy.layout.shuffle_right(), desc="Move window to the right"),
    Key([mod, "shift"], "j", lazy.layout.shuffle_down(), desc="Move window down"),
    Key([mod, "shift"], "k", 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"], "j", lazy.layout.shrink(), desc="Shrink window"),
    Key([mod, "control"], "k", lazy.layout.grow(), desc="Grow window"),
    Key([mod], "n", lazy.layout.normalize(), desc="Reset all window sizes"),
    # 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], "z", lazy.next_layout(), desc="Toggle between layouts"),
    Key([mod, "Shift"], "q", lazy.window.kill(), desc="Kill focused window"),
    Key([mod], "f", lazy.window.toggle_fullscreen(), desc="Toggle fullscreen on the focused window"),
    Key([mod], "t", lazy.window.toggle_floating(), desc="Toggle floating on the focused window"),
    Key([mod, "control"], "r", lazy.reload_config(), desc="Reload the config"),
    Key([mod, "control"], "q", lazy.shutdown(), desc="Shutdown Qtile"),
    Key([mod], "d", lazy.spawn(launcher), desc="Exec app launcher"),
    Key([mod], "e", lazy.spawn(fileManager), desc="Exec File manager"),
    Key([mod], "b", lazy.spawn(browser), desc="Exec browser"),
    Key([mod], "c", lazy.spawn(editor), desc="Exec editor"),
    Key([mod], "Tab", lazy.spawn(ntCenter), desc="Exec notification center"),
    Key([mod, "Shift"], "s", lazy.spawn('grim -g "$(slurp -d)" - | wl-copy', shell=True)),
    Key(["Shift"], "Tab", lazy.widget["keyboardlayout"].next_keyboard()),
    Key([], "XF86AudioRaiseVolume", lazy.spawn("wpctl set-volume -l 1.4 @DEFAULT_AUDIO_SINK@ 5%+")),
    Key([], "XF86AudioLowerVolume", lazy.spawn("wpctl set-volume -l 1.4 @DEFAULT_AUDIO_SINK@ 5%-")),
    Key([], "XF86AudioMute", lazy.spawn("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")),
    Key([], "XF86MonBrightnessDown", lazy.spawn("brightnessctl set 5%-")),
    Key([], "XF86MonBrightnessUp", lazy.spawn("brightnessctl set +5%")),
    Key([], "XF86AudioPlay", lazy.spawn('playerctl --player=spotify,%any play-pause')),
    Key([], "XF86AudioPrev", lazy.spawn('playerctl --player=spotify,%any previous')),
    Key([], "XF86AudioNext", lazy.spawn('playerctl --player=spotify,%any next')),

    KeyChord([mod], "i", [
        Key([mod], "i", lazy.ungrab_all_chords())],
        mode=True,
        name='Vm Mode',
    ),
]

# Add key bindings to switch VTs in Wayland.
# We can't check qtile.core.name in default config as it is loaded before qtile is started
# We therefore defer the check until the key binding is run by using .when(func=...)
for vt in range(1, 8):
    keys.append(
        Key(
            ["control", "mod1"],
            f"f{vt}",
            lazy.core.change_vt(vt).when(func=lambda: qtile.core.name == "wayland"),
            desc=f"Switch to VT{vt}",
        )
    )


# groups = [Group(i) for i in "123456789"]
groups = [
    ScratchPad("p", [
        DropDown("Music", "spotify", opacity=1, height=0.5, on_focus_lost_hide=False),
        DropDown("Term", terminal, opacity=1, height=0.5, on_focus_lost_hide=False),
    ]),
    Group("1", label=" "),
    Group("2", label="󰈹 ", matches=[Match(wm_class="Navigator"), Match(wm_class="vivaldi-stable")], spawn=browser),
    Group("3", label=" "),
    Group("4", label=" ", matches=[Match(wm_class="obsidian")]),
    Group("5", label=" "),
    Group("6", label="󰑈 "),
    Group("7", label=" "),
    Group("8", label="󰧮 "),
    Group("9", label=" ", matches=[Match(wm_class="vesktop")], spawn="vesktop"),
    Group("0", label="󰮂 ", matches=[Match(wm_class="steam")]),
]

for i in groups:
    keys.extend(
        [
            # mod1 + group number = switch to group
            Key(
                [mod],
                i.name,
                lazy.group[i.name].toscreen(),
                desc="Switch to group {}".format(i.name),
            ),
            # mod1 + shift + group number = 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),
            ),
            Key([mod], "s", lazy.group['p'].dropdown_toggle('Music')),
            Key([mod], "a", lazy.group['p'].dropdown_toggle('Term')),


            # Or, use below if you prefer not to switch to that group.
            # # mod1 + shift + group number = move focused window to group
            # Key([mod, "shift"], i.name, lazy.window.togroup(i.name),
            #     desc="move focused window to group {}".format(i.name)),
        ]
    )

if colors["theme"] == "Everforest":
    layout_theme = {
        "border_focus": "#A7C080",
        "border_normal": "#48584E",
        "border_width": 2,
        "margin": 5,
    }
elif colors["theme"] == "Nord":
    layout_theme = {
    "border_focus": "#5E81AC",
    "border_normal": "#4C566A",
    "border_width": 2,
    "margin": 5,
    }

layouts = [
    # layout.Columns(**layout_theme),
    #layout.Max(),
    # Try more layouts by unleashing below layouts.
    # layout.Stack(num_stacks=2),
    # layout.Bsp(),
    # layout.Matrix(),
    layout.MonadTall(**layout_theme),
    # layout.MonadWide(),
    # layout.RatioTile(),
    # layout.Tile(),
    # layout.TreeTab(),
    # layout.VerticalTile(),
    # layout.Zoomy(),
]

widget_defaults = dict(
    font="Fira Code Nerd Font Medium",
    fontsize=14,
    padding=3,
)
extension_defaults = widget_defaults.copy()

widget_list = [
    widget.TextBox(
        fmt = " ",
        padding = None,
        fontsize = 20,
        foreground = colors["base12"],
        mouse_callbacks = {"Button1": lambda: qtile.spawn(f"{browser} github.com/gurjaka")},
    ),

    widget.Clock(
        foreground = colors["base14"],
        format=" %a,%b,%d -  %H:%M",
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base14"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.GroupBox(
        fontsize = 20,
        active = colors["base09"],
        inactive = colors["base03"],
        block_highlight_text_color = "#FFFFFF",
        highlight_method = "line",
        highlight_color = colors["base01"],
        this_current_screen_border = colors["base09"],
        urgent_alert_method = "line",
        urgent_border = colors["base11"],
        disable_drag = True,
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base03"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.TextBox(
        foreground = colors["base05"],
        text = '|',
        padding = 2,
    ),

    widget.CurrentLayoutIcon(
        foreground = colors["base05"],
        padding = 4,
        scale = 0.6
    ),

    widget.CurrentLayout(
        foreground = colors["base05"],
        padding = 5,
    ),

    widget.TextBox(
        foreground = colors["base05"],
        text = '|',
        padding = 2,
    ),

    widget.WindowName(
        foreground = colors["base13"],
        #format = "{}",
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base13"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.Chord(
        chords_colors={
            "launch": (colors["base01"], colors["base12"]),
        },
        foreground = colors["base12"],
        name_transform=lambda name: name.upper(),
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base12"],
                padding_x = None,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    # NB Systray is incompatible with Wayland, consider using StatusNotifier instead
    # widget.StatusNotifier(),
    widget.StatusNotifier(
        background = colors["base02"],
        padding = 5,
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base11"],
                padding_x = None,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.Battery(
        foreground = colors["base14"],
        charge_char = "󰂄",
        discharge_char = "󰁿",
        empty_char = "󰂎",
        format = '{char} {percent:2.0%} {hour:d}:{min:02d}',
        decorations=[
            BorderDecoration(
                border_width = [0, 0, 2, 0],
                colour = colors["base14"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),


    widget.GenPollText(
        update_interval = 300,
        func = lambda: subprocess.check_output("printf $(uname -r)", shell=True, text=True),
        foreground = colors["base12"],
        fmt = ' {}',
        decorations=[
            BorderDecoration(
                border_width = [0, 0, 2, 0],
                colour = colors["base12"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.CPU(
        foreground = colors["base09"],
        format = '󰍹 Cpu:{load_percent}%',
        mouse_callbacks = {"Button1": lambda: qtile.spawn(f"{terminal} btop")},
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base09"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.DF(
        update_interval = 60,
        foreground = colors["base14"],
        mouse_callbacks = {'Button1': lambda: qtile.spawn(f"{terminal} df")},
        partition = '/',
        #format = '[{p}] {uf}{m} ({r:.0f}%)',
        format = '🖴 Disk:{uf}{m}',
        visible_on_warn = False,
        decorations=[
            BorderDecoration(
                border_width = [0, 0, 2, 0],
                colour = colors["base14"],
                padding_x = 3,
                padding_y = None,
            )
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.Memory(
        foreground = colors["base15"],
        format = ' Mem:{NotAvailable:.0f}{mm}',
        mouse_callbacks = {"Button1": lambda: qtile.spawn(f"{terminal} btop")},
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base15"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.PulseVolume(
        foreground = colors["base13"],
        fmt = " :{}",
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base13"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),

    widget.KeyboardLayout(
        foreground = colors["base08"],
        configured_keyboards = ["us dvorak", "ge"],
        display_map = {"us dvorak": "US", "ge": "GE"},
        option = "caps:escape",
        fmt = " Kbd:{}",
        decorations = [
            BorderDecoration(
                border_width = [0,0,2,0],
                colour = colors["base08"],
                padding_x = 3,
                padding_y = None,
            ),
        ],
    ),

    widget.Spacer(
        length = 2,
    ),
]

if host != "laptop":
    del widget_list[15]

screens = [
    Screen(
        wallpaper = f"~/Dotfiles/wallpapers/{colors["theme"]}/main.jpg",
        wallpaper_mode = "fill",
        top=bar.Bar(
            widget_list,
            24,
            background = colors["base00"],
        ),
    ),
]

# 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
follow_mouse_focus = True
bring_front_click = True
floats_kept_above = True
cursor_warp = False
floating_layout = layout.Floating(
    **layout_theme,
    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 = "smart"
reconfigure_screens = True

# If things like steam games want to auto-minimize themselves when losing
# focus, should we respect this or not?
auto_minimize = True

# When using the Wayland backend, this can be used to configure input devices.
wl_input_rules = {
    "type:pointer": InputConfig(
        accel_profile = "flat",
    ),
    # "type:keyboard": InputConfig(
    #     # kb_layout = "us, ge",
    #     # kb_options = "grp:alt_shift_toggle, caps:escape",
    # ),
}

wl_xcursor_theme = "Breeze_Light"
wl_xcursor_size = 24

# 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 = "QTILE"

Logs

qtile.log

Required

  • I have searched past issues to see if this bug has already been reported, and it hasn't been.
  • I understand that people give their precious time for free, and thus I've done my very best to make this problem as easy as possible to investigate.
@elParaguayo
Copy link
Member

@tych0 I think this is caused by removing the double fork.

I get the following error waitpid for Xwayland fork failed: No child processes which, from a 2 second search, seems to be caused by the SIGCHLD handler (see djpohly/dwl#177 (comment) and fix in djpohly/dwl#212).

@tych0
Copy link
Member

tych0 commented Nov 27, 2024

Ah man, that is... unfortunate.

I don't think it's double fork, but our SIGCHLD handler, based on: https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/871646d22522141c45db2c0bfa1528d595bb69df the wlroots library wants to fork (which seems slightly impolite) an X server. When its waitpid() races with our SIGCHLD handler, it fails.

I think if wlroots really wants to do this, they should mask off the SIGCHLD handler while they're doing their own thing. Probably we should send them a patch, but in the meantime, we can manage it ourselves. Does: tych0@dbe76e9 work?

If not, we'll have to do something like what the patch you linked did (or maybe pywlroots should, like I mention flacjacket/pywlroots#207 ?). But really wlroots proper should do that. I don't have enough wayland-fu to be building my own wayland stuff to test patches, though.

@elParaguayo
Copy link
Member

Your patch works for me.

Would be good to get @jwijenbergh thoughts here too as he's more familiar with the wlroots stuff.

tych0 added a commit to tych0/qtile that referenced this issue Nov 27, 2024
the wayland backend via wlroots wants to double fork an X server for
XWayland, and expects to waitpid() on the middle fork to make sure things
were successful. our SIGCHLD handler races with this process and
interferes, causing things like:

    waitpid for Xwayland fork failed: No child processes

let's delay installing our SIGCHLD handler until after XWayland is
initialized to hopefully dodge this error.

Fixes qtile#5101

Signed-off-by: Tycho Andersen <[email protected]>
@tych0
Copy link
Member

tych0 commented Nov 27, 2024

Ok, thanks for confirming. Happy to wait for @jwijenbergh's review, although I am reasonably confident that's the right fix given the implementations you linked to.

I will have a think about how wlroots can really handle this. it feels like double posix_spawn() is pretty heavyweight for them, but also forking in library code means many downstream users have to do tap dancing like this to get things to work correctly, which is unfortunate.

tych0 added a commit to tych0/qtile that referenced this issue Nov 27, 2024
the wayland backend via wlroots wants to double fork an X server for
XWayland, and expects to waitpid() on the middle fork to make sure things
were successful. our SIGCHLD handler races with this process and
interferes, causing things like:

    waitpid for Xwayland fork failed: No child processes

let's delay installing our SIGCHLD handler until after XWayland is
initialized to hopefully dodge this error.

Fixes qtile#5101

Signed-off-by: Tycho Andersen <[email protected]>
@tych0
Copy link
Member

tych0 commented Nov 27, 2024

It turns out they already do some pipe-notify thing for xwayland, and so this waitpid() is just about zombie prevention, so I think we can safely ignore it. I sent them a patch: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4926

If they merge it, then once we move to that wlroots version, we can revert my fix.

@elParaguayo
Copy link
Member

I've merged your fix for now as it solves a real issue for some users. We can revert and implement a different fix if necessary later.

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

Successfully merging a pull request may close this issue.

3 participants