simh-testsetgenerator/display/x11.c
Mark Pizzolato b804964514 PDP11, PDP1, TX-0: Added SDL based graphics support using sim_video.
Both VT11 and VS60 properly autoconfigure on the PDP11.
PDP11 now runs Lunar Lander on all SDL supported platforms.
Reworked refresh logic to not require internal delays in the display library
2016-01-29 10:16:30 -08:00

494 lines
13 KiB
C

/*
* $Id: x11.c,v 1.32 2005/01/14 18:58:03 phil Exp $
* X11 support for XY display simulator
* Phil Budne <phil@ultimate.com>
* September 2003
*
* Changes from Douglas A. Gwyn, Jan 8, 2004
*
* started from PDP-8/E simulator (vc8e.c & kc8e.c);
* This PDP8 Emulator was written by Douglas W. Jones at the
* University of Iowa. It is distributed as freeware, of
* uncertain function and uncertain utility.
*/
/*
* Copyright (c) 2003-2004, Philip L. Budne
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the names of the authors shall
* not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization
* from the authors.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ws.h"
#include "xy.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Core.h>
#include <X11/Shell.h>
#include <X11/cursorfont.h>
#include <sys/types.h>
#include <sys/time.h>
#ifndef PIX_SIZE
#define PIX_SIZE 1
#endif
/*
* light pen location
* see ws.h for full description
*/
int ws_lp_x = -1;
int ws_lp_y = -1;
static XtAppContext app_context; /* the topmost context for everything */
static Display* dpy; /* its display */
static int scr; /* its screen */
static Colormap cmap; /* its colormap */
static Widget crtshell; /* the X window shell */
static Widget crt; /* the X window in which output will plot */
static int xpixels, ypixels;
#ifdef FULL_SCREEN
/* occupy entire screen for vintage computer fan Sellam Ismail */
static int xoffset, yoffset;
#endif
static GC whiteGC; /* gc with white foreground */
static GC blackGC; /* gc with black foreground */
static int buttons = 0; /* tracks state of all buttons */
static int os_pollfd(int, int); /* forward */
/* here on any mouse button down, AND movement when any button down */
static void
handle_button_press(w, d, e, b)
Widget w;
XtPointer d;
XEvent *e;
Boolean *b;
{
int x, y;
x = e->xbutton.x;
y = e->xbutton.y;
#ifdef FULL_SCREEN
/* untested! */
x -= xoffset;
y -= yoffset;
#endif
#if PIX_SIZE > 1
x *= PIX_SIZE;
y *= PIX_SIZE;
#endif
if (!display_tablet)
/* crosshair cursor to indicate tip of active pen */
XDefineCursor(dpy, XtWindow(crt),
(Cursor) XCreateFontCursor(dpy, XC_crosshair));
y = ypixels - y - 1;
/*printf("lightpen at %d,%d\n", x, y); fflush(stdout);*/
ws_lp_x = x;
ws_lp_y = y;
if (e->type == ButtonPress) {
buttons |= e->xbutton.button;
if (e->xbutton.button == 1) {
display_lp_sw = 1;
/*printf("tip switch activated\n"); fflush(stdout);*/
}
}
if (b)
*b = TRUE;
}
static void
handle_button_release(w, d, e, b)
Widget w;
XtPointer d;
XEvent *e;
Boolean *b;
{
if ((buttons &= ~e->xbutton.button) == 0) { /* all buttons released */
if (!display_tablet)
/* pencil cursor (close to a pen!) to indicate inactive pen posn */
XDefineCursor(dpy, XtWindow(crt),
(Cursor) XCreateFontCursor(dpy, XC_pencil));
/* XXX change cursor back?? */
ws_lp_x = ws_lp_y = -1;
}
if (e->xbutton.button == 1) {
display_lp_sw = 0;
/*printf("tip switch deactivated\n"); fflush(stdout);*/
}
if (b)
*b = TRUE;
}
static void
handle_key_press(w, d, e, b)
Widget w;
XtPointer d;
XEvent *e;
Boolean *b;
{
int shift = (ShiftMask & e->xkey.state) != 0;
KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift );
/*printf("key %d down\n", key); fflush(stdout);*/
if ((key & 0xff00) == 0)
display_keydown(key);
if (b)
*b = TRUE;
}
static void
handle_key_release(w, d, e, b)
Widget w;
XtPointer d;
XEvent *e;
Boolean *b;
{
int shift = (ShiftMask & e->xkey.state) != 0;
KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift );
/*printf("key %d up\n", key); fflush(stdout);*/
if ((key & 0xff00) == 0)
display_keyup(key);
if (b)
*b = TRUE;
}
static void
handle_exposure(w, d, e, b)
Widget w;
XtPointer d;
XEvent *e;
Boolean *b;
{
display_repaint();
if (b)
*b = TRUE;
}
int
ws_init(char *crtname, /* crt type name */
int xp, int yp, /* screen size in pixels */
int colors, /* colors to support (not used) */
void *dptr)
{
Arg arg[25];
XGCValues gcvalues;
unsigned int n;
int argc;
char *argv[1];
int height, width;
xpixels = xp; /* save screen size */
ypixels = yp;
XtToolkitInitialize();
app_context = XtCreateApplicationContext();
argc = 0;
argv[0] = NULL;
dpy = XtOpenDisplay( app_context, NULL, NULL, crtname, NULL, 0,
&argc, argv);
scr = DefaultScreen(dpy);
crtshell = XtAppCreateShell( crtname, /* app name */
crtname, /* app class */
applicationShellWidgetClass, /* wclass */
dpy, /* display */
NULL, /* arglist */
0); /* nargs */
cmap = DefaultColormap(dpy, scr);
/*
* Create a drawing area
*/
n = 0;
#ifdef FULL_SCREEN
/* center raster in full-screen black window */
width = DisplayWidth(dpy,scr);
height = DisplayHeight(dpy,scr);
xoffset = (width - xpixels*PIX_SIZE)/2;
yoffset = (height - ypixels*PIX_SIZE)/2;
#else
width = xpixels*PIX_SIZE;
height = ypixels*PIX_SIZE;
#endif
XtSetArg(arg[n], XtNwidth, width); n++;
XtSetArg(arg[n], XtNheight, height); n++;
XtSetArg(arg[n], XtNbackground, BlackPixel( dpy, scr )); n++;
crt = XtCreateWidget( crtname, widgetClass, crtshell, arg, n);
XtManageChild(crt);
XtPopup(crtshell, XtGrabNonexclusive);
XtSetKeyboardFocus(crtshell, crt); /* experimental? */
/*
* Create black and white Graphics Contexts
*/
gcvalues.foreground = BlackPixel( dpy, scr );
gcvalues.background = BlackPixel( dpy, scr );
blackGC = XCreateGC(dpy, XtWindow(crt),
GCForeground | GCBackground, &gcvalues);
gcvalues.foreground = WhitePixel( dpy, scr );
whiteGC = XCreateGC(dpy, XtWindow(crt),
GCForeground | GCBackground, &gcvalues);
if (!display_tablet) {
/* pencil cursor */
XDefineCursor(dpy, XtWindow(crt),
(Cursor) XCreateFontCursor(dpy, XC_pencil));
}
/*
* Setup to handle events
*/
XtAddEventHandler(crt, ButtonPressMask|ButtonMotionMask, FALSE,
handle_button_press, NULL);
XtAddEventHandler(crt, ButtonReleaseMask, FALSE,
handle_button_release, NULL);
XtAddEventHandler(crt, KeyPressMask, FALSE,
handle_key_press, NULL);
XtAddEventHandler(crt, KeyReleaseMask, FALSE,
handle_key_release, NULL);
XtAddEventHandler(crt, ExposureMask, FALSE,
handle_exposure, NULL);
return 1;
} /* ws_init */
void ws_shutdown (void)
{
}
void *
ws_color_black(void)
{
return blackGC;
}
void *
ws_color_white(void)
{
return whiteGC;
}
void *
ws_color_rgb(int r, int g, int b)
{
XColor color;
color.red = r;
color.green = g;
color.blue = b;
/* ignores flags */
if (XAllocColor(dpy, cmap, &color)) {
XGCValues gcvalues;
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.foreground = gcvalues.background = color.pixel;
return XCreateGC(dpy, XtWindow(crt),
GCForeground | GCBackground,
&gcvalues);
}
/* allocation failed */
return NULL;
}
/* put a point on the screen */
void
ws_display_point(int x, int y, void *color)
{
GC gc = (GC) color;
if (x > xpixels || y > ypixels)
return;
y = ypixels - y - 1; /* X11 coordinate system */
#ifdef FULL_SCREEN
x += xoffset;
y += yoffset;
#endif
if (gc == NULL)
gc = blackGC; /* default to off */
#if PIX_SIZE == 1
XDrawPoint(dpy, XtWindow(crt), gc, x, y);
#else
XFillRectangle(dpy, XtWindow(crt), gc,
x*PIX_SIZE, y*PIX_SIZE, PIX_SIZE, PIX_SIZE);
#endif
}
void
ws_sync(void)
{
XFlush(dpy);
}
/*
* elapsed wall clock time since last call
* +INF on first call
*/
struct elapsed_state {
struct timeval tvs[2];
int new;
};
static unsigned long
elapsed(struct elapsed_state *ep)
{
unsigned long val;
gettimeofday(&ep->tvs[ep->new], NULL);
if (ep->tvs[!ep->new].tv_sec == 0)
val = ~0L;
else
val = ((ep->tvs[ep->new].tv_sec - ep->tvs[!ep->new].tv_sec) * 1000000 +
(ep->tvs[ep->new].tv_usec - ep->tvs[!ep->new].tv_usec));
ep->new = !ep->new;
return val;
}
/* called periodically */
int
ws_poll(int *valp, int maxusec)
{
static struct elapsed_state es; /* static to avoid clearing! */
#ifdef WS_POLL_DEBUG
printf("ws_poll %d\n", maxusec);
fflush(stdout);
#endif
elapsed(&es); /* start clock */
do {
unsigned long e;
/* tried checking return, but lost on TCP connections? */
os_pollfd(ConnectionNumber(dpy), maxusec);
while (XtAppPending(app_context)) {
XEvent event;
/* XXX check for connection loss; set *valp? return 0 */
XtAppNextEvent(app_context, &event );
XtDispatchEvent( &event );
}
e = elapsed(&es);
#ifdef WS_POLL_DEBUG
printf(" maxusec %d e %d\r\n", maxusec, e);
fflush(stdout);
#endif
maxusec -= e;
} while (maxusec > 10000); /* 10ms */
return 1;
}
/* utility: can be called from main program
* which is willing to cede control
*/
int
ws_loop(void (*func)(void *), void *arg)
{
int val;
/* XXX use XtAppAddWorkProc & XtAppMainLoop? */
while (ws_poll(&val,0))
(*func)(arg);
return val;
}
void
ws_beep(void)
{
XBell(dpy, 0); /* ring at base volume */
XFlush(dpy);
}
/****************
* could move these to unix.c, if VMS versions needed
* (or just (GASP!) ifdef)
*/
/* public version, used by delay code */
unsigned long
os_elapsed(void)
{
static struct elapsed_state es;
return elapsed(&es);
}
/*
* select/DisplayNumber works on VMS 7.0+?
* could move to "unix.c"
* (I have some nasty VMS code that's supposed to to the job
* for older systems)
*/
/*
* sleep for maxus microseconds, returning TRUE sooner if fd is readable
* used by X11 driver
*/
static int
os_pollfd(int fd, int maxus)
{
/* use trusty old select (most portable) */
fd_set rfds;
struct timeval tv;
if (maxus >= 1000000) { /* not bloody likely, avoid divide */
tv.tv_sec = maxus / 1000000;
tv.tv_usec = maxus % 1000000;
}
else {
tv.tv_sec = 0;
tv.tv_usec = maxus;
}
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
return select(fd+1, &rfds, NULL, NULL, &tv) > 0;
}