1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-27 01:41:34 +03:00
Files
fuse/ui/xlib/xdisplay.c
2018-06-11 00:13:40 +02:00

1057 lines
29 KiB
C

/* xdisplay.c: Routines for dealing with drawing the Speccy's screen via Xlib
Copyright (c) 2000-2005 Philip Kendall, Darren Salt, Gergely Szász
Copyright (c) 2015 Stuart Brady
Copyright (c) 2015 Sergio Baldoví
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef HAVE_SIGINFO_H
#include <siginfo.h> /* Needed for psignal on Solaris */
#endif /* #ifdef HAVE_SIGINFO_H */
#ifdef HAVE_X11_EXTENSIONS_XSHM_H
#define X_USE_SHM
#endif /* #ifdef HAVE_X11_EXTENSIONS_XSHM_H */
#ifdef X_USE_SHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif /* #ifdef X_USE_SHM */
#include <libspectrum.h>
#include "display.h"
#include "fuse.h"
#include "keyboard.h"
#include "machine.h"
#include "peripherals/scld.h"
#include "screenshot.h"
#include "settings.h"
#include "xdisplay.h"
#include "xui.h"
#include "ui/scaler/scaler.h"
#include "ui/ui.h"
#include "ui/uidisplay.h"
void xstatusbar_init( int size );
typedef enum {
MSB_RED = 0, /* 0RGB */
LSB_RED, /* 0BGR */
/* ARGB/ABGR RGBA/BGRA ????*/
} redmask_t;
static XImage *image = NULL; /* The image structure to draw the
Speccy's screen on */
static GC gc; /* A graphics context to draw with */
Colormap currentMap; /* The used colormap */
/* The size of a 1x1 image in units of
DISPLAY_ASPECT WIDTH x DISPLAY_SCREEN_HEIGHT
scale * 4, so 2 => 0.5, 6 => 1.5, 4 => 1.0 */
static int image_scale = 1;
/* The height and width of a 1x1 image in pixels */
static int image_width, image_height, scaled_image_w, scaled_image_h;
/* An RGB image of the Spectrum screen; slightly bigger than the real
screen to handle the smoothing filters which read around each pixel 16bpp */
static libspectrum_word
rgb_image[2 * ( DISPLAY_SCREEN_HEIGHT + 4 )][2 * ( DISPLAY_SCREEN_WIDTH + 3 )];
static const int rgb_pitch = 2 * ( DISPLAY_SCREEN_WIDTH + 3 );
/* A scaled copy of the image displayed on the Spectrum's screen */
static libspectrum_word
scaled_image[3 * DISPLAY_SCREEN_HEIGHT][3 * DISPLAY_SCREEN_WIDTH];
static const ptrdiff_t scaled_pitch =
3 * DISPLAY_SCREEN_WIDTH * 2;
/* A scaled copy of the image displayed on the Spectrum's screen */
static libspectrum_word
rgb_image_backup[2 * ( DISPLAY_SCREEN_HEIGHT + 4 )][2 * ( DISPLAY_SCREEN_WIDTH + 3 )];
static unsigned long colours[128];
static int colours_allocated = 0;
/* The current size of the window (in units of DISPLAY_SCREEN_*) */
static int xdisplay_current_size = 1;
static int xdisplay_depth = -1;
static Visual *xdisplay_visual = NULL;
static int xdisplay_bw = -1;
static redmask_t xdisplay_redpos = MSB_RED; /* red_mask 0xff000 ...*/
static int rShift = 16, gShift = 8, bShift = 0;
/* This is a rule of thumb for the maximum number of rects that can be updated
each frame. If more are generated we just update the whole screen */
typedef struct {
int x; int y; int w; int h;
} X_Rect;
#define MAX_UPDATE_RECT 300
static X_Rect updated_rects[MAX_UPDATE_RECT];
static int num_rects = 0;
static libspectrum_byte xdisplay_force_full_refresh = 1;
#ifdef X_USE_SHM
static XShmSegmentInfo shm_info;
int shm_eventtype;
static int try_shm( void );
static int get_shm_id( const int size );
#endif /* #ifdef X_USE_SHM */
static int shm_used = 0;
typedef void xdisplay_update_rect_t( int x, int y, int w, int h );
static xdisplay_update_rect_t *xdisplay_update_rect;
static xdisplay_update_rect_t xdisplay_update_rect_noscale;
static xdisplay_update_rect_t xdisplay_update_rect_scale;
static int xdisplay_find_visual( void );
static int xdisplay_allocate_colours4( void );
static int xdisplay_allocate_colours8( void );
static int xdisplay_allocate_gc( Window window, GC *new_gc );
static int xdisplay_allocate_image( void );
static void register_scalers( void );
static void xdisplay_destroy_image( void );
static void xdisplay_catch_signal( int sig );
typedef void xdisplay_putpixel_t( int x, int y, libspectrum_word *color );
static xdisplay_putpixel_t *xdisplay_putpixel;
static xdisplay_putpixel_t xdisplay_putpixel_4;
static xdisplay_putpixel_t xdisplay_putpixel_8;
static xdisplay_putpixel_t xdisplay_putpixel_15;
static xdisplay_putpixel_t xdisplay_putpixel_16;
static xdisplay_putpixel_t xdisplay_putpixel_24;
#include "ui/xlib/xpixmaps.c"
void xstatusbar_overlay( void );
static libspectrum_word pal_colour[16] = {
0x0000, 0x0017, 0xb800, 0xb817, 0x05e0, 0x05f7, 0xbde0, 0xbdf7,
0x0000, 0x001f, 0xf800, 0xf81f, 0x07e0, 0x07ff, 0xffe0, 0xffff,
};
static libspectrum_word pal_grey[16] = {
0x0000, 0x39c7, 0x18a3, 0x526a, 0x738e, 0xad55, 0x8430, 0xbdf7,
0x0000, 0x4a69, 0x20e4, 0x6b4d, 0x94b2, 0xdf1b, 0xb596, 0xffff,
};
static int rgb_for_4[] = {
0x00, 0x00, 0x00,
0x00, 0x00, 0xBF,
0xBF, 0x00, 0x00,
0xBF, 0x00, 0xBF,
0x00, 0xBF, 0x00,
0x00, 0xBF, 0xBF,
0xBF, 0xBF, 0x00,
0xBF, 0xBF, 0xBF,
0x00, 0x00, 0x00,
0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00,
0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00,
0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0xFF
};
int
xdisplay_init( void )
{
if( xdisplay_find_visual() ) return 1;
if( xdisplay_depth == 4 && xdisplay_allocate_colours4() ) return 1;
if( xdisplay_depth == 8 && xdisplay_allocate_colours8() ) return 1;
if( xdisplay_allocate_gc( xui_mainWindow,&gc ) ) return 1;
if( xdisplay_allocate_image() ) return 1;
ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_INACTIVE );
return 0;
}
static int
xdisplay_find_visual( void )
{
XVisualInfo visual_tmpl;
XVisualInfo *vis;
int nvis, i;
int sel_v = -1;
int sel_v_depth = -1;
int sel_v_class = -1;
visual_tmpl.screen = xui_screenNum;
vis = XGetVisualInfo( display,
VisualScreenMask,
&visual_tmpl, &nvis );
if( vis != NULL ) {
for( i = 0; i < nvis; i++ ) {
/*
* Save the visual index and its depth, if this is the first
* truecolor visual, or a visual that is 'preferred' over the
* previous 'best' visual.
*/
if( ( sel_v_depth == -1 && vis[i].depth >= 4 ) || /* depth >= 4 */
( vis[i].depth > sel_v_depth &&
vis[i].depth <= 16 ) || /* depth up to 16 */
( vis[i].depth <= 8 && vis[i].depth == sel_v_depth &&
vis[i].class != sel_v_class &&
vis[i].class == PseudoColor ) || /* indexed changable palette */
( vis[i].depth > 8 && vis[i].depth == sel_v_depth &&
vis[i].class != sel_v_class &&
vis[i].class == TrueColor ) /* decomposed constatant colors */
) {
sel_v = i;
sel_v_depth = vis[i].depth;
sel_v_class = vis[i].class;
}
}
if ( sel_v != -1 ) {
xdisplay_visual = vis[sel_v].visual;
xdisplay_depth = vis[sel_v].depth;
}
XFree(vis);
}
return sel_v == -1 ? 1 : 0;
}
static void
xdisplay_putpixel_4( int x, int y, libspectrum_word *colour)
{
XPutPixel( image, x, y, *colour );
}
static void
xdisplay_putpixel_8( int x, int y, libspectrum_word *colour)
{
unsigned long c = *colour;
c = ( c & 0x1f ) * 3 / 31 +
( ( ( ( c >> 5 ) & 0x3f ) * 7 / 63 ) << 2 ) +
( ( ( c >> 11 ) * 3 / 31 ) << 5 );
XPutPixel( image, x, y, colours[ c ] );
}
static void
xdisplay_putpixel_15( int x, int y, libspectrum_word *colour)
{
unsigned long c = *colour;
c = ( c & 0x1f ) + ( ( c >> 1 ) & 0xffe0 );
XPutPixel( image, x, y, c );
}
static void
xdisplay_putpixel_16( int x, int y, libspectrum_word *colour)
{
XPutPixel( image, x, y, *colour );
}
static void
xdisplay_putpixel_24( int x, int y, libspectrum_word *colour)
{
unsigned long c = *colour;
c = ( ( ( c & 0x1f ) * 255 / 31 ) << bShift ) +
( ( ( ( c >> 5 ) & 0x3f ) * 255 / 63 ) << gShift ) +
( ( ( c >> 11 ) * 255 / 31 ) << rShift );
XPutPixel( image, x, y, c );
}
static int
xdisplay_alloc_colour( Colormap *map, XColor *colour )
{
for(;;) {
if( XAllocColor( display, *map, colour ) )
return 0;
fprintf(stderr,"%s: XAllocColor failed (%04x %04x %04x)\n", fuse_progname,
colour->red, colour->green, colour->blue );
if( *map == DefaultColormap( display, xui_screenNum ) ) {
fprintf( stderr,"%s: switching to private colour map\n", fuse_progname );
*map = XCopyColormapAndFree( display, *map );
XSetWindowColormap( display, xui_mainWindow, *map );
/* Need to repeat the failed allocation */
} else {
return 1;
}
}
return 0;
}
static int
xdisplay_allocate_colours4( void )
{
XColor c;
int i;
currentMap = DefaultColormap( display, xui_screenNum );
if( colours_allocated ) { /* free it */
XFreeColors( display, currentMap, colours, 16, 0x00 );
colours_allocated = 0;
}
if( settings_current.bw_tv ) {
for( i=0; i<16; i++ ) { /* grey */
c.red = c.green = c.blue =
rgb_for_4[i * 3 ] * 19595 / 255 +
rgb_for_4[i * 3 + 1] * 38469 / 255 +
rgb_for_4[i * 3 + 2] * 7471 / 255;
if( xdisplay_alloc_colour( &currentMap, &c ) )
return 1;
colours[i] = pal_grey[i] = c.pixel;
}
} else {
for( i=0; i<16; i++ ) { /* rgb */
c.red = rgb_for_4[i * 3 ] * 65535 / 255;
c.green = rgb_for_4[i * 3 + 1] * 65535 / 255;
c.blue = rgb_for_4[i * 3 + 2] * 65535 / 255;
if( xdisplay_alloc_colour( &currentMap, &c ) )
return 1;
colours[i] = pal_colour[i] = c.pixel;
}
}
colours_allocated = 1;
return 0;
}
static int
xdisplay_allocate_colours8( void )
{
XColor c;
int i, r, g, b;
currentMap = DefaultColormap( display, xui_screenNum );
if( colours_allocated ) { /* free it */
XFreeColors( display, currentMap, colours, 128, 0x00 );
colours_allocated = 0;
}
i = 0;
for( r=0; r<4; r++ ) /* rgb232 => 128 */
for( g=0; g<8; g++ )
for( b=0; b<4; b++ ) {
if( settings_current.bw_tv ) {
c.red = c.green = c.blue = r * 19595 / 3 +
g * 38469 / 7 +
b * 7471 / 3;
} else {
c.red = r * 65535 / 3;
c.green = g * 65535 / 7;
c.blue = b * 65535 / 3;
}
if( xdisplay_alloc_colour( &currentMap, &c ) )
return 1;
colours[i++] = c.pixel;
}
colours_allocated = 1;
return 0;
}
static int
xdisplay_allocate_gc( Window window, GC *new_gc )
{
unsigned valuemask=0;
XGCValues values;
*new_gc = XCreateGC( display, window, valuemask, &values );
return 0;
}
static int
xdisplay_allocate_image( void )
{
struct sigaction handler;
handler.sa_handler = xdisplay_catch_signal;
sigemptyset( &handler.sa_mask );
handler.sa_flags = 0;
sigaction( SIGINT, &handler, NULL );
#ifdef X_USE_SHM
shm_used = try_shm();
#endif /* #ifdef X_USE_SHM */
/* If SHM isn't available, or we're not using it for some reason,
just get a normal image */
if( !shm_used ) {
image = XCreateImage( display, xdisplay_visual,
xdisplay_depth, ZPixmap, 0, NULL,
3 * DISPLAY_ASPECT_WIDTH,
3 * DISPLAY_SCREEN_HEIGHT + 3 * PIXMAPS_H, 8, 0 );
/*
we allocate extra space after the screen for status bar icons
status bar icons total width always smaller than 3xDISPLAY_ASPECT_WIDTH
*/
if(!image) {
fprintf(stderr,"%s: couldn't create image\n",fuse_progname);
return 1;
}
if( ( image->data = malloc( image->bytes_per_line *
image->height ) ) == NULL ) {
fprintf(stderr, "%s: out of memory for image data\n", fuse_progname);
return 1;
}
}
if( image ) {
switch( image->red_mask ) {
case 0xff0000: /* 24bit/32bit 0RGB */
rShift = 16, gShift = 8, bShift = 0;
break;
case 0x0000ff: /* 24bit/32bit 0BGR */
rShift = 0, gShift = 8, bShift = 16;
break;
case 0xff000000: /* 24bit/32bit RGB0 */
rShift = 24, gShift = 16, bShift = 8;
break;
case 0x0000ff00: /* 24bit/32bit BGR0 */
rShift = 8, gShift = 16, bShift = 24;
break;
case 0xf800: /* 16 RGB */
case 0x7c00: /* 15 RGB */
xdisplay_redpos = MSB_RED;
break;
case 0x001f: /* 16/15 BGR */
xdisplay_redpos = LSB_RED;
break;
}
}
return 0;
}
#ifdef X_USE_SHM
static int
try_shm( void )
{
int id;
int error;
if( !XShmQueryExtension( display ) ) return 0;
shm_eventtype = XShmGetEventBase( display ) + ShmCompletion;
image = XShmCreateImage( display, xdisplay_visual,
xdisplay_depth, ZPixmap,
NULL, &shm_info,
3 * DISPLAY_ASPECT_WIDTH,
3 * DISPLAY_SCREEN_HEIGHT + 3 * PIXMAPS_H);
/*
we allocate extra space after the screen for status bar icons
status bar icons total width always smaller than 3xDISPLAY_ASPECT_WIDTH
*/
if( !image ) return 0;
/* Get an SHM to work with */
id = get_shm_id( image->bytes_per_line * image->height );
if( id == -1 ) return 0;
/* Attempt to attach to the shared memory */
shm_info.shmid = id;
image->data = shm_info.shmaddr = shmat( id, 0, 0 );
/* If we couldn't attach, remove the chunk and give up */
if( image->data == (void*)-1 ) {
shmctl( id, IPC_RMID, NULL );
image->data = NULL;
return 0;
}
/* This may generate an X error */
xerror_error = 0; xerror_expecting = 1;
error = !XShmAttach( display, &shm_info );
/* Force any X errors to occur before we disable traps */
XSync( display, False );
xerror_expecting = 0;
/* If we caught an error, don't use SHM */
if( error || xerror_error ) {
shmctl( id, IPC_RMID, NULL );
shmdt( image->data ); image->data = NULL;
return 0;
}
/* Now flag the chunk for deletion; this will take effect when
everything has detached from it */
shmctl( id, IPC_RMID, NULL );
return 1;
}
/* Get an SHM ID; also attempt to reclaim any stale chunks we find */
static int
get_shm_id( const int size )
{
key_t key = 'F' << 24 | 'u' << 16 | 's' << 8 | 'e';
struct shmid_ds shm;
int id;
int pollution = 5;
do {
/* See if a chunk already exists with this key */
id = shmget( key, size, 0777 );
/* If the chunk didn't already exist, try and create one for our
use */
if( id == -1 ) {
id = shmget( key, size, IPC_CREAT | 0777 );
continue; /* And then jump to the end of the loop */
}
/* If the chunk already exists, try and get information about it */
if( shmctl( id, IPC_STAT, &shm ) != -1 ) {
/* If something's actively using this chunk, try another key */
if( shm.shm_nattch ) {
key++;
} else { /* Otherwise, attempt to remove the chunk */
/* If we couldn't remove that chunk, try another key. If we
could, just try again */
if( shmctl( id, IPC_RMID, NULL ) != 0 ) key++;
}
} else { /* Couldn't get info on the chunk, so try next key */
key++;
}
id = -1; /* To prevent early exit from loop */
} while( id == -1 && --pollution );
return id;
}
#endif /* #ifdef X_USE_SHM */
int
uidisplay_init( int width, int height )
{
image_width = width;
image_height = height;
if( !scaler_is_supported( current_scaler ) ) {
if( machine_current->timex )
scaler_select_scaler( SCALER_HALFSKIP );
else
scaler_select_scaler( SCALER_NORMAL );
}
register_scalers();
display_ui_initialised = 1;
display_refresh_all();
return 0;
}
static void
resize_window( int w, int h )
{
if( xdisplay_current_size != w / DISPLAY_ASPECT_WIDTH ) {
XResizeWindow( display, xui_mainWindow, w, h );
xdisplay_current_size = w / DISPLAY_ASPECT_WIDTH;
}
}
static void
register_scalers( void )
{
int f = -1;
scaler_register_clear();
scaler_select_bitformat( 565 ); /* 16bit always */
if( xdisplay_depth == 4 ) {
scaler_register( SCALER_NORMAL );
if( machine_current->timex ) {
scaler_register( SCALER_HALFSKIP );
scaler_register( SCALER_TIMEX1_5X );
} else {
scaler_register( SCALER_DOUBLESIZE );
scaler_register( SCALER_ADVMAME2X );
scaler_register( SCALER_TRIPLESIZE );
scaler_register( SCALER_ADVMAME3X );
}
} else {
scaler_register( SCALER_NORMAL );
scaler_register( SCALER_PALTV );
if( machine_current->timex ) {
scaler_register( SCALER_HALF );
scaler_register( SCALER_HALFSKIP );
scaler_register( SCALER_TIMEXTV );
scaler_register( SCALER_TIMEX1_5X );
} else {
scaler_register( SCALER_DOUBLESIZE );
scaler_register( SCALER_2XSAI );
scaler_register( SCALER_SUPER2XSAI );
scaler_register( SCALER_SUPEREAGLE );
scaler_register( SCALER_ADVMAME2X );
scaler_register( SCALER_TV2X );
scaler_register( SCALER_DOTMATRIX );
scaler_register( SCALER_PALTV2X );
scaler_register( SCALER_HQ2X );
scaler_register( SCALER_TRIPLESIZE );
scaler_register( SCALER_ADVMAME3X );
scaler_register( SCALER_TV3X );
scaler_register( SCALER_PALTV3X );
scaler_register( SCALER_HQ3X );
}
}
if( current_scaler != SCALER_NUM )
f = 4.0 * scaler_get_scaling_factor( current_scaler ) *
( machine_current->timex ? 2 : 1 );
if( scaler_is_supported( current_scaler ) &&
( xdisplay_current_size * 4 == f ) ) {
uidisplay_hotswap_gfx_mode();
} else {
switch( xdisplay_current_size ) {
case 1:
scaler_select_scaler( machine_current->timex ? SCALER_HALF : SCALER_NORMAL );
break;
case 2:
scaler_select_scaler( machine_current->timex ? SCALER_NORMAL : SCALER_DOUBLESIZE );
break;
case 3:
scaler_select_scaler( machine_current->timex ? SCALER_TIMEX1_5X : SCALER_TRIPLESIZE );
break;
}
}
}
int
xdisplay_configure_notify( int width, int height )
{
int size;
/* If we're the same size as before, nothing special needed */
size = width / DISPLAY_ASPECT_WIDTH;
if( size != height / DISPLAY_SCREEN_HEIGHT ) { /* out of aspect */
if( size > height / DISPLAY_SCREEN_HEIGHT ) {
size = height / DISPLAY_SCREEN_HEIGHT;
width = size * DISPLAY_ASPECT_WIDTH;
} else {
height = size * DISPLAY_SCREEN_HEIGHT;
}
xdisplay_current_size = 0; /* force resize */
resize_window( width, height );
} else if( size == xdisplay_current_size ) {
return 0;
}
/* Else set ourselves to the new height */
xdisplay_current_size = size;
/* Get a new scaler */
register_scalers();
return 0;
}
static void
xdisplay_update_rect_noscale( int x, int y, int w, int h )
{
int yy, xx;
/* Call putpixel multiple times */
for( yy = y; yy < y + h; yy++ )
for( xx = x; xx < x + w; xx++ )
xdisplay_putpixel( xx, yy, &rgb_image[yy + 2][xx + 1] );
/* Blit to the real screen at the frame end end */
xdisplay_area( x, y, w, h );
}
static void
xdisplay_update_rect_scale( int x, int y, int w, int h )
{
int yy = y, xx = x;
y = y * image_scale >> 2;
x = x * image_scale >> 2;
scaler_proc16(
(libspectrum_byte *)&(rgb_image[yy + 2][xx + 1]),
rgb_pitch * sizeof(rgb_image[0][0]),
(libspectrum_byte *)&(scaled_image[y][x]),
scaled_pitch,
w, h
);
w = w * image_scale >> 2;
h = h * image_scale >> 2;
/* Call putpixel multiple times */
for( yy = y; yy < y + h; yy++ )
for( xx = x; xx < x + w; xx++ )
xdisplay_putpixel( xx, yy, &scaled_image[yy][xx] );
/* Blit to the real screen */
xdisplay_area( x, y, w, h );
}
void
uidisplay_frame_end( void )
{
X_Rect *r, *last_rect;
/* Force a full redraw if requested */
if ( xdisplay_force_full_refresh ) {
num_rects = 1;
updated_rects[0].x = 0;
updated_rects[0].y = 0;
updated_rects[0].w = image_width;
updated_rects[0].h = image_height;
}
if ( !( ui_widget_level >= 0 ) && num_rects == 0 && !status_updated ) return;
last_rect = updated_rects + num_rects;
for( r = updated_rects; r != last_rect; r++ )
xdisplay_update_rect( r->x, r->y, r->w, r->h );
if ( settings_current.statusbar )
xstatusbar_overlay();
num_rects = 0;
xdisplay_force_full_refresh = 0;
}
void
uidisplay_frame_save( void )
{
memcpy( rgb_image_backup, rgb_image, sizeof( rgb_image ) );
}
void
uidisplay_frame_restore( void )
{
memcpy( rgb_image, rgb_image_backup, sizeof( rgb_image ) );
xdisplay_update_rect( 0, 0, image_width, image_height );
}
void
uidisplay_area( int x, int y, int w, int h )
{
if ( xdisplay_force_full_refresh )
return;
if( num_rects == MAX_UPDATE_RECT ) {
xdisplay_force_full_refresh = 1;
return;
}
/* Extend the dirty region by 1 pixel for scalers
that "smear" the screen, e.g. 2xSAI */
if( scaler_flags & SCALER_FLAGS_EXPAND )
scaler_expander( &x, &y, &w, &h, image_width, image_height );
updated_rects[num_rects].x = x;
updated_rects[num_rects].y = y;
updated_rects[num_rects].w = w;
updated_rects[num_rects].h = h;
num_rects++;
}
void
xdisplay_area( int x, int y, int w, int h )
{
/* e.g. dwm first expose with too big w and h */
if( x + w > 3 * DISPLAY_ASPECT_WIDTH )
w = 3 * DISPLAY_ASPECT_WIDTH - x;
if( y + h > 3 * DISPLAY_SCREEN_HEIGHT )
h = 3 * DISPLAY_SCREEN_HEIGHT - y;
if( shm_used ) {
#ifdef X_USE_SHM
XShmPutImage( display, xui_mainWindow, gc, image, x, y, x, y, w, h, True );
/* FIXME: should wait for an ShmCompletion event here */
#endif /* #ifdef X_USE_SHM */
} else {
XPutImage( display, xui_mainWindow, gc, image, x, y, x, y, w, h );
}
}
static void
xdisplay_destroy_image(void)
{
/* Free the XImage used to store screen data; also frees the malloc'd
data */
#ifdef X_USE_SHM
if( shm_used ) {
XShmDetach( display, &shm_info );
shmdt( shm_info.shmaddr );
image->data = NULL;
shm_used = 0;
}
#endif
if( image ) XDestroyImage( image ); image = NULL;
}
static void
xdisplay_setup_rgb_putpixel( void )
{
switch( xdisplay_depth ) {
case 4:
xdisplay_putpixel = xdisplay_putpixel_4;
break;
case 8:
xdisplay_putpixel = xdisplay_putpixel_8;
break;
case 15:
xdisplay_putpixel = xdisplay_putpixel_15;
break;
case 16:
xdisplay_putpixel = xdisplay_putpixel_16;
break;
case 24:
case 32:
xdisplay_putpixel = xdisplay_putpixel_24;
break;
}
resize_window( scaled_image_w, scaled_image_h );
}
int
uidisplay_hotswap_gfx_mode( void )
{
image_scale = 4.0 * scaler_get_scaling_factor( current_scaler );
scaled_image_w = image_width * image_scale >> 2;
scaled_image_h = image_height * image_scale >> 2;
if( current_scaler == SCALER_NORMAL )
xdisplay_update_rect = xdisplay_update_rect_noscale;
else
xdisplay_update_rect = xdisplay_update_rect_scale;
xdisplay_force_full_refresh = 1;
if( settings_current.bw_tv != xdisplay_bw ) {
xdisplay_bw = settings_current.bw_tv;
if( xdisplay_depth == 4 )
xdisplay_allocate_colours4();
else if( xdisplay_depth == 8 )
xdisplay_allocate_colours8();
}
xdisplay_setup_rgb_putpixel();
xstatusbar_init( xdisplay_current_size );
display_refresh_all();
return 0;
}
int
uidisplay_end( void )
{
display_ui_initialised = 0;
return 0;
}
static void
xdisplay_catch_signal( int sig )
{
xdisplay_end();
psignal( sig, fuse_progname );
exit( 1 );
}
int
xdisplay_end( void )
{
xdisplay_destroy_image();
/* Free the allocated GC */
if( gc ) {
XFreeGC( display, gc );
gc = 0;
}
return 0;
}
/* Set one pixel in the display */
void
uidisplay_putpixel( int x, int y, int colour )
{
libspectrum_word pc = settings_current.bw_tv ? pal_grey[ colour ] :
pal_colour[ colour ];
if( machine_current->timex ) {
x <<= 1; y <<= 1;
rgb_image[y + 2][x + 1] = pc;
rgb_image[y + 2][x + 2] = pc;
rgb_image[y + 3][x + 1] = pc;
rgb_image[y + 3][x + 2] = pc;
} else {
rgb_image[y + 2][x + 1] = pc;
}
}
/* Print the 8 pixels in `data' using ink colour `ink' and paper
colour `paper' to the screen at ( (8*x) , y ) */
void
uidisplay_plot8( int x, int y, libspectrum_byte data,
libspectrum_byte ink, libspectrum_byte paper )
{
libspectrum_word *dest;
libspectrum_word pi = settings_current.bw_tv ? pal_grey[ ink ] :
pal_colour[ ink ];
libspectrum_word pp = settings_current.bw_tv ? pal_grey[ paper ] :
pal_colour[ paper ];
if( machine_current->timex ) {
x <<= 4; y <<= 1;
dest = &(rgb_image[y + 2][x + 1]);
*(dest + rgb_pitch) = *dest = ( data & 0x80 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x80 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x40 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x40 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x20 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x20 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x10 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x10 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x08 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x08 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x04 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x04 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x02 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x02 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x01 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x01 ) ? pi : pp;
} else {
x <<= 3;
dest = &(rgb_image[y + 2][x + 1]);
*(dest++) = ( data & 0x80 ) ? pi : pp;
*(dest++) = ( data & 0x40 ) ? pi : pp;
*(dest++) = ( data & 0x20 ) ? pi : pp;
*(dest++) = ( data & 0x10 ) ? pi : pp;
*(dest++) = ( data & 0x08 ) ? pi : pp;
*(dest++) = ( data & 0x04 ) ? pi : pp;
*(dest++) = ( data & 0x02 ) ? pi : pp;
*dest = ( data & 0x01 ) ? pi : pp;
}
}
/* Print the 16 pixels in `data' using ink colour `ink' and paper
colour `paper' to the screen at ( (16*x) , y ) */
void
uidisplay_plot16( int x, int y, libspectrum_word data,
libspectrum_byte ink, libspectrum_byte paper )
{
libspectrum_word *dest;
libspectrum_word pi = settings_current.bw_tv ? pal_grey[ ink ] :
pal_colour[ ink ];
libspectrum_word pp = settings_current.bw_tv ? pal_grey[ paper ] :
pal_colour[ paper ];
x <<= 4; y <<= 1;
dest = &(rgb_image[y + 2][x + 1]);
*(dest + rgb_pitch) = *dest = ( data & 0x8000 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x4000 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x2000 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x1000 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0800 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0400 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0200 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0100 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0080 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0040 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0020 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0010 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0008 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0004 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0002 ) ? pi : pp; dest++;
*(dest + rgb_pitch) = *dest = ( data & 0x0001 ) ? pi : pp;
}
int
ui_statusbar_update( ui_statusbar_item item, ui_statusbar_state state )
{
switch( item ) {
case UI_STATUSBAR_ITEM_DISK:
pixmap_disk_state = state;
status_updated = 1;
return 0;
case UI_STATUSBAR_ITEM_PAUSED:
/* We don't support pausing this version of Fuse */
return 0;
case UI_STATUSBAR_ITEM_TAPE:
pixmap_tape_state = state;
status_updated = 1;
return 0;
case UI_STATUSBAR_ITEM_MICRODRIVE:
pixmap_mdr_state = state;
status_updated = 1;
return 0;
case UI_STATUSBAR_ITEM_MOUSE:
/* We don't support showing a grab icon */
return 0;
}
ui_error( UI_ERROR_ERROR, "Attempt to update unknown statusbar item %d",
item );
return 1;
}
int
ui_statusbar_update_speed( float speed )
{
char *list[2];
char buffer[16];
XTextProperty text;
list[0] = buffer;
list[1] = 0;
snprintf( buffer, 16, "Fuse - %4.0f%%", speed );
XStringListToTextProperty( list, 1, &text);
XSetWMName( display, xui_mainWindow, &text );
XFree( text.value );
return 0;
}