cómo dejar de bloquear el XNextEvent de xlib

5 minutos de lectura

En Windows, el subproceso de la GUI generalmente llama a GetMessage para esperar un mensaje, cuando otro subproceso usa PoseMessage coloca un mensaje en la cola, entonces el subproceso de la GUI devolverá GetMessage (dejar de bloquear).

¿Alguien puede decirme, cuando uso XNextEvent en XWindows para esperar un evento, cómo puedo “activar” el subproceso de la GUI en otro subproceso? ¿Hay alguna API como PoseMessage que pueda usar?

avatar de usuario
Aarón Digulla

No. Esta es la razón por la cual la mayoría de los marcos de UI (Gtk, KDE, etc.) usan bucles principales personalizados para poder escuchar más fuentes de eventos.

Internamente, XNextEvent usa un socket, por lo que llama select() para saber cuándo la entrada está disponible. Llamada ConnectionNumber(display) para obtener el descriptor de archivo que necesita pasar select()

Eso le permite escuchar varios descriptores de archivos.

Ejemplo de código de 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);
}

  • Hola Aaron, esta es una solución increíble. Esto solo captura eventos en esa ventana, ¿es posible escuchar los eventos del mouse a nivel mundial? ¿Y es posible bloquear los eventos del mouse con este método?

    – Noitidart

    23 de octubre de 2015 a las 12:03

  • @Noitidart: Tal vez, pero no puedo responder eso en un comentario. Haga una nueva pregunta, por favor y no olvide dar más detalles de lo que necesita lograr exactamente.

    – Aarón Digulla

    23/10/2015 a las 15:43

  • Ah, muchas gracias, publiqué una pregunta aquí, por favor: stackoverflow.com/questions/32262767/mouse-events-callback

    – Noitidart

    23/10/2015 a las 19:25

  • El primer argumento a select() debiera ser x11_fd + 1no 1. Desde man 2 select: > nfds es el descriptor de archivo con el número más alto en cualquiera de los tres conjuntos, más 1.

    – Daniel Jano

    31 de julio de 2016 a las 19:59

  • Preferir pselect() o poll() sobre select()ya que pselect() tiene un mejor manejo de señales y puede evitar condiciones de carrera con señales que pueden ocurrir con select().

    – 12431234123412341234123

    25 de agosto de 2020 a las 11:57


Puede salir del bloqueo de XNextEvent enviándose un evento ficticio.

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);
  ...
}

En otro hilo puedes hacer esto para terminar el bucle:

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);

  • ¡Peligro, Will Robinson! Xlib es no hilo seguro! El código anterior parecerá que funciona, pero de hecho fallará al azar y con poca frecuencia de una manera muy difícil de depurar. ¡No lo hagas!

    – David dado

    23 de febrero de 2015 a las 0:03


  • Eso lo hará seguro para subprocesos, pero aún tendrá una condición de carrera.

    – elmindreda

    10 de febrero de 2019 a las 16:38

  • Sí, intenté llenar la cola en una interrupción (controlador de señal). Puedo dar fe del hecho de que tiene fallas intermitentes.

    –Scott Franco

    28 de abril de 2021 a las 17:17

avatar de usuario
kevin mille

Debe usar: Bool XCheckMaskEvent (Display*, long, XEvent)

los XCheckMaskEvent La función primero busca en la cola de eventos y luego en cualquier evento disponible en la conexión del servidor para el primer evento que coincida con la máscara especificada.

Si encuentra una coincidencia, XCheckMaskEvent elimina ese evento, lo copia en el especificado XEvent estructura y retornos True. Los demás eventos almacenados en la cola no se descartan.

Si el evento que solicitaste no está disponible, XCheckMaskEvent devoluciones Falsey el búfer de salida se habrá vaciado.

  • No pude hacer que eso funcionara. Estaba esperando los eventos de PointerBarrier y no estaba seguro de qué máscara sería la adecuada. Lo que funcionó fue usar if (XPending(_display) > 0); luego XNextEvent; de lo contrario continuar;

    –Kevin

    11 mayo 2016 a las 18:28

  • Gracias por esta respuesta. @kevinf Esto funcionó para mí: while(XCheckMaskEvent(display -1, &event)) { /*hacer cosas*/ } Algunas máscaras de implementación no están firmadas, por lo que es posible que deba emitirlas. En Ubuntu 18, la máscara tiene una firma de 32 bits, por lo que simplemente pasar -1 cubre cualquier indicador.

    – Sin cielo

    23 de agosto de 2018 a las 0:28


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad