Elvyria / Mixxc

Minimalistic volume mixer.
MIT License
30 stars 0 forks source link

odd focus and border behaviour when running in xmonad - xubuntu #4

Open dpnova opened 5 months ago

dpnova commented 5 months ago

Needed to sudo apt install libgtk-4-dev

Then installed with: cargo install mixxc --locked --features X11

Window pops up ok:

image

After some time it crashes, and I think if I change focus to another window it crashes instantly - here's the logs:

image

I'm not rust master but very experienced developer, so not afraid to get my hands dirty if required (might need a hand getting dev env set up :sweat_smile: )

Elvyria commented 5 months ago

Ehh... Window probably doesn't crash. By default it automatically closes, when it looses focus together with mouse cursor, because under Wayland WM's don't send close request to layer shells like they do for regular windows. You can change this behavior with --keep flag, it's documented in README.md. I understand why you would think so, because of these GtkErrors, but you can safely ignore them, they appear only under X11 and only because i did some "hacks" for X11 to prevent window flickering on resizes.

Not sure why window is so big tho, default sizes even if you didn't specify them through -w and -h shouldn't be as big.

Elvyria commented 5 months ago

Thanks for the report by the way, i tested Mixxc under X11 only once for I3 and AwesomeWM when i was writing X11 feature. I'll test xmonad and see if there's a need for more atomics for X11, because this window size is not normal and WM shouldn't allow you to resize it manually.

Elvyria commented 5 months ago

So, the issue with size is related to xmonad's behavior towards windows with _NET_WM_WINDOW_TYPE set to _NET_WM_WINDOW_TYPE_UTILITY, both AwesomeWM and I3 treat _NET_WM_WINDOW_TYPE_UTILITY as floating windows with a free position.

You'll have change window rules inside your ${XDG_CONFIG_HOME:-$HOME/.config}/xmonad/xmonad.hs to something like this:

myManageHook = fullscreenManageHook <+> manageDocks <+> composeAll
    [ className =? "mixxc" --> doFloat, hasBorder False ]

or globally (affects PnP in Firefox)

import XMonad.Hooks.ManageHelpers ( isInProperty )
myManageHook = fullscreenManageHook <+> manageDocks <+> composeAll
    [ isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_UTILITY" --> doFloat ]
dpnova commented 5 months ago

Ehh... Window probably doesn't crash. By default it automatically closes, when it looses focus together with mouse cursor, because under Wayland WM's don't send close request to layer shells like they do for regular windows. You can change this behavior with --keep flag, it's documented in README.md. I understand why you would think so, because of these GtkErrors, but you can safely ignore them, they appear only under X11 and only because i did some "hacks" for X11 to prevent window flickering on resizes.

Not sure why window is so big tho, default sizes even if you didn't specify them through -w and -h shouldn't be as big.

Big window is the xmonad layout I'm using. Opens new windows as the larger middle one :)

Sorry didn't occur to me that it'd auto shut! I will totally add a hook for it. Sorry I rushed this report before work yesterday!

dpnova commented 5 months ago

className =? "mixxc" --> (doFloat <+> hasBorder False)

this did the trick for me, thanks!

I did notice some other weird behaviour, but I suspect that's a result of some weird interplay with layer shells. I've attached a video showing the border and focus behaviour. First I start mixxc which xmonad attempts to control in the current layout, then I use alt-click to drag it out of the layout manager, which makes a huge window :D - then I alt-right click to resize, which seems to trigger the default expect sizing.

I'm used to that stuff as an xmonad user. I think I'd have to stop using focus follows mouse to deal with the other wierdness you'll see in the vid.

https://github.com/Elvyria/Mixxc/assets/229943/4081b08f-9125-4bd3-be42-cd064ddaf39a

dpnova commented 5 months ago

I tested with my mouse follows focus hack disabled, commenting out this:

logHook = updatePointer (0.5, 0.5) (0, 0),

and didn't get the cursor issues, but still have the flickering border - which I guess may be a focus issue from the gtk hacks you mentioned too now that I think about it. 🤔

Either way, I love the widget, I think there's huge potential to combine with eww.

Cheers! (happy to test/hack btw)

dpnova commented 5 months ago

Just noticed that I don't get any of the flickering while the window is "managed" by xmonad - ie I haven't pulled it out of the layout manager. 🤔

noticed because I pulled the code and built locally to have a play with the x protocol

Elvyria commented 5 months ago

That's some wild stuff. Looks like WM doesn't accept that window was resized and treats it as a fullscreen? one and it just constantly switches focus as 2 windows are in the same place.

The only thing that i noticed on X11 was a couple of frames of the window in the center of the screen, while it was sending resize+reposition request, but that's cosmetic and should be fixable.

Don't know if this will do anything for this situation, but you could try a different rendering backend, I've seen people mentioning that they have graphical glitches with the modern default one.

GSK_RENDERER=cairo mixxc

You can list everything that's available to you with.

GSK_RENDERER=help mixxc

If you want to try different things yourself you could start gutting out x.rs, maybe some _NET_WM_STATE_ flag is responsible.

I will do some more test with the default xmonad config that i have, but maybe it's something specific with your config and could you post it?

(if you didn't compile with Wayland feature you have no "layer-shell" in your binary to worry about, it's a Wayland specific thing)

Elvyria commented 5 months ago

I absolutely lied about "default" config btw, i got some random one while trying to get xmonad to my distro :p

This is the one I'm testing with, maybe you can reference some differences that will help you: https://github.com/Axarva/dotfiles-2.0/blob/main/xmonad/xmonad.hs

dpnova commented 4 months ago

Seems people have noticed something similar with Reaper: https://www.reddit.com/r/xmonad/comments/18rck0e/reaper_daw_and_xmonad/

EDIT: actually nm - removing ewmh didn't resolve the underlying issue for me

In the mean time when i use mixxc as an xmonad managed window I don't have any problems :) that's my workaround for now.

Elvyria commented 4 months ago

That could be xmonad <18.0 behavior.... https://xmonad.org/news/2024/02/03/xmonad-0-18-0.html https://packages.ubuntu.com/search?keywords=xmonad&searchon=names&suite=all&section=all

XMonad.Operations.floatLocation now applies size hints. This means windows will snap to these hints as soon as they’re floated (mouse move, keybinding). Previously that only happened on mouse resize.

dpnova commented 4 months ago

That could be xmonad <18.0 behavior.... https://xmonad.org/news/2024/02/03/xmonad-0-18-0.html

interesting - I'll try upgrading from 0.17 and see how I go.

EDIT: new xmonad indeed fixed the initial layout - window goes directly to anchor point. I end up putting it back into the layout manager though because of the border flickering and focus issues.

Elvyria commented 4 months ago

Can we close this then? Without a way to reproduce the remaining focus issue (not on xmonad, not on i3 and not on AwesomeWM), i have no idea how to help you further and i don't know if it's related to Mixxc or your config.

dpnova commented 4 months ago

Can we close this then? Without a way to reproduce the remaining focus issue (not on xmonad, not on i3 and not on AwesomeWM), i have no idea how to help you further and i don't know if it's related to Mixxc or your config.

Fair point - I'll try create a minimal config to reproduce, or at least isolate the problem to something xmonad specific so we can close this.

btw - i started adding unit tests in my local clone, would you be open to PRs with tests?

Elvyria commented 4 months ago

No, i don't need tests atm, there are much more important things to implement.

dpnova commented 4 months ago

No, i don't need tests atm, there are much more important things to implement.

Fair enough. I'll likely still do it for the rust learning. I'll keep an eye out for things you may accept contributions for.

dpnova commented 4 months ago
import XMonad

import XMonad.Util.EZConfig

main :: IO ()
main = xmonad def

I can reproduce the behaviour with this xmonad config. Not sure if it matters but I'm using nvidia proprietary drivers in prime mode.

image

I tried with cairo backend and have the same issues. From what I can see, the window is taking focus all the time. With this in mind, I ran xev while observing the behaviour, my log was filled with this whenever I was moving my mouse inside the mixxc window and no music is playing:

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390186855, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390186868, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390186903, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187028, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187057, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187104, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187162, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187180, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187198, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187233, state PropertyNewValue

PropertyNotify event, serial 30, synthetic NO, window 0xc400001,
    atom 0x1d8 (WM_STATE), time 390187324, state PropertyNewValue

and when I'm doing something in any other window while music is playing (ie the level bar is animated in the mixxc window):


MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482423, (843,427), root:(1101,592),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482424, (835,425), root:(1093,590),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482425, (829,425), root:(1087,590),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482426, (823,423), root:(1081,588),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482427, (817,423), root:(1075,588),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482428, (811,421), root:(1069,586),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482429, (807,421), root:(1065,586),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482430, (799,419), root:(1057,584),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482431, (795,417), root:(1053,582),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482432, (791,415), root:(1049,580),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482433, (787,413), root:(1045,578),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482434, (785,413), root:(1043,578),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482435, (781,411), root:(1039,576),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482436, (779,419), root:(1037,584),
    state 0x0, is_hint 0, same_screen YES

MotionNotify event, serial 33, synthetic NO, window 0xc000001,
    root 0x973, subw 0x0, time 390482437, (783,421), root:(1041,586),
    state 0x0, is_hint 0, same_screen YES

note - if you'd rather mark this as a gotcha of using xmonad and just refer anyone with issues to this thread - I'm happy to close the ticket.

Elvyria commented 4 months ago

After countless attempts and config removals, i was able to reproduce some form of border shimmering, but no focus problems.

import XMonad

import XMonad.Util.SpawnOnce ( spawnOnce )
import XMonad.Util.EZConfig

main :: IO ()
main = xmonad def { 
    manageHook = composeAll [ className =? "mixxc" --> doFloat ], 
    startupHook = do spawnOnce "kitty"
}

So the only thing that affected the behavior was my X11 Nvidia config at /etc/X11/xorg.conf.d/10-nvidia.conf

Section "Device"
    Identifier     "Nvidia Card"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
EndSection

Section "Screen"
    Identifier     "Default Screen"
    Device         "Nvidia Card"
    DefaultDepth    24
    Option         "Coolbits" "28"
    Option         "metamodes" "nvidia-auto-select +0+0 {ForceCompositionPipeline=On}"

    # Legacy option for VSYNC
    Option         "UseNvKmsCompositionPipeline" "false"
EndSection

Removing this option leads to shimmering on window's borders:

Option "metamodes" "nvidia-auto-select +0+0 {ForceCompositionPipeline=On}"

Using picom with glx backend and without composition pipeline also solved the issue $XDG_CONFIG_HOME/picom.conf:

animations: true;
animation-window-mass = 0.2;
animation-dampening = 15;
animation-clamping = true;
animation-for-open-window = "zoom";
animation-for-unmap-window = "zoom";
animation-for-transient-window = "slide-down";
animation-for-workspace-switch-in = "slide-down";
animation-for-workspace-switch-out = "zoom";
animation-for-prev-tag = "minimize";
enable-fading-prev-tag = false;
animation-for-next-tag = "slide-in-center";
enable-fading-next-tag = false;
shadow = true;
shadow-radius = 10;
shadow-opacity = .5;
shadow-offset-x = -5;
shadow-offset-y = -5;
shadow-exclude = [
    "name = 'Notification'",
    "class_g = 'Conky'",
    "class_g = 'flameshot'",
    "class_g = 'firefox'",
    "class_g = 'awesome'",
    "class_g ?= 'Notify-osd'",
    "class_g = 'Cairo-clock'",

    "_GTK_FRAME_EXTENTS@:c"
];
fading = true;
fade-in-step = 0.03;
fade-out-step = 0.03;
fade-delta = 4;
inactive-opacity = 1;
frame-opacity = 1.0;
inactive-opacity-override = false;
inactive-dim = 0.0
focus-exclude = [ "class_g = 'Cairo-clock'" ];
corner-radius = 0
rounded-corners-exclude = [
    "window_type = 'dock'",
    "window_type = 'desktop'"
];
blur: {
    method = "dual_kawase";
    strength = 1;
    background = true;
    background-frame = true;
    background-fixed = true;
}
blur-kern = "3x3box";
blur-background-exclude = [
    "window_type = 'dock'",
    "window_type = 'desktop'",
    "class_g = 'awesome'",
    "class_g = 'flameshot'",
    "class_g = 'xdg-desktop-portal-gnome'",
    "_GTK_FRAME_EXTENTS@:c"
];
backend = "glx"
vsync = false;
mark-wmwin-focused = true;
mark-ovredir-focused = true;
detect-rounded-corners = true;
detect-client-opacity = true;
use-ewmh-active-win = true
unredir-if-possible = true
unredir-if-possible-delay = 1000
detect-transient = true;
detect-client-leader = false;
glx-no-stencil = true;
glx-no-rebind-pixmap = true;
use-damage = true;
xrender-sync-fence = false;
no-ewmh-fullscreen = false
wintypes:
{
    tooltip = { animation = "none"; fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
    dock = { shadow = false; clip-shadow-above = true; }
    dnd = { shadow = false; }
    menu = { shadow = false; }
    popup_menu = { shadow = false; }
    dropdown_menu = { shadow = false; }
    utility = { shadow = false; };
};

Yeah, and next time send the minimal config i can actually use to reproduce, because with

import XMonad

import XMonad.Util.EZConfig

main :: IO ()
main = xmonad def

this is not reproducible, because as discussed it doesn't contain the rule for the floating window

manageHook = composeAll [ className =? "mixxc" --> doFloat ]
dpnova commented 1 month ago

I posted that config because that's how I reproduced it, I wasn't lying when I said that.

Anyway, it's probably edge case behaviour - close the ticket if it's too much of a pain 🤷

Elvyria commented 1 month ago

This is prooooooobably an upstream issue. I tested for a bit in Xorg and aside from your issue, something from Nvidia + GTK::Scale skyrockets Xorg's CPU usage at nvkms_unlocked_ioctl. I'll leave a minimal example to build bellow, and if it causes the same issues, there's probably nothing that i can do. (also can recommend to profile with Sysprof and see what's up)

You could set env var PULSE_PEAK_RATE=0 to disable peakers and it should mitigate the issue.

main.rs

use std::time::Duration;

use rand::Rng;
use relm4::{gtk, ComponentParts, ComponentSender};

use relm4::component::Component;

use gtk::prelude::{GtkWindowExt, RangeExt};

#[tracker::track]
struct App { value: f64, }

#[derive(Debug)]
enum Command { ValueDown, ValueUp(f64), }

#[relm4::component(pub)]
impl Component for App {
    type Init = ();
    type Input = ();
    type Output = ();
    type CommandOutput = Command;

    view! {
        gtk::Window {
            set_resizable:     false,
            set_title:         Some(APP_NAME),
            set_decorated:     false,

            set_default_height: 100,
            set_default_width: 25,

            gtk::Box {
                #[name(scale)]
                gtk::Scale::with_range(gtk::Orientation::Vertical, 0.0, 1.0, 0.005) {
                    #[track = "model.changed(App::value())"]
                    set_value: model.value,
                },
            }
        }
    }

    fn init(_: Self::Init, root: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self> {
        let model = App { value: 0.0, tracker: 0 };
        let widgets = view_output!();

        sender.command(|sender, shutdown| {
            shutdown.register(async move {
                let mut interval = tokio::time::interval(Duration::from_millis(10));

                loop {
                    interval.tick().await;
                    sender.emit(Command::ValueDown);
                }
            })
            .drop_on_shutdown()
        });

        sender.command(|sender, shutdown| {
            shutdown.register(async move {
                let mut interval = tokio::time::interval(Duration::from_millis(100));

                loop {
                    interval.tick().await;

                    let mut rng = rand::thread_rng();
                    sender.emit(Command::ValueUp(rng.gen_range(0.0..1.0)));
                }
            })
            .drop_on_shutdown()
        });

        ComponentParts { model, widgets }
    }

    fn update_cmd(&mut self, message: Self::CommandOutput, _: ComponentSender<Self>, _: &Self::Root) {
        match message {
            Command::ValueUp(peak) => {
               let peak = peak * 0.9;

               if peak > self.value + 0.035 {
                   self.set_value(peak + 0.015);
               }
            }
            Command::ValueDown => if self.value > 0.0 {
                self.set_value((self.value - 0.01).max(0.0));
            },
        }
    }
}

static APP_NAME: &str = "BITE_ME";

fn main() {
    let app = relm4::RelmApp::new(crate::APP_NAME).with_args(vec![]);

    app.set_global_css("scale { background-color: gray; }");

    app.run::<App>(());
}

Cargo.toml

[package]
name = "bug-debug"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8.5"
tracker = "0.2"

[dependencies.tokio]
version = "1.37"
features = ["time", "macros"]

[dependencies.relm4]
version = "0.8.1"
default-features = false
features = ["macros"]