First import
[xorg_rtime.git] / xorg-server-1.4 / hw / kdrive / ephyr / hostx.c
blob36d3cbd465db1421d60054c71036f652ce0fb4a7
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
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.
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.
26 #include "hostx.h"
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <string.h> /* for memset */
32 #include <time.h>
34 #include <sys/ipc.h>
35 #include <sys/shm.h>
36 #include <sys/time.h>
38 #include <X11/Xlib.h>
39 #include <X11/Xutil.h>
40 #include <X11/Xatom.h>
41 #include <X11/keysym.h>
42 #include <X11/extensions/XShm.h>
44 /*
45 * All xlib calls go here, which gets built as its own .a .
46 * Mixing kdrive and xlib headers causes all sorts of types
47 * to get clobbered.
50 struct EphyrHostXVars
52 char *server_dpy_name;
53 Display *dpy;
54 int screen;
55 Visual *visual;
56 Window win, winroot;
57 Window win_pre_existing; /* Set via -parent option like xnest */
58 GC gc;
59 int depth;
60 int server_depth;
61 XImage *ximg;
62 int win_width, win_height;
63 Bool use_host_cursor;
64 Bool use_fullscreen;
65 Bool have_shm;
67 long damage_debug_msec;
69 unsigned char *fb_data; /* only used when host bpp != server bpp */
70 unsigned long cmap[256];
72 XShmSegmentInfo shminfo;
75 /* memset ( missing> ) instead of below */
76 static EphyrHostXVars HostX = { "?", 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
78 static int HostXWantDamageDebug = 0;
80 extern EphyrKeySyms ephyrKeySyms;
82 extern int monitorResolution;
84 static void
85 hostx_set_fullscreen_hint(void);
87 /* X Error traps */
89 static int trapped_error_code = 0;
90 static int (*old_error_handler) (Display *d, XErrorEvent *e);
92 #define host_depth_matches_server() (HostX.depth == HostX.server_depth)
95 static int
96 error_handler(Display *display,
97 XErrorEvent *error)
99 trapped_error_code = error->error_code;
100 return 0;
103 static void
104 hostx_errors_trap(void)
106 trapped_error_code = 0;
107 old_error_handler = XSetErrorHandler(error_handler);
110 static int
111 hostx_errors_untrap(void)
113 XSetErrorHandler(old_error_handler);
114 return trapped_error_code;
118 hostx_want_screen_size(int *width, int *height)
120 if (HostX.win_pre_existing != None
121 || HostX.use_fullscreen == True)
123 *width = HostX.win_width;
124 *height = HostX.win_height;
125 return 1;
128 return 0;
131 void
132 hostx_set_display_name(char *name)
134 HostX.server_dpy_name = strdup(name);
137 void
138 hostx_set_win_title(char *extra_text)
140 char buf[256];
142 snprintf(buf, 256, "Xephyr on %s %s",
143 HostX.server_dpy_name,
144 (extra_text != NULL) ? extra_text : "");
146 XStoreName(HostX.dpy, HostX.win, buf);
150 hostx_want_host_cursor(void)
152 return HostX.use_host_cursor;
155 void
156 hostx_use_host_cursor(void)
158 HostX.use_host_cursor = True;
162 hostx_want_preexisting_window(void)
164 if (HostX.win_pre_existing)
165 return 1;
166 else
167 return 0;
170 void
171 hostx_use_fullscreen(void)
173 HostX.use_fullscreen = True;
177 hostx_want_fullscreen(void)
179 return HostX.use_fullscreen;
182 static void
183 hostx_set_fullscreen_hint(void)
185 Atom atom_WINDOW_STATE, atom_WINDOW_STATE_FULLSCREEN;
187 atom_WINDOW_STATE
188 = XInternAtom(HostX.dpy, "_NET_WM_STATE", False);
189 atom_WINDOW_STATE_FULLSCREEN
190 = XInternAtom(HostX.dpy, "_NET_WM_STATE_FULLSCREEN",False);
192 XChangeProperty(HostX.dpy, HostX.win,
193 atom_WINDOW_STATE, XA_ATOM, 32,
194 PropModeReplace,
195 (unsigned char *)&atom_WINDOW_STATE_FULLSCREEN, 1);
198 void
199 hostx_use_preexisting_window(unsigned long win_id)
201 HostX.win_pre_existing = win_id;
204 static void
205 hostx_toggle_damage_debug(void)
207 HostXWantDamageDebug ^= 1;
210 void
211 hostx_handle_signal(int signum)
213 hostx_toggle_damage_debug();
214 EPHYR_DBG("Signal caught. Damage Debug:%i\n", HostXWantDamageDebug);
218 hostx_init(void)
220 XSetWindowAttributes attr;
221 Cursor empty_cursor;
222 Pixmap cursor_pxm;
223 XColor col;
225 attr.event_mask =
226 ButtonPressMask
227 |ButtonReleaseMask
228 |PointerMotionMask
229 |KeyPressMask
230 |KeyReleaseMask
231 |ExposureMask;
233 EPHYR_DBG("mark");
235 if ((HostX.dpy = XOpenDisplay(getenv("DISPLAY"))) == NULL)
237 fprintf(stderr, "\nXephyr cannot open host display. Is DISPLAY set?\n");
238 exit(1);
241 HostX.screen = DefaultScreen(HostX.dpy);
242 HostX.winroot = RootWindow(HostX.dpy, HostX.screen);
243 HostX.gc = XCreateGC(HostX.dpy, HostX.winroot, 0, NULL);
244 HostX.depth = DefaultDepth(HostX.dpy, HostX.screen);
245 HostX.visual = DefaultVisual(HostX.dpy, HostX.screen);
247 HostX.server_depth = HostX.depth;
249 if (HostX.win_pre_existing != None)
251 Status result;
252 XWindowAttributes prewin_attr;
254 /* Get screen size from existing window */
256 hostx_errors_trap();
258 result = XGetWindowAttributes(HostX.dpy,
259 HostX.win_pre_existing,
260 &prewin_attr);
263 if (hostx_errors_untrap() || !result)
265 fprintf(stderr, "\nXephyr -parent window' does not exist!\n");
266 exit(1);
269 HostX.win_width = prewin_attr.width;
270 HostX.win_height = prewin_attr.height;
272 HostX.win = XCreateWindow(HostX.dpy,
273 HostX.win_pre_existing,
274 0,0,HostX.win_width,HostX.win_height,
276 CopyFromParent,
277 CopyFromParent,
278 CopyFromParent,
279 CWEventMask,
280 &attr);
282 else
284 HostX.win = XCreateWindow(HostX.dpy,
285 HostX.winroot,
286 0,0,100,100, /* will resize */
288 CopyFromParent,
289 CopyFromParent,
290 CopyFromParent,
291 CWEventMask,
292 &attr);
294 hostx_set_win_title("( ctrl+shift grabs mouse and keyboard )");
296 if (HostX.use_fullscreen)
298 HostX.win_width = DisplayWidth(HostX.dpy, HostX.screen);
299 HostX.win_height = DisplayHeight(HostX.dpy, HostX.screen);
301 hostx_set_fullscreen_hint();
306 XParseColor(HostX.dpy, DefaultColormap(HostX.dpy,HostX.screen), "red", &col);
307 XAllocColor(HostX.dpy, DefaultColormap(HostX.dpy, HostX.screen), &col);
308 XSetForeground(HostX.dpy, HostX.gc, col.pixel);
310 if (!hostx_want_host_cursor())
312 /* Ditch the cursor, we provide our 'own' */
313 cursor_pxm = XCreatePixmap (HostX.dpy, HostX.winroot, 1, 1, 1);
314 memset (&col, 0, sizeof (col));
315 empty_cursor = XCreatePixmapCursor (HostX.dpy,
316 cursor_pxm, cursor_pxm,
317 &col, &col, 1, 1);
318 XDefineCursor (HostX.dpy, HostX.win, empty_cursor);
319 XFreePixmap (HostX.dpy, cursor_pxm);
322 HostX.ximg = NULL;
324 /* Try to get share memory ximages for a little bit more speed */
326 if (!XShmQueryExtension(HostX.dpy) || getenv("XEPHYR_NO_SHM"))
328 fprintf(stderr, "\nXephyr unable to use SHM XImages\n");
329 HostX.have_shm = False;
331 else
333 /* Really really check we have shm - better way ?*/
334 XShmSegmentInfo shminfo;
336 HostX.have_shm = True;
338 shminfo.shmid=shmget(IPC_PRIVATE, 1, IPC_CREAT|0777);
339 shminfo.shmaddr=shmat(shminfo.shmid,0,0);
340 shminfo.readOnly=True;
342 hostx_errors_trap();
344 XShmAttach(HostX.dpy, &shminfo);
345 XSync(HostX.dpy, False);
347 if (hostx_errors_untrap())
349 fprintf(stderr, "\nXephyr unable to use SHM XImages\n");
350 HostX.have_shm = False;
353 shmdt(shminfo.shmaddr);
354 shmctl(shminfo.shmid, IPC_RMID, 0);
357 XFlush(HostX.dpy);
359 /* Setup the pause time between paints when debugging updates */
361 HostX.damage_debug_msec = 20000; /* 1/50 th of a second */
363 if (getenv("XEPHYR_PAUSE"))
365 HostX.damage_debug_msec = strtol(getenv("XEPHYR_PAUSE"), NULL, 0);
366 EPHYR_DBG("pause is %li\n", HostX.damage_debug_msec);
369 return 1;
373 hostx_get_depth (void)
375 return HostX.depth;
379 hostx_get_server_depth (void)
381 return HostX.server_depth;
384 void
385 hostx_set_server_depth(int depth)
387 HostX.server_depth = depth;
391 hostx_get_bpp(void)
393 if (host_depth_matches_server())
394 return HostX.visual->bits_per_rgb;
395 else
396 return HostX.server_depth; /* XXX correct ? */
399 void
400 hostx_get_visual_masks (CARD32 *rmsk,
401 CARD32 *gmsk,
402 CARD32 *bmsk)
404 if (host_depth_matches_server())
406 *rmsk = HostX.visual->red_mask;
407 *gmsk = HostX.visual->green_mask;
408 *bmsk = HostX.visual->blue_mask;
410 else if (HostX.server_depth == 16)
412 /* Assume 16bpp 565 */
413 *rmsk = 0xf800;
414 *gmsk = 0x07e0;
415 *bmsk = 0x001f;
417 else
419 *rmsk = 0x0;
420 *gmsk = 0x0;
421 *bmsk = 0x0;
425 void
426 hostx_set_cmap_entry(unsigned char idx,
427 unsigned char r,
428 unsigned char g,
429 unsigned char b)
431 /* XXX Will likely break for 8 on 16, not sure if this is correct */
432 HostX.cmap[idx] = (r << 16) | (g << 8) | (b);
436 * hostx_screen_init creates the XImage that will contain the front buffer of
437 * the ephyr screen, and possibly offscreen memory.
439 * @param width width of the screen
440 * @param height height of the screen
441 * @param buffer_height height of the rectangle to be allocated.
443 * hostx_screen_init() creates an XImage, using MIT-SHM if it's available.
444 * buffer_height can be used to create a larger offscreen buffer, which is used
445 * by fakexa for storing offscreen pixmap data.
447 void*
448 hostx_screen_init (int width, int height, int buffer_height)
450 int bitmap_pad;
451 Bool shm_success = False;
452 XSizeHints *size_hints;
454 EPHYR_DBG("mark");
456 if (HostX.ximg != NULL)
458 /* Free up the image data if previously used
459 * i.ie called by server reset
462 if (HostX.have_shm)
464 XShmDetach(HostX.dpy, &HostX.shminfo);
465 XDestroyImage (HostX.ximg);
466 shmdt(HostX.shminfo.shmaddr);
467 shmctl(HostX.shminfo.shmid, IPC_RMID, 0);
469 else
471 if (HostX.ximg->data)
473 free(HostX.ximg->data);
474 HostX.ximg->data = NULL;
477 XDestroyImage(HostX.ximg);
481 if (HostX.have_shm)
483 HostX.ximg = XShmCreateImage(HostX.dpy, HostX.visual, HostX.depth,
484 ZPixmap, NULL, &HostX.shminfo,
485 width, buffer_height );
487 HostX.shminfo.shmid = shmget(IPC_PRIVATE,
488 HostX.ximg->bytes_per_line * buffer_height,
489 IPC_CREAT|0777);
490 HostX.shminfo.shmaddr = HostX.ximg->data = shmat(HostX.shminfo.shmid,
491 0, 0);
493 if (HostX.ximg->data == (char *)-1)
495 EPHYR_DBG("Can't attach SHM Segment, falling back to plain XImages");
496 HostX.have_shm = False;
497 XDestroyImage(HostX.ximg);
498 shmctl(HostX.shminfo.shmid, IPC_RMID, 0);
500 else
502 EPHYR_DBG("SHM segment attached");
503 HostX.shminfo.readOnly = False;
504 XShmAttach(HostX.dpy, &HostX.shminfo);
505 shm_success = True;
509 if (!shm_success)
511 bitmap_pad = ( HostX.depth > 16 )? 32 : (( HostX.depth > 8 )? 16 : 8 );
513 HostX.ximg = XCreateImage( HostX.dpy,
514 HostX.visual,
515 HostX.depth,
516 ZPixmap, 0, 0,
517 width,
518 buffer_height,
519 bitmap_pad,
522 HostX.ximg->data = malloc( HostX.ximg->bytes_per_line * buffer_height );
526 XResizeWindow(HostX.dpy, HostX.win, width, height);
528 /* Ask the WM to keep our size static */
529 size_hints = XAllocSizeHints();
530 size_hints->max_width = size_hints->min_width = width;
531 size_hints->max_height = size_hints->min_height = height;
532 size_hints->flags = PMinSize|PMaxSize;
533 XSetWMNormalHints(HostX.dpy, HostX.win, size_hints);
534 XFree(size_hints);
536 XMapWindow(HostX.dpy, HostX.win);
538 XSync(HostX.dpy, False);
540 HostX.win_width = width;
541 HostX.win_height = height;
543 if (host_depth_matches_server())
545 EPHYR_DBG("Host matches server");
546 return HostX.ximg->data;
548 else
550 EPHYR_DBG("server bpp %i", HostX.server_depth>>3);
551 HostX.fb_data = malloc(width*buffer_height*(HostX.server_depth>>3));
552 return HostX.fb_data;
556 void
557 hostx_paint_rect(int sx, int sy,
558 int dx, int dy,
559 int width, int height)
562 * Copy the image data updated by the shadow layer
563 * on to the window
566 if (HostXWantDamageDebug)
568 hostx_paint_debug_rect(dx, dy, width, height);
572 * If the depth of the ephyr server is less than that of the host,
573 * the kdrive fb does not point to the ximage data but to a buffer
574 * ( fb_data ), we shift the various bits from this onto the XImage
575 * so they match the host.
577 * Note, This code is pretty new ( and simple ) so may break on
578 * endian issues, 32 bpp host etc.
579 * Not sure if 8bpp case is right either.
580 * ... and it will be slower than the matching depth case.
583 if (!host_depth_matches_server())
585 int x,y,idx, bytes_per_pixel = (HostX.server_depth>>3);
586 unsigned char r,g,b;
587 unsigned long host_pixel;
589 for (y=sy; y<sy+height; y++)
590 for (x=sx; x<sx+width; x++)
592 idx = (HostX.win_width*y*bytes_per_pixel)+(x*bytes_per_pixel);
594 switch (HostX.server_depth)
596 case 16:
598 unsigned short pixel = *(unsigned short*)(HostX.fb_data+idx);
600 r = ((pixel & 0xf800) >> 8);
601 g = ((pixel & 0x07e0) >> 3);
602 b = ((pixel & 0x001f) << 3);
604 host_pixel = (r << 16) | (g << 8) | (b);
606 XPutPixel(HostX.ximg, x, y, host_pixel);
607 break;
609 case 8:
611 unsigned char pixel = *(unsigned char*)(HostX.fb_data+idx);
612 XPutPixel(HostX.ximg, x, y, HostX.cmap[pixel]);
613 break;
615 default:
616 break;
621 if (HostX.have_shm)
623 XShmPutImage(HostX.dpy, HostX.win, HostX.gc, HostX.ximg,
624 sx, sy, dx, dy, width, height, False);
626 else
628 XPutImage(HostX.dpy, HostX.win, HostX.gc, HostX.ximg,
629 sx, sy, dx, dy, width, height);
632 XSync(HostX.dpy, False);
635 void
636 hostx_paint_debug_rect(int x, int y,
637 int width, int height)
639 struct timespec tspec;
641 tspec.tv_sec = HostX.damage_debug_msec / (1000000);
642 tspec.tv_nsec = (HostX.damage_debug_msec % 1000000) * 1000;
644 EPHYR_DBG("msec: %li tv_sec %li, tv_msec %li",
645 HostX.damage_debug_msec, tspec.tv_sec, tspec.tv_nsec);
647 /* fprintf(stderr, "Xephyr updating: %i+%i %ix%i\n", x, y, width, height); */
649 XFillRectangle(HostX.dpy, HostX.win, HostX.gc, x, y, width,height);
650 XSync(HostX.dpy, False);
652 /* nanosleep seems to work better than usleep for me... */
653 nanosleep(&tspec, NULL);
656 void
657 hostx_load_keymap(void)
659 XID *keymap;
660 int host_width, min_keycode, max_keycode, width;
661 int i,j;
663 XDisplayKeycodes(HostX.dpy, &min_keycode, &max_keycode);
665 EPHYR_DBG("min: %d, max: %d", min_keycode, max_keycode);
667 keymap = XGetKeyboardMapping(HostX.dpy,
668 min_keycode,
669 max_keycode - min_keycode + 1,
670 &host_width);
672 /* Try and copy the hosts keymap into our keymap to avoid loads
673 * of messing around.
675 * kdrive cannot can have more than 4 keysyms per keycode
676 * so we only copy at most the first 4 ( xorg has 6 per keycode, XVNC 2 )
678 width = (host_width > 4) ? 4 : host_width;
680 ephyrKeySyms.map = (CARD32 *)calloc(sizeof(CARD32),
681 (max_keycode - min_keycode + 1) *
682 width);
683 if (!ephyrKeySyms.map)
684 return;
686 for (i=0; i<(max_keycode - min_keycode+1); i++)
687 for (j=0; j<width; j++)
688 ephyrKeySyms.map[(i*width)+j] = (CARD32) keymap[(i*host_width) + j];
690 EPHYR_DBG("keymap width, host:%d kdrive:%d", host_width, width);
692 ephyrKeySyms.minKeyCode = min_keycode;
693 ephyrKeySyms.maxKeyCode = max_keycode;
694 ephyrKeySyms.mapWidth = width;
696 XFree(keymap);
700 hostx_get_event(EphyrHostXEvent *ev)
702 XEvent xev;
703 static Bool grabbed;
705 if (XPending(HostX.dpy))
707 XNextEvent(HostX.dpy, &xev);
709 switch (xev.type)
711 case Expose:
712 /* Not so great event compression, but works ok */
713 while (XCheckTypedWindowEvent(HostX.dpy, xev.xexpose.window,
714 Expose, &xev));
715 hostx_paint_rect(0, 0, 0, 0, HostX.win_width, HostX.win_height);
716 return 0;
718 case MotionNotify:
719 ev->type = EPHYR_EV_MOUSE_MOTION;
720 ev->data.mouse_motion.x = xev.xmotion.x;
721 ev->data.mouse_motion.y = xev.xmotion.y;
722 return 1;
724 case ButtonPress:
725 ev->type = EPHYR_EV_MOUSE_PRESS;
726 ev->key_state = xev.xkey.state;
728 * This is a bit hacky. will break for button 5 ( defined as 0x10 )
729 * Check KD_BUTTON defines in kdrive.h
731 ev->data.mouse_down.button_num = 1<<(xev.xbutton.button-1);
732 return 1;
734 case ButtonRelease:
735 ev->type = EPHYR_EV_MOUSE_RELEASE;
736 ev->key_state = xev.xkey.state;
737 ev->data.mouse_up.button_num = 1<<(xev.xbutton.button-1);
738 return 1;
740 case KeyPress:
742 ev->type = EPHYR_EV_KEY_PRESS;
743 ev->key_state = xev.xkey.state;
744 ev->data.key_down.scancode = xev.xkey.keycode;
745 return 1;
747 case KeyRelease:
749 if ((XKeycodeToKeysym(HostX.dpy,xev.xkey.keycode,0) == XK_Shift_L
750 || XKeycodeToKeysym(HostX.dpy,xev.xkey.keycode,0) == XK_Shift_R)
751 && (xev.xkey.state & ControlMask))
753 if (grabbed)
755 XUngrabKeyboard (HostX.dpy, CurrentTime);
756 XUngrabPointer (HostX.dpy, CurrentTime);
757 grabbed = False;
758 hostx_set_win_title("( ctrl+shift grabs mouse and keyboard )");
760 else
762 /* Attempt grab */
763 if (XGrabKeyboard (HostX.dpy, HostX.win, True,
764 GrabModeAsync,
765 GrabModeAsync,
766 CurrentTime) == 0)
768 if (XGrabPointer (HostX.dpy, HostX.win, True,
769 NoEventMask,
770 GrabModeAsync,
771 GrabModeAsync,
772 HostX.win, None, CurrentTime) == 0)
774 grabbed = True;
775 hostx_set_win_title("( ctrl+shift releases mouse and keyboard )");
777 else /* Failed pointer grabm ungrab keyboard */
778 XUngrabKeyboard (HostX.dpy, CurrentTime);
783 /* Still send the release event even if above has happened
784 * server will get confused with just an up event.
785 * Maybe it would be better to just block shift+ctrls getting to
786 * kdrive all togeather.
788 ev->type = EPHYR_EV_KEY_RELEASE;
789 ev->key_state = xev.xkey.state;
790 ev->data.key_up.scancode = xev.xkey.keycode;
791 return 1;
793 default:
794 break;
798 return 0;