wie man die Blockierung von XNextEvent von xlib aufhebt

Lesezeit: 5 Minuten

Unter Windows ruft der GUI-Thread normalerweise GetMessage auf, um auf eine Nachricht zu warten. Wenn ein anderer Thread PoseMessage verwendet, um eine Nachricht in die Warteschlange zu stellen, gibt der GUI-Thread GetMessage zurück (Beendigung der Blockierung).

Kann mir jemand sagen, wie ich den GUI-Thread in einem anderen Thread “aufwecken” kann, wenn ich XNextEvent unter XWindows verwende, um auf ein Ereignis zu warten? Gibt es eine API wie PoseMessage, die ich verwenden kann?

Benutzer-Avatar
Aaron Digulla

Nein. Aus diesem Grund verwenden die meisten UI-Frameworks (Gtk, KDE usw.) benutzerdefinierte Hauptschleifen, um auf mehr Ereignisquellen lauschen zu können.

Intern verwendet XNextEvent einen Socket, also ruft es auf select() um zu wissen, wann eine Eingabe verfügbar ist. Anruf ConnectionNumber(display) um den Dateideskriptor zu erhalten, den Sie übergeben müssen select()

Dadurch können Sie auf mehrere Dateideskriptoren lauschen.

Beispielcode von http://www.linuxquestions.org/questions/showthread.php?p=2431345#post2431345

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

Display *dis;
Window win;
int x11_fd;
fd_set in_fds;

struct timeval tv;
XEvent ev;

int main() {
    dis = XOpenDisplay(NULL);
    win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, \
        0, BlackPixel (dis, 0), BlackPixel(dis, 0));

    // You don't need all of these. Make the mask as you normally would.
    XSelectInput(dis, win, 
        ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
        ButtonPressMask | ButtonReleaseMask  | StructureNotifyMask 
        );

    XMapWindow(dis, win);
    XFlush(dis);

    // This returns the FD of the X11 display (or something like that)
    x11_fd = ConnectionNumber(dis);

    // Main loop
    while(1) {
        // Create a File Description Set containing x11_fd
        FD_ZERO(&in_fds);
        FD_SET(x11_fd, &in_fds);

        // Set our timer.  One second sounds good.
        tv.tv_usec = 0;
        tv.tv_sec = 1;

        // Wait for X Event or a Timer
        int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
        if (num_ready_fds > 0)
            printf("Event Received!\n");
        else if (num_ready_fds == 0)
            // Handle timer here
            printf("Timer Fired!\n");
        else
            printf("An error occured!\n");

        // Handle XEvents and flush the input 
        while(XPending(dis))
            XNextEvent(dis, &ev);
    }
    return(0);
}

  • Hallo Aaron, das ist eine tolle Lösung. Dies fängt nur Ereignisse in diesem Fenster ab, ist es möglich, Mausereignisse global abzuhören? Und möglich, die Mausereignisse mit dieser Methode zu blockieren?

    – Noitidart

    23. Oktober 2015 um 12:03 Uhr

  • @Noitidart: Vielleicht, aber das kann ich nicht in einem Kommentar beantworten. Stellen Sie bitte eine neue Frage und vergessen Sie nicht, mehr Details zu geben, was genau Sie erreichen müssen.

    – Aaron Digulla

    23. Oktober 2015 um 15:43 Uhr

  • Ah, vielen Dank, ich habe hier bitte eine Frage gepostet: stackoverflow.com/questions/32262767/mouse-events-callback

    – Noitidart

    23. Oktober 2015 um 19:25 Uhr

  • Das erste Argument zu select() sollte sein x11_fd + 1nicht 1. Von man 2 select: > nfds ist der Dateideskriptor mit der höchsten Nummer in einem der drei Sätze, plus 1.

    – Daniel Janus

    31. Juli 2016 um 19:59 Uhr

  • Vorziehen pselect() oder poll() Über select()seit pselect() hat eine bessere Signalhandhabung und kann Rennbedingungen mit Signalen vermeiden, die mit passieren können select().

    – 12431234123412341234123

    25. August 2020 um 11:57 Uhr


Sie können das blockierende XNextEvent verlassen, indem Sie sich selbst ein Dummy-Ereignis senden.

Window interClientCommunicationWindow;
Bool x11EventLoopActive = True;

// create a dummy window, that we can use to end the blocking XNextEvent call
interClientCommunicationWindow = XCreateSimpleWindow(dpy, root, 10, 10, 10, 10, 0, 0, 0);
XSelectInput(dpy, interClientCommunicationWindow, StructureNotifyMask);

XEvent event;
while(x11EventLoopActive) {
  XNextEvent(dpy, &event);
  ...
}

In einem anderen Thread können Sie dies tun, um die Schleife zu beenden:

x11EventLoopActive = False;
// push a dummy event into the queue so that the event loop has a chance to stop
XClientMessageEvent dummyEvent;
memset(&dummyEvent, 0, sizeof(XClientMessageEvent));
dummyEvent.type = ClientMessage;
dummyEvent.window = interClientCommunicationWindow;
dummyEvent.format = 32;
XSendEvent(dpy, interClientCommunicationWindow, 0, 0, (XEvent*)&dummyEvent);
XFlush(dpy);

  • Gefahr, Will Robinson! Xlib ist nicht threadsicher! Der obige Code sieht so aus, als ob er funktioniert, stürzt aber tatsächlich zufällig und selten auf eine sehr schwer zu debuggende Weise ab. Tu es nicht!

    – David gegeben

    23. Februar 2015 um 0:03 Uhr


  • Das macht es Thread-sicher, aber es wird immer noch eine Race-Condition haben.

    – elmindreda

    10. Februar 2019 um 16:38 Uhr

  • Ja, ich habe versucht, die Warteschlange mit einem Interrupt (Signalhandler) zu füllen. Ich kann für die Tatsache bürgen, dass es sporadische Ausfälle hat.

    – Scott Franco

    28. April 2021 um 17:17 Uhr

Benutzer-Avatar
Kevin Mille

Sie sollten verwenden: Bool XCheckMaskEvent(Display*, long, XEvent)

Das XCheckMaskEvent Die Funktion durchsucht zuerst die Ereigniswarteschlange und dann alle auf der Serververbindung verfügbaren Ereignisse nach dem ersten Ereignis, das mit der angegebenen Maske übereinstimmt.

Wenn es eine Übereinstimmung findet, XCheckMaskEvent entfernt dieses Ereignis, kopiert es in die angegebene XEvent Struktur und Rendite True. Die anderen in der Warteschlange gespeicherten Ereignisse werden nicht verworfen.

Wenn die gewünschte Veranstaltung nicht verfügbar ist, XCheckMaskEvent kehrt zurück Falseund der Ausgabepuffer wurde geleert.

  • Ich konnte das nicht zum Laufen bringen. Ich habe auf PointerBarrier-Ereignisse gewartet und bin mir nicht sicher, welche Maske angemessen wäre? Was funktioniert hat, war die Verwendung von if (XPending(_display) > 0); dann XNextEvent; andernfalls weiter;

    – Kevin

    11. Mai 2016 um 18:28 Uhr

  • Danke für diese Antwort. @kevinf Das hat bei mir funktioniert: while(XCheckMaskEvent(display -1, &event)) { /*do things*/ } Einige Implementierungsmasken sind nicht signiert, daher müssen Sie möglicherweise umwandeln. Unter Ubuntu 18 ist die Maske 32-Bit-signiert, sodass das Übergeben von -1 alle Flags abdeckt.

    – Unendlich

    23. August 2018 um 0:28 Uhr


Sie können auch Ihre eigene Schleife und Abfrage ausführen pending. Dieser Code ist in Crystal geschrieben, aber in C genauso:

loop do
    while @display.pending > 0
        event = @display.next_event
        # do stuff with event
    end
    sleep 16.milliseconds
end

Auf diese Weise ist es jedoch erheblich ressourcenintensiver (natürlich abhängig von der Schlafzeit), weshalb die akzeptierte Antwort möglicherweise eine bessere Idee ist.

1383170cookie-checkwie man die Blockierung von XNextEvent von xlib aufhebt

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy