hostx.c revision 4642e01f
1/* 2 * Xephyr - A kdrive X server thats runs in a host X window. 3 * Authored by Matthew Allum <mallum@o-hand.com> 4 * 5 * Copyright � 2004 Nokia 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that 10 * copyright notice and this permission notice appear in supporting 11 * documentation, and that the name of Nokia not be used in 12 * advertising or publicity pertaining to distribution of the software without 13 * specific, written prior permission. Nokia makes no 14 * representations about the suitability of this software for any purpose. It 15 * is provided "as is" without express or implied warranty. 16 * 17 * NOKIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19 * EVENT SHALL NOKIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 23 * PERFORMANCE OF THIS SOFTWARE. 24 */ 25 26#ifdef HAVE_CONFIG_H 27#include <kdrive-config.h> 28#endif 29 30/* 31 * including some server headers (like kdrive-config.h) 32 * might define the macro _XSERVER64 33 * on 64 bits machines. That macro must _NOT_ be defined for Xlib 34 * client code, otherwise bad things happen. 35 * So let's undef that macro if necessary. 36 */ 37#ifdef _XSERVER64 38#undef _XSERVER64 39#endif 40 41 42#include "hostx.h" 43 44#include <stdlib.h> 45#include <stdio.h> 46#include <unistd.h> 47#include <string.h> /* for memset */ 48#include <time.h> 49 50#include <sys/ipc.h> 51#include <sys/shm.h> 52#include <sys/time.h> 53 54#include <X11/Xlib.h> 55#include <X11/Xutil.h> 56#include <X11/Xatom.h> 57#include <X11/keysym.h> 58#include <X11/extensions/XShm.h> 59#include <X11/extensions/shape.h> 60#ifdef XF86DRI 61#include <GL/glx.h> 62#endif /* XF86DRI */ 63#include "ephyrlog.h" 64 65#ifdef XF86DRI 66extern Bool XF86DRIQueryExtension (Display *dpy, 67 int *event_basep, 68 int *error_basep); 69#endif 70 71/* 72 * All xlib calls go here, which gets built as its own .a . 73 * Mixing kdrive and xlib headers causes all sorts of types 74 * to get clobbered. 75 */ 76 77struct EphyrHostScreen 78{ 79 Window win; 80 Window win_pre_existing; /* Set via -parent option like xnest */ 81 XImage *ximg; 82 int win_width, win_height; 83 int server_depth; 84 unsigned char *fb_data; /* only used when host bpp != server bpp */ 85 XShmSegmentInfo shminfo; 86 87 void *info; /* Pointer to the screen this is associated with */ 88 int mynum; /* Screen number */ 89}; 90 91struct EphyrHostXVars 92{ 93 char *server_dpy_name; 94 Display *dpy; 95 int screen; 96 Visual *visual; 97 Window winroot; 98 GC gc; 99 int depth; 100 Bool use_host_cursor; 101 Bool use_fullscreen; 102 Bool have_shm; 103 104 int n_screens; 105 struct EphyrHostScreen *screens; 106 107 long damage_debug_msec; 108 109 unsigned long cmap[256]; 110}; 111 112/* memset ( missing> ) instead of below */ 113/*static EphyrHostXVars HostX = { "?", 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};*/ 114static EphyrHostXVars HostX; 115 116static int HostXWantDamageDebug = 0; 117 118extern EphyrKeySyms ephyrKeySyms; 119 120extern int monitorResolution; 121 122char *ephyrResName = NULL; 123int ephyrResNameFromCmd = 0; 124 125static void 126hostx_set_fullscreen_hint(void); 127 128/* X Error traps */ 129 130static int trapped_error_code = 0; 131static int (*old_error_handler) (Display *d, XErrorEvent *e); 132 133#define host_depth_matches_server(_vars) (HostX.depth == (_vars)->server_depth) 134 135static struct EphyrHostScreen * 136host_screen_from_screen_info (EphyrScreenInfo *screen) 137{ 138 int i; 139 140 for (i = 0 ; i < HostX.n_screens ; i++) 141 { 142 if ( HostX.screens[i].info == screen) 143 { 144 return &HostX.screens[i]; 145 } 146 } 147 return NULL; 148} 149 150static int 151error_handler(Display *display, 152 XErrorEvent *error) 153{ 154 trapped_error_code = error->error_code; 155 return 0; 156} 157 158static void 159hostx_errors_trap(void) 160{ 161 trapped_error_code = 0; 162 old_error_handler = XSetErrorHandler(error_handler); 163} 164 165static int 166hostx_errors_untrap(void) 167{ 168 XSetErrorHandler(old_error_handler); 169 return trapped_error_code; 170} 171 172int 173hostx_want_screen_size (EphyrScreenInfo screen, int *width, int *height ) 174{ 175 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 176 177 if (host_screen && 178 (host_screen->win_pre_existing != None || 179 HostX.use_fullscreen == True)) 180 { 181 *width = host_screen->win_width; 182 *height = host_screen->win_height; 183 return 1; 184 } 185 186 return 0; 187} 188 189void 190hostx_add_screen (EphyrScreenInfo screen, 191 unsigned long win_id, 192 int screen_num) 193{ 194 int index = HostX.n_screens; 195 196 HostX.n_screens += 1; 197 HostX.screens = realloc (HostX.screens, 198 HostX.n_screens * sizeof(struct EphyrHostScreen)); 199 memset (&HostX.screens[index], 0, sizeof (struct EphyrHostScreen)); 200 201 HostX.screens[index].info = screen; 202 HostX.screens[index].win_pre_existing = win_id; 203} 204 205 206void 207hostx_set_display_name (char *name) 208{ 209 HostX.server_dpy_name = strdup (name); 210} 211 212void 213hostx_set_screen_number(EphyrScreenInfo screen, int number) 214{ 215 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 216 if (host_screen) { 217 host_screen->mynum = number; 218 hostx_set_win_title (host_screen->info, "") ; 219 }} 220 221void 222hostx_set_win_title (EphyrScreenInfo screen, char *extra_text) 223{ 224 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 225#define BUF_LEN 256 226 char buf[BUF_LEN+1]; 227 228 if (!host_screen) 229 return; 230 231 memset (buf, 0, BUF_LEN+1) ; 232 snprintf (buf, BUF_LEN, "Xephyr on %s.%d %s", 233 HostX.server_dpy_name, 234 host_screen->mynum, 235 (extra_text != NULL) ? extra_text : ""); 236 237 XStoreName (HostX.dpy, host_screen->win, buf); 238} 239 240int 241hostx_want_host_cursor (void) 242{ 243 return HostX.use_host_cursor; 244} 245 246void 247hostx_use_host_cursor (void) 248{ 249 HostX.use_host_cursor = True; 250} 251 252int 253hostx_want_preexisting_window (EphyrScreenInfo screen) 254{ 255 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 256 257 if (host_screen && host_screen->win_pre_existing) 258 { 259 return 1; 260 } 261 else 262 { 263 return 0; 264 } 265} 266 267void 268hostx_use_fullscreen (void) 269{ 270 HostX.use_fullscreen = True; 271} 272 273int 274hostx_want_fullscreen (void) 275{ 276 return HostX.use_fullscreen; 277} 278 279static void 280hostx_set_fullscreen_hint (void) 281{ 282 Atom atom_WINDOW_STATE, atom_WINDOW_STATE_FULLSCREEN; 283 int index; 284 285 atom_WINDOW_STATE 286 = XInternAtom(HostX.dpy, "_NET_WM_STATE", False); 287 atom_WINDOW_STATE_FULLSCREEN 288 = XInternAtom(HostX.dpy, "_NET_WM_STATE_FULLSCREEN",False); 289 290 for (index = 0 ; index < HostX.n_screens ; index++) 291 { 292 XChangeProperty (HostX.dpy, HostX.screens[index].win, 293 atom_WINDOW_STATE, XA_ATOM, 32, 294 PropModeReplace, 295 (unsigned char *)&atom_WINDOW_STATE_FULLSCREEN, 1); 296 } 297} 298 299 300static void 301hostx_toggle_damage_debug (void) 302{ 303 HostXWantDamageDebug ^= 1; 304} 305 306void 307hostx_handle_signal (int signum) 308{ 309 hostx_toggle_damage_debug(); 310 EPHYR_DBG ("Signal caught. Damage Debug:%i\n", 311 HostXWantDamageDebug); 312} 313 314void 315hostx_use_resname (char *name, int fromcmd) 316{ 317 ephyrResName = name; 318 ephyrResNameFromCmd = fromcmd; 319} 320 321int 322hostx_init (void) 323{ 324 XSetWindowAttributes attr; 325 Cursor empty_cursor; 326 Pixmap cursor_pxm; 327 XColor col; 328 int index; 329 char *tmpstr; 330 XClassHint *class_hint; 331 332 attr.event_mask = 333 ButtonPressMask 334 |ButtonReleaseMask 335 |PointerMotionMask 336 |KeyPressMask 337 |KeyReleaseMask 338 |ExposureMask; 339 340 EPHYR_DBG("mark"); 341 342 if ((HostX.dpy = XOpenDisplay(getenv("DISPLAY"))) == NULL) 343 { 344 fprintf(stderr, "\nXephyr cannot open host display. Is DISPLAY set?\n"); 345 exit(1); 346 } 347 348 HostX.screen = DefaultScreen(HostX.dpy); 349 HostX.winroot = RootWindow(HostX.dpy, HostX.screen); 350 HostX.gc = XCreateGC(HostX.dpy, HostX.winroot, 0, NULL); 351 HostX.depth = DefaultDepth(HostX.dpy, HostX.screen); 352 HostX.visual = DefaultVisual(HostX.dpy, HostX.screen); 353 354 class_hint = XAllocClassHint(); 355 356 for (index = 0 ; index < HostX.n_screens ; index++) 357 { 358 struct EphyrHostScreen *host_screen = &HostX.screens[index]; 359 360 host_screen->server_depth = HostX.depth; 361 if (host_screen->win_pre_existing != None) 362 { 363 Status result; 364 XWindowAttributes prewin_attr; 365 366 /* Get screen size from existing window */ 367 368 hostx_errors_trap(); 369 370 result = XGetWindowAttributes (HostX.dpy, 371 host_screen->win_pre_existing, 372 &prewin_attr); 373 374 375 if (hostx_errors_untrap() || !result) 376 { 377 fprintf (stderr, "\nXephyr -parent window' does not exist!\n"); 378 exit (1); 379 } 380 381 host_screen->win_width = prewin_attr.width; 382 host_screen->win_height = prewin_attr.height; 383 384 host_screen->win = XCreateWindow (HostX.dpy, 385 host_screen->win_pre_existing, 386 0,0, 387 host_screen->win_width, 388 host_screen->win_height, 389 0, 390 CopyFromParent, 391 CopyFromParent, 392 CopyFromParent, 393 CWEventMask, 394 &attr); 395 } 396 else 397 { 398 host_screen->win = XCreateWindow (HostX.dpy, 399 HostX.winroot, 400 0,0,100,100, /* will resize */ 401 0, 402 CopyFromParent, 403 CopyFromParent, 404 CopyFromParent, 405 CWEventMask, 406 &attr); 407 408 hostx_set_win_title (host_screen->info, 409 "(ctrl+shift grabs mouse and keyboard)"); 410 411 if (HostX.use_fullscreen) 412 { 413 host_screen->win_width = DisplayWidth(HostX.dpy, HostX.screen); 414 host_screen->win_height = DisplayHeight(HostX.dpy, HostX.screen); 415 416 hostx_set_fullscreen_hint(); 417 } 418 419 if (class_hint) 420 { 421 tmpstr = getenv("RESOURCE_NAME"); 422 if (tmpstr && (!ephyrResNameFromCmd)) 423 ephyrResName = tmpstr; 424 class_hint->res_name = ephyrResName; 425 class_hint->res_class = "Xephyr"; 426 XSetClassHint(hostx_get_display(), host_screen->win, class_hint); 427 428 } 429 430 } 431 } 432 433 if (class_hint) 434 XFree(class_hint); 435 436 XParseColor (HostX.dpy, DefaultColormap (HostX.dpy,HostX.screen), 437 "red", &col); 438 XAllocColor (HostX.dpy, DefaultColormap (HostX.dpy, HostX.screen), 439 &col); 440 XSetForeground (HostX.dpy, HostX.gc, col.pixel); 441 442 if (!hostx_want_host_cursor ()) 443 { 444 /* Ditch the cursor, we provide our 'own' */ 445 cursor_pxm = XCreatePixmap (HostX.dpy, HostX.winroot, 1, 1, 1); 446 memset (&col, 0, sizeof (col)); 447 empty_cursor = XCreatePixmapCursor (HostX.dpy, 448 cursor_pxm, cursor_pxm, 449 &col, &col, 1, 1); 450 for ( index = 0 ; index < HostX.n_screens ; index++ ) 451 { 452 XDefineCursor (HostX.dpy, 453 HostX.screens[index].win, 454 empty_cursor); 455 } 456 XFreePixmap (HostX.dpy, cursor_pxm); 457 } 458 459 for (index = 0 ; index < HostX.n_screens ; index++) 460 { 461 HostX.screens[index].ximg = NULL; 462 } 463 /* Try to get share memory ximages for a little bit more speed */ 464 465 if (!XShmQueryExtension(HostX.dpy) || getenv("XEPHYR_NO_SHM")) 466 { 467 fprintf(stderr, "\nXephyr unable to use SHM XImages\n"); 468 HostX.have_shm = False; 469 } 470 else 471 { 472 /* Really really check we have shm - better way ?*/ 473 XShmSegmentInfo shminfo; 474 475 HostX.have_shm = True; 476 477 shminfo.shmid=shmget(IPC_PRIVATE, 1, IPC_CREAT|0777); 478 shminfo.shmaddr=shmat(shminfo.shmid,0,0); 479 shminfo.readOnly=True; 480 481 hostx_errors_trap(); 482 483 XShmAttach(HostX.dpy, &shminfo); 484 XSync(HostX.dpy, False); 485 486 if (hostx_errors_untrap()) 487 { 488 fprintf(stderr, "\nXephyr unable to use SHM XImages\n"); 489 HostX.have_shm = False; 490 } 491 492 shmdt(shminfo.shmaddr); 493 shmctl(shminfo.shmid, IPC_RMID, 0); 494} 495 496 XFlush(HostX.dpy); 497 498 /* Setup the pause time between paints when debugging updates */ 499 500 HostX.damage_debug_msec = 20000; /* 1/50 th of a second */ 501 502 if (getenv ("XEPHYR_PAUSE")) 503 { 504 HostX.damage_debug_msec = strtol (getenv ("XEPHYR_PAUSE"), NULL, 0); 505 EPHYR_DBG ("pause is %li\n", HostX.damage_debug_msec); 506 } 507 508 return 1; 509} 510 511int 512hostx_get_depth (void) 513{ 514 return HostX.depth; 515} 516 517int 518hostx_get_server_depth (EphyrScreenInfo screen) 519{ 520 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 521 522 return (host_screen ? host_screen->server_depth : 0); 523} 524 525void 526hostx_set_server_depth (EphyrScreenInfo screen, int depth) 527{ 528 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 529 530 if (host_screen) 531 host_screen->server_depth = depth; 532} 533 534int 535hostx_get_bpp (EphyrScreenInfo screen) 536{ 537 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 538 539 if (!host_screen) 540 return 0; 541 542 if (host_depth_matches_server (host_screen)) 543 return HostX.visual->bits_per_rgb; 544 else 545 return host_screen->server_depth; /*XXX correct ?*/ 546} 547 548void 549hostx_get_visual_masks (EphyrScreenInfo screen, 550 CARD32 *rmsk, 551 CARD32 *gmsk, 552 CARD32 *bmsk) 553{ 554 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 555 556 if (!host_screen) 557 return; 558 559 if (host_depth_matches_server(host_screen)) 560 { 561 *rmsk = HostX.visual->red_mask; 562 *gmsk = HostX.visual->green_mask; 563 *bmsk = HostX.visual->blue_mask; 564 } 565 else if (host_screen->server_depth == 16) 566 { 567 /* Assume 16bpp 565 */ 568 *rmsk = 0xf800; 569 *gmsk = 0x07e0; 570 *bmsk = 0x001f; 571 } 572 else 573 { 574 *rmsk = 0x0; 575 *gmsk = 0x0; 576 *bmsk = 0x0; 577 } 578} 579 580static int 581hostx_calculate_color_shift(unsigned long mask) 582{ 583 int shift = 1; 584 /* count # of bits in mask */ 585 while (mask=(mask>>1)) shift++; 586 /* cmap entry is an unsigned char so adjust it by size of that */ 587 shift = shift - sizeof(unsigned char) * 8; 588 if (shift < 0) shift = 0; 589 return shift; 590} 591 592void 593hostx_set_cmap_entry(unsigned char idx, 594 unsigned char r, 595 unsigned char g, 596 unsigned char b) 597{ 598/* need to calculate the shifts for RGB because server could be BGR. */ 599/* XXX Not sure if this is correct for 8 on 16, but this works for 8 on 24.*/ 600 static int rshift, bshift, gshift = 0; 601 static int first_time = 1; 602 if (first_time) { 603 first_time = 0; 604 rshift = hostx_calculate_color_shift(HostX.visual->red_mask); 605 gshift = hostx_calculate_color_shift(HostX.visual->green_mask); 606 bshift = hostx_calculate_color_shift(HostX.visual->blue_mask); 607 } 608 HostX.cmap[idx] = ((r << rshift) & HostX.visual->red_mask) | 609 ((g << gshift) & HostX.visual->green_mask) | 610 ((b << bshift) & HostX.visual->blue_mask); 611} 612 613/** 614 * hostx_screen_init creates the XImage that will contain the front buffer of 615 * the ephyr screen, and possibly offscreen memory. 616 * 617 * @param width width of the screen 618 * @param height height of the screen 619 * @param buffer_height height of the rectangle to be allocated. 620 * 621 * hostx_screen_init() creates an XImage, using MIT-SHM if it's available. 622 * buffer_height can be used to create a larger offscreen buffer, which is used 623 * by fakexa for storing offscreen pixmap data. 624 */ 625void* 626hostx_screen_init (EphyrScreenInfo screen, 627 int width, int height, 628 int buffer_height) 629{ 630 int bitmap_pad; 631 Bool shm_success = False; 632 XSizeHints *size_hints; 633 634 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 635 if (!host_screen) 636 { 637 fprintf (stderr, "%s: Error in accessing hostx data\n", __func__ ); 638 exit(1); 639 } 640 641 EPHYR_DBG ("host_screen=%p wxh=%dx%d, buffer_height=%d", 642 host_screen, width, height, buffer_height); 643 644 if (host_screen->ximg != NULL) 645 { 646 /* Free up the image data if previously used 647 * i.ie called by server reset 648 */ 649 650 if (HostX.have_shm) 651 { 652 XShmDetach(HostX.dpy, &host_screen->shminfo); 653 XDestroyImage (host_screen->ximg); 654 shmdt(host_screen->shminfo.shmaddr); 655 shmctl(host_screen->shminfo.shmid, IPC_RMID, 0); 656 } 657 else 658 { 659 if (host_screen->ximg->data) 660 { 661 free(host_screen->ximg->data); 662 host_screen->ximg->data = NULL; 663 } 664 665 XDestroyImage(host_screen->ximg); 666 } 667 } 668 669 if (HostX.have_shm) 670 { 671 host_screen->ximg = XShmCreateImage (HostX.dpy, HostX.visual, HostX.depth, 672 ZPixmap, NULL, &host_screen->shminfo, 673 width, buffer_height ); 674 675 host_screen->shminfo.shmid = 676 shmget(IPC_PRIVATE, 677 host_screen->ximg->bytes_per_line * buffer_height, 678 IPC_CREAT|0777); 679 host_screen->ximg->data = shmat(host_screen->shminfo.shmid, 0, 0); 680 host_screen->shminfo.shmaddr = host_screen->ximg->data; 681 682 if (host_screen->ximg->data == (char *)-1) 683 { 684 EPHYR_DBG("Can't attach SHM Segment, falling back to plain XImages"); 685 HostX.have_shm = False; 686 XDestroyImage(host_screen->ximg); 687 shmctl(host_screen->shminfo.shmid, IPC_RMID, 0); 688 } 689 else 690 { 691 EPHYR_DBG("SHM segment attached %p", host_screen->shminfo.shmaddr); 692 host_screen->shminfo.readOnly = False; 693 XShmAttach(HostX.dpy, &host_screen->shminfo); 694 shm_success = True; 695 } 696 } 697 698 if (!shm_success) 699 { 700 bitmap_pad = ( HostX.depth > 16 )? 32 : (( HostX.depth > 8 )? 16 : 8 ); 701 702 EPHYR_DBG("Creating image %dx%d for screen host_screen=%p\n", 703 width, buffer_height, host_screen ); 704 host_screen->ximg = XCreateImage (HostX.dpy, 705 HostX.visual, 706 HostX.depth, 707 ZPixmap, 0, 0, 708 width, 709 buffer_height, 710 bitmap_pad, 711 0); 712 713 host_screen->ximg->data = 714 malloc (host_screen->ximg->bytes_per_line * buffer_height); 715 } 716 717 XResizeWindow (HostX.dpy, host_screen->win, width, height); 718 719 /* Ask the WM to keep our size static */ 720 size_hints = XAllocSizeHints(); 721 size_hints->max_width = size_hints->min_width = width; 722 size_hints->max_height = size_hints->min_height = height; 723 size_hints->flags = PMinSize|PMaxSize; 724 XSetWMNormalHints(HostX.dpy, host_screen->win, size_hints); 725 XFree(size_hints); 726 727 XMapWindow(HostX.dpy, host_screen->win); 728 729 XSync(HostX.dpy, False); 730 731 host_screen->win_width = width; 732 host_screen->win_height = height; 733 734 if (host_depth_matches_server(host_screen)) 735 { 736 EPHYR_DBG("Host matches server"); 737 return host_screen->ximg->data; 738 } 739 else 740 { 741 EPHYR_DBG("server bpp %i", host_screen->server_depth>>3); 742 host_screen->fb_data = malloc(width*buffer_height*(host_screen->server_depth>>3)); 743 return host_screen->fb_data; 744 } 745} 746 747static void hostx_paint_debug_rect (struct EphyrHostScreen *host_screen, 748 int x, int y, 749 int width, int height); 750 751void 752hostx_paint_rect (EphyrScreenInfo screen, 753 int sx, int sy, 754 int dx, int dy, 755 int width, int height) 756{ 757 struct EphyrHostScreen *host_screen = host_screen_from_screen_info (screen); 758 759 EPHYR_DBG ("painting in screen %d\n", host_screen->mynum) ; 760 761 /* 762 * Copy the image data updated by the shadow layer 763 * on to the window 764 */ 765 766 if (HostXWantDamageDebug) 767 { 768 hostx_paint_debug_rect(host_screen, dx, dy, width, height); 769 } 770 771 /* 772 * If the depth of the ephyr server is less than that of the host, 773 * the kdrive fb does not point to the ximage data but to a buffer 774 * ( fb_data ), we shift the various bits from this onto the XImage 775 * so they match the host. 776 * 777 * Note, This code is pretty new ( and simple ) so may break on 778 * endian issues, 32 bpp host etc. 779 * Not sure if 8bpp case is right either. 780 * ... and it will be slower than the matching depth case. 781 */ 782 783 if (!host_depth_matches_server(host_screen)) 784 { 785 int x,y,idx, bytes_per_pixel = (host_screen->server_depth>>3); 786 unsigned char r,g,b; 787 unsigned long host_pixel; 788 789 EPHYR_DBG("Unmatched host depth host_screen=%p\n", host_screen); 790 for (y=sy; y<sy+height; y++) 791 for (x=sx; x<sx+width; x++) 792 { 793 idx = (host_screen->win_width*y*bytes_per_pixel)+(x*bytes_per_pixel); 794 795 switch (host_screen->server_depth) 796 { 797 case 16: 798 { 799 unsigned short pixel = *(unsigned short*)(host_screen->fb_data+idx); 800 801 r = ((pixel & 0xf800) >> 8); 802 g = ((pixel & 0x07e0) >> 3); 803 b = ((pixel & 0x001f) << 3); 804 805 host_pixel = (r << 16) | (g << 8) | (b); 806 807 XPutPixel(host_screen->ximg, x, y, host_pixel); 808 break; 809 } 810 case 8: 811 { 812 unsigned char pixel = *(unsigned char*)(host_screen->fb_data+idx); 813 XPutPixel(host_screen->ximg, x, y, HostX.cmap[pixel]); 814 break; 815 } 816 default: 817 break; 818 } 819 } 820 } 821 822 if (HostX.have_shm) 823 { 824 XShmPutImage (HostX.dpy, host_screen->win, 825 HostX.gc, host_screen->ximg, 826 sx, sy, dx, dy, width, height, False); 827 } 828 else 829 { 830 XPutImage (HostX.dpy, host_screen->win, HostX.gc, host_screen->ximg, 831 sx, sy, dx, dy, width, height); 832 } 833 834 XSync (HostX.dpy, False); 835} 836 837static void 838hostx_paint_debug_rect (struct EphyrHostScreen *host_screen, 839 int x, int y, 840 int width, int height) 841{ 842 struct timespec tspec; 843 844 tspec.tv_sec = HostX.damage_debug_msec / (1000000); 845 tspec.tv_nsec = (HostX.damage_debug_msec % 1000000) * 1000; 846 847 EPHYR_DBG("msec: %li tv_sec %li, tv_msec %li", 848 HostX.damage_debug_msec, tspec.tv_sec, tspec.tv_nsec); 849 850 /* fprintf(stderr, "Xephyr updating: %i+%i %ix%i\n", x, y, width, height); */ 851 852 XFillRectangle (HostX.dpy, host_screen->win, HostX.gc, x, y, width,height); 853 XSync (HostX.dpy, False); 854 855 /* nanosleep seems to work better than usleep for me... */ 856 nanosleep(&tspec, NULL); 857} 858 859void 860hostx_load_keymap(void) 861{ 862 XID *keymap; 863 int host_width, min_keycode, max_keycode, width; 864 int i,j; 865 866 XDisplayKeycodes (HostX.dpy, &min_keycode, &max_keycode); 867 868 EPHYR_DBG ("min: %d, max: %d", min_keycode, max_keycode); 869 870 keymap = XGetKeyboardMapping (HostX.dpy, 871 min_keycode, 872 max_keycode - min_keycode + 1, 873 &host_width); 874 875 /* Try and copy the hosts keymap into our keymap to avoid loads 876 * of messing around. 877 * 878 * kdrive cannot can have more than 4 keysyms per keycode 879 * so we only copy at most the first 4 ( xorg has 6 per keycode, XVNC 2 ) 880 */ 881 width = (host_width > 4) ? 4 : host_width; 882 883 ephyrKeySyms.map = (CARD32 *)calloc(sizeof(CARD32), 884 (max_keycode - min_keycode + 1) * 885 width); 886 if (!ephyrKeySyms.map) 887 return; 888 889 for (i=0; i<(max_keycode - min_keycode+1); i++) 890 for (j=0; j<width; j++) 891 ephyrKeySyms.map[(i*width)+j] = (CARD32) keymap[(i*host_width) + j]; 892 893 EPHYR_DBG("keymap width, host:%d kdrive:%d", host_width, width); 894 895 ephyrKeySyms.minKeyCode = min_keycode; 896 ephyrKeySyms.maxKeyCode = max_keycode; 897 ephyrKeySyms.mapWidth = width; 898 899 XFree(keymap); 900} 901 902static struct EphyrHostScreen * 903host_screen_from_window (Window w) 904{ 905 int index = 0; 906 struct EphyrHostScreen *result = NULL; 907#if 0 908 unsigned int num_children = 0; 909 Window root = None, parent = None, *children = NULL; 910#endif 911 912 for (index = 0 ; index < HostX.n_screens ; index++) 913 { 914 if (HostX.screens[index].win == w) 915 { 916 result = &HostX.screens[index]; 917 goto out; 918 } 919 } 920#if 0 921 XQueryTree (hostx_get_display (), w, &root, &parent, 922 &children, &num_children); 923 if (parent == root || parent == None) 924 goto out; 925 result = host_screen_from_window (parent); 926#endif 927 928out: 929#if 0 930 if (children) 931 { 932 XFree (children); 933 children = NULL; 934 } 935#endif 936 return result; 937} 938 939int 940hostx_get_event(EphyrHostXEvent *ev) 941{ 942 XEvent xev; 943 static int grabbed_screen = -1; 944 945 if (XPending(HostX.dpy)) 946 { 947 XNextEvent(HostX.dpy, &xev); 948 949 switch (xev.type) 950 { 951 case Expose: 952 /* Not so great event compression, but works ok */ 953 while (XCheckTypedWindowEvent(HostX.dpy, xev.xexpose.window, 954 Expose, &xev)); 955 { 956 struct EphyrHostScreen *host_screen = 957 host_screen_from_window (xev.xexpose.window); 958 if (host_screen) 959 { 960 hostx_paint_rect (host_screen->info, 0, 0, 0, 0, 961 host_screen->win_width, 962 host_screen->win_height); 963 } 964 else 965 { 966 EPHYR_LOG_ERROR ("failed to get host screen\n"); 967 ev->type = EPHYR_EV_EXPOSE; 968 ev->data.expose.window = xev.xexpose.window; 969 return 1; 970 } 971 } 972 return 0; 973 974 case MotionNotify: 975 { 976 struct EphyrHostScreen *host_screen = 977 host_screen_from_window (xev.xmotion.window); 978 979 ev->type = EPHYR_EV_MOUSE_MOTION; 980 ev->data.mouse_motion.x = xev.xmotion.x; 981 ev->data.mouse_motion.y = xev.xmotion.y; 982 ev->data.mouse_motion.window = xev.xmotion.window; 983 ev->data.mouse_motion.screen = (host_screen ? host_screen->mynum : -1); 984 } 985 return 1; 986 987 case ButtonPress: 988 ev->type = EPHYR_EV_MOUSE_PRESS; 989 ev->key_state = xev.xkey.state; 990 /* 991 * This is a bit hacky. will break for button 5 ( defined as 0x10 ) 992 * Check KD_BUTTON defines in kdrive.h 993 */ 994 ev->data.mouse_down.button_num = 1<<(xev.xbutton.button-1); 995 return 1; 996 997 case ButtonRelease: 998 ev->type = EPHYR_EV_MOUSE_RELEASE; 999 ev->key_state = xev.xkey.state; 1000 ev->data.mouse_up.button_num = 1<<(xev.xbutton.button-1); 1001 return 1; 1002 1003 case KeyPress: 1004 { 1005 ev->type = EPHYR_EV_KEY_PRESS; 1006 ev->key_state = xev.xkey.state; 1007 ev->data.key_down.scancode = xev.xkey.keycode; 1008 return 1; 1009 } 1010 case KeyRelease: 1011 1012 if ((XKeycodeToKeysym(HostX.dpy,xev.xkey.keycode,0) == XK_Shift_L 1013 || XKeycodeToKeysym(HostX.dpy,xev.xkey.keycode,0) == XK_Shift_R) 1014 && (xev.xkey.state & ControlMask)) 1015 { 1016 struct EphyrHostScreen *host_screen = 1017 host_screen_from_window (xev.xexpose.window); 1018 1019 if (grabbed_screen != -1) 1020 { 1021 XUngrabKeyboard (HostX.dpy, CurrentTime); 1022 XUngrabPointer (HostX.dpy, CurrentTime); 1023 grabbed_screen = -1; 1024 hostx_set_win_title (host_screen->info, 1025 "(ctrl+shift grabs mouse and keyboard)"); 1026 } 1027 else 1028 { 1029 /* Attempt grab */ 1030 if (XGrabKeyboard (HostX.dpy, host_screen->win, True, 1031 GrabModeAsync, 1032 GrabModeAsync, 1033 CurrentTime) == 0) 1034 { 1035 if (XGrabPointer (HostX.dpy, host_screen->win, True, 1036 NoEventMask, 1037 GrabModeAsync, 1038 GrabModeAsync, 1039 host_screen->win, None, CurrentTime) == 0) 1040 { 1041 grabbed_screen = host_screen->mynum; 1042 hostx_set_win_title 1043 (host_screen->info, 1044 "(ctrl+shift releases mouse and keyboard)"); 1045 } 1046 else /* Failed pointer grabm ungrab keyboard */ 1047 XUngrabKeyboard (HostX.dpy, CurrentTime); 1048 } 1049 } 1050 } 1051 1052 /* Still send the release event even if above has happened 1053 * server will get confused with just an up event. 1054 * Maybe it would be better to just block shift+ctrls getting to 1055 * kdrive all togeather. 1056 */ 1057 ev->type = EPHYR_EV_KEY_RELEASE; 1058 ev->key_state = xev.xkey.state; 1059 ev->data.key_up.scancode = xev.xkey.keycode; 1060 return 1; 1061 1062 default: 1063 break; 1064 1065 } 1066 } 1067 return 0; 1068} 1069 1070void* 1071hostx_get_display(void) 1072{ 1073 return HostX.dpy ; 1074} 1075 1076int 1077hostx_get_window (int a_screen_number) 1078{ 1079 if (a_screen_number < 0 || a_screen_number >= HostX.n_screens) { 1080 EPHYR_LOG_ERROR ("bad screen number:%d\n", a_screen_number) ; 1081 return 0; 1082 } 1083 return HostX.screens[a_screen_number].win ; 1084} 1085 1086int 1087hostx_get_window_attributes (int a_window, EphyrHostWindowAttributes *a_attrs) 1088{ 1089 XWindowAttributes attrs ; 1090 1091 memset (&attrs, 0, sizeof (attrs)) ; 1092 1093 if (!XGetWindowAttributes (hostx_get_display (), 1094 a_window, 1095 &attrs)) { 1096 return FALSE ; 1097 } 1098 a_attrs->x = attrs.x ; 1099 a_attrs->y = attrs.y ; 1100 a_attrs->width = attrs.width ; 1101 a_attrs->height = attrs.height ; 1102 if (attrs.visual) 1103 a_attrs->visualid = attrs.visual->visualid ; 1104 return TRUE ; 1105} 1106 1107int 1108hostx_get_extension_info (const char *a_ext_name, 1109 int *a_major_opcode, 1110 int *a_first_event, 1111 int *a_first_error) 1112{ 1113 if (!a_ext_name || !a_major_opcode || !a_first_event || !a_first_error) 1114 return 0 ; 1115 if (!XQueryExtension (HostX.dpy, 1116 a_ext_name, 1117 a_major_opcode, 1118 a_first_event, 1119 a_first_error)) 1120 { 1121 return 0 ; 1122 } 1123 return 1 ; 1124} 1125 1126int 1127hostx_get_visuals_info (EphyrHostVisualInfo **a_visuals, 1128 int *a_num_entries) 1129{ 1130 Display *dpy=hostx_get_display () ; 1131 Bool is_ok=False ; 1132 XVisualInfo templ, *visuals=NULL; 1133 EphyrHostVisualInfo *host_visuals=NULL ; 1134 int nb_items=0, i=0; 1135 1136 EPHYR_RETURN_VAL_IF_FAIL (a_visuals && a_num_entries && dpy, 1137 False) ; 1138 EPHYR_LOG ("enter\n") ; 1139 memset (&templ, 0, sizeof (templ)) ; 1140 visuals = XGetVisualInfo (dpy, VisualNoMask, &templ, &nb_items) ; 1141 if (!visuals) { 1142 EPHYR_LOG_ERROR ("host does not advertise any visual\n") ; 1143 goto out ; 1144 } 1145 EPHYR_LOG ("host advertises %d visuals\n", nb_items) ; 1146 host_visuals = calloc (nb_items, sizeof (EphyrHostVisualInfo)) ; 1147 for (i=0; i<nb_items; i++) { 1148 host_visuals[i].visualid = visuals[i].visualid ; 1149 host_visuals[i].screen = visuals[i].screen ; 1150 host_visuals[i].depth = visuals[i].depth ; 1151 host_visuals[i].class = visuals[i].class ; 1152 host_visuals[i].red_mask = visuals[i].red_mask ; 1153 host_visuals[i].green_mask = visuals[i].green_mask ; 1154 host_visuals[i].blue_mask = visuals[i].blue_mask ; 1155 host_visuals[i].colormap_size = visuals[i].colormap_size ; 1156 host_visuals[i].bits_per_rgb = visuals[i].bits_per_rgb ; 1157 } 1158 *a_visuals = host_visuals ; 1159 *a_num_entries = nb_items; 1160 host_visuals=NULL; 1161 1162 is_ok = TRUE; 1163out: 1164 if (visuals) { 1165 XFree (visuals) ; 1166 visuals = NULL; 1167 } 1168 if (host_visuals) { 1169 free (host_visuals) ; 1170 host_visuals = NULL; 1171 } 1172 EPHYR_LOG ("leave\n") ; 1173 return is_ok ; 1174 1175} 1176 1177int 1178hostx_create_window (int a_screen_number, 1179 EphyrBox *a_geometry, 1180 int a_visual_id, 1181 int *a_host_peer /*out parameter*/) 1182{ 1183 Bool is_ok=FALSE ; 1184 Display *dpy=hostx_get_display () ; 1185 XVisualInfo *visual_info=NULL, visual_info_templ; 1186 int visual_mask=VisualIDMask ; 1187 Window win=None ; 1188 int nb_visuals=0, winmask=0; 1189 XSetWindowAttributes attrs; 1190 1191 EPHYR_RETURN_VAL_IF_FAIL (dpy && a_geometry, FALSE) ; 1192 1193 EPHYR_LOG ("enter\n") ; 1194 1195 /*get visual*/ 1196 memset (&visual_info, 0, sizeof (visual_info)) ; 1197 visual_info_templ.visualid = a_visual_id ; 1198 visual_info = XGetVisualInfo (dpy, visual_mask, 1199 &visual_info_templ, 1200 &nb_visuals) ; 1201 if (!visual_info) { 1202 EPHYR_LOG_ERROR ("argh, could not find a remote visual with id:%d\n", 1203 a_visual_id) ; 1204 goto out ; 1205 } 1206 memset (&attrs, 0, sizeof (attrs)) ; 1207 attrs.colormap = XCreateColormap (dpy, 1208 RootWindow (dpy, 1209 visual_info->screen), 1210 visual_info->visual, 1211 AllocNone) ; 1212 attrs.event_mask = ButtonPressMask 1213 |ButtonReleaseMask 1214 |PointerMotionMask 1215 |KeyPressMask 1216 |KeyReleaseMask 1217 |ExposureMask; 1218 winmask = CWColormap|CWEventMask; 1219 1220 win = XCreateWindow (dpy, hostx_get_window (a_screen_number), 1221 a_geometry->x, a_geometry->y, 1222 a_geometry->width, a_geometry->height, 0, 1223 visual_info->depth, CopyFromParent, 1224 visual_info->visual, winmask, &attrs) ; 1225 if (win == None) { 1226 EPHYR_LOG_ERROR ("failed to create peer window\n") ; 1227 goto out ; 1228 } 1229 XFlush (dpy) ; 1230 XMapWindow (dpy, win) ; 1231 *a_host_peer = win ; 1232 is_ok = TRUE ; 1233out: 1234 EPHYR_LOG ("leave\n") ; 1235 return is_ok ; 1236} 1237 1238int 1239hostx_destroy_window (int a_win) 1240{ 1241 Display *dpy=hostx_get_display () ; 1242 1243 EPHYR_RETURN_VAL_IF_FAIL (dpy, FALSE) ; 1244 XDestroyWindow (dpy, a_win) ; 1245 XFlush (dpy) ; 1246 return TRUE ; 1247} 1248 1249int 1250hostx_set_window_geometry (int a_win, EphyrBox *a_geo) 1251{ 1252 Display *dpy=hostx_get_display (); 1253 1254 EPHYR_RETURN_VAL_IF_FAIL (dpy && a_geo, FALSE) ; 1255 1256 EPHYR_LOG ("enter. x,y,w,h:(%d,%d,%d,%d)\n", 1257 a_geo->x, a_geo->y, 1258 a_geo->width, a_geo->height) ; 1259 1260 XMoveWindow (dpy, a_win, a_geo->x, a_geo->y) ; 1261 XResizeWindow (dpy, a_win, a_geo->width, a_geo->height) ; 1262 EPHYR_LOG ("leave\n") ; 1263 return TRUE; 1264} 1265 1266int 1267hostx_set_window_bounding_rectangles (int a_window, 1268 EphyrRect *a_rects, 1269 int a_num_rects) 1270{ 1271 Bool is_ok=FALSE; 1272 Display *dpy=hostx_get_display () ; 1273 int i=0 ; 1274 XRectangle *rects=NULL ; 1275 1276 EPHYR_RETURN_VAL_IF_FAIL (dpy && a_rects, FALSE) ; 1277 1278 EPHYR_LOG ("enter. num rects:%d\n", a_num_rects) ; 1279 1280 rects = calloc (a_num_rects, sizeof (XRectangle)) ; 1281 for (i=0; i<a_num_rects; i++) { 1282 rects[i].x = a_rects[i].x1 ; 1283 rects[i].y = a_rects[i].y1 ; 1284 rects[i].width = abs (a_rects[i].x2 - a_rects[i].x1); 1285 rects[i].height = abs (a_rects[i].y2 - a_rects[i].y1) ; 1286 EPHYR_LOG ("borders clipped to rect[x:%d,y:%d,w:%d,h:%d]\n", 1287 rects[i].x, rects[i].y, 1288 rects[i].width, rects[i].height) ; 1289 } 1290 /*this aways returns 1*/ 1291 XShapeCombineRectangles (dpy, a_window, ShapeBounding, 0, 0, 1292 rects, a_num_rects, ShapeSet, YXBanded) ; 1293 is_ok = TRUE ; 1294 1295 if (rects) { 1296 free (rects) ; 1297 rects = NULL ; 1298 } 1299 EPHYR_LOG ("leave\n") ; 1300 return is_ok; 1301} 1302 1303int 1304hostx_set_window_clipping_rectangles (int a_window, 1305 EphyrRect *a_rects, 1306 int a_num_rects) 1307{ 1308 Bool is_ok=FALSE; 1309 Display *dpy=hostx_get_display () ; 1310 int i=0 ; 1311 XRectangle *rects=NULL ; 1312 1313 EPHYR_RETURN_VAL_IF_FAIL (dpy && a_rects, FALSE) ; 1314 1315 EPHYR_LOG ("enter. num rects:%d\n", a_num_rects) ; 1316 1317 rects = calloc (a_num_rects, sizeof (XRectangle)) ; 1318 for (i=0; i<a_num_rects; i++) { 1319 rects[i].x = a_rects[i].x1 ; 1320 rects[i].y = a_rects[i].y1 ; 1321 rects[i].width = abs (a_rects[i].x2 - a_rects[i].x1); 1322 rects[i].height = abs (a_rects[i].y2 - a_rects[i].y1) ; 1323 EPHYR_LOG ("clipped to rect[x:%d,y:%d,w:%d,h:%d]\n", 1324 rects[i].x, rects[i].y, 1325 rects[i].width, rects[i].height) ; 1326 } 1327 /*this aways returns 1*/ 1328 XShapeCombineRectangles (dpy, a_window, ShapeClip, 0, 0, 1329 rects, a_num_rects, ShapeSet, YXBanded) ; 1330 is_ok = TRUE ; 1331 1332 if (rects) { 1333 free (rects) ; 1334 rects = NULL ; 1335 } 1336 EPHYR_LOG ("leave\n") ; 1337 return is_ok; 1338} 1339 1340int 1341hostx_has_xshape (void) 1342{ 1343 int event_base=0, error_base=0 ; 1344 Display *dpy=hostx_get_display () ; 1345 if (!XShapeQueryExtension (dpy, 1346 &event_base, 1347 &error_base)) { 1348 return FALSE ; 1349 } 1350 return TRUE; 1351} 1352 1353#ifdef XF86DRI 1354typedef struct { 1355 int is_valid ; 1356 int local_id ; 1357 int remote_id ; 1358} ResourcePair ; 1359 1360#define RESOURCE_PEERS_SIZE 1024*10 1361static ResourcePair resource_peers[RESOURCE_PEERS_SIZE] ; 1362 1363 1364int 1365hostx_allocate_resource_id_peer (int a_local_resource_id, 1366 int *a_remote_resource_id) 1367{ 1368 int i=0 ; 1369 ResourcePair *peer=NULL ; 1370 Display *dpy=hostx_get_display (); 1371 1372 /* 1373 * first make sure a resource peer 1374 * does not exist already for 1375 * a_local_resource_id 1376 */ 1377 for (i=0; i<RESOURCE_PEERS_SIZE; i++) { 1378 if (resource_peers[i].is_valid 1379 && resource_peers[i].local_id == a_local_resource_id) { 1380 peer = &resource_peers[i] ; 1381 break ; 1382 } 1383 } 1384 /* 1385 * find one free peer entry, an feed it with 1386 */ 1387 if (!peer) { 1388 for (i=0; i<RESOURCE_PEERS_SIZE; i++) { 1389 if (!resource_peers[i].is_valid) { 1390 peer = &resource_peers[i] ; 1391 break ; 1392 } 1393 } 1394 if (peer) { 1395 peer->remote_id = XAllocID (dpy); 1396 peer->local_id = a_local_resource_id ; 1397 peer->is_valid = TRUE ; 1398 } 1399 } 1400 if (peer) { 1401 *a_remote_resource_id = peer->remote_id ; 1402 return TRUE ; 1403 } 1404 return FALSE ; 1405} 1406 1407int 1408hostx_get_resource_id_peer (int a_local_resource_id, 1409 int *a_remote_resource_id) 1410{ 1411 int i=0 ; 1412 ResourcePair *peer=NULL ; 1413 for (i=0; i<RESOURCE_PEERS_SIZE; i++) { 1414 if (resource_peers[i].is_valid 1415 && resource_peers[i].local_id == a_local_resource_id) { 1416 peer = &resource_peers[i] ; 1417 break ; 1418 } 1419 } 1420 if (peer) { 1421 *a_remote_resource_id = peer->remote_id ; 1422 return TRUE ; 1423 } 1424 return FALSE ; 1425} 1426 1427int 1428hostx_has_dri (void) 1429{ 1430 int event_base=0, error_base=0 ; 1431 Display *dpy=hostx_get_display () ; 1432 1433 if (!dpy) 1434 return FALSE ; 1435 1436 if (!XF86DRIQueryExtension (dpy, 1437 &event_base, 1438 &error_base)) { 1439 return FALSE ; 1440 } 1441 return TRUE ; 1442} 1443 1444int 1445hostx_has_glx (void) 1446{ 1447 Display *dpy=hostx_get_display () ; 1448 int event_base=0, error_base=0 ; 1449 1450 if (!glXQueryExtension (dpy, &event_base, &error_base)) { 1451 return FALSE ; 1452 } 1453 return TRUE ; 1454} 1455 1456#endif /* XF86DRI */ 1457