diff options
author | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 |
---|---|---|
committer | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 |
commit | fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch) | |
tree | ce87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/gfx/drivers/xlib_driver.c | |
parent | 7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff) | |
download | scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.gz scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.bz2 scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.zip |
Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet)
svn-id: r38192
Diffstat (limited to 'engines/sci/gfx/drivers/xlib_driver.c')
-rw-r--r-- | engines/sci/gfx/drivers/xlib_driver.c | 1460 |
1 files changed, 1460 insertions, 0 deletions
diff --git a/engines/sci/gfx/drivers/xlib_driver.c b/engines/sci/gfx/drivers/xlib_driver.c new file mode 100644 index 0000000000..baa65d406b --- /dev/null +++ b/engines/sci/gfx/drivers/xlib_driver.c @@ -0,0 +1,1460 @@ +/*************************************************************************** + xlib_driver.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <creichen@gmail.com> + +***************************************************************************/ + +#include <sci_memory.h> +#include <gfx_driver.h> +#ifndef X_DISPLAY_MISSING +#include <gfx_tools.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> + +#ifdef HAVE_MITSHM +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#if defined(HAVE_X11_EXTENSIONS_XRENDER_H) +# define HAVE_RENDER +# include <X11/extensions/Xrender.h> +# if defined(HAVE_X11_XFT_XFT_H) +# include <X11/Xft/Xft.h> +# endif +#endif +#include <errno.h> +#endif + +#ifdef HAVE_XM_MWMUTIL_H +#include <Xm/MwmUtil.h> +#endif + +#define SCI_XLIB_PIXMAP_HANDLE_NORMAL 0 +#define SCI_XLIB_PIXMAP_HANDLE_GRABBED 1 + +#define SCI_XLIB_SWAP_CTRL_CAPS (1 << 0) +#define SCI_XLIB_INSERT_MODE (1 << 1) +#define SCI_XLIB_NLS (1 << 2) /* Non-US keyboard support, sacrificing shortcut keys */ + +#define X_COLOR_EXT(c) ((c << 8) | c) + +/* In C++ mode, we re-name ``class'' to ``class_''. However, this screws up +** our interface to xlib, so we're not doing it in here. +*/ +#ifdef class +# undef class +#endif + +static int flags; + +struct _xlib_state { + Display *display; + Window window; + GC gc; + XGCValues gc_values; + Colormap colormap; + Pixmap visual[3]; + gfx_pixmap_t *priority[2]; +#ifdef HAVE_MITSHM + XShmSegmentInfo *shm[4]; +#endif + int use_render; + int buckystate; + XErrorHandler old_error_handler; + Cursor mouse_cursor; + byte *pointer_data[2]; + int used_bytespp; /* bytes actually used to display stuff, rather than bytes occupied in data space */ + XVisualInfo visinfo; +#ifdef HAVE_RENDER + Picture picture; +#endif +}; + +#define VISUAL S->visinfo.visual + +#define S ((struct _xlib_state *)(drv->state)) + +#define XASS(foo) { int val = foo; if (!val) xlderror(drv, __LINE__); } +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact + +#define DEBUGB if (drv->debug_flags & GFX_DEBUG_BASIC && ((debugline = __LINE__))) xldprintf +#define DEBUGU if (drv->debug_flags & GFX_DEBUG_UPDATES && ((debugline = __LINE__))) xldprintf +#define DEBUGPXM if (drv->debug_flags & GFX_DEBUG_PIXMAPS && ((debugline = __LINE__))) xldprintf +#define DEBUGPTR if (drv->debug_flags & GFX_DEBUG_POINTER && ((debugline = __LINE__))) xldprintf +#define ERROR if ((debugline = __LINE__)) xldprintf + +#ifdef HAVE_MITSHM + +XShmSegmentInfo shminfo; +int have_shmem = 0; +int x11_error = 0; + +static int check_for_xshm(Display *display) +{ + int major, minor, ignore; + Bool pixmaps; + if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) { + if (XShmQueryVersion( display, &major, &minor, &pixmaps) == True) { + return (pixmaps == True) ? 2 : 1 ; + } else { + return 0; + } + } + return 0; +} +#endif + +#ifdef HAVE_RENDER +static int x_have_render(Display *display) +{ + int ignore, retval; + + printf("Checking for X11 RENDER extension:"); + + retval = XQueryExtension(display, "RENDER", &ignore, &ignore, &ignore); + + if (retval) + printf(" found.\n"); + else + printf(" not found.\n"); + + return retval; +} +#endif + +static int debugline = 0; + +static void +xldprintf(const char *fmt, ...) +{ + va_list argp; + fprintf(stderr,"GFX-XLIB %d:", debugline); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static void +xlderror(gfx_driver_t *drv, int line) +{ + xldprintf("Xlib Error in line %d\n", line); +} + +static unsigned long +xlib_map_color(gfx_driver_t *drv, gfx_color_t color) +{ + gfx_mode_t *mode = drv->mode; + unsigned long temp; + unsigned long retval = 0; + + if (drv->mode->palette) + return color.visual.global_index; + + temp = color.visual.r; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->red_shift) & (mode->red_mask); + temp = color.visual.g; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->green_shift) & (mode->green_mask); + temp = color.visual.b; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->blue_shift) & (mode->blue_mask); + + return retval; +} + + +static int +xlib_error_handler(Display *display, XErrorEvent *error) +{ + char errormsg[256]; +#ifdef HAVE_MITSHM + x11_error = 1; +#endif + XGetErrorText(display, error->error_code, errormsg, 255); + ERROR(" X11: %s\n", errormsg); + return 0; +} + +#define UPDATE_NLS_CAPABILITY \ + if (flags & SCI_XLIB_NLS) \ + drv->capabilities |= GFX_CAPABILITY_KEYTRANSLATE; \ + else \ + drv->capabilities &= ~GFX_CAPABILITY_KEYTRANSLATE + + + +static int +xlib_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strncmp(attribute, "swap_ctrl_caps",17) || + !strncmp(attribute, "swap_caps_ctrl",17)) { + if (string_truep(value)) + flags |= SCI_XLIB_SWAP_CTRL_CAPS; + else + flags &= ~SCI_XLIB_SWAP_CTRL_CAPS; + + + return GFX_OK; + } + + if (!strncmp(attribute, "localised_keyboard", 18) + || !strncmp(attribute, "localized_keyboard", 18)) { + if (string_truep(value)) + flags |= SCI_XLIB_NLS; + else + flags &= ~SCI_XLIB_NLS; + + UPDATE_NLS_CAPABILITY; + + return GFX_OK; + + } + + if (!strncmp(attribute, "disable_shmem", 14)) { +#ifdef HAVE_MITSHM + if (string_truep(value)) + have_shmem = -1; +#endif + return GFX_OK; + } + + ERROR("Attempt to set xlib parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + +Cursor +x_empty_cursor(Display *display, Drawable drawable) /* Generates an empty X cursor */ +{ + byte cursor_data[] = {0}; + XColor black = {0,0,0}; + Pixmap cursor_map; + + Cursor retval; + + cursor_map = XCreateBitmapFromData(display, drawable, (char *) cursor_data, 1, 1); + + retval = XCreatePixmapCursor(display, cursor_map, cursor_map, &black, &black, 0, 0); + + XFreePixmap(display, cursor_map); + + return retval; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer); + + +static int +xlib_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + XVisualInfo xvisinfo; + XSetWindowAttributes win_attr; + int default_screen; + int vistype = (bytespp == 1)? 3 /* PseudoColor */ : 4 /* TrueColor */; + int red_shift, green_shift, blue_shift, alpha_shift; + int bytespp_physical; + int depth_mod; /* Number of bits to subtract from the depth while checking */ + unsigned int alpha_mask; + int xsize, ysize; + XSizeHints *size_hints; + XClassHint *class_hint; + XImage *foo_image = NULL; + int reverse_endian = 0; +#ifdef HAVE_XM_MWMUTIL_H + PropMotifWmHints motif_hints; + Atom prop, proptype; +#endif + + int i; + + + UPDATE_NLS_CAPABILITY; + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _xlib_state)); + + flags |= SCI_XLIB_INSERT_MODE; + + S->display = XOpenDisplay(NULL); + + if (!S->display) { + ERROR("Could not open X connection!\n"); + return GFX_FATAL; + } + + default_screen = DefaultScreen(S->display); + + if (xfact == -1 && yfact == -1) { /* Detect (used INTERNALLY!) */ + xfact = 2; + if (DisplayWidth(S->display, default_screen) < 640 + || DisplayHeight(S->display, default_screen) < 400) + xfact = 1; + + yfact = xfact; + } + + xsize = xfact * 320; + ysize = yfact * 200; + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + ERROR("Internal error: Attempt to open window w/ scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + BREAKPOINT(); + } + +#ifdef HAVE_MITSHM + if (!have_shmem) { + have_shmem = check_for_xshm(S->display); + if (have_shmem) { + printf("Using the MIT-SHM extension (%d/%d)\n", have_shmem, + XShmPixmapFormat(S->display)); + } + memset(&shminfo, 0, sizeof(XShmSegmentInfo)); + } + for (i = 0; i < 4; i++) + S->shm[i] = NULL; +#endif + + depth_mod = 0; + + /* Try all bit size twiddling */ + for (depth_mod = 0; depth_mod < 8; depth_mod++) + /* Try all viable depths */ + for (; bytespp > 0; bytespp--) + /* Try all interesting visuals */ + for (vistype = 4; vistype >= 3; vistype--) + if (XMatchVisualInfo(S->display, default_screen, + (bytespp << 3) - depth_mod, vistype, &xvisinfo)) + goto found_visual; + found_visual: + + S->visinfo = xvisinfo; + + if (vistype < 3 || ((vistype == 3) && (bytespp != 1))) { + if (bytespp == 1) { + ERROR("Could not get an 8 bit Pseudocolor visual!\n"); + } else { + ERROR("Could not get a %d bit TrueColor visual!\n", bytespp << 3); + } + return GFX_FATAL; + } + + S->colormap = win_attr.colormap = + XCreateColormap(S->display, RootWindow(S->display, default_screen), + VISUAL, (bytespp == 1)? AllocAll : AllocNone); + + win_attr.event_mask = PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ExposureMask; + win_attr.background_pixel = win_attr.border_pixel = 0; + + S->window = XCreateWindow(S->display, RootWindow(S->display, default_screen), + 0, 0, xsize, ysize, 0, xvisinfo.depth, InputOutput, + VISUAL, (CWBackPixel | CWBorderPixel | CWColormap | CWEventMask), + &win_attr); + + if (!S->window) { + ERROR("Could not create window of size %dx%d!\n", 320*xfact, 200*yfact); + return GFX_FATAL; + } + + XSync(S->display, False); +#ifdef HAVE_XM_MWMUTIL_H + motif_hints.flags = MWM_HINTS_DECORATIONS|MWM_HINTS_FUNCTIONS; + motif_hints.decorations = MWM_DECOR_BORDER|MWM_DECOR_TITLE|MWM_DECOR_MENU|MWM_DECOR_MINIMIZE; + motif_hints.functions=0 +#ifdef MWM_FUNC_MOVE + | MWM_FUNC_MOVE +#endif +#ifdef MWM_FUNC_MINIMIZE + | MWM_FUNC_MINIMIZE +#endif +#ifdef MWM_FUNC_CLOSE + | MWM_FUNC_CLOSE +#endif +#ifdef MWM_FUNC_QUIT_APP + | MWM_FUNC_QUIT_APP +#endif + ; + + prop = XInternAtom(S->display, "_MOTIF_WM_HINTS", True ); + if (prop) { + proptype = prop; + XChangeProperty(S->display, S->window, prop, proptype, 32, PropModeReplace, (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS); + } +#endif + + XStoreName(S->display, S->window, "FreeSCI"); + XDefineCursor(S->display, S->window, (S->mouse_cursor = x_empty_cursor(S->display, S->window))); + + XMapWindow(S->display, S->window); + S->buckystate = 0; + + if (bytespp == 1) + red_shift = green_shift = blue_shift = 0; + else { + red_shift = 32 - ffs((~xvisinfo.red_mask >> 1) & (xvisinfo.red_mask)); + green_shift = 32 - ffs((~xvisinfo.green_mask >> 1) & (xvisinfo.green_mask)); + blue_shift = 32 - ffs((~xvisinfo.blue_mask >> 1) & (xvisinfo.blue_mask)); + } + + class_hint = XAllocClassHint(); + class_hint->res_name = "FreeSCI"; + class_hint->res_class = "FreeSCI"; + XSetIconName(S->display, S->window, "FreeSCI"); + XSetClassHint(S->display, S->window, class_hint); + XFree(class_hint); + size_hints = XAllocSizeHints(); + size_hints->base_width = size_hints->min_width = size_hints->max_width = xsize; + size_hints->base_height = size_hints->min_height = size_hints->max_height = ysize; + size_hints->flags |= PMinSize | PMaxSize | PBaseSize; + XSetWMNormalHints(S->display, S->window, size_hints); + XFree(size_hints); + + S->gc_values.foreground = BlackPixel(S->display, DefaultScreen(S->display)); + S->gc = XCreateGC(S->display, S->window, GCForeground, &(S->gc_values)); + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xsize, ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + ERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", + xsize, ysize); + return GFX_FATAL; + } + } + + foo_image = XCreateImage(S->display, VISUAL, + bytespp << 3, ZPixmap, 0, (char*)sci_malloc(23), 2, 2, 8, 0); + + bytespp_physical = foo_image->bits_per_pixel >> 3; +#ifdef WORDS_BIGENDIAN + reverse_endian = foo_image->byte_order == LSBFirst; +#else + reverse_endian = foo_image->byte_order == MSBFirst; +#endif + XDestroyImage(foo_image); + +#ifdef HAVE_MITSHM + /* set up and test the XSHM extension to make sure it's sane */ + if (have_shmem) { + XErrorHandler old_handler; + + x11_error = 0; + old_handler = XSetErrorHandler(xlib_error_handler); + + foo_image = XShmCreateImage(S->display, VISUAL, + bytespp_physical << 3, ZPixmap, 0, &shminfo, 2, 2); + if (foo_image) + shminfo.shmid = shmget(IPC_PRIVATE, + foo_image->bytes_per_line * + foo_image->height, + IPC_CREAT | 0777); + if (-1 == shminfo.shmid) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + foo_image = NULL; + } else { + + shminfo.shmaddr = (char *) shmat(shminfo.shmid, 0, 0); + if ((void *) -1 == shminfo.shmaddr) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + if (foo_image) + XDestroyImage(foo_image); + return GFX_FATAL; + } + + foo_image->data = shminfo.shmaddr; + shminfo.readOnly = False; + + XShmAttach(S->display, &shminfo); + XSync(S->display, False); + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (x11_error) { + have_shmem = 0; + ERROR("System does not support Shared XImages, disabling\n"); + shmdt(shminfo.shmaddr); + XDestroyImage(foo_image); + foo_image = NULL; + x11_error = 0; + } + XSetErrorHandler(old_handler); + } + } + +#endif + + + alpha_mask = xvisinfo.red_mask | xvisinfo.green_mask | xvisinfo.blue_mask; + if (!reverse_endian && bytespp_physical == 4 && (!(alpha_mask & 0xff000000) || !(alpha_mask & 0xff))) { + if (alpha_mask & 0xff) { + alpha_mask = 0xff000000; + alpha_shift = 0; + } else { /* Lowest byte */ + alpha_mask = 0x000000ff; + alpha_shift = 24; + } + } else { + alpha_mask = 0; + alpha_shift = 0; + } + /* create the visual buffers */ + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + XErrorHandler old_handler; + + if (have_shmem && have_shmem != 2) { + ERROR("Shared memory pixmaps not supported. Reverting\n"); + perror("reason"); + have_shmem = 0; + } + + if (have_shmem) { + old_handler = XSetErrorHandler(xlib_error_handler); + + if ((S->shm[i] = (XShmSegmentInfo*)sci_malloc(sizeof(XShmSegmentInfo))) == 0) { + ERROR("AIEEEE! Malloc failed!\n"); + return GFX_FATAL; + } + memset(S->shm[i], 0, sizeof(XShmSegmentInfo)); + + S->shm[i]->shmid = shmget(IPC_PRIVATE, xsize * ysize * + bytespp_physical, + IPC_CREAT | IPC_EXCL | 0666); + S->shm[i]->readOnly = False; + + if (S->shm[i]->shmid == -1) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + } + } + if (have_shmem) { + S->shm[i]->shmaddr = (char *) shmat(S->shm[i]->shmid, 0, 0); + if (S->shm[i]->shmaddr == (void *) -1) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + have_shmem = 0; + } + } + if (have_shmem) { + if (!XShmAttach(S->display, S->shm[i]) || x11_error) { + ERROR("ARGH! Can't attach shared memory segment\n"); + have_shmem = 0; + } + XSync(S->display, False); + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + } + + if (have_shmem && !x11_error) { + S->visual[i] = XShmCreatePixmap(S->display, S->window, + S->shm[i]->shmaddr, + S->shm[i], xsize, ysize, + bytespp << 3); + XSync(S->display, False); + + if (x11_error || !S->visual[i]) { + ERROR("Shared Memory Pixmaps not supported on this system. Disabling!\n"); + have_shmem = 0; + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display ,S->shm[i]); + XSync(S->display, False); + S->visual[i] = 0; + x11_error = 0; + shmdt(S->shm[i]->shmaddr); + sci_free(S->shm[i]); + + } + + } + XSetErrorHandler(old_handler); + + if (!have_shmem) +#endif + S->visual[i] = XCreatePixmap(S->display, S->window, xsize, ysize, bytespp << 3); + + XFillRectangle(S->display, S->visual[i], S->gc, 0, 0, xsize, ysize); + } + + /** X RENDER handling **/ +#ifdef HAVE_RENDER + S->use_render = x_have_render(S->display); + + if (S->use_render) { + XRenderPictFormat * format + = XRenderFindVisualFormat (S->display, + VISUAL); + S->picture = XRenderCreatePicture (S->display, + (Drawable) S->visual[1], + format, + 0, 0); + } else /* No Xrender */ + drv->draw_filled_rect = xlib_draw_filled_rect; +#else + S->use_render = 0; +#endif + /** End of X RENDER handling **/ + + + drv->mode = gfx_new_mode(xfact, yfact, bytespp_physical, + xvisinfo.red_mask, xvisinfo.green_mask, + xvisinfo.blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (bytespp == 1)? xvisinfo.colormap_size : 0, + (reverse_endian)? GFX_MODE_FLAG_REVERSE_ENDIAN : 0); + +#ifdef HAVE_MITSHM + if (have_shmem) { + XShmDetach(S->display, &shminfo); + XDestroyImage(foo_image); + shmdt(shminfo.shmaddr); + } +#endif + + S->used_bytespp = bytespp; + S->old_error_handler = (XErrorHandler) XSetErrorHandler(xlib_error_handler); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + + + return GFX_OK; +} + + +static void +xlib_xdpy_info() +{ + int i; + XVisualInfo foo; + XVisualInfo *visuals; + int visuals_nr; + Display *display = XOpenDisplay(NULL); + const char *vis_classes[6] = {"StaticGray", "GrayScale", "StaticColor", + "PseudoColor", "TrueColor", "DirectColor"}; + + printf("Visuals provided by X11 server:\n"); + visuals = XGetVisualInfo(display, VisualNoMask, &foo, &visuals_nr); + + if (!visuals_nr) { + printf(" None!\n"); + } + + for (i = 0; i < visuals_nr; i++) { + XVisualInfo *visual = visuals + i; + + /* This works around an incompatibility between C++ and xlib: access visual->class */ + int visual_class = *((int *) (((byte *)(&(visual->depth))) + sizeof(unsigned int))); + + printf("%d:\t%d bpp %s(%d)\n" + "\tR:%08lx G:%08lx B:%08lx\n" + "\tbits_per_rgb=%d\n" + "\tcolormap_size=%d\n\n", + i, + visual->depth, + (visual_class < 0 || visual_class >5)? + "INVALID" : + vis_classes[visual_class], + visual_class, + visual->red_mask, visual->green_mask, visual->blue_mask, + visual->bits_per_rgb, visual->colormap_size); + + } + + if (visuals) + XFree(visuals); +} + +static int +xlib_init(struct _gfx_driver *drv) +{ + int i; + + /* Try 32-bit mode last due to compiz issue with bit depth 32. */ + for (i = 3; i > 0; i--) + if (!xlib_init_specific(drv, -1, -1, i)) + return GFX_OK; + + if (!xlib_init_specific(drv, -1, -1, 4)) + return GFX_OK; + + fprintf(stderr, "Could not find supported mode!\n"); + xlib_xdpy_info(); + + return GFX_FATAL; +} + +static void +xlib_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + if (have_shmem && S->shm[i]) { + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display, S->shm[i]); + + if (S->shm[i]->shmid >=0) + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + if (S->shm[i]->shmaddr) + shmdt(S->shm[i]->shmaddr); + + sci_free(S->shm[i]); + S->shm[i] = NULL; + } else +#endif + XFreePixmap(S->display, S->visual[i]); + } + +#ifdef HAVE_RENDER + XRenderFreePicture(S->display, S->picture); +#endif + XFreeGC(S->display, S->gc); + XDestroyWindow(S->display, S->window); + XCloseDisplay(S->display); + XSetErrorHandler((XErrorHandler) (S->old_error_handler)); + sci_free(S); + drv->state /* = S */ = NULL; + gfx_free_mode(drv->mode); + } +} + + + /*** Drawing operations ***/ + +static int +xlib_draw_line(struct _gfx_driver *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + int linewidth = (line_mode == GFX_LINE_MODE_FINE)? 1: + (drv->mode->xfact + drv->mode->yfact) >> 1; + + if (color.mask & GFX_MASK_VISUAL) { + int xmod = drv->mode->xfact >> 1; + int ymod = drv->mode->yfact >> 1; + + if (line_mode == GFX_LINE_MODE_FINE) + xmod = ymod = 0; + + S->gc_values.foreground = xlib_map_color(drv, color); + S->gc_values.line_width = linewidth; + S->gc_values.line_style = (line_style == GFX_LINE_STYLE_NORMAL)? + LineSolid : LineOnOffDash; + S->gc_values.cap_style = CapProjecting; + + XChangeGC(S->display, S->gc, GCLineWidth | GCLineStyle | GCForeground | GCCapStyle, &(S->gc_values)); + + XASS(XDrawLine(S->display, S->visual[1], S->gc, + start.x + xmod, start.y + ymod, + end.x + xmod, end.y + ymod)); + } + + if (color.mask & GFX_MASK_PRIORITY) { + int xc, yc; + point_t nstart, nend; + + linewidth--; + for (xc = 0; xc <= linewidth; xc++) + for (yc = -linewidth; yc <= linewidth; yc++) { + nstart.x = start.x + xc; + nstart.y = start.y + yc; + + nend.x = end.x + xc; + nend.y = end.y + yc; + + gfx_draw_line_pixmap_i(S->priority[0], + nstart, nend, color.priority); + } + } + + return GFX_OK; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + S->gc_values.foreground = xlib_map_color(drv, color1); + XChangeGC(S->display, S->gc, GCForeground, &(S->gc_values)); + XASS(XFillRectangle(S->display, S->visual[1], S->gc, rect.x, rect.y, + rect.xl, rect.yl)); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + +#ifdef HAVE_RENDER +static int +xlib_draw_filled_rect_RENDER(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (S->used_bytespp == 1) /* No room for alpha! */ + return xlib_draw_filled_rect(drv, rect, color1, color2, shade_mode); + + if (color1.mask & GFX_MASK_VISUAL) { + XRenderColor fg; + + fg.red = X_COLOR_EXT(color1.visual.r); + fg.green = X_COLOR_EXT(color1.visual.g); + fg.blue = X_COLOR_EXT(color1.visual.b); + fg.alpha = 0xffff - X_COLOR_EXT(color1.alpha); + + + XRenderFillRectangle(S->display, + PictOpOver, + S->picture, + &fg, + rect.x, rect.y, + rect.xl, rect.yl); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} +#endif + + /*** Pixmap operations ***/ + +static int +xlib_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + if (pxm->internal.info) { + ERROR("Attempt to register pixmap twice!\n"); + return GFX_ERROR; + } + pxm->internal.info = XCreateImage(S->display, VISUAL, + S->used_bytespp << 3, ZPixmap, 0, (char *) pxm->data, pxm->xl, + pxm->yl, 8, 0); + + DEBUGPXM("Registered pixmap %d/%d/%d at %p (%dx%d)\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info, pxm->xl, pxm->yl); + return GFX_OK; +} + +static int +xlib_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + DEBUGPXM("Freeing pixmap %d/%d/%d at %p\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info); + + if (!pxm->internal.info) { + ERROR("Attempt to unregister pixmap twice!\n"); + return GFX_ERROR; + } + + XDestroyImage((XImage *) pxm->internal.info); + pxm->internal.info = NULL; + pxm->data = NULL; /* Freed by XDestroyImage */ + return GFX_OK; +} + +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + XImage *tempimg; + + if (dest.xl != src.xl || dest.yl != src.yl) { + ERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + if (pxm->internal.handle == SCI_XLIB_PIXMAP_HANDLE_GRABBED) { + XPutImage(S->display, S->visual[bufnr], S->gc, (XImage *) pxm->internal.info, + src.x, src.y, dest.x, dest.y, dest.xl, dest.yl); + return GFX_OK; + } + + tempimg = XGetImage(S->display, S->visual[bufnr], dest.x, dest.y, + dest.xl, dest.yl, 0xffffffff, ZPixmap); + + if (!tempimg) { + ERROR("Failed to grab X image!\n"); + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) tempimg->data, tempimg->bytes_per_line, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + + XPutImage(S->display, S->visual[bufnr], S->gc, tempimg, + 0, 0, dest.x, dest.y, dest.xl, dest.yl); + + XDestroyImage(tempimg); + return GFX_OK; +} + + +static int +xlib_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + + if (src.x < 0 || src.y < 0) { + ERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + ERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + switch (map) { + + case GFX_MASK_VISUAL: + pxm->xl = src.xl; + pxm->yl = src.yl; + + pxm->internal.info = XGetImage(S->display, S->visual[1], src.x, src.y, + src.xl, src.yl, 0xffffffff, ZPixmap); + pxm->internal.handle = SCI_XLIB_PIXMAP_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + sci_free(pxm->data); + pxm->data = (byte *) ((XImage *)(pxm->internal.info))->data; + break; + + case GFX_MASK_PRIORITY: + ERROR("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + ERROR("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + + /*** Buffer operations ***/ + +static int +xlib_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + + + if (src.x != dest.x || src.y != dest.y) { + DEBUGU("Updating %d (%d,%d)(%dx%d) to (%d,%d)\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y); + } else { + DEBUGU("Updating %d (%d,%d)(%dx%d)\n", buffer, src.x, src.y, src.xl, src.yl); + } + + XCopyArea(S->display, S->visual[data_source], S->visual[data_dest], S->gc, + src.x, src.y, src.xl, src.yl, dest.x, dest.y); + + if (buffer == GFX_BUFFER_BACK && (src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + else { + gfx_color_t col; + col.mask = GFX_MASK_VISUAL; + col.visual.r = 0xff; + col.visual.g = 0; + col.visual.b = 0; + + XCopyArea(S->display, S->visual[0], S->window, S->gc, + dest.x, dest.y, src.xl, src.yl, dest.x, dest.y); + } + + return GFX_OK; +} + +static int +xlib_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + + if (!pic->internal.info) { + ERROR("Attempt to set static buffer with unregisterd pixmap!\n"); + return GFX_ERROR; + } + XPutImage(S->display, S->visual[2], S->gc, (XImage *) pic->internal.info, + 0, 0, 0, 0, 320 * XFACT, 200 * YFACT); + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, 320*XFACT, 200*YFACT)); + + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + +byte * +xlib_create_cursor_data(gfx_driver_t *drv, gfx_pixmap_t *pointer, int mode) +{ + int linewidth = (pointer->xl + 7) >> 3; + int lines = pointer->yl; + int xc, yc; + int xfact = drv->mode->xfact; + byte *data = (byte*)sci_calloc(linewidth, lines); + byte *linebase = data, *pos; + byte *src = pointer->index_data; + + + for (yc = 0; yc < pointer->index_yl; yc++) { + int scalectr; + int bitc = 0; + pos = linebase; + + + for (xc = 0; xc < pointer->index_xl; xc++) { + int draw = mode? + (*src == 0) : (*src < 255); + + for (scalectr = 0; scalectr < xfact; scalectr++) { + if (draw) + *pos |= (1 << bitc); + + bitc++; + if (bitc == 8) { + bitc = 0; + pos++; + } + } + + src++; + } + for (scalectr = 1; scalectr < drv->mode->yfact; scalectr++) + memcpy(linebase + linewidth * scalectr, linebase, linewidth); + + linebase += linewidth * drv->mode->yfact; + } + + return data; +} + +static int +xlib_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + int i; + XFreeCursor(S->display, S->mouse_cursor); + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + sci_free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + if (pointer == NULL) + S->mouse_cursor = x_empty_cursor(S->display, S->window); + else { + XColor cols[2]; + Pixmap visual, mask; + byte *mask_data, *visual_data; + int real_xl = ((pointer->xl + 7) >> 3) << 3; + int i; + + for (i = 0; i < 2; i++) { + cols[i].red = pointer->colors[i].r; + cols[i].red |= (cols[i].red << 8); + cols[i].green = pointer->colors[i].g; + cols[i].green |= (cols[i].green << 8); + cols[i].blue = pointer->colors[i].b; + cols[i].blue |= (cols[i].blue << 8); + } + + S->pointer_data[0] = visual_data = xlib_create_cursor_data(drv, pointer, 1); + S->pointer_data[1] = mask_data = xlib_create_cursor_data(drv, pointer, 0); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + visual = XCreateBitmapFromData(S->display, S->window, (char *) visual_data, real_xl, pointer->yl); + mask = XCreateBitmapFromData(S->display, S->window, (char *) mask_data, real_xl, pointer->yl); + + + S->mouse_cursor = + XCreatePixmapCursor(S->display, visual, mask, + &(cols[0]), &(cols[1]), + pointer->xoffset, pointer->yoffset); + + XFreePixmap(S->display, visual); + XFreePixmap(S->display, mask); + sci_free(mask_data); + sci_free(visual_data); + } + + XDefineCursor(S->display, S->window, S->mouse_cursor); + + return 0; +} + + + /*** Palette operations ***/ + +static int +xlib_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + char stringbuf[8]; + sprintf(stringbuf, "#%02x%02x%02x", red, green, blue); /* Argh. */ + + XStoreNamedColor(S->display, S->colormap, stringbuf, index, DoRed | DoGreen | DoBlue); + /* Isn't there some way to do this without strings? */ + + return GFX_OK; +} + + + /*** Event management ***/ +/* +int +x_unmap_key(gfx_driver_t *drv, int keycode) +{ + KeySym xkey = XKeycodeToKeysym(S->display, keycode, 0); + + return 0; +} +*/ +int +x_map_key(gfx_driver_t *drv, XEvent *key_event, char *character) +{ + KeySym xkey = XKeycodeToKeysym(S->display, key_event->xkey.keycode, 0); + + *character = 0; + + if (flags & SCI_XLIB_SWAP_CTRL_CAPS) { + switch (xkey) { + case XK_Control_L: xkey = XK_Caps_Lock; break; + case XK_Caps_Lock: xkey = XK_Control_L; break; + } + } + + switch(xkey) { + + case XK_BackSpace: return SCI_K_BACKSPACE; + case XK_Tab: return 9; + case XK_Escape: return SCI_K_ESC; + case XK_Return: + case XK_KP_Enter: return SCI_K_ENTER; + + case XK_KP_Decimal: + case XK_KP_Delete: return SCI_K_DELETE; + case XK_KP_0: + case XK_KP_Insert: return SCI_K_INSERT; + case XK_End: + case XK_KP_End: + case XK_KP_1: return SCI_K_END; + case XK_Down: + case XK_KP_Down: + case XK_KP_2: return SCI_K_DOWN; + case XK_Page_Down: + case XK_KP_Page_Down: + case XK_KP_3: return SCI_K_PGDOWN; + case XK_Left: + case XK_KP_Left: + case XK_KP_4: return SCI_K_LEFT; + case XK_KP_Begin: + case XK_KP_5: return SCI_K_CENTER; + case XK_Right: + case XK_KP_Right: + case XK_KP_6: return SCI_K_RIGHT; + case XK_Home: + case XK_KP_Home: + case XK_KP_7: return SCI_K_HOME; + case XK_Up: + case XK_KP_Up: + case XK_KP_8: return SCI_K_UP; + case XK_Page_Up: + case XK_KP_Page_Up: + case XK_KP_9: return SCI_K_PGUP; + + case XK_F1: return SCI_K_F1; + case XK_F2: return SCI_K_F2; + case XK_F3: return SCI_K_F3; + case XK_F4: return SCI_K_F4; + case XK_F5: return SCI_K_F5; + case XK_F6: return SCI_K_F6; + case XK_F7: return SCI_K_F7; + case XK_F8: return SCI_K_F8; + case XK_F9: return SCI_K_F9; + case XK_F10: return SCI_K_F10; + + + case XK_Control_L: + case XK_Control_R:/* S->buckystate |= SCI_EVM_CTRL; return 0; */ + case XK_Alt_L: + case XK_Alt_R:/* S->buckystate |= SCI_EVM_ALT; return 0; */ + case XK_Caps_Lock: + case XK_Shift_Lock:/* S->buckystate ^= SCI_EVM_CAPSLOCK; return 0; */ + case XK_Scroll_Lock:/* S->buckystate ^= SCI_EVM_SCRLOCK; return 0; */ + case XK_Num_Lock:/* S->buckystate ^= SCI_EVM_NUMLOCK; return 0; */ + case XK_Shift_L:/* S->buckystate |= SCI_EVM_LSHIFT; return 0; */ + case XK_Shift_R:/* S->buckystate |= SCI_EVM_RSHIFT; return 0; */ + return 0; + default: + break; + } + + + if (flags & SCI_XLIB_NLS) { + /* Localised key lookup */ + XLookupString(&(key_event->xkey), character, 1, &xkey, NULL); + } + + if ((xkey >= ' ') && (xkey <= '~')) + return xkey; /* All printable ASCII characters */ + + switch (xkey) { + case XK_KP_Add: return '+'; + case XK_KP_Divide: return '/'; + case XK_KP_Subtract: return '-'; + case XK_KP_Multiply: return '*'; + } + + if (*character) + return xkey; /* Should suffice for all practical purposes */ + + sciprintf("Unknown X keysym: %04x\n", xkey); + return 0; +} + + +void +x_get_event(gfx_driver_t *drv, int eventmask, long wait_usec, sci_event_t *sci_event) +{ + int x_button_xlate[] = {0, 1, 3, 2, 4, 5}; + XEvent event; + Window window = S->window; + Display *display = S->display; + struct timeval ctime, timeout_time, sleep_time; + long usecs_to_sleep; + + eventmask |= ExposureMask; /* Always check for this */ + + gettimeofday(&timeout_time, NULL); + timeout_time.tv_usec += wait_usec; + + /* Calculate wait time */ + timeout_time.tv_sec += (timeout_time.tv_usec / 1000000); + timeout_time.tv_usec %= 1000000; + + do { + int hasnext_event = 1; + + while (hasnext_event) { + if (sci_event) { /* Capable of handling any event? */ + hasnext_event = XPending(display); + if (hasnext_event) + XNextEvent(display, &event); + } else + hasnext_event = XCheckWindowEvent(display, window, eventmask, &event); + + + if (hasnext_event) + switch (event.type) { + + case ReparentNotify: + case ConfigureNotify: + case MapNotify: + case UnmapNotify: + break; + + case KeyPress: { + int modifiers = event.xkey.state; + char ch = 0; + sci_event->type = SCI_EVT_KEYBOARD; + + S->buckystate = ((flags & SCI_XLIB_INSERT_MODE)? SCI_EVM_INSERT : 0) + | (((modifiers & LockMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & ControlMask)? SCI_EVM_CTRL : 0) + | ((modifiers & (Mod1Mask | Mod4Mask))? SCI_EVM_ALT : 0) + | ((modifiers & Mod2Mask)? SCI_EVM_NUMLOCK : 0) + | ((modifiers & Mod5Mask)? SCI_EVM_SCRLOCK : 0)) + ^ ((modifiers & ShiftMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + + sci_event->buckybits = S->buckystate; + sci_event->data = + x_map_key(drv, &event, &ch); + + if (ch) + sci_event->character = ch; + else + sci_event->character = sci_event->data; + + if (sci_event->data == SCI_K_INSERT) + flags ^= SCI_XLIB_INSERT_MODE; + + if (sci_event->data) + return; + + break; + } + + case KeyRelease: + /*x_unmap_key(drv, event.xkey.keycode);*/ + break; + + case ButtonPress: { + sci_event->type = SCI_EVT_MOUSE_PRESS; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case ButtonRelease: { + sci_event->type = SCI_EVT_MOUSE_RELEASE; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case MotionNotify: { + + drv->pointer_x = event.xmotion.x; + drv->pointer_y = event.xmotion.y; + if (!sci_event) + /* Wake up from sleep */ + return; + } + break; + + case GraphicsExpose: + case Expose: { + XCopyArea(S->display, S->visual[0], S->window, S->gc, + event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, + event.xexpose.x, event.xexpose.y); + } + break; + + case NoExpose: + break; + + default: + ERROR("Received unhandled X event %04x\n", event.type); + } + } + + gettimeofday(&ctime, NULL); + + usecs_to_sleep = (timeout_time.tv_sec > ctime.tv_sec)? 1000000 : 0; + usecs_to_sleep += timeout_time.tv_usec - ctime.tv_usec; + if (ctime.tv_sec > timeout_time.tv_sec) usecs_to_sleep = -1; + + + if (usecs_to_sleep > 0) { + + if (usecs_to_sleep > 10000) + usecs_to_sleep = 10000; /* Sleep for a maximum of 10 ms */ + + sleep_time.tv_usec = usecs_to_sleep; + sleep_time.tv_sec = 0; + + select(0, NULL, NULL, NULL, &sleep_time); /* Sleep. */ + } + + } while (usecs_to_sleep >= 0); + + if (sci_event) + sci_event->type = SCI_EVT_NONE; /* No event. */ +} + + +static sci_event_t +xlib_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + x_get_event(drv, PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask, + 0, &input); + + return input; +} + + +static int +xlib_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + x_get_event(drv, PointerMotionMask | StructureNotifyMask, usecs, NULL); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_xlib = { + "xlib", + "0.6a", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_STIPPLED_LINES | GFX_CAPABILITY_MOUSE_SUPPORT + | GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_PIXMAP_REGISTRY + | GFX_CAPABILITY_FINE_LINES | GFX_CAPABILITY_WINDOWED + | GFX_CAPABILITY_KEYTRANSLATE, + 0/*GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC*/, + xlib_set_parameter, + xlib_init_specific, + xlib_init, + xlib_exit, + xlib_draw_line, +#ifdef HAVE_RENDER + xlib_draw_filled_rect_RENDER, +#else + xlib_draw_filled_rect, +#endif + xlib_register_pixmap, + xlib_unregister_pixmap, + xlib_draw_pixmap, + xlib_grab_pixmap, + xlib_update, + xlib_set_static_buffer, + xlib_set_pointer, + xlib_set_palette, + xlib_get_event, + xlib_usec_sleep, + NULL +}; + +#endif /* X_DISPLAY_MISSING */ |