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.