elkowar / eww

ElKowars wacky widgets
https://elkowar.github.io/eww
MIT License
9.46k stars 384 forks source link

[BUG] Once clicked, button triggers from anywhere on the widget #1022

Open altacountbabi opened 9 months ago

altacountbabi commented 9 months ago

Checklist before submitting an issue

Description of the bug

Whenever I click on a button it opens fine but then I can click anywhere on the window and it will activate the button. This only seems to happen when wlogout with the layer shell protocol is the button action. I have tested with notify-send and it seems to work fine

Reproducing the issue

eww.yuck

(
    defwindow bar
    :monitor 0
    :geometry (
        geometry
        :x "0%"
        :y "0%"
        :width "100%"
        :height "40px"
        :anchor "top center"
    )
    :exclusive true
    (
        centerbox
        :orientation "h"
        "workspaces"
        "00:00"
        (
            box
            :class "sidestuff"
            :orientation "h"
            :space-evenly false
            :halign "end"
            (
                button
                :class ""
                :timeout "9999h"
                :onclick "wlogout -p layer-shell"
                ""
            )
        )
    )
)

style.scss

* {
    all: unset;
    font-weight: 600;
}

.bar {
    background-color: #000000;
    padding: 10px;
}

.sidestuff {
    margin-right: 10px;
}

Expected behaviour

The button only activating when clicked on it

Additional context

reproducing this bug requires wlogout installed using eww version 0.4.0 65d622c81f2e753f462d23121fa1939b0a84a3e0

https://github.com/elkowar/eww/assets/82091823/006f0cd3-9cf9-4d87-a3bf-a45ae0d1cb07

JohnOberhauser commented 9 months ago

I'm having a similar issue. When my bar gets into this state, I no longer see hover highlighting on my other buttons, and it seems like only the one button can be interacted with.

Rayzeq commented 9 months ago

It's just a theory, but here's what I think is happening:

  1. When a GTK button detects a button press event, it grabs the pointer. This behavior is detailed in the GDK documentation here: Note that if the event mask of a GdkWindow has selected both button press and button release events, or touch begin and touch end, then a press event will cause an automatic grab until the button is released, equivalent to a grab on the window with owner_events set to TRUE. This is done because most applications expect to receive paired press and release events.
  2. Wayland (or only Hyprland, I don't know) has some weird behavior with grabs, which can lead to them being disregarded. However, GTK retains its own grab mechanism, which redirects all events received by the main window (your bar) to the button.
  3. wlogout opens before the button release event is received by your bar, and intercepts it. Because it hasn't received a button release event, your power button won't ungrab the pointer.
  4. This means that at this stage, all events received by your bar are sent to your power button.

Whether my theory is correct or not, I've done some tests and I've noticed that this bug indeed only triggers if you release your mouse button after wlogout has opened. So to avoid triggering the issue, you need to release your mouse button before wlogout opens, either by adding a delay before wlogout opens, or by clicking faster.

Note that releasing your mouse button before wlogout opens will also fix your bar if it's in this bugged state.

Rayzeq commented 9 months ago

A way to solve the issue properly would be to replace your button with:

(
    eventbox
    :class ""
    :timeout "9999h"
    :onbuttonrelease "wlogout -p layer-shell"
    ""
)

Unfortunately, eww currently doesn't have a onbuttonrelease event.

You may also note that you don't need to put a really high timeout, you can do:

(
    button
    :class ""
    :onclick "wlogout -p layer-shell &"
    ""
)
j0ng4b commented 9 months ago

It's just a theory, but here's what I think is happening:

1. When a GTK button detects a button press event, it grabs the pointer. This behavior is detailed in the GDK documentation [here](https://docs.gtk.org/gdk3/method.Seat.grab.html):
   `Note that if the event mask of a GdkWindow has selected both button press and button release events, or touch begin and touch end, then a press event will cause an automatic grab until the button is released, equivalent to a grab on the window with owner_events set to TRUE. This is done because most applications expect to receive paired press and release events.`

2. Wayland (or only Hyprland, I don't know) has some weird behavior with grabs, which can lead to them being disregarded. However, GTK retains its own grab mechanism, which redirects all events received by the main window (your bar) to the button.

3. `wlogout` opens before the button release event is received by your bar, and intercepts it. Because it hasn't received a button release event, your power button won't ungrab the pointer.

4. This means that at this stage, all events received by your bar are sent to your power button.

Whether my theory is correct or not, I've done some tests and I've noticed that this bug indeed only triggers if you release your mouse button after wlogout has opened. So to avoid triggering the issue, you need to release your mouse button before wlogout opens, either by adding a delay before wlogout opens, or by clicking faster.

Note that releasing your mouse button before wlogout opens will also fix your bar if it's in this bugged state.

This seems to be true, the same happens with rofi wayland fork that uses layer shell protocol, when I added a sleep before open rofi everything work fine but without the sleep when click on any place that is an eww widget the rofi is (re)opened independent of the widget onclick event action.

I'm using Hyprland too.

MarvinRuesenberg commented 7 months ago

I am/was experiencing the same behavior with eventboxes and buttons.

Since the issue seems to be related to the connect_button_press event, i got a very dirty workaround by using the connect_button_release_event instead.

As i am not really familiar with the whole ecosystem of gtk, signal handling, nor rust really - I dont know whether this is just a lucky guess or will break other stuff down the road. I am assuming however that the button press event is not released by default and therefore tied to the widget until the pointer/focus gets taken by a different one. Maybe there is a way to release it inside of the function?

I changed the lines for the eventboxes to the following in: eww/crates/eww/src/widgets/widget_definitions.rs - line 852++:

gtk_widget.add_events(gdk::EventMask::BUTTON_RELEASE_MASK);
            connect_signal_handler!(gtk_widget, gtk_widget.connect_button_release_event(move |_, evt| {
                match evt.button() {
                    1 => run_command(timeout, &onclick, &[] as &[&str]),
                    2 => run_command(timeout, &onmiddleclick, &[] as &[&str]),
                    3 => run_command(timeout, &onrightclick, &[] as &[&str]),
                    _ => {},
                }