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

ttyx[i] module replacement for Wayland API #2040

Open
unxed opened this issue Mar 5, 2024 · 16 comments
Open

ttyx[i] module replacement for Wayland API #2040

unxed opened this issue Mar 5, 2024 · 16 comments

Comments

@unxed
Copy link
Contributor

unxed commented Mar 5, 2024

We probably won't be able to listen to all keyboard events in Wayland like we do in X11/Xi (this is also evidenced by the fact that ttyxi keyboard capabilities don't work in GNOME Terminal on Ubuntu 23.10, where Wayland is the default; instead they only introduce input delays). However, we can at least detect the presence of Wayland so as not to unnecessarily activate delay-causing x11-related code. And we can still work with the clipboard through the Wayland API.

Alternative solution would be to use tools like wlclip along with this workaround, still don't know what to do with input delays. Also possible solution for Wayland-only systems is to use OSC52- and extended keyboard input mode supporting terminal emulatior, like kovidgoyal's kitty: all keys supported, no input delays, no x11 code needed for accessing clipboard, but still a little problem persists, see #2039

UPD: Using

--ee --nodetect=xi

makes input in GNOME Terminal working without delay even under Wayland, also clipboard works as expected and one ESC press is enough. Would be great to add whose two switches under Wayland by default.

@unxed
Copy link
Contributor Author

unxed commented Mar 5, 2024

Input delay bug under Wayland was also probably described here.

As Xi is not used in advanced input modes, input delay bug is not present in kovidgoyal's kitty.

@unxed
Copy link
Contributor Author

unxed commented Mar 20, 2024

Wayland console clipboard access tool sources for reference:
https://github.com/bugaevc/wl-clipboard

@unxed
Copy link
Contributor Author

unxed commented Mar 27, 2024

Тут подумал: нам же, вроде бы, только нажатия клавиш-модификаторов слушать нужно? В чем, собственно говоря, угроза безопасности, если разрешать слушать глобально только их? Это бы и нам нормальный UX вернуло бы, и не давало бы такой дыры в безопасности, как в KWin хотят сделать, разрешив слушать через xwayland что угодно. Может, разработчикам предложить?

@unxed
Copy link
Contributor Author

unxed commented Mar 27, 2024

XQueryPointer() под Wayland молчит, увы. Не хочет отдавать состояние альтов и контролов.

Тут ещё одна идейка пришла в голову. Чтобы починить самые нужные кнопкосочетания тем, кому очень хочется гонять именно скажем в GNOME Terminal или Konsole, можно сделать так: в системные горячие клавиши прописывать на эти комбинации запуск бинарника far2l с какими-нибудь ключиками, передающими через некий IPC активному в данный момент фару, что нажата такая-то комбинация. Фар проверяет, падало ли недавно что-то в терминал, ну и если падало, уточняет событию состояние модификаторов, ну как мы сейчас делаем, собственно.

Можно причём сам far2l научить самостоятельно прописывать нужные настойки для основных DE, если пользователь попросит (по ключику командной строки или кнопкой в меню настроек клавиатуры где-нибудь).

Финт конечно своеобразный, но кому-то может очень сильно жизнь облегчить.

@unxed
Copy link
Contributor Author

unxed commented May 19, 2024

Тут подумал. Ведь среды рабочего стола как-то перехватывают под Wayland глобальные хоткеи! Значит, принципиальная возможность это сделать должна существовать. Похоже, для этого нужно стучаться в композитор и просить отдавать события клавиатуры у него. Возможно, перед этим каким-то образом нужно будет получить какие-то дополнительные разрешения.

@unxed
Copy link
Contributor Author

unxed commented May 19, 2024

Perhaps I was too hasty in reporting that implementing keyboard input similar to ttyxi under Wayland is fundamentally impossible. A similar request came from application developers requiring a global key combination for the PushToTalk function and is currently being discussed in the Wayland bug tracker.

https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/73

@elfmz could you please add far2l's wishes for the required functionality there?

@unxed unxed changed the title ttyx module replacement for Wayland API ttyx[i] module replacement for Wayland API Dec 23, 2024
@unxed
Copy link
Contributor Author

unxed commented Dec 23, 2024

This protocol may eventually make xi working on XWayland, so we need some code to check if it is supported and do not disable xi in such cases.

https://wayland.app/protocols/xwayland-keyboard-grab-unstable-v1

Compositors are required to restrict access to this application specific protocol to Xwayland alone.

This protocol probably could be used to implement exclusive key combinations handling.

https://wayland.app/protocols/keyboard-shortcuts-inhibit-unstable-v1

@unxed
Copy link
Contributor Author

unxed commented Dec 24, 2024

Иными словами, будто бы ttyxi модуль на Wayland системах через XWayland скоро должен начать работать. См. первую из двух ссылок выше.

Но оно ещё не стабильное, и поддержки в композиторах пока может и не быть. Но если будет, надо ещё будет придумать способ определять, поддерживается ли эта штука, и на всякий случай иметь опцию ручного отключения xi, чтобы там, где не поддерживается, он задержки ввода не вносил

@unxed
Copy link
Contributor Author

unxed commented Dec 24, 2024

О, кажется, оно есть в KDE и GNOME!

https://bugs.kde.org/show_bug.cgi?id=455159

https://bugzilla.gnome.org/show_bug.cgi?id=783342

Интересно, а чего xi не работал у нас под Wayland тогда?

@elfmz можете, пожалуйста, глянуть?

И, если у нас теперь есть трекинг фокуса, почему бы не слушать вообще весь ввод иксов, когда мы точно знаем, что фокус на терминале?

Можно даже переключить фокус с текущего окна на любое другое, и тут же обратно, через X11 API по старое, чтобы терминал сообщил текущее состояние, а то не все это делают при включении режима отслеживания фокуса.

@unxed
Copy link
Contributor Author

unxed commented Dec 24, 2024

Ага, в GNOME оно должно быть, но надо включать вручную:

gsettings set org.gnome.mutter.wayland xwayland-allow-grabs true

Тут некоторые подробности:
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1257

А в KDE я собственно баг с задержками ввода и не пытался воспроизводить, кажется.

@unxed
Copy link
Contributor Author

unxed commented Dec 25, 2024

gsettings set org.gnome.mutter.wayland xwayland-allow-grabs true

Такая настройка в ванильном Ubuntu 24.04 действительно есть, по умолчанию false. Но даже с true xi у меня не заработал, и под sudo тоже, и после перезагрузки тоже.

Должно работать, но, почему-то, не работает. Реквестую помощь зала!

@unxed
Copy link
Contributor Author

unxed commented Dec 25, 2024

Должно работать, но, почему-то, не работает. Реквестую помощь зала!

Написал тикет в Mutter

https://gitlab.gnome.org/GNOME/mutter/-/issues/3842

@unxed
Copy link
Contributor Author

unxed commented Dec 25, 2024

Написал тикет в Mutter

И в Muffin (это у меня в Mint/Cinnamon). Там вообще пока поддержки нет, вот сделал тикет чтоб добавили:
linuxmint/muffin#708

@unxed
Copy link
Contributor Author

unxed commented Dec 25, 2024

Выяснил, что там надо ещё в whitelist добавлять WM_CLASS (который для начала надо для xi модуля ещё выставить.
gsettings set org.gnome.mutter.wayland xwayland-grab-access-rules "['far2l']"

Или вот такое делать:
TigerVNC/tigervnc@da10343#diff-d6e393c78ed27b12a400d1527a70c1ecdd8e28391ef4d7d83005287dbe901179R299

btw, заставить это всё работать пока всё равно не получилось.

@unxed
Copy link
Contributor Author

unxed commented Dec 26, 2024

Еееееее у меня получилось заставить перехват работать в KDE на 24.10 (Wayland там впервые по умолчанию в KDE) без каких-то специальных настроке DE. Перехватывается всё, кроме букв. Вот код тестового приложения:

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

int main() {
    Display *display;
    Window root_window;
    XEvent event;
    bool running = true;
    Window focused_window;
    int revert_to;
    Window intercept_window;
    XSetWindowAttributes attrib;

    // Opens a connection to the X server
    if ((display = XOpenDisplay(NULL)) == NULL) {
        fprintf(stderr, "Failed to open connection to X server\n");
        return 1;
    }

    // Gets the root window
    root_window = DefaultRootWindow(display);

    // Create an override-redirect window
    attrib.override_redirect = True;
    intercept_window = XCreateWindow(display, root_window, 0, 0, 1, 1, 0,
                                    CopyFromParent, InputOnly, CopyFromParent,
                                    CWOverrideRedirect, &attrib);

    if (!intercept_window) {
        fprintf(stderr, "Failed to create override-redirect window\n");
        XCloseDisplay(display);
        return 1;
    }

    // **Send _XWAYLAND_MAY_GRAB_KEYBOARD ClientMessage**
    Atom may_grab_atom = XInternAtom(display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False);

    if (may_grab_atom != None) {
        XClientMessageEvent client_message;
        client_message.type = ClientMessage;
        client_message.window = intercept_window; // **Target the grab window**
        client_message.format = 32;
        client_message.message_type = may_grab_atom;
        client_message.data.l[0] = 1; // **Non-zero value**
        client_message.data.l[1] = CurrentTime;

        XSendEvent(display, root_window, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&client_message);
        XFlush(display);
        printf("Sent _XWAYLAND_MAY_GRAB_KEYBOARD message targeting the intercept window.\n");
    } else {
        fprintf(stderr, "Failed to get _XWAYLAND_MAY_GRAB_KEYBOARD atom.\n");
        XDestroyWindow(display, intercept_window);
        XCloseDisplay(display);
        return 1;
    }

    // Map the window (though it won't be visible)
    XMapWindow(display, intercept_window);

    // Grabs the keyboard to the override-redirect window
    int result = XGrabKeyboard(display, intercept_window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
    if (result != GrabSuccess) {
        fprintf(stderr, "Failed to grab keyboard (error %d)\n", result);
        XDestroyWindow(display, intercept_window);
        XCloseDisplay(display);
        return 1;
    }

    printf("Starting to intercept and forward key presses to other applications... Press Escape to exit.\n");

    while (running) {
        // Gets the next event
        XNextEvent(display, &event);

        // Checks the event type
        switch (event.type) {
            case KeyPress:
            case KeyRelease: {
                KeySym keysym;
                char buffer[255];
                int len = XLookupString(&event.xkey, buffer, sizeof(buffer), &keysym, NULL);

                printf("Event: %s, KeyCode: %u, KeySym: %lu",
                       event.type == KeyPress ? "KeyPress" : "KeyRelease",
                       (unsigned int)event.xkey.keycode,
                       keysym);

                if (len > 0) {
                    printf(", Symbol: %.*s", len, buffer);
                }
                printf("\n");

                // Gets the window with the current input focus
                XGetInputFocus(display, &focused_window, &revert_to);

                // Resends the event to the window with the current focus
                if (focused_window != None) {
                    XEvent send_event = event; // Copies the received event
                    send_event.xany.window = focused_window; // Specifies the window with focus
                    send_event.xkey.window = focused_window; // Specifies the window with focus for keyboard events
                    XSendEvent(display, focused_window, True, KeyPressMask | KeyReleaseMask, &send_event);
                    XFlush(display); // Sends the event immediately
                }
                break;
            }
            default:
                // Ignores other event types
                break;
        }

        // Checks if the Escape key is pressed to exit
        if (event.type == KeyPress && event.xkey.keycode == 9) { // KeyCode 9 corresponds to Escape
            running = false;
        }
    }

    // Releases the keyboard grab and closes the connection
    XUngrabKeyboard(display, CurrentTime);
    XDestroyWindow(display, intercept_window);
    XCloseDisplay(display);
    printf("Exiting.\n");
    return 0;
}

@unxed
Copy link
Contributor Author

unxed commented Dec 27, 2024

Не подходит нам метод c XGrabKeyboard всё же. Grab может делать только одно приложение в единицу времени. Несколько экземпляров фара не смогут работать параллельно :(

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

No branches or pull requests

1 participant