/* * 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 #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 enum { RUI_UNSET = 1, RUI_POINTER, RUI_KEYBOARD, } RESIZE_UI; 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 LX_RECTANGLE c; LX_RECTANGLE p; 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 } ; 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_h; static EM *ems_t; 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_L[] = { 1,1,6,1, 2,2,4,1, 1,1,2,17, 2,16,4,1, 1,17,6,1, -1, 1, 9 }; static const signed char cursrects_R[] = { 12,1,6,1, 13,2,5,1, 16,1,2,17, 13,16,5,1, 12,17,6,1, -1, 17, 9 }; static const signed char cursrects_LR[] = { 1,1,6,1, 2,2,4,1, 1,1,2,17, 2,16,4,1, 1,17,6,1, 13,1,5,1, 14,2,4,1, 16,1,2,17, 14,16,4,1, 13,17,5,1, -1, 9, 9 }; static const signed char cursrects_T[] = { 1,1,1,6, 2,2,1,4, 1,1,17,2, 16,2,1,4, 17,1,1,6, -1, 9, 1 }; static const signed char cursrects_LT[] = { 1,1,1,17, 1,1,17,1, 2,2,15,1, 2,2,1,15, -1, 1, 1 }; static const signed char cursrects_RT[] = { 1,1,17,1, 2,2,16,1, 16,1,1,16, 17,1,1,17, -1, 17, 1 }; static const signed char cursrects_LRT[] = { 1,1,1,17, 2,1,1,16, 1,1,17,2, 16,1,1,16, 17,1,1,17, -1, 9, 9 }; static const signed char cursrects_B[] = { 1,12,1,6, 2,13,1,5, 1,16,17,2, 16,13,1,5, 17,12,1,6, -1, 9, 17 }; static const signed char cursrects_LB[] = { 1,1,1,17, 2,2,1,16, 1,16,16,1, 1,17,17,1, -1, 1, 17 }; static const signed char cursrects_RB[] = { 1,17,17,1, 2,16,16,1, 16,2,1,16, 17,1,1,17, -1, 17, 17 }; static const signed char cursrects_LRB[] = { 1,1,1,17, 2,2,1,16, 1,16,17,2, 16,2,1,16, 17,1,1,17, -1, 9, 9 }; static const signed char cursrects_TB[] = { 1,1,1,6, 2,2,1,4, 1,1,17,2, 16,2,1,4, 17,1,1,6, 1,13,1,5, 2,14,1,4, 1,16,17,2, 16,14,1,4, 17,13,1,5, -1, 9, 9 }; static const signed char cursrects_LTB[] = { 1,1,17,1, 1,2,16,1, 1,1,2,17, 1,16,16,1, 1,17,17,1, -1, 9, 9 }; static const signed char cursrects_RTB[] = { 1,1,17,1, 2,2,16,1, 16,1,2,17, 2,16,16,1, 1,17,17,1, -1, 9, 9 }; static const signed char cursrects_LRTB[] = { 1,1,17,2, 1,1,2,17, 16,1,2,17, 1,16,17,2, -1, 9, 9 }; static const signed char *resize_cursdata[16] = { 0, &cursrects_L[0], &cursrects_R[0], &cursrects_LR[0], &cursrects_T[0], &cursrects_LT[0], &cursrects_RT[0], &cursrects_LRT[0], &cursrects_B[0], &cursrects_LB[0], &cursrects_RB[0], &cursrects_LRB[0], &cursrects_TB[0], &cursrects_LTB[0], &cursrects_RTB[0], &cursrects_LRTB[0] }; static LX_XID resize_cursors[16]; static LX_XID resize_cwin[3][3]; static int cwin_cursx[3][3] = { { 5, 1, 9 }, { 4, 15, 8 }, { 6, 2, 10 } }; static LX_XID boxwin[8]; static LX_XID nilcurs; static RESIZE_UI resize_ui; static unsigned int boxwant; static unsigned int boxmap; static LX_RECTANGLE boxrect; static LX_RECTANGLE resize_orig; 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 LX_XID resize_sizewin; static LX_RECTANGLE resize_sizewin_g; static int resize_sizewin_map; static LX_XID resize_sizepm; static int resize_sizepm_w; static int resize_sizepm_h; static int resize_down_x; static int resize_down_y; static int resize_dragging; #define DRAG_L 0x00000001 #define DRAG_R 0x00000002 #define DRAG_T 0x00000004 #define DRAG_B 0x00000008 static LX_QUERYPOINTER_STATUS resize_qpstatus; static int (*keystroke)(LX_KEYSYM, unsigned int, const char *, int, LX_TIME); static int (*pick_op)(LX_KEYSYM, unsigned int, const char *, int, LX_TIME); static EM *pick_em; static int haveshape = 0; static const char *helplines[] = { "This is mmterm, a multi-mterm container.", "", "Ctl-Shift-ESC toggles this help screen", "Ctl-Shift-F starts refocusing", "Ctl-Shift-N starts a new emulator", "Ctl-Shift-Q quits immediately", "Ctl-Shift-R starts raising", "Ctl-Shift-Z 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]; mdisplayarg = 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; } if (!strcmp(*av,"-noshape")) { haveshape = -1; 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 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; else ems_t = e->blink; if (e->blink) e->blink->flink = e->flink; else ems_h = e->flink; e->flags &= ~EMF_LINKED; } static void em_link_head(EM *e) { if (e->flags & (EMF_DEAD|EMF_LINKED)) abort(); e->flink = ems_h; e->blink = 0; if (ems_h) ems_h->blink = e; else ems_t = e; ems_h = e; e->flags |= EMF_LINKED; } static void em_link_below(EM *e, EM *other) { if (e->flags & (EMF_DEAD|EMF_LINKED)) abort(); e->flink = other->flink; e->blink = other; if (other->flink) other->flink->blink = e; else ems_t = e; other->flink = e; e->flags |= EMF_LINKED; } static void em_link_above(EM *e, EM *other) { if (e->flags & (EMF_DEAD|EMF_LINKED)) abort(); e->blink = other->blink; e->flink = other; if (other->blink) other->blink->flink = e; else ems_h = e; other->blink = 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->c.x = x; e->c.y = y; e->c.w = w; e->c.h = h; e->p.x = px; e->p.y = py; e->p.w = pw; e->p.h = 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]); fcntl(ctl[0],F_SETFD,1); 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 focus_on(EM *e) { if (focus == e) return; if (focus) tell_em_point(focus,"fl",2); focus = e; tell_em_point(e,"ft",2); } static int check_focus(void *arg __attribute__((__unused__))) { if (!focus && ems_h) { focus_on(ems_h); 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,19,19); mpm = lx_CreatePixmap(xc,root,1,19,19); 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,19,19); lx_fill_rectangle(xc,mpm,lx_SGC_GC(xc,gc1),0,0,19,19); 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,19,19); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),1,0,0,0,18,19); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,1,0,0,19,18); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,0,1,0,18,19); lx_CopyArea(xc,ipm,mpm,lx_SGC_GC(xc,gc1),0,0,0,1,19,18); x = *rdesc++; y = *rdesc++; curs = lx_CreateCursor_rgb(xc,ipm,mpm,fg_col.rgb,bg_col.rgb,x,y); 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); printf("shape: usable %d\n",haveshape); if (!fg_col.good || !bg_col.good || !bc_col.good || !ec_col.good) exit(1); ems_h = 0; ems_t = 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); pm_d = lx_CreatePixmap(xc,root,1,1,1); lx_ChangeSGC_va(xc,gc1,LX_GCV_Foreground(0),LX_GCV_END); lx_draw_point(xc,pm_d,lx_SGC_GC(xc,gc1),0,0); nilcurs = lx_CreateCursor_rgb(xc,pm_d,pm_d,fg_col.rgb,bg_col.rgb,0,0); 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=16-1;i>0;i--) resize_cursors[i] = create_resize_cursor(resize_cursdata[i]); resize_cursors[0] = nilcurs; 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_cwin[0][0])[i] = lx_CreateWindow_va(xc,resize_parent,0,0,1,1,0,0,LX_WCLASS_InputOnly,visual,LX_CWV_Cursor(resize_cursors[(&cwin_cursx[0][0])[i]]),LX_CWV_END); if (haveshape) { boxwin[0] = lx_CreateWindow_va(xc,contwin,0,0,65535,65535,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); boxwin[1] = lx_CreateWindow_va(xc,contwin,0,0,65535,65535,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(fg_col.pix),LX_CWV_END); } else { boxwin[0] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); boxwin[1] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); boxwin[2] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); boxwin[3] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); boxwin[4] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(fg_col.pix),LX_CWV_END); boxwin[5] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(fg_col.pix),LX_CWV_END); boxwin[6] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(fg_col.pix),LX_CWV_END); boxwin[7] = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(fg_col.pix),LX_CWV_END); } resize_sizewin = lx_CreateWindow_va(xc,contwin,0,0,1,1,0,depth,LX_WCLASS_InputOutput,visual,LX_CWV_BackPixel(bg_col.pix),LX_CWV_END); resize_sizewin_g.x = 0; resize_sizewin_g.y = 0; resize_sizewin_g.w = 1; resize_sizewin_g.h = 1; resize_sizewin_map = 0; resize_sizepm = LX_PIXMAP_None; resize_sizepm_w = -1; resize_sizepm_h = -1; boxwant = 0; boxmap = 0; 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 remap_box(void) { unsigned int chg; unsigned int bit; int i; chg = boxwant ^ boxmap; boxmap = boxwant; for (i=0,bit=1;bit<=chg;i++,bit<<=1) { if (chg & bit) { if (boxwant & bit) lx_MapWindow(xc,boxwin[i]); else lx_UnmapWindow(xc,boxwin[i]); } } } static void resize_sizewin_setmap(int want) { if (want && !resize_sizewin_map) { lx_MapWindow(xc,resize_sizewin); resize_sizewin_map = 1; } else if (!want && resize_sizewin_map) { lx_UnmapWindow(xc,resize_sizewin); resize_sizewin_map = 0; } } static void cancel_resize_live(void) { boxwant = 0; remap_box(); lx_UnmapWindow(xc,resize_parent); // ungrabs if grabbed resize_sizewin_setmap(0); resizing = 0; keystroke = 0; } static void resize_check_grab(void *flagv) { int *flag; flag = flagv; if (*flag) { cancel_resize = &cancel_resize_live; 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; keystroke = 0; } static void reset_boxwins(int dosz) { LX_RECTANGLE r[8]; int nr; int i; int cw; int ch; int szx; int szy; char *szstr; int szcw; int szpw; int szpmw; int szpmh; LX_TEXTITEM8 ti; boxwant = 0; if (dosz) { if ((boxrect.w % charwidth) || (boxrect.h % baselineskip)) abort(); cw = boxrect.w / charwidth; ch = boxrect.h / baselineskip; szcw = asprintf(&szstr,"%dx%d",cw,ch); szpw = szcw * charwidth; szpmw = borderwidth + bordermargin + szpw + bordermargin + borderwidth; szpmh = borderwidth + bordermargin + baselineskip + bordermargin + borderwidth; // 6 is for rubberband box if ((boxrect.w >= szpmw+6) && (boxrect.h >= szpmh+6)) { szx = bordermargin + boxrect.x + ((boxrect.w - szpmw) / 2); szy = bordermargin + boxrect.y + ((boxrect.h - szpmh) / 2); if ((szpmw > resize_sizepm_w) || (szpmh > resize_sizepm_h)) { if (resize_sizepm != LX_PIXMAP_None) lx_FreePixmap(xc,resize_sizepm); resize_sizepm_w = szpmw; resize_sizepm_h = szpmh; resize_sizepm = lx_CreatePixmap(xc,root,depth,szpmw,szpmh); } lx_ChangeSGC_va(xc,gc,LX_GCV_Function(LX_GCFUNCTION_Copy),LX_GCV_Foreground(bc_col.pix),LX_GCV_END); lx_fill_rectangle(xc,resize_sizepm,lx_SGC_GC(xc,gc),0,0,szpmw,szpmh); lx_ChangeSGC_va(xc,gc,LX_GCV_Function(LX_GCFUNCTION_Copy),LX_GCV_Foreground(bg_col.pix),LX_GCV_END); lx_fill_rectangle(xc,resize_sizepm,lx_SGC_GC(xc,gc),borderwidth,borderwidth,szpmw-(2*borderwidth),szpmh-(2*borderwidth)); lx_ChangeSGC_va(xc,gc,LX_GCV_Foreground(fg_col.pix),LX_GCV_END); ti.text = szstr; ti.nchars = szcw; ti.delta = 0; ti.font = LX_FONT_None; lx_PolyText8(xc,resize_sizepm,lx_SGC_GC(xc,gc),borderwidth+bordermargin,borderwidth+bordermargin+fontinfo->maxbounds.ascent,1,&ti); if ( (szx != resize_sizewin_g.x) || (szpmw != resize_sizewin_g.w) || (szy != resize_sizewin_g.y) || (szpmh != resize_sizewin_g.h) ) { lx_ConfigureWindow_va(xc,resize_sizewin,LX_CWV_X(szx),LX_CWV_Y(szy),LX_CWV_W(szpmw),LX_CWV_H(szpmh),LX_CWV_END); resize_sizewin_g.x = szx; resize_sizewin_g.y = szy; resize_sizewin_g.w = szpmw; resize_sizewin_g.h = szpmh; } lx_ChangeWindowAttributes_va(xc,resize_sizewin,LX_CWV_BackPixmap(resize_sizepm),LX_CWV_END); lx_ClearArea(xc,resize_sizewin,0,0,0,0,0); resize_sizewin_setmap(1); } else { resize_sizewin_setmap(0); } free(szstr); } if ((boxrect.w <= 6) || (boxrect.h <= 6)) { r[0].x = bordermargin + boxrect.x; r[0].y = bordermargin + boxrect.y; r[0].w = boxrect.w; r[0].h = boxrect.h; nr = 1; } else { r[0].x = bordermargin + boxrect.x; r[0].y = bordermargin + boxrect.y; r[0].w = boxrect.w; r[0].h = 3; r[1].x = bordermargin + boxrect.x; r[1].y = bordermargin + boxrect.y; r[1].w = 3; r[1].h = boxrect.h; r[2].x = bordermargin + boxrect.x; r[2].y = bordermargin + boxrect.y + boxrect.h - 3; r[2].w = boxrect.w; r[2].h = 3; r[3].x = bordermargin + boxrect.x + boxrect.w - 3; r[3].y = bordermargin + boxrect.y; r[3].w = 3; r[3].h = boxrect.h; nr = 4; } if (haveshape) { lx_shape_Rectangles(xc,boxwin[0],LX_SHAPE_Bounding,LX_SHAPE_Set,0,0,LX_RECTORDER_UnSorted,nr,&r[0]); boxwant |= 1; } else { for (i=nr-1;i>=0;i--) { lx_ConfigureWindow_va( xc, boxwin[i], LX_CWV_X(r[i].x), LX_CWV_Y(r[i].y), LX_CWV_W(r[i].w), LX_CWV_H(r[i].h), LX_CWV_END ); boxwant |= 1 << i; } } if ((boxrect.w >= 3) && (boxrect.h >= 3)) { if ((boxrect.w < 5) || (boxrect.h < 5)) { r[0].x = bordermargin + boxrect.x + 1; r[0].y = bordermargin + boxrect.y + 1; r[0].w = boxrect.w - 2; r[0].h = boxrect.h - 2; nr = 1; } else { r[0].x = bordermargin + boxrect.x + 1; r[0].y = bordermargin + boxrect.y + 1; r[0].w = boxrect.w - 2; r[0].h = 1; r[1].x = bordermargin + boxrect.x + 1; r[1].y = bordermargin + boxrect.y + 1; r[1].w = 1; r[1].h = boxrect.h - 2; r[2].x = bordermargin + boxrect.x + 1; r[2].y = bordermargin + boxrect.y + boxrect.h - 2; r[2].w = boxrect.w - 2; r[2].h = 1; r[3].x = bordermargin + boxrect.x + boxrect.w - 2; r[3].y = bordermargin + boxrect.y + 1; r[3].w = 1; r[3].h = boxrect.h - 2; nr = 4; } if (haveshape) { lx_shape_Rectangles(xc,boxwin[1],LX_SHAPE_Bounding,LX_SHAPE_Set,0,0,LX_RECTORDER_UnSorted,nr,&r[0]); boxwant |= 2; } else { for (i=nr-1;i>=0;i--) { lx_ConfigureWindow_va( xc, boxwin[4+i], LX_CWV_X(r[i].x), LX_CWV_Y(r[i].y), LX_CWV_W(r[i].w), LX_CWV_H(r[i].h), LX_CWV_END ); boxwant |= 0x10 << i; } } } remap_box(); } static void reset_cwins(LX_RECTANGLE r) { int iw3; int ih3; lx_ConfigureWindow_va(xc,resize_parent, LX_CWV_X(r.x+bordermargin), LX_CWV_Y(r.y+bordermargin), LX_CWV_W(r.w), LX_CWV_H(r.h), LX_CWV_StackMode(LX_STACKMODE_Above), LX_CWV_END); #define iw r.w #define ih r.h iw3 = iw / 3; ih3 = ih / 3; lx_ConfigureWindow_va(xc,resize_cwin[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_cwin[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_cwin[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_cwin[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_cwin[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_cwin[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_cwin[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_cwin[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_cwin[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 } static void stop_dragging(LX_TIME time) { resize_dragging = 0; if (resize_ui != RUI_KEYBOARD) lx_ChangeActivePointerGrab(xc,LX_EM_ButtonPress,LX_CURSOR_None,time); reset_cwins(boxrect); } static void cancel_drag(LX_TIME time) { boxrect = resize_orig; reset_boxwins(0); stop_dragging(time); } static void resize_drag_kb(int dx, int dy) { int pw; int ph; int x1; int y1; int x2; int y2; int w; int h; pw = geom.w * charwidth; ph = geom.h * baselineskip; x1 = boxrect.x; y1 = boxrect.y; x2 = x1 + boxrect.w; y2 = y1 + boxrect.h; if (resize_dragging & DRAG_L) x1 += dx * charwidth; if (resize_dragging & DRAG_R) x2 += dx * charwidth; if (resize_dragging & DRAG_T) y1 += dy * baselineskip; if (resize_dragging & DRAG_B) y2 += dy * baselineskip; if ((x1 < 0) || (x2 > pw) || (y1 < 0) || (y2 > ph) || (x1 >= x2) || (y1 >= y2)) return; w = x2 - x1; h = y2 - y1; if ((boxrect.x != x1) || (boxrect.y != y1) || (boxrect.w != w) || (boxrect.h != h)) { boxrect.x = x1; boxrect.y = y1; boxrect.w = w; boxrect.h = h; reset_boxwins(1); } } static int resize_set_ui(RESIZE_UI ui) { if (resize_ui == ui) return(0); if (resize_ui == RUI_UNSET) { resize_ui = ui; return(0); } return(1); } static void apply_resize(void) { if ( (boxrect.x % charwidth) || (boxrect.y % baselineskip) || (boxrect.w % charwidth) || (boxrect.h % baselineskip) ) abort(); resizing->c.x = boxrect.x / charwidth; resizing->c.y = boxrect.y / baselineskip; resizing->c.w = boxrect.w / charwidth; resizing->c.h = boxrect.h / baselineskip; resizing->p = boxrect; resizing->p.x += bordermargin; resizing->p.y += bordermargin; lx_ConfigureWindow_va(xc,resizing->pwin,LX_CWV_X(resizing->p.x),LX_CWV_Y(resizing->p.y),LX_CWV_W(resizing->p.w),LX_CWV_H(resizing->p.h),LX_CWV_END); cancel_resize_live(); } // The constants here must agree with resize_cursdata[] subscripts. static void reset_grab_cursor(LX_TIME time) { int x; x = ((resize_dragging & DRAG_L) ? 1 : 0) | ((resize_dragging & DRAG_R) ? 2 : 0) | ((resize_dragging & DRAG_T) ? 4 : 0) | ((resize_dragging & DRAG_B) ? 8 : 0); if ((x < 0) || (x > 15)) abort(); lx_ChangeActivePointerGrab(xc,LX_EM_ButtonPress,resize_cursors[x],time); } static int keystroke_resize( LX_KEYSYM ks, unsigned int state, const char *str __attribute__((__unused__)), int len __attribute__((__unused__)), LX_TIME time ) { unsigned int dragbit; int movex; int movey; if (! resizing) { keystroke = 0; return(0); } resize_set_ui(RUI_KEYBOARD); dragbit = 0; movex = 0; movey = 0; switch (ks) { case LX_KEYSYM_H: case LX_KEYSYM_h: dragbit = DRAG_L; movex = -1; break; case LX_KEYSYM_J: case LX_KEYSYM_j: dragbit = DRAG_B; movey = 1; break; case LX_KEYSYM_K: case LX_KEYSYM_k: dragbit = DRAG_T; movey = -1; break; case LX_KEYSYM_L: case LX_KEYSYM_l: dragbit = DRAG_R; movex = 1; break; case LX_KEYSYM_Escape: if (! (state & LX_EVS_Control)) return(0); if (resize_dragging) { cancel_drag(time); } else { stop_resizing(); } return(1); break; case LX_KEYSYM_Return: if (! (state & LX_EVS_Control)) return(0); if (resize_dragging) stop_dragging(time); apply_resize(); return(1); break; default: return(0); break; } if (state & LX_EVS_Control) { if (resize_dragging == 0) { resize_dragging = dragbit; reset_grab_cursor(time); } else { resize_dragging ^= dragbit; reset_grab_cursor(time); if (resize_dragging == 0) stop_dragging(time); } } else { if (resize_dragging) { if (state & LX_EVS_Shift) { movex *= 10; movey *= 5; } resize_drag_kb(movex,movey); } } return(1); } static void raisebox(void) { LX_XID prev; int i; prev = LX_WINDOW_None; for (i=(haveshape?2:8)-1;i>=0;i--) { if (prev == LX_WINDOW_None) { lx_ConfigureWindow_va(xc,boxwin[i],LX_CWV_StackMode(LX_STACKMODE_Above),LX_CWV_END); } else { lx_ConfigureWindow_va(xc,boxwin[i],LX_CWV_StackMode(LX_STACKMODE_Below),LX_CWV_Sibling(prev),LX_CWV_END); } prev = boxwin[i]; } } static void start_resize(EM *e, LX_TIME when) { if (resizing) return; raisebox(); lx_ConfigureWindow_va(xc,resize_sizewin,LX_CWV_StackMode(LX_STACKMODE_Above),LX_CWV_END); resizing = e; boxrect = e->p; boxrect.x -= bordermargin; boxrect.y -= bordermargin; reset_cwins(boxrect); reset_boxwins(1); lx_MapWindow(xc,resize_parent); resize_op = lx_GrabPointer(xc, resize_parent, 0, LX_EM_ButtonPress, 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; resize_dragging = 0; keystroke = &keystroke_resize; resize_ui = RUI_UNSET; } static void set_pick_em(EM *e) { pick_em = e; boxrect = e->p; boxrect.x -= bordermargin; boxrect.y -= bordermargin; reset_boxwins(0); } static void stop_picking(void) { keystroke = 0; boxwant = 0; remap_box(); } static int keystroke_pick(LX_KEYSYM ks, unsigned int state, const char *str, int len, LX_TIME time) { EM *e; switch (ks) { case LX_KEYSYM_H: case LX_KEYSYM_h: e = pick_em->blink; if (! e) e = ems_t; set_pick_em(e); return(1); break; case LX_KEYSYM_L: case LX_KEYSYM_l: e = pick_em->flink; if (! e) e = ems_h; set_pick_em(e); return(1); break; case LX_KEYSYM_Escape: stop_picking(); return(1); break; } return((*pick_op)(ks,state,str,len,time)); } static void start_picking(EM *init_e, int (*op)(LX_KEYSYM, unsigned int, const char *, int, LX_TIME)) { if (! init_e) { lx_Bell(xc,0); return; } pick_op = op; raisebox(); set_pick_em(init_e); keystroke = &keystroke_pick; } static int pick_op_focus( LX_KEYSYM ks, unsigned int state, const char *str __attribute__((__unused__)), int len __attribute__((__unused__)), LX_TIME time ) { switch (ks) { case LX_KEYSYM_Return: focus_on(pick_em); stop_picking(); return(1); break; } (void)state; (void)time; return(0); } static int pick_op_raise( LX_KEYSYM ks, unsigned int state __attribute__((__unused__)), const char *str __attribute__((__unused__)), int len __attribute__((__unused__)), LX_TIME time __attribute__((__unused__)) ) { EM *o; switch (ks) { case LX_KEYSYM_K: case LX_KEYSYM_k: o = pick_em->blink; if (o) { em_unlink(pick_em); em_link_above(pick_em,o); lx_ConfigureWindow_va(xc,pick_em->pwin,LX_CWV_StackMode(LX_STACKMODE_Above),LX_CWV_Sibling(o->pwin),LX_CWV_END); } else { lx_Bell(xc,0); } return(1); break; case LX_KEYSYM_J: case LX_KEYSYM_j: o = pick_em->flink; if (o) { em_unlink(pick_em); em_link_below(pick_em,o); lx_ConfigureWindow_va(xc,pick_em->pwin,LX_CWV_StackMode(LX_STACKMODE_Below),LX_CWV_Sibling(o->pwin),LX_CWV_END); } else { lx_Bell(xc,0); } return(1); break; } return(0); } static void keypress(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 (keystroke && (*keystroke)(ks,e->state,&s[0],l,e->time)) return; 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_F: case LX_KEYSYM_f: start_picking(focus,&pick_op_focus); 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_Q: case LX_KEYSYM_q: exit(0); break; case LX_KEYSYM_R: case LX_KEYSYM_r: if (ems_h && ems_h->flink) start_picking(ems_h,&pick_op_raise); else lx_Bell(xc,0); return; break; case LX_KEYSYM_Z: case LX_KEYSYM_z: if (focus) { start_resize(focus,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 configurenotify(const LX_EVENT_ConfigureNotify *e) { printf("ConfigureNotify: "); if (e->window != contwin) { printf("[*** win=%lx] ",(unsigned long int)e->window); } printf("%dx%d+%d+%d, bw %d\n",e->w,e->h,e->x,e->y,e->borderwidth); } static void start_dragging_click(const LX_EVENT_ButtonPress *e) { int thirdx; int thirdy; resize_down_x = e->eventx + boxrect.x; resize_down_y = e->eventy + boxrect.y; resize_orig = boxrect; do <"found"> { for (thirdx=2;thirdx>=0;thirdx--) { for (thirdy=2;thirdy>=0;thirdy--) { if (e->childw == resize_cwin[thirdx][thirdy]) break <"found">; } } printf("resize press: can't find subwindow\n"); return; } while (0); switch (thirdx+(thirdy*3)) { case 0: resize_dragging = DRAG_L | DRAG_T; resize_down_x = boxrect.x; resize_down_y = boxrect.y; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,-e->eventx,-e->eventy); break; case 1: resize_dragging = DRAG_T; resize_down_y = boxrect.y; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,0,-e->eventy); break; case 2: resize_dragging = DRAG_R | DRAG_T; resize_down_x = boxrect.x + boxrect.w - 1; resize_down_y = boxrect.y; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,boxrect.w-1-e->eventx,-e->eventy); break; case 3: resize_dragging = DRAG_L; resize_down_x = boxrect.x; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,-e->eventx,0); break; case 4: resize_dragging = DRAG_L | DRAG_R | DRAG_T | DRAG_B; break; case 5: resize_dragging = DRAG_R; resize_down_x = boxrect.x + boxrect.w - 1; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,boxrect.w-1-e->eventx,0); break; case 6: resize_dragging = DRAG_L | DRAG_B; resize_down_x = boxrect.x; resize_down_y = boxrect.y + boxrect.h - 1; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,-e->eventx,boxrect.h-1-e->eventy); break; case 7: resize_dragging = DRAG_B; resize_down_y = boxrect.y + boxrect.h - 1; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,0,boxrect.h-1-e->eventy); break; case 8: resize_dragging = DRAG_R | DRAG_B; resize_down_x = boxrect.x + boxrect.w - 1; resize_down_y = boxrect.y + boxrect.h - 1; lx_WarpPointer(xc,LX_WINDOW_None,LX_WINDOW_None,0,0,0,0,boxrect.w-e->eventx,boxrect.h-1-e->eventy); break; default: printf("resize press; impossible third: (%d,%d)\n",thirdx,thirdy); return; break; } lx_ChangeActivePointerGrab(xc,LX_EM_ButtonPress|LX_EM_PointerMotionHint|LX_EM_PointerMotion,resize_cursors[cwin_cursx[thirdx][thirdy]],e->time); reset_cwins((LX_RECTANGLE){.x=0,.y=0,.w=geom.w*charwidth,.h=geom.h*baselineskip}); } static void buttonpress(const LX_EVENT_ButtonPress *e) { if (resizing && (e->eventw == resize_parent)) { if (resize_set_ui(RUI_POINTER)) return; if (resize_dragging) { switch (e->button) { case 1: stop_dragging(e->time); break; case 2: stop_dragging(e->time); apply_resize(); break; case 3: cancel_drag(e->time); break; } } else { switch (e->button) { case 1: start_dragging_click(e); break; case 2: apply_resize(); break; case 3: (*cancel_resize)(); break; } } } } static void resize_drag_to(int x, int y) { int x1; int y1; int x2; int y2; int pw; int ph; LX_RECTANGLE new; pw = geom.w * charwidth; ph = geom.h * baselineskip; x -= resize_down_x; y -= resize_down_y; x1 = resize_orig.x; y1 = resize_orig.y; x2 = x1 + resize_orig.w; y2 = y1 + resize_orig.h; if (resize_dragging & DRAG_L) x1 += x; if (resize_dragging & DRAG_R) x2 += x; if (resize_dragging & DRAG_T) y1 += y; if (resize_dragging & DRAG_B) y2 += y; if (x1 < 0) { if (resize_dragging & DRAG_R) x2 -= x1; x1 = 0; } if (y1 < 0) { if (resize_dragging & DRAG_B) y2 -= y1; y1 = 0; } if (x2 > pw) { if (resize_dragging & DRAG_L) x1 -= x2 - pw; x2 = pw; } if (y2 > ph) { if (resize_dragging & DRAG_T) y1 -= y2 - ph; y2 = ph; } x1 = (x1 + (charwidth/2)) / charwidth; x2 = (x2 + (charwidth/2)) / charwidth; y1 = (y1 + (baselineskip/2)) / baselineskip; y2 = (y2 + (baselineskip/2)) / baselineskip; if (x1 >= x2) { if (resize_dragging & DRAG_L) x1 = x2-1; else x2 = x1+1; } if (y1 >= y2) { if (resize_dragging & DRAG_T) y1 = y2-1; else y2 = y1+1; } if ((x1 < 0) || (x2 > geom.w) || (y1 < 0) || (y2 > geom.w) || (x1 >= x2) || (y1 >= y2)) abort(); new.x = x1 * charwidth; new.y = y1 * baselineskip; new.w = (x2 - x1) * charwidth; new.h = (y2 - y1) * baselineskip; if ( (boxrect.x != new.x) || (boxrect.y != new.y) || (boxrect.w != new.w) || (boxrect.h != new.h) ) { boxrect = new; reset_boxwins(1); } } static void resize_querypointer_done(void *arg __attribute__((__unused__))) { if (resizing && resize_dragging) { resize_drag_to(resize_qpstatus.winx,resize_qpstatus.winy); } } static void motionnotify(const LX_EVENT_MotionNotify *e) { if (resizing && resize_dragging) { resize_drag_to(e->eventx,e->eventy); if (e->detail == LX_MOTIONDETAIL_Hint) { lx_op_callback(lx_QueryPointer_status(xc,resize_parent,&resize_qpstatus),&resize_querypointer_done,0,0); } } } static void handle_event(LX_CONN *c, LX_EVENT *e) { if (c != xc) abort(); switch (e->type) { case LX_EV_KeyRelease: case LX_EV_NoExposure: break; case LX_EV_MappingNotify: lx_kbmap_update(xc,&e->u.MappingNotify); break; case LX_EV_ConfigureNotify: configurenotify(&e->u.ConfigureNotify); break; case LX_EV_KeyPress: keypress(&e->u.KeyPress); break; case LX_EV_ButtonPress: buttonpress(&e->u.ButtonPress); break; case LX_EV_MotionNotify: motionnotify(&e->u.MotionNotify); break; default: printf("Event type %d\n",(int)e->type); break; } } static void dec_pending(void *arg __attribute__((__unused__))) { 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),&dec_pending,c,0); } else { 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 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 (haveshape < 0) { haveshape = 0; } else { lx_op_drop(lx_shape_query(xc,&haveshape)); } 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(); return(1); }