#include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "lx.h" #define INTERVAL 100000 #define LINESTEP 13 typedef unsigned long int ULI; typedef unsigned long long int ULLI; typedef signed long int SLI; typedef signed long long int SLLI; typedef unsigned long long int TIME; typedef struct pendline PENDLINE; typedef struct pendlines PENDLINES; typedef struct lwc LWC; typedef struct cmd CMD; struct pendlines { PENDLINE *newest; PENDLINE *oldest; } ; struct pendline { PENDLINE *nlink; PENDLINE *olink; char *text; int len; } ; struct lwc { unsigned char *b; int a; int l; int c; } ; struct cmd { const char *cmd; void (*impl)(const char *, int, CMD *); #define CMD_IMPL_ARGS \ const char *arg __attribute__((__unused__)), \ int arglen __attribute__((__unused__)), \ CMD *cmd __attribute__((__unused__)) int cmdlen; } ; static int bid; static LX_CONN *xc; static LX_XID root; static int depth; static LX_XID win; static LX_XID visual; static LX_XID cmap; static unsigned int pixv[8]; static LX_XID gc; static LX_XID pm; static AIO_OQ out_oq; static int iid; static int oid; static int redrawid; static int want_redraw; static LWC il; static LWC dl; static LWC pl; static int iid; static int oid; static struct termios old_tio; static struct termios our_tio; static PENDLINES pendlines; static FILE *f; static ES oline = ES_STATIC_INIT; static int line_appended; static int flushid; static unsigned int exiting; #define EXIT_SET 0x00000001 #define EXIT_UPDATE 0x00000002 static LX_X_ERR_ACTION (*prev_X)(LX_CONN *, const LX_X_ERR *); static void (*prev_lib)(LX_CONN *, const LX_LIB_ERR *); static TIME exp; static int x; static int pixx; static LX_ACCESSCONTROL accctl; static LX_HOSTLIST *hl; static LX_STRLIST *exts; static const char backspaces[64] = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; static const char spaces[64] = " "; static int curscreen; #define Cisspace(x) isspace((unsigned char)(x)) static TIME now_time(void) { struct timeval tv; gettimeofday(&tv,0); return((tv.tv_sec*1000000ULL)+tv.tv_usec); } static void nbio(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); } static void one_tick(void) { LX_SEGMENT seg; seg.x1 = 0; seg.y1 = 0; seg.x2 = x; seg.y2 = 255; lx_PolySegment(xc,win,gc,1,&seg); x = (x + LINESTEP) & 255; if (x == 0) { pixx = (pixx + 1) & 7; lx_ChangeGC(xc,gc,LX_GCV_Foreground(pixv[pixx]),LX_GCV_END); } } static int tick(void *arg __attribute__((__unused__))) { TIME now; int d; now = now_time(); if (now < exp) { d = (exp - now) / 1000; if (d < 1) d = 1; return(d); } exp += INTERVAL; one_tick(); return(AIO_BLOCK_LOOP); } static void startup3(void *arg __attribute__((__unused__))) { LX_POINT pt[2]; gc = lx_CreateGC(xc,root,LX_GCV_Foreground(pixv[0]),LX_GCV_END); fprintf(f,"gc = %08lx\n",(unsigned long int)gc); pm = lx_CreatePixmap(xc,root,depth,2,2); fprintf(f,"pm = %08lx\n",(unsigned long int)pm); pt[0].x = 0; pt[0].y = 0; pt[1].x = 1; pt[1].y = 1; lx_PolyPoint(xc,pm,gc,LX_COORDMODE_Origin,2,&pt[0]); lx_ChangeGC(xc,gc,LX_GCV_Foreground(pixv[7]),LX_GCV_END); pt[0].x = 0; pt[0].y = 1; pt[1].x = 1; pt[1].y = 0; lx_PolyPoint(xc,pm,gc,LX_COORDMODE_Origin,2,&pt[0]); win = lx_CreateWindow( xc, root, 100, 100, 256, 256, 1, depth, LX_WCLASS_InputOutput, visual, LX_CWV_BackPixmap(pm), LX_CWV_BorderPixel(pixv[7]), LX_CWV_EventMask( LX_EM_KeyPress | LX_EM_KeyRelease | LX_EM_ButtonPress | LX_EM_ButtonRelease | LX_EM_EnterWindow | LX_EM_LeaveWindow | LX_EM_Exposure), LX_CWV_END ); fprintf(f,"win = %08lx\n",(unsigned long int)win); lx_MapWindow(xc,win); pixx = 7; x = 0; exp = now_time() + INTERVAL; aio_add_block(&tick,0); } static void got_hostlist(void *arg __attribute__((__unused__))) { int i; int n; LX_HOSTTYPE t; int s; const unsigned char *d; int j; fprintf(f,"got host list\n"); n = lx_hostlist_entries(hl); for (i=0;i>2,((d[1]&3)*256)+d[0]); continue; } else { fprintf(f," DECnet, size %d (expected 2)",s); } break; case LX_HOSTTYPE_Chaos: if (s == 2) { fprintf(f," Chaos, net %d host %d\n",d[1],d[0]); continue; } else { fprintf(f," DECnet, size %d (expected 2)",s); } break; case LX_HOSTTYPE_Other: fprintf(f," Other"); break; default: fprintf(f," unknown type %d",(int)t); break; } fprintf(f," [%d]:",s); for (j=0;jkeycode, e->seq, e->time, e->rootw, e->eventw, e->childw, e->rootx, e->rooty, e->eventx, e->eventy, e->state, e->samescreen); } static void key_release(const LX_EVENT_KeyRelease *e) { fprintf(f,"KeyRelease: kc=%d seq=%04x ts=%d rw=%08x ew=%08x cw=%08x rxy=(%d,%d) exy=(%d,%d) st=%08x ss=%d\n", e->keycode, e->seq, e->time, e->rootw, e->eventw, e->childw, e->rootx, e->rooty, e->eventx, e->eventy, e->state, e->samescreen); } static void handle_event(LX_CONN *c, LX_EVENT *e) { if (c != xc) abort(); switch (e->type) { case LX_EV_KeyPress: key_press(&e->u.KeyPress); break; case LX_EV_KeyRelease: key_release(&e->u.KeyRelease); break; case LX_EV_ButtonPress: fprintf(f,"ButtonPress"); break; case LX_EV_ButtonRelease: fprintf(f,"ButtonRelease"); break; case LX_EV_MotionNotify: fprintf(f,"MotionNotify"); break; case LX_EV_EnterNotify: fprintf(f,"EnterNotify"); break; case LX_EV_LeaveNotify: fprintf(f,"LeaveNotify"); break; case LX_EV_FocusIn: fprintf(f,"FocusIn"); break; case LX_EV_FocusOut: fprintf(f,"FocusOut"); break; case LX_EV_KeymapNotify: fprintf(f,"KeymapNotify"); break; case LX_EV_Expose: fprintf(f,"Expose"); break; case LX_EV_GraphicsExposure: fprintf(f,"GraphicsExposure"); break; case LX_EV_NoExposure: fprintf(f,"NoExposure"); break; case LX_EV_VisibilityNotify: fprintf(f,"VisibilityNotify"); break; case LX_EV_CreateNotify: fprintf(f,"CreateNotify"); break; case LX_EV_DestroyNotify: fprintf(f,"DestroyNotify"); break; case LX_EV_UnmapNotify: fprintf(f,"UnmapNotify"); break; case LX_EV_MapNotify: fprintf(f,"MapNotify"); break; case LX_EV_MapRequest: fprintf(f,"MapRequest"); break; case LX_EV_ReparentNotify: fprintf(f,"ReparentNotify"); break; case LX_EV_ConfigureNotify: fprintf(f,"ConfigureNotify"); break; case LX_EV_ConfigureRequest: fprintf(f,"ConfigureRequest"); break; case LX_EV_GravityNotify: fprintf(f,"GravityNotify"); break; case LX_EV_ResizeRequest: fprintf(f,"ResizeRequest"); break; case LX_EV_CirculateNotify: fprintf(f,"CirculateNotify"); break; case LX_EV_CirculateRequest: fprintf(f,"CirculateRequest"); break; case LX_EV_PropertyNotify: fprintf(f,"PropertyNotify"); break; case LX_EV_SelectionClear: fprintf(f,"SelectionClear"); break; case LX_EV_SelectionRequest: fprintf(f,"SelectionRequest"); break; case LX_EV_SelectionNotify: fprintf(f,"SelectionNotify"); break; case LX_EV_ColormapNotify: fprintf(f,"ColormapNotify"); break; case LX_EV_ClientMessage: fprintf(f,"ClientMessage"); break; case LX_EV_MappingNotify: fprintf(f,"MappingNotify"); break; default: fprintf(f,"Type %d",e->type); break; } fprintf(f," event\n"); } static void use_screen(int sno) { curscreen = sno; root = lx_root(xc,sno); depth = lx_root_depth(xc,sno); visual = lx_root_visual(xc,sno); } static void lwc_init(LWC *l) { l->b = 0; l->a = 0; l->l = 0; l->c = 0; } static int wtest_out(void *arg __attribute__((__unused__))) { return(aio_oq_nonempty(&out_oq)); } static void w_out(void *arg __attribute__((__unused__))) { int nw; nw = aio_oq_writev(&out_oq,1,-1); if (nw < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: stdout write: %s\n",__progname,strerror(errno)); exit(1); } aio_oq_dropdata(&out_oq,nw); } static int exit_check(void *arg __attribute__((__unused__))) { if (!(exiting & EXIT_UPDATE) || aio_oq_nonempty(&out_oq)) return(AIO_BLOCK_NIL); tcsetattr(0,TCSADRAIN|TCSASOFT,&old_tio); exit(0); } static void setup_exit(void) { exiting = EXIT_SET; aio_add_block(&exit_check,0); want_redraw = 1; } static CMD cmds[]; // forward static void cmd_help(CMD_IMPL_ARGS) { int i; CMD *c; int n; CMD *last; int any; if (! cmd) { fprintf(f,"Print help"); return; } n = 0; any = 0; last = 0; for (i=0;;i++) { c = &cmds[i]; if (last && (c->impl != last->impl)) { if (n > 6) putc('\n',f); putc('\t',f); (*last->impl)(0,0,0); putc('\n',f); n = 0; last = 0; } if (! c->cmd) break; if (c->cmdlen < arglen) continue; if (strncasecmp(c->cmd,arg,arglen)) continue; n += fprintf(f,"%s%s",n?", ":"",c->cmd); last = c; any = 1; } if (! any) { fprintf(f,"%.*s: no match (? for a list)\n",arglen,arg); } } static void cmd_exit(CMD_IMPL_ARGS) { if (! cmd) { fprintf(f,"Shut down the tester"); return; } if (arglen) { fprintf(f,"%s: takes no arguments\n",cmd->cmd); } else { setup_exit(); } } static void cmd_print(CMD_IMPL_ARGS) { if (! cmd) { fprintf(f,"Print some basic values"); return; } if (arglen) { fprintf(f,"%s: takes no arguments\n",cmd->cmd); } else { if (lx_nscreens(xc) > 1) { fprintf(f,"%d screens available, 0..%d\n",lx_nscreens(xc),lx_nscreens(xc)-1); } else { fprintf(f,"Only screen 0 available\n"); } fprintf(f,"Currently using screen %d (root %08lx, depth %d, visual %08lx)\n",curscreen,(ULI)root,depth,(ULI)visual); } } static void cmd_screen(CMD_IMPL_ARGS) { SLI v; char *e; if (! cmd) { fprintf(f,"Print or set the screen in use"); return; } if (arglen) { v = strtol(arg,&e,0); if (e == arg) { fprintf(f,"%s: argument must be a number\n",cmd->cmd); } else if (*e) { fprintf(f,"%s: junk after argument\n",cmd->cmd); } else if ((v < 0) || (v >= lx_nscreens(xc))) { if (lx_nscreens(xc) > 1) { fprintf(f,"%s: out of range 0..%d\n",cmd->cmd,lx_nscreens(xc)-1); } else { fprintf(f,"%s: only screen 0 exists on this display\n",cmd->cmd); } } else { use_screen(v); fprintf(f,"Now using screen %d\n",curscreen); } } else { fprintf(f,"Using screen %d\n",curscreen); } } /* * Multiple commands with identical implementations must be consecutive * in this array in order for cmd_help to recognize them as identical * and print them together. Help prints commands in the order they * appear here. Order does not otherwise matter. Alphabetic case * also does not matter. */ static CMD cmds[] = { { "help", &cmd_help }, { "exit", &cmd_exit }, { "quit", &cmd_exit }, { "print", &cmd_print }, { "screen", &cmd_screen }, { 0 } }; static void iedit_line_done(const char *text, int len) { int x; int cl; const char *cmd; int i; int found; int foundn; const CMD *c; for (x=0;(x= len) return; if (text[x] == '?') { for (x++;(xcmd) break; if (c->cmdlen < cl) continue; if (strncasecmp(cmd,c->cmd,cl)) continue; if (cl == c->cmdlen) { found = i; foundn = 1; break; } else { found = i; foundn ++; } } if (foundn == 0) { fprintf(f,"Unrecognized command `%.*s' (? for help)\n",cl,cmd); } else if (foundn > 1) { fprintf(f,"Ambiguous command `%.*s', could be:",cl,cmd); for (i=0;;i++) { c = &cmds[i]; if (! c->cmd) break; if (c->cmdlen < cl) continue; if (strncasecmp(cmd,c->cmd,cl)) continue; fprintf(f," %s",c->cmd); } fprintf(f,"\n"); } else { (*cmds[found].impl)(text+x,len-x,&cmds[found]); } } static void iedit_delete(int at, int n) { if ((at < 0) || (n < 0) || (at > il.l) || (n > il.l) || (at+n > il.l)) abort(); if (n < 1) return; if (at+n < il.l) bcopy(il.b+at+n,il.b+at,il.l-(at+n)); il.l -= n; } static void iedit_insert(int at, unsigned char c) { if ((at < 0) || (at > il.l)) abort(); if (il.l+1 > il.a) { il.a = il.l + 16; il.b = realloc(il.b,il.a); } if (at < il.l) bcopy(il.b+at,il.b+at+1,il.l-at); il.b[at] = c; il.l ++; } static void iedit_typed(unsigned char c) { switch (c) { case 1: // ^A il.c = 0; break; case 2: // ^B if (il.c > 0) il.c --; break; case 4: // ^D if (il.c < il.l) iedit_delete(il.c,1); break; case 5: // ^E il.c = il.l; break; case 6: // ^F if (il.c < il.l) il.c ++; break; case 8: // ^H case 127: // DEL if (il.c > 0) { il.c --; iedit_delete(il.c,1); } break; case 9: // ^I iedit_insert(il.c,9); il.c ++; break; case 10: // ^J case 13: // ^M iedit_insert(il.l,'\0'); il.l --; iedit_line_done(il.b,il.l); il.l = 0; il.c = 0; break; case 11: // ^K il.l = il.c; break; case 12: // ^L aio_oq_queue_point(&out_oq,"\n",1); dl.l = 0; dl.c = 0; break; case 20: // ^T if (il.c >= 2) { c = il.b[il.c-2]; il.b[il.c-2] = il.b[il.c-1]; il.b[il.c-1] = c; } break; case 21: // ^U il.l = 0; il.c = 0; break; case 26: // ^Z setup_exit(); break; default: if (((c >= 32) && (c <= 126)) || (c >= 160)) { iedit_insert(il.c,c); il.c ++; } break; } } static void r_in(void *arg __attribute__((__unused__))) { unsigned char buf[256]; int nr; int i; nr = read(0,&buf[0],sizeof(buf)); if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: stdin read: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { fprintf(stderr,"%s: stdin read got EOF\n",__progname); exit(1); } for (i=0;i sizeof(backspaces)) { aio_oq_queue_point(&out_oq,&backspaces[0],sizeof(backspaces)); n -= sizeof(backspaces); } if (n > 0) aio_oq_queue_point(&out_oq,&backspaces[0],n); } static void queue_spaces(int n) { while (n > sizeof(spaces)) { aio_oq_queue_point(&out_oq,&spaces[0],sizeof(spaces)); n -= sizeof(spaces); } if (n > 0) aio_oq_queue_point(&out_oq,&spaces[0],n); } static void il_to_dl(void) { int i; static ES s = ES_STATIC_INIT; unsigned char c; es_clear(&s); es_append_n(&s,"tester> ",8); dl.c = -1; for (i=0;i= 127) && (c < 160))) { es_append_1(&s,'^'); es_append_1(&s,c^64); } else { es_append_1(&s,c); } } if (i == il.c) dl.c = es_len(&s); i = es_len(&s); if (i > dl.a) { free(dl.b); dl.a = i; dl.b = malloc(i); } dl.l = es_len(&s); bcopy(es_buf(&s),dl.b,dl.l); if ((dl.c < 0) || (dl.c > dl.l)) abort(); } static void update_to_match(const unsigned char *text, int len, int curs) { int fd; int ld; int minl; if (pl.a < len) { pl.a = len; pl.b = realloc(pl.b,pl.a); } minl = (len < pl.l) ? len : pl.l; for (ld=minl-1;(ld>=0)&&(text[ld]==pl.b[ld]);ld--) ; if (ld >= 0) { ld ++; for (fd=0;(fd= minl) abort(); if (pl.c > fd) { queue_backspaces(pl.c-fd); } else if (pl.c < fd) { aio_oq_queue_copy(&out_oq,pl.b+pl.c,fd-pl.c); } pl.c = fd; aio_oq_queue_copy(&out_oq,text+fd,ld-fd); bcopy(text+fd,pl.b+fd,ld-fd); pl.c = ld; } if (len > pl.l) { aio_oq_queue_copy(&out_oq,text+pl.c,len-pl.c); pl.c = len; } else if (len < pl.l) { if (pl.c < len) { aio_oq_queue_copy(&out_oq,text+pl.c,len-pl.c); } else if (pl.c > len) { queue_backspaces(pl.c-len); } queue_spaces(pl.l-len); queue_backspaces(pl.l-len); pl.c = len; } pl.l = len; if (pl.c > curs) { queue_backspaces(pl.c-curs); } else if (pl.c < curs) { aio_oq_queue_copy(&out_oq,pl.b+pl.c,curs-pl.c); } pl.c = curs; } static int iedit_redraw(void *arg __attribute__((__unused__))) { PENDLINE *l; if (! want_redraw) return(AIO_BLOCK_NIL); want_redraw = 0; il_to_dl(); while ((l = pendlines.oldest)) { pendlines.oldest = l->nlink; if (l->olink) abort(); if (l->nlink) l->nlink->olink = 0; else pendlines.newest = 0; update_to_match(l->text,l->len,l->len); aio_oq_queue_point(&out_oq,"\n",1); pl.l = 0; pl.c = 0; free(l->text); free(l); } if (exiting) { update_to_match(0,0,0); exiting |= EXIT_UPDATE; } else { update_to_match(dl.b,dl.l,dl.c); } return(AIO_BLOCK_LOOP); } static void pendline_append(const char *s1, int l1, const char *s2, int l2) { PENDLINE *l; l = malloc(sizeof(PENDLINE)); l->len = l1 + l2; l->text = malloc(l->len); if (l1) bcopy(s1,l->text,l1); if (l2) bcopy(s2,l->text+l1,l2); l->nlink = 0; l->olink = pendlines.newest; if (l->olink) l->olink->nlink = l; else pendlines.oldest = l; pendlines.newest = l; want_redraw = 1; } static int output_wr(void *cookie __attribute__((__unused__)), const char *data, int len) { const char *nl; const char *tab; int o; if (len < 1) return(0); o = 0; while (o < len) { nl = memchr(data+o,'\n',len-o); tab = memchr(data+o,'\t',len-o); if (tab && nl && (tab > nl)) tab = 0; if (tab) { es_append_n(&oline,data+o,tab-(data+o)); do es_append_1(&oline,' '); while (es_len(&oline) & 7); o = (tab + 1) - data; } else if (nl) { if (es_len(&oline)) { pendline_append(es_buf(&oline),es_len(&oline),data+o,nl-(data+o)); } else { pendline_append(0,0,data+o,nl-(data+o)); } o = (nl + 1) - data; es_clear(&oline); line_appended = 1; } else { es_append_n(&oline,data+o,len-o); o = len; } } return(len); } static int flush_f(void *arg __attribute__((__unused__))) { line_appended = 0; fflush(f); return(line_appended?AIO_BLOCK_LOOP:AIO_BLOCK_NIL); } static LX_X_ERR_ACTION err_X(LX_CONN *c, const LX_X_ERR *e) { if (c != xc) abort(); printf("X error\n"); return((*prev_X)(c,e)); } static void err_lib(LX_CONN *c, const LX_LIB_ERR *e) { if (c != xc) abort(); printf("Library error\n"); (*prev_lib)(c,e); } static void startup2(LX_CONN *newc, void *arg __attribute__((__unused__))) { int i; LX_OP *op; xc = newc; lx_set_event_handler(xc,&handle_event); prev_X = lx_err_set_X(xc,&err_X); prev_lib = lx_err_set_lib(xc,&err_lib); nbio(0); nbio(1); use_screen(lx_default_screen(xc)); aio_oq_init(&out_oq); lwc_init(&il); lwc_init(&dl); tcgetattr(0,&old_tio); our_tio = old_tio; our_tio.c_cc[VMIN] = 1; our_tio.c_cc[VTIME] = 0; our_tio.c_oflag = OPOST | ONLCR; our_tio.c_lflag = 0; tcsetattr(0,TCSADRAIN|TCSASOFT,&our_tio); iid = aio_add_poll(0,&aio_rwtest_always,&aio_rwtest_never,&r_in,0,0); oid = aio_add_poll(1,&aio_rwtest_never,&wtest_out,0,&w_out,0); redrawid = aio_add_block(&iedit_redraw,0); pendlines.oldest = 0; pendlines.newest = 0; f = funopen(0,0,&output_wr,0,0); want_redraw = 1; flushid = aio_add_block(&flush_f,0); fprintf(f,"Started\n"); lx_Bell(xc,0); cmap = lx_CreateColormap(xc,visual,root,LX_AllocNone); printf("cmap = %08lx\n",(unsigned long int)cmap); op = 0; for (i=7;i>=0;i--) { if (op) lx_op_drop(op); op = lx_AllocColor(xc,cmap,(i&1)*65535,((i>>1)&1)*65535,((i>>2)&1)*65535,&pixv[i],0,0,0); } lx_op_callback(op,&startup3,0,0); lx_op_callback(lx_ListHosts(xc,&accctl,&hl),&got_hostlist,0,0); lx_op_callback(lx_ListExtensions(xc,&exts),&got_extlist,0,0); #if 0 GetWindowAttributes GetGeometry QueryTree InternAtom GetAtomName GetProperty ListProperties GetSelectionOwner GrabPointer GrabKeyboard QueryPointer QueryPointer_status GetMotionEvents TranslateCoordinates GetInputFocus QueryKeymap QueryFont QueryTextExtents8 QueryTextExtents16 ListFonts ListFontsWithInfo GetFontPath GetImage ListInstalledColormaps AllocNamedColor AllocNamedColor_rgb AllocColorCells AllocColorPlanes QueryColors LookupColor LookupColor_rgb QueryBestSize QueryExtension ListExtensions GetKeyboardMapping GetKeyboardControl GetPointerControl GetScreenSaver ListHosts GetPointerMapping GetModifierMapping #endif } static int startup(void *arg __attribute__((__unused__))) { aio_remove_block(bid); lx_open(0,0,&startup2,0,0,getenv("TESTER_DEBUG")?LX_OPENF_DEBUG:0); return(AIO_BLOCK_LOOP); } static void debug_wait(void) { volatile int done; done = 0; while (! done) poll(0,0,100); } int main(void); int main(void) { if (getenv("TESTER_DEBUG_WAIT")) debug_wait(); aio_poll_init(); bid = aio_add_block(&startup,0); aio_event_loop(); return(0); }