mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-01-01 07:30:45 +00:00
953 lines
27 KiB
C
953 lines
27 KiB
C
/*
|
|
* Apple // emulator for Linux: X-Windows graphics support
|
|
*
|
|
* Copyright 1994 Alexander Jean-Claude Bottema
|
|
* Copyright 1995 Stephen Lee
|
|
* Copyright 1997, 1998 Aaron Culliney
|
|
* Copyright 1998, 1999, 2000 Michael Deutschmann
|
|
*
|
|
* This software package is subject to the GNU General Public License
|
|
* version 2 or later (your choice) as published by the Free Software
|
|
* Foundation.
|
|
*
|
|
* THERE ARE NO WARRANTIES WHATSOEVER.
|
|
*
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/XKBlib.h>
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <X11/extensions/XShm.h> /* MITSHM! */
|
|
#else
|
|
#error non-XShm is buggy ... key autorepeat delay is somehow set too low
|
|
#endif
|
|
|
|
static Display *display;
|
|
static Window win;
|
|
static GC gc;
|
|
static unsigned int width, height; /* window size */
|
|
static unsigned int scale = 1;
|
|
|
|
static int screen_num;
|
|
static XVisualInfo visualinfo;
|
|
static XImage *image=NULL;
|
|
static Colormap cmap;
|
|
static XEvent xevent;
|
|
|
|
static XSizeHints *size_hints=NULL;
|
|
static XWMHints *wm_hints=NULL;
|
|
static XClassHint *class_hints=NULL;
|
|
static XTextProperty windowName, iconName;
|
|
|
|
static uint32_t red_shift;
|
|
static uint32_t green_shift;
|
|
static uint32_t blue_shift;
|
|
static uint32_t alpha_shift;
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
static int doShm = 1; /* assume true */
|
|
static XShmSegmentInfo xshminfo;
|
|
static int xshmeventtype;
|
|
#endif
|
|
|
|
// pad pixels to uint32_t boundaries
|
|
static int bitmap_pad = sizeof(uint32_t);
|
|
|
|
static video_backend_s xvideo_backend = { 0 };
|
|
static bool request_set_mode = false;
|
|
static int request_mode = 0;
|
|
|
|
volatile unsigned long _backend_vid_dirty = 0;
|
|
|
|
typedef struct {
|
|
unsigned long flags;
|
|
unsigned long functions;
|
|
unsigned long decorations;
|
|
long inputMode;
|
|
unsigned long status;
|
|
} FullScreenHints;
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
/*
|
|
* XShm code influenced from the DOOM source code.
|
|
* This tries to find some shared memory to use. It checks for stale segments
|
|
* (maybe due to an emulator crash) and tries to remove them or use them.
|
|
*/
|
|
static void getshm(int size) {
|
|
int key = ('a'<<24) | ('p'<<16) | ('p'<<8) | 'l';
|
|
struct shmid_ds shminfo;
|
|
int id;
|
|
int rc;
|
|
int counter=5;
|
|
|
|
/* Try to allocate shared memory segment. Use stale segments if any are
|
|
* found.
|
|
*/
|
|
do
|
|
{
|
|
id = shmget((key_t) key, 0, 0777);
|
|
if (id == -1)
|
|
{
|
|
/* no stale ID's */
|
|
id = shmget((key_t)key, size, IPC_CREAT|0777);
|
|
if (id == -1)
|
|
{
|
|
perror("shmget");
|
|
printf("Could not get shared memory\n");
|
|
++key;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* we got someone else's ID. check if it's stale. */
|
|
printf("Found shared memory key=`%c%c%c%c', id=%d\n", (key & 0xff000000)>>24, (key & 0xff0000)>>16, (key & 0xff00)>>8, (key & 0xff), id);
|
|
rc=shmctl(id, IPC_STAT, &shminfo); /* get stats */
|
|
if (rc)
|
|
{
|
|
/* error. what to do now? */
|
|
perror("shmctl");
|
|
printf("Could not get stats on key=`%c%c%c%c', id=%d\n", (key & 0xff000000)>>24, (key & 0xff0000)>>16, (key & 0xff00)>>8, (key & 0xff), id);
|
|
++key;
|
|
}
|
|
else
|
|
{
|
|
if (shminfo.shm_nattch)
|
|
{
|
|
printf( "User uid=%d, key=`%c%c%c%c' appears to be running the emulator.\n", shminfo.shm_perm.cuid, (key & 0xff000000)>>24, (key & 0xff0000)>>16, (key & 0xff00)>>8, (key & 0xff));
|
|
++key; /* increase the key count */
|
|
}
|
|
else
|
|
{
|
|
if (getuid() == shminfo.shm_perm.cuid)
|
|
{
|
|
/* it's my stale ID */
|
|
rc = shmctl(id, IPC_RMID, 0);
|
|
if (!rc)
|
|
{
|
|
printf("Was able to kill my old shared memory\n");
|
|
}
|
|
else
|
|
{
|
|
perror("shmctl");
|
|
printf("Was NOT able to kill my old shared memory\n");
|
|
}
|
|
|
|
id = shmget((key_t)key, size, IPC_CREAT|0777);
|
|
if (id == -1)
|
|
{
|
|
perror("shmget");
|
|
printf("Could not get shared memory\n");
|
|
}
|
|
|
|
rc=shmctl(id, IPC_STAT, &shminfo);
|
|
if (rc)
|
|
{
|
|
perror("shmctl");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (size == shminfo.shm_segsz)
|
|
{
|
|
/* not my ID, but maybe we can use it */
|
|
printf("Will use stale shared memory of uid=%d\n", shminfo.shm_perm.cuid);
|
|
break;
|
|
}
|
|
/* not my ID, and we can't use it */
|
|
else
|
|
{
|
|
printf("Can't use stale shared memory belonging to uid=%d, key=`%c%c%c%c', id=%d\n", shminfo.shm_perm.cuid, (key & 0xff000000)>>24, (key & 0xff0000)>>16, (key & 0xff00)>>8, (key & 0xff), id);
|
|
++key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (--counter);
|
|
|
|
if (!counter)
|
|
{
|
|
printf( "System has too many stale/used shared memory segments!\n");
|
|
}
|
|
|
|
xshminfo.shmid = id;
|
|
|
|
/* attach to the shared memory segment */
|
|
image->data = xshminfo.shmaddr = shmat(id, 0, 0);
|
|
|
|
if ((size_t)(image->data) == -1)
|
|
{
|
|
perror("shmat");
|
|
printf("Could not attach to shared memory\n");
|
|
exit(1);
|
|
}
|
|
|
|
printf("Using shared memory key=`%c%c%c%c', id=%d, addr=%p\n", (key & 0xff000000)>>24, (key & 0xff0000)>>16, (key & 0xff00)>>8, (key & 0xff), id, image->data);
|
|
}
|
|
#endif
|
|
|
|
#if !TESTING
|
|
// Map X keysyms into Apple//ix internal-representation scancodes.
|
|
static int keysym_to_scancode(void) {
|
|
int rc = XkbKeycodeToKeysym(display, xevent.xkey.keycode, 0, 0);
|
|
switch (rc)
|
|
{
|
|
case XK_F1:
|
|
rc = SCODE_F1; break;
|
|
case XK_F2:
|
|
rc = SCODE_F2; break;
|
|
case XK_F3:
|
|
rc = SCODE_F3; break;
|
|
case XK_F4:
|
|
rc = SCODE_F4; break;
|
|
case XK_F5:
|
|
rc = SCODE_F5; break;
|
|
case XK_F6:
|
|
rc = SCODE_F6; break;
|
|
case XK_F7:
|
|
rc = SCODE_F7; break;
|
|
case XK_F8:
|
|
rc = SCODE_F8; break;
|
|
case XK_F9:
|
|
rc = SCODE_F9; break;
|
|
case XK_F10:
|
|
rc = SCODE_F10; break;
|
|
case XK_F11:
|
|
rc = SCODE_F11; break;
|
|
case XK_F12:
|
|
rc = SCODE_F12; break;
|
|
case XK_Left:
|
|
rc = SCODE_L; break;
|
|
case XK_Right:
|
|
rc = SCODE_R; break;
|
|
case XK_Down:
|
|
rc = SCODE_D; break;
|
|
case XK_Up:
|
|
rc = SCODE_U; break;
|
|
case XK_Escape:
|
|
rc = SCODE_ESC; break;
|
|
case XK_Return:
|
|
rc = SCODE_RET; break;
|
|
case XK_Tab:
|
|
rc = SCODE_TAB; break;
|
|
case XK_Shift_L:
|
|
rc = SCODE_L_SHIFT; break;
|
|
case XK_Shift_R:
|
|
rc = SCODE_R_SHIFT; break;
|
|
case XK_Control_L:
|
|
rc = SCODE_L_CTRL; break;
|
|
case XK_Control_R:
|
|
rc = SCODE_R_CTRL; break;
|
|
case XK_Caps_Lock:
|
|
{
|
|
// NOTE : emulator initially sets state based on a user preference. But if the user actually taps the Caps Lock
|
|
// key, then sync to system state.
|
|
unsigned int caps_state = 0;
|
|
XkbGetIndicatorState(display, XkbUseCoreKbd, &caps_state);
|
|
caps_lock = (caps_state & 0x01);
|
|
rc = -1; break;
|
|
}
|
|
case XK_BackSpace:
|
|
rc = SCODE_BS; break;
|
|
case XK_Insert:
|
|
rc = SCODE_INS; break;
|
|
case XK_Pause:
|
|
rc = SCODE_PAUSE; break;
|
|
case XK_Break:
|
|
/* Pause and Break are the same key, but have different
|
|
* scancodes (on PC keyboards). Ctrl makes the difference.
|
|
*
|
|
* We assume the X server is passing along the distinction to us,
|
|
* rather than making us check Ctrl manually.
|
|
*/
|
|
rc = SCODE_BRK; break;
|
|
case XK_Print:
|
|
rc = SCODE_PRNT; break;
|
|
case XK_Delete:
|
|
rc = SCODE_DEL; break;
|
|
case XK_End:
|
|
rc = SCODE_END; break;
|
|
case XK_Home:
|
|
rc = SCODE_HOME; break;
|
|
case XK_Page_Down:
|
|
rc = SCODE_PGDN; break;
|
|
case XK_Page_Up:
|
|
rc = SCODE_PGUP; break;
|
|
|
|
// keypad joystick movement
|
|
case XK_KP_5:
|
|
case XK_KP_Begin:
|
|
rc = SCODE_KPAD_C; break;
|
|
case XK_KP_4:
|
|
case XK_KP_Left:
|
|
rc = SCODE_KPAD_L; break;
|
|
case XK_KP_8:
|
|
case XK_KP_Up:
|
|
rc = SCODE_KPAD_U; break;
|
|
case XK_KP_6:
|
|
case XK_KP_Right:
|
|
rc = SCODE_KPAD_R; break;
|
|
case XK_KP_2:
|
|
case XK_KP_Down:
|
|
rc = SCODE_KPAD_D; break;
|
|
case XK_KP_7:
|
|
case XK_KP_Home:
|
|
rc = SCODE_KPAD_UL; break;
|
|
break;
|
|
case XK_KP_9:
|
|
case XK_KP_Page_Up:
|
|
rc = SCODE_KPAD_UR; break;
|
|
break;
|
|
case XK_KP_1:
|
|
case XK_KP_End:
|
|
rc = SCODE_KPAD_DL; break;
|
|
break;
|
|
case XK_KP_3:
|
|
case XK_KP_Page_Down:
|
|
rc = SCODE_KPAD_DR; break;
|
|
break;
|
|
|
|
case XK_Alt_L:
|
|
rc = SCODE_L_ALT; break;
|
|
case XK_Alt_R:
|
|
rc = SCODE_R_ALT; break;
|
|
|
|
default:
|
|
if ((rc >= XK_space) && (rc <= XK_asciitilde))
|
|
{
|
|
rc = xevent.xkey.keycode - 8;
|
|
}
|
|
else
|
|
{
|
|
rc = -1; // unmapped
|
|
}
|
|
break;
|
|
}
|
|
|
|
assert(rc < 0x80);
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
static void post_image() {
|
|
// copy Apple //e video memory into XImage uint32_t buffer
|
|
uint8_t *fb = !video__current_page ? video__fb1 : video__fb2;
|
|
uint8_t index;
|
|
|
|
unsigned int count = SCANWIDTH * SCANHEIGHT;
|
|
for (unsigned int i=0, j=0; i<count; i++, j+=4)
|
|
{
|
|
index = *(fb + i);
|
|
*( (uint32_t*)(image->data + j) ) = (uint32_t)(
|
|
((uint32_t)(colormap[index].red) << red_shift) |
|
|
((uint32_t)(colormap[index].green) << green_shift) |
|
|
((uint32_t)(colormap[index].blue) << blue_shift) |
|
|
((uint32_t)0xff /* alpha */ << alpha_shift)
|
|
);
|
|
if (scale > 1)
|
|
{
|
|
j+=4;
|
|
|
|
// duplicate pixel
|
|
*( (uint32_t*)(image->data + j) ) = (uint32_t)(
|
|
((uint32_t)(colormap[index].red) << red_shift) |
|
|
((uint32_t)(colormap[index].green) << green_shift) |
|
|
((uint32_t)(colormap[index].blue) << blue_shift) |
|
|
((uint32_t)0xff /* alpha */ << alpha_shift)
|
|
);
|
|
|
|
if (((i+1) % SCANWIDTH) == 0)
|
|
{
|
|
// duplicate entire row
|
|
int stride8 = SCANWIDTH<<3;//*8
|
|
memcpy(/* dest */image->data + j + 4, /* src */image->data + j + 4 - stride8, stride8);
|
|
j += stride8;
|
|
}
|
|
}
|
|
}
|
|
|
|
// post image...
|
|
#ifdef HAVE_X11_SHM
|
|
if (doShm)
|
|
{
|
|
if (!XShmPutImage(
|
|
display,
|
|
win,
|
|
gc,
|
|
image,
|
|
0, 0,
|
|
0, 0,
|
|
width, height,
|
|
True))
|
|
{
|
|
fprintf(stderr, "XShmPutImage() failed\n");
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (XPutImage(
|
|
display,
|
|
win,
|
|
gc,
|
|
image,
|
|
0, 0,
|
|
0, 0,
|
|
width, height
|
|
))
|
|
{
|
|
fprintf(stderr, "XPutImage() failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void c_flash_cursor(int on) {
|
|
// flash only if it's text or mixed modes.
|
|
if (softswitches & (SS_TEXT|SS_MIXED))
|
|
{
|
|
if (!on)
|
|
{
|
|
colormap[ COLOR_FLASHING_BLACK].red = 0;
|
|
colormap[ COLOR_FLASHING_BLACK].green = 0;
|
|
colormap[ COLOR_FLASHING_BLACK].blue = 0;
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].red = (uint8_t)0xffff;
|
|
colormap[ COLOR_FLASHING_WHITE].green = (uint8_t)0xffff;
|
|
colormap[ COLOR_FLASHING_WHITE].blue = (uint8_t)0xffff;
|
|
}
|
|
else
|
|
{
|
|
colormap[ COLOR_FLASHING_WHITE].red = 0;
|
|
colormap[ COLOR_FLASHING_WHITE].green = 0;
|
|
colormap[ COLOR_FLASHING_WHITE].blue = 0;
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].red = (uint8_t)0xffff;
|
|
colormap[ COLOR_FLASHING_BLACK].green = (uint8_t)0xffff;
|
|
colormap[ COLOR_FLASHING_BLACK].blue = (uint8_t)0xffff;
|
|
}
|
|
}
|
|
}
|
|
|
|
void video_driver_sync(void) {
|
|
|
|
static int flash_count = 0;
|
|
// post the image and loop waiting for it to finish and
|
|
// also process other input events
|
|
post_image();
|
|
|
|
#if TESTING
|
|
// no input processing if test-driven ...
|
|
#else
|
|
bool keyevent = true;
|
|
do {
|
|
#ifdef HAVE_X11_SHM
|
|
if (doShm)
|
|
{
|
|
XNextEvent(display, &xevent);
|
|
keyevent = !(xevent.type == xshmeventtype);
|
|
} else
|
|
#endif
|
|
{
|
|
keyevent = XCheckMaskEvent(display, KeyPressMask|KeyReleaseMask, &xevent);
|
|
}
|
|
|
|
int scancode = -1;
|
|
int pressed = 0;
|
|
switch (xevent.type)
|
|
{
|
|
case KeyPress:
|
|
scancode = keysym_to_scancode();
|
|
pressed = 1;
|
|
break;
|
|
case KeyRelease:
|
|
scancode = keysym_to_scancode();
|
|
pressed = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
c_keys_handle_input(scancode, pressed, 0);
|
|
|
|
} while (keyevent);
|
|
#endif
|
|
|
|
#warning HACKISH flash count needs refactoring ...
|
|
switch (++flash_count)
|
|
{
|
|
case 6:
|
|
c_flash_cursor(1);
|
|
break;
|
|
case 12:
|
|
c_flash_cursor(0);
|
|
flash_count = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void _redo_image(void);
|
|
|
|
static void xdriver_main_loop(void) {
|
|
struct timespec sleeptime = { .tv_sec=0, .tv_nsec=8333333 }; // 120Hz
|
|
do {
|
|
if (request_set_mode) {
|
|
request_set_mode = false;
|
|
_redo_image();
|
|
}
|
|
video_driver_sync();
|
|
nanosleep(&sleeptime, NULL);
|
|
} while (1);
|
|
}
|
|
|
|
#if 0
|
|
static Cursor hidecursor() {
|
|
Pixmap cursormask;
|
|
XGCValues xgc;
|
|
XColor dummycolour;
|
|
Cursor cursor;
|
|
GC cursor_gc;
|
|
|
|
cursormask = XCreatePixmap(display, win, 1, 1, 1 /*depth*/);
|
|
xgc.function = GXclear;
|
|
cursor_gc = XCreateGC(display, cursormask, GCFunction, &xgc);
|
|
XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
|
|
dummycolour.pixel = 0;
|
|
dummycolour.red = 0;
|
|
dummycolour.flags = 04;
|
|
cursor = XCreatePixmapCursor(display, cursormask, cursormask, &dummycolour,&dummycolour, 0,0);
|
|
XFreePixmap(display,cursormask);
|
|
XFreeGC(display,cursor_gc);
|
|
return cursor;
|
|
}
|
|
#endif
|
|
|
|
static void parseArgs() {
|
|
int i;
|
|
for (i=0; i<argc; i++)
|
|
{
|
|
#ifdef HAVE_X11_SHM
|
|
if (strstr(argv[i], "--noshm"))
|
|
{
|
|
doShm=0;
|
|
} else
|
|
#endif
|
|
if (strstr(argv[i], "--help") || strstr(argv[i], "-h")) {
|
|
printf("%s v%s emulator help :\n", PACKAGE_NAME, PACKAGE_VERSION);
|
|
printf("\tPress F10 for main menu\n");
|
|
printf("\tPress F1 and F2 to insert disk images\n");
|
|
exit(0);
|
|
} else if (strstr(argv[i], "--version") || strstr(argv[i], "-v")) {
|
|
printf("%s v%s\n", PACKAGE_NAME, PACKAGE_VERSION);
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _destroy_image() {
|
|
#ifdef HAVE_X11_SHM
|
|
if (doShm)
|
|
{
|
|
// Detach from X server
|
|
if (!XShmDetach(display, &xshminfo))
|
|
{
|
|
fprintf(stderr,"XShmDetach() failed in video_shutdown()\n");
|
|
}
|
|
|
|
XDestroyImage(image);
|
|
|
|
// Release shared memory.
|
|
shmdt(xshminfo.shmaddr);
|
|
shmctl(xshminfo.shmid, IPC_RMID, 0);
|
|
} else
|
|
#endif
|
|
{
|
|
XDestroyImage(image);
|
|
//free(image->data);
|
|
}
|
|
}
|
|
|
|
static void _create_image() {
|
|
int pixel_buffer_size = width*height*bitmap_pad;
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
if (doShm) {
|
|
image = XShmCreateImage(display, visualinfo.visual, visualinfo.depth, ZPixmap, NULL, &xshminfo, width, height);
|
|
|
|
if (!image) {
|
|
ERRQUIT("XShmCreateImage failed");
|
|
}
|
|
|
|
LOG("Allocating shared memory: bytes_per_line:%ds height:x%d (depth:%d) bitmap_pad:%d",
|
|
image->bytes_per_line, image->height, visualinfo.depth, bitmap_pad);
|
|
|
|
getshm(pixel_buffer_size);
|
|
|
|
/* get the X server to attach to it */
|
|
if (!XShmAttach(display, &xshminfo)) {
|
|
ERRQUIT("XShmAttach() failed");
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
void *data = malloc(pixel_buffer_size);
|
|
if (!data) {
|
|
ERRQUIT("no memory for image data!");
|
|
}
|
|
|
|
LOG("WARNING!!! No XShm extension ... creating regular XImage");
|
|
image = XCreateImage(display, visualinfo.visual, visualinfo.depth, ZPixmap, 0 /*offset*/, data, width, height, 8, width*bitmap_pad /*bytes_per_line*/);
|
|
|
|
if (!image) {
|
|
ERRQUIT("XCreateImage failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _size_hints_set_fixed() {
|
|
size_hints->flags = PPosition | PSize | PMinSize | PMaxSize;
|
|
size_hints->min_width = width;
|
|
size_hints->min_height = height;
|
|
size_hints->max_width = XDisplayWidth(display, 0);
|
|
size_hints->max_height = XDisplayHeight(display, 0);
|
|
}
|
|
|
|
static void _size_hints_set_resize() {
|
|
size_hints->flags = USPosition | USSize | PMinSize | PMaxSize;
|
|
size_hints->min_width = width;
|
|
size_hints->min_height = height;
|
|
size_hints->max_width = XDisplayWidth(display, 0);
|
|
size_hints->max_height = XDisplayHeight(display, 0);
|
|
}
|
|
|
|
void video_set_mode(a2_video_mode_t mode) {
|
|
request_mode = mode;
|
|
request_set_mode = true;
|
|
}
|
|
|
|
static void _redo_image(void) {
|
|
_destroy_image();
|
|
|
|
int mode = request_mode;
|
|
scale = mode;
|
|
if (mode == VIDEO_FULLSCREEN) {
|
|
scale = 1; // HACK FIXME for now ................
|
|
}
|
|
|
|
width = SCANWIDTH*scale;
|
|
height = SCANHEIGHT*scale;
|
|
|
|
_size_hints_set_resize();
|
|
|
|
//XResizeWindow(display, win, width, height);
|
|
XSetWMProperties(display, win, &windowName, &iconName,
|
|
argv, argc, size_hints, wm_hints,
|
|
class_hints);
|
|
|
|
#if 0
|
|
// TODO ...
|
|
// Fullscreen mode really should "fuzz" the graphics like AppleWin does when emulating NTSC.
|
|
// Also will need to verify the canonical way to switch to fullscreen in X11
|
|
// http://www.tonyobryan.com/index.php?article=9
|
|
if (mode == VIDEO_FULLSCREEN) {
|
|
FullScreenHints hints = { .flags=2, .decorations=0 };
|
|
Atom property = XInternAtom(display, "_MOTIF_WM_HINTS", True);
|
|
|
|
XChangeProperty(display, win, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
|
|
|
|
/*
|
|
int modecount_return = 0;
|
|
XF86VidModeModeInfo *modesinfo = NULL;
|
|
XF86VidModeGetAllModeLines(display, DefaultScreen(display), &modecount_return, &modesinfo);
|
|
XF86VidModeSwitchToMode(display, DefaultScreen(display), video_mode);
|
|
XF86VidModeSetViewPort(display, DefaultScreen(display), 0, 0);
|
|
*/
|
|
|
|
int display_w = XDisplayWidth(display, 0);
|
|
int display_h = XDisplayHeight(display, 0);
|
|
LOG("Fullscreen : %d x %d", display_w, display_h);
|
|
|
|
XMoveResizeWindow(display, win, 0, 0, display_w, display_h);
|
|
XMapRaised(display, win);
|
|
XGrabPointer(display, win, True, 0, GrabModeAsync, GrabModeAsync, win, 0L, CurrentTime);
|
|
XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
}
|
|
#endif
|
|
|
|
XWindowChanges changes = { .width=width, .height=height };
|
|
XConfigureWindow(display, win, CWWidth|CWHeight, &changes);
|
|
|
|
_create_image();
|
|
|
|
_size_hints_set_fixed();
|
|
}
|
|
|
|
static void xdriver_init(void *context) {
|
|
XSetWindowAttributes attribs;
|
|
unsigned long attribmask;
|
|
int x, y; /* window position */
|
|
int drawingok;
|
|
//unsigned int display_width, display_height;
|
|
XGCValues xgcvalues;
|
|
int valuemask;
|
|
char *window_name = "Apple //ix";
|
|
char *icon_name = window_name;
|
|
//GC gc;
|
|
char *displayname = NULL;
|
|
|
|
if (argv == NULL) {
|
|
LOG("No command line arguments ...");
|
|
argc = 0;
|
|
} else {
|
|
parseArgs();
|
|
}
|
|
|
|
if (!(size_hints = XAllocSizeHints()))
|
|
{
|
|
fprintf(stderr, "cannot allocate memory for SizeHints\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (!(wm_hints = XAllocWMHints()))
|
|
{
|
|
fprintf(stderr, "cannot allocate memory for WMHints\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (!(class_hints = XAllocClassHint()))
|
|
{
|
|
fprintf(stderr, "cannot allocate memory for ClassHints\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* connect to X server */
|
|
if ( (display=XOpenDisplay(displayname)) == NULL )
|
|
{
|
|
fprintf(stderr, "cannot connect to X server \"%s\"\n",
|
|
XDisplayName(displayname));
|
|
exit(1);
|
|
}
|
|
|
|
screen_num = DefaultScreen(display);
|
|
// Note that in a real Xlib application, we would support more than the default visual :-P
|
|
//visual = DefaultVisual(display, screen_num);
|
|
//XVisualInfo *visuals_list=NULL;
|
|
//visualinfo.screen=screen_num;
|
|
/*
|
|
int numvisuals=0;
|
|
if (!(visuals_list = XGetVisualInfo(display, VisualScreenMask, &visualinfo, &numvisuals))) {
|
|
fprintf(stderr, "XGetVisualInfo() failed...");
|
|
exit(1);
|
|
}
|
|
visualinfo = visuals_list[0];
|
|
if ( (visualinfo.class == PseudoColor) || (visualinfo.depth == 8) ) {
|
|
fprintf(stderr, "PseudoColor or 8bit color is unimplemented, FIXME!");
|
|
exit(1);
|
|
}
|
|
XFree(visuals_list);
|
|
*/
|
|
if (!XMatchVisualInfo(display, XDefaultScreen(display), 32, TrueColor, &visualinfo))
|
|
{
|
|
fprintf(stderr, "no such visual\n");
|
|
exit(1);
|
|
}
|
|
|
|
// determine mask bits ...
|
|
// red_mask: 00ff0000
|
|
// green_mask: 0000ff00
|
|
// blue_mask: 000000ff
|
|
// bits_per_rgb: 8
|
|
unsigned int shift = 0;
|
|
for (unsigned int i=0; i<4; i++)
|
|
{
|
|
if ((((uint32_t)visualinfo.red_mask >>shift) & 0xff) == (uint32_t)0xff)
|
|
{
|
|
red_shift = shift;
|
|
}
|
|
else if ((((uint32_t)visualinfo.green_mask>>shift) & 0xff) == (uint32_t)0xff)
|
|
{
|
|
green_shift = shift;
|
|
}
|
|
else if ((((uint32_t)visualinfo.blue_mask >>shift) & 0xff) == (uint32_t)0xff)
|
|
{
|
|
blue_shift = shift;
|
|
}
|
|
else
|
|
{
|
|
alpha_shift = shift;
|
|
}
|
|
|
|
shift += 8;
|
|
}
|
|
|
|
if ((!red_shift) && (!green_shift) && (!blue_shift))
|
|
{
|
|
fprintf(stderr, "Could not calculate red/green/blue color masks...\n");
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(stderr, "red mask:%08x green mask:%08x blue mask:%08x\n", (uint32_t)visualinfo.red_mask, (uint32_t)visualinfo.blue_mask, (uint32_t)visualinfo.green_mask);
|
|
fprintf(stderr, "redshift:%08d greenshift:%08d blueshift:%08d alphashift:%08d\n", red_shift, blue_shift, green_shift, alpha_shift);
|
|
|
|
scale = a2_video_mode;
|
|
if (a2_video_mode == VIDEO_FULLSCREEN) {
|
|
scale = 1; // HACK FIXME FOR NOW ...
|
|
}
|
|
|
|
/* Note that in a real Xlib application, x and y would default to 0
|
|
* but would be settable from the command line or resource database.
|
|
*/
|
|
x = y = 0;
|
|
width = SCANWIDTH*scale;
|
|
height = SCANHEIGHT*scale;
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
/* init MITSHM if we're doing it */
|
|
if (doShm)
|
|
{
|
|
/* make sure we have it */
|
|
doShm = XShmQueryExtension(display);
|
|
}
|
|
#endif
|
|
|
|
displayname = getenv("DISPLAY");
|
|
if (displayname)
|
|
{
|
|
if (*displayname != ':')
|
|
{
|
|
printf("NOTE: Sound not allowed for remote display \"%s\".\n", displayname);
|
|
#ifdef HAVE_X11_SHM
|
|
if (doShm)
|
|
{
|
|
printf("NOTE: Cannot run MITSHM version of emulator with display \"%s\"\n"
|
|
"Try setting DISPLAY to something like \":0.0\"...Reverting to regular X.\n",
|
|
displayname);
|
|
}
|
|
|
|
doShm=0;
|
|
//soundAllowed=0; FIXME TODO enforce this ...
|
|
#endif
|
|
}
|
|
}
|
|
|
|
cmap = XCreateColormap(display, XDefaultRootWindow(display), visualinfo.visual, AllocNone);
|
|
//XStoreColors(display, cmap, colors, 256);
|
|
attribs.colormap = cmap;
|
|
attribs.border_pixel = 0;
|
|
|
|
/* select event types wanted */
|
|
attribmask = CWEventMask | CWColormap | CWBorderPixel; /* HACK CWBorderPixel? */
|
|
attribs.event_mask = KeyPressMask | KeyReleaseMask | ExposureMask;
|
|
|
|
/* create opaque window */
|
|
win = XCreateWindow(display, RootWindow(display, screen_num),
|
|
x, y, width, height,
|
|
0, /* border_width */
|
|
visualinfo.depth, /* depth */
|
|
InputOutput,
|
|
visualinfo.visual,
|
|
attribmask,
|
|
&attribs);
|
|
|
|
/* store window_name and icon_name for niceity. */
|
|
if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
|
|
{
|
|
fprintf(stderr, "structure allocation for windowName failed.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0)
|
|
{
|
|
fprintf(stderr, "structure allocation for iconName failed.\n");
|
|
exit(1);
|
|
}
|
|
|
|
// set up window manager hints...
|
|
wm_hints->initial_state = NormalState;
|
|
wm_hints->input = True;
|
|
wm_hints->flags = StateHint | IconPixmapHint /* | InputHint*/;
|
|
|
|
class_hints->res_name = "apple2ix";
|
|
class_hints->res_class = "Apple2";
|
|
|
|
_size_hints_set_fixed();
|
|
XSetWMProperties(display, win, &windowName, &iconName,
|
|
argv, argc, size_hints, wm_hints,
|
|
class_hints);
|
|
|
|
// FIXME!!!!! hidecursor segfaults
|
|
//XDefineCursor(display, win, hidecursor(display, win));
|
|
|
|
/* create the GC */
|
|
valuemask = GCGraphicsExposures;
|
|
xgcvalues.graphics_exposures = False;
|
|
gc = XCreateGC(display, win, valuemask, &xgcvalues);
|
|
|
|
/* display window */
|
|
XMapWindow(display, win);
|
|
|
|
/* wait until it is OK to draw */
|
|
drawingok = 0;
|
|
while (!drawingok)
|
|
{
|
|
XNextEvent(display, &xevent);
|
|
if ((xevent.type == Expose) && !xevent.xexpose.count)
|
|
{
|
|
drawingok = 1;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_X11_SHM
|
|
xshmeventtype = XShmGetEventBase(display) + ShmCompletion;
|
|
#endif
|
|
|
|
_create_image();
|
|
|
|
#ifdef KEYPAD_JOYSTICK
|
|
int autorepeat_supported = 0;
|
|
XkbGetDetectableAutoRepeat(display, &autorepeat_supported);
|
|
if (autorepeat_supported)
|
|
{
|
|
LOG("Xkb Setting detectable autorepeat ...");
|
|
XkbSetDetectableAutoRepeat(display, true, NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void xdriver_shutdown(void) {
|
|
_destroy_image();
|
|
}
|
|
|
|
static void xdriver_reshape(int width, int height) {
|
|
// no-op
|
|
}
|
|
|
|
static void xdriver_render(void) {
|
|
// no-op
|
|
}
|
|
|
|
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
|
|
static void _init_xvideo(void) {
|
|
LOG("Initializing X11 renderer");
|
|
|
|
assert((video_backend == NULL) && "there can only be one!");
|
|
|
|
xvideo_backend.init = &xdriver_init;
|
|
xvideo_backend.main_loop = &xdriver_main_loop;
|
|
xvideo_backend.reshape = &xdriver_reshape;
|
|
xvideo_backend.render = &xdriver_render;
|
|
xvideo_backend.shutdown = &xdriver_shutdown;
|
|
|
|
video_backend = &xvideo_backend;
|
|
}
|
|
|