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