Re: Help with external socket handling and xcb
Uli Schlachter <[email protected]> Sat, 25 Sep 2021 11:12:09 +0200
| Newsgroups | gmane.comp.freedesktop.xcb |
|---|---|
| Message-ID | <[email protected]> |
Hi, Am 09.09.21 um 12:48 schrieb Carlo Wood: > Just to be clear; so this means that in order for xcb_poll_for_event > to work while only calling it when I see that the fd is readable, I only > ever have to monitor that the fd is readable, and never have to worry > about monitoring if the fd is writable, because all writing is done > blocking, even though the fd itself is non-blocking? Attached is a short example based on the code that I already linked to elsewhere. This code sends some requests and non-blockingly waits for the replies (via poll()). When poll() indicates a timeout, some more requests are sent just so that the example does something. The important bits are: Whenever anything used the xcb connection (in this case: through xcb_get_input_focus()), this calls both xcb_flush() (to (blockingly - sorry) send the new requests) and dispatch_replies()/xcb_poll_for_reply() to check whether new replies were received. The later is necessary since both xcb_get_input_focus() (sending a request) and xcb_flush() (flushing the output buffer) might read from the fd. Put differently: We always have to check whether the fd is readable *or* some replies are already pending. This example does not deal with events, but that is just one xcb_poll_for_event() away. I hope this helps, Uli -- I'd be delighted to offer any advice I can. When I have some, I'll let you know.
foo.c
(text/x-csrc, 2.9 KB)
#include <xcb/xcb.h>
#include <xcb/xcbext.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
typedef void reply_callback(void *reply, xcb_generic_error_t *error, void *data);
typedef struct pending_reply_t {
unsigned int sequence;
reply_callback *callback;
void *data;
struct pending_reply_t *next;
} pending_reply_t;
// global state is bad
static pending_reply_t *pending_replies = NULL;
static void add_pending_reply(unsigned int sequence, reply_callback *callback, void *data) {
struct pending_reply_t **prev = &pending_replies;
struct pending_reply_t *new_reply = malloc(sizeof(*new_reply));
if (sequence == 0) {
// Sending the request failed.
// TODO: Uhm... call the callback? Just drop things on the
// floor?
return;
}
new_reply->sequence = sequence;
new_reply->callback = callback;
new_reply->data = data;
// Insert so that ->sequence is sorted
// TODO: This should probably take overflows into account
while (*prev && (*prev)->sequence < new_reply->sequence) {
prev = &(*prev)->next;
}
// Actually insert
new_reply->next = *prev;
*prev = new_reply;
}
static void dispatch_replies(xcb_connection_t *conn, int poll) {
while (pending_replies) {
void *reply;
xcb_generic_error_t *error = NULL;
pending_reply_t *next = pending_replies;
if (poll) {
if (!xcb_poll_for_reply(conn, next->sequence, &reply, &error))
// There was no reply yet
return;
} else {
reply = xcb_wait_for_reply(conn, next->sequence, &error);
}
pending_replies = next->next;
(*next->callback)(reply, error, next->data);
free(next);
}
}
static void print_reply(xcb_get_input_focus_reply_t *reply, xcb_generic_error_t *err, const char *str) {
if (reply) {
printf("%s got 0x%x\n", str, reply->focus);
}
if (err) {
puts("How the heck did GetInputFocus fail?!");
}
free(reply);
free(err);
}
int main() {
xcb_connection_t *conn = xcb_connect(NULL, NULL);
add_pending_reply(xcb_get_input_focus(conn).sequence, (reply_callback *) print_reply, "The first reply");
add_pending_reply(xcb_get_input_focus(conn).sequence, (reply_callback *) print_reply, "The second reply");
// Fake a main loop
while (!xcb_connection_has_error(conn)) {
struct pollfd fds[1];
fds[0].fd = xcb_get_file_descriptor(conn);
fds[0].events = POLLIN;
fds[0].revents = 0;
if (0 == poll(fds, 1, 1000)) {
puts("Timeout! Sending more requests");
add_pending_reply(xcb_get_input_focus(conn).sequence, (reply_callback *) print_reply, "The third reply");
add_pending_reply(xcb_get_input_focus(conn).sequence, (reply_callback *) print_reply, "The fourth reply");
}
// The above might have put new stuff in xcb's output buffer.
// Send that. (Yes, this does a blocking send - sorry).
xcb_flush(conn);
// The above calls to xcb_get_input_focus() and xcb_flush()
// might have read from the socket. Make sure we are not missing
// anything and check for new replies.
dispatch_replies(conn, 1);
}
puts("done");
xcb_disconnect(conn);
return 0;
}