ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/arch/unix/video.c

This is video.c in view mode; [Download] [Up]

/*
 * video.c - X11 graphics routines.
 *
 * Written by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *  Teemu Rantanen (tvr@cs.hut.fi)
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

#include "vice.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Intrinsic.h>

#include <X11/cursorfont.h>
#ifdef XPM
#include <X11/xpm.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>

#include "video.h"
#include "kbd.h"
#include "resources.h"
#include "cmdline.h"
#include "utils.h"

/* Define this for additional shared memory verbosity. */
#undef MITSHM_DEBUG

#ifdef MITSHM_DEBUG
#define DEBUG_MITSHM(x)		printf x
#else
#define DEBUG_MITSHM(x)
#endif

/* ------------------------------------------------------------------------- */

/* Flag: Do we call `XSync()' after blitting?  */
int _video_use_xsync;

/* Flag: Do we try to use the MIT shared memory extensions?  */
static int try_mitshm;

static int set_use_xsync(resource_value_t v)
{
    _video_use_xsync = (int) v;
    return 0;
}

static int set_try_mitshm(resource_value_t v)
{
    try_mitshm = (int) v;
    return 0;
}

/* Video-related resources.  */
static resource_t resources[] = {
    { "UseXSync", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &_video_use_xsync, set_use_xsync },
    { "MITSHM", RES_INTEGER, (resource_value_t) -1,
      (resource_value_t *) &try_mitshm, set_try_mitshm },
    { NULL }
};

int video_init_resources(void)
{
    return resources_register(resources);
}

/* ------------------------------------------------------------------------- */

/* Video-related command-line options.  */
static cmdline_option_t cmdline_options[] = {
    { "-xsync", SET_RESOURCE, 0, NULL, NULL,
      "XSync", (resource_value_t) 1,
      NULL, "Call `XSync()' after updating the emulation window" },
    { "+xsync", SET_RESOURCE, 0, NULL, NULL,
      "XSync", (resource_value_t) 0,
      NULL, "Do not call `XSync()' after updating the emulation window" },
    { "-mitshm", SET_RESOURCE, 0, NULL, NULL,
      "MITSHM", (resource_value_t) 1,
      NULL, "Try to use shared memory if the X server allows it (faster)" },
    { "+mitshm", SET_RESOURCE, 0, NULL, NULL,
      "MITSHM", (resource_value_t) 0,
      NULL, "Never use shared memory (slower)" },
    { "-mitshmauto", SET_RESOURCE, 0, NULL, NULL,
      "MITSHM", (resource_value_t) -1,
      NULL, "Autodetect shared memory. If it fails use +/-mitshm." },
    { NULL }
};

int video_init_cmdline_options(void)
{
    return cmdline_register_options(cmdline_options);
}

/* ------------------------------------------------------------------------- */

/* These are exported by `ui.c'.  FIXME: Ugly!  */
extern Display *display;
extern int screen;
extern Visual *visual;
extern int depth;

/* These are made public so that the other modules can use them via macros. */
GC _video_gc;
void (*_refresh_func)();
#if X_DISPLAY_DEPTH == 0
void (*_convert_func) (frame_buffer_t *p, int x, int y, int w, int h);
PIXEL  real_pixel1[256];
PIXEL2 real_pixel2[256];
PIXEL4 real_pixel4[256];
long   real_pixel[256];
BYTE   shade_table[256];
#endif

/* This is set to 1 if the Shared Memory Extensions can actually be used. */
static int use_mitshm = 0;

/* The RootWindow of our screen. */
static Window root_window;

/* ------------------------------------------------------------------------- */

#if X_DISPLAY_DEPTH == 0

/* Conversion routines between 8bit and other sizes. */

#define SRCPTR(i, x, y) \
	((i)->tmpframebuffer + (y)*(i)->tmpframebufferlinesize + (x))
#define DESTPTR(i, x, y, t) \
	((t *)((PIXEL *)(i)->x_image->data + \
	       (i)->x_image->bytes_per_line*(y)) + (x))

static void convert_8to16(frame_buffer_t *p, int sx, int sy, int w, int h)
{
    PIXEL *src;
    PIXEL2 *dst;
    int x, y;

    for (y = 0; y < h; y++) {
	src = SRCPTR(p, sx, sy + y);
	dst = DESTPTR(p, sx, sy + y, PIXEL2);
	for (x = 0; x < w; x++)
	    dst[x] = real_pixel2[src[x]];
    }
}

static void convert_8to32(frame_buffer_t *p, int sx, int sy, int w, int h)
{
    PIXEL *src;
    PIXEL4 *dst;
    int x, y;

    for (y = 0; y < h; y++) {
	src = SRCPTR(p, sx, sy + y);
	dst = DESTPTR(p, sx, sy + y, PIXEL4);
	for (x = 0; x < w; x++)
	    dst[x] = real_pixel4[src[x]];
    }
}

/* This doesn't usually happen, but if it does, this is a great speedup
   comparing the general convert_8toall() -routine. */
static void convert_8to8(frame_buffer_t *p, int sx, int sy, int w, int h)
{
    PIXEL *src;
    PIXEL *dst;
    int x, y;

    for (y = 0; y < h; y++) {
	src = SRCPTR(p, sx, sy + y);
	dst = DESTPTR(p, sx, sy + y, PIXEL);
	for (x = 0; x < w; x++)
	    dst[x] = real_pixel1[src[x]];
    }
}

/* Use dither on 1bit display. This is slow but who cares... */
static BYTE dither_table[4][4] =
{
    {  0,  8,  2, 10 },
    { 12,  4, 14,  6 },
    {  3, 11,  1,  9 },
    { 15,  7, 13,  5 }
};

static void convert_8to1_dither(frame_buffer_t *p, int sx, int sy, int w,
				int h)
{
    PIXEL *src, *dither;
    int x, y;
    for (y = 0; y < h; y++) {
	src = SRCPTR(p, sx, sy + y);
	dither = dither_table[(sy+y)%4];
	for (x = 0; x < w; x++) {
	    /* XXX: trusts that real_pixel[0, 1] == black, white */
	    XPutPixel(p->x_image,sx+x, sy+y,
		      real_pixel[shade_table[src[x]] > dither[(sx+x)%4]]);
	}
    }
}

/* And this is inefficient... */
static void convert_8toall(frame_buffer_t *p, int sx, int sy, int w, int h)
{
    PIXEL *src;
    int x, y;
    for (y = 0; y < h; y++) {
	src = SRCPTR(p, sx, sy + y);
	for (x = 0; x < w; x++)
	    XPutPixel(p->x_image, sx+x, sy+y, real_pixel[src[x]]);
    }
}

#endif


int video_init(void)
{
    XGCValues gc_values;

    root_window = RootWindow(display, screen);
    gc_values.foreground = BlackPixel(display, screen);
    gc_values.background = WhitePixel(display, screen);
    _video_gc = XCreateGC(display, root_window, GCForeground | GCBackground,
			   &gc_values);

#ifdef MITSHM

    if(try_mitshm < 0) {
	/* check wether we are on the same machine or not */
	char *p,*dname = stralloc(XDisplayName(NULL));
        struct utsname uts;

	try_mitshm = 0; /* no MIT shm */
        uname(&uts);
        if((p=strchr(dname,':'))) p[0]=0;
        if(!strlen(dname)
                || !strcmp(dname,"localhost")
                || !strcmp(dname,uts.nodename)
          ) {
	  try_mitshm = 1; /* use MIT shm */
        }
	free(dname);
    }

    if (!try_mitshm)
	use_mitshm = 0;
    else {
	int major_version, minor_version, pixmap_flag, dummy;

	/* Check whether we can use the Shared Memory Extension. */
	if (!XShmQueryVersion(display, &major_version, &minor_version,
			      &pixmap_flag)
	    || !XQueryExtension(display, "MIT-SHM", &dummy, &dummy, &dummy)) {
	    fprintf(stderr,
		    "Warning: the MITSHM extension is not supported "
		    "on this display!\n");
	    use_mitshm = 0;
	} else {
	    DEBUG_MITSHM(("MITSHM extensions version %d.%d detected.\n",
			  major_version, minor_version));
	    use_mitshm = 1;
	}
    }

#else
    use_mitshm = 0;
#endif

    return 0;
}

/* Allocate a frame buffer. */
int frame_buffer_alloc(frame_buffer_t * i, unsigned int width,
		       unsigned int height)
{
    int sizeofpixel = sizeof(PIXEL);

    if (sizeof(PIXEL2) != sizeof(PIXEL)*2 ||
	sizeof(PIXEL4) != sizeof(PIXEL)*4) {
	fprintf(stderr, "Error: PIXEL2 / PIXEL4 typedefs have wrong size.\n");
	return -1;
    }

    /* Round up to 32-bit boundary. */
    width = (width + 3) & ~0x3;

#if X_DISPLAY_DEPTH == 0
    /* sizeof(PIXEL) is not always what we are using. I guess this should
       be checked from the XImage but I'm lazy... */
    if (depth > 8)
	sizeofpixel *= 2;
    if (depth > 16)
	sizeofpixel *= 2;
#endif

#ifdef MITSHM
    if (use_mitshm) {
	DEBUG_MITSHM(("frame_buffer_alloc(): allocating XImage with MITSHM, "
		      "%d x %d pixels... ", width, height));
	i->x_image = XShmCreateImage(display, visual, depth, ZPixmap,
				     NULL, &(i->xshm_info), width, height);
	if (!i->x_image) {
	    fprintf(stderr, "Error: cannot allocate XImage with XShm.\n");
	    return -1;
	}
	DEBUG_MITSHM(("done.\nframe_buffer_alloc(): shmgetting %ld bytes... ",
		      (long) i->x_image->bytes_per_line * i->x_image->height));
	i->xshm_info.shmid = shmget(IPC_PRIVATE, i->x_image->bytes_per_line *
				    i->x_image->height, IPC_CREAT | 0700);
	if (i->xshm_info.shmid == -1) {
	    fprintf(stderr, "Error: cannot get shared memory.\n");
	    return -1;
	}
	DEBUG_MITSHM(("done, id = 0x%lx.\nframe_buffer_alloc(): getting address... ",
		      (unsigned long)i->xshm_info.shmid));
	i->xshm_info.shmaddr = i->x_image->data = shmat(i->xshm_info.shmid,
							0, 0);
	if (!i->x_image->data) {
	    fprintf(stderr, "Error: cannot get the shared memory address.\n");
	    return -1;
	}
	DEBUG_MITSHM(("0x%lx OK.\n", (unsigned long)i->xshm_info.shmaddr));
	i->xshm_info.readOnly = 0;
	if (!XShmAttach(display, &(i->xshm_info))) {
	    fprintf(stderr, "Error: cannot attach shared memory.\n");
	    return -1;
	}
	_refresh_func = (void (*)()) XShmPutImage;
    } else
#endif
    {			/* !use_mitshm */
	PIXEL *data = (PIXEL *)malloc(width * height * sizeofpixel);

	if (!data)
	    return -1;
	i->x_image = XCreateImage(display, visual, depth, ZPixmap, 0,
				  (char *)data, width, height, 32, 0);
	if (!i->x_image)
	    return -1;

	_refresh_func = (void (*)()) XPutImage;
    }

    printf("VIDEO: Successfully initialized%s shared memory.\n",
           use_mitshm ? ", using" : " without");
    if (!use_mitshm)
        printf("VIDEO: Warning: performance will be poor.\n");

#if X_DISPLAY_DEPTH == 0
    /* if display depth != 8 we need a temporary buffer */
    if (depth == 8) {
	i->tmpframebuffer = i->x_image->data;
	i->tmpframebufferlinesize = i->x_image->bytes_per_line;
	_convert_func = NULL;
    } else {
	i->tmpframebufferlinesize = width;
	i->tmpframebuffer = (PIXEL *)malloc(width * height);
	if (i->x_image->bits_per_pixel == 8)
	    _convert_func = convert_8to8;
	else if (i->x_image->bits_per_pixel == 1)
	    _convert_func = convert_8to1_dither;
	else if (i->x_image->bits_per_pixel == 16)
	    _convert_func = convert_8to16;
	else if (i->x_image->bits_per_pixel == 32)
	    _convert_func = convert_8to32;
	else
	    _convert_func = convert_8toall;
    }
#endif

    return 0;
}

/* Free an allocated frame buffer. */
void frame_buffer_free(frame_buffer_t * i)
{
    if (!i)
	return;
#if defined( MITSHM )
    if (use_mitshm) {
	XShmDetach(display, &(i->xshm_info));
	if (i->x_image)
	    XDestroyImage(i->x_image);
	if (shmdt(i->xshm_info.shmaddr))
	    fprintf(stderr, "*WARNING!* couldn't release shared memory!\n");
	shmctl(i->xshm_info.shmid, IPC_RMID, 0);
    } else
#endif
    if (i->x_image)
	XDestroyImage(i->x_image);
#if X_DISPLAY_DEPTH == 0
    /* free temporary 8bit frame buffer */
    if (depth != 8 && i->tmpframebuffer)
	free(i->tmpframebuffer);
#endif
}

void frame_buffer_clear(frame_buffer_t * f, PIXEL value)
{
#if X_DISPLAY_DEPTH == 0
    memset(f->tmpframebuffer, value,
	   f->x_image->height * f->tmpframebufferlinesize);
    if (_convert_func)
	_convert_func(f, 0, 0, f->x_image->width, f->x_image->height);
#else
    int i;

    for (i = 0; i < f->x_image->height * f->x_image->bytes_per_line;
	 i += sizeof(PIXEL))
	*((PIXEL *)(f->x_image->data + i)) = value;
#endif
}

/* ------------------------------------------------------------------------- */

/* Create a canvas.  In the X11 implementation, this is just (guess what?) a
   window. */
canvas_t canvas_create(const char *win_name, unsigned int *width,
		       unsigned int *height, int mapped,
		       canvas_redraw_t exposure_handler,
		       const palette_t *palette, PIXEL * pixel_return)
{
    canvas_t c;
    Widget w;

    w = ui_open_canvas_window(win_name, *width, *height, 1, exposure_handler,
                              palette, pixel_return);
    if (!w)
	return (canvas_t) NULL;

    c = (canvas_t) XtMalloc(sizeof(struct _canvas));
    c->emuwindow = w;
    c->drawable = ui_canvas_get_drawable(w);
    c->width = *width;
    c->height = *height;
    XtAddEventHandler(w,(EnterWindowMask | LeaveWindowMask | KeyReleaseMask
			 | KeyPressMask), True,
		      (XtEventHandler) kbd_event_handler, NULL);

    /* ...ugly... */
    XtAddEventHandler(XtParent(w), (EnterWindowMask | LeaveWindowMask
				    | KeyReleaseMask | KeyPressMask), True,
		      (XtEventHandler) kbd_event_handler, NULL);
    return c;
}

int canvas_set_palette(canvas_t c, const palette_t *palette,
                       PIXEL *pixel_return)
{
    return ui_canvas_set_palette(c->emuwindow, palette, pixel_return);
}

/* Make the canvas visible. */
void canvas_map(canvas_t s)
{
    XMapWindow(display, s->drawable);
    XFlush(display);
}

/* Make the canvas not visible. */
void canvas_unmap(canvas_t s)
{
    XUnmapWindow(display, s->drawable);
    XFlush(display);
}

/* Change the size of the canvas. */
void canvas_resize(canvas_t s, unsigned int width, unsigned int height)
{
    ui_resize_canvas_window(s->emuwindow, width, height);
}

void enable_text(void)
{
}

void disable_text(void)
{
}

int num_text_lines(void)
{
    char *s;

    s = getenv("LINES");
    if (s != NULL)
	return atoi(s);
    else
	return 24;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.