1 #include "u.h" 2 #include "lib.h" 3 #include "dat.h" 4 #include "fns.h" 5 #include "error.h" 6 7 #include <draw.h> 8 #include <memdraw.h> 9 #include <keyboard.h> 10 #include <cursor.h> 11 #include "screen.h" 12 13 #define argv0 "drawterm" 14 15 typedef struct Cursor Cursor; 16 17 #undef long 18 #define Font XFont 19 #define Screen XScreen 20 #define Display XDisplay 21 #define Cursor XCursor 22 23 #include <X11/Xlib.h> 24 #include <X11/Xatom.h> 25 #include <X11/Xutil.h> 26 #include <X11/IntrinsicP.h> 27 #include <X11/StringDefs.h> 28 #include <X11/keysym.h> 29 #include "keysym2ucs.h" 30 31 #undef Font 32 #undef Screen 33 #undef Display 34 #undef Cursor 35 #define long int 36 37 /* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */ 38 #define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) 39 40 enum 41 { 42 PMundef = ~0 /* undefined pixmap id */ 43 }; 44 45 /* 46 * Structure pointed to by X field of Memimage 47 */ 48 typedef struct Xmem Xmem; 49 struct Xmem 50 { 51 int pmid; /* pixmap id for screen ldepth instance */ 52 XImage *xi; /* local image if we currenty have the data */ 53 int dirty; 54 Rectangle dirtyr; 55 Rectangle r; 56 uintptr pc; /* who wrote into xi */ 57 }; 58 59 static int xgcfillcolor; 60 static int xgcfillcolor0; 61 static int xgcsimplecolor0; 62 static int xgcsimplepm0; 63 64 static XDisplay* xdisplay; /* used holding draw lock */ 65 static int xtblbit; 66 static int plan9tox11[256]; /* Values for mapping between */ 67 static int x11toplan9[256]; /* X11 and Plan 9 */ 68 static GC xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc; 69 static GC xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0; 70 static ulong xscreenchan; 71 static Drawable xscreenid; 72 static Visual *xvis; 73 74 static int xdraw(Memdrawparam*); 75 76 #define glenda_width 48 77 #define glenda_height 48 78 static unsigned short glenda_bits[] = { 79 0xffff, 0xffff, 0xffff, 0xffff, 0xffe9, 0xffff, 0x7fff, 0xffae, 0xffff, 80 0xffff, 0xffbe, 0xffff, 0x1fff, 0xff3f, 0xffff, 0xbfff, 0xfe6e, 0xffff, 81 0xbbff, 0xfcce, 0xffff, 0xffff, 0xf98c, 0xffff, 0xe5ff, 0xf31b, 0xffff, 82 0x87ff, 0xe617, 0xffff, 0x05ff, 0xdf37, 0xffff, 0x0fff, 0x7ffe, 0xffff, 83 0x1bff, 0xfffc, 0xfffa, 0x37ff, 0xfffc, 0xfffb, 0xd7ff, 0xfffc, 0xfff7, 84 0xcfff, 0xffff, 0xfff7, 0xcfff, 0xffff, 0xffef, 0xdfff, 0xffff, 0xffef, 85 0xafff, 0xffff, 0xffdf, 0xefff, 0xffff, 0xfff3, 0xdfff, 0xefff, 0xffd3, 86 0xdfff, 0xc7ff, 0xffdf, 0xefff, 0xefff, 0xffef, 0xcfff, 0xffff, 0xffcf, 87 0xdfff, 0xffff, 0xffd9, 0x9fff, 0x7fff, 0xffd0, 0xbfff, 0xffff, 0xffd7, 88 0x7fff, 0xbfff, 0xffd0, 0x3fff, 0x3fff, 0xffd9, 0x7fff, 0x3fff, 0xffcb, 89 0x3fff, 0xffff, 0xffdc, 0x3fff, 0xffff, 0xffdf, 0x3fff, 0xffff, 0xff9f, 90 0x3fff, 0xffff, 0xffdf, 0x8fff, 0xffff, 0xff9f, 0xa7ff, 0xffff, 0xffdf, 91 0xe3ff, 0xffff, 0xffcf, 0xe9ff, 0xffff, 0xffcf, 0xf1ff, 0xffff, 0xffef, 92 0xf3ff, 0xffff, 0xffe7, 0xf9ff, 0xffff, 0xffe7, 0x53ff, 0xffff, 0xffe1, 93 0x07ff, 0x7ffc, 0xffc6, 0x17ff, 0xeff0, 0xffee, 0xffff, 0xc781, 0xffe5, 94 0xffff, 0x8807, 0xffe0, 0xffff, 0x003f, 0xfff0, 0xffff, 0x1fff, 0xfffe 95 }; 96 97 /* 98 * Synchronize images between X bitmaps and in-memory bitmaps. 99 */ 100 static void 101 addrect(Rectangle *rp, Rectangle r) 102 { 103 if(rp->min.x >= rp->max.x) 104 *rp = r; 105 else 106 combinerect(rp, r); 107 } 108 109 static XImage* 110 getXdata(Memimage *m, Rectangle r) 111 { 112 uchar *p; 113 int x, y; 114 Xmem *xm; 115 Point xdelta, delta; 116 Point tp; 117 118 xm = m->X; 119 if(xm == nil) 120 return nil; 121 122 assert(xm != nil && xm->xi != nil); 123 124 if(xm->dirty == 0) 125 return xm->xi; 126 127 r = xm->dirtyr; 128 if(Dx(r)==0 || Dy(r)==0) 129 return xm->xi; 130 131 delta = subpt(r.min, m->r.min); 132 tp = xm->r.min; /* avoid unaligned access on digital unix */ 133 xdelta = subpt(r.min, tp); 134 135 XGetSubImage(xdisplay, xm->pmid, delta.x, delta.y, Dx(r), Dy(r), 136 AllPlanes, ZPixmap, xm->xi, xdelta.x, xdelta.y); 137 138 if(xtblbit && m->chan == CMAP8) 139 for(y=r.min.y; y<r.max.y; y++) 140 for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) 141 *p = x11toplan9[*p]; 142 143 xm->dirty = 0; 144 xm->dirtyr = Rect(0,0,0,0); 145 return xm->xi; 146 } 147 148 static void 149 putXdata(Memimage *m, Rectangle r) 150 { 151 Xmem *xm; 152 XImage *xi; 153 GC g; 154 Point xdelta, delta; 155 Point tp; 156 int x, y; 157 uchar *p; 158 159 xm = m->X; 160 if(xm == nil) 161 return; 162 163 assert(xm != nil); 164 assert(xm->xi != nil); 165 166 xi = xm->xi; 167 168 g = (m->chan == GREY1) ? xgccopy0 : xgccopy; 169 170 delta = subpt(r.min, m->r.min); 171 tp = xm->r.min; /* avoid unaligned access on digital unix */ 172 xdelta = subpt(r.min, tp); 173 174 if(xtblbit && m->chan == CMAP8) 175 for(y=r.min.y; y<r.max.y; y++) 176 for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) 177 *p = plan9tox11[*p]; 178 179 XPutImage(xdisplay, xm->pmid, g, xi, xdelta.x, xdelta.y, delta.x, delta.y, Dx(r), Dy(r)); 180 181 if(xtblbit && m->chan == CMAP8) 182 for(y=r.min.y; y<r.max.y; y++) 183 for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) 184 *p = x11toplan9[*p]; 185 } 186 187 static void 188 dirtyXdata(Memimage *m, Rectangle r) 189 { 190 Xmem *xm; 191 192 if((xm = m->X) != nil){ 193 xm->dirty = 1; 194 addrect(&xm->dirtyr, r); 195 } 196 } 197 198 Memimage* 199 xallocmemimage(Rectangle r, ulong chan, int pmid) 200 { 201 Memimage *m; 202 Xmem *xm; 203 XImage *xi; 204 int offset; 205 int d; 206 207 m = _allocmemimage(r, chan); 208 if(m == nil) 209 return nil; 210 if(chan != GREY1 && chan != xscreenchan) 211 return m; 212 213 d = m->depth; 214 xm = mallocz(sizeof(Xmem), 1); 215 if(pmid != PMundef) 216 xm->pmid = pmid; 217 else 218 xm->pmid = XCreatePixmap(xdisplay, xscreenid, Dx(r), Dy(r), (d==32) ? 24 : d); 219 220 if(m->depth == 24) 221 offset = r.min.x&(4-1); 222 else 223 offset = r.min.x&(31/m->depth); 224 r.min.x -= offset; 225 226 assert(wordsperline(r, m->depth) <= m->width); 227 228 xi = XCreateImage(xdisplay, xvis, m->depth==32?24:m->depth, ZPixmap, 0, 229 (char*)m->data->bdata, Dx(r), Dy(r), 32, m->width*sizeof(ulong)); 230 231 if(xi == nil){ 232 _freememimage(m); 233 return nil; 234 } 235 236 xm->xi = xi; 237 xm->pc = getcallerpc(&r); 238 xm->r = r; 239 240 /* 241 * Set the parameters of the XImage so its memory looks exactly like a 242 * Memimage, so we can call _memimagedraw on the same data. All frame 243 * buffers we've seen, and Plan 9's graphics code, require big-endian 244 * bits within bytes, but little endian byte order within pixels. 245 */ 246 xi->bitmap_unit = m->depth < 8 || m->depth == 24 ? 8 : m->depth; 247 xi->byte_order = LSBFirst; 248 xi->bitmap_bit_order = MSBFirst; 249 xi->bitmap_pad = 32; 250 xm->r = Rect(0,0,0,0); 251 XInitImage(xi); 252 XFlush(xdisplay); 253 254 m->X = xm; 255 return m; 256 } 257 258 void 259 xfillcolor(Memimage *m, Rectangle r, ulong v) 260 { 261 GC gc; 262 Xmem *dxm; 263 264 dxm = m->X; 265 assert(dxm != nil); 266 r = rectsubpt(r, m->r.min); 267 268 if(m->chan == GREY1){ 269 gc = xgcfill0; 270 if(xgcfillcolor0 != v){ 271 XSetForeground(xdisplay, gc, v); 272 xgcfillcolor0 = v; 273 } 274 }else{ 275 if(m->chan == CMAP8 && xtblbit) 276 v = plan9tox11[v]; 277 278 gc = xgcfill; 279 if(xgcfillcolor != v){ 280 XSetForeground(xdisplay, gc, v); 281 xgcfillcolor = v; 282 } 283 } 284 XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, Dx(r), Dy(r)); 285 } 286 287 /* 288 * Replacements for libmemdraw routines. 289 * (They've been underscored.) 290 */ 291 Memimage* 292 allocmemimage(Rectangle r, ulong chan) 293 { 294 return xallocmemimage(r, chan, PMundef); 295 } 296 297 void 298 freememimage(Memimage *m) 299 { 300 Xmem *xm; 301 302 if(m == nil) 303 return; 304 305 if(m->data->ref == 1){ 306 if((xm = m->X) != nil){ 307 if(xm->xi){ 308 xm->xi->data = nil; 309 XFree(xm->xi); 310 } 311 XFreePixmap(xdisplay, xm->pmid); 312 free(xm); 313 m->X = nil; 314 } 315 } 316 _freememimage(m); 317 } 318 319 void 320 memfillcolor(Memimage *m, ulong val) 321 { 322 _memfillcolor(m, val); 323 if(m->X){ 324 if((val & 0xFF) == 0xFF) 325 xfillcolor(m, m->r, _rgbatoimg(m, val)); 326 else 327 putXdata(m, m->r); 328 } 329 } 330 331 int 332 loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) 333 { 334 int n; 335 336 n = _loadmemimage(i, r, data, ndata); 337 if(n > 0 && i->X) 338 putXdata(i, r); 339 return n; 340 } 341 342 int 343 cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) 344 { 345 int n; 346 347 n = _cloadmemimage(i, r, data, ndata); 348 if(n > 0 && i->X) 349 putXdata(i, r); 350 return n; 351 } 352 353 ulong 354 pixelbits(Memimage *m, Point p) 355 { 356 if(m->X) 357 getXdata(m, Rect(p.x, p.y, p.x+1, p.y+1)); 358 return _pixelbits(m, p); 359 } 360 361 void 362 memimageinit(void) 363 { 364 static int didinit = 0; 365 366 if(didinit) 367 return; 368 369 didinit = 1; 370 _memimageinit(); 371 372 xfillcolor(memblack, memblack->r, 0); 373 xfillcolor(memwhite, memwhite->r, 1); 374 } 375 376 void 377 memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op) 378 { 379 Memdrawparam *par; 380 381 if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op)) == nil) 382 return; 383 _memimagedraw(par); 384 if(!xdraw(par)) 385 putXdata(dst, par->r); 386 } 387 388 static int 389 xdraw(Memdrawparam *par) 390 { 391 int dy, dx; 392 unsigned m; 393 Memimage *src, *dst, *mask; 394 Xmem *dxm, *sxm, *mxm; 395 GC gc; 396 Rectangle r, sr, mr; 397 ulong sdval; 398 399 dx = Dx(par->r); 400 dy = Dy(par->r); 401 src = par->src; 402 dst = par->dst; 403 mask = par->mask; 404 r = par->r; 405 sr = par->sr; 406 mr = par->mr; 407 sdval = par->sdval; 408 409 /* 410 * drawterm was distributed for years with 411 * "return 0;" right here. 412 * maybe we should give up on all this? 413 */ 414 415 if((dxm = dst->X) == nil) 416 return 0; 417 418 /* 419 * If we have an opaque mask and source is one opaque pixel we can convert to the 420 * destination format and just XFillRectangle. 421 */ 422 m = Simplesrc|Simplemask|Fullmask; 423 if((par->state&m)==m){ 424 xfillcolor(dst, r, sdval); 425 dirtyXdata(dst, par->r); 426 return 1; 427 } 428 429 /* 430 * If no source alpha, an opaque mask, we can just copy the 431 * source onto the destination. If the channels are the same and 432 * the source is not replicated, XCopyArea suffices. 433 */ 434 m = Simplemask|Fullmask; 435 if((par->state&(m|Replsrc))==m && src->chan == dst->chan && src->X){ 436 sxm = src->X; 437 r = rectsubpt(r, dst->r.min); 438 sr = rectsubpt(sr, src->r.min); 439 if(dst->chan == GREY1) 440 gc = xgccopy0; 441 else 442 gc = xgccopy; 443 XCopyArea(xdisplay, sxm->pmid, dxm->pmid, gc, 444 sr.min.x, sr.min.y, dx, dy, r.min.x, r.min.y); 445 dirtyXdata(dst, par->r); 446 return 1; 447 } 448 449 /* 450 * If no source alpha, a 1-bit mask, and a simple source 451 * we can just copy through the mask onto the destination. 452 */ 453 if(dst->X && mask->X && !(mask->flags&Frepl) 454 && mask->chan == GREY1 && (par->state&Simplesrc)){ 455 Point p; 456 457 mxm = mask->X; 458 r = rectsubpt(r, dst->r.min); 459 mr = rectsubpt(mr, mask->r.min); 460 p = subpt(r.min, mr.min); 461 if(dst->chan == GREY1){ 462 gc = xgcsimplesrc0; 463 if(xgcsimplecolor0 != sdval){ 464 XSetForeground(xdisplay, gc, sdval); 465 xgcsimplecolor0 = sdval; 466 } 467 if(xgcsimplepm0 != mxm->pmid){ 468 XSetStipple(xdisplay, gc, mxm->pmid); 469 xgcsimplepm0 = mxm->pmid; 470 } 471 }else{ 472 /* somehow this doesn't work on rob's mac 473 gc = xgcsimplesrc; 474 if(dst->chan == CMAP8 && xtblbit) 475 sdval = plan9tox11[sdval]; 476 477 if(xgcsimplecolor != sdval){ 478 XSetForeground(xdisplay, gc, sdval); 479 xgcsimplecolor = sdval; 480 } 481 if(xgcsimplepm != mxm->pmid){ 482 XSetStipple(xdisplay, gc, mxm->pmid); 483 xgcsimplepm = mxm->pmid; 484 } 485 */ 486 return 0; 487 } 488 XSetTSOrigin(xdisplay, gc, p.x, p.y); 489 XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, dx, dy); 490 dirtyXdata(dst, par->r); 491 return 1; 492 } 493 return 0; 494 } 495 496 /* 497 * X11 window management and kernel hooks. 498 * Oh, how I loathe this code! 499 */ 500 501 static XColor map[256]; /* Plan 9 colormap array */ 502 static XColor map7[128]; /* Plan 9 colormap array */ 503 static uchar map7to8[128][2]; 504 static Colormap xcmap; /* Default shared colormap */ 505 506 extern int mousequeue; 507 508 /* for copy/paste, lifted from plan9ports */ 509 static Atom clipboard; 510 static Atom utf8string; 511 static Atom targets; 512 static Atom text; 513 static Atom compoundtext; 514 515 static Drawable xdrawable; 516 static void xexpose(XEvent*); 517 static void xmouse(XEvent*); 518 static void xkeyboard(XEvent*); 519 static void xmapping(XEvent*); 520 static void xdestroy(XEvent*); 521 static void xselect(XEvent*, XDisplay*); 522 static void xproc(void*); 523 static Memimage* xinitscreen(void); 524 static void initmap(Window); 525 static GC creategc(Drawable); 526 static void graphicscmap(XColor*); 527 static int xscreendepth; 528 static XDisplay* xkmcon; /* used only in xproc */ 529 static XDisplay* xsnarfcon; /* used holding clip.lk */ 530 static ulong xblack; 531 static ulong xwhite; 532 533 static int putsnarf, assertsnarf; 534 535 Memimage *gscreen; 536 Screeninfo screen; 537 538 void 539 flushmemscreen(Rectangle r) 540 { 541 assert(!drawcanqlock()); 542 if(r.min.x >= r.max.x || r.min.y >= r.max.y) 543 return; 544 XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, r.min.x, r.min.y, Dx(r), Dy(r), r.min.x, r.min.y); 545 XFlush(xdisplay); 546 } 547 548 void 549 screeninit(void) 550 { 551 _memmkcmap(); 552 553 gscreen = xinitscreen(); 554 kproc("xscreen", xproc, nil); 555 556 memimageinit(); 557 terminit(); 558 drawqlock(); 559 flushmemscreen(gscreen->r); 560 drawqunlock(); 561 } 562 563 uchar* 564 attachscreen(Rectangle *r, ulong *chan, int *depth, 565 int *width, int *softscreen, void **X) 566 { 567 *r = gscreen->r; 568 *chan = gscreen->chan; 569 *depth = gscreen->depth; 570 *width = gscreen->width; 571 *X = gscreen->X; 572 *softscreen = 1; 573 574 return gscreen->data->bdata; 575 } 576 577 static int 578 revbyte(int b) 579 { 580 int r; 581 582 r = 0; 583 r |= (b&0x01) << 7; 584 r |= (b&0x02) << 5; 585 r |= (b&0x04) << 3; 586 r |= (b&0x08) << 1; 587 r |= (b&0x10) >> 1; 588 r |= (b&0x20) >> 3; 589 r |= (b&0x40) >> 5; 590 r |= (b&0x80) >> 7; 591 return r; 592 } 593 594 void 595 mouseset(Point xy) 596 { 597 drawqlock(); 598 XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, xy.x, xy.y); 599 XFlush(xdisplay); 600 drawqunlock(); 601 } 602 603 static XCursor xcursor; 604 605 void 606 setcursor(void) 607 { 608 XCursor xc; 609 XColor fg, bg; 610 Pixmap xsrc, xmask; 611 int i; 612 uchar src[2*16], mask[2*16]; 613 614 for(i=0; i<2*16; i++){ 615 src[i] = revbyte(cursor.set[i]); 616 mask[i] = revbyte(cursor.set[i] | cursor.clr[i]); 617 } 618 619 drawqlock(); 620 fg = map[0]; 621 bg = map[255]; 622 xsrc = XCreateBitmapFromData(xdisplay, xdrawable, (char*)src, 16, 16); 623 xmask = XCreateBitmapFromData(xdisplay, xdrawable, (char*)mask, 16, 16); 624 xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -cursor.offset.x, -cursor.offset.y); 625 if(xc != 0) { 626 XDefineCursor(xdisplay, xdrawable, xc); 627 if(xcursor != 0) 628 XFreeCursor(xdisplay, xcursor); 629 xcursor = xc; 630 } 631 XFreePixmap(xdisplay, xsrc); 632 XFreePixmap(xdisplay, xmask); 633 XFlush(xdisplay); 634 drawqunlock(); 635 } 636 637 void 638 cursorarrow(void) 639 { 640 drawqlock(); 641 if(xcursor != 0){ 642 XFreeCursor(xdisplay, xcursor); 643 xcursor = 0; 644 } 645 XUndefineCursor(xdisplay, xdrawable); 646 XFlush(xdisplay); 647 drawqunlock(); 648 } 649 650 static void 651 xproc(void *arg) 652 { 653 ulong mask; 654 XEvent event; 655 656 mask = KeyPressMask| 657 ButtonPressMask| 658 ButtonReleaseMask| 659 PointerMotionMask| 660 Button1MotionMask| 661 Button2MotionMask| 662 Button3MotionMask| 663 Button4MotionMask| 664 Button5MotionMask| 665 ExposureMask| 666 StructureNotifyMask; 667 668 XSelectInput(xkmcon, xdrawable, mask); 669 for(;;) { 670 //XWindowEvent(xkmcon, xdrawable, mask, &event); 671 XNextEvent(xkmcon, &event); 672 xselect(&event, xkmcon); 673 xkeyboard(&event); 674 xmouse(&event); 675 xexpose(&event); 676 xmapping(&event); 677 xdestroy(&event); 678 } 679 } 680 681 static int 682 shutup(XDisplay *d, XErrorEvent *e) 683 { 684 char buf[200]; 685 iprint("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code); 686 XGetErrorText(d, e->error_code, buf, sizeof(buf)); 687 iprint("%s\n", buf); 688 USED(d); 689 USED(e); 690 return 0; 691 } 692 693 static int 694 panicshutup(XDisplay *d) 695 { 696 panic("x error"); 697 return -1; 698 } 699 700 static Memimage* 701 xinitscreen(void) 702 { 703 Memimage *gscreen; 704 int i, xsize, ysize, pmid; 705 char *argv[2]; 706 char *disp_val; 707 Window rootwin; 708 Rectangle r; 709 XWMHints hints; 710 XScreen *screen; 711 XVisualInfo xvi; 712 int rootscreennum; 713 XTextProperty name; 714 XClassHint classhints; 715 XSizeHints normalhints; 716 XSetWindowAttributes attrs; 717 XPixmapFormatValues *pfmt; 718 int n; 719 Pixmap icon_pixmap; 720 721 xscreenid = 0; 722 xdrawable = 0; 723 724 xdisplay = XOpenDisplay(NULL); 725 if(xdisplay == 0){ 726 iprint("xinitscreen: XOpenDisplay: %r [DISPLAY=%s]\n", 727 getenv("DISPLAY")); 728 exit(0); 729 } 730 731 XSetErrorHandler(shutup); 732 XSetIOErrorHandler(panicshutup); 733 rootscreennum = DefaultScreen(xdisplay); 734 rootwin = DefaultRootWindow(xdisplay); 735 736 xscreendepth = DefaultDepth(xdisplay, rootscreennum); 737 if(XMatchVisualInfo(xdisplay, rootscreennum, 16, TrueColor, &xvi) 738 || XMatchVisualInfo(xdisplay, rootscreennum, 16, DirectColor, &xvi)){ 739 xvis = xvi.visual; 740 xscreendepth = 16; 741 xtblbit = 1; 742 } 743 else if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi) 744 || XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){ 745 xvis = xvi.visual; 746 xscreendepth = 24; 747 xtblbit = 1; 748 } 749 else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi) 750 || XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){ 751 if(xscreendepth > 8) 752 panic("drawterm: can't deal with colormapped depth %d screens\n", xscreendepth); 753 xvis = xvi.visual; 754 xscreendepth = 8; 755 } 756 else{ 757 if(xscreendepth != 8) 758 panic("drawterm: can't deal with depth %d screens\n", xscreendepth); 759 xvis = DefaultVisual(xdisplay, rootscreennum); 760 } 761 762 /* 763 * xscreendepth is only the number of significant pixel bits, 764 * not the total. We need to walk the display list to find 765 * how many actual bits are being used per pixel. 766 */ 767 xscreenchan = 0; /* not a valid channel */ 768 pfmt = XListPixmapFormats(xdisplay, &n); 769 for(i=0; i<n; i++){ 770 if(pfmt[i].depth == xscreendepth){ 771 switch(pfmt[i].bits_per_pixel){ 772 case 1: /* untested */ 773 xscreenchan = GREY1; 774 break; 775 case 2: /* untested */ 776 xscreenchan = GREY2; 777 break; 778 case 4: /* untested */ 779 xscreenchan = GREY4; 780 break; 781 case 8: 782 xscreenchan = CMAP8; 783 break; 784 case 16: /* uses 16 rather than 15, empirically. */ 785 xscreenchan = RGB16; 786 break; 787 case 24: /* untested (impossible?) */ 788 xscreenchan = RGB24; 789 break; 790 case 32: 791 xscreenchan = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8); 792 break; 793 } 794 } 795 } 796 if(xscreenchan == 0) 797 panic("drawterm: unknown screen pixel format\n"); 798 799 screen = DefaultScreenOfDisplay(xdisplay); 800 xcmap = DefaultColormapOfScreen(screen); 801 802 if(xvis->class != StaticColor){ 803 graphicscmap(map); 804 initmap(rootwin); 805 } 806 807 r.min = ZP; 808 r.max.x = WidthOfScreen(screen); 809 r.max.y = HeightOfScreen(screen); 810 811 xsize = Dx(r)*3/4; 812 ysize = Dy(r)*3/4; 813 814 attrs.colormap = xcmap; 815 attrs.background_pixel = 0; 816 attrs.border_pixel = 0; 817 /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ 818 xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, 819 xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); 820 821 /* load the given bitmap data and create an X pixmap containing it. */ 822 icon_pixmap = XCreateBitmapFromData(xdisplay, 823 rootwin, (char *)glenda_bits, 824 glenda_width, glenda_height); 825 826 /* 827 * set up property as required by ICCCM 828 */ 829 name.value = (uchar*)"drawterm"; 830 name.encoding = XA_STRING; 831 name.format = 8; 832 name.nitems = strlen((char*)name.value); 833 normalhints.flags = USSize|PMaxSize; 834 normalhints.max_width = Dx(r); 835 normalhints.max_height = Dy(r); 836 normalhints.width = xsize; 837 normalhints.height = ysize; 838 hints.flags = IconPixmapHint |InputHint|StateHint; 839 hints.input = 1; 840 hints.initial_state = NormalState; 841 hints.icon_pixmap = icon_pixmap; 842 843 classhints.res_name = "drawterm"; 844 classhints.res_class = "Drawterm"; 845 argv[0] = "drawterm"; 846 argv[1] = nil; 847 XSetWMProperties(xdisplay, xdrawable, 848 &name, /* XA_WM_NAME property for ICCCM */ 849 &name, /* XA_WM_ICON_NAME */ 850 argv, /* XA_WM_COMMAND */ 851 1, /* argc */ 852 &normalhints, /* XA_WM_NORMAL_HINTS */ 853 &hints, /* XA_WM_HINTS */ 854 &classhints); /* XA_WM_CLASS */ 855 XFlush(xdisplay); 856 857 /* 858 * put the window on the screen 859 */ 860 XMapWindow(xdisplay, xdrawable); 861 XFlush(xdisplay); 862 863 xscreenid = XCreatePixmap(xdisplay, xdrawable, Dx(r), Dy(r), xscreendepth); 864 gscreen = xallocmemimage(r, xscreenchan, xscreenid); 865 866 xgcfill = creategc(xscreenid); 867 XSetFillStyle(xdisplay, xgcfill, FillSolid); 868 xgccopy = creategc(xscreenid); 869 xgcsimplesrc = creategc(xscreenid); 870 XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled); 871 xgczero = creategc(xscreenid); 872 xgcreplsrc = creategc(xscreenid); 873 XSetFillStyle(xdisplay, xgcreplsrc, FillTiled); 874 875 pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1); 876 xgcfill0 = creategc(pmid); 877 XSetForeground(xdisplay, xgcfill0, 0); 878 XSetFillStyle(xdisplay, xgcfill0, FillSolid); 879 xgccopy0 = creategc(pmid); 880 xgcsimplesrc0 = creategc(pmid); 881 XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled); 882 xgczero0 = creategc(pmid); 883 xgcreplsrc0 = creategc(pmid); 884 XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled); 885 XFreePixmap(xdisplay, pmid); 886 887 XSetForeground(xdisplay, xgccopy, plan9tox11[0]); 888 XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize); 889 890 xkmcon = XOpenDisplay(NULL); 891 if(xkmcon == 0){ 892 disp_val = getenv("DISPLAY"); 893 if(disp_val == 0) 894 disp_val = "not set"; 895 iprint("drawterm: open %r, DISPLAY is %s\n", disp_val); 896 exit(0); 897 } 898 xsnarfcon = XOpenDisplay(NULL); 899 if(xsnarfcon == 0){ 900 disp_val = getenv("DISPLAY"); 901 if(disp_val == 0) 902 disp_val = "not set"; 903 iprint("drawterm: open %r, DISPLAY is %s\n", disp_val); 904 exit(0); 905 } 906 907 clipboard = XInternAtom(xkmcon, "CLIPBOARD", False); 908 utf8string = XInternAtom(xkmcon, "UTF8_STRING", False); 909 targets = XInternAtom(xkmcon, "TARGETS", False); 910 text = XInternAtom(xkmcon, "TEXT", False); 911 compoundtext = XInternAtom(xkmcon, "COMPOUND_TEXT", False); 912 913 xblack = screen->black_pixel; 914 xwhite = screen->white_pixel; 915 return gscreen; 916 } 917 918 static void 919 graphicscmap(XColor *map) 920 { 921 int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; 922 923 for(r=0; r!=4; r++) { 924 for(g = 0; g != 4; g++) { 925 for(b = 0; b!=4; b++) { 926 for(v = 0; v!=4; v++) { 927 den=r; 928 if(g > den) 929 den=g; 930 if(b > den) 931 den=b; 932 /* divide check -- pick grey shades */ 933 if(den==0) 934 cr=cg=cb=v*17; 935 else { 936 num=17*(4*den+v); 937 cr=r*num/den; 938 cg=g*num/den; 939 cb=b*num/den; 940 } 941 idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); 942 map[idx].red = cr*0x0101; 943 map[idx].green = cg*0x0101; 944 map[idx].blue = cb*0x0101; 945 map[idx].pixel = idx; 946 map[idx].flags = DoRed|DoGreen|DoBlue; 947 948 v7 = v >> 1; 949 idx7 = r*32 + v7*16 + g*4 + b; 950 if((v & 1) == v7){ 951 map7to8[idx7][0] = idx; 952 if(den == 0) { /* divide check -- pick grey shades */ 953 cr = ((255.0/7.0)*v7)+0.5; 954 cg = cr; 955 cb = cr; 956 } 957 else { 958 num=17*15*(4*den+v7*2)/14; 959 cr=r*num/den; 960 cg=g*num/den; 961 cb=b*num/den; 962 } 963 map7[idx7].red = cr*0x0101; 964 map7[idx7].green = cg*0x0101; 965 map7[idx7].blue = cb*0x0101; 966 map7[idx7].pixel = idx7; 967 map7[idx7].flags = DoRed|DoGreen|DoBlue; 968 } 969 else 970 map7to8[idx7][1] = idx; 971 } 972 } 973 } 974 } 975 } 976 977 /* 978 * Initialize and install the drawterm colormap as a private colormap for this 979 * application. Drawterm gets the best colors here when it has the cursor focus. 980 */ 981 static void 982 initmap(Window w) 983 { 984 XColor c; 985 int i; 986 ulong p, pp; 987 char buf[30]; 988 989 if(xscreendepth <= 1) 990 return; 991 992 if(xscreendepth >= 24) { 993 /* The pixel value returned from XGetPixel needs to 994 * be converted to RGB so we can call rgb2cmap() 995 * to translate between 24 bit X and our color. Unfortunately, 996 * the return value appears to be display server endian 997 * dependant. Therefore, we run some heuristics to later 998 * determine how to mask the int value correctly. 999 * Yeah, I know we can look at xvis->byte_order but 1000 * some displays say MSB even though they run on LSB. 1001 * Besides, this is more anal. 1002 */ 1003 if(xscreendepth != DefaultDepth(xdisplay, DefaultScreen(xdisplay))) 1004 xcmap = XCreateColormap(xdisplay, w, xvis, AllocNone); 1005 1006 c = map[19]; 1007 /* find out index into colormap for our RGB */ 1008 if(!XAllocColor(xdisplay, xcmap, &c)) 1009 panic("drawterm: screen-x11 can't alloc color"); 1010 1011 p = c.pixel; 1012 pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); 1013 if(pp!=map[19].pixel) { 1014 /* check if endian is other way */ 1015 pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); 1016 if(pp!=map[19].pixel) 1017 panic("cannot detect x server byte order"); 1018 switch(xscreenchan){ 1019 case RGB24: 1020 xscreenchan = BGR24; 1021 break; 1022 case XRGB32: 1023 xscreenchan = XBGR32; 1024 break; 1025 default: 1026 panic("don't know how to byteswap channel %s", 1027 chantostr(buf, xscreenchan)); 1028 break; 1029 } 1030 } 1031 } else if(xvis->class == TrueColor || xvis->class == DirectColor) { 1032 } else if(xvis->class == PseudoColor) { 1033 if(xtblbit == 0){ 1034 xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); 1035 XStoreColors(xdisplay, xcmap, map, 256); 1036 for(i = 0; i < 256; i++) { 1037 plan9tox11[i] = i; 1038 x11toplan9[i] = i; 1039 } 1040 } 1041 else { 1042 for(i = 0; i < 128; i++) { 1043 c = map7[i]; 1044 if(!XAllocColor(xdisplay, xcmap, &c)) { 1045 iprint("drawterm: can't alloc colors in default map, don't use -7\n"); 1046 exit(0); 1047 } 1048 plan9tox11[map7to8[i][0]] = c.pixel; 1049 plan9tox11[map7to8[i][1]] = c.pixel; 1050 x11toplan9[c.pixel] = map7to8[i][0]; 1051 } 1052 } 1053 } 1054 else 1055 panic("drawterm: unsupported visual class %d\n", xvis->class); 1056 } 1057 1058 static void 1059 xdestroy(XEvent *e) 1060 { 1061 XDestroyWindowEvent *xe; 1062 if(e->type != DestroyNotify) 1063 return; 1064 xe = (XDestroyWindowEvent*)e; 1065 if(xe->window == xdrawable) 1066 exit(0); 1067 } 1068 1069 static void 1070 xmapping(XEvent *e) 1071 { 1072 XMappingEvent *xe; 1073 1074 if(e->type != MappingNotify) 1075 return; 1076 xe = (XMappingEvent*)e; 1077 USED(xe); 1078 } 1079 1080 1081 /* 1082 * Disable generation of GraphicsExpose/NoExpose events in the GC. 1083 */ 1084 static GC 1085 creategc(Drawable d) 1086 { 1087 XGCValues gcv; 1088 1089 gcv.function = GXcopy; 1090 gcv.graphics_exposures = False; 1091 return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); 1092 } 1093 1094 static void 1095 xexpose(XEvent *e) 1096 { 1097 Rectangle r; 1098 XExposeEvent *xe; 1099 1100 if(e->type != Expose) 1101 return; 1102 xe = (XExposeEvent*)e; 1103 r.min.x = xe->x; 1104 r.min.y = xe->y; 1105 r.max.x = xe->x + xe->width; 1106 r.max.y = xe->y + xe->height; 1107 drawflushr(r); 1108 } 1109 1110 static void 1111 xkeyboard(XEvent *e) 1112 { 1113 KeySym k; 1114 1115 /* 1116 * I tried using XtGetActionKeysym, but it didn't seem to 1117 * do case conversion properly 1118 * (at least, with Xterminal servers and R4 intrinsics) 1119 */ 1120 if(e->xany.type != KeyPress) 1121 return; 1122 1123 1124 XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL); 1125 1126 if(k == XK_Multi_key || k == NoSymbol) 1127 return; 1128 if(k&0xFF00){ 1129 switch(k){ 1130 case XK_BackSpace: 1131 case XK_Tab: 1132 case XK_Escape: 1133 case XK_Delete: 1134 case XK_KP_0: 1135 case XK_KP_1: 1136 case XK_KP_2: 1137 case XK_KP_3: 1138 case XK_KP_4: 1139 case XK_KP_5: 1140 case XK_KP_6: 1141 case XK_KP_7: 1142 case XK_KP_8: 1143 case XK_KP_9: 1144 case XK_KP_Divide: 1145 case XK_KP_Multiply: 1146 case XK_KP_Subtract: 1147 case XK_KP_Add: 1148 case XK_KP_Decimal: 1149 k &= 0x7F; 1150 break; 1151 case XK_Linefeed: 1152 k = '\r'; 1153 break; 1154 case XK_KP_Space: 1155 k = ' '; 1156 break; 1157 case XK_Home: 1158 case XK_KP_Home: 1159 k = Khome; 1160 break; 1161 case XK_Left: 1162 case XK_KP_Left: 1163 k = Kleft; 1164 break; 1165 case XK_Up: 1166 case XK_KP_Up: 1167 k = Kup; 1168 break; 1169 case XK_Down: 1170 case XK_KP_Down: 1171 k = Kdown; 1172 break; 1173 case XK_Right: 1174 case XK_KP_Right: 1175 k = Kright; 1176 break; 1177 case XK_Page_Down: 1178 case XK_KP_Page_Down: 1179 k = Kpgdown; 1180 break; 1181 case XK_End: 1182 case XK_KP_End: 1183 k = Kend; 1184 break; 1185 case XK_Page_Up: 1186 case XK_KP_Page_Up: 1187 k = Kpgup; 1188 break; 1189 case XK_Insert: 1190 case XK_KP_Insert: 1191 k = Kins; 1192 break; 1193 case XK_KP_Enter: 1194 case XK_Return: 1195 k = '\n'; 1196 break; 1197 case XK_Alt_L: 1198 case XK_Alt_R: 1199 k = Kalt; 1200 break; 1201 case XK_F1: 1202 case XK_F2: 1203 case XK_F3: 1204 case XK_F4: 1205 case XK_F5: 1206 case XK_F6: 1207 case XK_F7: 1208 case XK_F8: 1209 case XK_F9: 1210 case XK_F10: 1211 case XK_F11: 1212 case XK_F12: 1213 k = KF|(k - XK_F1 + 1); 1214 break; 1215 case XK_Shift_L: 1216 case XK_Shift_R: 1217 case XK_Control_L: 1218 case XK_Control_R: 1219 case XK_Caps_Lock: 1220 case XK_Shift_Lock: 1221 1222 case XK_Meta_L: 1223 case XK_Meta_R: 1224 case XK_Super_L: 1225 case XK_Super_R: 1226 case XK_Hyper_L: 1227 case XK_Hyper_R: 1228 return; 1229 default: /* not ISO-1 or tty control */ 1230 if(k>0xff){ 1231 k = keysym2ucs(k); /* supplied by X */ 1232 if(k == -1) 1233 return; 1234 } 1235 break; 1236 } 1237 } 1238 1239 /* Compensate for servers that call a minus a hyphen */ 1240 if(k == XK_hyphen) 1241 k = XK_minus; 1242 /* Do control mapping ourselves if translator doesn't */ 1243 if(e->xkey.state&ControlMask && k != Kalt) 1244 k &= 0x9f; 1245 if(k == NoSymbol) { 1246 return; 1247 } 1248 1249 kbdputc(kbdq, k); 1250 } 1251 1252 static void 1253 xmouse(XEvent *e) 1254 { 1255 Mousestate ms; 1256 int i, s; 1257 XButtonEvent *be; 1258 XMotionEvent *me; 1259 1260 if(putsnarf != assertsnarf){ 1261 assertsnarf = putsnarf; 1262 XSetSelectionOwner(xkmcon, XA_PRIMARY, xdrawable, CurrentTime); 1263 if(clipboard != None) 1264 XSetSelectionOwner(xkmcon, clipboard, xdrawable, CurrentTime); 1265 XFlush(xkmcon); 1266 } 1267 1268 switch(e->type){ 1269 case ButtonPress: 1270 be = (XButtonEvent *)e; 1271 /* 1272 * Fake message, just sent to make us announce snarf. 1273 * Apparently state and button are 16 and 8 bits on 1274 * the wire, since they are truncated by the time they 1275 * get to us. 1276 */ 1277 if(be->send_event 1278 && (~be->state&0xFFFF)==0 1279 && (~be->button&0xFF)==0) 1280 return; 1281 ms.xy.x = be->x; 1282 ms.xy.y = be->y; 1283 s = be->state; 1284 ms.msec = be->time; 1285 switch(be->button){ 1286 case 1: 1287 s |= Button1Mask; 1288 break; 1289 case 2: 1290 s |= Button2Mask; 1291 break; 1292 case 3: 1293 s |= Button3Mask; 1294 break; 1295 case 4: 1296 s |= Button4Mask; 1297 break; 1298 case 5: 1299 s |= Button5Mask; 1300 break; 1301 } 1302 break; 1303 case ButtonRelease: 1304 be = (XButtonEvent *)e; 1305 ms.xy.x = be->x; 1306 ms.xy.y = be->y; 1307 ms.msec = be->time; 1308 s = be->state; 1309 switch(be->button){ 1310 case 1: 1311 s &= ~Button1Mask; 1312 break; 1313 case 2: 1314 s &= ~Button2Mask; 1315 break; 1316 case 3: 1317 s &= ~Button3Mask; 1318 break; 1319 case 4: 1320 s &= ~Button4Mask; 1321 break; 1322 case 5: 1323 s &= ~Button5Mask; 1324 break; 1325 } 1326 break; 1327 case MotionNotify: 1328 me = (XMotionEvent *)e; 1329 s = me->state; 1330 ms.xy.x = me->x; 1331 ms.xy.y = me->y; 1332 ms.msec = me->time; 1333 break; 1334 default: 1335 return; 1336 } 1337 1338 ms.buttons = 0; 1339 if(s & Button1Mask) 1340 ms.buttons |= 1; 1341 if(s & Button2Mask) 1342 ms.buttons |= 2; 1343 if(s & Button3Mask) 1344 ms.buttons |= 4; 1345 if(s & Button4Mask) 1346 ms.buttons |= 8; 1347 if(s & Button5Mask) 1348 ms.buttons |= 16; 1349 1350 lock(&mouse.lk); 1351 i = mouse.wi; 1352 if(mousequeue) { 1353 if(i == mouse.ri || mouse.lastb != ms.buttons || mouse.trans) { 1354 mouse.wi = (i+1)%Mousequeue; 1355 if(mouse.wi == mouse.ri) 1356 mouse.ri = (mouse.ri+1)%Mousequeue; 1357 mouse.trans = mouse.lastb != ms.buttons; 1358 } else { 1359 i = (i-1+Mousequeue)%Mousequeue; 1360 } 1361 } else { 1362 mouse.wi = (i+1)%Mousequeue; 1363 mouse.ri = i; 1364 } 1365 mouse.queue[i] = ms; 1366 mouse.lastb = ms.buttons; 1367 unlock(&mouse.lk); 1368 wakeup(&mouse.r); 1369 } 1370 1371 void 1372 getcolor(ulong i, ulong *r, ulong *g, ulong *b) 1373 { 1374 ulong v; 1375 1376 v = cmap2rgb(i); 1377 *r = (v>>16)&0xFF; 1378 *g = (v>>8)&0xFF; 1379 *b = v&0xFF; 1380 } 1381 1382 void 1383 setcolor(ulong i, ulong r, ulong g, ulong b) 1384 { 1385 /* no-op */ 1386 } 1387 1388 int 1389 atlocalconsole(void) 1390 { 1391 char *p, *q; 1392 char buf[128]; 1393 1394 p = getenv("DRAWTERM_ATLOCALCONSOLE"); 1395 if(p && atoi(p) == 1) 1396 return 1; 1397 1398 p = getenv("DISPLAY"); 1399 if(p == nil) 1400 return 0; 1401 1402 /* extract host part */ 1403 q = strchr(p, ':'); 1404 if(q == nil) 1405 return 0; 1406 *q = 0; 1407 1408 if(strcmp(p, "") == 0) 1409 return 1; 1410 1411 /* try to match against system name (i.e. for ssh) */ 1412 if(gethostname(buf, sizeof buf) == 0){ 1413 if(strcmp(p, buf) == 0) 1414 return 1; 1415 if(strncmp(p, buf, strlen(p)) == 0 && buf[strlen(p)]=='.') 1416 return 1; 1417 } 1418 1419 return 0; 1420 } 1421 1422 /* 1423 * Cut and paste. Just couldn't stand to make this simple... 1424 */ 1425 1426 typedef struct Clip Clip; 1427 struct Clip 1428 { 1429 char buf[SnarfSize]; 1430 QLock lk; 1431 }; 1432 Clip clip; 1433 1434 #undef long /* sic */ 1435 #undef ulong 1436 1437 static char* 1438 _xgetsnarf(XDisplay *xd) 1439 { 1440 uchar *data, *xdata; 1441 Atom clipboard, type, prop; 1442 unsigned long lastlen; 1443 unsigned long dummy, len; 1444 int fmt, i; 1445 Window w; 1446 1447 qlock(&clip.lk); 1448 /* 1449 * Have we snarfed recently and the X server hasn't caught up? 1450 */ 1451 if(putsnarf != assertsnarf) 1452 goto mine; 1453 1454 /* 1455 * Is there a primary selection (highlighted text in an xterm)? 1456 */ 1457 clipboard = XA_PRIMARY; 1458 w = XGetSelectionOwner(xd, XA_PRIMARY); 1459 if(w == xdrawable){ 1460 mine: 1461 data = (uchar*)strdup(clip.buf); 1462 goto out; 1463 } 1464 1465 /* 1466 * If not, is there a clipboard selection? 1467 */ 1468 if(w == None && clipboard != None){ 1469 clipboard = clipboard; 1470 w = XGetSelectionOwner(xd, clipboard); 1471 if(w == xdrawable) 1472 goto mine; 1473 } 1474 1475 /* 1476 * If not, give up. 1477 */ 1478 if(w == None){ 1479 data = nil; 1480 goto out; 1481 } 1482 1483 /* 1484 * We should be waiting for SelectionNotify here, but it might never 1485 * come, and we have no way to time out. Instead, we will clear 1486 * local property #1, request our buddy to fill it in for us, and poll 1487 * until he's done or we get tired of waiting. 1488 * 1489 * We should try to go for utf8string instead of XA_STRING, 1490 * but that would add to the polling. 1491 */ 1492 prop = 1; 1493 XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); 1494 XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime); 1495 XFlush(xd); 1496 lastlen = 0; 1497 for(i=0; i<10 || (lastlen!=0 && i<30); i++){ 1498 usleep(100*1000); 1499 XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType, 1500 &type, &fmt, &dummy, &len, &data); 1501 if(lastlen == len && len > 0) 1502 break; 1503 lastlen = len; 1504 } 1505 if(i == 10){ 1506 data = nil; 1507 goto out; 1508 } 1509 /* get the property */ 1510 data = nil; 1511 XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, 1512 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); 1513 if((type != XA_STRING && type != utf8string) || len == 0){ 1514 if(xdata) 1515 XFree(xdata); 1516 data = nil; 1517 }else{ 1518 if(xdata){ 1519 data = (uchar*)strdup((char*)xdata); 1520 XFree(xdata); 1521 }else 1522 data = nil; 1523 } 1524 out: 1525 qunlock(&clip.lk); 1526 return (char*)data; 1527 } 1528 1529 static void 1530 _xputsnarf(XDisplay *xd, char *data) 1531 { 1532 XButtonEvent e; 1533 1534 if(strlen(data) >= SnarfSize) 1535 return; 1536 qlock(&clip.lk); 1537 strcpy(clip.buf, data); 1538 1539 /* leave note for mouse proc to assert selection ownership */ 1540 putsnarf++; 1541 1542 /* send mouse a fake event so snarf is announced */ 1543 memset(&e, 0, sizeof e); 1544 e.type = ButtonPress; 1545 e.window = xdrawable; 1546 e.state = ~0; 1547 e.button = ~0; 1548 XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e); 1549 XFlush(xd); 1550 qunlock(&clip.lk); 1551 } 1552 1553 static void 1554 xselect(XEvent *e, XDisplay *xd) 1555 { 1556 char *name; 1557 XEvent r; 1558 XSelectionRequestEvent *xe; 1559 Atom a[4]; 1560 1561 if(e->xany.type != SelectionRequest) 1562 return; 1563 1564 memset(&r, 0, sizeof r); 1565 xe = (XSelectionRequestEvent*)e; 1566 if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n", 1567 xe->target, xe->requestor, xe->property, xe->selection); 1568 r.xselection.property = xe->property; 1569 if(xe->target == targets){ 1570 a[0] = XA_STRING; 1571 a[1] = utf8string; 1572 a[2] = text; 1573 a[3] = compoundtext; 1574 1575 XChangeProperty(xd, xe->requestor, xe->property, XA_ATOM, 1576 32, PropModeReplace, (uchar*)a, sizeof a); 1577 }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){ 1578 text: 1579 /* if the target is STRING we're supposed to reply with Latin1 XXX */ 1580 qlock(&clip.lk); 1581 XChangeProperty(xd, xe->requestor, xe->property, xe->target, 1582 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); 1583 qunlock(&clip.lk); 1584 }else{ 1585 name = XGetAtomName(xd, xe->target); 1586 if(name == nil) 1587 iprint("XGetAtomName %d failed\n", xe->target); 1588 if(name){ 1589 if(strcmp(name, "TIMESTAMP") == 0){ 1590 /* nothing */ 1591 }else if(strncmp(name, "image/", 6) == 0){ 1592 /* nothing */ 1593 }else if(strcmp(name, "text/html") == 0){ 1594 /* nothing */ 1595 }else if(strcmp(name, "text/plain") == 0 || strcmp(name, "text/plain;charset=UTF-8") == 0){ 1596 goto text; 1597 }else 1598 iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); 1599 } 1600 r.xselection.property = None; 1601 } 1602 1603 r.xselection.display = xe->display; 1604 /* r.xselection.property filled above */ 1605 r.xselection.target = xe->target; 1606 r.xselection.type = SelectionNotify; 1607 r.xselection.requestor = xe->requestor; 1608 r.xselection.time = xe->time; 1609 r.xselection.send_event = True; 1610 r.xselection.selection = xe->selection; 1611 XSendEvent(xd, xe->requestor, False, 0, &r); 1612 XFlush(xd); 1613 } 1614 1615 char* 1616 clipread(void) 1617 { 1618 return _xgetsnarf(xsnarfcon); 1619 } 1620 1621 int 1622 clipwrite(char *buf) 1623 { 1624 _xputsnarf(xsnarfcon, buf); 1625 return 0; 1626 } 1627 1628