如何在等待terminalinput的同时处理窗口事件?

我有一个跨平台(windows和unix + xcb)terminal+ graphics_window应用程序,它大多数工作正常,直到您等待太长时间在input提示,在重载下图像可能会消失。 🙁

我有一个解释器(postscript解释器)的主循环(REPL),它每次在其循环中调用一个事件处理函数。 事件处理程序执行通常是窗口的消息/事件循环的一个迭代。 但是input是用普通的C / O进行处理的,所以事件处理程序在fgetc()被阻塞时不会被调用。

graphics窗口是仅输出的。 它没有button,只需要响应Raise,Map,Expose等事件。

如何在input读取循环的过程中调用事件处理程序? 这需要使用POSIX和win32 API来实现。

选项似乎是

  • 非阻塞I / O
    在unix中比较简单。 看起来像在窗户上的痛苦
  • 轮询
  • input线程
    并行线程?
  • 窗口线程
    并行线程?

这些中的任何一个可能会比其他人更不痛苦?

如果我可以留在unix上,那么这似乎是完成了一个窍门:

 #include <errno.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include <fcntl.h> void idleproc () { /* simulates calling the event handler (ie. one slice of the window loop) */ //printf("idle\n"); putchar('.'); } int idlefgetc (FILE *stream) { int ret; do { ret = fgetc(stream); idleproc(); } while(ret == EOF && (errno == EAGAIN || errno == EINTR)); return ret; } int setraw (FILE *stream) { struct termios tbuf; if (tcgetattr(fileno(stream), &tbuf) == -1) return -1; tbuf.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); tbuf.c_oflag &= ~OPOST; tbuf.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tbuf.c_cflag &= ~(CSIZE | PARENB); tbuf.c_cflag |= CS8; if (tcsetattr(fileno(stream), TCSANOW, &tbuf) == -1) return -1; return 0; } int setnonblocking (FILE *stream) { int flags; if (setraw(stream) != 0) return -1; if (!((flags = fcntl(fileno(stream), F_GETFL)) & O_NONBLOCK)) { flags |= O_NONBLOCK; fcntl(fileno(stream), F_SETFL, flags); } return 0; } int main (int argc, char **argv) { if (setnonblocking(stdin)) { perror(argv[0]); return 0; } printf("'%d'\n", idlefgetc(stdin)); system("stty sane"); return 0; } 

Solutions Collecting From Web of "如何在等待terminalinput的同时处理窗口事件?"

在Windows下,您需要使用Console API。 您可以使用ReadFileEx执行异步,非阻塞,读取字符。 另一种可能是ReadConsoleInput ,并持续轮询输入,而不会阻塞。 通过SetConsole,您可以决定要捕获哪些事件。

有关Windows下异步I / O的更多详细信息,请参阅此处 。

毕竟,另一个解决方案似乎是可能的,因为这是一个编程语言解释器

应该可以重新实现使用fgetc的代码来代替使用ps原语: -file- read int bool 。 这个postscript运算符本身使用stdio调用,但是可以通过推入exec堆栈并返回,由其他具有继续传递样式的文件读取函数调用。 这会自然地将更多的调用交织到事件处理程序,因为它更频繁地返回到主循环。

我可能仍然需要使用非阻塞式读取。 但是如果仅在一个地方调用,这将更容易管理。 延续传递的优点是将大的函数分解成不同的阶段,并且通过覆盖基类中的方法(将基类实现为后缀字典)成功用于实现窗口设备本身。 但对我来说还是比较新的,所以这还不是我的首选 。 🙂

我将重新制作这个原型来说明下班后的方法。 🙂

编辑:花了几天。 但这是新的想法。 对于Windows,它需要以不同的方式进行非阻塞呼叫,但呼叫可以隔离到这一个地方。 而且,通过继续传递,文件读取功能不需要访问(或知道)事件处理程序,所以更好的封装

该程序的行为与问题中的行为相同,即打印. 重复直到按键,然后打印按键的ascii代码。 的. 在等待按键时反复模拟调用事件处理程序。 我不得不模拟一些解释器的东西:一个对象类型,一些堆栈和一个eval()函数。 所以这也更好地说明了REPL。

 #include <errno.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include <fcntl.h> int set_raw_term (FILE *stream) { struct termios tbuf; if (tcgetattr(fileno(stream), &tbuf) == -1) return -1; tbuf.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); tbuf.c_oflag &= ~OPOST; tbuf.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tbuf.c_cflag &= ~(CSIZE | PARENB); tbuf.c_cflag |= CS8; if (tcsetattr(fileno(stream), TCSANOW, &tbuf) == -1) return -1; return 0; } int set_nonblocking (FILE *stream) { int flags; if (set_raw_term(stream) != 0) return -1; if (!((flags = fcntl(fileno(stream), F_GETFL)) & O_NONBLOCK)) { flags |= O_NONBLOCK; fcntl(fileno(stream), F_SETFL, flags); } return 0; } int event_handler() { putchar('.'); } enum { null, integer, file, operator }; typedef union { short tag; struct { short tag; int val; } int_; struct { short tag; FILE *f; } file_; struct { short tag; int (*fp)(); } oper_; } object; /* object union allows multiple types on the stacks */ object os[100]; /* operand stack */ object *tos = os; /* top of operand stack */ object es[100]; /* execution stack */ object *tes = es; /* top of execution stack */ int eval () { if (tes == es) /* execution stack is empty */ return -1; /* return "finished" */ event_handler(); switch(tes[-1].tag) { /* type of object on top of execution stack */ case integer: case file: *tos++ = *--tes; /* push file or integer to operand stack */ break; case operator: (--tes)->oper_.fp(); /* call operator function */ break; } return 0; /* return "not finished" */ } int file_read_byte () { int ret; object arg; arg = *--tos; /* pop argument from operand stack */ ret = fgetc(arg.file_.f); if (ret == EOF && (errno == EAGAIN || errno == EINTR)) { /* if no data */ *tos++ = arg; /* restore argument to operand stack */ *tes++ = (object){ .oper_.tag = operator, .oper_.fp = file_read_byte }; /* push continuation to execution stack */ return 0; } else { *tos++ = (object){ .int_.tag = integer, .int_.val = ret }; /* push result to operand stack */ return 0; } } int main(int argc, char **argv) { int ret; if (set_nonblocking(stdin) != 0) { perror(argv[0]); return 0; } //printf("'%d'\n", file_read_byte(stdin)); *tos++ = (object){ .file_.tag = file, .file_.f = stdin }; /* push file argument to operand stack */ *tes++ = (object){ .oper_.tag = operator, .oper_.fp = file_read_byte }; /* push operator object to execution stack */ ret = 0; while (ret == 0) { /* call eval until execution is "finished" */ ret = eval(); } printf("'%d'\n", (--tos)->int_.val); /* pop returned value */ system("stty sane"); return 0; }