mirror of
https://git.code.sf.net/p/fuse-emulator/fuse
synced 2026-01-28 14:20:54 +03:00
474 lines
12 KiB
C
474 lines
12 KiB
C
/* widget.c: Simple dialog boxes for all user interfaces.
|
|
Copyright (c) 2001-2003 Matan Ziv-Av, Philip Kendall, Russell Marks
|
|
|
|
$Id$
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
Author contact information:
|
|
|
|
E-mail: pak21-fuse@srcf.ucam.org
|
|
Postal address: 15 Crescent Road, Wokingham, Berks, RG40 2DB, England
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "fuse.h"
|
|
#include "display.h"
|
|
#include "machine.h"
|
|
#include "ui/uidisplay.h"
|
|
#include "keyboard.h"
|
|
#include "options.h"
|
|
#include "screenshot.h"
|
|
#include "timer.h"
|
|
#include "widget_internals.h"
|
|
|
|
static void printchar(int x, int y, int col, int ch);
|
|
static void widget_putpixel( int x, int y, int colour );
|
|
|
|
static char widget_font[768];
|
|
|
|
/* The current widget keyhandler */
|
|
widget_keyhandler_fn widget_keyhandler;
|
|
|
|
/* The data used for recursive widgets */
|
|
typedef struct widget_recurse_t {
|
|
|
|
widget_type type; /* Which type of widget are we? */
|
|
void *data; /* What data were we passed? */
|
|
|
|
int finished; /* Have we finished this widget yet? */
|
|
|
|
} widget_recurse_t;
|
|
|
|
static widget_recurse_t widget_return[10]; /* The stack to recurse on */
|
|
|
|
/* The settings used whilst playing with an options dialog box */
|
|
settings_info widget_options_settings;
|
|
|
|
static int widget_read_font( const char *filename, size_t offset )
|
|
{
|
|
int fd; struct stat file_info; unsigned char *buffer;
|
|
|
|
fd = machine_find_rom( filename );
|
|
if( fd == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "couldn't find ROM '%s'", filename );
|
|
return 1;
|
|
}
|
|
|
|
if( fstat( fd, &file_info) ) {
|
|
ui_error( UI_ERROR_ERROR, "Couldn't stat '%s': %s", filename,
|
|
strerror( errno ) );
|
|
close(fd);
|
|
return errno;
|
|
}
|
|
|
|
buffer = mmap( 0, file_info.st_size, PROT_READ, MAP_SHARED, fd, 0 );
|
|
if( buffer == (void*)-1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Couldn't mmap '%s': %s", filename,
|
|
strerror( errno ) );
|
|
close(fd);
|
|
return errno;
|
|
}
|
|
|
|
if( close(fd) ) {
|
|
ui_error( UI_ERROR_ERROR, "Couldn't close '%s': %s", filename,
|
|
strerror( errno ) );
|
|
munmap( buffer, file_info.st_size );
|
|
return errno;
|
|
}
|
|
|
|
memcpy( widget_font, buffer+offset-1, 768 );
|
|
|
|
if( munmap( buffer, file_info.st_size ) == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Couldn't munmap '%s': %s", filename,
|
|
strerror( errno ) );
|
|
return errno;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void printchar(int x, int y, int col, int ch) {
|
|
|
|
int mx, my;
|
|
|
|
if((ch<32)||(ch>127)) ch=33;
|
|
ch-=32;
|
|
|
|
for(my=0; my<8; my++) {
|
|
int b;
|
|
b=widget_font[ch*8+my];
|
|
for(mx=0; mx<8; mx++) {
|
|
if( b & 0x80 ) widget_putpixel( mx + 8 * x, my + 8 * y, col );
|
|
b<<=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void widget_printstring(int x, int y, int col, const char *s)
|
|
{
|
|
int i;
|
|
i=0;
|
|
if(s) {
|
|
while((x<32) && s[i]) {
|
|
printchar(x,y,col,s[i]);
|
|
i++;
|
|
x++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void widget_rectangle( int x, int y, int w, int h, int col )
|
|
{
|
|
int mx, my;
|
|
|
|
for( my = 0; my < h; my++ )
|
|
for( mx = 0; mx < w; mx++ )
|
|
widget_putpixel( x + mx, y + my, col );
|
|
}
|
|
|
|
/* Force screen lines y to (y+h) inclusive to be redrawn */
|
|
void
|
|
widget_display_lines( int y, int h )
|
|
{
|
|
int scale = machine_current->timex ? 2 : 1;
|
|
|
|
uidisplay_area( 0, scale * ( DISPLAY_BORDER_HEIGHT + 8 * y ),
|
|
scale * DISPLAY_ASPECT_WIDTH, scale * 8 * h );
|
|
uidisplay_frame_end();
|
|
}
|
|
|
|
/* Global initialisation/end routines */
|
|
|
|
int widget_init( void )
|
|
{
|
|
int error;
|
|
|
|
error = widget_read_font( "48.rom", 15617 );
|
|
if( error ) return error;
|
|
|
|
widget_filenames = NULL;
|
|
widget_numfiles = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int widget_end( void )
|
|
{
|
|
size_t i;
|
|
|
|
if( widget_filenames ) {
|
|
for( i=0; i<widget_numfiles; i++) {
|
|
free( widget_filenames[i]->name );
|
|
free( widget_filenames[i] );
|
|
}
|
|
free( widget_filenames );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* General widget routine */
|
|
|
|
/* We don't start in a widget */
|
|
int widget_level = -1;
|
|
|
|
int widget_do( widget_type which, void *data )
|
|
{
|
|
int error;
|
|
|
|
/* If we don't have a UI yet, we can't output widgets */
|
|
if( !display_ui_initialised ) return 1;
|
|
|
|
/* We're now one widget level deeper */
|
|
widget_level++;
|
|
|
|
/* If we're the top-level widget, save the screen and set up the timer */
|
|
if( ! widget_level ) {
|
|
|
|
#ifdef USE_LIBPNG
|
|
error = screenshot_save(); if( error ) { widget_level--; return error; }
|
|
#endif /* #ifdef USE_LIBPNG */
|
|
|
|
error = widget_timer_init(); if( error ) { widget_level--; return error; }
|
|
}
|
|
|
|
/* Store what type of widget we are and what data we were given */
|
|
widget_return[widget_level].type = which;
|
|
widget_return[widget_level].data = data;
|
|
|
|
/* Draw this widget */
|
|
error = widget_data[ which ].draw( data );
|
|
|
|
/* Set up the keyhandler for this widget */
|
|
widget_keyhandler = widget_data[which].keyhandler;
|
|
|
|
/* Process this widget until it returns */
|
|
widget_return[widget_level].finished = 0;
|
|
while( ! widget_return[widget_level].finished ) {
|
|
|
|
/* Go to sleep for a bit */
|
|
pause();
|
|
|
|
/* Process any events */
|
|
ui_event();
|
|
}
|
|
|
|
/* Do any post-widget processing if it exists */
|
|
if( widget_data[which].finish ) {
|
|
widget_data[which].finish( widget_return[widget_level].finished );
|
|
}
|
|
|
|
/* Now return to the previous widget level */
|
|
widget_level--;
|
|
|
|
if( widget_level >= 0 ) {
|
|
|
|
/* If we're going back to another widget, set up its keyhandler and
|
|
draw it again, unless it's already finished */
|
|
if( ! widget_return[widget_level].finished ) {
|
|
widget_keyhandler =
|
|
widget_data[ widget_return[widget_level].type ].keyhandler;
|
|
widget_data[ widget_return[widget_level].type ].draw(
|
|
widget_return[widget_level].data
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Restore the old keyboard handler */
|
|
error = widget_timer_end();
|
|
if( error ) return error;
|
|
|
|
/* Refresh the Spectrum's display, including the border */
|
|
display_refresh_all();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* End the currently running widget */
|
|
int
|
|
widget_end_widget( widget_finish_state state )
|
|
{
|
|
widget_return[ widget_level ].finished = state;
|
|
return 0;
|
|
}
|
|
|
|
/* End all currently running widgets */
|
|
int widget_end_all( widget_finish_state state )
|
|
{
|
|
int i;
|
|
|
|
for( i=0; i<=widget_level; i++ )
|
|
widget_return[i].finished = state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Widget timer routines */
|
|
|
|
static struct sigaction widget_timer_old_handler;
|
|
static struct itimerval widget_timer_old_timer;
|
|
|
|
static void widget_timer_signal( int signo );
|
|
|
|
int widget_timer_init( void )
|
|
{
|
|
struct sigaction handler;
|
|
struct itimerval timer;
|
|
|
|
handler.sa_handler = widget_timer_signal;
|
|
sigemptyset( &handler.sa_mask );
|
|
handler.sa_flags = 0;
|
|
sigaction( SIGALRM, &handler, &widget_timer_old_handler );
|
|
|
|
timer.it_interval.tv_sec = 0;
|
|
timer.it_interval.tv_usec = 100000UL;
|
|
timer.it_value.tv_sec = 0;
|
|
timer.it_value.tv_usec = 100000UL;
|
|
setitimer( ITIMER_REAL, &timer, &widget_timer_old_timer );
|
|
|
|
/* FIXME: Is this really necessary? Without this on Solaris, we
|
|
often get called such taht widget_timer_old_timer.it_value is zero
|
|
which then means we die when we reactivate this timer as it is
|
|
disabled. Reread Stevens. */
|
|
if( widget_timer_old_timer.it_value.tv_sec == 0 &&
|
|
widget_timer_old_timer.it_value.tv_usec == 0 )
|
|
{
|
|
widget_timer_old_timer.it_value.tv_sec =
|
|
widget_timer_old_timer.it_interval.tv_sec;
|
|
|
|
widget_timer_old_timer.it_value.tv_usec =
|
|
widget_timer_old_timer.it_interval.tv_usec;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The signal handler: just wake up */
|
|
static void widget_timer_signal( int signo GCC_UNUSED )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int widget_timer_end( void )
|
|
{
|
|
/* Restore the old timer */
|
|
setitimer( ITIMER_REAL, &widget_timer_old_timer, NULL);
|
|
|
|
/* And the old signal handler */
|
|
sigaction( SIGALRM, &widget_timer_old_handler, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* End of timer functions */
|
|
|
|
int widget_dialog( int x, int y, int width, int height )
|
|
{
|
|
widget_rectangle( 8*x, 8*y, 8*width, 8*height, WIDGET_COLOUR_BACKGROUND );
|
|
return 0;
|
|
}
|
|
|
|
int widget_dialog_with_border( int x, int y, int width, int height )
|
|
{
|
|
int i;
|
|
|
|
widget_rectangle( 8*(x-1), 8*(y-1), 8*(width+2), 8*(height+2),
|
|
WIDGET_COLOUR_BACKGROUND );
|
|
|
|
for( i=(8*x)-1; i<(8*(x+width))+1; i++ ) {
|
|
widget_putpixel( i, 8 * y - 4, WIDGET_COLOUR_FOREGROUND );
|
|
widget_putpixel( i, 8 * ( y + height ) + 3, WIDGET_COLOUR_FOREGROUND );
|
|
}
|
|
|
|
for( i=(8*y)-1; i<(8*(y+height))+1; i++ ) {
|
|
widget_putpixel( 8 * x - 4, i, WIDGET_COLOUR_FOREGROUND );
|
|
widget_putpixel( 8 * ( x + width ) + 3, i, WIDGET_COLOUR_FOREGROUND );
|
|
}
|
|
|
|
for( i=0; i<2; i++ ) {
|
|
widget_putpixel( 8 * x - 3 + i, 8 * y - 2 - i,
|
|
WIDGET_COLOUR_FOREGROUND );
|
|
widget_putpixel( 8 * ( x + width ) + 2 - i, 8 * y - 2 - i,
|
|
WIDGET_COLOUR_FOREGROUND );
|
|
widget_putpixel( 8 * x - 3 + i, 8 * ( y + height ) + 1 + i,
|
|
WIDGET_COLOUR_FOREGROUND );
|
|
widget_putpixel( 8 * ( x + width ) + 2 - i, 8 * ( y + height ) + 1 + i,
|
|
WIDGET_COLOUR_FOREGROUND );
|
|
}
|
|
|
|
widget_display_lines( y-1, height+2 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
widget_putpixel( int x, int y, int colour )
|
|
{
|
|
display_putpixel( x + DISPLAY_BORDER_ASPECT_WIDTH, y + DISPLAY_BORDER_HEIGHT,
|
|
colour );
|
|
}
|
|
|
|
/* General functions used by the options dialogs */
|
|
|
|
int widget_options_print_option( int number, const char* string, int value )
|
|
{
|
|
char buffer[29];
|
|
|
|
snprintf( buffer, 29, "%-22s : %s", string, value ? " On" : "Off" );
|
|
widget_printstring( 2, number+4, WIDGET_COLOUR_FOREGROUND, buffer );
|
|
|
|
widget_display_lines( number + 4, 1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int widget_options_print_value( int number, int value )
|
|
{
|
|
widget_rectangle( 27*8, (number+4)*8, 24, 8, WIDGET_COLOUR_BACKGROUND );
|
|
widget_printstring( 27, number+4, WIDGET_COLOUR_FOREGROUND,
|
|
value ? " On" : "Off" );
|
|
|
|
widget_display_lines( number + 4, 1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
widget_options_print_entry( int number, const char *prefix, int value,
|
|
const char *suffix )
|
|
{
|
|
char buffer[29];
|
|
|
|
widget_rectangle( 2*8, (number+4)*8, 28*8, 8, WIDGET_COLOUR_BACKGROUND );
|
|
|
|
snprintf( buffer, 29, "%s: %d %s", prefix, value, suffix );
|
|
widget_printstring( 2, number + 4, WIDGET_COLOUR_FOREGROUND, buffer );
|
|
|
|
widget_display_lines( number + 4, 1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int widget_options_finish( widget_finish_state finished )
|
|
{
|
|
int error;
|
|
|
|
/* If we exited normally, actually set the options */
|
|
if( finished == WIDGET_FINISHED_OK ) {
|
|
error = settings_copy( &settings_current, &widget_options_settings );
|
|
settings_free( &widget_options_settings );
|
|
if( error ) return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The widgets actually available. Make sure the order here matches the
|
|
order defined in enum widget_type (widget.h) */
|
|
|
|
widget_t widget_data[] = {
|
|
|
|
{ widget_filesel_draw, widget_filesel_finish, widget_filesel_keyhandler },
|
|
{ widget_general_draw, widget_options_finish, widget_general_keyhandler },
|
|
{ widget_picture_draw, NULL, widget_picture_keyhandler },
|
|
{ widget_menu_draw, NULL, widget_menu_keyhandler },
|
|
{ widget_select_draw, widget_select_finish, widget_select_keyhandler },
|
|
{ widget_sound_draw, widget_options_finish, widget_sound_keyhandler },
|
|
{ widget_error_draw, NULL, widget_error_keyhandler },
|
|
{ widget_rzx_draw, widget_options_finish, widget_rzx_keyhandler },
|
|
{ widget_browse_draw, widget_browse_finish, widget_browse_keyhandler },
|
|
{ widget_text_draw, widget_text_finish, widget_text_keyhandler },
|
|
{ widget_scaler_draw, widget_scaler_finish, widget_scaler_keyhandler },
|
|
{ widget_debugger_draw, NULL, widget_debugger_keyhandler },
|
|
{ widget_roms_draw, widget_roms_finish, widget_roms_keyhandler },
|
|
|
|
};
|