diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/port/win-x11a.c | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/port/win-x11a.c')
| -rw-r--r-- | emu/port/win-x11a.c | 1424 |
1 files changed, 1424 insertions, 0 deletions
diff --git a/emu/port/win-x11a.c b/emu/port/win-x11a.c new file mode 100644 index 00000000..19c7a179 --- /dev/null +++ b/emu/port/win-x11a.c @@ -0,0 +1,1424 @@ +/* + * This implementation of the screen functions for X11 uses the + * portable implementation of the Inferno drawing operations (libmemdraw) + * to do the work, then has flushmemscreen copy the result to the X11 display. + * Thus it potentially supports all colour depths but with a possible + * performance penalty (although it tries to use the X11 shared memory extension + * to copy the result to the screen, which might reduce the latter). + * + * CraigN + */ + +#define _GNU_SOURCE 1 +#include "dat.h" +#include "fns.h" +#undef log2 +#include <draw.h> +#include "cursor.h" +#include "keyboard.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define Colormap XColormap +#define Cursor XCursor +#define Display XDisplay +#define Drawable XDrawable +#define Font XFont +#define GC XGC +#define Point XPoint +#define Rectangle XRectangle +#define Screen XScreen +#define Visual XVisual +#define Window XWindow + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/extensions/XShm.h> + +#include "keysym2ucs.h" + +#undef Colormap +#undef Cursor +#undef Display +#undef XDrawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window + +#include <sys/ipc.h> +#include <sys/shm.h> + +static int displaydepth; +extern ulong displaychan; + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +/* screen data .... */ +static uchar* gscreendata; +static uchar* xscreendata; + +XColor map[256]; /* Inferno colormap array */ +XColor mapr[256]; /* Inferno red colormap array */ +XColor mapg[256]; /* Inferno green colormap array */ +XColor mapb[256]; /* Inferno blue colormap array */ +XColor map7[128]; /* Inferno colormap array */ +uchar map7to8[128][2]; + +/* for copy/paste, lifted from plan9ports via drawterm */ +static Atom clipboard; +static Atom utf8string; +static Atom targets; +static Atom text; +static Atom compoundtext; + +static Atom cursorchange; + +static XColormap xcmap; /* Default shared colormap */ +static int infernotox11[256]; /* Values for mapping between */ +static int infernortox11[256]; /* Values for mapping between */ +static int infernogtox11[256]; /* Values for mapping between */ +static int infernobtox11[256]; /* Values for mapping between */ +static int triedscreen; +static XDrawable xdrawable; +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xsetcursor(XEvent*); +static void xkbdproc(void*); +static void xdestroy(XEvent*); +static void xselect(XEvent*, XDisplay*); +static void xproc(void*); +static void xinitscreen(int, int, ulong, ulong*, int*); +static void initmap(XWindow, ulong, ulong*, int*); +static XGC creategc(XDrawable); +static void graphicsgmap(XColor*, int); +static void graphicscmap(XColor*); +static void graphicsrgbmap(XColor*, XColor*, XColor*); +static int xscreendepth; +static XDisplay* xdisplay; /* used holding draw lock */ +static XDisplay* xmcon; /* used only in xproc */ +static XDisplay* xkbdcon; /* used only in xkbdproc */ +static XDisplay* xsnarfcon; /* used holding clip.lk */ +static XVisual *xvis; +static XGC xgc; +static XImage *img; +static int is_shm; +static XShmSegmentInfo *shminfo; + +static int putsnarf, assertsnarf; + +/* + * The documentation for the XSHM extension implies that if the server + * supports XSHM but is not the local machine, the XShm calls will + * return False; but this turns out not to be the case. Instead, the + * server throws a BadAccess error. So, we need to catch X errors + * around all of our XSHM calls, sigh. + */ +static int shm_got_x_error = False; +static XErrorHandler old_handler = 0; +static XErrorHandler old_io_handler = 0; + +static int +shm_ehandler(XDisplay *dpy, XErrorEvent *error) +{ + shm_got_x_error = 1; + return 0; +} + +static void +clean_errhandlers(void) +{ + /* remove X11 error handler(s) */ + if(old_handler) + XSetErrorHandler(old_handler); + old_handler = 0; + if(old_io_handler) + XSetErrorHandler(old_io_handler); + old_io_handler = 0; +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + ulong c; + int depth; + + Xsize &= ~0x3; /* ensure multiple of 4 */ + + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + + c = displaychan; + if(c == 0) + c = CMAP8; + + if(!triedscreen){ + xinitscreen(Xsize, Ysize, c, chan, d); + /* + * moved xproc from here to end since it could cause an expose event and + * hence a flushmemscreen before xscreendata is initialized + */ + } + else{ + *chan = displaychan; + *d = displaydepth; + } + + *width = (Xsize/4)*(*d/8); + *softscreen = 1; + displaychan = *chan; + displaydepth = *d; + + /* check for X Shared Memory Extension */ + is_shm = XShmQueryExtension(xdisplay); + + if(is_shm) { + shminfo = malloc(sizeof(XShmSegmentInfo)); + if(shminfo == nil) { + fprint(2, "emu: cannot allocate XShmSegmentInfo\n"); + cleanexit(0); + } + + /* setup to catch X11 error(s) */ + XSync(xdisplay, 0); + shm_got_x_error = 0; + if(old_handler != shm_ehandler) + old_handler = XSetErrorHandler(shm_ehandler); + if(old_io_handler != shm_ehandler) + old_io_handler = XSetErrorHandler(shm_ehandler); + + img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, + NULL, shminfo, Xsize, Ysize); + XSync(xdisplay, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + is_shm = 0; + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + goto next; + } + + if(img == nil) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + cleanexit(0); + } + + shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, + IPC_CREAT|0777); + shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0); + shminfo->readOnly = True; + + if(!XShmAttach(xdisplay, shminfo)) { + fprint(2, "emu: cannot allocate virtual screen buffer\n"); + cleanexit(0); + } + XSync(xdisplay, 0); + + /* + * Delete the shared segment right now; the segment + * won't actually go away until both the client and + * server have deleted it. The server will delete it + * as soon as the client disconnects, so we might as + * well delete our side now as later. + */ + shmctl(shminfo->shmid, IPC_RMID, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + is_shm = 0; + XDestroyImage(img); + XSync(xdisplay, 0); + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + goto next; + } + + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + if(gscreendata == nil) { + fprint(2, "emu: cannot allocate screen buffer (%dx%d)\n", Xsize*Ysize); + cleanexit(0); + } + xscreendata = (uchar*)img->data; + + clean_errhandlers(); + } + next: + if(!is_shm) { + depth = xscreendepth; + if(depth == 24) + depth = 32; + + /* allocate virtual screen */ + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + xscreendata = malloc(Xsize * Ysize * (depth >> 3)); + if(!gscreendata || !xscreendata) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + return 0; + } + img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0, + (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3)); + if(img == nil) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + return 0; + } + + } + + if(!triedscreen){ + triedscreen = 1; + kproc("xproc", xproc, xmcon, 0); + kproc("xkbdproc", xkbdproc, xkbdcon, KPX11); /* silly stack size for bloated X11 */ + } + + return gscreendata; +} + +void +flushmemscreen(Rectangle r) +{ + int x, y, width, height, dx; + uchar *p, *ep, *cp; + + // Clip to screen + if(r.min.x < 0) + r.min.x = 0; + if(r.min.y < 0) + r.min.y = 0; + if(r.max.x >= Xsize) + r.max.x = Xsize - 1; + if(r.max.y >= Ysize) + r.max.y = Ysize - 1; + + // is there anything left ... + width = r.max.x-r.min.x; + height = r.max.y-r.min.y; + if((width < 1) | (height < 1)) + return; + + // Blit the pixel data ... + if(displaydepth == 32){ + u32int v, w, *dp, *wp, *edp; + + dx = Xsize - width; + dp = (unsigned int *)(gscreendata + (r.min.y * Xsize + r.min.x) * 4); + wp = (unsigned int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + edp = (unsigned int *)(gscreendata + (r.max.y * Xsize + r.max.x) * 4); + while (dp < edp) { + const unsigned int *lp = dp + width; + + while (dp < lp){ + v = *dp++; + w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0; + *wp++ = w; + } + + dp += dx; + wp += dx; + } + } + else if(displaydepth == 8){ + if(xscreendepth == 24 || xscreendepth == 32) { + u32int *wp; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *wp++ = infernotox11[*p++]; + + p += dx; + wp += dx; + } + + } else if(xscreendepth == 24) { + int v; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp){ + v = infernotox11[*p++]; + cp[0] = (v>>16)&0xff; + cp[1] = (v>>8)&0xff; + cp[2] = (v>>0)&0xff; + cp += 3; + } + + p += dx; + cp += 3*dx; + } + + } else if(xscreendepth == 16) { + u16int *sp; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *sp++ = infernotox11[*p++]; + + p += dx; + sp += dx; + } + + } else if(xscreendepth == 8) { + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + r.min.y * Xsize + r.min.x; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *cp++ = infernotox11[*p++]; + + p += dx; + cp += dx; + } + + } else { + for (y = r.min.y; y < r.max.y; y++) { + x = r.min.x; + p = gscreendata + y * Xsize + x; + while (x < r.max.x) + XPutPixel(img, x++, y, infernotox11[*p++]); + } + } + } + else{ + fprint(2, "emu: bad display depth %d\n", displaydepth); + cleanexit(0); + } + + /* Display image on X11 */ + if(is_shm) + XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, width, height, 0); + else + XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, width, height); + XSync(xdisplay, 0); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +void +setpointer(int x, int y) +{ + drawqlock(); + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y); + XFlush(xdisplay); + drawqunlock(); +} + +static void +xkbdproc(void *arg) +{ + XEvent event; + XDisplay *xd; + + xd = arg; + + /* BEWARE: the value of up is not defined for this proc on some systems */ + + XSelectInput(xd, xdrawable, KeyPressMask); + for(;;){ + XNextEvent(xd, &event); + xkeyboard(&event); + xsetcursor(&event); + } +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + XDisplay *xd; + + closepgrp(up->env->pgrp); + closefgrp(up->env->fgrp); + closeegrp(up->env->egrp); + closesigs(up->env->sigs); + + xd = arg; + mask = ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + Button4MotionMask| + Button5MotionMask| + ExposureMask| + StructureNotifyMask; + + XSelectInput(xd, xdrawable, mask); + for(;;){ + XNextEvent(xd, &event); + xselect(&event, xd); + xmouse(&event); + xexpose(&event); + xdestroy(&event); + } +} + +/* + * this crud is here because X11 can put huge amount of data + * on the stack during keyboard translation and cursor changing(!). + * we do both in a dedicated process with lots of stack, perhaps even enough. + */ + +enum { + CursorSize= 32 /* biggest cursor size */ +}; + +typedef struct ICursor ICursor; +struct ICursor { + ulong inuse; + int modify; + int hotx; + int hoty; + int w; + int h; + uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */ + uchar mask[(CursorSize/8)*CursorSize]; +}; +static ICursor icursor; + +static void +xcurslock(void) +{ + while(_tas(&icursor.inuse) != 0) + osyield(); +} + +static void +xcursunlock(void) +{ + icursor.inuse = 0; +} + +static void +xcursnotify(void) +{ + XClientMessageEvent e; + + memset(&e, 0, sizeof e); + e.type = ClientMessage; + e.window = xdrawable; + e.message_type = cursorchange; + e.format = 8; + XSendEvent(xkbdcon, xdrawable, True, KeyPressMask, (XEvent*)&e); + XFlush(xkbdcon); +} + +void +drawcursor(Drawcursor* c) +{ + uchar *bs, *bc, *ps, *pm; + int i, j, w, h, bpl; + + if(c->data == nil){ + drawqlock(); + if(icursor.h != 0){ + xcurslock(); + icursor.h = 0; + icursor.modify = 1; + xcursunlock(); + } + xcursnotify(); + drawqunlock(); + return; + } + + drawqlock(); + xcurslock(); + icursor.modify = 0; /* xsetcursor will now ignore it */ + xcursunlock(); + + h = (c->maxy-c->miny)/2; /* image, then mask */ + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + w = bpl; + if(w > CursorSize/8) + w = CursorSize/8; + + ps = icursor.src; + pm = icursor.mask; + bc = c->data; + bs = c->data + h*bpl; + for(i = 0; i < h; i++){ + for(j = 0; j < bpl && j < w; j++) { + *ps++ = revbyte(bs[j]); + *pm++ = revbyte(bs[j] | bc[j]); + } + bs += bpl; + bc += bpl; + } + icursor.h = h; + icursor.w = w*8; + icursor.hotx = c->hotx; + icursor.hoty = c->hoty; + icursor.modify = 1; + xcursnotify(); + drawqunlock(); +} + +static void +xsetcursor(XEvent *e) +{ + ICursor ic; + XCursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + static XCursor xcursor; + + if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange) + return; + + xcurslock(); + if(icursor.modify == 0){ + xcursunlock(); + return; + } + icursor.modify = 0; + if(icursor.h == 0){ + xcursunlock(); + /* set the default system cursor */ + if(xcursor != 0) { + XFreeCursor(xkbdcon, xcursor); + xcursor = 0; + } + XUndefineCursor(xkbdcon, xdrawable); + XFlush(xkbdcon); + return; + } + ic = icursor; + xcursunlock(); + + xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h); + xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h); + + fg = map[0]; + bg = map[255]; + fg.pixel = infernotox11[0]; + bg.pixel = infernotox11[255]; + xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty); + if(xc != 0) { + XDefineCursor(xkbdcon, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xkbdcon, xcursor); + xcursor = xc; + } + XFreePixmap(xkbdcon, xsrc); + XFreePixmap(xkbdcon, xmask); + XFlush(xkbdcon); +} + +static void +xinitscreen(int xsize, int ysize, ulong c, ulong *chan, int *d) +{ + char *argv[2]; + char *dispname; + XWindow rootwin; + XWMHints hints; + XScreen *screen; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + + xdrawable = 0; + + dispname = getenv("DISPLAY"); + if(dispname == nil) + dispname = "not set"; + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + xvis = DefaultVisual(xdisplay, rootscreennum); + screen = DefaultScreenOfDisplay(xdisplay); + xcmap = DefaultColormapOfScreen(screen); + + *chan = CMAP8; + *d = 8; + + if(xvis->class != StaticColor) { + if(TYPE(c) == CGrey) + graphicsgmap(map, NBITS(c)); + else{ + graphicscmap(map); + graphicsrgbmap(mapr, mapg, mapb); + } + initmap(rootwin, c, chan, d); + } + + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, + InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + name.value = (uchar*)"inferno"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = normalhints.width = xsize; + normalhints.max_height = normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + classhints.res_name = "inferno"; + classhints.res_class = "Inferno"; + argv[0] = "inferno"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + + XMapWindow(xdisplay, xdrawable); + XFlush(xdisplay); + + xgc = creategc(xdrawable); + + xmcon = XOpenDisplay(NULL); + xsnarfcon = XOpenDisplay(NULL); + xkbdcon = XOpenDisplay(NULL); + if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + clipboard = XInternAtom(xmcon, "CLIPBOARD", False); + utf8string = XInternAtom(xmcon, "UTF8_STRING", False); + targets = XInternAtom(xmcon, "TARGETS", False); + text = XInternAtom(xmcon, "TEXT", False); + compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False); + + cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False); + +} + +static void +graphicsgmap(XColor *map, int d) +{ + int i, j, s, m, p; + + s = 8-d; + m = 1; + while(--d >= 0) + m *= 2; + m = 255/(m-1); + for(i=0; i < 256; i++){ + j = (i>>s)*m; + p = 255-i; + map[p].red = map[p].green = map[p].blue = (255-j)*0x0101; + map[p].pixel = p; + map[p].flags = DoRed|DoGreen|DoBlue; + } +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + /* was idx = 255 - idx; */ + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +static void +graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb) +{ + int i; + + memset(mapr, 0, 256*sizeof(XColor)); + memset(mapg, 0, 256*sizeof(XColor)); + memset(mapb, 0, 256*sizeof(XColor)); + for(i=0; i < 256; i++){ + mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101; + mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i; + mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue; + } +} + +/* + * Initialize and install the Inferno colormap as a private colormap for this + * application. Inferno gets the best colors here when it has the cursor focus. + */ +static void +initmap(XWindow w, ulong cc, ulong *chan, int *d) +{ + XColor c; + int i; + + if(xscreendepth <= 1) + return; + + if(xvis->class == TrueColor || xvis->class == DirectColor) { + for(i = 0; i < 256; i++) { + c = map[i]; + /* find out index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc color\n"); + cleanexit(0); + } + infernotox11[map[i].pixel] = c.pixel; + if(xscreendepth >= 24){ + c = mapr[i]; + XAllocColor(xdisplay, xcmap, &c); + infernortox11[i] = (c.pixel>>16)&0xff; + c = mapg[i]; + XAllocColor(xdisplay, xcmap, &c); + infernogtox11[i] = (c.pixel>>8)&0xff; + c = mapb[i]; + XAllocColor(xdisplay, xcmap, &c); + infernobtox11[i] = (c.pixel>>0)&0xff; + } + } + if(TYPE(cc) != CGrey && cc != CMAP8 && xscreendepth >= 24){ + *chan = XRGB32; + *d = 32; + } + } + else if(xvis->class == PseudoColor) { + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) + infernotox11[i] = i; + } else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); + cleanexit(0); + } + infernotox11[map7to8[i][0]] = c.pixel; + infernotox11[map7to8[i][1]] = c.pixel; + } + } + } + else { + xtblbit = 0; + fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); + } + return; +} + +static void +xdestroy(XEvent *e) +{ + XDestroyWindowEvent *xe; + if(e->type != DestroyNotify) + return; + xe = (XDestroyWindowEvent*)e; + if(xe->window == xdrawable) + cleanexit(0); +} + +/* + * Disable generation of GraphicsExpose/NoExpose events in the XGC. + */ +static XGC +creategc(XDrawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + Rectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + +static void +xkeyboard(XEvent *e) +{ + int ind, md; + KeySym k; + + if(e->type == KeyPress && gkscanq != nil){ + uchar ch = e->xkey.keycode; + if(e->xany.type == KeyRelease) + ch |= 0x80; + qproduce(gkscanq, &ch, 1); + return; + } + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + md = e->xkey.state; + ind = 0; + if(md & ShiftMask) + ind = 1; + if(0){ + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); + + /* May have to try unshifted version */ + if(k == NoSymbol && ind == 1) + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); + }else + XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Space: + k = ' '; + break; +// case XK_Home: +// case XK_KP_Home: +// k = Khome; +// break; + case XK_Left: + case XK_KP_Left: + k = Left; + break; + case XK_Up: + case XK_KP_Up: + k = Up; + break; + case XK_Down: + case XK_KP_Down: + k = Down; + break; + case XK_Right: + case XK_KP_Right: + k = Right; + break; +// case XK_Page_Down: +// case XK_KP_Page_Down: +// k = Kpgdown; +// break; + case XK_End: + case XK_KP_End: + k = End; + break; +// case XK_Page_Up: +// case XK_KP_Page_Up: +// k = Kpgup; +// break; +// case XK_Insert: +// case XK_KP_Insert: +// k = Kins; +// break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + k = Latin; + break; + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Caps_Lock: + case XK_Shift_Lock: + + case XK_Meta_L: + case XK_Meta_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return; + default: /* not ISO-1 or tty control */ + if(k>0xff){ + k = keysym2ucs(k); /* supplied by X */ + if(k == -1) + return; + } + break; + } + } + + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(md & ControlMask) + k &= 0x9f; + if(0){ + if(k == '\t' && ind) + k = BackTab; + + if(md & Mod1Mask) + k = APP|(k&0xff); + } + if(k == NoSymbol) + return; + + gkbdputc(gkbdq, k); +} + +static void +xmouse(XEvent *e) +{ + int s, dbl; + XButtonEvent *be; + XMotionEvent *me; + XEvent motion; + int x, y, b; + static ulong lastb, lastt; + + if(putsnarf != assertsnarf){ + assertsnarf = putsnarf; + XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime); + if(clipboard != None) + XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime); + XFlush(xmcon); + } + + dbl = 0; + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + /* + * Fake message, just sent to make us announce snarf. + * Apparently state and button are 16 and 8 bits on + * the wire, since they are truncated by the time they + * get to us. + */ + if(be->send_event + && (~be->state&0xFFFF)==0 + && (~be->button&0xFF)==0) + return; + x = be->x; + y = be->y; + s = be->state; + if(be->button == lastb && be->time - lastt < DblTime) + dbl = 1; + lastb = be->button; + lastt = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + case 4: + s |= Button4Mask; + break; + case 5: + s |= Button5Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + x = be->x; + y = be->y; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + case 4: + s &= ~Button4Mask; + break; + case 5: + s &= ~Button5Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *) e; + + /* remove excess MotionNotify events from queue and keep last one */ + while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True) + me = (XMotionEvent *) &motion; + + s = me->state; + x = me->x; + y = me->y; + break; + default: + return; + } + + b = 0; + if(s & Button1Mask) + b |= 1; + if(s & Button2Mask) + b |= 2; + if(s & Button3Mask) + b |= 4; + if(s & Button4Mask) + b |= 8; + if(s & Button5Mask) + b |= 16; + if(dbl) + b |= 1<<8; + + mousetrack(b, x, y, 0); +} + +#include "x11-keysym2ucs.c" + +/* + * Cut and paste. Just couldn't stand to make this simple... + */ + +enum{ + SnarfSize= 100*1024 +}; + +typedef struct Clip Clip; +struct Clip +{ + char buf[SnarfSize]; + QLock lk; +}; +Clip clip; + +#undef long /* sic */ +#undef ulong + +static char* +_xgetsnarf(XDisplay *xd) +{ + uchar *data, *xdata; + Atom clipboard, type, prop; + unsigned long len, lastlen, dummy; + int fmt, i; + XWindow w; + + qlock(&clip.lk); + /* + * Have we snarfed recently and the X server hasn't caught up? + */ + if(putsnarf != assertsnarf) + goto mine; + + /* + * Is there a primary selection (highlighted text in an xterm)? + */ + clipboard = XA_PRIMARY; + w = XGetSelectionOwner(xd, XA_PRIMARY); + if(w == xdrawable){ + mine: + data = (uchar*)strdup(clip.buf); + goto out; + } + + /* + * If not, is there a clipboard selection? + */ + if(w == None && clipboard != None){ + clipboard = clipboard; + w = XGetSelectionOwner(xd, clipboard); + if(w == xdrawable) + goto mine; + } + + /* + * If not, give up. + */ + if(w == None){ + data = nil; + goto out; + } + + /* + * We should be waiting for SelectionNotify here, but it might never + * come, and we have no way to time out. Instead, we will clear + * local property #1, request our buddy to fill it in for us, and poll + * until he's done or we get tired of waiting. + * + * We should try to go for utf8string instead of XA_STRING, + * but that would add to the polling. + */ + prop = 1; + XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); + XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime); + XFlush(xd); + lastlen = 0; + for(i=0; i<10 || (lastlen!=0 && i<30); i++){ + osmillisleep(100); + XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType, + &type, &fmt, &dummy, &len, &data); + if(lastlen == len && len > 0) + break; + lastlen = len; + } + if(i == 10){ + data = nil; + goto out; + } + /* get the property */ + data = nil; + XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); + if((type != XA_STRING && type != utf8string) || len == 0){ + if(xdata) + XFree(xdata); + data = nil; + }else{ + if(xdata){ + data = (uchar*)strdup((char*)xdata); + XFree(xdata); + }else + data = nil; + } +out: + qunlock(&clip.lk); + return (char*)data; +} + +static void +_xputsnarf(XDisplay *xd, char *data) +{ + XButtonEvent e; + + if(strlen(data) >= SnarfSize) + return; + qlock(&clip.lk); + strcpy(clip.buf, data); + + /* leave note for mouse proc to assert selection ownership */ + putsnarf++; + + /* send mouse a fake event so snarf is announced */ + memset(&e, 0, sizeof e); + e.type = ButtonPress; + e.window = xdrawable; + e.state = ~0; + e.button = ~0; + XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e); + XFlush(xd); + qunlock(&clip.lk); +} + +static void +xselect(XEvent *e, XDisplay *xd) +{ + char *name; + XEvent r; + XSelectionRequestEvent *xe; + Atom a[4]; + + if(e->xany.type != SelectionRequest) + return; + + memset(&r, 0, sizeof r); + xe = (XSelectionRequestEvent*)e; +if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n", + xe->target, xe->requestor, xe->property, xe->selection); + r.xselection.property = xe->property; + if(xe->target == targets){ + a[0] = XA_STRING; + a[1] = utf8string; + a[2] = text; + a[3] = compoundtext; + + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)a, sizeof a); + }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){ + /* if the target is STRING we're supposed to reply with Latin1 XXX */ + qlock(&clip.lk); + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); + qunlock(&clip.lk); + }else{ + iprint("get %d\n", xe->target); + name = XGetAtomName(xd, xe->target); + if(name == nil) + iprint("XGetAtomName failed\n"); + else if(strcmp(name, "TIMESTAMP") != 0) + iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); + r.xselection.property = None; + } + + r.xselection.display = xe->display; + /* r.xselection.property filled above */ + r.xselection.target = xe->target; + r.xselection.type = SelectionNotify; + r.xselection.requestor = xe->requestor; + r.xselection.time = xe->time; + r.xselection.send_event = True; + r.xselection.selection = xe->selection; + XSendEvent(xd, xe->requestor, False, 0, &r); + XFlush(xd); +} + +char* +clipread(void) +{ + return _xgetsnarf(xsnarfcon); +} + +int +clipwrite(char *buf) +{ + _xputsnarf(xsnarfcon, buf); + return 0; +} |
