// Copyright status: this file is in the public domain. #include #include #include #include #include #include "lx.h" #include "internal.h" // This file knows we always use big-endian CARD16 r_card16(const unsigned char *pp) { return((pp[0]*256U)+pp[1]); } int r_int16(const unsigned char *pp) { CARD16 v; v = r_card16(pp); if (v & 0x8000) return((int)((int)(v&0x7fff)-0x8000)); else return(v); } void w_card16(unsigned char *pp, CARD16 val) { if (val > 65535) lx_abort(); pp[0] = val >> 8; pp[1] = val & 0xff; } // Always BE; because we always run BE, same as w_card16. void w_char2b(unsigned char *pp, unsigned int val) { if (val > 65535) lx_abort(); pp[0] = val >> 8; pp[1] = val & 0xff; } void w_int16(unsigned char *pp, INT16 val) { /* * I would normally put a firewall * if ((val < -32768) || (val > 32767)) lx_abort(); * here, but it draws "warning: comparison is always 0 due to limited * range of data type". I'm not sure what to do about that. The * similar tests in w_card16 and w_card32 don't, for reasons I don't * understand, and I like this kind of firewalling. I'm skipping it * for now, to make it build until I figure out what the right thing * to do is. */ pp[0] = (CARD16)val >> 8; pp[1] = (CARD16)val & 0xff; } CARD32 r_card32(const unsigned char *pp) { return((pp[0]*16777216U)+(pp[1]*65536U)+(pp[2]*256U)+pp[3]); } void w_card32(unsigned char *pp, CARD32 val) { if (val > 0xffffffff) lx_abort(); pp[0] = val >> 24 ; pp[1] = (val >> 16) & 0xff; pp[2] = (val >> 8) & 0xff; pp[3] = val & 0xff; } void w_int32(unsigned char *pp, int val) { pp[0] = (CARD32)val >> 24; pp[1] = ((CARD32)val >> 16) & 0xff; pp[2] = ((CARD32)val >> 8) & 0xff; pp[3] = (CARD32)val & 0xff; } int r_int32(const unsigned char *pp) { CARD32 v; v = r_card32(pp); if (v & 0x80000000) return((int)((int)(v&0x7fffffff)-0x80000000)); else return(v); } /* * Always uses big-endian. Because we always run big-endian anyway, * this is identical to w_card32. */ void w_card32be(unsigned char *pp, CARD32 val) { if (val > 0xffffffff) lx_abort(); pp[0] = val >> 24 ; pp[1] = (val >> 16) & 0xff; pp[2] = (val >> 8) & 0xff; pp[3] = val & 0xff; } static void clear_seqsync(void *xcv) { ((LX_CONN *)xcv)->flags &= ~XCF_SEQSYNC; } static void post_request(LX_CONN *xc) { xc->next_oserial ++; if ( ((CARD32)(xc->next_oserial-xc->last_iserial) > 0x2000) && !(xc->flags & XCF_SEQSYNC) ) { xc->flags |= XCF_SEQSYNC; lx_op_callback(lx_GetInputFocus(xc,0,0),&clear_seqsync,xc,0); } } void lx__send_req(LX_CONN *xc, const unsigned char *req, int len) { if (len < 0) len = r_card16(req+2) * 4; if (aio_oq_queue_copy(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } post_request(xc); } void lx__send_req_free(LX_CONN *xc, unsigned char *req, int len) { if (len < 0) len = r_card16(req+2) * 4; if (aio_oq_queue_free(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } post_request(xc); } void lx__just_send(LX_CONN *xc, const unsigned char *req, int len) { if (aio_oq_queue_copy(&xc->oq,req,len) < 0) lx__nomem_fail(xc); } void lx__send_padding(LX_CONN *xc, unsigned int len) { if ((len & 3) && (aio_oq_queue_point(&xc->oq,"\0\0\0",4-(len&3)) < 0)) lx__nomem_fail(xc); } static void op_link(LX_OP *op) { LX_OP **rootf; LX_OP **rootb; if (op->flags & LXOF_LINK) lx_abort(); rootf = (op->flags & LXOF_INTL) ? &op->conn->intlf : &op->conn->pendf; rootb = (op->flags & LXOF_INTL) ? &op->conn->intlb : &op->conn->pendb; op->flink = 0; op->blink = *rootb; if (op->blink) op->blink->flink = op; else *rootf = op; *rootb = op; op->flags |= LXOF_LINK; } static void op_unlink(LX_OP *op) { LX_OP **rootf; LX_OP **rootb; if (! (op->flags & LXOF_LINK)) lx_abort(); rootf = (op->flags & LXOF_INTL) ? &op->conn->intlf : &op->conn->pendf; rootb = (op->flags & LXOF_INTL) ? &op->conn->intlb : &op->conn->pendb; if (op->flink) op->flink->blink = op->blink; else *rootb = op->blink; if (op->blink) op->blink->flink = op->flink; else *rootf = op->flink; op->flags &= ~LXOF_LINK; } static void op_free(LX_OP *op) { free(op); } void lx_op_callback(LX_OP *op, void (*cb)(void *), void *arg, unsigned int flags) { if (! op) return; if (op->flags & LXOF_DROP) lx_abort(); if (op->flags & LXOF_DONE) { (*cb)(arg); if (! (flags & LX_OP_KEEP)) lx_op_drop(op); return; } if (flags & LX_OP_KEEP) op->flags |= LXOF_KEEP; else op->flags &= ~LXOF_KEEP; op->usercb = cb; op->usercbarg = arg; } int lx_op_test(LX_OP *op) { if (!op || (op->flags & LXOF_DROP)) lx_abort(); return((op->flags&LXOF_DONE)?1:0); } void lx_op_drop(LX_OP *op) { if (! op) return; if (op->flags & LXOF_DROP) lx_abort(); op->flags = (op->flags & ~LXOF_KEEP) | LXOF_DROP; if (op->flags & LXOF_DONE) { op_unlink(op); op_free(op); } } void lx_op_set_udata(LX_OP *op, void *ud) { if (op->flags & LXOF_DROP) lx_abort(); op->udata = ud; } void *lx_op_udata(LX_OP *op) { if (op->flags & LXOF_DROP) lx_abort(); return(op->udata); } LX_OP_ERR_SETTING lx_op_err(LX_OP *op, int (*fn)(LX_CONN *, LX_OP *, const LX_X_ERR *, void *), void *arg) { LX_OP_ERR_SETTING old; old.fn = op->uerr; old.arg = op->uerrarg; op->uerr = fn; op->uerrarg = arg; return(old); } LX_OP_ERR_SETTING lx_op_err_setting(LX_OP *op, LX_OP_ERR_SETTING new) { LX_OP_ERR_SETTING old; old.fn = op->uerr; old.arg = op->uerrarg; op->uerr = new.fn; op->uerrarg = new.arg; return(old); } void lx__protoerr_v(LX_CONN *xc, const char *fmt, va_list ap) { char *s; int l; LX_LIB_ERR e; l = vasprintf(&s,fmt,ap); if (xc->flags & XCF_OPENING) { if (l < 0) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); } else { e.type = LX_LE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->os->err)(&e,xc->os->cbarg); } } else { if (l < 0) { (*xc->lib_err)(xc,&lx__nomem_err); } else { e.type = LX_LE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->lib_err)(xc,&e); } } xc->flags |= XCF_FAIL; } void lx__protoerr(LX_CONN *xc, const char *fmt, ...) { va_list ap; va_start(ap,fmt); lx__protoerr_v(xc,fmt,ap); va_end(ap); } static int op_no_errs( LX_CONN *xc __attribute__((__unused__)), const LX_X_ERR *err __attribute__((__unused__)), void *arg __attribute__((__unused__)) ) { return(0); } static LX_OP *new_lx_op(LX_CONN *c, CARD32 ser) { LX_OP *op; op = malloc(sizeof(LX_OP)); if (op) { op->conn = c; op->serial = ser; op->usercb = 0; op->usercbarg = 0; op->done = 0; op->err = &op_no_errs; op->uerr = 0; op->reqarg = 0; op->flags = LXOF_KEEP; op->udata = 0; } return(op); } LX_OP *lx__expect_reply( LX_CONN *xc, const unsigned char *req, int reqlen, void (*done)(LX_CONN *, const unsigned char *, void *), void *arg ) { LX_OP *op; CARD32 ser; ser = xc->next_oserial; lx__send_req(xc,req,reqlen); if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,ser); if (op == 0) { lx__nomem_fail(xc); return(0); } op_link(op); op->done = done; op->reqarg = arg; return(op); } LX_OP *lx__predone_op(LX_CONN *xc) { LX_OP *op; if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,0); if (op == 0) { lx__nomem_fail(xc); return(0); } op->flags |= LXOF_DONE | LXOF_INTL; op_link(op); return(op); } LX_OP *lx__internal_op(LX_CONN *xc) { LX_OP *op; if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,0); if (op == 0) { lx__nomem_fail(xc); return(0); } op->flags |= LXOF_INTL; op_link(op); return(op); } static int rb_room(LX_CONN *xc, int n) { if (xc->rba < n) { unsigned char *new; new = realloc(xc->rb,n); if (new) { xc->rb = new; xc->rba = n; return(0); } return(1); } return(0); } void lx__proto_newpkt(LX_CONN *xc) { if (rb_room(xc,32)) { lx__nomem_fail(xc); return; } xc->rbl = 0; xc->rbw = 32; } void lx__dumphex(const char *tag, const unsigned char *data, int len) { int i; if (tag) printf("%s\n",tag); for (i=0;iu.field.seq, \ (unsigned int)e->u.field.opc_major, \ (unsigned int)e->u.field.opc_minor); \ break; #define VAL(name,field,valfield,label) \ case LX_XE_##name: \ fprintf(stderr,"X error: Bad%s, sequence %04x opcode %u.%u: bad %s %08lx\n", \ #name, \ (unsigned int)e->u.field.seq, \ (unsigned int)e->u.field.opc_major, \ (unsigned int)e->u.field.opc_minor, \ label, (unsigned long int)e->u.field.valfield); \ break; switch (e->type) { NOVAL(Request,request) VAL(Value,value,value,"value") VAL(Window,window,id,"window id") VAL(Pixmap,pixmap,id,"pixmap id") VAL(Atom,atom,id,"atom id") VAL(Cursor,cursor,id,"cursor id") VAL(Font,font,id,"font id") NOVAL(Match,match) VAL(Drawable,drawable,id,"drawable id") NOVAL(Access,access) NOVAL(Alloc,alloc) VAL(Colormap,colormap,id,"colormap id") VAL(GContext,gcontext,id,"GC id") VAL(IDChoice,idchoice,id,"id") NOVAL(Name,name) NOVAL(Length,length) NOVAL(Implementation,implementation) case LX_XE_Other: fprintf(stderr,"X error: type %u, sequence %04x, opcode %u.%u\n", e->u.other.type,e->u.other.seq,e->u.other.opc_major,e->u.other.opc_minor); break; } #undef NOVAL #undef VAL exit(1); } static void advance_iseq(LX_CONN *xc, CARD16 protoseq) { CARD32 prev; prev = xc->last_iserial; if (protoseq > (CARD16)(xc->last_iserial&0xffff)) { xc->last_iserial = (xc->last_iserial & 0xffff0000) | protoseq; } else if (protoseq < (CARD16)(xc->last_iserial&0xffff)) { xc->last_iserial = ((xc->last_iserial & 0xffff0000) + 0x10000) | protoseq; } if (xc->pendf && ((CARD32)(xc->pendf->serial-prev) < (CARD32)(xc->last_iserial-prev))) { lx__protoerr(xc,"missing reply, sequence %08lx",(unsigned long int)xc->pendf->serial); } } void lx__op_finished(LX_OP *op) { if (op->flags & LXOF_DROP) { op_unlink(op); op_free(op); } else { if (op->usercb) (*op->usercb)(op->usercbarg); op->flags |= LXOF_DONE; if (! (op->flags & LXOF_KEEP)) { op_unlink(op); op_free(op); } } } // XXX Figure out how to cancel LX_OP for an erroring request! static void process_error(LX_CONN *xc) { LX_X_ERR e; CARD16 seq; CARD32 val; CARD16 minor; CARD8 major; if (xc->rbl != 32) lx_abort(); seq = r_card16(xc->rb+2); advance_iseq(xc,seq); if (xc->flags & XCF_FAIL) return; val = r_card32(xc->rb+4); minor = r_card16(xc->rb+8); major = xc->rb[10]; #define NOVAL(code,tn,field) \ case code: \ e.type = LX_XE_##tn; \ e.u.field.seq = seq; \ e.u.field.opc_major = major; \ e.u.field.opc_minor = minor; \ break; #define VAL(code,tn,field,valfield) \ case code: \ e.type = LX_XE_##tn; \ e.u.field.seq = seq; \ e.u.field.valfield = val; \ e.u.field.opc_major = major; \ e.u.field.opc_minor = minor; \ break; switch (xc->rb[1]) { NOVAL(1,Request,request) VAL(2,Value,value,value) VAL(3,Window,window,id) VAL(4,Pixmap,pixmap,id) VAL(5,Atom,atom,id) VAL(6,Cursor,cursor,id) VAL(7,Font,font,id) NOVAL(8,Match,match) VAL(9,Drawable,drawable,id) NOVAL(10,Access,access) NOVAL(11,Alloc,alloc) VAL(12,Colormap,colormap,id) VAL(13,GContext,gcontext,id) VAL(14,IDChoice,idchoice,id) NOVAL(15,Name,name) NOVAL(16,Length,length) NOVAL(17,Implementation,implementation) default: e.type = LX_XE_Other; e.u.other.type = xc->rb[1]; e.u.other.seq = seq; e.u.other.opc_major = major; e.u.other.opc_minor = minor; break; } #undef NOVAL #undef VAL if ( xc->pendf && (xc->last_iserial == xc->pendf->serial) && ( (*xc->pendf->err)(xc,&e,xc->pendf->reqarg) || ( xc->pendf->uerr && (*xc->pendf->uerr)(xc,xc->pendf,&e,xc->pendf->uerrarg) ) ) ) { lx__op_finished(xc->pendf); return; } switch ((*xc->x_err)(xc,&e)) { case LX_X_ERR_IGNORE: return; break; case LX_X_ERR_CRASH: x_err_crash(&e); break; default: fprintf(stderr,"X library: invalid application X error handler return code\n"); exit(1); break; } } static void process_reply(LX_CONN *xc) { LX_OP *op; CARD16 seq; if (xc->rbl < 32) lx_abort(); op = xc->pendf; if (! op) { lx__protoerr(xc,"response when nothing expected"); return; } seq = r_card16(&xc->rb[2]); advance_iseq(xc,seq); if (xc->flags & XCF_FAIL) return; if (op->serial != xc->last_iserial) { lx__protoerr(xc,"response to %04lx -> %08lx when expecting (%04lx)%04lx\n", (unsigned long int)seq, (unsigned long int)xc->last_iserial, (unsigned long int)(op->serial>>16), (unsigned long int)(op->serial&0xffff) ); return; } (*op->done)(xc,xc->rb,op->reqarg); if (op->flags & LXOF_MORE) { op->flags &= ~LXOF_MORE; return; } lx__op_finished(op); } void lx__more_replies(LX_CONN *xc) { xc->pendf->flags |= LXOF_MORE; } int lx__proto_pkt(LX_CONN *xc, const void *data, int len) { const unsigned char *dp; int n; int o; CARD32 l; if (xc->flags & XCF_FAIL) return(len); dp = data; o = 0; while (o < len) { n = xc->rbw - xc->rbl; if (n < 1) lx_abort(); if (n > len-o) { bcopy(dp+o,xc->rb+xc->rbl,len-o); xc->rbl += len - o; break; } bcopy(dp+o,xc->rb+xc->rbl,n); xc->rbl += n; o += n; switch (xc->rb[0]) { case 0: // Error if (xc->rbl != 32) lx_abort(); process_error(xc); break; case 1: // Reply l = r_card32(xc->rb+4); if (l > 0x3fffffff-8) lx__protoerr(xc,"insanely long reply (length field %lu)\n",(unsigned long int)l); xc->rbw = (l * 4) + 32; if (rb_room(xc,xc->rbw)) { lx__nomem_fail(xc); return(len); } if (xc->rbl < xc->rbw) continue; process_reply(xc); break; default: // Event if (xc->rbl != 32) lx_abort(); lx__process_event(xc); break; } lx__proto_newpkt(xc); } return(len); } void lx__nomem_fail(LX_CONN *xc) { (*xc->lib_err)(xc,&lx__nomem_err); xc->flags |= XCF_FAIL; } void lx__bad_call(LX_CONN *xc, const char *call) { LX_LIB_ERR e; e.type = LX_LE_BAD_CALL; e.u.bad_call.fn = call; (*xc->lib_err)(xc,&e); } void lx__grab_charinfo(LX_CHARINFO *ci, const unsigned char *data) { ci->lbearing = r_int16(data); ci->rbearing = r_int16(data+2); ci->width = r_int16(data+4); ci->ascent = r_int16(data+6); ci->descent = r_int16(data+8); ci->attr = r_card16(data+10); }