2013-07-06 04:37:13 +00:00
/*
2013-06-11 07:08:15 +00:00
* 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
2013-07-06 04:37:13 +00:00
* version 2 or later ( your choice ) as published by the Free Software
2013-06-11 07:08:15 +00:00
* Foundation .
*
2013-07-06 04:37:13 +00:00
* THERE ARE NO WARRANTIES WHATSOEVER .
2013-06-11 07:08:15 +00:00
*
*/
2014-01-23 04:42:34 +00:00
# include "common.h"
2013-06-11 07:08:15 +00:00
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/keysym.h>
2013-06-19 07:07:41 +00:00
# include <X11/XKBlib.h>
2013-06-11 07:08:15 +00:00
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
# include <sys/ipc.h>
# include <sys/shm.h>
# include <X11/extensions/XShm.h> /* MITSHM! */
2014-06-08 21:14:47 +00:00
# else
# error non-XShm is buggy ... key autorepeat delay is somehow set too low
2014-01-23 04:42:34 +00:00
# endif
2013-06-11 07:08:15 +00:00
static Display * display ;
static Window win ;
static GC gc ;
2013-07-06 04:37:13 +00:00
static unsigned int width , height ; /* window size */
2014-01-04 22:24:55 +00:00
static unsigned int scale = 1 ;
2013-06-11 07:08:15 +00:00
static int screen_num ;
static XVisualInfo visualinfo ;
2014-01-04 22:24:55 +00:00
static XImage * image = NULL ;
2013-06-11 07:08:15 +00:00
static Colormap cmap ;
2014-01-04 22:24:55 +00:00
static XEvent xevent ;
static XSizeHints * size_hints = NULL ;
static XWMHints * wm_hints = NULL ;
static XClassHint * class_hints = NULL ;
static XTextProperty windowName , iconName ;
2013-06-11 07:08:15 +00:00
2013-06-25 06:14:39 +00:00
static uint32_t red_shift ;
static uint32_t green_shift ;
static uint32_t blue_shift ;
static uint32_t alpha_shift ;
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2014-01-04 22:24:55 +00:00
static int doShm = 1 ; /* assume true */
static XShmSegmentInfo xshminfo ;
static int xshmeventtype ;
2014-01-23 04:42:34 +00:00
# endif
2014-01-04 22:24:55 +00:00
// pad pixels to uint32_t boundaries
static int bitmap_pad = sizeof ( uint32_t ) ;
2013-06-11 07:08:15 +00:00
2014-01-04 22:24:55 +00:00
typedef struct {
unsigned long flags ;
unsigned long functions ;
unsigned long decorations ;
long inputMode ;
unsigned long status ;
} FullScreenHints ;
2013-06-11 07:08:15 +00:00
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-06-11 07:08:15 +00:00
/*
* 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 ) {
2013-07-06 04:37:13 +00:00
int key = ( ' a ' < < 24 ) | ( ' p ' < < 16 ) | ( ' p ' < < 8 ) | ' l ' ;
struct shmid_ds shminfo ;
int id ;
int rc ;
int counter = 5 ;
2013-06-11 07:08:15 +00:00
/* Try to allocate shared memory segment. Use stale segments if any are
* found .
*/
do
{
2013-07-06 04:37:13 +00:00
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 )
{
2013-06-25 06:14:39 +00:00
/* it's my stale ID */
2013-07-06 04:37:13 +00:00
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 )
{
2013-06-25 06:14:39 +00:00
/* not my ID, but maybe we can use it */
2013-07-06 04:37:13 +00:00
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 ;
}
}
}
}
2013-06-11 07:08:15 +00:00
} while ( - - counter ) ;
2013-07-06 04:37:13 +00:00
if ( ! counter )
{
printf ( " System has too many stale/used shared memory segments! \n " ) ;
}
2013-06-11 07:08:15 +00:00
xshminfo . shmid = id ;
/* attach to the shared memory segment */
image - > data = xshminfo . shmaddr = shmat ( id , 0 , 0 ) ;
2014-06-08 22:50:34 +00:00
if ( ( size_t ) ( image - > data ) = = - 1 )
2013-07-06 04:37:13 +00:00
{
perror ( " shmat " ) ;
printf ( " Could not attach to shared memory \n " ) ;
exit ( 1 ) ;
2013-06-11 07:08:15 +00:00
}
2013-06-25 06:14:39 +00:00
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 ) ;
2013-06-11 07:08:15 +00:00
}
2014-01-23 04:42:34 +00:00
# endif
2013-06-11 07:08:15 +00:00
2014-04-26 20:22:19 +00:00
# if !defined(TESTING)
2013-12-30 02:07:24 +00:00
// Map X keysyms into Apple//ix internal-representation scancodes.
2013-06-11 07:08:15 +00:00
static int keysym_to_scancode ( void ) {
2013-12-30 02:07:24 +00:00
int rc = XkbKeycodeToKeysym ( display , xevent . xkey . keycode , 0 , 0 ) ;
switch ( rc )
2013-07-06 04:37:13 +00:00
{
case XK_F1 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F1 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F2 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F2 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F3 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F3 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F4 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F4 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F5 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F5 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F6 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F6 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F7 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F7 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F8 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F8 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F9 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F9 ; break ;
2013-07-06 04:37:13 +00:00
case XK_F10 :
2013-12-30 02:07:24 +00:00
rc = SCODE_F10 ; break ;
case XK_F11 :
rc = SCODE_F11 ; break ;
case XK_F12 :
rc = SCODE_F12 ; break ;
2013-07-06 04:37:13 +00:00
case XK_Left :
2013-12-30 02:07:24 +00:00
rc = SCODE_L ; break ;
2013-07-06 04:37:13 +00:00
case XK_Right :
2013-12-30 02:07:24 +00:00
rc = SCODE_R ; break ;
2013-07-06 04:37:13 +00:00
case XK_Down :
2013-12-30 02:07:24 +00:00
rc = SCODE_D ; break ;
2013-07-06 04:37:13 +00:00
case XK_Up :
2013-12-30 02:07:24 +00:00
rc = SCODE_U ; break ;
2013-07-06 04:37:13 +00:00
case XK_Escape :
2013-12-30 02:07:24 +00:00
rc = SCODE_ESC ; break ;
2013-07-06 04:37:13 +00:00
case XK_Return :
2013-12-30 02:07:24 +00:00
rc = SCODE_RET ; break ;
2013-07-06 04:37:13 +00:00
case XK_Tab :
2013-12-30 02:07:24 +00:00
rc = SCODE_TAB ; break ;
2013-07-06 04:37:13 +00:00
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 :
2014-03-30 20:20:56 +00:00
{
// 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 ;
}
2013-07-06 04:37:13 +00:00
case XK_BackSpace :
2013-12-30 02:07:24 +00:00
rc = SCODE_BS ; break ;
2013-07-06 04:37:13 +00:00
case XK_Insert :
2013-12-30 02:07:24 +00:00
rc = SCODE_INS ; break ;
2013-07-06 04:37:13 +00:00
case XK_Pause :
2013-12-30 02:07:24 +00:00
rc = SCODE_PAUSE ; break ;
2013-07-06 04:37:13 +00:00
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 .
*/
2013-12-30 02:07:24 +00:00
rc = SCODE_BRK ; break ;
2013-07-06 04:37:13 +00:00
case XK_Print :
2013-12-30 02:07:24 +00:00
rc = SCODE_PRNT ; break ;
2013-07-06 04:37:13 +00:00
case XK_Delete :
2013-12-30 02:07:24 +00:00
rc = SCODE_DEL ; break ;
2013-07-06 04:37:13 +00:00
case XK_End :
2013-12-30 02:07:24 +00:00
rc = SCODE_END ; break ;
2013-07-06 04:37:13 +00:00
case XK_Home :
2013-12-30 02:07:24 +00:00
rc = SCODE_HOME ; break ;
2013-07-06 04:37:13 +00:00
case XK_Page_Down :
2013-12-30 02:07:24 +00:00
rc = SCODE_PGDN ; break ;
2013-07-06 04:37:13 +00:00
case XK_Page_Up :
2013-12-30 02:07:24 +00:00
rc = SCODE_PGUP ; break ;
2013-07-06 04:37:13 +00:00
// keypad joystick movement
case XK_KP_5 :
case XK_KP_Begin :
2013-12-30 02:07:24 +00:00
rc = SCODE_KPAD_C ; break ;
2013-07-06 04:37:13 +00:00
case XK_KP_4 :
case XK_KP_Left :
2013-12-30 02:07:24 +00:00
rc = SCODE_KPAD_L ; break ;
2013-07-06 04:37:13 +00:00
case XK_KP_8 :
case XK_KP_Up :
2013-12-30 02:07:24 +00:00
rc = SCODE_KPAD_U ; break ;
2013-07-06 04:37:13 +00:00
case XK_KP_6 :
case XK_KP_Right :
2013-12-30 02:07:24 +00:00
rc = SCODE_KPAD_R ; break ;
2013-07-06 04:37:13 +00:00
case XK_KP_2 :
case XK_KP_Down :
2013-12-30 02:07:24 +00:00
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 ;
2013-07-06 04:37:13 +00:00
case XK_Alt_L :
2013-12-30 02:07:24 +00:00
rc = SCODE_L_ALT ; break ;
2013-07-06 04:37:13 +00:00
case XK_Alt_R :
2013-12-30 02:07:24 +00:00
rc = SCODE_R_ALT ; break ;
2013-07-06 04:37:13 +00:00
default :
if ( ( rc > = XK_space ) & & ( rc < = XK_asciitilde ) )
{
rc = xevent . xkey . keycode - 8 ;
}
2013-12-30 02:07:24 +00:00
else
{
rc = - 1 ; // unmapped
}
2013-07-06 04:37:13 +00:00
break ;
2013-06-11 07:08:15 +00:00
}
2013-12-30 02:07:24 +00:00
assert ( rc < 0x80 ) ;
return rc ;
2013-06-11 07:08:15 +00:00
}
2014-04-26 20:22:19 +00:00
# endif
2013-06-11 07:08:15 +00:00
static void post_image ( ) {
2013-06-25 06:14:39 +00:00
// 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 ;
2013-07-06 04:37:13 +00:00
for ( unsigned int i = 0 , j = 0 ; i < count ; i + + , j + = 4 )
{
2013-06-25 06:14:39 +00:00
index = * ( fb + i ) ;
* ( ( uint32_t * ) ( image - > data + j ) ) = ( uint32_t ) (
2014-08-17 22:31:49 +00:00
( ( uint32_t ) ( colormap [ index ] . red ) < < red_shift ) |
( ( uint32_t ) ( colormap [ index ] . green ) < < green_shift ) |
( ( uint32_t ) ( colormap [ index ] . blue ) < < blue_shift ) |
2013-07-06 04:37:13 +00:00
( ( uint32_t ) 0xff /* alpha */ < < alpha_shift )
2013-06-25 06:14:39 +00:00
) ;
2014-01-04 22:24:55 +00:00
if ( scale > 1 )
2013-11-25 02:24:00 +00:00
{
j + = 4 ;
// duplicate pixel
* ( ( uint32_t * ) ( image - > data + j ) ) = ( uint32_t ) (
2014-08-17 22:31:49 +00:00
( ( uint32_t ) ( colormap [ index ] . red ) < < red_shift ) |
( ( uint32_t ) ( colormap [ index ] . green ) < < green_shift ) |
( ( uint32_t ) ( colormap [ index ] . blue ) < < blue_shift ) |
2013-11-25 02:24:00 +00:00
( ( 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 ;
}
}
2013-06-25 06:14:39 +00:00
}
// post image...
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-07-06 04:37:13 +00:00
if ( doShm )
{
if ( ! XShmPutImage (
display ,
win ,
gc ,
image ,
0 , 0 ,
0 , 0 ,
2013-11-25 02:24:00 +00:00
width , height ,
2013-07-06 04:37:13 +00:00
True ) )
{
fprintf ( stderr , " XShmPutImage() failed \n " ) ;
}
2014-01-23 04:42:34 +00:00
} else
# endif
2013-07-06 04:37:13 +00:00
{
if ( XPutImage (
display ,
win ,
gc ,
image ,
0 , 0 ,
0 , 0 ,
2013-11-25 02:24:00 +00:00
width , height
2013-07-06 04:37:13 +00:00
) )
{
fprintf ( stderr , " XPutImage() failed \n " ) ;
}
2013-06-11 07:08:15 +00:00
}
}
static void c_flash_cursor ( int on ) {
// flash only if it's text or mixed modes.
2013-07-06 04:37:13 +00:00
if ( softswitches & ( SS_TEXT | SS_MIXED ) )
{
if ( ! on )
{
2014-08-17 22:31:49 +00:00
colormap [ COLOR_FLASHING_BLACK ] . red = 0 ;
colormap [ COLOR_FLASHING_BLACK ] . green = 0 ;
colormap [ COLOR_FLASHING_BLACK ] . blue = 0 ;
2013-07-06 04:37:13 +00:00
2014-08-17 22:31:49 +00:00
colormap [ COLOR_FLASHING_WHITE ] . red = 0xffff ;
colormap [ COLOR_FLASHING_WHITE ] . green = 0xffff ;
colormap [ COLOR_FLASHING_WHITE ] . blue = 0xffff ;
2013-07-06 04:37:13 +00:00
}
else
{
2014-08-17 22:31:49 +00:00
colormap [ COLOR_FLASHING_WHITE ] . red = 0 ;
colormap [ COLOR_FLASHING_WHITE ] . green = 0 ;
colormap [ COLOR_FLASHING_WHITE ] . blue = 0 ;
2013-07-06 04:37:13 +00:00
2014-08-17 22:31:49 +00:00
colormap [ COLOR_FLASHING_BLACK ] . red = 0xffff ;
colormap [ COLOR_FLASHING_BLACK ] . green = 0xffff ;
colormap [ COLOR_FLASHING_BLACK ] . blue = 0xffff ;
2013-07-06 04:37:13 +00:00
}
2013-06-11 07:08:15 +00:00
}
}
2014-09-27 18:03:51 +00:00
static void video_driver_sync ( void ) {
2014-04-26 20:22:19 +00:00
if ( is_headless ) {
return ;
}
2013-06-11 07:08:15 +00:00
static int flash_count = 0 ;
// post the image and loop waiting for it to finish and
// also process other input events
post_image ( ) ;
2013-07-06 04:37:13 +00:00
2014-04-26 20:22:19 +00:00
# ifdef TESTING
// no input processing if test-driven ...
# else
2013-12-28 21:08:35 +00:00
bool keyevent = true ;
do {
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-12-28 21:08:35 +00:00
if ( doShm )
2013-07-06 04:37:13 +00:00
{
2013-12-28 21:08:35 +00:00
XNextEvent ( display , & xevent ) ;
keyevent = ! ( xevent . type = = xshmeventtype ) ;
2014-01-23 04:42:34 +00:00
} else
# endif
2013-12-28 21:08:35 +00:00
{
keyevent = XCheckMaskEvent ( display , KeyPressMask | KeyReleaseMask , & xevent ) ;
2013-07-06 04:37:13 +00:00
}
2013-12-28 21:08:35 +00:00
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 ;
}
2013-07-06 04:37:13 +00:00
2014-09-22 00:26:37 +00:00
c_keys_handle_input ( scancode , pressed , 0 ) ;
2013-06-11 07:08:15 +00:00
2013-12-28 21:08:35 +00:00
} while ( keyevent ) ;
2014-04-26 20:22:19 +00:00
# endif
2013-06-11 07:08:15 +00:00
2014-09-07 03:04:32 +00:00
# warning HACKISH flash count needs refactoring ...
2013-06-11 07:08:15 +00:00
switch ( + + flash_count )
{
2013-07-06 04:37:13 +00:00
case 6 :
2013-06-11 07:08:15 +00:00
c_flash_cursor ( 1 ) ;
break ;
2013-07-06 04:37:13 +00:00
case 12 :
2013-06-11 07:08:15 +00:00
c_flash_cursor ( 0 ) ;
2013-07-06 04:37:13 +00:00
flash_count = 0 ;
2013-06-11 07:08:15 +00:00
break ;
default :
break ;
}
}
2014-09-07 03:04:32 +00:00
void video_driver_main_loop ( void ) {
struct timespec sleeptime = { . tv_sec = 0 , . tv_nsec = 8333333 } ; // 120Hz
do {
video_driver_sync ( ) ;
nanosleep ( & sleeptime , NULL ) ;
} while ( 1 ) ;
}
2013-06-25 06:14:39 +00:00
#if 0
2013-06-11 07:08:15 +00:00
static Cursor hidecursor ( ) {
Pixmap cursormask ;
XGCValues xgc ;
XColor dummycolour ;
Cursor cursor ;
2013-06-25 06:14:39 +00:00
GC cursor_gc ;
2013-06-11 07:08:15 +00:00
2013-07-06 04:37:13 +00:00
cursormask = XCreatePixmap ( display , win , 1 , 1 , 1 /*depth*/ ) ;
2013-06-11 07:08:15 +00:00
xgc . function = GXclear ;
2013-06-25 06:14:39 +00:00
cursor_gc = XCreateGC ( display , cursormask , GCFunction , & xgc ) ;
2013-06-11 07:08:15 +00:00
XFillRectangle ( display , cursormask , gc , 0 , 0 , 1 , 1 ) ;
dummycolour . pixel = 0 ;
dummycolour . red = 0 ;
dummycolour . flags = 04 ;
2013-06-25 06:14:39 +00:00
cursor = XCreatePixmapCursor ( display , cursormask , cursormask , & dummycolour , & dummycolour , 0 , 0 ) ;
2013-06-11 07:08:15 +00:00
XFreePixmap ( display , cursormask ) ;
2013-06-25 06:14:39 +00:00
XFreeGC ( display , cursor_gc ) ;
2013-06-11 07:08:15 +00:00
return cursor ;
}
2013-06-25 06:14:39 +00:00
# endif
2013-06-11 07:08:15 +00:00
static void parseArgs ( ) {
int i ;
2013-07-06 04:37:13 +00:00
for ( i = 0 ; i < argc ; i + + )
{
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
if ( strstr ( argv [ i ] , " --noshm " ) )
2013-07-06 04:37:13 +00:00
{
doShm = 0 ;
2014-01-23 04:42:34 +00:00
} else
# endif
if ( strstr ( argv [ i ] , " --help " ) | | strstr ( argv [ i ] , " -h " ) ) {
printf ( " %s v%s emulator help : \n " , PACKAGE_NAME , PACKAGE_VERSION ) ;
printf ( " \t Manpage : %s \n " , PACKAGE_MANPAGE ) ;
printf ( " \t Web Resources : %s \n " , WEB_RESOURCES ) ;
exit ( 0 ) ;
} else if ( strstr ( argv [ i ] , " --version " ) | | strstr ( argv [ i ] , " -v " ) ) {
printf ( " %s v%s \n " , PACKAGE_NAME , PACKAGE_VERSION ) ;
exit ( 0 ) ;
2013-07-06 04:37:13 +00:00
}
2014-01-04 22:24:55 +00:00
}
}
static void _destroy_image ( ) {
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2014-01-04 22:24:55 +00:00
if ( doShm )
{
// Detach from X server
if ( ! XShmDetach ( display , & xshminfo ) )
2013-11-25 02:24:00 +00:00
{
2014-01-04 22:24:55 +00:00
fprintf ( stderr , " XShmDetach() failed in video_shutdown() \n " ) ;
2013-11-25 02:24:00 +00:00
}
2014-01-04 22:24:55 +00:00
XDestroyImage ( image ) ;
// Release shared memory.
shmdt ( xshminfo . shmaddr ) ;
shmctl ( xshminfo . shmid , IPC_RMID , 0 ) ;
2014-01-23 04:42:34 +00:00
} else
# endif
2014-01-04 22:24:55 +00:00
{
XDestroyImage ( image ) ;
//free(image->data);
}
}
static void _create_image ( ) {
int pixel_buffer_size = width * height * bitmap_pad ;
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2014-01-04 22:24:55 +00:00
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 " ) ;
}
2014-01-23 04:42:34 +00:00
} else
# endif
{
2014-01-04 22:24:55 +00:00
void * data = malloc ( pixel_buffer_size ) ;
if ( ! data ) {
ERRQUIT ( " no memory for image data! " ) ;
}
2014-06-08 21:14:47 +00:00
LOG ( " WARNING!!! No XShm extension ... creating regular XImage " ) ;
2014-01-04 22:24:55 +00:00
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 ) {
_destroy_image ( ) ;
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 ) ;
2013-06-11 07:08:15 +00:00
}
2014-01-04 22:24:55 +00:00
# endif
XWindowChanges changes = { . width = width , . height = height } ;
XConfigureWindow ( display , win , CWWidth | CWHeight , & changes ) ;
_create_image ( ) ;
_size_hints_set_fixed ( ) ;
2013-06-11 07:08:15 +00:00
}
2014-09-27 18:03:51 +00:00
void video_driver_init ( void * context ) {
2013-07-06 04:37:13 +00:00
XSetWindowAttributes attribs ;
unsigned long attribmask ;
int x , y ; /* window position */
int drawingok ;
//unsigned int display_width, display_height;
XGCValues xgcvalues ;
int valuemask ;
2014-01-04 22:24:55 +00:00
char * window_name = " Apple //ix " ;
2013-07-06 04:37:13 +00:00
char * icon_name = window_name ;
//GC gc;
char * displayname = NULL ;
2014-01-25 22:13:38 +00:00
if ( argv = = NULL ) {
2014-04-26 20:22:19 +00:00
LOG ( " No command line arguments ... " ) ;
argc = 0 ;
} else {
parseArgs ( ) ;
2014-01-25 22:13:38 +00:00
}
2013-07-06 04:37:13 +00:00
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;
2013-06-25 06:14:39 +00:00
/*
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 ) ;
2013-07-06 04:37:13 +00:00
*/
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 + + )
{
2014-01-04 22:24:55 +00:00
if ( ( ( ( uint32_t ) visualinfo . red_mask > > shift ) & 0xff ) = = ( uint32_t ) 0xff )
2013-06-25 06:14:39 +00:00
{
2013-07-06 04:37:13 +00:00
red_shift = shift ;
2013-06-25 06:14:39 +00:00
}
2013-11-24 23:38:31 +00:00
else if ( ( ( ( uint32_t ) visualinfo . green_mask > > shift ) & 0xff ) = = ( uint32_t ) 0xff )
2013-07-06 04:37:13 +00:00
{
green_shift = shift ;
}
2013-11-24 23:38:31 +00:00
else if ( ( ( ( uint32_t ) visualinfo . blue_mask > > shift ) & 0xff ) = = ( uint32_t ) 0xff )
2013-07-06 04:37:13 +00:00
{
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 ) ;
2014-01-04 22:24:55 +00:00
scale = a2_video_mode ;
if ( a2_video_mode = = VIDEO_FULLSCREEN ) {
scale = 1 ; // HACK FIXME FOR NOW ...
}
2013-07-06 04:37:13 +00:00
/* 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 ;
2013-11-25 02:24:00 +00:00
width = SCANWIDTH * scale ;
height = SCANHEIGHT * scale ;
2013-06-25 06:14:39 +00:00
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-07-06 04:37:13 +00:00
/* init MITSHM if we're doing it */
if ( doShm )
{
/* make sure we have it */
doShm = XShmQueryExtension ( display ) ;
}
2014-01-23 04:42:34 +00:00
# endif
2013-07-06 04:37:13 +00:00
displayname = getenv ( " DISPLAY " ) ;
if ( displayname )
{
if ( * displayname ! = ' : ' )
{
printf ( " NOTE: Sound not allowed for remote display \" %s \" . \n " , displayname ) ;
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-07-06 04:37:13 +00:00
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 ) ;
2013-06-25 06:14:39 +00:00
}
2013-07-06 04:37:13 +00:00
doShm = 0 ;
2013-10-06 08:31:58 +00:00
//soundAllowed=0; FIXME TODO enforce this ...
2014-01-23 04:42:34 +00:00
# endif
2013-06-25 06:14:39 +00:00
}
2013-07-06 04:37:13 +00:00
}
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*/ ;
2014-04-26 20:22:19 +00:00
class_hints - > res_name = " apple2ix " ;
2013-07-06 04:37:13 +00:00
class_hints - > res_class = " Apple2 " ;
2014-01-04 22:24:55 +00:00
_size_hints_set_fixed ( ) ;
2013-07-06 04:37:13 +00:00
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 ;
2013-06-25 06:14:39 +00:00
}
2013-07-06 04:37:13 +00:00
}
2014-01-23 04:42:34 +00:00
# ifdef HAVE_X11_SHM
2013-07-06 04:37:13 +00:00
xshmeventtype = XShmGetEventBase ( display ) + ShmCompletion ;
2014-01-23 04:42:34 +00:00
# endif
2013-07-06 04:37:13 +00:00
2014-01-04 22:24:55 +00:00
_create_image ( ) ;
2013-06-11 07:08:15 +00:00
2013-12-29 05:48:42 +00:00
# ifdef KEYPAD_JOYSTICK
int autorepeat_supported = 0 ;
XkbGetDetectableAutoRepeat ( display , & autorepeat_supported ) ;
if ( autorepeat_supported )
{
LOG ( " Xkb Setting detectable autorepeat ... " ) ;
XkbSetDetectableAutoRepeat ( display , true , NULL ) ;
}
# endif
2013-06-11 07:08:15 +00:00
}
2014-09-07 03:04:32 +00:00
void video_driver_shutdown ( void ) {
2014-01-04 22:24:55 +00:00
_destroy_image ( ) ;
2013-06-11 07:08:15 +00:00
}
2014-01-04 22:24:55 +00:00