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