/* * Multi-mterm front end. * * Manages multiple mterms in a single window-manager window. * * The effect is rather like window(1), only the interface is natively * X instead of termcap-based. * * We set up a container window and manage multiple mterm instances * within it. Each mterm instance is run with -blanket and * -control-fd, -blanket to make it fit a window we manage within * the container window and -control-fd so we can get keystrokes to it * properly. * * There is a bit of an issue with event distribution. If we don't do * anything, keystroke events go to the mterm window with the pointer * in it, which is not what we want. If we use a blanket InputOnly * window, we can capture keystrokes, but that also prevents pointer * events from going to the mterms, which is also not what we want. * We could set the focus window to the container window for the mterm * with focus, but that requires our tracking when we have focus, or * we can steal focus when we shouldn't. * * The best alternative I've found is something I hadn't formerly come * up with any use for: a passive grab on AnyKey/AnyModifier on the * container window. This causes all keystrokes to go to us, but * doesn't interfere with pointer events. We could ignore the * resulting grabs, because they automatically terminate when the * keyboard goes idle; for better behaviour when a key (probably a * modifier) is held down, we AllowEvents to thaw the keybaord upon * receiving a KeyPress. * * To toggle typing to mmterm, instead of a sub-mterm, type * control-shift-escape. */ #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; static const char *mmdisplayarg = 0; static const char *mdisplayarg = 0; static const char *fontarg = 0; static const char *geometryarg = 0; static const char *fgarg = "white"; static const char *bgarg = "black"; static const char *bcarg = "white"; static const char *ecarg = "#800080008000"; static const char *bwarg = "1"; static const char *bmarg = "1"; typedef struct callonce CALLONCE; typedef struct geom GEOM; typedef struct col COL; typedef struct em EM; typedef struct emstart EMSTART; struct emstart { EM *e; int go; int xp; void (*started)(EM *, int); } ; struct em { EM *flink; EM *blink; unsigned int flags; #define EMF_DEAD 0x00000001 #define EMF_LINKED 0x00000002 int cx; int cy; int cw; int ch; int px; int py; int pw; int ph; LX_XID pwin; pid_t kid; int ctlfd; AIO_OQ ctloq; int id; } ; struct col { const char *str; LX_RGB rgb; unsigned int pix; int good; } ; struct geom { unsigned int x; unsigned int y; int w; int h; unsigned int flags; #define GF_POS 0x00000001 #define GF_X_NEG 0x00000002 #define GF_Y_NEG 0x00000004 } ; struct callonce { void (*fn)(void *); void *arg; int id; } ; static LX_CONN *xc; static int scrno; static LX_XID root; static int depth; static LX_XID visual; static LX_XID cmap; static LX_XID fontid; static LX_FONTINFO *fontinfo; static LX_SGC gc; static LX_SGC gc1; static COL fg_col; static COL bg_col; static COL bc_col; static COL ec_col; static int startup_pending; static int startid; static EM *ems; static LX_XID topwin; static LX_XID contwin; static GEOM geom; static int bordermargin; static int borderwidth; static int charwidth; static int baselineskip; static LX_XID cursor; static unsigned int scrw; static unsigned int scrh; static EM *focus; static int focusid; static int help_want; static int help_up; static LX_XID help_pm; static LX_XID help_win; static int help_id; static const signed char cursrects_0_0[] = { 1,1,1,16, 1,1,16,1, 2,2,14,1, 2,2,1,14, -1 }; static const signed char cursrects_1_0[] = { 1,1,1,6, 2,2,1,4, 1,1,16,2, 15,1,1,4, 16,1,1,6, -1 }; static const signed char cursrects_2_0[] = { 1,1,16,1, 2,2,15,1, 15,1,1,15, 16,1,1,16, -1 }; static const signed char cursrects_0_1[] = { 1,1,6,1, 2,2,4,1, 1,1,2,16, 2,15,4,1, 1,16,6,1, -1 }; static const signed char cursrects_1_1[] = { 1,1,16,2, 1,1,2,16, 15,1,2,16, 1,15,16,2, -1 }; static const signed char cursrects_2_1[] = { 11,1,6,1, 12,2,5,1, 15,1,2,16, 12,15,5,1, 11,16,6,1, -1 }; static const signed char cursrects_0_2[] = { 1,1,1,16, 2,2,1,15, 1,15,15,1, 1,16,16,1, -1 }; static const signed char cursrects_1_2[] = { 1,11,1,6, 2,12,1,5, 1,15,16,2, 15,12,1,5, 16,11,1,6, -1 }; static const signed char cursrects_2_2[] = { 1,16,16,1, 2,15,15,1, 15,2,1,15, 16,1,1,16, -1 }; static LX_XID resize_cursors[3][3]; static const signed char *resize_cursdata[3][3] = { { &cursrects_0_0[0], &cursrects_0_1[0], &cursrects_0_2[0] }, { &cursrects_1_0[0], &cursrects_1_1[0], &cursrects_1_2[0] }, { &cursrects_2_0[0], &cursrects_2_1[0], &cursrects_2_2[0] } }; static LX_XID resize_windows[3][3]; static LX_XID resize_parent; static EM *resizing; static void (*cancel_resize)(void); static LX_OP *resize_op; static LX_XID resize_grab_status; static int *resize_grabflag; static const char *helplines[] = { "This is mmterm, a multi-mterm container.", "", "Ctl-Shift-ESC toggles this help screen", "Ctl-Shift-N starts a new emulator", "Ctl-Shift-R starts resizing the current emulator", 0 }; static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: stray argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); mmdisplayarg = av[skip]; continue; } if (!strcmp(*av,"-mmdisplay")) { WANTARG(); mmdisplayarg = av[skip]; continue; } if (!strcmp(*av,"-mdisplay")) { WANTARG(); mdisplayarg = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryarg = av[skip]; continue; } if (!strcmp(*av,"-font")) { WANTARG(); fontarg = av[skip]; continue; } if (!strcmp(*av,"-fg") || !strcmp(*av,"-foreground")) { WANTARG(); fgarg = av[skip]; continue; } if (!strcmp(*av,"-bg") || !strcmp(*av,"-background")) { WANTARG(); bgarg = av[skip]; continue; } if (!strcmp(*av,"-bc") || !strcmp(*av,"-bordercolour") || !strcmp(*av,"-bordercolor")) { WANTARG(); bcarg = av[skip]; continue; } if (!strcmp(*av,"-ec") || !strcmp(*av,"-emptycolour") || !strcmp(*av,"-emptycolor")) { WANTARG(); ecarg = av[skip]; continue; } if (!strcmp(*av,"-bw") || !strcmp(*av,"-borderwidth")) { WANTARG(); bwarg = av[skip]; continue; } if (!strcmp(*av,"-bm") || !strcmp(*av,"-bordermargin")) { WANTARG(); bmarg = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void setup_preopen(void) { borderwidth = atoi(bwarg); if (borderwidth < 0) borderwidth = 0; bordermargin = atoi(bmarg); if (bordermargin < 0) bordermargin = 0; } static int call_once_call(void *cov) { CALLONCE *co; void (*fn)(void *); void *arg; co = cov; fn = co->fn; arg = co->arg; aio_remove_block(co->id); free(co); (*fn)(arg); return(AIO_BLOCK_LOOP); } static void call_once(void (*fn)(void *), void *arg) { CALLONCE *co; co = malloc(sizeof(CALLONCE)); co->fn = fn; co->arg = arg; co->id = aio_add_block(&call_once_call,co); } static GEOM parse_geometry(const char *arg) { int v[4]; char c[2][2]; int n1; int n2; n1 = -1; n2 = -1; sscanf(arg,"%dx%d%1[+-]%d%1[+-]%d %n%*c%n",&v[0],&v[1],&c[0][0],&v[2],&c[1][0],&v[3],&n1,&n2); if (n2 >= 0) { fprintf(stderr,"%s: geometry spec has trailing junk: %s\n",__progname,arg); exit(1); } if (n1 >= 0) { if ((v[0] <= 0) || (v[1] <= 0)) { fprintf(stderr,"%s: geometry spec has nonpositive width or height: %s\n",__progname,arg); exit(1); } return((GEOM){.w=v[0],.h=v[1],.x=v[2],.y=v[3],.flags=GF_POS|((c[0][0]=='-')?GF_X_NEG:0)|((c[1][0]=='-')?GF_Y_NEG:0)}); } sscanf(arg,"%dx%d %n%*c%n",&v[0],&v[1],&n1,&n2); if (n2 >= 0) { fprintf(stderr,"%s: geometry spec has trailing junk: %s\n",__progname,arg); exit(1); } if (n1 >= 0) { if ((v[0] <= 0) || (v[1] <= 0)) { fprintf(stderr,"%s: geometry spec has nonpositive width or height: %s\n",__progname,arg); exit(1); } return((GEOM){.w=v[0],.h=v[1],.x=0,.y=0,.flags=0}); } fprintf(stderr,"%s: can't parse geometry spec: %s\n",__progname,arg); exit(1); } static void em_unlink(EM *e) { if ((e->flags & (EMF_DEAD|EMF_LINKED)) != EMF_LINKED) abort(); if (e->flink) e->flink->blink = e->blink; if (e->blink) e->blink->flink = e->flink; else ems = e->flink; e->flags &= ~EMF_LINKED; } static void em_link_head(EM *e) { if (e->flags & (EMF_DEAD|EMF_LINKED)) abort(); e->flink = ems; e->blink = 0; if (ems) ems->blink = e; ems = e; e->flags |= EMF_LINKED; } static void em_win_created(void *esv) { EMSTART *es; EM *e; int err; int n; void (*started)(EM *, int); es = esv; e = es->e; write(es->go,"\1",1); close(es->go); n = recv(es->xp,&err,sizeof(err),MSG_WAITALL); if (n < 0) { fprintf(stderr,"%s: exec mterm: protocol error: recv: %s\n",__progname,strerror(errno)); exit(1); } close(es->xp); switch (n) { case 0: em_link_head(e); started = es->started; free(es); (*started)(e,1); break; case sizeof(int): fprintf(stderr,"%s: exec mterm: %s\n",__progname,strerror(err)); close(e->ctlfd); lx_DestroyWindow(xc,e->pwin); aio_oq_flush(&e->ctloq); aio_remove_poll(e->id); free(es); (*started)(e,0); free(e); break; default: fprintf(stderr,"%s: exec mterm: protocol error: read %d, expected 0 or %d\n",__progname,n,(int)sizeof(int)); exit(1); break; } } static int destroy_em(void *ev) { EM *e; e = ev; if ((e->flags & (EMF_DEAD|EMF_LINKED)) != EMF_DEAD) abort(); aio_remove_block(e->id); lx_DestroyWindow(xc,e->pwin); close(e->ctlfd); aio_oq_flush(&e->ctloq); free(e); return(AIO_BLOCK_LOOP); } static void stop_resizing(void) { (*cancel_resize)(); resizing = 0; } static void close_em(EM *e) { if (e->flags & EMF_DEAD) return; em_unlink(e); e->flags |= EMF_DEAD; aio_remove_poll(e->id); e->id = aio_add_block(&destroy_em,e); if (focus == e) focus = 0; if (resizing == e) stop_resizing(); } static int wtest_em_ctl(void *ev) { EM *e; e = ev; return((e->flags&EMF_DEAD)?0:aio_oq_nonempty(&e->ctloq)); } static int em_ctl_send(void *ev, const struct iovec *iov, int niov) { EM *e; struct msghdr mh; e = ev; mh.msg_name = 0; mh.msg_namelen = 0; // XXX const poisoning botch *(const struct iovec **)&mh.msg_iov = iov; mh.msg_iovlen = niov; mh.msg_control = 0; mh.msg_controllen = 0; mh.msg_flags = 0; return(sendmsg(e->ctlfd,&mh,MSG_NOSIGNAL)); } static void rd_em_ctl(void *ev) { // If the ctlfd shows readable, mterm must have died, so... close_em(ev); } static void wr_em_ctl(void *ev) { EM *e; int w; e = ev; if (e->flags & EMF_DEAD) return; w = aio_oq_custom_writev(&e->ctloq,-1,&em_ctl_send,e); if (w < 0) { if (w == AIO_WRITEV_ERROR) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: emulator write: %s\n",__progname,strerror(errno)); close_em(e); return; } fprintf(stderr,"%s: aio_oq_writev returned impossible %d\n",__progname,w); } aio_oq_dropdata(&e->ctloq,w); } static EM *start_emulator(int x, int y, int w, int h, void (*started)(EM *, int)) { int px; int py; int pw; int ph; EM *e; EMSTART *es; int ctl[2]; int go[2]; int xp[2]; char cmd; px = (x * charwidth) + bordermargin; py = (y * baselineskip) + bordermargin; pw = w * charwidth; ph = h * baselineskip; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&ctl[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } if (socketpair(AF_LOCAL,SOCK_STREAM,0,&xp[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } if (pipe(&go[0]) < 0) { fprintf(stderr,"%s: pipe: %s\n",__progname,strerror(errno)); exit(1); } e = malloc(sizeof(EM)); es = malloc(sizeof(EMSTART)); e->flags = 0; e->cx = x; e->cy = y; e->cw = w; e->ch = h; e->px = px; e->py = py; e->pw = pw; e->ph = ph; e->pwin = lx_CreateWindow_va(xc,contwin,px,py,pw,ph,0,depth,LX_WCLASS_InputOutput,visual, LX_CWV_BackPixmap(LX_PIXMAP_None), LX_CWV_Colormap(cmap), LX_CWV_END); lx_MapWindow(xc,e->pwin); fflush(0); e->kid = fork(); if (e->kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } fcntl(xp[1],F_SETFD,1); if (e->kid == 0) { char *winarg; char *fdarg; char *fgarg; char *bgarg; const char *args[18]; int argx; int err; lx_drop(xc); asprintf(&winarg,"%#lx",(unsigned long int)e->pwin); asprintf(&fdarg,"%d",ctl[1]); asprintf(&fgarg,"#%04x%04x%04x",fg_col.rgb.r,fg_col.rgb.g,fg_col.rgb.b); asprintf(&bgarg,"#%04x%04x%04x",bg_col.rgb.r,bg_col.rgb.g,bg_col.rgb.b); close(xp[0]); close(ctl[0]); close(go[1]); if ((read(go[0],&cmd,1) != 1) || (cmd != 1)) _exit(0); close(go[0]); argx = 0; args[argx++] = "mterm"; args[argx++] = "-blanket"; args[argx++] = winarg; args[argx++] = "-control-fd"; args[argx++] = fdarg; args[argx++] = "-fg"; args[argx++] = fgarg; args[argx++] = "-bg"; args[argx++] = bgarg; args[argx++] = "-bm"; args[argx++] = "0"; args[argx++] = "-bw"; args[argx++] = "0"; if (mdisplayarg) { args[argx++] = "-display"; args[argx++] = mdisplayarg; } if (fontarg) { args[argx++] = "-font"; args[argx++] = fontarg; } args[argx++] = 0; execvp("mterm",(void *)&args[0]); err = errno; write(xp[1],&err,sizeof(int)); _exit(1); } close(ctl[1]); close(xp[1]); close(go[0]); e->ctlfd = ctl[0]; aio_oq_init(&e->ctloq); e->id = aio_add_poll(e->ctlfd,&aio_rwtest_always,&wtest_em_ctl,&rd_em_ctl,&wr_em_ctl,e); es->e = e; es->go = go[1]; es->xp = xp[0]; es->started = started; lx_op_callback(lx_GetInputFocus(xc,0,0),&em_win_created,es,0); return(e); } static void tell_em_point(EM *e, const char *s, int l) { aio_oq_queue_point(&e->ctloq,s,l); } static void tell_em_copy(EM *e, const char *s, int l) { aio_oq_queue_copy(&e->ctloq,s,l); } static void em_raise(EM *e) { if (e == ems) return; em_unlink(e); em_link_head(e); lx_ConfigureWindow_va(xc,e->pwin,LX_CWV_StackMode(LX_STACKMODE_Above),LX_CWV_END); } static void focus_on(EM *e) { if (focus == e) return; if (focus) tell_em_point(focus,"fl",2); em_raise(e); focus = e; tell_em_point(e,"ft",2); } static int check_focus(void *arg __attribute__((__unused__))) { if (!focus && ems) { focus_on(ems); return(AIO_BLOCK_LOOP); } return(AIO_BLOCK_NIL); } static int remap_help_win(void *arg __attribute__((__unused__))) { if (help_want && !help_up) { lx_MapWindow(xc,help_win); help_up = 1; return(AIO_BLOCK_LOOP); } else if (!help_want && help_up) { lx_UnmapWindow(xc,help_win); help_up = 0; return(AIO_BLOCK_LOOP); } return(AIO_BLOCK_NIL); } static LX_XID create_resize_cursor(const signed char *rdesc) { LX_XID ipm; LX_XID mpm; int x; int y; int w; int h; LX_XID curs; ipm = lx_CreatePixmap(xc,root,1,18,18); mpm = lx_CreatePixmap(xc,root,1,18,18); lx_ChangeSGC_va(xc,gc1,LX_GCV_Foreground(0),LX_GCV_Function(LX_GCFUNCTION_Copy),LX_GCV_END); lx_fill_rectangle(xc,ipm,lx_SGC_GC(xc,gc1),0,0,18,18); lx_fill_rectangle(xc,mpm,lx_SGC_GC(xc,gc1),0,0,18,18); lx_ChangeSGC_va(xc,gc1,LX_GCV_Foreground(1),LX_GCV_END); while (1) { x = *rdesc++; if (x < 0) break; y = *rdesc++; w = *rdesc++; h = *rdesc++; lx_fill_rectangle(xc,ipm,lx_SGC_GC(xc,gc1),x,y,w,h); } lx_ChangeSGC_va(xc,gc1,LX_GCV_Function(LX_GCFUNCTION_Or),LX_GCV_END); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,0,0,0,18,18); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),1,0,0,0,17,18); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,1,0,0,18,17); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,0,1,0,17,18); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,0,0,1,18,17); curs = lx_CreateCursor_rgb(xc,ipm,mpm,fg_col.rgb,bg_col.rgb,9,9); lx_FreePixmap(xc,ipm); lx_FreePixmap(xc,mpm); return(curs); } static int startup_step2(void *arg __attribute__((__unused__))) { int x; int y; int iw; int ih; int ow; int oh; int i; LX_XID pm_d; if (startup_pending > 0) return(AIO_BLOCK_NIL); aio_remove_block(startid); if (!fg_col.good || !bg_col.good || !bc_col.good || !ec_col.good) exit(1); ems = 0; baselineskip = fontinfo->maxbounds.ascent + fontinfo->maxbounds.descent; charwidth = fontinfo->maxbounds.width; pm_d = lx_CreatePixmap(xc,root,1,3,baselineskip+2); lx_ChangeSGC_va(xc,gc1,LX_GCV_Foreground(0),LX_GCV_END); lx_fill_rectangle(xc,pm_d,lx_SGC_GC(xc,gc1),0,0,3,baselineskip+2); lx_ChangeSGC_va(xc,gc1,LX_GCV_Foreground(1),LX_GCV_END); lx_fill_rectangle(xc,pm_d,lx_SGC_GC(xc,gc1),1,1,1,baselineskip); cursor = lx_CreateCursor_rgb(xc,pm_d,LX_PIXMAP_None,fg_col.rgb,bg_col.rgb,1,(baselineskip/2)+1); lx_FreePixmap(xc,pm_d); iw = (geom.w * charwidth) + (2 * bordermargin); ih = (geom.h * baselineskip) + (2 * bordermargin); ow = iw + (2 * borderwidth); oh = ih + (2 * borderwidth); if (geom.flags & GF_POS) { x = (geom.flags & GF_X_NEG) ? scrw - (ow + geom.x) : geom.x; y = (geom.flags & GF_Y_NEG) ? scrh - (oh + geom.y) : geom.y; } else { x = (scrw - ow) / 2; y = (scrh - oh) / 2; } topwin = lx_CreateWindow_va(xc,root,x,y,iw,ih,borderwidth,depth,LX_WCLASS_InputOutput,visual, LX_CWV_BackPixmap(LX_PIXMAP_None), LX_CWV_BorderPixel(bc_col.pix), LX_CWV_EventMask(LX_EM_StructureNotify|LX_EM_KeyPress), LX_CWV_Colormap(cmap), LX_CWV_Cursor(cursor), LX_CWV_END); contwin = lx_CreateWindow_va(xc,topwin,0,0,65535,65535,0,depth,LX_WCLASS_InputOutput,visual, LX_CWV_BackPixel(ec_col.pix), LX_CWV_END); help_pm = lx_CreatePixmap(xc,contwin,depth,iw,ih); lx_ChangeSGC_va(xc,gc,LX_GCV_Foreground(bg_col.pix),LX_GCV_END); lx_fill_rectangle(xc,help_pm,lx_SGC_GC(xc,gc),0,0,65535,65535); lx_ChangeSGC_va(xc,gc,LX_GCV_Foreground(fg_col.pix),LX_GCV_END); x = bordermargin; y = bordermargin + fontinfo->maxbounds.ascent; for (i=0;helplines[i];i++) { LX_TEXTITEM8 ti; ti.text = (const void *)helplines[i]; ti.nchars = strlen(helplines[i]); ti.delta = 0; ti.font = LX_FONT_None; lx_PolyText8(xc,help_pm,lx_SGC_GC(xc,gc),x,y,1,&ti); y += baselineskip; } help_win = lx_CreateWindow_va(xc,topwin,0,0,65535,65535,0,depth,LX_WCLASS_InputOutput,visual, LX_CWV_BackPixmap(help_pm), LX_CWV_END); lx_MapSubwindows(xc,topwin); lx_MapWindow(xc,topwin); lx_MapSubwindows(xc,contwin); for (i=9-1;i>=0;i--) (&resize_cursors[0][0])[i] = create_resize_cursor((&resize_cursdata[0][0])[i]); resize_parent = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,0,LX_WCLASS_InputOnly,visual,LX_CWV_END); for (i=9-1;i>=0;i--) (&resize_windows[0][0])[i] = lx_CreateWindow_va(xc,resize_parent,0,0,1,1,0,0,LX_WCLASS_InputOnly,visual,LX_CWV_Cursor((&resize_cursors[0][0])[i]),LX_CWV_END); lx_MapSubwindows(xc,resize_parent); lx_GrabKey(xc,LX_AnyKey,LX_AnyModifier,topwin,0,LX_GRABMODE_Asynchronous,LX_GRABMODE_Synchronous); focus = 0; focusid = aio_add_block(&check_focus,0); help_want = 1; help_up = 1; help_id = aio_add_block(&remap_help_win,0); return(AIO_BLOCK_LOOP); } static void once_started(EM *e, int succ) { if (succ) focus_on(e); } static void resize_check_grab(void *flagv) { int *flag; flag = flagv; if (*flag) return; if (resize_grab_status != LX_GRABSTATUS_Success) { printf("resize grab failed: "); switch (resize_grab_status) { case LX_GRABSTATUS_AlreadyGrabbed: printf("AlreadyGrabbed"); break; case LX_GRABSTATUS_InvalidTime: printf("InvalidTime"); break; case LX_GRABSTATUS_NotViewable: printf("NotViewable"); break; case LX_GRABSTATUS_Frozen: printf("Frozen"); break; default: printf("?%lx",(unsigned long int)resize_grab_status); break; } printf("\n"); lx_UnmapWindow(xc,resize_parent); lx_Bell(xc,0); resizing = 0; return; } } static void cancel_resize_grab(void) { lx_UnmapWindow(xc,resize_parent); *resize_grabflag = 1; resizing = 0; } static void start_resize(EM *e, LX_TIME when) { int iw3; int ih3; if (resizing) return; resizing = e; #define iw (e->pw) #define ih (e->ph) iw3 = iw / 3; ih3 = ih / 3; lx_ConfigureWindow_va(xc,resize_windows[0][0], LX_CWV_X(0), LX_CWV_Y(0), LX_CWV_W(iw3), LX_CWV_H(ih3), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[1][0], LX_CWV_X(iw3), LX_CWV_Y(0), LX_CWV_W(iw-(2*iw3)), LX_CWV_H(ih3), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[2][0], LX_CWV_X(iw-iw3), LX_CWV_Y(0), LX_CWV_W(iw3), LX_CWV_H(ih3), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[0][1], LX_CWV_X(0), LX_CWV_Y(ih3), LX_CWV_W(iw3), LX_CWV_H(ih-(2*ih3)), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[1][1], LX_CWV_X(iw3), LX_CWV_Y(ih3), LX_CWV_W(iw-(2*iw3)), LX_CWV_H(ih-(2*ih3)), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[2][1], LX_CWV_X(iw-iw3), LX_CWV_Y(ih3), LX_CWV_W(iw3), LX_CWV_H(ih-(2*ih3)), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[0][2], LX_CWV_X(0), LX_CWV_Y(ih-ih3), LX_CWV_W(iw3), LX_CWV_H(ih3), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[1][2], LX_CWV_X(iw3), LX_CWV_Y(ih-ih3), LX_CWV_W(iw-(2*iw3)), LX_CWV_H(ih3), LX_CWV_END); lx_ConfigureWindow_va(xc,resize_windows[2][2], LX_CWV_X(iw-iw3), LX_CWV_Y(ih-ih3), LX_CWV_W(iw3), LX_CWV_H(ih3), LX_CWV_END); #undef iw #undef ih lx_ConfigureWindow_va(xc,resize_parent, LX_CWV_X(e->px), LX_CWV_Y(e->py), LX_CWV_W(e->pw), LX_CWV_H(e->ph), LX_CWV_StackMode(LX_STACKMODE_Above), LX_CWV_END); lx_MapWindow(xc,resize_parent); resize_op = lx_GrabPointer(xc, resize_parent, 0, LX_EM_ButtonPress | LX_EM_ButtonRelease, LX_GRABMODE_Asynchronous, LX_GRABMODE_Asynchronous, resize_parent, LX_CURSOR_None, when, &resize_grab_status); resize_grabflag = malloc(sizeof(int)); *resize_grabflag = 0; lx_op_callback(resize_op,&resize_check_grab,&resize_grabflag,0); cancel_resize = &cancel_resize_grab; } static void keypress(LX_CONN *xc, LX_EVENT_KeyPress *e) { unsigned char s[LX_KBMAP_MAX_STRING]; int l; unsigned char hdr[2]; int o; LX_KEYSYM ks; lx_AllowEvents(xc,LX_ALLOWMODE_AsyncKeyboard,e->time); l = lx_kbmap_string(xc,e->keycode,e->state,&s[0],&ks); if ((e->state & (LX_EVS_Control|LX_EVS_Shift)) == (LX_EVS_Control|LX_EVS_Shift)) { switch (ks) { case LX_KEYSYM_Escape: help_want = ! help_want; return; break; case LX_KEYSYM_N: case LX_KEYSYM_n: start_emulator(0,0,geom.w,geom.h,&once_started); help_want = 0; return; break; case LX_KEYSYM_R: case LX_KEYSYM_r: if (ems) { start_resize(ems,e->time); help_want = 0; } else { lx_Bell(xc,0); } return; break; } } if (l) { // int i; // printf("KeyPress: kc %d state %04x, string",e->keycode,e->state); // for (i=0;i 255) { hdr[0] = 'i'; hdr[1] = 255; tell_em_copy(focus,&hdr[0],2); tell_em_copy(focus,s+o,255); o += 255; } hdr[0] = 'i'; hdr[1] = l - o; tell_em_copy(focus,&hdr[0],2); tell_em_copy(focus,s+o,l-o); } } } static void handle_event(LX_CONN *c, LX_EVENT *e) { if (c != xc) abort(); switch (e->type) { case LX_EV_ConfigureNotify: printf("ConfigureNotify: "); if (e->u.ConfigureNotify.window != contwin) { printf("[*** win=%lx] ",(unsigned long int)e->u.ConfigureNotify.window); } printf( "%dx%d+%d+%d, bw %d\n", e->u.ConfigureNotify.w, e->u.ConfigureNotify.h, e->u.ConfigureNotify.x, e->u.ConfigureNotify.y, e->u.ConfigureNotify.borderwidth ); break; case LX_EV_KeyPress: keypress(xc,&e->u.KeyPress); break; case LX_EV_KeyRelease: case LX_EV_NoExposure: break; default: printf("Event type %d\n",(int)e->type); break; } } static void startup_got_colour(void *cv) { COL *c; c = cv; printf("Colour: %s -> %04x,%04x,%04x, pixel %lx\n",c->str,c->rgb.r,c->rgb.g,c->rgb.b,(unsigned long int)c->pix); startup_pending --; } static void startup_alloc_colour(void *cv) { COL *c; c = cv; if (c->good) { lx_op_callback(lx_AllocColor(xc,cmap,c->rgb.r,c->rgb.g,c->rgb.b,&c->pix,&c->rgb.r,&c->rgb.g,&c->rgb.b),&startup_got_colour,c,0); } else { printf("Colour: %s -> fail\n",c->str); startup_pending --; } } static void setup_colour(COL *c, const char *s) { c->str = s; lx_op_callback(lx_lookup_color_rgb(xc,cmap,s,-1,&c->rgb,&c->good),&startup_alloc_colour,c,0); startup_pending ++; } static void dec_pending(void *arg __attribute__((__unused__))) { startup_pending --; } static void startup_step1(LX_CONN *newxc, void *arg __attribute__((__unused__))) { LX_XID pm1; xc = newxc; lx_set_event_handler(xc,&handle_event); scrno = lx_default_screen(xc); root = lx_root(xc,scrno); depth = lx_root_depth(xc,scrno); visual = lx_root_visual(xc,scrno); cmap = lx_root_colormap(xc,scrno); scrw = lx_root_width(xc,scrno); scrh = lx_root_height(xc,scrno); if (! geometryarg) geometryarg = "80x24"; geom = parse_geometry(geometryarg); if (fontarg) { fontid = lx_OpenFont(xc,fontarg); gc = lx_CreateSGC_va(xc,root,LX_GCV_Font(fontid),LX_GCV_END); } else { gc = lx_CreateSGC_va(xc,root,LX_GCV_END); fontid = lx_SGC_GC(xc,gc); } pm1 = lx_CreatePixmap(xc,root,1,1,1); gc1 = lx_CreateSGC_va(xc,pm1,LX_GCV_END); lx_SGC_GC(xc,gc1); lx_FreePixmap(xc,pm1); startup_pending = 2; // for QueryFont and kbmap setup lx_op_callback(lx_QueryFont(xc,fontid,&fontinfo),&dec_pending,0,0); lx_op_callback(lx_kbmap_setup(xc),&dec_pending,0,0); setup_colour(&fg_col,fgarg); setup_colour(&bg_col,bgarg); setup_colour(&bc_col,bcarg); setup_colour(&ec_col,ecarg); startid = aio_add_block(&startup_step2,0); } static void setup_signals(void) { signal(SIGCHLD,SIG_IGN); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); setup_signals(); setup_preopen(); aio_poll_init(); lx_open(mmdisplayarg,0,&startup_step1,0,0,0); aio_event_loop(); (void)&call_once; return(1); }