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