1 /* 2 * This implementation of the screen functions for X11 uses the 3 * portable implementation of the Inferno drawing operations (libmemdraw) 4 * to do the work, then has flushmemscreen copy the result to the X11 display. 5 * Thus it potentially supports all colour depths but with a possible 6 * performance penalty (although it tries to use the X11 shared memory extension 7 * to copy the result to the screen, which might reduce the latter). 8 * 9 * CraigN 10 */ 11 12 #define _GNU_SOURCE 1 13 #define XTHREADS 14 #include "dat.h" 15 #include "fns.h" 16 #undef log2 17 #include <draw.h> 18 #include "cursor.h" 19 #include "keyboard.h" 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <fcntl.h> 23 24 #define Colormap XColormap 25 #define Cursor XCursor 26 #define Display XDisplay 27 #define Drawable XDrawable 28 #define Font XFont 29 #define GC XGC 30 #define Point XPoint 31 #define Rectangle XRectangle 32 #define Screen XScreen 33 #define Visual XVisual 34 #define Window XWindow 35 36 #include <X11/Xlib.h> 37 #include <X11/Xatom.h> 38 #include <X11/Xutil.h> 39 #include <X11/keysym.h> 40 #include <X11/extensions/XShm.h> 41 42 #include "keysym2ucs.h" 43 44 #undef Colormap 45 #undef Cursor 46 #undef Display 47 #undef XDrawable 48 #undef Font 49 #undef GC 50 #undef Point 51 #undef Rectangle 52 #undef Screen 53 #undef Visual 54 #undef Window 55 56 #include <sys/ipc.h> 57 #include <sys/shm.h> 58 59 static int displaydepth; 60 extern ulong displaychan; 61 62 enum 63 { 64 DblTime = 300 /* double click time in msec */ 65 }; 66 67 /* screen data .... */ 68 static uchar* gscreendata; 69 static uchar* xscreendata; 70 71 XColor map[256]; /* Inferno colormap array */ 72 XColor mapr[256]; /* Inferno red colormap array */ 73 XColor mapg[256]; /* Inferno green colormap array */ 74 XColor mapb[256]; /* Inferno blue colormap array */ 75 XColor map7[128]; /* Inferno colormap array */ 76 uchar map7to8[128][2]; 77 78 /* for copy/paste, lifted from plan9ports via drawterm */ 79 static Atom clipboard; 80 static Atom utf8string; 81 static Atom targets; 82 static Atom text; 83 static Atom compoundtext; 84 85 static Atom cursorchange; 86 87 static XColormap xcmap; /* Default shared colormap */ 88 static int infernotox11[256]; /* Values for mapping between */ 89 static int infernortox11[256]; /* Values for mapping between */ 90 static int infernogtox11[256]; /* Values for mapping between */ 91 static int infernobtox11[256]; /* Values for mapping between */ 92 static int triedscreen; 93 static XDrawable xdrawable; 94 static void xexpose(XEvent*); 95 static void xmouse(XEvent*); 96 static void xkeyboard(XEvent*); 97 static void xsetcursor(XEvent*); 98 static void xkbdproc(void*); 99 static void xdestroy(XEvent*); 100 static void xselect(XEvent*, XDisplay*); 101 static void xproc(void*); 102 static void xinitscreen(int, int, ulong, ulong*, int*); 103 static void initxcmap(XWindow); 104 static XGC creategc(XDrawable); 105 static void graphicsgmap(XColor*, int); 106 static void graphicscmap(XColor*); 107 static void graphicsrgbmap(XColor*, XColor*, XColor*); 108 109 static int xscreendepth; 110 static XDisplay* xdisplay; /* used holding draw lock */ 111 static XDisplay* xmcon; /* used only in xproc */ 112 static XDisplay* xkbdcon; /* used only in xkbdproc */ 113 static XDisplay* xsnarfcon; /* used holding clip.lk */ 114 static XVisual *xvis; 115 static XGC xgc; 116 static XImage *img; 117 static int is_shm; 118 119 static int putsnarf, assertsnarf; 120 char *gkscanid = "emu_x11"; 121 122 /* 123 * The documentation for the XSHM extension implies that if the server 124 * supports XSHM but is not the local machine, the XShm calls will 125 * return False; but this turns out not to be the case. Instead, the 126 * server throws a BadAccess error. So, we need to catch X errors 127 * around all of our XSHM calls, sigh. 128 */ 129 static int shm_got_x_error = 0; 130 static XErrorHandler old_handler = 0; 131 static XErrorHandler old_io_handler = 0; 132 133 static int 134 shm_ehandler(XDisplay *dpy, XErrorEvent *error) 135 { 136 shm_got_x_error = 1; 137 return 0; 138 } 139 140 static void 141 clean_errhandlers(void) 142 { 143 /* remove X11 error handler(s) */ 144 if(old_handler) 145 XSetErrorHandler(old_handler); 146 old_handler = 0; 147 if(old_io_handler) 148 XSetErrorHandler(old_io_handler); 149 old_io_handler = 0; 150 } 151 152 static int 153 makesharedfb(void) 154 { 155 XShmSegmentInfo *shminfo; 156 157 shminfo = malloc(sizeof(XShmSegmentInfo)); 158 if(shminfo == nil) { 159 fprint(2, "emu: cannot allocate XShmSegmentInfo\n"); 160 cleanexit(0); 161 } 162 163 /* setup to catch X11 error(s) */ 164 XSync(xdisplay, 0); 165 shm_got_x_error = 0; 166 if(old_handler != shm_ehandler) 167 old_handler = XSetErrorHandler(shm_ehandler); 168 if(old_io_handler != shm_ehandler) 169 old_io_handler = XSetErrorHandler(shm_ehandler); 170 171 img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 172 NULL, shminfo, Xsize, Ysize); 173 XSync(xdisplay, 0); 174 175 /* did we get an X11 error? if so then try without shm */ 176 if(shm_got_x_error) { 177 free(shminfo); 178 shminfo = NULL; 179 clean_errhandlers(); 180 return 0; 181 } 182 183 if(img == nil) { 184 fprint(2, "emu: cannot allocate virtual screen buffer\n"); 185 cleanexit(0); 186 } 187 188 shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, IPC_CREAT|0777); 189 shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0); 190 shminfo->readOnly = True; 191 192 if(!XShmAttach(xdisplay, shminfo)) { 193 fprint(2, "emu: cannot allocate virtual screen buffer\n"); 194 cleanexit(0); 195 } 196 XSync(xdisplay, 0); 197 198 /* 199 * Delete the shared segment right now; the segment 200 * won't actually go away until both the client and 201 * server have deleted it. The server will delete it 202 * as soon as the client disconnects, so we might as 203 * well delete our side now as later. 204 */ 205 shmctl(shminfo->shmid, IPC_RMID, 0); 206 207 /* did we get an X11 error? if so then try without shm */ 208 if(shm_got_x_error) { 209 XDestroyImage(img); 210 XSync(xdisplay, 0); 211 free(shminfo); 212 shminfo = NULL; 213 clean_errhandlers(); 214 return 0; 215 } 216 217 gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); 218 if(gscreendata == nil) { 219 fprint(2, "emu: cannot allocate screen buffer (%dx%dx%d)\n", Xsize, Ysize, displaydepth); 220 cleanexit(0); 221 } 222 xscreendata = (uchar*)img->data; 223 224 clean_errhandlers(); 225 return 1; 226 } 227 228 uchar* 229 attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) 230 { 231 int depth; 232 233 Xsize &= ~0x3; /* ensure multiple of 4 */ 234 235 r->min.x = 0; 236 r->min.y = 0; 237 r->max.x = Xsize; 238 r->max.y = Ysize; 239 240 if(!triedscreen){ 241 xinitscreen(Xsize, Ysize, displaychan, chan, d); 242 /* 243 * moved xproc from here to end since it could cause an expose event and 244 * hence a flushmemscreen before xscreendata is initialized 245 */ 246 } 247 else{ 248 *chan = displaychan; 249 *d = displaydepth; 250 } 251 252 *width = (Xsize/4)*(*d/8); 253 *softscreen = 1; 254 displaychan = *chan; 255 displaydepth = *d; 256 257 /* check for X Shared Memory Extension */ 258 is_shm = XShmQueryExtension(xdisplay); 259 260 if(!is_shm || !makesharedfb()){ 261 is_shm = 0; 262 depth = xscreendepth; 263 if(depth == 24) 264 depth = 32; 265 266 /* allocate virtual screen */ 267 gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); 268 xscreendata = malloc(Xsize * Ysize * (depth >> 3)); 269 if(gscreendata == nil || xscreendata == nil) { 270 fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d[%d])\n", Xsize, Ysize, displaydepth, depth); 271 return 0; 272 } 273 img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0, 274 (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3)); 275 if(img == nil) { 276 fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d)\n", Xsize, Ysize, depth); 277 return 0; 278 } 279 280 } 281 282 if(!triedscreen){ 283 triedscreen = 1; 284 kproc("xproc", xproc, xmcon, 0); 285 kproc("xkbdproc", xkbdproc, xkbdcon, KPX11); /* silly stack size for bloated X11 */ 286 } 287 288 return gscreendata; 289 } 290 291 static void 292 copy32to32(Rectangle r) 293 { 294 int dx, width; 295 uchar *p, *ep, *cp; 296 u32int v, w, *dp, *wp, *edp, *lp; 297 298 width = Dx(r); 299 dx = Xsize - width; 300 dp = (u32int*)(gscreendata + (r.min.y * Xsize + r.min.x) * 4); 301 wp = (u32int*)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); 302 edp = (u32int*)(gscreendata + (r.max.y * Xsize + r.max.x) * 4); 303 while(dp < edp) { 304 lp = dp + width; 305 while(dp < lp){ 306 v = *dp++; 307 w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0; 308 *wp++ = w; 309 } 310 dp += dx; 311 wp += dx; 312 } 313 } 314 315 static void 316 copy8to32(Rectangle r) 317 { 318 int dx, width; 319 uchar *p, *ep, *lp; 320 u32int *wp; 321 322 width = Dx(r); 323 dx = Xsize - width; 324 p = gscreendata + r.min.y * Xsize + r.min.x; 325 wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); 326 ep = gscreendata + r.max.y * Xsize + r.max.x; 327 while(p < ep) { 328 lp = p + width; 329 while(p < lp) 330 *wp++ = infernotox11[*p++]; 331 p += dx; 332 wp += dx; 333 } 334 } 335 336 static void 337 copy8to24(Rectangle r) 338 { 339 int dx, width, v; 340 uchar *p, *cp, *ep, *lp; 341 342 width = Dx(r); 343 dx = Xsize - width; 344 p = gscreendata + r.min.y * Xsize + r.min.x; 345 cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3; 346 ep = gscreendata + r.max.y * Xsize + r.max.x; 347 while(p < ep) { 348 lp = p + width; 349 while(p < lp){ 350 v = infernotox11[*p++]; 351 cp[0] = (v>>16)&0xff; 352 cp[1] = (v>>8)&0xff; 353 cp[2] = (v>>0)&0xff; 354 cp += 3; 355 } 356 p += dx; 357 cp += 3*dx; 358 } 359 } 360 361 static void 362 copy8to16(Rectangle r) 363 { 364 int dx, width; 365 uchar *p, *ep, *lp; 366 u16int *sp; 367 368 width = Dx(r); 369 dx = Xsize - width; 370 p = gscreendata + r.min.y * Xsize + r.min.x; 371 sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2); 372 ep = gscreendata + r.max.y * Xsize + r.max.x; 373 while(p < ep) { 374 lp = p + width; 375 while(p < lp) 376 *sp++ = infernotox11[*p++]; 377 p += dx; 378 sp += dx; 379 } 380 } 381 382 static void 383 copy8to8(Rectangle r) 384 { 385 int dx, width; 386 uchar *p, *cp, *ep, *lp; 387 388 width = Dx(r); 389 dx = Xsize - width; 390 p = gscreendata + r.min.y * Xsize + r.min.x; 391 cp = xscreendata + r.min.y * Xsize + r.min.x; 392 ep = gscreendata + r.max.y * Xsize + r.max.x; 393 while(p < ep) { 394 lp = p + width; 395 while(p < lp) 396 *cp++ = infernotox11[*p++]; 397 p += dx; 398 cp += dx; 399 } 400 } 401 402 static void 403 copy8topixel(Rectangle r) 404 { 405 int x, y; 406 uchar *p; 407 408 /* mainly for 4-bit greyscale */ 409 for (y = r.min.y; y < r.max.y; y++) { 410 x = r.min.x; 411 p = gscreendata + y * Xsize + x; 412 while (x < r.max.x) 413 XPutPixel(img, x++, y, infernotox11[*p++]); 414 } 415 } 416 417 void 418 flushmemscreen(Rectangle r) 419 { 420 char chanbuf[16]; 421 422 // Clip to screen 423 if(r.min.x < 0) 424 r.min.x = 0; 425 if(r.min.y < 0) 426 r.min.y = 0; 427 if(r.max.x >= Xsize) 428 r.max.x = Xsize - 1; 429 if(r.max.y >= Ysize) 430 r.max.y = Ysize - 1; 431 if(r.max.x <= r.min.x || r.max.y <= r.min.y) 432 return; 433 434 switch(displaydepth){ 435 case 32: 436 copy32to32(r); 437 break; 438 case 8: 439 switch(xscreendepth){ 440 case 24: 441 /* copy8to24(r); */ /* doesn't happen? */ 442 /* break */ 443 case 32: 444 copy8to32(r); 445 break; 446 case 16: 447 copy8to16(r); 448 break; 449 case 8: 450 copy8to8(r); 451 break; 452 default: 453 copy8topixel(r); 454 break; 455 } 456 break; 457 default: 458 fprint(2, "emu: bad display depth %d chan %s xscreendepth %d\n", displaydepth, 459 chantostr(chanbuf, displaychan), xscreendepth); 460 cleanexit(0); 461 } 462 463 XLockDisplay(xdisplay); 464 /* Display image on X11 */ 465 if(is_shm) 466 XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r), 0); 467 else 468 XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r)); 469 XSync(xdisplay, 0); 470 XUnlockDisplay(xdisplay); 471 } 472 473 static int 474 revbyte(int b) 475 { 476 int r; 477 478 r = 0; 479 r |= (b&0x01) << 7; 480 r |= (b&0x02) << 5; 481 r |= (b&0x04) << 3; 482 r |= (b&0x08) << 1; 483 r |= (b&0x10) >> 1; 484 r |= (b&0x20) >> 3; 485 r |= (b&0x40) >> 5; 486 r |= (b&0x80) >> 7; 487 return r; 488 } 489 490 void 491 setpointer(int x, int y) 492 { 493 drawqlock(); 494 XLockDisplay(xdisplay); 495 XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y); 496 XFlush(xdisplay); 497 XUnlockDisplay(xdisplay); 498 drawqunlock(); 499 } 500 501 static void 502 xkbdproc(void *arg) 503 { 504 XEvent event; 505 XDisplay *xd; 506 507 xd = arg; 508 509 /* BEWARE: the value of up is not defined for this proc on some systems */ 510 511 XLockDisplay(xd); /* should be ours alone */ 512 XSelectInput(xd, xdrawable, KeyPressMask | KeyReleaseMask); 513 for(;;){ 514 XNextEvent(xd, &event); 515 xkeyboard(&event); 516 xsetcursor(&event); 517 } 518 } 519 520 static void 521 xproc(void *arg) 522 { 523 ulong mask; 524 XEvent event; 525 XDisplay *xd; 526 527 closepgrp(up->env->pgrp); 528 closefgrp(up->env->fgrp); 529 closeegrp(up->env->egrp); 530 closesigs(up->env->sigs); 531 532 xd = arg; 533 mask = ButtonPressMask| 534 ButtonReleaseMask| 535 PointerMotionMask| 536 Button1MotionMask| 537 Button2MotionMask| 538 Button3MotionMask| 539 Button4MotionMask| 540 Button5MotionMask| 541 ExposureMask| 542 StructureNotifyMask; 543 544 XLockDisplay(xd); /* should be ours alone */ 545 XSelectInput(xd, xdrawable, mask); 546 for(;;){ 547 XNextEvent(xd, &event); 548 xselect(&event, xd); 549 xmouse(&event); 550 xexpose(&event); 551 xdestroy(&event); 552 } 553 } 554 555 /* 556 * this crud is here because X11 can put huge amount of data 557 * on the stack during keyboard translation and cursor changing(!). 558 * we do both in a dedicated process with lots of stack, perhaps even enough. 559 */ 560 561 enum { 562 CursorSize= 32 /* biggest cursor size */ 563 }; 564 565 typedef struct ICursor ICursor; 566 struct ICursor { 567 int inuse; 568 int modify; 569 int hotx; 570 int hoty; 571 int w; 572 int h; 573 uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */ 574 uchar mask[(CursorSize/8)*CursorSize]; 575 }; 576 static ICursor icursor; 577 578 static void 579 xcurslock(void) 580 { 581 while(_tas(&icursor.inuse) != 0) 582 osyield(); 583 } 584 585 static void 586 xcursunlock(void) 587 { 588 coherence(); 589 icursor.inuse = 0; 590 } 591 592 static void 593 xcursnotify(void) 594 { 595 XClientMessageEvent e; 596 597 memset(&e, 0, sizeof e); 598 e.type = ClientMessage; 599 e.window = xdrawable; 600 e.message_type = cursorchange; 601 e.format = 8; 602 XLockDisplay(xdisplay); 603 XSendEvent(xdisplay, xdrawable, True, KeyPressMask, (XEvent*)&e); 604 XFlush(xdisplay); 605 XUnlockDisplay(xdisplay); 606 } 607 608 void 609 drawcursor(Drawcursor* c) 610 { 611 uchar *bs, *bc, *ps, *pm; 612 int i, j, w, h, bpl; 613 614 if(c->data == nil){ 615 drawqlock(); 616 if(icursor.h != 0){ 617 xcurslock(); 618 icursor.h = 0; 619 icursor.modify = 1; 620 xcursunlock(); 621 } 622 xcursnotify(); 623 drawqunlock(); 624 return; 625 } 626 627 drawqlock(); 628 xcurslock(); 629 icursor.modify = 0; /* xsetcursor will now ignore it */ 630 xcursunlock(); 631 632 h = (c->maxy-c->miny)/2; /* image, then mask */ 633 if(h > CursorSize) 634 h = CursorSize; 635 bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); 636 w = bpl; 637 if(w > CursorSize/8) 638 w = CursorSize/8; 639 640 ps = icursor.src; 641 pm = icursor.mask; 642 bc = c->data; 643 bs = c->data + h*bpl; 644 for(i = 0; i < h; i++){ 645 for(j = 0; j < bpl && j < w; j++) { 646 *ps++ = revbyte(bs[j]); 647 *pm++ = revbyte(bs[j] | bc[j]); 648 } 649 bs += bpl; 650 bc += bpl; 651 } 652 icursor.h = h; 653 icursor.w = w*8; 654 icursor.hotx = c->hotx; 655 icursor.hoty = c->hoty; 656 icursor.modify = 1; 657 xcursnotify(); 658 drawqunlock(); 659 } 660 661 static void 662 xsetcursor(XEvent *e) 663 { 664 ICursor ic; 665 XCursor xc; 666 XColor fg, bg; 667 Pixmap xsrc, xmask; 668 static XCursor xcursor; 669 670 if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange) 671 return; 672 673 xcurslock(); 674 if(icursor.modify == 0){ 675 xcursunlock(); 676 return; 677 } 678 icursor.modify = 0; 679 if(icursor.h == 0){ 680 xcursunlock(); 681 /* set the default system cursor */ 682 if(xcursor != 0) { 683 XFreeCursor(xkbdcon, xcursor); 684 xcursor = 0; 685 } 686 XUndefineCursor(xkbdcon, xdrawable); 687 XFlush(xkbdcon); 688 return; 689 } 690 ic = icursor; 691 xcursunlock(); 692 693 xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h); 694 xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h); 695 696 fg = map[0]; 697 bg = map[255]; 698 fg.pixel = infernotox11[0]; 699 bg.pixel = infernotox11[255]; 700 xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty); 701 if(xc != 0) { 702 XDefineCursor(xkbdcon, xdrawable, xc); 703 if(xcursor != 0) 704 XFreeCursor(xkbdcon, xcursor); 705 xcursor = xc; 706 } 707 XFreePixmap(xkbdcon, xsrc); 708 XFreePixmap(xkbdcon, xmask); 709 XFlush(xkbdcon); 710 } 711 712 typedef struct Mg Mg; 713 struct Mg 714 { 715 int code; 716 int bit; 717 int len; 718 ulong mask; 719 }; 720 721 static int 722 maskx(Mg* g, int code, ulong mask) 723 { 724 int i; 725 726 for(i=0; i<32; i++) 727 if(mask & (1<<i)) 728 break; 729 if(i == 32) 730 return 0; 731 g->code = code; 732 g->bit = i; 733 g->mask = mask; 734 for(g->len = 0; i<32 && (mask & (1<<i))!=0; i++) 735 g->len++; 736 return 1; 737 } 738 739 /* 740 * for a given depth, we need to check the available formats 741 * to find how many actual bits are used per pixel. 742 */ 743 static int 744 xactualdepth(int screenno, int depth) 745 { 746 XPixmapFormatValues *pfmt; 747 int i, n; 748 749 pfmt = XListPixmapFormats(xdisplay, &n); 750 for(i=0; i<n; i++) 751 if(pfmt[i].depth == depth) 752 return pfmt[i].bits_per_pixel; 753 return -1; 754 } 755 756 static int 757 xtruevisual(int screenno, int reqdepth, XVisualInfo *vi, ulong *chan) 758 { 759 XVisual *xv; 760 Mg r, g, b; 761 int pad, d; 762 ulong c; 763 char buf[30]; 764 765 if(XMatchVisualInfo(xdisplay, screenno, reqdepth, TrueColor, vi) || 766 XMatchVisualInfo(xdisplay, screenno, reqdepth, DirectColor, vi)){ 767 xv = vi->visual; 768 if(maskx(&r, CRed, xv->red_mask) && 769 maskx(&g, CGreen, xv->green_mask) && 770 maskx(&b, CBlue, xv->blue_mask)){ 771 d = xactualdepth(screenno, reqdepth); 772 if(d < 0) 773 return 0; 774 pad = d - (r.len + g.len + b.len); 775 if(0){ 776 fprint(2, "r: %8.8lux %d %d\ng: %8.8lux %d %d\nb: %8.8lux %d %d\n", 777 xv->red_mask, r.bit, r.len, xv->green_mask, g.bit, g.len, xv->blue_mask, b.bit, b.len); 778 } 779 if(r.bit > b.bit) 780 c = CHAN3(CRed, r.len, CGreen, g.len, CBlue, b.len); 781 else 782 c = CHAN3(CBlue, b.len, CGreen, g.len, CRed, r.len); 783 if(pad > 0) 784 c |= CHAN1(CIgnore, pad) << 24; 785 *chan = c; 786 xscreendepth = reqdepth; 787 if(0) 788 fprint(2, "chan=%s reqdepth=%d bits=%d\n", chantostr(buf, c), reqdepth, d); 789 return 1; 790 } 791 } 792 return 0; 793 } 794 795 static int 796 xmapvisual(int screenno, XVisualInfo *vi, ulong *chan) 797 { 798 if(XMatchVisualInfo(xdisplay, screenno, 8, PseudoColor, vi) || 799 XMatchVisualInfo(xdisplay, screenno, 8, StaticColor, vi)){ 800 *chan = CMAP8; 801 xscreendepth = 8; 802 return 1; 803 } 804 return 0; 805 } 806 807 static void 808 xinitscreen(int xsize, int ysize, ulong reqchan, ulong *chan, int *d) 809 { 810 char *argv[2]; 811 char *dispname; 812 XWindow rootwin; 813 XWMHints hints; 814 XVisualInfo xvi; 815 XScreen *screen; 816 int rootscreennum; 817 XTextProperty name; 818 XClassHint classhints; 819 XSizeHints normalhints; 820 XSetWindowAttributes attrs; 821 char buf[30]; 822 int i; 823 824 xdrawable = 0; 825 826 dispname = getenv("DISPLAY"); 827 if(dispname == nil) 828 dispname = "not set"; 829 XInitThreads(); 830 xdisplay = XOpenDisplay(NULL); 831 if(xdisplay == 0){ 832 fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); 833 cleanexit(0); 834 } 835 836 rootscreennum = DefaultScreen(xdisplay); 837 rootwin = DefaultRootWindow(xdisplay); 838 xscreendepth = DefaultDepth(xdisplay, rootscreennum); 839 xvis = DefaultVisual(xdisplay, rootscreennum); 840 screen = DefaultScreenOfDisplay(xdisplay); 841 xcmap = DefaultColormapOfScreen(screen); 842 843 if(reqchan == 0){ 844 *chan = 0; 845 if(xscreendepth <= 16){ /* try for better colour */ 846 xtruevisual(rootscreennum, 16, &xvi, chan) || 847 xtruevisual(rootscreennum, 15, &xvi, chan) || 848 xtruevisual(rootscreennum, 24, &xvi, chan) || 849 xmapvisual(rootscreennum, &xvi, chan); 850 }else{ 851 xtruevisual(rootscreennum, xscreendepth, &xvi, chan) || 852 xtruevisual(rootscreennum, 24, &xvi, chan); 853 } 854 if(*chan == 0){ 855 fprint(2, "emu: could not find suitable x11 pixel format for depth %d on this display\n", xscreendepth); 856 cleanexit(0); 857 } 858 reqchan = *chan; 859 *d = chantodepth(reqchan); 860 xvis = xvi.visual; 861 }else{ 862 *chan = reqchan; /* not every channel description will work */ 863 *d = chantodepth(reqchan); 864 if(*d != xactualdepth(rootscreennum, *d)){ 865 fprint(2, "emu: current x11 display configuration does not support %s (depth %d) directly\n", 866 chantostr(buf, reqchan), *d); 867 cleanexit(0); 868 } 869 } 870 871 if(xvis->class != StaticColor) { 872 if(TYPE(*chan) == CGrey) 873 graphicsgmap(map, NBITS(reqchan)); 874 else{ 875 graphicscmap(map); 876 graphicsrgbmap(mapr, mapg, mapb); 877 } 878 initxcmap(rootwin); 879 } 880 881 memset(&attrs, 0, sizeof(attrs)); 882 attrs.colormap = xcmap; 883 attrs.background_pixel = 0; 884 attrs.border_pixel = 0; 885 /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ 886 xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, 887 InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); 888 889 /* 890 * set up property as required by ICCCM 891 */ 892 memset(&name, 0, sizeof(name)); 893 name.value = (uchar*)"inferno"; 894 name.encoding = XA_STRING; 895 name.format = 8; 896 name.nitems = strlen((char*)name.value); 897 898 memset(&normalhints, 0, sizeof(normalhints)); 899 normalhints.flags = USSize|PMaxSize; 900 normalhints.max_width = normalhints.width = xsize; 901 normalhints.max_height = normalhints.height = ysize; 902 hints.flags = InputHint|StateHint; 903 hints.input = 1; 904 hints.initial_state = NormalState; 905 906 memset(&classhints, 0, sizeof(classhints)); 907 classhints.res_name = "inferno"; 908 classhints.res_class = "Inferno"; 909 argv[0] = "inferno"; 910 argv[1] = nil; 911 XSetWMProperties(xdisplay, xdrawable, 912 &name, /* XA_WM_NAME property for ICCCM */ 913 &name, /* XA_WM_ICON_NAME */ 914 argv, /* XA_WM_COMMAND */ 915 1, /* argc */ 916 &normalhints, /* XA_WM_NORMAL_HINTS */ 917 &hints, /* XA_WM_HINTS */ 918 &classhints); /* XA_WM_CLASS */ 919 920 XMapWindow(xdisplay, xdrawable); 921 XFlush(xdisplay); 922 923 xgc = creategc(xdrawable); 924 925 xmcon = XOpenDisplay(NULL); 926 xsnarfcon = XOpenDisplay(NULL); 927 xkbdcon = XOpenDisplay(NULL); 928 if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){ 929 fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); 930 cleanexit(0); 931 } 932 933 clipboard = XInternAtom(xmcon, "CLIPBOARD", False); 934 utf8string = XInternAtom(xmcon, "UTF8_STRING", False); 935 targets = XInternAtom(xmcon, "TARGETS", False); 936 text = XInternAtom(xmcon, "TEXT", False); 937 compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False); 938 939 cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False); 940 941 } 942 943 static void 944 graphicsgmap(XColor *map, int d) 945 { 946 int i, j, s, m, p; 947 948 s = 8-d; 949 m = 1; 950 while(--d >= 0) 951 m *= 2; 952 m = 255/(m-1); 953 for(i=0; i < 256; i++){ 954 j = (i>>s)*m; 955 p = 255-i; 956 map[p].red = map[p].green = map[p].blue = (255-j)*0x0101; 957 map[p].pixel = p; 958 map[p].flags = DoRed|DoGreen|DoBlue; 959 } 960 } 961 962 static void 963 graphicscmap(XColor *map) 964 { 965 int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; 966 967 for(r=0; r!=4; r++) { 968 for(g = 0; g != 4; g++) { 969 for(b = 0; b!=4; b++) { 970 for(v = 0; v!=4; v++) { 971 den=r; 972 if(g > den) 973 den=g; 974 if(b > den) 975 den=b; 976 /* divide check -- pick grey shades */ 977 if(den==0) 978 cr=cg=cb=v*17; 979 else { 980 num=17*(4*den+v); 981 cr=r*num/den; 982 cg=g*num/den; 983 cb=b*num/den; 984 } 985 idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); 986 /* was idx = 255 - idx; */ 987 map[idx].red = cr*0x0101; 988 map[idx].green = cg*0x0101; 989 map[idx].blue = cb*0x0101; 990 map[idx].pixel = idx; 991 map[idx].flags = DoRed|DoGreen|DoBlue; 992 993 v7 = v >> 1; 994 idx7 = r*32 + v7*16 + g*4 + b; 995 if((v & 1) == v7){ 996 map7to8[idx7][0] = idx; 997 if(den == 0) { /* divide check -- pick grey shades */ 998 cr = ((255.0/7.0)*v7)+0.5; 999 cg = cr; 1000 cb = cr; 1001 } 1002 else { 1003 num=17*15*(4*den+v7*2)/14; 1004 cr=r*num/den; 1005 cg=g*num/den; 1006 cb=b*num/den; 1007 } 1008 map7[idx7].red = cr*0x0101; 1009 map7[idx7].green = cg*0x0101; 1010 map7[idx7].blue = cb*0x0101; 1011 map7[idx7].pixel = idx7; 1012 map7[idx7].flags = DoRed|DoGreen|DoBlue; 1013 } 1014 else 1015 map7to8[idx7][1] = idx; 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 static void 1023 graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb) 1024 { 1025 int i; 1026 1027 memset(mapr, 0, 256*sizeof(XColor)); 1028 memset(mapg, 0, 256*sizeof(XColor)); 1029 memset(mapb, 0, 256*sizeof(XColor)); 1030 for(i=0; i < 256; i++){ 1031 mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101; 1032 mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i; 1033 mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue; 1034 } 1035 } 1036 1037 /* 1038 * Initialize and install the Inferno colormap as a private colormap for this 1039 * application. Inferno gets the best colors here when it has the cursor focus. 1040 */ 1041 static void 1042 initxcmap(XWindow w) 1043 { 1044 XColor c; 1045 int i; 1046 1047 if(xscreendepth <= 1) 1048 return; 1049 1050 switch(xvis->class){ 1051 case TrueColor: 1052 case DirectColor: 1053 for(i = 0; i < 256; i++) { 1054 c = map[i]; 1055 /* find index into colormap for our RGB */ 1056 if(!XAllocColor(xdisplay, xcmap, &c)) { 1057 fprint(2, "emu: win-x11 can't alloc color\n"); 1058 cleanexit(0); 1059 } 1060 infernotox11[map[i].pixel] = c.pixel; 1061 if(xscreendepth >= 24){ 1062 c = mapr[i]; 1063 XAllocColor(xdisplay, xcmap, &c); 1064 infernortox11[i] = (c.pixel>>16)&0xff; 1065 c = mapg[i]; 1066 XAllocColor(xdisplay, xcmap, &c); 1067 infernogtox11[i] = (c.pixel>>8)&0xff; 1068 c = mapb[i]; 1069 XAllocColor(xdisplay, xcmap, &c); 1070 infernobtox11[i] = (c.pixel>>0)&0xff; 1071 } 1072 } 1073 if(0){int i, j; for(i=0;i<256; i+=16){print("%3d", i); for(j=i; j<i+16; j++)print(" %2.2ux/%2.2ux/%2.2ux", infernortox11[j], infernogtox11[j],infernobtox11[j]); print("\n");}} 1074 /* TO DO: if the map(s) used give the identity map, don't use the map during copy */ 1075 break; 1076 1077 case PseudoColor: 1078 if(xtblbit == 0){ 1079 xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); 1080 XStoreColors(xdisplay, xcmap, map, 256); 1081 for(i = 0; i < 256; i++) 1082 infernotox11[i] = i; 1083 /* TO DO: the map is the identity, so don't need the map in copy */ 1084 } else { 1085 for(i = 0; i < 128; i++) { 1086 c = map7[i]; 1087 if(!XAllocColor(xdisplay, xcmap, &c)) { 1088 fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); 1089 cleanexit(0); 1090 } 1091 infernotox11[map7to8[i][0]] = c.pixel; 1092 infernotox11[map7to8[i][1]] = c.pixel; 1093 } 1094 } 1095 break; 1096 1097 default: 1098 xtblbit = 0; 1099 fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); 1100 break; 1101 } 1102 } 1103 1104 static void 1105 xdestroy(XEvent *e) 1106 { 1107 XDestroyWindowEvent *xe; 1108 if(e->type != DestroyNotify) 1109 return; 1110 xe = (XDestroyWindowEvent*)e; 1111 if(xe->window == xdrawable) 1112 cleanexit(0); 1113 } 1114 1115 /* 1116 * Disable generation of GraphicsExpose/NoExpose events in the XGC. 1117 */ 1118 static XGC 1119 creategc(XDrawable d) 1120 { 1121 XGCValues gcv; 1122 1123 gcv.function = GXcopy; 1124 gcv.graphics_exposures = False; 1125 return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); 1126 } 1127 1128 static void 1129 xexpose(XEvent *e) 1130 { 1131 Rectangle r; 1132 XExposeEvent *xe; 1133 1134 if(e->type != Expose) 1135 return; 1136 xe = (XExposeEvent*)e; 1137 r.min.x = xe->x; 1138 r.min.y = xe->y; 1139 r.max.x = xe->x + xe->width; 1140 r.max.y = xe->y + xe->height; 1141 drawqlock(); 1142 flushmemscreen(r); 1143 drawqunlock(); 1144 } 1145 1146 static void 1147 xkeyboard(XEvent *e) 1148 { 1149 int ind, md; 1150 KeySym k; 1151 1152 if(gkscanq != nil && (e->type == KeyPress || e->type == KeyRelease)){ 1153 uchar ch = e->xkey.keycode; 1154 if(e->xany.type == KeyRelease) 1155 ch |= 0x80; 1156 qproduce(gkscanq, &ch, 1); 1157 return; 1158 } 1159 1160 /* 1161 * I tried using XtGetActionKeysym, but it didn't seem to 1162 * do case conversion properly 1163 * (at least, with Xterminal servers and R4 intrinsics) 1164 */ 1165 if(e->xany.type != KeyPress) 1166 return; 1167 1168 md = e->xkey.state; 1169 ind = 0; 1170 if(md & ShiftMask) 1171 ind = 1; 1172 if(0){ 1173 k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); 1174 1175 /* May have to try unshifted version */ 1176 if(k == NoSymbol && ind == 1) 1177 k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); 1178 }else 1179 XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL); 1180 1181 if(k == XK_Multi_key || k == NoSymbol) 1182 return; 1183 if(k&0xFF00){ 1184 switch(k){ 1185 case XK_BackSpace: 1186 case XK_Tab: 1187 case XK_Escape: 1188 case XK_Delete: 1189 case XK_KP_0: 1190 case XK_KP_1: 1191 case XK_KP_2: 1192 case XK_KP_3: 1193 case XK_KP_4: 1194 case XK_KP_5: 1195 case XK_KP_6: 1196 case XK_KP_7: 1197 case XK_KP_8: 1198 case XK_KP_9: 1199 case XK_KP_Divide: 1200 case XK_KP_Multiply: 1201 case XK_KP_Subtract: 1202 case XK_KP_Add: 1203 case XK_KP_Decimal: 1204 k &= 0x7F; 1205 break; 1206 case XK_Linefeed: 1207 k = '\r'; 1208 break; 1209 case XK_KP_Space: 1210 k = ' '; 1211 break; 1212 case XK_Home: 1213 case XK_KP_Home: 1214 k = Home; 1215 break; 1216 case XK_Left: 1217 case XK_KP_Left: 1218 k = Left; 1219 break; 1220 case XK_Up: 1221 case XK_KP_Up: 1222 k = Up; 1223 break; 1224 case XK_Down: 1225 case XK_KP_Down: 1226 k = Down; 1227 break; 1228 case XK_Right: 1229 case XK_KP_Right: 1230 k = Right; 1231 break; 1232 case XK_Page_Down: 1233 case XK_KP_Page_Down: 1234 k = Pgdown; 1235 break; 1236 case XK_End: 1237 case XK_KP_End: 1238 k = End; 1239 break; 1240 case XK_Page_Up: 1241 case XK_KP_Page_Up: 1242 k = Pgup; 1243 break; 1244 case XK_Insert: 1245 case XK_KP_Insert: 1246 k = Ins; 1247 break; 1248 case XK_KP_Enter: 1249 case XK_Return: 1250 k = '\n'; 1251 break; 1252 case XK_Alt_L: 1253 case XK_Alt_R: 1254 k = Latin; 1255 break; 1256 case XK_Shift_L: 1257 case XK_Shift_R: 1258 case XK_Control_L: 1259 case XK_Control_R: 1260 case XK_Caps_Lock: 1261 case XK_Shift_Lock: 1262 1263 case XK_Meta_L: 1264 case XK_Meta_R: 1265 case XK_Super_L: 1266 case XK_Super_R: 1267 case XK_Hyper_L: 1268 case XK_Hyper_R: 1269 return; 1270 default: /* not ISO-1 or tty control */ 1271 if(k>0xff){ 1272 k = keysym2ucs(k); /* supplied by X */ 1273 if(k == -1) 1274 return; 1275 } 1276 break; 1277 } 1278 } 1279 1280 /* Compensate for servers that call a minus a hyphen */ 1281 if(k == XK_hyphen) 1282 k = XK_minus; 1283 /* Do control mapping ourselves if translator doesn't */ 1284 if(md & ControlMask) 1285 k &= 0x9f; 1286 if(0){ 1287 if(k == '\t' && ind) 1288 k = BackTab; 1289 1290 if(md & Mod1Mask) 1291 k = APP|(k&0xff); 1292 } 1293 if(k == NoSymbol) 1294 return; 1295 1296 gkbdputc(gkbdq, k); 1297 } 1298 1299 static void 1300 xmouse(XEvent *e) 1301 { 1302 int s, dbl; 1303 XButtonEvent *be; 1304 XMotionEvent *me; 1305 XEvent motion; 1306 int x, y, b; 1307 static ulong lastb, lastt; 1308 1309 if(putsnarf != assertsnarf){ 1310 assertsnarf = putsnarf; 1311 XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime); 1312 if(clipboard != None) 1313 XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime); 1314 XFlush(xmcon); 1315 } 1316 1317 dbl = 0; 1318 switch(e->type){ 1319 case ButtonPress: 1320 be = (XButtonEvent *)e; 1321 /* 1322 * Fake message, just sent to make us announce snarf. 1323 * Apparently state and button are 16 and 8 bits on 1324 * the wire, since they are truncated by the time they 1325 * get to us. 1326 */ 1327 if(be->send_event 1328 && (~be->state&0xFFFF)==0 1329 && (~be->button&0xFF)==0) 1330 return; 1331 x = be->x; 1332 y = be->y; 1333 s = be->state; 1334 if(be->button == lastb && be->time - lastt < DblTime) 1335 dbl = 1; 1336 lastb = be->button; 1337 lastt = be->time; 1338 switch(be->button){ 1339 case 1: 1340 s |= Button1Mask; 1341 break; 1342 case 2: 1343 s |= Button2Mask; 1344 break; 1345 case 3: 1346 s |= Button3Mask; 1347 break; 1348 case 4: 1349 s |= Button4Mask; 1350 break; 1351 case 5: 1352 s |= Button5Mask; 1353 break; 1354 } 1355 break; 1356 case ButtonRelease: 1357 be = (XButtonEvent *)e; 1358 x = be->x; 1359 y = be->y; 1360 s = be->state; 1361 switch(be->button){ 1362 case 1: 1363 s &= ~Button1Mask; 1364 break; 1365 case 2: 1366 s &= ~Button2Mask; 1367 break; 1368 case 3: 1369 s &= ~Button3Mask; 1370 break; 1371 case 4: 1372 s &= ~Button4Mask; 1373 break; 1374 case 5: 1375 s &= ~Button5Mask; 1376 break; 1377 } 1378 break; 1379 case MotionNotify: 1380 me = (XMotionEvent *) e; 1381 1382 /* remove excess MotionNotify events from queue and keep last one */ 1383 while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True) 1384 me = (XMotionEvent *) &motion; 1385 1386 s = me->state; 1387 x = me->x; 1388 y = me->y; 1389 break; 1390 default: 1391 return; 1392 } 1393 1394 b = 0; 1395 if(s & Button1Mask) 1396 b |= 1; 1397 if(s & Button2Mask) 1398 b |= 2; 1399 if(s & Button3Mask) 1400 b |= 4; 1401 if(s & Button4Mask) 1402 b |= 8; 1403 if(s & Button5Mask) 1404 b |= 16; 1405 if(dbl) 1406 b |= 1<<8; 1407 1408 mousetrack(b, x, y, 0); 1409 } 1410 1411 #include "x11-keysym2ucs.c" 1412 1413 /* 1414 * Cut and paste. Just couldn't stand to make this simple... 1415 */ 1416 1417 enum{ 1418 SnarfSize= 100*1024 1419 }; 1420 1421 typedef struct Clip Clip; 1422 struct Clip 1423 { 1424 char buf[SnarfSize]; 1425 QLock lk; 1426 }; 1427 Clip clip; 1428 1429 #undef long /* sic */ 1430 #undef ulong 1431 1432 static char* 1433 _xgetsnarf(XDisplay *xd) 1434 { 1435 uchar *data, *xdata; 1436 Atom clipboard, type, prop; 1437 unsigned long len, lastlen, dummy; 1438 int fmt, i; 1439 XWindow w; 1440 1441 qlock(&clip.lk); 1442 /* 1443 * Have we snarfed recently and the X server hasn't caught up? 1444 */ 1445 if(putsnarf != assertsnarf) 1446 goto mine; 1447 1448 /* 1449 * Is there a primary selection (highlighted text in an xterm)? 1450 */ 1451 clipboard = XA_PRIMARY; 1452 w = XGetSelectionOwner(xd, XA_PRIMARY); 1453 if(w == xdrawable){ 1454 mine: 1455 data = (uchar*)strdup(clip.buf); 1456 goto out; 1457 } 1458 1459 /* 1460 * If not, is there a clipboard selection? 1461 */ 1462 if(w == None && clipboard != None){ 1463 clipboard = clipboard; 1464 w = XGetSelectionOwner(xd, clipboard); 1465 if(w == xdrawable) 1466 goto mine; 1467 } 1468 1469 /* 1470 * If not, give up. 1471 */ 1472 if(w == None){ 1473 data = nil; 1474 goto out; 1475 } 1476 1477 /* 1478 * We should be waiting for SelectionNotify here, but it might never 1479 * come, and we have no way to time out. Instead, we will clear 1480 * local property #1, request our buddy to fill it in for us, and poll 1481 * until he's done or we get tired of waiting. 1482 * 1483 * We should try to go for utf8string instead of XA_STRING, 1484 * but that would add to the polling. 1485 */ 1486 prop = 1; 1487 XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); 1488 XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime); 1489 XFlush(xd); 1490 lastlen = 0; 1491 for(i=0; i<10 || (lastlen!=0 && i<30); i++){ 1492 osmillisleep(100); 1493 XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType, 1494 &type, &fmt, &dummy, &len, &data); 1495 if(lastlen == len && len > 0) 1496 break; 1497 lastlen = len; 1498 } 1499 if(i == 10){ 1500 data = nil; 1501 goto out; 1502 } 1503 /* get the property */ 1504 data = nil; 1505 XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, 1506 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); 1507 if((type != XA_STRING && type != utf8string) || len == 0){ 1508 if(xdata) 1509 XFree(xdata); 1510 data = nil; 1511 }else{ 1512 if(xdata){ 1513 data = (uchar*)strdup((char*)xdata); 1514 XFree(xdata); 1515 }else 1516 data = nil; 1517 } 1518 out: 1519 qunlock(&clip.lk); 1520 return (char*)data; 1521 } 1522 1523 static void 1524 _xputsnarf(XDisplay *xd, char *data) 1525 { 1526 XButtonEvent e; 1527 1528 if(strlen(data) >= SnarfSize) 1529 return; 1530 qlock(&clip.lk); 1531 strcpy(clip.buf, data); 1532 1533 /* leave note for mouse proc to assert selection ownership */ 1534 putsnarf++; 1535 1536 /* send mouse a fake event so snarf is announced */ 1537 memset(&e, 0, sizeof e); 1538 e.type = ButtonPress; 1539 e.window = xdrawable; 1540 e.state = ~0; 1541 e.button = ~0; 1542 XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e); 1543 XFlush(xd); 1544 qunlock(&clip.lk); 1545 } 1546 1547 static void 1548 xselect(XEvent *e, XDisplay *xd) 1549 { 1550 char *name; 1551 XEvent r; 1552 XSelectionRequestEvent *xe; 1553 Atom a[4]; 1554 1555 if(e->xany.type != SelectionRequest) 1556 return; 1557 1558 memset(&r, 0, sizeof r); 1559 xe = (XSelectionRequestEvent*)e; 1560 if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n", 1561 xe->target, xe->requestor, xe->property, xe->selection); 1562 r.xselection.property = xe->property; 1563 if(xe->target == targets){ 1564 a[0] = XA_STRING; 1565 a[1] = utf8string; 1566 a[2] = text; 1567 a[3] = compoundtext; 1568 1569 XChangeProperty(xd, xe->requestor, xe->property, xe->target, 1570 8, PropModeReplace, (uchar*)a, sizeof a); 1571 }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){ 1572 /* if the target is STRING we're supposed to reply with Latin1 XXX */ 1573 qlock(&clip.lk); 1574 XChangeProperty(xd, xe->requestor, xe->property, xe->target, 1575 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); 1576 qunlock(&clip.lk); 1577 }else{ 1578 iprint("get %d\n", xe->target); 1579 name = XGetAtomName(xd, xe->target); 1580 if(name == nil) 1581 iprint("XGetAtomName failed\n"); 1582 else if(strcmp(name, "TIMESTAMP") != 0) 1583 iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); 1584 r.xselection.property = None; 1585 } 1586 1587 r.xselection.display = xe->display; 1588 /* r.xselection.property filled above */ 1589 r.xselection.target = xe->target; 1590 r.xselection.type = SelectionNotify; 1591 r.xselection.requestor = xe->requestor; 1592 r.xselection.time = xe->time; 1593 r.xselection.send_event = True; 1594 r.xselection.selection = xe->selection; 1595 XSendEvent(xd, xe->requestor, False, 0, &r); 1596 XFlush(xd); 1597 } 1598 1599 char* 1600 clipread(void) 1601 { 1602 char *p; 1603 1604 if(xsnarfcon == nil) 1605 return nil; 1606 XLockDisplay(xsnarfcon); 1607 p = _xgetsnarf(xsnarfcon); 1608 XUnlockDisplay(xsnarfcon); 1609 return p; 1610 } 1611 1612 int 1613 clipwrite(char *buf) 1614 { 1615 if(xsnarfcon == nil) 1616 return 0; 1617 XLockDisplay(xsnarfcon); 1618 _xputsnarf(xsnarfcon, buf); 1619 XUnlockDisplay(xsnarfcon); 1620 return 0; 1621 } 1622