x11-screen.c (39111B)
1 #include <u.h> 2 #include "x11-inc.h" 3 #include "x11-keysym2ucs.h" 4 #include <errno.h> 5 #include <libc.h> 6 #include <draw.h> 7 #include <memdraw.h> 8 #include <memlayer.h> 9 #include <keyboard.h> 10 #include <mouse.h> 11 #include <cursor.h> 12 #include <thread.h> 13 #include "x11-memdraw.h" 14 #include "devdraw.h" 15 16 #undef time 17 18 static void plan9cmap(void); 19 static int setupcmap(XWindow); 20 static XGC xgc(XDrawable, int, int); 21 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask 22 23 #define MouseMask (\ 24 ButtonPressMask|\ 25 ButtonReleaseMask|\ 26 PointerMotionMask|\ 27 Button1MotionMask|\ 28 Button2MotionMask|\ 29 Button3MotionMask) 30 31 Xprivate _x; 32 33 static void runxevent(XEvent *xev); 34 static int _xconfigure(Xwin *w, XEvent *e); 35 static int _xdestroy(Xwin *w, XEvent *e); 36 static void _xexpose(Xwin *w, XEvent *e); 37 static int _xreplacescreenimage(Client *client); 38 static int _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m); 39 static void _xmovewindow(Xwin *w, Rectangle r); 40 static int _xtoplan9kbd(XEvent *e); 41 static int _xselect(XEvent *e); 42 43 static void rpc_resizeimg(Client*); 44 static void rpc_resizewindow(Client*, Rectangle); 45 static void rpc_setcursor(Client*, Cursor*, Cursor2*); 46 static void rpc_setlabel(Client*, char*); 47 static void rpc_setmouse(Client*, Point); 48 static void rpc_topwin(Client*); 49 static void rpc_bouncemouse(Client*, Mouse); 50 static void rpc_flush(Client*, Rectangle); 51 52 static ClientImpl x11impl = { 53 rpc_resizeimg, 54 rpc_resizewindow, 55 rpc_setcursor, 56 rpc_setlabel, 57 rpc_setmouse, 58 rpc_topwin, 59 rpc_bouncemouse, 60 rpc_flush 61 }; 62 63 static Xwin* 64 newxwin(Client *c) 65 { 66 Xwin *w; 67 68 w = mallocz(sizeof *w, 1); 69 if(w == nil) 70 sysfatal("out of memory"); 71 w->client = c; 72 w->next = _x.windows; 73 _x.windows = w; 74 c->impl = &x11impl; 75 c->view = w; 76 return w; 77 } 78 79 static Xwin* 80 findxwin(XDrawable d) 81 { 82 Xwin *w, **l; 83 84 for(l=&_x.windows; (w=*l) != nil; l=&w->next) { 85 if(w->drawable == d) { 86 /* move to front */ 87 *l = w->next; 88 w->next = _x.windows; 89 _x.windows = w; 90 return w; 91 } 92 } 93 return nil; 94 } 95 96 static int 97 xerror(XDisplay *d, XErrorEvent *e) 98 { 99 char buf[200]; 100 101 if(e->request_code == 42) /* XSetInputFocus */ 102 return 0; 103 if(e->request_code == 18) /* XChangeProperty */ 104 return 0; 105 /* 106 * BadDrawable happens in apps that get resized a LOT, 107 * e.g. when KDE is configured to resize continuously 108 * during a window drag. 109 */ 110 if(e->error_code == 9) /* BadDrawable */ 111 return 0; 112 113 fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n", 114 e->error_code, e->request_code, e->minor_code, d); 115 XGetErrorText(d, e->error_code, buf, sizeof buf); 116 fprint(2, "%s\n", buf); 117 return 0; 118 } 119 120 static int 121 xioerror(XDisplay *d) 122 { 123 /*print("X I/O error\n"); */ 124 exit(0); 125 /*sysfatal("X I/O error\n");*/ 126 abort(); 127 return -1; 128 } 129 130 static void xloop(void); 131 132 static QLock xlk; 133 134 void 135 xlock(void) 136 { 137 qlock(&xlk); 138 } 139 140 void 141 xunlock(void) 142 { 143 qunlock(&xlk); 144 } 145 146 void 147 gfx_main(void) 148 { 149 char *disp; 150 int i, n, xrootid; 151 XPixmapFormatValues *pfmt; 152 XScreen *xscreen; 153 XVisualInfo xvi; 154 XWindow xrootwin; 155 156 /* 157 if(XInitThreads() == 0) 158 sysfatal("XInitThread: %r"); 159 */ 160 161 /* 162 * Connect to X server. 163 */ 164 _x.display = XOpenDisplay(NULL); 165 if(_x.display == nil){ 166 disp = getenv("DISPLAY"); 167 werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); 168 free(disp); 169 sysfatal("%r"); 170 } 171 _x.fd = ConnectionNumber(_x.display); 172 XSetErrorHandler(xerror); 173 XSetIOErrorHandler(xioerror); 174 xrootid = DefaultScreen(_x.display); 175 xrootwin = DefaultRootWindow(_x.display); 176 177 /* 178 * Figure out underlying screen format. 179 */ 180 if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi) 181 || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){ 182 _x.vis = xvi.visual; 183 _x.depth = 24; 184 } 185 else 186 if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi) 187 || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){ 188 _x.vis = xvi.visual; 189 _x.depth = 16; 190 } 191 else 192 if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi) 193 || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){ 194 _x.vis = xvi.visual; 195 _x.depth = 15; 196 } 197 else 198 if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi) 199 || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){ 200 if(_x.depth > 8){ 201 werrstr("can't deal with colormapped depth %d screens", 202 _x.depth); 203 goto err0; 204 } 205 _x.vis = xvi.visual; 206 _x.depth = 8; 207 } 208 else{ 209 _x.depth = DefaultDepth(_x.display, xrootid); 210 if(_x.depth != 8){ 211 werrstr("can't understand depth %d screen", _x.depth); 212 goto err0; 213 } 214 _x.vis = DefaultVisual(_x.display, xrootid); 215 } 216 217 if(DefaultDepth(_x.display, xrootid) == _x.depth) 218 _x.usetable = 1; 219 220 /* 221 * _x.depth is only the number of significant pixel bits, 222 * not the total number of pixel bits. We need to walk the 223 * display list to find how many actual bits are used 224 * per pixel. 225 */ 226 _x.chan = 0; 227 pfmt = XListPixmapFormats(_x.display, &n); 228 for(i=0; i<n; i++){ 229 if(pfmt[i].depth == _x.depth){ 230 switch(pfmt[i].bits_per_pixel){ 231 case 1: /* untested */ 232 _x.chan = GREY1; 233 break; 234 case 2: /* untested */ 235 _x.chan = GREY2; 236 break; 237 case 4: /* untested */ 238 _x.chan = GREY4; 239 break; 240 case 8: 241 _x.chan = CMAP8; 242 break; 243 case 15: 244 _x.chan = RGB15; 245 break; 246 case 16: /* how to tell RGB15? */ 247 _x.chan = RGB16; 248 break; 249 case 24: /* untested (impossible?) */ 250 _x.chan = RGB24; 251 break; 252 case 32: 253 _x.chan = XRGB32; 254 break; 255 } 256 } 257 } 258 XFree(pfmt); 259 if(_x.chan == 0){ 260 werrstr("could not determine screen pixel format"); 261 goto err0; 262 } 263 264 /* 265 * Set up color map if necessary. 266 */ 267 xscreen = DefaultScreenOfDisplay(_x.display); 268 _x.cmap = DefaultColormapOfScreen(xscreen); 269 if(_x.vis->class != StaticColor){ 270 plan9cmap(); 271 setupcmap(xrootwin); 272 } 273 gfx_started(); 274 xloop(); 275 276 err0: 277 XCloseDisplay(_x.display); 278 sysfatal("%r"); 279 } 280 281 static void 282 xloop(void) 283 { 284 fd_set rd, wr, xx; 285 XEvent event; 286 287 xlock(); 288 _x.fd = ConnectionNumber(_x.display); 289 for(;;) { 290 FD_ZERO(&rd); 291 FD_ZERO(&wr); 292 FD_ZERO(&xx); 293 FD_SET(_x.fd, &rd); 294 FD_SET(_x.fd, &xx); 295 if(_x.windows != nil) 296 XSelectInput(_x.display, _x.windows->drawable, Mask); // TODO: when is this needed? 297 XFlush(_x.display); 298 xunlock(); 299 300 again: 301 if(select(_x.fd+1, &rd, &wr, &xx, nil) < 0) { 302 if(errno == EINTR) 303 goto again; 304 sysfatal("select: %r"); // TODO: quiet exit? 305 } 306 307 xlock(); 308 while(XPending(_x.display)) { 309 XNextEvent(_x.display, &event); 310 runxevent(&event); 311 } 312 } 313 } 314 315 static int kcodecontrol, kcodealt, kcodeshift; 316 317 /* 318 * Handle an incoming X event. 319 */ 320 static void 321 runxevent(XEvent *xev) 322 { 323 int c; 324 int modp; 325 KeySym k; 326 static Mouse m; 327 XButtonEvent *be; 328 XKeyEvent *ke; 329 Xwin *w; 330 331 modp = 0; 332 333 #ifdef SHOWEVENT 334 static int first = 1; 335 if(first){ 336 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1); 337 setbuf(stdout, 0); 338 first = 0; 339 } 340 #endif 341 342 if(xev == 0) 343 return; 344 345 #ifdef SHOWEVENT 346 print("\n"); 347 ShowEvent(xev); 348 #endif 349 350 w = nil; 351 switch(xev->type){ 352 case Expose: 353 w = findxwin(((XExposeEvent*)xev)->window); 354 break; 355 case DestroyNotify: 356 w = findxwin(((XDestroyWindowEvent*)xev)->window); 357 break; 358 case ConfigureNotify: 359 w = findxwin(((XConfigureEvent*)xev)->window); 360 break; 361 case ButtonPress: 362 case ButtonRelease: 363 w = findxwin(((XButtonEvent*)xev)->window); 364 break; 365 case MotionNotify: 366 w = findxwin(((XMotionEvent*)xev)->window); 367 break; 368 case KeyRelease: 369 case KeyPress: 370 w = findxwin(((XKeyEvent*)xev)->window); 371 break; 372 case FocusOut: 373 w = findxwin(((XFocusChangeEvent*)xev)->window); 374 break; 375 } 376 if(w == nil) 377 w = _x.windows; 378 379 int shift; 380 switch(xev->type){ 381 case Expose: 382 _xexpose(w, xev); 383 break; 384 385 case DestroyNotify: 386 if(_xdestroy(w, xev)) 387 threadexitsall(nil); 388 break; 389 390 case ConfigureNotify: 391 if(_xconfigure(w, xev)) 392 _xreplacescreenimage(w->client); 393 break; 394 395 case ButtonPress: 396 be = (XButtonEvent*)xev; 397 if(be->button == 1) { 398 if(_x.kstate & ControlMask) 399 be->button = 2; 400 else if(_x.kstate & Mod1Mask) 401 be->button = 3; 402 } 403 // fall through 404 case ButtonRelease: 405 _x.altdown = 0; 406 // fall through 407 case MotionNotify: 408 if(_xtoplan9mouse(w, xev, &m) < 0) 409 return; 410 shift = 0; 411 if(_x.kstate & ShiftMask) 412 shift = 5; 413 gfx_mousetrack(w->client, m.xy.x, m.xy.y, (m.buttons|_x.kbuttons)<<shift, m.msec); 414 break; 415 416 case KeyRelease: 417 case KeyPress: 418 ke = (XKeyEvent*)xev; 419 XLookupString(ke, NULL, 0, &k, NULL); 420 c = ke->state; 421 switch(k) { 422 case XK_Alt_L: 423 case XK_Meta_L: /* Shift Alt on PCs */ 424 case XK_Alt_R: 425 case XK_Meta_R: /* Shift Alt on PCs */ 426 case XK_Multi_key: 427 if(xev->type == KeyPress) 428 _x.altdown = 1; 429 else if(_x.altdown) { 430 _x.altdown = 0; 431 gfx_keystroke(w->client, Kalt); 432 } 433 break; 434 } 435 436 if(xev->type == KeyPress) 437 switch(k) { 438 case XK_Control_L: 439 case XK_Control_R: 440 kcodecontrol = ke->keycode; 441 c |= ControlMask; 442 modp = 1; 443 break; 444 case XK_Alt_L: 445 case XK_Alt_R: 446 kcodealt = ke->keycode; 447 c |= Mod1Mask; 448 modp = 1; 449 break; 450 case XK_Shift_L: 451 case XK_Shift_R: 452 kcodeshift = ke->keycode; 453 c |= ShiftMask; 454 modp = 1; 455 break; 456 } 457 else { 458 if(ke->keycode == kcodecontrol){ 459 c &= ~ControlMask; 460 modp = 1; 461 } else if(ke->keycode == kcodealt){ 462 c &= ~Mod1Mask; 463 modp = 1; 464 } else if(ke->keycode == kcodeshift) { 465 c &= ~ShiftMask; 466 modp = 1; 467 } 468 } 469 if(modp){ 470 _x.kstate = c; 471 if(m.buttons || _x.kbuttons) { 472 int shift = 0; 473 _x.altdown = 0; // used alt 474 _x.kbuttons = 0; 475 if(c & ControlMask) 476 _x.kbuttons |= 2; 477 if(c & Mod1Mask) 478 _x.kbuttons |= 4; 479 if(c & ShiftMask) 480 shift = 5; 481 gfx_mousetrack(w->client, m.xy.x, m.xy.y, (m.buttons|_x.kbuttons)<<shift, m.msec); 482 } 483 modp = 0; 484 } 485 486 if(xev->type != KeyPress) 487 break; 488 if(k == XK_F11){ 489 w->fullscreen = !w->fullscreen; 490 _xmovewindow(w, w->fullscreen ? w->screenrect : w->windowrect); 491 return; 492 } 493 if((c = _xtoplan9kbd(xev)) < 0) 494 return; 495 gfx_keystroke(w->client, c); 496 break; 497 498 case FocusOut: 499 /* 500 * Some key combinations (e.g. Alt-Tab) can cause us 501 * to see the key down event without the key up event, 502 * so clear out the keyboard state when we lose the focus. 503 */ 504 _x.kstate = 0; 505 _x.kbuttons = 0; 506 _x.altdown = 0; 507 gfx_abortcompose(w->client); 508 break; 509 510 case SelectionRequest: 511 _xselect(xev); 512 break; 513 } 514 } 515 516 517 static Memimage* 518 xattach(Client *client, char *label, char *winsize) 519 { 520 char *argv[2]; 521 int havemin, height, mask, width, x, y; 522 Rectangle r; 523 XClassHint classhint; 524 XDrawable pmid; 525 XScreen *xscreen; 526 XSetWindowAttributes attr; 527 XSizeHints normalhint; 528 XTextProperty name; 529 XWindow xrootwin; 530 XWindowAttributes wattr; 531 XWMHints hint; 532 Atom atoms[2]; 533 Xwin *w; 534 535 USED(client); 536 xscreen = DefaultScreenOfDisplay(_x.display); 537 xrootwin = DefaultRootWindow(_x.display); 538 539 /* 540 * We get to choose the initial rectangle size. 541 * This is arbitrary. In theory we should read the 542 * command line and allow the traditional X options. 543 */ 544 mask = 0; 545 x = 0; 546 y = 0; 547 if(winsize && winsize[0]){ 548 if(parsewinsize(winsize, &r, &havemin) < 0) 549 sysfatal("%r"); 550 }else{ 551 /* 552 * Parse the various X resources. Thanks to Peter Canning. 553 */ 554 char *screen_resources, *display_resources, *geom, 555 *geomrestype, *home, *file, *dpitype; 556 XrmDatabase database; 557 XrmValue geomres, dpires; 558 559 database = XrmGetDatabase(_x.display); 560 screen_resources = XScreenResourceString(xscreen); 561 if(screen_resources != nil){ 562 XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False); 563 XFree(screen_resources); 564 } 565 566 display_resources = XResourceManagerString(_x.display); 567 if(display_resources == nil){ 568 home = getenv("HOME"); 569 if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){ 570 XrmCombineFileDatabase(file, &database, False); 571 free(file); 572 } 573 free(home); 574 }else 575 XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False); 576 577 if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) { 578 if (dpires.addr) { 579 client->displaydpi = atoi(dpires.addr); 580 } 581 } 582 geom = smprint("%s.geometry", label); 583 if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres)) 584 mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height); 585 XrmDestroyDatabase(database); 586 free(geom); 587 588 if((mask & WidthValue) && (mask & HeightValue)){ 589 r = Rect(0, 0, width, height); 590 }else{ 591 r = Rect(0, 0, WidthOfScreen(xscreen)*3/4, 592 HeightOfScreen(xscreen)*3/4); 593 if(Dx(r) > Dy(r)*3/2) 594 r.max.x = r.min.x + Dy(r)*3/2; 595 if(Dy(r) > Dx(r)*3/2) 596 r.max.y = r.min.y + Dx(r)*3/2; 597 } 598 if(mask & XNegative){ 599 x += WidthOfScreen(xscreen); 600 } 601 if(mask & YNegative){ 602 y += HeightOfScreen(xscreen); 603 } 604 havemin = 0; 605 } 606 w = newxwin(client); 607 608 memset(&attr, 0, sizeof attr); 609 attr.colormap = _x.cmap; 610 attr.background_pixel = ~0; 611 attr.border_pixel = 0; 612 w->drawable = XCreateWindow( 613 _x.display, /* display */ 614 xrootwin, /* parent */ 615 x, /* x */ 616 y, /* y */ 617 Dx(r), /* width */ 618 Dy(r), /* height */ 619 0, /* border width */ 620 _x.depth, /* depth */ 621 InputOutput, /* class */ 622 _x.vis, /* visual */ 623 /* valuemask */ 624 CWBackPixel|CWBorderPixel|CWColormap, 625 &attr /* attributes (the above aren't?!) */ 626 ); 627 628 /* 629 * Label and other properties required by ICCCCM. 630 */ 631 memset(&name, 0, sizeof name); 632 if(label == nil) 633 label = "pjw-face-here"; 634 name.value = (uchar*)label; 635 name.encoding = XA_STRING; 636 name.format = 8; 637 name.nitems = strlen((char*)name.value); 638 639 memset(&normalhint, 0, sizeof normalhint); 640 normalhint.flags = PSize|PMaxSize; 641 if(winsize && winsize[0]){ 642 normalhint.flags &= ~PSize; 643 normalhint.flags |= USSize; 644 normalhint.width = Dx(r); 645 normalhint.height = Dy(r); 646 }else{ 647 if((mask & WidthValue) && (mask & HeightValue)){ 648 normalhint.flags &= ~PSize; 649 normalhint.flags |= USSize; 650 normalhint.width = width; 651 normalhint.height = height; 652 } 653 if((mask & WidthValue) && (mask & HeightValue)){ 654 normalhint.flags |= USPosition; 655 normalhint.x = x; 656 normalhint.y = y; 657 } 658 } 659 660 normalhint.max_width = WidthOfScreen(xscreen); 661 normalhint.max_height = HeightOfScreen(xscreen); 662 663 memset(&hint, 0, sizeof hint); 664 hint.flags = InputHint|StateHint; 665 hint.input = 1; 666 hint.initial_state = NormalState; 667 668 memset(&classhint, 0, sizeof classhint); 669 classhint.res_name = label; 670 classhint.res_class = label; 671 672 argv[0] = label; 673 argv[1] = nil; 674 675 XSetWMProperties( 676 _x.display, /* display */ 677 w->drawable, /* window */ 678 &name, /* XA_WM_NAME property */ 679 &name, /* XA_WM_ICON_NAME property */ 680 argv, /* XA_WM_COMMAND */ 681 1, /* argc */ 682 &normalhint, /* XA_WM_NORMAL_HINTS */ 683 &hint, /* XA_WM_HINTS */ 684 &classhint /* XA_WM_CLASSHINTS */ 685 ); 686 XFlush(_x.display); 687 688 if(havemin){ 689 XWindowChanges ch; 690 691 memset(&ch, 0, sizeof ch); 692 ch.x = r.min.x; 693 ch.y = r.min.y; 694 XConfigureWindow(_x.display, w->drawable, CWX|CWY, &ch); 695 /* 696 * Must pretend origin is 0,0 for X. 697 */ 698 r = Rect(0,0,Dx(r),Dy(r)); 699 } 700 /* 701 * Look up clipboard atom. 702 */ 703 if(_x.clipboard == 0) { 704 _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False); 705 _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False); 706 _x.targets = XInternAtom(_x.display, "TARGETS", False); 707 _x.text = XInternAtom(_x.display, "TEXT", False); 708 _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False); 709 _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False); 710 _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False); 711 _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False); 712 } 713 714 atoms[0] = _x.takefocus; 715 atoms[1] = _x.losefocus; 716 XChangeProperty(_x.display, w->drawable, _x.wmprotos, XA_ATOM, 32, 717 PropModeReplace, (uchar*)atoms, 2); 718 719 /* 720 * Put the window on the screen, check to see what size we actually got. 721 */ 722 XMapWindow(_x.display, w->drawable); 723 XSync(_x.display, False); 724 725 if(!XGetWindowAttributes(_x.display, w->drawable, &wattr)) 726 fprint(2, "XGetWindowAttributes failed\n"); 727 else if(wattr.width && wattr.height){ 728 if(wattr.width != Dx(r) || wattr.height != Dy(r)){ 729 r.max.x = wattr.width; 730 r.max.y = wattr.height; 731 } 732 }else 733 fprint(2, "XGetWindowAttributes: bad attrs\n"); 734 w->screenrect = Rect(0, 0, WidthOfScreen(xscreen), HeightOfScreen(xscreen)); 735 w->windowrect = r; 736 737 /* 738 * Allocate our local backing store. 739 */ 740 w->screenr = r; 741 w->screenpm = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth); 742 w->nextscreenpm = w->screenpm; 743 w->screenimage = _xallocmemimage(r, _x.chan, w->screenpm); 744 client->mouserect = r; 745 746 /* 747 * Allocate some useful graphics contexts for the future. 748 * These can be used with any drawable matching w->drawable's 749 * pixel format (which is all the drawables we create). 750 */ 751 if(_x.gcfill == 0) { 752 _x.gcfill = xgc(w->screenpm, FillSolid, -1); 753 _x.gccopy = xgc(w->screenpm, -1, -1); 754 _x.gcsimplesrc = xgc(w->screenpm, FillStippled, -1); 755 _x.gczero = xgc(w->screenpm, -1, -1); 756 _x.gcreplsrc = xgc(w->screenpm, FillTiled, -1); 757 758 pmid = XCreatePixmap(_x.display, w->drawable, 1, 1, 1); 759 _x.gcfill0 = xgc(pmid, FillSolid, 0); 760 _x.gccopy0 = xgc(pmid, -1, -1); 761 _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1); 762 _x.gczero0 = xgc(pmid, -1, -1); 763 _x.gcreplsrc0 = xgc(pmid, FillTiled, -1); 764 XFreePixmap(_x.display, pmid); 765 } 766 767 return w->screenimage; 768 } 769 770 Memimage* 771 rpc_attach(Client *client, char *label, char *winsize) 772 { 773 Memimage *m; 774 775 xlock(); 776 m = xattach(client, label, winsize); 777 xunlock(); 778 return m; 779 } 780 781 void 782 rpc_setlabel(Client *client, char *label) 783 { 784 Xwin *w = (Xwin*)client->view; 785 XTextProperty name; 786 787 /* 788 * Label and other properties required by ICCCCM. 789 */ 790 xlock(); 791 memset(&name, 0, sizeof name); 792 if(label == nil) 793 label = "pjw-face-here"; 794 name.value = (uchar*)label; 795 name.encoding = XA_STRING; 796 name.format = 8; 797 name.nitems = strlen((char*)name.value); 798 799 XSetWMProperties( 800 _x.display, /* display */ 801 w->drawable, /* window */ 802 &name, /* XA_WM_NAME property */ 803 &name, /* XA_WM_ICON_NAME property */ 804 nil, /* XA_WM_COMMAND */ 805 0, /* argc */ 806 nil, /* XA_WM_NORMAL_HINTS */ 807 nil, /* XA_WM_HINTS */ 808 nil /* XA_WM_CLASSHINTS */ 809 ); 810 XFlush(_x.display); 811 xunlock(); 812 } 813 814 /* 815 * Create a GC with a particular fill style and XXX. 816 * Disable generation of GraphicsExpose/NoExpose events in the GC. 817 */ 818 static XGC 819 xgc(XDrawable d, int fillstyle, int foreground) 820 { 821 XGC gc; 822 XGCValues v; 823 824 memset(&v, 0, sizeof v); 825 v.function = GXcopy; 826 v.graphics_exposures = False; 827 gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v); 828 if(fillstyle != -1) 829 XSetFillStyle(_x.display, gc, fillstyle); 830 if(foreground != -1) 831 XSetForeground(_x.display, gc, 0); 832 return gc; 833 } 834 835 836 /* 837 * Initialize map with the Plan 9 rgbv color map. 838 */ 839 static void 840 plan9cmap(void) 841 { 842 int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; 843 static int once; 844 845 if(once) 846 return; 847 once = 1; 848 849 for(r=0; r!=4; r++) 850 for(g = 0; g != 4; g++) 851 for(b = 0; b!=4; b++) 852 for(v = 0; v!=4; v++){ 853 den=r; 854 if(g > den) 855 den=g; 856 if(b > den) 857 den=b; 858 /* divide check -- pick grey shades */ 859 if(den==0) 860 cr=cg=cb=v*17; 861 else { 862 num=17*(4*den+v); 863 cr=r*num/den; 864 cg=g*num/den; 865 cb=b*num/den; 866 } 867 idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); 868 _x.map[idx].red = cr*0x0101; 869 _x.map[idx].green = cg*0x0101; 870 _x.map[idx].blue = cb*0x0101; 871 _x.map[idx].pixel = idx; 872 _x.map[idx].flags = DoRed|DoGreen|DoBlue; 873 874 v7 = v >> 1; 875 idx7 = r*32 + v7*16 + g*4 + b; 876 if((v & 1) == v7){ 877 _x.map7to8[idx7][0] = idx; 878 if(den == 0) { /* divide check -- pick grey shades */ 879 cr = ((255.0/7.0)*v7)+0.5; 880 cg = cr; 881 cb = cr; 882 } 883 else { 884 num=17*15*(4*den+v7*2)/14; 885 cr=r*num/den; 886 cg=g*num/den; 887 cb=b*num/den; 888 } 889 _x.map7[idx7].red = cr*0x0101; 890 _x.map7[idx7].green = cg*0x0101; 891 _x.map7[idx7].blue = cb*0x0101; 892 _x.map7[idx7].pixel = idx7; 893 _x.map7[idx7].flags = DoRed|DoGreen|DoBlue; 894 } 895 else 896 _x.map7to8[idx7][1] = idx; 897 } 898 } 899 900 /* 901 * Initialize and install the rgbv color map as a private color map 902 * for this application. It gets the best colors when it has the 903 * cursor focus. 904 * 905 * We always choose the best depth possible, but that might not 906 * be the default depth. On such "suboptimal" systems, we have to allocate an 907 * empty color map anyway, according to Axel Belinfante. 908 */ 909 static int 910 setupcmap(XWindow w) 911 { 912 char buf[30]; 913 int i; 914 u32int p, pp; 915 XColor c; 916 917 if(_x.depth <= 1) 918 return 0; 919 920 if(_x.depth >= 24) { 921 if(_x.usetable == 0) 922 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); 923 924 /* 925 * The pixel value returned from XGetPixel needs to 926 * be converted to RGB so we can call rgb2cmap() 927 * to translate between 24 bit X and our color. Unfortunately, 928 * the return value appears to be display server endian 929 * dependant. Therefore, we run some heuristics to later 930 * determine how to mask the int value correctly. 931 * Yeah, I know we can look at _x.vis->byte_order but 932 * some displays say MSB even though they run on LSB. 933 * Besides, this is more anal. 934 */ 935 c = _x.map[19]; /* known to have different R, G, B values */ 936 if(!XAllocColor(_x.display, _x.cmap, &c)){ 937 werrstr("XAllocColor: %r"); 938 return -1; 939 } 940 p = c.pixel; 941 pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); 942 if(pp != _x.map[19].pixel) { 943 /* check if endian is other way */ 944 pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); 945 if(pp != _x.map[19].pixel){ 946 werrstr("cannot detect X server byte order"); 947 return -1; 948 } 949 950 switch(_x.chan){ 951 case RGB24: 952 _x.chan = BGR24; 953 break; 954 case XRGB32: 955 _x.chan = XBGR32; 956 break; 957 default: 958 werrstr("cannot byteswap channel %s", 959 chantostr(buf, _x.chan)); 960 break; 961 } 962 } 963 }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){ 964 /* 965 * Do nothing. We have no way to express a 966 * mixed-endian 16-bit screen, so pretend they don't exist. 967 */ 968 if(_x.usetable == 0) 969 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); 970 }else if(_x.vis->class == PseudoColor){ 971 if(_x.usetable == 0){ 972 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll); 973 XStoreColors(_x.display, _x.cmap, _x.map, 256); 974 for(i = 0; i < 256; i++){ 975 _x.tox11[i] = i; 976 _x.toplan9[i] = i; 977 } 978 }else{ 979 for(i = 0; i < 128; i++){ 980 c = _x.map7[i]; 981 if(!XAllocColor(_x.display, _x.cmap, &c)){ 982 werrstr("can't allocate colors in 7-bit map"); 983 return -1; 984 } 985 _x.tox11[_x.map7to8[i][0]] = c.pixel; 986 _x.tox11[_x.map7to8[i][1]] = c.pixel; 987 _x.toplan9[c.pixel] = _x.map7to8[i][0]; 988 } 989 } 990 }else{ 991 werrstr("unsupported visual class %d", _x.vis->class); 992 return -1; 993 } 994 return 0; 995 } 996 997 void 998 rpc_shutdown(void) 999 { 1000 } 1001 1002 void 1003 rpc_flush(Client *client, Rectangle r) 1004 { 1005 Xwin *w = (Xwin*)client->view; 1006 1007 xlock(); 1008 if(w->nextscreenpm != w->screenpm){ 1009 XSync(_x.display, False); 1010 XFreePixmap(_x.display, w->screenpm); 1011 w->screenpm = w->nextscreenpm; 1012 } 1013 1014 if(r.min.x >= r.max.x || r.min.y >= r.max.y) { 1015 xunlock(); 1016 return; 1017 } 1018 1019 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, 1020 Dx(r), Dy(r), r.min.x, r.min.y); 1021 XFlush(_x.display); 1022 xunlock(); 1023 } 1024 1025 static void 1026 _xexpose(Xwin *w, XEvent *e) 1027 { 1028 XExposeEvent *xe; 1029 Rectangle r; 1030 1031 if(w->screenpm != w->nextscreenpm) 1032 return; 1033 xe = (XExposeEvent*)e; 1034 r.min.x = xe->x; 1035 r.min.y = xe->y; 1036 r.max.x = xe->x+xe->width; 1037 r.max.y = xe->y+xe->height; 1038 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, 1039 Dx(r), Dy(r), r.min.x, r.min.y); 1040 XSync(_x.display, False); 1041 } 1042 1043 static int 1044 _xdestroy(Xwin *w, XEvent *e) 1045 { 1046 XDestroyWindowEvent *xe; 1047 1048 xe = (XDestroyWindowEvent*)e; 1049 if(xe->window == w->drawable){ 1050 w->destroyed = 1; 1051 return 1; 1052 } 1053 return 0; 1054 } 1055 1056 static int 1057 _xconfigure(Xwin *w, XEvent *e) 1058 { 1059 Rectangle r; 1060 XConfigureEvent *xe = (XConfigureEvent*)e; 1061 1062 if(!w->fullscreen){ 1063 int rx, ry; 1064 XWindow xw; 1065 if(XTranslateCoordinates(_x.display, w->drawable, DefaultRootWindow(_x.display), 0, 0, &rx, &ry, &xw)) 1066 w->windowrect = Rect(rx, ry, rx+xe->width, ry+xe->height); 1067 } 1068 1069 if(xe->width == Dx(w->screenr) && xe->height == Dy(w->screenr)) 1070 return 0; 1071 r = Rect(0, 0, xe->width, xe->height); 1072 1073 if(w->screenpm != w->nextscreenpm){ 1074 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, 1075 Dx(r), Dy(r), r.min.x, r.min.y); 1076 XSync(_x.display, False); 1077 } 1078 w->newscreenr = r; 1079 return 1; 1080 } 1081 1082 static int 1083 _xreplacescreenimage(Client *client) 1084 { 1085 Memimage *m; 1086 XDrawable pixmap; 1087 Rectangle r; 1088 Xwin *w; 1089 1090 w = (Xwin*)client->view; 1091 r = w->newscreenr; 1092 pixmap = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth); 1093 m = _xallocmemimage(r, _x.chan, pixmap); 1094 if(w->nextscreenpm != w->screenpm) 1095 XFreePixmap(_x.display, w->nextscreenpm); 1096 w->nextscreenpm = pixmap; 1097 w->screenr = r; 1098 client->mouserect = r; 1099 xunlock(); 1100 gfx_replacescreenimage(client, m); 1101 xlock(); 1102 return 1; 1103 } 1104 1105 void 1106 rpc_resizeimg(Client *client) 1107 { 1108 xlock(); 1109 _xreplacescreenimage(client); 1110 xunlock(); 1111 } 1112 1113 void 1114 rpc_gfxdrawlock(void) 1115 { 1116 xlock(); 1117 } 1118 1119 void 1120 rpc_gfxdrawunlock(void) 1121 { 1122 xunlock(); 1123 } 1124 void 1125 rpc_topwin(Client *client) 1126 { 1127 Xwin *w = (Xwin*)client->view; 1128 1129 xlock(); 1130 XMapRaised(_x.display, w->drawable); 1131 XSetInputFocus(_x.display, w->drawable, RevertToPointerRoot, 1132 CurrentTime); 1133 XFlush(_x.display); 1134 xunlock(); 1135 } 1136 1137 void 1138 rpc_resizewindow(Client *client, Rectangle r) 1139 { 1140 Xwin *w = (Xwin*)client->view; 1141 XWindowChanges e; 1142 int value_mask; 1143 1144 xlock(); 1145 memset(&e, 0, sizeof e); 1146 value_mask = CWX|CWY|CWWidth|CWHeight; 1147 e.width = Dx(r); 1148 e.height = Dy(r); 1149 XConfigureWindow(_x.display, w->drawable, value_mask, &e); 1150 XFlush(_x.display); 1151 xunlock(); 1152 } 1153 1154 static void 1155 _xmovewindow(Xwin *w, Rectangle r) 1156 { 1157 XWindowChanges e; 1158 int value_mask; 1159 1160 memset(&e, 0, sizeof e); 1161 value_mask = CWX|CWY|CWWidth|CWHeight; 1162 e.x = r.min.x; 1163 e.y = r.min.y; 1164 e.width = Dx(r); 1165 e.height = Dy(r); 1166 XConfigureWindow(_x.display, w->drawable, value_mask, &e); 1167 XFlush(_x.display); 1168 } 1169 1170 static int 1171 _xtoplan9kbd(XEvent *e) 1172 { 1173 KeySym k; 1174 1175 if(e->xany.type != KeyPress) 1176 return -1; 1177 needstack(64*1024); /* X has some *huge* buffers in openobject */ 1178 /* and they're even bigger on SuSE */ 1179 XLookupString((XKeyEvent*)e,NULL,0,&k,NULL); 1180 if(k == NoSymbol) 1181 return -1; 1182 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 = Khome; 1215 break; 1216 case XK_Left: 1217 case XK_KP_Left: 1218 k = Kleft; 1219 break; 1220 case XK_Up: 1221 case XK_KP_Up: 1222 k = Kup; 1223 break; 1224 case XK_Down: 1225 case XK_KP_Down: 1226 k = Kdown; 1227 break; 1228 case XK_Right: 1229 case XK_KP_Right: 1230 k = Kright; 1231 break; 1232 case XK_Page_Down: 1233 case XK_KP_Page_Down: 1234 k = Kpgdown; 1235 break; 1236 case XK_End: 1237 case XK_KP_End: 1238 k = Kend; 1239 break; 1240 case XK_Page_Up: 1241 case XK_KP_Page_Up: 1242 k = Kpgup; 1243 break; 1244 case XK_Insert: 1245 case XK_KP_Insert: 1246 k = Kins; 1247 break; 1248 case XK_KP_Enter: 1249 case XK_Return: 1250 k = '\n'; 1251 break; 1252 case XK_Alt_L: 1253 case XK_Meta_L: /* Shift Alt on PCs */ 1254 case XK_Alt_R: 1255 case XK_Meta_R: /* Shift Alt on PCs */ 1256 case XK_Multi_key: 1257 return -1; 1258 default: /* not ISO-1 or tty control */ 1259 if(k>0xff) { 1260 k = _p9keysym2ucs(k); 1261 if(k==-1) return -1; 1262 } 1263 } 1264 } 1265 1266 /* Compensate for servers that call a minus a hyphen */ 1267 if(k == XK_hyphen) 1268 k = XK_minus; 1269 /* Do control mapping ourselves if translator doesn't */ 1270 if(e->xkey.state&ControlMask) 1271 k &= 0x9f; 1272 if(k == NoSymbol) { 1273 return -1; 1274 } 1275 1276 return k+0; 1277 } 1278 1279 int 1280 _xtoplan9buttons(unsigned int b) 1281 { 1282 if(b == 0){ 1283 return 0; 1284 } 1285 return 1<<(b-1); 1286 } 1287 1288 static int 1289 _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m) 1290 { 1291 XButtonEvent *be; 1292 XMotionEvent *me; 1293 1294 if(_x.putsnarf != _x.assertsnarf){ 1295 _x.assertsnarf = _x.putsnarf; 1296 XSetSelectionOwner(_x.display, XA_PRIMARY, w->drawable, CurrentTime); 1297 if(_x.clipboard != None) 1298 XSetSelectionOwner(_x.display, _x.clipboard, w->drawable, CurrentTime); 1299 XFlush(_x.display); 1300 } 1301 1302 switch(e->type){ 1303 case ButtonPress: 1304 be = (XButtonEvent*)e; 1305 1306 /* 1307 * Fake message, just sent to make us announce snarf. 1308 * Apparently state and button are 16 and 8 bits on 1309 * the wire, since they are truncated by the time they 1310 * get to us. 1311 */ 1312 if(be->send_event 1313 && (~be->state&0xFFFF)==0 1314 && (~be->button&0xFF)==0) 1315 return -1; 1316 /* BUG? on mac need to inherit these from elsewhere? */ 1317 m->xy.x = be->x; 1318 m->xy.y = be->y; 1319 m->msec = be->time; 1320 m->buttons |= _xtoplan9buttons(be->button); 1321 break; 1322 case ButtonRelease: 1323 be = (XButtonEvent*)e; 1324 m->xy.x = be->x; 1325 m->xy.y = be->y; 1326 m->msec = be->time; 1327 m->buttons &= ~_xtoplan9buttons(be->button); 1328 1329 case MotionNotify: 1330 me = (XMotionEvent*)e; 1331 m->xy.x = me->x; 1332 m->xy.y = me->y; 1333 m->msec = me->time; 1334 return 0; // do not set buttons 1335 1336 default: 1337 return -1; 1338 } 1339 return 0; 1340 } 1341 1342 void 1343 rpc_setmouse(Client *client, Point p) 1344 { 1345 Xwin *w = (Xwin*)client->view; 1346 1347 xlock(); 1348 XWarpPointer(_x.display, None, w->drawable, 0, 0, 0, 0, p.x, p.y); 1349 XFlush(_x.display); 1350 xunlock(); 1351 } 1352 1353 static int 1354 revbyte(int b) 1355 { 1356 int r; 1357 1358 r = 0; 1359 r |= (b&0x01) << 7; 1360 r |= (b&0x02) << 5; 1361 r |= (b&0x04) << 3; 1362 r |= (b&0x08) << 1; 1363 r |= (b&0x10) >> 1; 1364 r |= (b&0x20) >> 3; 1365 r |= (b&0x40) >> 5; 1366 r |= (b&0x80) >> 7; 1367 return r; 1368 } 1369 1370 static void 1371 xcursorarrow(Xwin *w) 1372 { 1373 if(_x.cursor != 0){ 1374 XFreeCursor(_x.display, _x.cursor); 1375 _x.cursor = 0; 1376 } 1377 XUndefineCursor(_x.display, w->drawable); 1378 XFlush(_x.display); 1379 } 1380 1381 1382 void 1383 rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2) 1384 { 1385 Xwin *w = (Xwin*)client->view; 1386 XColor fg, bg; 1387 XCursor xc; 1388 Pixmap xsrc, xmask; 1389 int i; 1390 uchar src[2*16], mask[2*16]; 1391 1392 USED(c2); 1393 1394 xlock(); 1395 if(c == nil){ 1396 xcursorarrow(w); 1397 xunlock(); 1398 return; 1399 } 1400 for(i=0; i<2*16; i++){ 1401 src[i] = revbyte(c->set[i]); 1402 mask[i] = revbyte(c->set[i] | c->clr[i]); 1403 } 1404 1405 fg = _x.map[0]; 1406 bg = _x.map[255]; 1407 xsrc = XCreateBitmapFromData(_x.display, w->drawable, (char*)src, 16, 16); 1408 xmask = XCreateBitmapFromData(_x.display, w->drawable, (char*)mask, 16, 16); 1409 xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y); 1410 if(xc != 0) { 1411 XDefineCursor(_x.display, w->drawable, xc); 1412 if(_x.cursor != 0) 1413 XFreeCursor(_x.display, _x.cursor); 1414 _x.cursor = xc; 1415 } 1416 XFreePixmap(_x.display, xsrc); 1417 XFreePixmap(_x.display, xmask); 1418 XFlush(_x.display); 1419 xunlock(); 1420 } 1421 1422 struct { 1423 QLock lk; 1424 char buf[SnarfSize]; 1425 #ifdef APPLESNARF 1426 Rune rbuf[SnarfSize]; 1427 PasteboardRef apple; 1428 #endif 1429 } clip; 1430 1431 static uchar* 1432 _xgetsnarffrom(Xwin *w, XWindow xw, Atom clipboard, Atom target, int timeout0, int timeout) 1433 { 1434 Atom prop, type; 1435 ulong len, lastlen, dummy; 1436 int fmt, i; 1437 uchar *data, *xdata; 1438 1439 /* 1440 * We should be waiting for SelectionNotify here, but it might never 1441 * come, and we have no way to time out. Instead, we will clear 1442 * local property #1, request our buddy to fill it in for us, and poll 1443 * until he's done or we get tired of waiting. 1444 */ 1445 prop = 1; 1446 XChangeProperty(_x.display, w->drawable, prop, target, 8, PropModeReplace, (uchar*)"", 0); 1447 XConvertSelection(_x.display, clipboard, target, prop, w->drawable, CurrentTime); 1448 XFlush(_x.display); 1449 lastlen = 0; 1450 timeout0 = (timeout0 + 9)/10; 1451 timeout = (timeout + 9)/10; 1452 for(i=0; i<timeout0 || (lastlen!=0 && i<timeout); i++){ 1453 usleep(10*1000); 1454 XGetWindowProperty(_x.display, w->drawable, prop, 0, 0, 0, AnyPropertyType, 1455 &type, &fmt, &dummy, &len, &xdata); 1456 if(lastlen == len && len > 0){ 1457 XFree(xdata); 1458 break; 1459 } 1460 lastlen = len; 1461 XFree(xdata); 1462 } 1463 if(len == 0) 1464 return nil; 1465 1466 /* get the property */ 1467 xdata = nil; 1468 XGetWindowProperty(_x.display, w->drawable, prop, 0, SnarfSize/sizeof(ulong), 0, 1469 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); 1470 if((type != target && type != XA_STRING && type != _x.utf8string) || len == 0){ 1471 if(xdata) 1472 XFree(xdata); 1473 return nil; 1474 } 1475 if(xdata){ 1476 data = (uchar*)strdup((char*)xdata); 1477 XFree(xdata); 1478 return data; 1479 } 1480 return nil; 1481 } 1482 1483 char* 1484 rpc_getsnarf(void) 1485 { 1486 uchar *data; 1487 Atom clipboard; 1488 XWindow xw; 1489 Xwin *w; 1490 1491 qlock(&clip.lk); 1492 xlock(); 1493 w = _x.windows; 1494 /* 1495 * Have we snarfed recently and the X server hasn't caught up? 1496 */ 1497 if(_x.putsnarf != _x.assertsnarf) 1498 goto mine; 1499 1500 /* 1501 * Is there a primary selection (highlighted text in an xterm)? 1502 */ 1503 clipboard = XA_PRIMARY; 1504 xw = XGetSelectionOwner(_x.display, XA_PRIMARY); 1505 // TODO check more 1506 if(xw == w->drawable){ 1507 mine: 1508 data = (uchar*)strdup(clip.buf); 1509 goto out; 1510 } 1511 1512 /* 1513 * If not, is there a clipboard selection? 1514 */ 1515 if(xw == None && _x.clipboard != None){ 1516 clipboard = _x.clipboard; 1517 xw = XGetSelectionOwner(_x.display, _x.clipboard); 1518 if(xw == w->drawable) 1519 goto mine; 1520 } 1521 1522 /* 1523 * If not, give up. 1524 */ 1525 if(xw == None){ 1526 data = nil; 1527 goto out; 1528 } 1529 1530 if((data = _xgetsnarffrom(w, xw, clipboard, _x.utf8string, 10, 100)) == nil) 1531 if((data = _xgetsnarffrom(w, xw, clipboard, XA_STRING, 10, 100)) == nil){ 1532 /* nothing left to do */ 1533 } 1534 1535 out: 1536 xunlock(); 1537 qunlock(&clip.lk); 1538 return (char*)data; 1539 } 1540 1541 void 1542 __xputsnarf(char *data) 1543 { 1544 XButtonEvent e; 1545 Xwin *w; 1546 1547 if(strlen(data) >= SnarfSize) 1548 return; 1549 qlock(&clip.lk); 1550 xlock(); 1551 w = _x.windows; 1552 strcpy(clip.buf, data); 1553 /* leave note for mouse proc to assert selection ownership */ 1554 _x.putsnarf++; 1555 1556 /* send mouse a fake event so snarf is announced */ 1557 memset(&e, 0, sizeof e); 1558 e.type = ButtonPress; 1559 e.window = w->drawable; 1560 e.state = ~0; 1561 e.button = ~0; 1562 XSendEvent(_x.display, w->drawable, True, ButtonPressMask, (XEvent*)&e); 1563 XFlush(_x.display); 1564 xunlock(); 1565 qunlock(&clip.lk); 1566 } 1567 1568 static int 1569 _xselect(XEvent *e) 1570 { 1571 char *name; 1572 XEvent r; 1573 XSelectionRequestEvent *xe; 1574 Atom a[4]; 1575 1576 memset(&r, 0, sizeof r); 1577 xe = (XSelectionRequestEvent*)e; 1578 if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d (sizeof atom=%d)\n", 1579 xe->target, xe->requestor, xe->property, xe->selection, sizeof a[0]); 1580 r.xselection.property = xe->property; 1581 if(xe->target == _x.targets){ 1582 a[0] = _x.utf8string; 1583 a[1] = XA_STRING; 1584 a[2] = _x.text; 1585 a[3] = _x.compoundtext; 1586 XChangeProperty(_x.display, xe->requestor, xe->property, XA_ATOM, 1587 32, PropModeReplace, (uchar*)a, nelem(a)); 1588 }else if(xe->target == XA_STRING 1589 || xe->target == _x.utf8string 1590 || xe->target == _x.text 1591 || xe->target == _x.compoundtext 1592 || ((name = XGetAtomName(_x.display, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){ 1593 /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */ 1594 /* if the target is STRING we're supposed to reply with Latin1 XXX */ 1595 qlock(&clip.lk); 1596 XChangeProperty(_x.display, xe->requestor, xe->property, xe->target, 1597 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); 1598 qunlock(&clip.lk); 1599 }else{ 1600 if(strcmp(name, "TIMESTAMP") != 0) 1601 fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); 1602 r.xselection.property = None; 1603 } 1604 1605 r.xselection.display = xe->display; 1606 /* r.xselection.property filled above */ 1607 r.xselection.target = xe->target; 1608 r.xselection.type = SelectionNotify; 1609 r.xselection.requestor = xe->requestor; 1610 r.xselection.time = xe->time; 1611 r.xselection.send_event = True; 1612 r.xselection.selection = xe->selection; 1613 XSendEvent(_x.display, xe->requestor, False, 0, &r); 1614 XFlush(_x.display); 1615 return 0; 1616 } 1617 1618 #ifdef APPLESNARF 1619 char* 1620 _applegetsnarf(void) 1621 { 1622 char *s, *t; 1623 CFArrayRef flavors; 1624 CFDataRef data; 1625 CFIndex nflavor, ndata, j; 1626 CFStringRef type; 1627 ItemCount nitem; 1628 PasteboardItemID id; 1629 PasteboardSyncFlags flags; 1630 UInt32 i; 1631 1632 /* fprint(2, "applegetsnarf\n"); */ 1633 qlock(&clip.lk); 1634 if(clip.apple == nil){ 1635 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ 1636 fprint(2, "apple pasteboard create failed\n"); 1637 qunlock(&clip.lk); 1638 return nil; 1639 } 1640 } 1641 flags = PasteboardSynchronize(clip.apple); 1642 if(flags&kPasteboardClientIsOwner){ 1643 s = strdup(clip.buf); 1644 qunlock(&clip.lk); 1645 return s; 1646 } 1647 if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ 1648 fprint(2, "apple pasteboard get item count failed\n"); 1649 qunlock(&clip.lk); 1650 return nil; 1651 } 1652 for(i=1; i<=nitem; i++){ 1653 if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) 1654 continue; 1655 if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) 1656 continue; 1657 nflavor = CFArrayGetCount(flavors); 1658 for(j=0; j<nflavor; j++){ 1659 type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); 1660 if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) 1661 continue; 1662 if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) 1663 continue; 1664 ndata = CFDataGetLength(data); 1665 qunlock(&clip.lk); 1666 s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data)); 1667 CFRelease(flavors); 1668 CFRelease(data); 1669 for(t=s; *t; t++) 1670 if(*t == '\r') 1671 *t = '\n'; 1672 return s; 1673 } 1674 CFRelease(flavors); 1675 } 1676 qunlock(&clip.lk); 1677 return nil; 1678 } 1679 1680 void 1681 _appleputsnarf(char *s) 1682 { 1683 CFDataRef cfdata; 1684 PasteboardSyncFlags flags; 1685 1686 /* fprint(2, "appleputsnarf\n"); */ 1687 1688 if(strlen(s) >= SnarfSize) 1689 return; 1690 qlock(&clip.lk); 1691 strcpy(clip.buf, s); 1692 runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); 1693 if(clip.apple == nil){ 1694 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ 1695 fprint(2, "apple pasteboard create failed\n"); 1696 qunlock(&clip.lk); 1697 return; 1698 } 1699 } 1700 if(PasteboardClear(clip.apple) != noErr){ 1701 fprint(2, "apple pasteboard clear failed\n"); 1702 qunlock(&clip.lk); 1703 return; 1704 } 1705 flags = PasteboardSynchronize(clip.apple); 1706 if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ 1707 fprint(2, "apple pasteboard cannot assert ownership\n"); 1708 qunlock(&clip.lk); 1709 return; 1710 } 1711 cfdata = CFDataCreate(kCFAllocatorDefault, 1712 (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2); 1713 if(cfdata == nil){ 1714 fprint(2, "apple pasteboard cfdatacreate failed\n"); 1715 qunlock(&clip.lk); 1716 return; 1717 } 1718 if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, 1719 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ 1720 fprint(2, "apple pasteboard putitem failed\n"); 1721 CFRelease(cfdata); 1722 qunlock(&clip.lk); 1723 return; 1724 } 1725 /* CFRelease(cfdata); ??? */ 1726 qunlock(&clip.lk); 1727 } 1728 #endif /* APPLESNARF */ 1729 1730 void 1731 rpc_putsnarf(char *data) 1732 { 1733 #ifdef APPLESNARF 1734 _appleputsnarf(data); 1735 #endif 1736 __xputsnarf(data); 1737 } 1738 1739 /* 1740 * Send the mouse event back to the window manager. 1741 * So that 9term can tell rio to pop up its button3 menu. 1742 */ 1743 void 1744 rpc_bouncemouse(Client *c, Mouse m) 1745 { 1746 Xwin *w = (Xwin*)c->view; 1747 XButtonEvent e; 1748 XWindow dw; 1749 1750 xlock(); 1751 e.type = ButtonPress; 1752 e.state = 0; 1753 e.button = 0; 1754 if(m.buttons&1) 1755 e.button = 1; 1756 else if(m.buttons&2) 1757 e.button = 2; 1758 else if(m.buttons&4) 1759 e.button = 3; 1760 e.same_screen = 1; 1761 XTranslateCoordinates(_x.display, w->drawable, 1762 DefaultRootWindow(_x.display), 1763 m.xy.x, m.xy.y, &e.x_root, &e.y_root, &dw); 1764 e.root = DefaultRootWindow(_x.display); 1765 e.window = e.root; 1766 e.subwindow = None; 1767 e.x = e.x_root; 1768 e.y = e.y_root; 1769 #undef time 1770 e.time = CurrentTime; 1771 XUngrabPointer(_x.display, m.msec); 1772 XSendEvent(_x.display, e.root, True, ButtonPressMask, (XEvent*)&e); 1773 XFlush(_x.display); 1774 xunlock(); 1775 }