gnuboy-for-dfi/sys/x11/xlib.c

527 lines
9.6 KiB
C

/*
* xlib.c
*
* Xlib interface.
* dist under gnu gpl
*/
#include <ctype.h>
#include "../../sys.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#if defined(HAVE_LIBXEXT) && defined(HAVE_X11_EXTENSIONS_XSHM_H) \
&& defined(HAVE_SYS_IPC_H) && defined(HAVE_SYS_SHM_H)
#define USE_XSHM
#endif
#else
#define USE_XSHM /* assume we have shm if no config.h - is this ok? */
#endif
#ifdef USE_XSHM
/* make sure ipc.h and shm.h will work! */
#define _SVID_SOURCE
#define _XOPEN_SOURCE
#endif
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#ifdef USE_XSHM
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include "fb.h"
#include "input.h"
#include "rc.h"
struct fb fb;
static int vmode[3] = { 0, 0, 0 };
static int x_shmsync = 1;
rcvar_t vid_exports[] =
{
RCV_VECTOR("vmode", &vmode, 3),
RCV_BOOL("x_shmsync", &x_shmsync),
RCV_END
};
static int initok;
/* Loads of bogus Xlib crap...bleh */
static char *x_displayname;
static Display *x_display;
static int x_screen;
static struct
{
int bits;
int vc;
int bytes;
} x_vissup[] =
{
{ 8, PseudoColor, 1 },
{ 15, TrueColor, 2 },
{ 16, TrueColor, 2 },
{ 32, TrueColor, 4 },
{ 24, TrueColor, 3 },
{ 0, 0, 0 }
};
static int x_bits, x_bytes;
static Visual *x_vis;
static XVisualInfo x_visinfo;
static int x_pseudo;
static Colormap x_cmap;
static XColor x_ctable[256];
static int x_wattrmask;
static XSetWindowAttributes x_wattr;
static int x_gcvalmask;
static XGCValues x_gcval;
static Window x_win;
static int x_win_x, x_win_y;
static int x_width, x_height;
static GC x_gc;
static XSizeHints x_size;
static XWMHints x_wmhints;
/*static XClassHint x_class;*/
#ifdef USE_XSHM
static XShmSegmentInfo x_shm;
#endif
static int x_useshm;
static int x_shmevent;
static int x_shmdone;
static XImage *x_image;
static int x_byteswap;
static XEvent x_ev;
static void freescreen()
{
if (!initok || !x_image) return;
if ((char *)fb.ptr != (char *)x_image->data)
free(fb.ptr);
#ifdef USE_XSHM
if (x_useshm)
{
/* FIXME - is this the right way to free shared mem? */
XSync(x_display, False);
if (!XShmDetach(x_display, &x_shm))
die ("XShmDetach failed\n");
XSync(x_display, False);
shmdt(x_shm.shmaddr);
shmctl(x_shm.shmid, IPC_RMID, 0);
x_image->data = NULL;
}
#endif
free(x_image);
x_image = NULL;
fb.ptr = NULL;
}
static void allocscreen()
{
if (initok) freescreen();
#ifdef USE_XSHM
if (x_useshm)
{
x_image = XShmCreateImage(
x_display, x_vis, x_bits, ZPixmap, 0,
&x_shm, x_width, x_height);
if (x_image)
{
x_shm.shmid = shmget(
IPC_PRIVATE,
x_image->bytes_per_line * x_image->height,
IPC_CREAT | 0777);
if (x_shm.shmid < 0)
die("shmget failed\n");
x_image->data = x_shm.shmaddr =
shmat(x_shm.shmid, 0, 0);
if (!x_image->data)
die("shmat failed\n");
if (!XShmAttach(x_display, &x_shm))
die("XShmAttach failed\n");
XSync(x_display, False);
x_shmdone = 1;
fb.pitch = x_image->bytes_per_line;
}
else
{
x_useshm = 0;
}
}
#endif
if (!x_useshm)
{
x_image = XCreateImage(
x_display, x_vis, x_bits, ZPixmap, 0,
malloc(x_width*x_height*x_bytes),
x_width, x_height, x_bits, x_width*x_bytes);
if (!x_image)
die("XCreateImage failed\n");
}
x_byteswap = x_image->byte_order ==
#ifdef IS_LITTLE_ENDIAN
MSBFirst
#else
LSBFirst
#endif
;
if (x_byteswap && x_bytes > 1)
fb.ptr = malloc(x_image->bytes_per_line * x_image->height);
else
fb.ptr = (byte *)x_image->data;
}
void vid_resize()
{
freescreen();
x_width = fb.w;
x_height = fb.h;
XResizeWindow(x_display, x_win, x_width, x_height);
x_size.flags = PSize | PMinSize | PMaxSize;
x_size.min_width = x_size.max_width = x_size.base_width = x_width;
x_size.min_height = x_size.max_height = x_size.base_height = x_height;
XSetWMNormalHints(x_display, x_win, &x_size);
XSync(x_display, False);
allocscreen();
}
static void colorshifts()
{
int i;
int mask[3];
int l, c;
mask[0] = x_vis->red_mask;
mask[1] = x_vis->green_mask;
mask[2] = x_vis->blue_mask;
for (i = 0; i < 3; i++)
{
for (l = 0; l < 32 && !((1<<l) & mask[i]); l++);
for (c = 0; l+c < 32 && ((1<<(l+c)) & mask[i]); c++);
fb.cc[i].l = l;
fb.cc[i].r = 8-c;
}
}
void vid_preinit()
{
/* do nothing; only needed on systems where we must drop perms */
}
void vid_init()
{
int i;
if (initok) return;
x_displayname = getenv("DISPLAY");
if (!x_displayname)
die("DISPLAY environment variable not set\n");
x_display = XOpenDisplay(NULL);
if (!x_display)
die("failed to connect to X display\n");
x_screen = DefaultScreen(x_display);
for (i = 0; x_vissup[i].bits; i++)
{
if (XMatchVisualInfo(
x_display, x_screen,
x_vissup[i].bits, x_vissup[i].vc, &x_visinfo))
{
if (x_vissup[i].vc == PseudoColor)
x_pseudo = 1;
else
x_pseudo = 0;
x_bits = x_vissup[i].bits;
x_bytes = x_vissup[i].bytes;
break;
}
}
if (!x_bits) die("no suitable X visuals\n");
x_vis = x_visinfo.visual;
if (!x_vis) die("X visual is NULL");
if (x_pseudo)
{
x_cmap = XCreateColormap(
x_display, RootWindow(x_display, x_screen),
x_vis, AllocAll);
for (i = 0; i < 256; i++)
{
x_ctable[i].pixel = i;
x_ctable[i].flags = DoRed|DoGreen|DoBlue;
}
}
x_wattrmask = CWEventMask | CWBorderPixel | CWColormap;
x_wattr.event_mask = KeyPressMask | KeyReleaseMask | ExposureMask
| FocusChangeMask;
x_wattr.border_pixel = 0;
x_wattr.colormap = x_cmap;
x_gcvalmask = GCGraphicsExposures;
x_gcval.graphics_exposures = False;
if (!vmode[0] || !vmode[1])
{
int scale = rc_getint("scale");
if (scale < 1) scale = 1;
vmode[0] = 160 * scale;
vmode[1] = 144 * scale;
}
fb.w = vmode[0];
fb.h = vmode[1];
fb.pelsize = x_bytes == 3 ? 4 : x_bytes;
fb.pitch = fb.w * fb.pelsize;
fb.indexed = x_pseudo;
fb.enabled = 1;
fb.dirty = 0;
x_win_x = x_win_y = 0;
x_width = fb.w;
x_height = fb.h;
x_win = XCreateWindow(
x_display, RootWindow(x_display, x_screen),
x_win_x, x_win_y, x_width, x_height, 0, x_bits,
InputOutput, x_vis, x_wattrmask, &x_wattr);
if (!x_win) die("could not create X window\n");
x_gc = XCreateGC(x_display, x_win, x_gcvalmask, &x_gcval);
x_size.flags = PSize | PMinSize | PMaxSize;
x_size.min_width = x_size.max_width = x_size.base_width = x_width;
x_size.min_height = x_size.max_height = x_size.base_height = x_height;
XSetWMNormalHints(x_display, x_win, &x_size);
x_wmhints.initial_state = NormalState;
x_wmhints.input = True;
x_wmhints.flags = StateHint | InputHint;
XSetWMHints(x_display, x_win, &x_wmhints);
/* FIXME - set X class info stuff (with XSetClassHint)... */
XMapWindow(x_display, x_win);
for(;;)
{
XNextEvent(x_display, &x_ev);
if (x_ev.type == Expose && !x_ev.xexpose.count)
break;
}
XSetInputFocus(x_display, x_win, RevertToPointerRoot, CurrentTime);
#ifdef USE_XSHM
if (XShmQueryExtension(x_display) && x_displayname[0] == ':')
{
x_useshm = 1;
x_shmevent = XShmGetEventBase(x_display) + ShmCompletion;
}
#endif
colorshifts();
allocscreen();
joy_init();
initok = 1;
}
void vid_close()
{
joy_close();
if (!initok) return;
freescreen();
XFreeGC(x_display, x_gc);
XDestroyWindow(x_display, x_win);
XCloseDisplay(x_display);
initok = 0;
}
/* keymap - mappings of the form { keysym, localcode } - from x11/keymap.c */
extern int keymap[][2];
static int mapxkeycode(int xkeycode)
{
int i;
int sym;
int code;
sym = XKeycodeToKeysym(x_display, xkeycode, 0);
for (i = 0; keymap[i][0]; i++)
if (keymap[i][0] == sym)
return keymap[i][1];
if (sym < XK_space || sym > XK_asciitilde)
return 0;
code = sym - XK_space + ' ';
if (code >= 'A' && code <= 'Z')
code = tolower(code);;
return code;
}
void vid_end();
static int nextevent(int sync)
{
event_t ev;
if (!sync && !XPending(x_display))
return 0;
XNextEvent(x_display, &x_ev);
switch(x_ev.type)
{
case KeyPress:
ev.type = EV_PRESS;
ev.code = mapxkeycode(x_ev.xkey.keycode);
break;
case KeyRelease:
ev.type = EV_RELEASE;
ev.code = mapxkeycode(x_ev.xkey.keycode);
break;
case Expose:
vid_end();
return 1;
break;
default:
if (x_ev.type == x_shmevent) x_shmdone = 1;
return 1;
break;
}
return ev_postevent(&ev); /* returns 0 if queue is full */
}
void ev_poll()
{
while (nextevent(0));
joy_poll();
}
void vid_settitle(char *title)
{
XStoreName(x_display, x_win, title);
XSetIconName(x_display, x_win, title);
}
void vid_setpal(int i, int r, int g, int b)
{
if (!initok) return;
if (x_pseudo == 1)
{
x_ctable[i].red = r << 8;
x_ctable[i].green = g << 8;
x_ctable[i].blue = b << 8;
XStoreColors(x_display, x_cmap, x_ctable, 256);
}
}
void vid_begin()
{
if (!x_useshm) return;
/* XSync(x_display, False); */
while (!x_shmdone && x_shmsync)
nextevent(1);
}
static void endianswap()
{
int cnt;
un16 t16;
un32 t32;
un16 *src16 = (void *)fb.ptr;
un16 *dst16 = (void *)x_image->data;
un32 *src32 = (void *)fb.ptr;
un32 *dst32 = (void *)x_image->data;
switch (x_bytes)
{
case 2:
cnt = (x_image->bytes_per_line * x_image->height)>>1;
while (cnt--)
{
t16 = *(src16++);
*(dst16++) = (t16 << 8) | (t16 >> 8);
}
break;
case 4:
cnt = (x_image->bytes_per_line * x_image->height)>>2;
while (cnt--)
{
t32 = *(src32++);
*(dst32++) = (t32 << 24) | ((t32 << 8) & 0x00FF0000) |
((t32 >> 8) & 0x0000FF00) | (t32 >> 24);
}
break;
}
}
void vid_end()
{
if (!initok) return;
if (x_byteswap) endianswap();
if (x_useshm)
{
if (!x_shmdone) return;
#ifdef USE_XSHM
if (!XShmPutImage(
x_display, x_win, x_gc, x_image,
0, 0, 0, 0, x_width, x_height, True))
die("XShmPutImage failed\n");
#endif
x_shmdone = 0;
}
else
{
XPutImage(x_display, x_win, x_gc, x_image,
0, 0, 0, 0, x_width, x_height);
}
}