mirror of
https://github.com/libretro/fuse-libretro.git
synced 2026-11-07 10:36:03 -10:00
Remove the dead standalone-Fuse UI: 277 files, 39% off the binary
With __LIBRETRO__ gone this is the only build, and the only build has
no way into any of the standalone frontend machinery it was still
compiling and shipping. The proof is one flag: display_ui_initialised
is set to 1 exclusively by the fb, gtk, sdl, svga, wii, win32 and xlib
display drivers, none of which are in the build, so it is permanently
0 here and widget_do() - the sole entry into the widget UI - returns
on its first line. Every menu, dialog, and the widget error popup were
compiled, linked, and unreachable. Likewise the movie recorder and the
profiler could only ever be started from those menus, the scaler was
selected once at startup and then never used (scalers16.o/scalers32.o
linked with zero references to any symbol in them), and the seven
other frontends' source trees were never compiled at all.
Deleted: fuse/ui/{fb,gtk,sdl,svga,wii,win32,xlib,null,widget,scaler},
fuse/menu.c, fuse/movie.[ch], fuse/profile.[ch], fuse/screenshot.[ch],
src/fuse/scalers16.c, src/fuse/scalers32.c, src/fuse/error.c (it
compiled the widget error dialog under another name), the platform
sound drivers in fuse/sound/ (blipbuffer stays - it is the mixer),
the platform compat trees in fuse/compat/ (getopt.h stays - the
generated settings code needs struct option on platforms without
getopt.h), fuse/timer/sdl.c, fuse/z80/coretest.c, libspectrum/test/,
and USE_WIDGET and the never-definable SOUND_FIFO branch with them.
What real code pulled out of the deleted cluster survives, relocated:
- The option_enumerate_*() string-to-enum mappings that sound.c, the
disk peripherals, the +3 and uimedia.c consume move verbatim from
the generated widget options.c into src/fuse/options_enumerate.c -
they were the only lines of that file the core ever executed.
- The rest of the ui_* surface is stubbed in src/compat/ui.c with
the answer each caller already effectively received: file dialogs
return NULL (cancel, handled quietly by tape/disk/mdr save), eject
with unsaved changes returns DONTSAVE, ui_confirm_joystick makes
the identical decision the widget version resolved to with
joy_prompt off (this core's only configuration), rollback returns
"no point chosen".
- ui_error_specific() now only logs. It previously also called into
the widget dialog, which drew nothing for the reason above.
The movie and profile frame hooks are deleted from display.c,
machine.c, rzx.c, sound.c, spectrum.c and the z80 opcode loop
(z80_ops.c and z80_checks.h stay consistent - both derive from the
same SETUP_CHECK list), fuse.c loses movie_init/movie_stop, the
start_scaler block and the screenshot startup registration, and
utils_read_screen() goes with screenshot.h since nothing called it.
ui_widget_level, pinned at -1 forever, is gone along with its checks
in input.c.
The built core shrinks from 2952496 to 1796304 bytes, 39%.
No functional change: the full verification matrix from issues #176
and #177 - Kempston Mouse hotplug and Fuller joystick wiring with and
without fastload, option flips mid-autoload, all five border modes
measured from the framebuffer, runtime border cycling, the keyboard
overlay, machine boots including Timex and NTSC, and a savestate round
trip - produces identical reset counts, port reads, framebuffer hashes
and audio sample counts to the build before this change, and the
corrupt-content error path hashes identically too, confirming the
widget error dialog never contributed a pixel. ASan and UBSan clean
across all of it. accessor.pl still regenerates snap_accessors.c
byte-identically.
This commit is contained in:
1 parent
5e3762d4e1
commit
c6b73c1a6c
296 files changed
+364
-66812
No files matched your search
+1
-25
@@ -13,8 +13,6 @@ SOURCES_C += $(CORE_DIR)/src/coreopt.c
|
||||
SOURCES_C += $(CORE_DIR)/src/missing.c
|
||||
SOURCES_C += $(CORE_DIR)/src/version.c
|
||||
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/scalers16.c
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/scalers32.c
|
||||
|
||||
SOURCES_C += $(CORE_DIR)/src/compat/dir.c
|
||||
SOURCES_C += $(CORE_DIR)/src/compat/display.c
|
||||
@@ -120,26 +118,7 @@ SOURCES_C += $(CORE_DIR)/fuse/sound/blipbuffer.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/timer/timer.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/timer/native.c
|
||||
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/browse.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/debugger.c
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/error.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/filesel.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/memory.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/menu.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/menu_data.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/options.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/picture.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/pokemem.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/pokefinder.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/query.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/roms.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/select.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/text.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/widget.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/about.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/widget/binary.c
|
||||
|
||||
SOURCES_C += $(CORE_DIR)/fuse/ui/scaler/scaler.c
|
||||
|
||||
SOURCES_C += $(CORE_DIR)/fuse/unittests/unittests.c
|
||||
|
||||
@@ -156,16 +135,13 @@ SOURCES_C += $(CORE_DIR)/fuse/loader.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/machine.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/memory_pages.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/mempool.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/menu.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/movie.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/module.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/periph.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/profile.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/psg.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/rectangle.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/rzx.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/screenshot.c
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/settings.c
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/options_enumerate.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/slt.c
|
||||
SOURCES_C += $(CORE_DIR)/src/fuse/snapshot.c
|
||||
SOURCES_C += $(CORE_DIR)/fuse/sound.c
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/* osname.c: Get a representation of the OS we're running on
|
||||
Copyright (c) 1999-2007 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <proto/exec.h>
|
||||
#include <proto/expansion.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
struct Library *ExpansionBase;
|
||||
struct ExpansionIFace *IExpansion;
|
||||
|
||||
int compat_osname( char *buffer, size_t length )
|
||||
{
|
||||
STRPTR machine;
|
||||
|
||||
if( ExpansionBase = IExec->OpenLibrary( "expansion.library", 52 ) ) {
|
||||
if( IExpansion = ( struct ExpansionIFace * ) IExec->GetInterface( ExpansionBase,"main",1,NULL ) ) {
|
||||
|
||||
IExpansion->GetMachineInfoTags( GMIT_MachineString, &machine,
|
||||
TAG_DONE );
|
||||
|
||||
snprintf( buffer, length, "%s %s %s", "AmigaOS", machine, "4.0" );
|
||||
|
||||
IExec->DropInterface( ( struct Interface * )IExpansion );
|
||||
}
|
||||
IExec->CloseLibrary( ExpansionBase );
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/* paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif /* #ifdef HAVE_LIBGEN_H */
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const char*
|
||||
compat_get_temp_path( void )
|
||||
{
|
||||
return "T:";
|
||||
}
|
||||
|
||||
const char*
|
||||
compat_get_config_path( void )
|
||||
{
|
||||
return "PROGDIR:settings";
|
||||
}
|
||||
|
||||
int
|
||||
compat_is_absolute_path( const char *path )
|
||||
{
|
||||
return strchr(path,':');
|
||||
}
|
||||
|
||||
int
|
||||
compat_get_next_path( path_context *ctx )
|
||||
{
|
||||
char buffer[ PATH_MAX ];
|
||||
const char *path_segment, *path2;
|
||||
|
||||
switch( (ctx->state)++ ) {
|
||||
|
||||
/* First look relative to the current directory */
|
||||
case 0:
|
||||
strncpy( ctx->path, "PROGDIR:", PATH_MAX );
|
||||
return 1;
|
||||
|
||||
/* Then relative to the Fuse executable */
|
||||
case 1:
|
||||
|
||||
switch( ctx->type ) {
|
||||
case UTILS_AUXILIARY_LIB: strncpy( ctx->path, "PROGDIR:lib/", PATH_MAX); return 1;
|
||||
case UTILS_AUXILIARY_ROM: strncpy( ctx->path, "PROGDIR:roms/", PATH_MAX); return 1;
|
||||
case UTILS_AUXILIARY_WIDGET: strncpy( ctx->path, "PROGDIR:ui/widget/", PATH_MAX); return 1;
|
||||
case UTILS_AUXILIARY_GTK: strncpy( ctx->path, "PROGDIR:ui/gtk/", PATH_MAX); return 1;
|
||||
default:
|
||||
ui_error( UI_ERROR_ERROR, "unknown auxiliary file type %d", ctx->type );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( compat_is_absolute_path( fuse_progname ) ) {
|
||||
strncpy( buffer, fuse_progname, PATH_MAX );
|
||||
buffer[ PATH_MAX - 1 ] = '\0';
|
||||
} else {
|
||||
size_t len;
|
||||
len = PATH_MAX - strlen( fuse_progname ) - strlen( FUSE_DIR_SEP_STR );
|
||||
if( !getcwd( buffer, len ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting current working directory: %s",
|
||||
strerror( errno ) );
|
||||
return 0;
|
||||
}
|
||||
strcat( buffer, FUSE_DIR_SEP_STR );
|
||||
strcat( buffer, fuse_progname );
|
||||
}
|
||||
|
||||
path2 = dirname( buffer );
|
||||
snprintf( ctx->path, PATH_MAX, "%s" FUSE_DIR_SEP_STR "%s", path2,
|
||||
path_segment );
|
||||
return 1;
|
||||
|
||||
/* Then where we may have installed the data files */
|
||||
case 2:
|
||||
|
||||
#ifndef ROMSDIR
|
||||
path2 = FUSEDATADIR;
|
||||
#else /* #ifndef ROMSDIR */
|
||||
path2 = ctx->type == UTILS_AUXILIARY_ROM ? ROMSDIR : FUSEDATADIR;
|
||||
#endif /* #ifndef ROMSDIR */
|
||||
strncpy( ctx->path, path2, PATH_MAX ); buffer[ PATH_MAX - 1 ] = '\0';
|
||||
return 1;
|
||||
|
||||
case 3: return 0;
|
||||
}
|
||||
ui_error( UI_ERROR_ERROR, "unknown path_context state %d", ctx->state );
|
||||
fuse_abort();
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/* dirname - return directory part of PATH.
|
||||
Copyright (C) 1996, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
This file was part of the GNU C Library, version 2.3.2.
|
||||
Contributed by Ulrich Drepper <[email protected]>, 1996.
|
||||
Very slightly modified for use in Fuse by Philip Kendall, 2004.
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
static char *
|
||||
__memrchr (char *s, int c, size_t n)
|
||||
{
|
||||
for (s += n - 1; n; n--, s--)
|
||||
if (*s == c)
|
||||
return s;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
dirname (char *path)
|
||||
{
|
||||
static const char dot[] = ".";
|
||||
char *last_slash;
|
||||
|
||||
/* Find last '/'. */
|
||||
last_slash = path != NULL ? strrchr (path, FUSE_DIR_SEP_CHR) : NULL;
|
||||
|
||||
if (last_slash != NULL && last_slash != path && last_slash[1] == '\0')
|
||||
{
|
||||
/* Determine whether all remaining characters are slashes. */
|
||||
char *runp;
|
||||
|
||||
for (runp = last_slash; runp != path; --runp)
|
||||
if (runp[-1] != FUSE_DIR_SEP_CHR)
|
||||
break;
|
||||
|
||||
/* The '/' is the last character, we have to look further. */
|
||||
if (runp != path)
|
||||
last_slash = __memrchr (path, FUSE_DIR_SEP_CHR, runp - path);
|
||||
}
|
||||
|
||||
if (last_slash != NULL)
|
||||
{
|
||||
/* Determine whether all remaining characters are slashes. */
|
||||
char *runp;
|
||||
|
||||
for (runp = last_slash; runp != path; --runp)
|
||||
if (runp[-1] != FUSE_DIR_SEP_CHR)
|
||||
break;
|
||||
|
||||
/* Terminate the path. */
|
||||
if (runp == path)
|
||||
{
|
||||
/* The last slash is the first character in the string. We have to
|
||||
return "/". As a special case we have to return "//" if there
|
||||
are exactly two slashes at the beginning of the string. See
|
||||
XBD 4.10 Path Name Resolution for more information. */
|
||||
if (last_slash == path + 1)
|
||||
++last_slash;
|
||||
else
|
||||
last_slash = path + 1;
|
||||
}
|
||||
else
|
||||
last_slash = runp;
|
||||
|
||||
last_slash[0] = '\0';
|
||||
}
|
||||
else
|
||||
/* This assignment is ill-designed but the XPG specs require to
|
||||
return a string containing "." in any case no directory part is
|
||||
found and so a static and constant string is required. */
|
||||
path = (char *) dot;
|
||||
|
||||
return path;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,198 +0,0 @@
|
||||
/* getopt_long and getopt_long_only entry points for GNU getopt.
|
||||
Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
|
||||
Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
Modifications copyright (c) 2003 Philip Kendall
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef _LIBC
|
||||
# include <getopt.h>
|
||||
#else
|
||||
# include "getopt.h"
|
||||
#endif
|
||||
|
||||
#if !defined __STDC__ || !__STDC__
|
||||
/* This is a separate conditional since some stdc systems
|
||||
reject `defined (const)'. */
|
||||
#ifndef const
|
||||
#define const
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Comment out all this code if we are using the GNU C Library, and are not
|
||||
actually compiling the library itself. This code is part of the GNU C
|
||||
Library, but also included in many other GNU distributions. Compiling
|
||||
and linking in this code is a waste when using the GNU C library
|
||||
(especially if it is a shared library). Rather than having every GNU
|
||||
program understand `configure --with-gnu-libc' and omit the object files,
|
||||
it is simpler to just do this in the source for each such file. */
|
||||
|
||||
#define GETOPT_INTERFACE_VERSION 2
|
||||
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
|
||||
#include <gnu-versions.h>
|
||||
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
|
||||
#define ELIDE_CODE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ELIDE_CODE
|
||||
|
||||
|
||||
/* This needs to come after some library #include
|
||||
to get __GNU_LIBRARY__ defined. */
|
||||
#ifdef __GNU_LIBRARY__
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
int
|
||||
getopt_long (argc, argv, options, long_options, opt_index)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
const char *options;
|
||||
const struct option *long_options;
|
||||
int *opt_index;
|
||||
{
|
||||
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
|
||||
}
|
||||
|
||||
/* Like getopt_long, but '-' as well as '--' can indicate a long option.
|
||||
If an option that starts with '-' (not '--') doesn't match a long option,
|
||||
but does match a short option, it is parsed as a short option
|
||||
instead. */
|
||||
|
||||
int
|
||||
getopt_long_only (argc, argv, options, long_options, opt_index)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
const char *options;
|
||||
const struct option *long_options;
|
||||
int *opt_index;
|
||||
{
|
||||
return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
|
||||
}
|
||||
|
||||
# ifdef _LIBC
|
||||
libc_hidden_def (getopt_long)
|
||||
libc_hidden_def (getopt_long_only)
|
||||
# endif
|
||||
|
||||
#endif /* Not ELIDE_CODE. */
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
int c;
|
||||
int digit_optind = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int this_option_optind = optind ? optind : 1;
|
||||
int option_index = 0;
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"add", 1, 0, 0},
|
||||
{"append", 0, 0, 0},
|
||||
{"delete", 1, 0, 0},
|
||||
{"verbose", 0, 0, 0},
|
||||
{"create", 0, 0, 0},
|
||||
{"file", 1, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long (argc, argv, "abc:d:0123456789",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
printf ("option %s", long_options[option_index].name);
|
||||
if (optarg)
|
||||
printf (" with arg %s", optarg);
|
||||
printf ("\n");
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
if (digit_optind != 0 && digit_optind != this_option_optind)
|
||||
printf ("digits occur in two different argv-elements.\n");
|
||||
digit_optind = this_option_optind;
|
||||
printf ("option %c\n", c);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
printf ("option a\n");
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
printf ("option b\n");
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
printf ("option c with value `%s'\n", optarg);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
printf ("option d with value `%s'\n", optarg);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("?? getopt returned character code 0%o ??\n", c);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
printf ("non-option ARGV-elements: ");
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
||||
@@ -1,43 +0,0 @@
|
||||
/* relative_paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void
|
||||
get_relative_directory( char *buffer, size_t bufsize )
|
||||
{
|
||||
ssize_t retval = readlink( "/proc/self/exe", buffer, bufsize - 1 );
|
||||
if( retval < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting current working directory: %s",
|
||||
strerror( -errno ) );
|
||||
fuse_abort();
|
||||
}
|
||||
buffer[ retval ] = '\0';
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/* osname.c: Get a representation of the OS we're running on
|
||||
Copyright (c) 1999-2007 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int compat_osname( char *buffer, size_t length )
|
||||
{
|
||||
snprintf( buffer, length, "%s %s %s", "MorphOS", "Pegasos", "1.4.4" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/* dir.c: Directory-related compatibility routines
|
||||
Copyright (c) 2009 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
compat_dir
|
||||
compat_opendir( const char *path )
|
||||
{
|
||||
return opendir( path );
|
||||
}
|
||||
|
||||
compat_dir_result_t
|
||||
compat_readdir( compat_dir directory, char *name, size_t length )
|
||||
{
|
||||
compat_dir_result_t r;
|
||||
struct dirent *dirent;
|
||||
|
||||
errno = 0;
|
||||
dirent = readdir( directory );
|
||||
|
||||
if( dirent ) {
|
||||
r = COMPAT_DIR_RESULT_OK;
|
||||
strncpy( name, dirent->d_name, length );
|
||||
name[ length - 1 ] = 0;
|
||||
} else {
|
||||
r = ( errno == 0 ? COMPAT_DIR_RESULT_END : COMPAT_DIR_RESULT_ERROR );
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
compat_closedir( compat_dir directory )
|
||||
{
|
||||
return closedir( directory );
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/* file.c: File-related compatibility routines
|
||||
Copyright (c) 2008 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "utils.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const compat_fd COMPAT_FILE_OPEN_FAILED = NULL;
|
||||
|
||||
compat_fd
|
||||
compat_file_open( const char *path, int write )
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if( !write ) {
|
||||
if( stat( path, &statbuf ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check file type */
|
||||
if( !S_ISREG( statbuf.st_mode ) ) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fopen( path, write ? "wb" : "rb" );
|
||||
}
|
||||
|
||||
off_t
|
||||
compat_file_get_length( compat_fd fd )
|
||||
{
|
||||
struct stat file_info;
|
||||
|
||||
if( fstat( fileno( fd ), &file_info ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't stat file: %s", strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_info.st_size;
|
||||
}
|
||||
|
||||
int
|
||||
compat_file_read( compat_fd fd, utils_file *file )
|
||||
{
|
||||
size_t bytes = fread( file->buffer, 1, file->length, fd );
|
||||
if( bytes != file->length ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"error reading file: expected %lu bytes, but read only %lu",
|
||||
(unsigned long)file->length, (unsigned long)bytes );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
compat_file_write( compat_fd fd, const unsigned char *buffer, size_t length )
|
||||
{
|
||||
size_t bytes = fwrite( buffer, 1, length, fd );
|
||||
if( bytes != length ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"error writing file: expected %lu bytes, but wrote only %lu",
|
||||
(unsigned long)length, (unsigned long)bytes );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
compat_file_close( compat_fd fd )
|
||||
{
|
||||
return fclose( fd );
|
||||
}
|
||||
|
||||
int
|
||||
compat_file_exists( const char *path )
|
||||
{
|
||||
return ( access( path, R_OK ) != -1 );
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* osname.c: Get a representation of the OS we're running on
|
||||
Copyright (c) 1999-2007 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
int compat_osname( char *buffer, size_t length )
|
||||
{
|
||||
struct utsname osname;
|
||||
int error;
|
||||
|
||||
error = uname( &osname );
|
||||
if( error < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting system information: %s",
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf( buffer, length, "%s %s %s", osname.sysname, osname.machine,
|
||||
osname.release );
|
||||
return 0;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/* paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif /* #ifdef HAVE_LIBGEN_H */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void get_relative_directory( char *buffer, size_t bufsize );
|
||||
|
||||
const char*
|
||||
compat_get_temp_path( void )
|
||||
{
|
||||
const char *dir;
|
||||
|
||||
/* Use TMPDIR if specified, if not /tmp */
|
||||
dir = getenv( "TMPDIR" ); if( dir ) return dir;
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
const char*
|
||||
compat_get_config_path( void )
|
||||
{
|
||||
const char *dir;
|
||||
dir = getenv( "HOME" ); if( dir ) return dir;
|
||||
return ".";
|
||||
}
|
||||
|
||||
int
|
||||
compat_is_absolute_path( const char *path )
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
|
||||
int
|
||||
compat_get_next_path( path_context *ctx )
|
||||
{
|
||||
char buffer[ PATH_MAX ];
|
||||
const char *path_segment, *path2;
|
||||
|
||||
switch( (ctx->state)++ ) {
|
||||
|
||||
/* First look relative to the current directory */
|
||||
case 0:
|
||||
strncpy( ctx->path, ".", PATH_MAX );
|
||||
return 1;
|
||||
|
||||
/* Then relative to the Fuse executable */
|
||||
case 1:
|
||||
|
||||
switch( ctx->type ) {
|
||||
case UTILS_AUXILIARY_LIB: path_segment = "lib"; break;
|
||||
case UTILS_AUXILIARY_ROM: path_segment = "roms"; break;
|
||||
case UTILS_AUXILIARY_WIDGET: path_segment = "ui/widget"; break;
|
||||
case UTILS_AUXILIARY_GTK: path_segment = "ui/gtk"; break;
|
||||
default:
|
||||
ui_error( UI_ERROR_ERROR, "unknown auxiliary file type %d", ctx->type );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( compat_is_absolute_path( fuse_progname ) ) {
|
||||
strncpy( buffer, fuse_progname, PATH_MAX );
|
||||
buffer[ PATH_MAX - 1 ] = '\0';
|
||||
} else {
|
||||
get_relative_directory( buffer, PATH_MAX );
|
||||
}
|
||||
|
||||
path2 = dirname( buffer );
|
||||
snprintf( ctx->path, PATH_MAX, "%s" FUSE_DIR_SEP_STR "%s", path2,
|
||||
path_segment );
|
||||
return 1;
|
||||
|
||||
/* Then where we may have installed the data files */
|
||||
case 2:
|
||||
|
||||
#ifndef ROMSDIR
|
||||
path2 = FUSEDATADIR;
|
||||
#else /* #ifndef ROMSDIR */
|
||||
path2 = ctx->type == UTILS_AUXILIARY_ROM ? ROMSDIR : FUSEDATADIR;
|
||||
#endif /* #ifndef ROMSDIR */
|
||||
strncpy( ctx->path, path2, PATH_MAX ); buffer[ PATH_MAX - 1 ] = '\0';
|
||||
return 1;
|
||||
|
||||
case 3: return 0;
|
||||
}
|
||||
ui_error( UI_ERROR_ERROR, "unknown path_context state %d", ctx->state );
|
||||
fuse_abort();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* relative_paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void
|
||||
get_relative_directory( char *buffer, size_t bufsize )
|
||||
{
|
||||
size_t len = bufsize - strlen( fuse_progname ) - strlen( FUSE_DIR_SEP_STR );
|
||||
if( !getcwd( buffer, len ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting current working directory: %s",
|
||||
strerror( -errno ) );
|
||||
fuse_abort();
|
||||
}
|
||||
strcat( buffer, FUSE_DIR_SEP_STR );
|
||||
strcat( buffer, fuse_progname );
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/* socket.c: Socket-related compatibility routines
|
||||
Copyright (c) 2011-2015 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const compat_socket_t compat_socket_invalid = -1;
|
||||
const int compat_socket_EBADF = EBADF;
|
||||
|
||||
struct compat_socket_selfpipe_t {
|
||||
int read_fd;
|
||||
int write_fd;
|
||||
};
|
||||
|
||||
void
|
||||
compat_socket_networking_init( void )
|
||||
{
|
||||
/* No action necessary */
|
||||
}
|
||||
|
||||
void
|
||||
compat_socket_networking_end( void )
|
||||
{
|
||||
/* No action necessary */
|
||||
}
|
||||
|
||||
int
|
||||
compat_socket_blocking_mode( compat_socket_t fd, int blocking )
|
||||
{
|
||||
int flags = fcntl( fd, F_GETFL, 0 );
|
||||
if( flags == -1 )
|
||||
return -1;
|
||||
flags = blocking ? ( flags | O_NONBLOCK ) : ( flags & ~O_NONBLOCK );
|
||||
return ( fcntl( fd, F_SETFL, flags ) != 0 );
|
||||
}
|
||||
|
||||
int
|
||||
compat_socket_close( compat_socket_t fd )
|
||||
{
|
||||
return close( fd );
|
||||
}
|
||||
|
||||
int compat_socket_get_error( void )
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
|
||||
const char *
|
||||
compat_socket_get_strerror( void )
|
||||
{
|
||||
return strerror( errno );
|
||||
}
|
||||
|
||||
compat_socket_selfpipe_t* compat_socket_selfpipe_alloc( void )
|
||||
{
|
||||
int error;
|
||||
int pipefd[2];
|
||||
|
||||
compat_socket_selfpipe_t *self =
|
||||
libspectrum_new( compat_socket_selfpipe_t, 1 );
|
||||
|
||||
error = pipe( pipefd );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "%s: %d: error %d creating pipe", __FILE__, __LINE__, error );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
self->read_fd = pipefd[0];
|
||||
self->write_fd = pipefd[1];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void compat_socket_selfpipe_free( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
close( self->read_fd );
|
||||
close( self->write_fd );
|
||||
libspectrum_free( self );
|
||||
}
|
||||
|
||||
compat_socket_t compat_socket_selfpipe_get_read_fd( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
return self->read_fd;
|
||||
}
|
||||
|
||||
void compat_socket_selfpipe_wake( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
const char dummy = 0;
|
||||
ssize_t unused = write( self->write_fd, &dummy, 1 );
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
void compat_socket_selfpipe_discard_data( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
char bitbucket;
|
||||
ssize_t bytes_read;
|
||||
|
||||
do {
|
||||
bytes_read = read( self->read_fd, &bitbucket, 1 );
|
||||
if( bytes_read == -1 && errno != EINTR ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s: %d: unexpected error %d (%s) reading from pipe", __FILE__,
|
||||
__LINE__, errno, strerror(errno) );
|
||||
}
|
||||
} while( bytes_read < 0 );
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/* timer.c: UNIX speed routines for Fuse
|
||||
Copyright (c) 1999-2008 Philip Kendall, Marek Januszewski, Fredrick Meunier
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
double
|
||||
compat_timer_get_time( void )
|
||||
{
|
||||
struct timeval tv;
|
||||
int error;
|
||||
|
||||
error = gettimeofday( &tv, NULL );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "%s: error getting time: %s", __func__, strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
void
|
||||
compat_timer_sleep( int ms )
|
||||
{
|
||||
usleep( ms * 1000 );
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/* tuntap.c: TUN/TAP compatability layer
|
||||
Copyright (c) 2009-2010 Patrik Persson, Philip Kendall
|
||||
Copyright (c) 2016 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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/if_tun.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
int
|
||||
compat_get_tap( const char *interface_name )
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
if ( (fd = open( "/dev/net/tun", O_RDWR | O_NONBLOCK )) < 0 )
|
||||
ui_error( UI_ERROR_ERROR, "couldn't open TUN/TAP device '/dev/net/tun'" );
|
||||
else {
|
||||
struct ifreq ifr;
|
||||
memset( &ifr, 0, sizeof( ifr ) );
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
strncpy( ifr.ifr_name, interface_name, IFNAMSIZ );
|
||||
ifr.ifr_name[ IFNAMSIZ - 1 ] = '\0';
|
||||
|
||||
if ( ioctl( fd, TUNSETIFF, (void *) &ifr ) < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't select TAP interface '%s'",
|
||||
ifr.ifr_name );
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* dir.c: Directory-related compatibility routines
|
||||
Copyright (c) 2009 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
compat_dir
|
||||
compat_opendir( const char *path )
|
||||
{
|
||||
return diropen( path );
|
||||
}
|
||||
|
||||
compat_dir_result_t
|
||||
compat_readdir( compat_dir directory, char *path, size_t length )
|
||||
{
|
||||
struct stat fstat;
|
||||
|
||||
int done = dirnext( directory, path, &fstat );
|
||||
|
||||
return done ? COMPAT_DIR_RESULT_END : COMPAT_DIR_RESULT_OK;
|
||||
}
|
||||
|
||||
int
|
||||
compat_closedir( compat_dir directory )
|
||||
{
|
||||
return dirclose( directory );
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/* osname.c: Get a representation of the OS we're running on
|
||||
Copyright (c) 1999-2009 Philip Kendall, Bjoern Giesler
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
int
|
||||
compat_osname( char *buffer, size_t length )
|
||||
{
|
||||
snprintf(buffer, length, "Wii");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/* paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall, Bjoern Giesler
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif /* #ifdef HAVE_LIBGEN_H */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const char*
|
||||
compat_get_temp_path( void )
|
||||
{
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
const char*
|
||||
compat_get_config_path( void )
|
||||
{
|
||||
return "sd:/apps/fuse";
|
||||
}
|
||||
|
||||
int
|
||||
compat_is_absolute_path( const char *path )
|
||||
{
|
||||
if(strlen(path) >= 1 && path[0] == '/')
|
||||
return 1;
|
||||
if(strlen(path) >= strlen("fat:/") &&
|
||||
strncmp(path, "fat:/", strlen("fat:/")) == 0)
|
||||
return 1;
|
||||
if(strlen(path) >= strlen("sd:/") &&
|
||||
strncmp(path, "sd:/", strlen("sd:/")) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
compat_get_next_path( path_context *ctx )
|
||||
{
|
||||
char buffer[ PATH_MAX ];
|
||||
const char *path_segment, *path2;
|
||||
|
||||
switch( (ctx->state)++ ) {
|
||||
|
||||
/* First look relative to the current directory */
|
||||
case 0:
|
||||
strncpy( ctx->path, ".", PATH_MAX );
|
||||
return 1;
|
||||
|
||||
/* Then relative to the Fuse executable */
|
||||
case 1:
|
||||
|
||||
switch( ctx->type ) {
|
||||
case UTILS_AUXILIARY_LIB: path_segment = "lib"; break;
|
||||
case UTILS_AUXILIARY_ROM: path_segment = "roms"; break;
|
||||
case UTILS_AUXILIARY_WIDGET: path_segment = "ui/widget"; break;
|
||||
case UTILS_AUXILIARY_GTK: path_segment = "ui/gtk"; break;
|
||||
default:
|
||||
ui_error( UI_ERROR_ERROR, "unknown auxiliary file type %d", ctx->type );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( compat_is_absolute_path( fuse_progname ) ) {
|
||||
strncpy( buffer, fuse_progname, PATH_MAX );
|
||||
buffer[ PATH_MAX - 1 ] = '\0';
|
||||
} else {
|
||||
size_t len;
|
||||
len = PATH_MAX - strlen( fuse_progname ) - strlen( FUSE_DIR_SEP_STR );
|
||||
if( !getcwd( buffer, len ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting current working directory: %s",
|
||||
strerror( errno ) );
|
||||
return 0;
|
||||
}
|
||||
strcat( buffer, FUSE_DIR_SEP_STR );
|
||||
strcat( buffer, fuse_progname );
|
||||
}
|
||||
|
||||
path2 = dirname( buffer );
|
||||
snprintf( ctx->path, PATH_MAX, "%s" FUSE_DIR_SEP_STR "%s", path2,
|
||||
path_segment );
|
||||
return 1;
|
||||
|
||||
/* Then where we may have installed the data files */
|
||||
case 2:
|
||||
|
||||
path2 = "sd:/apps/fuse";
|
||||
strncpy( ctx->path, path2, PATH_MAX ); buffer[ PATH_MAX - 1 ] = '\0';
|
||||
return 1;
|
||||
|
||||
case 3: return 0;
|
||||
}
|
||||
ui_error( UI_ERROR_ERROR, "unknown path_context state %d", ctx->state );
|
||||
fuse_abort();
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/* unix.c: UNIX speed routines for Fuse
|
||||
Copyright (c) 1999-2007 Philip Kendall, Marek Januszewski, Fredrick Meunier
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* FIXME: where should we get this prototype from? */
|
||||
extern int clock_gettime(struct timespec *tp);
|
||||
|
||||
double
|
||||
compat_timer_get_time( void )
|
||||
{
|
||||
int error;
|
||||
struct timespec tp;
|
||||
|
||||
error = clock_gettime(&tp);
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "%s: error getting time: %s", __func__, strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tp.tv_sec + tp.tv_nsec / 1000000000.0;
|
||||
}
|
||||
|
||||
void
|
||||
compat_timer_sleep( int ms )
|
||||
{
|
||||
usleep( ms * 1000 );
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/* osname.c: Get a representation of the OS we're running on
|
||||
Copyright (c) 1999-2007 Philip Kendall
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
int compat_osname( char *osname, size_t length )
|
||||
{
|
||||
OSVERSIONINFO buf;
|
||||
const char *windows_name;
|
||||
int error;
|
||||
|
||||
buf.dwOSVersionInfoSize = sizeof( buf );
|
||||
error = GetVersionEx( &buf );
|
||||
if( error == 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "error getting system information." );
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch( buf.dwPlatformId ) {
|
||||
case VER_PLATFORM_WIN32_NT: windows_name = "NT"; break;
|
||||
case VER_PLATFORM_WIN32_WINDOWS: windows_name = "95/98"; break;
|
||||
case VER_PLATFORM_WIN32s: windows_name = "3.1"; break;
|
||||
default: windows_name = "unknown"; break;
|
||||
}
|
||||
|
||||
/* FIXME: verify function below is unicode compliant */
|
||||
/* The casts to int work around a suspected Wine (or MinGW) bug */
|
||||
snprintf( osname, length, "Windows %s %i.%i build %i %s",
|
||||
windows_name, (int)buf.dwMajorVersion, (int)buf.dwMinorVersion,
|
||||
(int)buf.dwBuildNumber, buf.szCSDVersion );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/* paths.c: Path-related compatibility routines
|
||||
Copyright (c) 1999-2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif /* #ifdef HAVE_LIBGEN_H */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const char*
|
||||
compat_get_temp_path( void )
|
||||
{
|
||||
const char *dir;
|
||||
|
||||
/* Something close to this algorithm specified at
|
||||
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
|
||||
*/
|
||||
dir = getenv( "TMP" ); if( dir ) return dir;
|
||||
dir = getenv( "TEMP" ); if( dir ) return dir;
|
||||
return ".";
|
||||
}
|
||||
|
||||
const char*
|
||||
compat_get_config_path( void )
|
||||
{
|
||||
const char *dir;
|
||||
dir = getenv( "USERPROFILE" ); if( dir ) return dir;
|
||||
dir = getenv( "WINDIR" ); if( dir ) return dir;
|
||||
return ".";
|
||||
}
|
||||
|
||||
int
|
||||
compat_is_absolute_path( const char *path )
|
||||
{
|
||||
if( path[0] == '\\' ) return 1;
|
||||
if( path[0] && path[1] == ':' ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
compat_get_next_path( path_context *ctx )
|
||||
{
|
||||
char buffer[ PATH_MAX ];
|
||||
const char *path_segment, *path2;
|
||||
|
||||
switch( ctx->type ) {
|
||||
case UTILS_AUXILIARY_LIB: path_segment = "lib"; break;
|
||||
case UTILS_AUXILIARY_ROM: path_segment = "roms"; break;
|
||||
case UTILS_AUXILIARY_WIDGET: path_segment = "ui/widget"; break;
|
||||
case UTILS_AUXILIARY_GTK: path_segment = "ui/gtk"; break;
|
||||
default:
|
||||
ui_error( UI_ERROR_ERROR, "unknown auxiliary file type %d", ctx->type );
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch( (ctx->state)++ ) {
|
||||
|
||||
/* First look relative to the Fuse executable */
|
||||
case 0:
|
||||
if( compat_is_absolute_path( fuse_progname ) ) {
|
||||
strncpy( buffer, fuse_progname, PATH_MAX );
|
||||
buffer[ PATH_MAX - 1 ] = '\0';
|
||||
} else {
|
||||
DWORD retval;
|
||||
retval = GetModuleFileName( NULL, buffer, PATH_MAX );
|
||||
if( !retval ) return 0;
|
||||
}
|
||||
|
||||
path2 = dirname( buffer );
|
||||
snprintf( ctx->path, PATH_MAX, "%s" FUSE_DIR_SEP_STR "%s", path2,
|
||||
path_segment );
|
||||
return 1;
|
||||
|
||||
/* Then relative to %APPDATA%/Fuse directory */
|
||||
case 1:
|
||||
path2 = getenv( "APPDATA" );
|
||||
if( !path2 ) return 0;
|
||||
snprintf( ctx->path, PATH_MAX, "%s" FUSE_DIR_SEP_STR "Fuse"
|
||||
FUSE_DIR_SEP_STR "%s", path2, path_segment );
|
||||
|
||||
return 1;
|
||||
|
||||
/* Then relative to the current directory */
|
||||
case 2:
|
||||
snprintf( ctx->path, PATH_MAX, "." FUSE_DIR_SEP_STR "%s",
|
||||
path_segment );
|
||||
return 1;
|
||||
|
||||
case 3: return 0;
|
||||
}
|
||||
|
||||
ui_error( UI_ERROR_ERROR, "unknown path_context state %d", ctx->state );
|
||||
fuse_abort();
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
/* socket.c: Socket-related compatibility routines
|
||||
Copyright (c) 2011-2015 Sergio Baldoví, Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
const compat_socket_t compat_socket_invalid = INVALID_SOCKET;
|
||||
const int compat_socket_EBADF = WSAENOTSOCK;
|
||||
|
||||
struct compat_socket_selfpipe_t {
|
||||
SOCKET self_socket;
|
||||
libspectrum_word port;
|
||||
};
|
||||
|
||||
int
|
||||
compat_socket_blocking_mode( compat_socket_t fd, int blocking )
|
||||
{
|
||||
u_long mode = blocking ? 1 : 0;
|
||||
return ( ioctlsocket( fd, FIONBIO, &mode ) );
|
||||
}
|
||||
|
||||
int
|
||||
compat_socket_close( compat_socket_t fd )
|
||||
{
|
||||
return closesocket( fd );
|
||||
}
|
||||
|
||||
int compat_socket_get_error( void )
|
||||
{
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
const char *
|
||||
compat_socket_get_strerror( void )
|
||||
{
|
||||
static TCHAR buffer[256];
|
||||
TCHAR *ptr;
|
||||
DWORD msg_size;
|
||||
|
||||
/* get description of last winsock error */
|
||||
msg_size = FormatMessage(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, WSAGetLastError(),
|
||||
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
||||
buffer, ARRAY_SIZE( buffer ), NULL );
|
||||
|
||||
if( !msg_size ) return NULL;
|
||||
|
||||
/* skip 'new line' like chars */
|
||||
for( ptr = buffer; *ptr; ptr++ ) {
|
||||
if( ( *ptr == '\r' ) || ( *ptr == '\n' ) ) {
|
||||
*ptr = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (const char *)buffer;
|
||||
}
|
||||
|
||||
static int
|
||||
selfpipe_test( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
fd_set readfds;
|
||||
int active;
|
||||
struct timeval tv = { 1, 0 };
|
||||
|
||||
/* Send testing packet */
|
||||
compat_socket_selfpipe_wake( self );
|
||||
|
||||
/* Safe reading from control socket */
|
||||
FD_ZERO( &readfds );
|
||||
FD_SET( self->self_socket, &readfds );
|
||||
active = select( 0, &readfds, NULL, NULL, &tv );
|
||||
if( active == 0 || active == compat_socket_invalid ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Discard testing packet */
|
||||
if( FD_ISSET( self->self_socket, &readfds ) ) {
|
||||
compat_socket_selfpipe_discard_data( self );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
compat_socket_selfpipe_t *
|
||||
compat_socket_selfpipe_alloc( void )
|
||||
{
|
||||
unsigned long mode = 1;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
compat_socket_selfpipe_t *self =
|
||||
libspectrum_new( compat_socket_selfpipe_t, 1 );
|
||||
|
||||
self->self_socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
|
||||
if( self->self_socket == compat_socket_invalid ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s: %d: failed to open socket; errno %d: %s\n",
|
||||
__FILE__, __LINE__, compat_socket_get_error(),
|
||||
compat_socket_get_strerror() );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
/* Set nonblocking mode */
|
||||
if( ioctlsocket( self->self_socket, FIONBIO, &mode ) == -1 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s: %d: failed to set socket nonblocking; errno %d: %s\n",
|
||||
__FILE__, __LINE__, compat_socket_get_error(),
|
||||
compat_socket_get_strerror() );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
memset( &sa, 0, sizeof(sa) );
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
|
||||
|
||||
if( bind( self->self_socket, (struct sockaddr*)&sa, sizeof(sa) ) == -1 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s: %d: failed to bind socket; errno %d: %s\n",
|
||||
__FILE__, __LINE__, compat_socket_get_error(),
|
||||
compat_socket_get_strerror() );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
/* Get ephemeral port number */
|
||||
if( getsockname( self->self_socket, (struct sockaddr *)&sa, &sa_len ) == -1 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s: %d: failed to get socket name; errno %d: %s\n",
|
||||
__FILE__, __LINE__, compat_socket_get_error(),
|
||||
compat_socket_get_strerror() );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
self->port = ntohs( sa.sin_port );
|
||||
|
||||
/* Test communications in order to detect blocking firewalls */
|
||||
if( selfpipe_test( self ) == -1 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Networking: failed to test internal communications" );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
compat_socket_selfpipe_free( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
compat_socket_close( self->self_socket );
|
||||
libspectrum_free( self );
|
||||
}
|
||||
|
||||
compat_socket_t
|
||||
compat_socket_selfpipe_get_read_fd( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
return self->self_socket;
|
||||
}
|
||||
|
||||
void
|
||||
compat_socket_selfpipe_wake( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
memset( &sa, 0, sizeof(sa) );
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
|
||||
sa.sin_port = htons( self->port );
|
||||
|
||||
sendto( self->self_socket, NULL, 0, 0, (struct sockaddr*)&sa, sizeof(sa) );
|
||||
}
|
||||
|
||||
void
|
||||
compat_socket_selfpipe_discard_data( compat_socket_selfpipe_t *self )
|
||||
{
|
||||
ssize_t bytes_read;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_length = sizeof(sa);
|
||||
static char bitbucket[0x100];
|
||||
|
||||
do {
|
||||
/* Socket is non blocking, so we can do this safely */
|
||||
bytes_read = recvfrom( self->self_socket, bitbucket, sizeof( bitbucket ),
|
||||
0, (struct sockaddr*)&sa, &sa_length );
|
||||
} while( bytes_read != -1 );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
compat_socket_networking_init( void )
|
||||
{
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int error;
|
||||
|
||||
wVersionRequested = MAKEWORD( 2, 2 );
|
||||
error = WSAStartup( wVersionRequested, &wsaData );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "%s:%d: error %d from WSAStartup()", __FILE__,
|
||||
__LINE__, error );
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
if( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"%s:%d: unexpected version 0x%02x from WSAStartup()",
|
||||
__FILE__, __LINE__, wsaData.wVersion );
|
||||
fuse_abort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
compat_socket_networking_end( void )
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/* timer.c: Win32 speed routines for Fuse
|
||||
Copyright (c) 1999-2008 Philip Kendall, Marek Januszewski, Fredrick Meunier
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
double
|
||||
compat_timer_get_time( void )
|
||||
{
|
||||
return GetTickCount() / 1000.0;
|
||||
}
|
||||
|
||||
void
|
||||
compat_timer_sleep( int ms )
|
||||
{
|
||||
Sleep( ms );
|
||||
}
|
||||
+1
-14
@@ -33,10 +33,8 @@
|
||||
#include "fuse.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "machine.h"
|
||||
#include "movie.h"
|
||||
#include "peripherals/scld.h"
|
||||
#include "rectangle.h"
|
||||
#include "screenshot.h"
|
||||
#include "settings.h"
|
||||
#include "spectrum.h"
|
||||
#include "ui/ui.h"
|
||||
@@ -926,15 +924,7 @@ update_ui_screen( void )
|
||||
|
||||
if( settings_current.frame_rate <= ++frame_count ) {
|
||||
frame_count = 0;
|
||||
if( movie_recording ) {
|
||||
movie_start_frame();
|
||||
}
|
||||
|
||||
if( display_redraw_all ) {
|
||||
if( movie_recording ) {
|
||||
movie_add_area( 0, 0, DISPLAY_ASPECT_WIDTH >> 3,
|
||||
DISPLAY_SCREEN_HEIGHT );
|
||||
}
|
||||
uidisplay_area( 0, 0,
|
||||
scale * DISPLAY_ASPECT_WIDTH,
|
||||
scale * DISPLAY_SCREEN_HEIGHT );
|
||||
@@ -943,10 +933,7 @@ update_ui_screen( void )
|
||||
for( i = 0, ptr = rectangle_inactive;
|
||||
i < rectangle_inactive_count;
|
||||
i++, ptr++ ) {
|
||||
if( movie_recording ) {
|
||||
movie_add_area( ptr->x, ptr->y, ptr->w, ptr->h );
|
||||
}
|
||||
uidisplay_area( 8 * scale * ptr->x, scale * ptr->y,
|
||||
uidisplay_area( 8 * scale * ptr->x, scale * ptr->y,
|
||||
8 * scale * ptr->w, scale * ptr->h );
|
||||
}
|
||||
}
|
||||
|
||||
+4
-26
@@ -63,7 +63,6 @@
|
||||
#include "machines/machines_periph.h"
|
||||
#include "memory_pages.h"
|
||||
#include "module.h"
|
||||
#include "movie.h"
|
||||
#include "mempool.h"
|
||||
#include "peripherals/ay.h"
|
||||
#include "peripherals/dck.h"
|
||||
@@ -93,10 +92,8 @@
|
||||
#include "peripherals/usource.h"
|
||||
#include "phantom_typist.h"
|
||||
#include "pokefinder/pokemem.h"
|
||||
#include "profile.h"
|
||||
#include "psg.h"
|
||||
#include "rzx.h"
|
||||
#include "screenshot.h"
|
||||
#include "settings.h"
|
||||
#include "slt.h"
|
||||
#include "snapshot.h"
|
||||
@@ -104,7 +101,6 @@
|
||||
#include "spectrum.h"
|
||||
#include "tape.h"
|
||||
#include "timer/timer.h"
|
||||
#include "ui/scaler/scaler.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uimedia.h"
|
||||
#include "unittests/unittests.h"
|
||||
@@ -325,11 +321,9 @@ run_startup_manager( int *argc, char ***argv )
|
||||
phantom_typist_register_startup();
|
||||
plusd_register_startup();
|
||||
printer_register_startup();
|
||||
profile_register_startup();
|
||||
psg_register_startup();
|
||||
rzx_register_startup();
|
||||
scld_register_startup();
|
||||
screenshot_register_startup();
|
||||
settings_register_startup();
|
||||
setuid_register_startup();
|
||||
simpleide_register_startup();
|
||||
@@ -356,7 +350,6 @@ run_startup_manager( int *argc, char ***argv )
|
||||
static int fuse_init(int argc, char **argv)
|
||||
{
|
||||
int error, first_arg;
|
||||
char *start_scaler;
|
||||
start_files_t start_files;
|
||||
|
||||
/* Seed the bad but widely-available random number
|
||||
@@ -387,27 +380,14 @@ static int fuse_init(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
start_scaler = utils_safe_strdup( settings_current.start_scaler_mode );
|
||||
|
||||
fuse_show_copyright();
|
||||
|
||||
/* Everything between here and the scaler_select_id() below owns
|
||||
start_scaler and has to release it before bailing out. machine_select_id()
|
||||
is a path users reach: selecting a model whose ROMs are not installed
|
||||
fails there, and a libretro core retries on the next content load rather
|
||||
than exiting. */
|
||||
if( run_startup_manager( &argc, &argv ) ) {
|
||||
libspectrum_free( start_scaler );
|
||||
return 1;
|
||||
}
|
||||
/* machine_select_id() is a path users reach: selecting a model whose ROMs
|
||||
are not installed fails there, and a libretro core retries on the next
|
||||
content load rather than exiting. */
|
||||
if( run_startup_manager( &argc, &argv ) ) return 1;
|
||||
|
||||
error = machine_select_id( settings_current.start_machine );
|
||||
if( error ) {
|
||||
libspectrum_free( start_scaler );
|
||||
return error;
|
||||
}
|
||||
|
||||
error = scaler_select_id( start_scaler ); libspectrum_free( start_scaler );
|
||||
if( error ) return error;
|
||||
|
||||
if( setup_start_files( &start_files ) ) return 1;
|
||||
@@ -420,7 +400,6 @@ static int fuse_init(int argc, char **argv)
|
||||
if( ui_mouse_present ) ui_mouse_grabbed = ui_mouse_grab( 1 );
|
||||
|
||||
fuse_emulation_paused = 0;
|
||||
movie_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -997,7 +976,6 @@ do_start_files( start_files_t *start_files )
|
||||
/* Tidy-up function called at end of emulation */
|
||||
static int fuse_end(void)
|
||||
{
|
||||
movie_stop(); /* stop movie recording */
|
||||
|
||||
startup_manager_run_end();
|
||||
|
||||
|
||||
@@ -236,11 +236,6 @@ keypress( const input_event_key_t *event )
|
||||
{
|
||||
int swallow;
|
||||
|
||||
if( ui_widget_level >= 0 ) {
|
||||
ui_widget_keyhandler( event->native_key );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Escape => ask UI to end mouse grab, return if grab ended */
|
||||
if( event->native_key == INPUT_KEY_Escape && ui_mouse_grabbed ) {
|
||||
ui_mouse_grabbed = ui_mouse_release( 0 );
|
||||
@@ -364,24 +359,6 @@ do_joystick( const input_event_joystick_t *joystick_event, int press )
|
||||
{
|
||||
int which;
|
||||
|
||||
#ifdef USE_WIDGET
|
||||
if( ui_widget_level >= 0 ) {
|
||||
if( press ) ui_widget_keyhandler( joystick_event->button );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef GEKKO /* Home button opens the menu on Wii */
|
||||
switch( joystick_event->button ) {
|
||||
case INPUT_JOYSTICK_FIRE_2:
|
||||
if( press ) ui_popup_menu( INPUT_KEY_F1 );
|
||||
break;
|
||||
|
||||
default: break; /* Remove gcc warning */
|
||||
|
||||
}
|
||||
#endif /* #ifndef GEKKO */
|
||||
|
||||
#endif /* #ifdef USE_WIDGET */
|
||||
|
||||
which = joystick_event->which;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "machines/tc2068.h"
|
||||
#include "memory_pages.h"
|
||||
#include "module.h"
|
||||
#include "movie.h"
|
||||
#include "peripherals/ula.h"
|
||||
#include "pokefinder/pokemem.h"
|
||||
#include "rzx.h"
|
||||
@@ -141,7 +140,6 @@ machine_select( libspectrum_machine type )
|
||||
|
||||
/* We don't want to have to deal with screen size changes in the movie code
|
||||
and recording movies where we change machines seems pretty obscure */
|
||||
movie_stop();
|
||||
|
||||
for( i=0; i < machine_count; i++ ) {
|
||||
if( machine_types[i]->machine == type ) {
|
||||
|
||||
-1248
File diff suppressed because it is too large.
Load diff
-490
@@ -1,490 +0,0 @@
|
||||
/* movie.c: Routines for creating 'movie' with border
|
||||
Copyright (c) 2006-2015 Gergely Szasz
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#define ZLIB_CONST
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "display.h"
|
||||
#include "fuse.h"
|
||||
#include "machine.h"
|
||||
#include "movie.h"
|
||||
#include "movie_tables.h"
|
||||
#include "options.h"
|
||||
#include "peripherals/scld.h"
|
||||
#include "screenshot.h"
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#undef MOVIE_DEBUG_PRINT
|
||||
|
||||
/*
|
||||
FMF - Fuse Movie File
|
||||
|
||||
Fuse Movie File:
|
||||
File Header:
|
||||
off len data description
|
||||
0 4 "FMF_" Magic header
|
||||
4 2 "V1" Version
|
||||
6 1 <e|E> Endianness (e - little / E- big)
|
||||
7 1 <U|Z> Compression ( U - uncompressed / Z - zlib compressed )
|
||||
8 1 # Frame rate ( 1:# )
|
||||
9 1 <$|R|C|X> Screen type
|
||||
10 1 <A|B|C|D|E> timing code
|
||||
11 1 <P|U|A> Sound encoding
|
||||
12 2 Freq Sound freq in Hz
|
||||
14 1 <S|M> Sound stereo / mono
|
||||
15 1 "\n" padding (<new line>)
|
||||
|
||||
e.g. FMF_V1eZ\001$AU\000\175M -> little endian compressed normal screen, 48k timing, u-Law mono 32000Hz sound
|
||||
Data
|
||||
Frame data header
|
||||
off len data description
|
||||
0 1 <$|S|N|X> Data chunk type
|
||||
|
||||
$ -> screen area (slice)
|
||||
off len data description
|
||||
0 1 x X coord (0-39)
|
||||
1 2 y Y coord (0-239)
|
||||
3 1 w width (1-40)
|
||||
4 2 h height (1-240)
|
||||
6 ? runlength encoded data bytes
|
||||
bitmap1; attrib1 ZX$/TX$/HiCol ($/X/C)
|
||||
bitmap1; bitmap2; attrib HiR (R)
|
||||
runlength encoding:
|
||||
abcdefghbb#gg# -> two identical byte plus len code a #+2 len
|
||||
|
||||
S -> sound chunk
|
||||
off len data description
|
||||
0 1 <P|U|A> sound encoding type P-> 16bit signed PCM, U -> u-Law, A -> A-Law
|
||||
1 2 Freq sound freq in Hz
|
||||
3 1 <S|M> channels S-> stereo, M-> mono
|
||||
4 2 length-1 length in frames (0-65535) -> 1-65536 sound frame
|
||||
|
||||
N -> New Frame
|
||||
off len data description
|
||||
0 1 # frame rate 1:# (1:1 -> ~50/s, 1:2 -> ~25/a ...)
|
||||
1 1 <$|R|C|X> screen type $ - standard screen,
|
||||
R - HiRes,
|
||||
C - HiColor,
|
||||
X - standard screen on Timex (double size)
|
||||
2 1 <A|B|C|D> frame timing code A - 16, 48, TC2048, TC2068, Scorpion, SE
|
||||
B - 128, +2, +2A, +3, +3E
|
||||
C - TS2068
|
||||
D - Pentagon
|
||||
E - 48 NTSC
|
||||
In a frame there are no, one or several screen rectangle, changed from
|
||||
the previouse frame.
|
||||
|
||||
X -> End of recording
|
||||
off len data description
|
||||
There is no any data... It mark the end of the last frame. So we can
|
||||
concat several FMF file without any problem...
|
||||
*/
|
||||
|
||||
int movie_recording = 0;
|
||||
static int movie_paused = 0;
|
||||
|
||||
static int frame_no, slice_no;
|
||||
|
||||
static RFILE *of = NULL; /* out file */
|
||||
static int fmf_screen;
|
||||
static libspectrum_byte head[8];
|
||||
static int freq = 0;
|
||||
static char stereo = 'M';
|
||||
static char format = '?';
|
||||
static int framesiz = 4;
|
||||
|
||||
static libspectrum_byte sbuff[ 4096 ];
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#define ZBUF_SIZE 8192
|
||||
static int fmf_compr = -1;
|
||||
static z_stream zstream;
|
||||
static unsigned char zbuf_o[ ZBUF_SIZE ];
|
||||
#endif /* HAVE_ZLIB_H */
|
||||
|
||||
static unsigned char alaw_table[2048 + 1] = { ALAW_ENC_TAB };
|
||||
|
||||
void movie_start_frame( void );
|
||||
void movie_init_sound( int f, int s );
|
||||
|
||||
static char
|
||||
get_timing( void )
|
||||
{
|
||||
switch( machine_current->machine ) {
|
||||
case LIBSPECTRUM_MACHINE_16:
|
||||
case LIBSPECTRUM_MACHINE_48:
|
||||
case LIBSPECTRUM_MACHINE_TC2048:
|
||||
case LIBSPECTRUM_MACHINE_TC2068:
|
||||
case LIBSPECTRUM_MACHINE_SCORP:
|
||||
case LIBSPECTRUM_MACHINE_SE:
|
||||
return 'A';
|
||||
case LIBSPECTRUM_MACHINE_128:
|
||||
case LIBSPECTRUM_MACHINE_PLUS2:
|
||||
case LIBSPECTRUM_MACHINE_PLUS2A:
|
||||
case LIBSPECTRUM_MACHINE_PLUS3:
|
||||
case LIBSPECTRUM_MACHINE_PLUS3E:
|
||||
return 'B';
|
||||
case LIBSPECTRUM_MACHINE_TS2068:
|
||||
return 'C';
|
||||
case LIBSPECTRUM_MACHINE_PENT:
|
||||
case LIBSPECTRUM_MACHINE_PENT512:
|
||||
case LIBSPECTRUM_MACHINE_PENT1024:
|
||||
return 'D';
|
||||
case LIBSPECTRUM_MACHINE_48_NTSC:
|
||||
return 'E';
|
||||
case LIBSPECTRUM_MACHINE_UNKNOWN:
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
static char
|
||||
get_screentype( void )
|
||||
{
|
||||
if( machine_current->timex ) { /* ALTDFILE and default */
|
||||
if( scld_last_dec.name.hires )
|
||||
return 'R'; /* HIRES screen */
|
||||
else if( scld_last_dec.name.b1 )
|
||||
return 'C'; /* HICOLOR screen */
|
||||
else
|
||||
return 'X'; /* STANDARD screen on timex machine */
|
||||
}
|
||||
return '$'; /* STANDARD screen */
|
||||
}
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
static void
|
||||
fwrite_compr( const void *b, size_t n, size_t m, RFILE *f )
|
||||
{
|
||||
if( fmf_compr == 0 ) {
|
||||
filestream_write( f, b, (int64_t)( n ) * ( m ) );
|
||||
} else {
|
||||
zstream.avail_in = n * m;
|
||||
zstream.next_in = b;
|
||||
zstream.avail_out = ZBUF_SIZE;
|
||||
zstream.next_out = zbuf_o;
|
||||
do {
|
||||
deflate( &zstream, Z_NO_FLUSH );
|
||||
while( zstream.avail_out != ZBUF_SIZE ) {
|
||||
filestream_write( of, zbuf_o, (int64_t)( ZBUF_SIZE - zstream.avail_out ) * ( 1 ) );
|
||||
zstream.avail_out = ZBUF_SIZE;
|
||||
zstream.next_out = zbuf_o;
|
||||
deflate( &zstream, Z_NO_FLUSH );
|
||||
}
|
||||
} while ( zstream.avail_in != 0 );
|
||||
}
|
||||
}
|
||||
#else /* HAVE_ZLIB_H */
|
||||
#define fwrite_compr fwrite
|
||||
#endif /* HAVE_ZLIB_H */
|
||||
|
||||
static void
|
||||
movie_compress_area( int x, int y, int w, int h, int s )
|
||||
{
|
||||
libspectrum_dword *dpoint, *dline;
|
||||
libspectrum_byte d, d1, *b;
|
||||
libspectrum_byte buff[ 960 ];
|
||||
int w0, h0, l;
|
||||
|
||||
dline = &display_last_screen[x + 40 * y];
|
||||
b = buff; l = -1;
|
||||
d1 = ( ( *dline >> s ) & 0xff ) + 1; /* *d1 != dpoint :-) */
|
||||
|
||||
for( h0 = h; h0 > 0; h0--, dline += 40 ) {
|
||||
dpoint = dline;
|
||||
for( w0 = w; w0 > 0; w0--, dpoint++) {
|
||||
d = ( *dpoint >> s ) & 0xff; /* bitmask1 */
|
||||
if( d != d1 ) {
|
||||
if( l > -1 ) { /* save running length 0-255 */
|
||||
*b++ = l;
|
||||
l = -1; /* reset l */
|
||||
}
|
||||
*b++ = d1 = d;
|
||||
} else if( l >= 0 ) {
|
||||
if( l == 255 ) { /* close run, and may start a new? */
|
||||
*b++ = l; *b++ = d; l = -1;
|
||||
} else {
|
||||
l++;
|
||||
}
|
||||
} else {
|
||||
*b++ = d;
|
||||
l++;
|
||||
}
|
||||
/* d1 = d; */
|
||||
}
|
||||
if( b - buff > 960 - 128 ) { /* worst case 40*1.5 per line */
|
||||
fwrite_compr( buff, b - buff, 1, of );
|
||||
b = buff;
|
||||
}
|
||||
}
|
||||
if( l > -1 ) { /* save running length 0-255 */
|
||||
*b++ = l;
|
||||
}
|
||||
if( b != buff ) { /* dump remain */
|
||||
fwrite_compr( buff, b - buff, 1, of );
|
||||
}
|
||||
}
|
||||
|
||||
/* Fetch pixel (x, y). On a Timex this will be a point on a 640x480 canvas,
|
||||
on a Sinclair/Amstrad/Russian clone this will be a point on a 320x240
|
||||
canvas */
|
||||
|
||||
/* x: 0 - 39; y: 0 - 239 */
|
||||
|
||||
/* abcdefghijkl... cc# where # mean cc + # c char*/
|
||||
|
||||
void
|
||||
movie_add_area( int x, int y, int w, int h )
|
||||
{
|
||||
if( movie_paused ) {
|
||||
movie_start_frame();
|
||||
return;
|
||||
}
|
||||
head[0] = '$'; /* RLE compressed data... */
|
||||
head[1] = x;
|
||||
head[2] = y & 0xff;
|
||||
head[3] = y >> 8;
|
||||
head[4] = w;
|
||||
head[5] = h & 0xff;
|
||||
head[6] = h >> 8;
|
||||
fwrite_compr( head, 7, 1, of );
|
||||
movie_compress_area( x, y, w, h, 0 ); /* Bitmap1 */
|
||||
movie_compress_area( x, y, w, h, 8 ); /* Attrib/B2 */
|
||||
if( fmf_screen == 'R' ) {
|
||||
movie_compress_area( x, y, w, h, 16 ); /* HiRes attrib */
|
||||
}
|
||||
slice_no++;
|
||||
}
|
||||
|
||||
static void
|
||||
movie_start_fmf( const char *name )
|
||||
{
|
||||
if( ( of = filestream_open( name, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE ) ) == NULL ) { /* trunc old file ? or append ? */
|
||||
ui_error( UI_ERROR_ERROR, "error opening movie file '%s': %s", name,
|
||||
strerror( errno ) );
|
||||
return;
|
||||
}
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
filestream_write( of, "FMF_V1E", (int64_t)( 7 ) * ( 1 ) ); /* write magic header Fuse Movie File */
|
||||
#else /* WORDS_BIGENDIAN */
|
||||
filestream_write( of, "FMF_V1e", (int64_t)( 7 ) * ( 1 ) ); /* write magic header Fuse Movie File */
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
#ifdef HAVE_ZLIB_H
|
||||
if( option_enumerate_movie_movie_compr() == 0 ) {
|
||||
fmf_compr = 0;
|
||||
filestream_write( of, "U", 1 ); /* not compressed */
|
||||
} else {
|
||||
fmf_compr = Z_DEFAULT_COMPRESSION;
|
||||
filestream_write( of, "Z", 1 ); /* compressed */
|
||||
}
|
||||
if( fmf_compr != 0 ) {
|
||||
zstream.zalloc = Z_NULL;
|
||||
zstream.zfree = Z_NULL;
|
||||
zstream.opaque = Z_NULL;
|
||||
zstream.avail_in = 0;
|
||||
zstream.next_in = Z_NULL;
|
||||
deflateInit( &zstream, fmf_compr );
|
||||
}
|
||||
#else /* HAVE_ZLIB_H */
|
||||
filestream_write( of, "U", 1 ); /* cannot be compressed */
|
||||
#endif /* HAVE_ZLIB_H */
|
||||
movie_init_sound( settings_current.sound_freq,
|
||||
sound_stereo_ay != SOUND_STEREO_AY_NONE );
|
||||
head[0] = settings_current.frame_rate;
|
||||
head[1] = get_screentype();
|
||||
head[2] = get_timing();
|
||||
head[3] = format; /* sound format */
|
||||
head[4] = freq & 0xff;
|
||||
head[5] = freq >> 8;
|
||||
head[6] = stereo;
|
||||
head[7] = '\n'; /* padding */
|
||||
filestream_write( of, head, (int64_t)( 8 ) * ( 1 ) ); /* write initial params */
|
||||
movie_add_area( 0, 0, 40, 240 );
|
||||
}
|
||||
|
||||
void
|
||||
movie_start( const char *name ) /* some init, open file (name)*/
|
||||
{
|
||||
frame_no = slice_no = 0;
|
||||
if( name == NULL || *name == '\0' )
|
||||
name = "fuse.fmf"; /* fuse movie file */
|
||||
|
||||
movie_start_fmf( name );
|
||||
movie_recording = 1;
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_RECORDING, 1 );
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_PAUSE, 1 );
|
||||
}
|
||||
|
||||
void
|
||||
movie_stop( void )
|
||||
{
|
||||
if( !movie_paused && !movie_recording ) return;
|
||||
|
||||
fwrite_compr( "X", 1, 1, of ); /* End of Recording! */
|
||||
#ifdef HAVE_ZLIB_H
|
||||
{
|
||||
if( fmf_compr != 0 ) { /* close zlib */
|
||||
zstream.avail_in = 0;
|
||||
do {
|
||||
zstream.avail_out = ZBUF_SIZE;
|
||||
zstream.next_out = zbuf_o;
|
||||
deflate( &zstream, Z_SYNC_FLUSH );
|
||||
if( zstream.avail_out != ZBUF_SIZE )
|
||||
filestream_write( of, zbuf_o, (int64_t)( ZBUF_SIZE - zstream.avail_out ) * ( 1 ) );
|
||||
} while ( zstream.avail_out != ZBUF_SIZE );
|
||||
deflateEnd( &zstream );
|
||||
fmf_compr = -1;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_ZLIB_H */
|
||||
format = '?';
|
||||
if( of ) {
|
||||
filestream_close( of );
|
||||
of = NULL;
|
||||
}
|
||||
#ifdef MOVIE_DEBUG_PRINT
|
||||
filestream_printf( stderr, "Debug movie: saved %d.%d frame(.slice)\n", frame_no, slice_no );
|
||||
#endif /* MOVIE_DEBUG_PRINT */
|
||||
movie_recording = 0;
|
||||
movie_paused = 0;
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_RECORDING, 0 );
|
||||
}
|
||||
|
||||
void
|
||||
movie_pause( void )
|
||||
{
|
||||
if( !movie_paused && !movie_recording ) return;
|
||||
|
||||
if( movie_recording ) {
|
||||
movie_recording = 0;
|
||||
movie_paused = 1;
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_PAUSE, 0 );
|
||||
} else {
|
||||
movie_recording = 1;
|
||||
movie_paused = 1;
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_PAUSE, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
movie_init_sound( int f, int s )
|
||||
{
|
||||
/* initialise sound format */
|
||||
format = option_enumerate_movie_movie_compr() == 2 ? 'A' : 'P';
|
||||
freq = f;
|
||||
stereo = ( s ? 'S' : 'M' );
|
||||
framesiz = ( stereo == 'S' ? 2 : 1 ) * ( format == 'P' ? 2 : 1 );
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_alaw( libspectrum_signed_word *buff, int len )
|
||||
{
|
||||
int i = 0;
|
||||
while( len-- ) {
|
||||
if( *buff >= 0)
|
||||
sbuff[i++] = alaw_table[*buff >> 4];
|
||||
else
|
||||
sbuff[i++] = 0x7f & alaw_table [- *buff >> 4];
|
||||
buff++;
|
||||
if( i == 4096 ) {
|
||||
i = 0;
|
||||
fwrite_compr( sbuff, 4096, 1, of ); /* write frame */
|
||||
}
|
||||
}
|
||||
if( i )
|
||||
fwrite_compr( sbuff, i, 1, of ); /* write remaind */
|
||||
}
|
||||
|
||||
static void
|
||||
add_sound( libspectrum_signed_word *buff, int len )
|
||||
{
|
||||
head[0] = 'S'; /* sound frame */
|
||||
head[1] = format; /* sound format */
|
||||
head[2] = freq & 0xff;
|
||||
head[3] = freq >> 8;
|
||||
head[4] = stereo;
|
||||
len--; /*len - 1*/
|
||||
head[5] = len & 0xff;
|
||||
head[6] = len >> 8;
|
||||
len++; /* len :-) */
|
||||
fwrite_compr( head, 7, 1, of ); /* Sound frame */
|
||||
if( format == 'P' )
|
||||
fwrite_compr( buff, len * framesiz , 1, of ); /* write frame */
|
||||
else if( format == 'A' )
|
||||
write_alaw( buff, len * framesiz );
|
||||
}
|
||||
|
||||
void
|
||||
movie_add_sound( libspectrum_signed_word *buff, int len )
|
||||
{
|
||||
while( len ) {
|
||||
if( stereo == 'S' ) {
|
||||
add_sound( buff, len > 131072 ? 65536 : len >> 1 );
|
||||
buff += len > 131072 ? 131072 : len;
|
||||
len -= len > 131072 ? 131072 : len;
|
||||
} else {
|
||||
add_sound( buff, len > 65536 ? 65536 : len );
|
||||
buff += len > 65536 ? 65536 : len;
|
||||
len -= len > 65536 ? 65536 : len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
movie_start_frame( void )
|
||||
{
|
||||
/* $ - ZX$, T - TX$, C - HiCol, R - HiRes */
|
||||
head[0] = 'N';
|
||||
head[1] = settings_current.frame_rate;
|
||||
head[2] = get_screentype();
|
||||
head[3] = get_timing();
|
||||
fwrite_compr( head, 4, 1, of ); /* New frame! */
|
||||
frame_no++;
|
||||
if( movie_paused ) {
|
||||
movie_paused = 0;
|
||||
movie_add_area( 0, 0, 40, 240 );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
movie_init( void )
|
||||
{
|
||||
/* start movie recording if user requested... */
|
||||
if( settings_current.movie_start )
|
||||
movie_start( settings_current.movie_start );
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* movie.h: header for movie.c
|
||||
Copyright (c) 2006 Gergely Szasz
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
FSMF - Fuse Spectrum Movie File
|
||||
*/
|
||||
extern int movie_recording;
|
||||
|
||||
void movie_init( void );
|
||||
void movie_start( const char *name );
|
||||
void movie_stop( void );
|
||||
void movie_pause( void );
|
||||
void movie_add_area( int x, int y, int w, int h );
|
||||
void movie_start_frame( void );
|
||||
void movie_init_sound( int f, int s );
|
||||
void movie_add_sound( libspectrum_signed_word *buf, int len );
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
/* profile.c: Z80 profiler
|
||||
Copyright (c) 2005-2016 Philip Kendall
|
||||
|
||||
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 Kendall <[email protected]>
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "module.h"
|
||||
#include "profile.h"
|
||||
#include "ui/ui.h"
|
||||
#include "z80/z80.h"
|
||||
|
||||
int profile_active = 0;
|
||||
|
||||
static int total_tstates[ 0x10000 ];
|
||||
static libspectrum_word profile_last_pc;
|
||||
static libspectrum_dword profile_last_tstates;
|
||||
|
||||
static void profile_from_snapshot( libspectrum_snap *snap GCC_UNUSED );
|
||||
|
||||
static module_info_t profile_module_info = {
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
profile_from_snapshot,
|
||||
NULL,
|
||||
|
||||
};
|
||||
|
||||
static int
|
||||
profile_init( void *context )
|
||||
{
|
||||
module_register( &profile_module_info );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
profile_register_startup( void )
|
||||
{
|
||||
startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID };
|
||||
startup_manager_register( STARTUP_MANAGER_MODULE_PROFILE, dependencies,
|
||||
ARRAY_SIZE( dependencies ), profile_init, NULL,
|
||||
NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
init_profiling_counters( void )
|
||||
{
|
||||
profile_last_pc = z80.pc.w;
|
||||
profile_last_tstates = tstates;
|
||||
}
|
||||
|
||||
void
|
||||
profile_start( void )
|
||||
{
|
||||
memset( total_tstates, 0, sizeof( total_tstates ) );
|
||||
|
||||
profile_active = 1;
|
||||
init_profiling_counters();
|
||||
|
||||
/* Schedule an event to ensure that the main z80 emulation loop recognises
|
||||
profiling is turned on; otherwise problems occur if we started while
|
||||
the debugger was active (bug #54) */
|
||||
event_add( tstates, event_type_null );
|
||||
|
||||
ui_menu_activate( UI_MENU_ITEM_MACHINE_PROFILER, 1 );
|
||||
}
|
||||
|
||||
void
|
||||
profile_map( libspectrum_word pc )
|
||||
{
|
||||
total_tstates[ profile_last_pc ] += tstates - profile_last_tstates;
|
||||
|
||||
profile_last_pc = z80.pc.w;
|
||||
profile_last_tstates = tstates;
|
||||
}
|
||||
|
||||
void
|
||||
profile_frame( libspectrum_dword frame_length )
|
||||
{
|
||||
profile_last_tstates -= frame_length;
|
||||
}
|
||||
|
||||
/* On snapshot load, PC and the tstate counter will jump so reset our
|
||||
current views of these */
|
||||
static void
|
||||
profile_from_snapshot( libspectrum_snap *snap GCC_UNUSED )
|
||||
{
|
||||
init_profiling_counters();
|
||||
}
|
||||
|
||||
void
|
||||
profile_finish( const char *filename )
|
||||
{
|
||||
RFILE *f;
|
||||
size_t i;
|
||||
|
||||
f = filestream_open( filename, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE );
|
||||
if( !f ) {
|
||||
ui_error( UI_ERROR_ERROR, "unable to open profile map '%s' for writing",
|
||||
filename );
|
||||
return;
|
||||
}
|
||||
|
||||
for( i = 0; i < 0x10000; i++ ) {
|
||||
|
||||
if( !total_tstates[ i ] ) continue;
|
||||
|
||||
filestream_printf( f, "0x%04lx,%d\n", (unsigned long)i, total_tstates[ i ] );
|
||||
|
||||
}
|
||||
|
||||
filestream_close( f );
|
||||
|
||||
profile_active = 0;
|
||||
|
||||
/* Again, schedule an event to ensure this change is picked up by
|
||||
the main loop */
|
||||
event_add( tstates, event_type_null );
|
||||
|
||||
ui_menu_activate( UI_MENU_ITEM_MACHINE_PROFILER, 0 );
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* profile.h: Z80 profiler
|
||||
Copyright (c) 2005-2016 Philip Kendall
|
||||
|
||||
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 Kendall <[email protected]>
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_PROFILE_H
|
||||
#define FUSE_PROFILE_H
|
||||
|
||||
extern int profile_active;
|
||||
|
||||
void profile_register_startup( void );
|
||||
void profile_start( void );
|
||||
void profile_map( libspectrum_word pc );
|
||||
void profile_frame( libspectrum_dword frame_length );
|
||||
void profile_finish( const char *filename );
|
||||
|
||||
#endif /* #ifndef FUSE_PROFILE_H */
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "fuse.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "machine.h"
|
||||
#include "movie.h"
|
||||
#include "peripherals/ula.h"
|
||||
#include "rzx.h"
|
||||
#include "settings.h"
|
||||
@@ -193,7 +192,6 @@ int rzx_stop_recording( void )
|
||||
|
||||
/* Stop recording data */
|
||||
rzx_recording = 0;
|
||||
if( settings_current.movie_stop_after_rzx ) movie_stop();
|
||||
|
||||
/* Embed final snapshot */
|
||||
if( !rzx_competition_mode ) rzx_add_snap( rzx, 0 );
|
||||
@@ -392,7 +390,6 @@ int rzx_stop_playback( int add_interrupt )
|
||||
if( !rzx_playback ) return 0;
|
||||
|
||||
rzx_playback = 0;
|
||||
if( settings_current.movie_stop_after_rzx ) movie_stop();
|
||||
|
||||
ui_menu_activate( UI_MENU_ITEM_RECORDING, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_RECORDING_ROLLBACK, 0 );
|
||||
|
||||
@@ -1,708 +0,0 @@
|
||||
/* screenshot.c: Routines for handling .png and .scr screenshots
|
||||
Copyright (c) 2002-2018 Philip Kendall, Fredrick Meunier
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "machine.h"
|
||||
#include "peripherals/scld.h"
|
||||
#include "screenshot.h"
|
||||
#include "settings.h"
|
||||
#include "ui/scaler/scaler.h"
|
||||
#include "ui/ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MONO_BITMAP_SIZE 6144
|
||||
#define HICOLOUR_SCR_SIZE (2 * MONO_BITMAP_SIZE)
|
||||
#define MLT_SIZE (2 * MONO_BITMAP_SIZE)
|
||||
#define HIRES_ATTR HICOLOUR_SCR_SIZE
|
||||
#define HIRES_SCR_SIZE (HICOLOUR_SCR_SIZE + 1)
|
||||
|
||||
#ifdef USE_LIBPNG
|
||||
|
||||
#include <png.h>
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#define ZLIB_CONST
|
||||
#include <zlib.h>
|
||||
#endif /* #ifdef HAVE_ZLIB_H */
|
||||
|
||||
static int get_rgb32_data( libspectrum_byte *rgb32_data, size_t stride,
|
||||
size_t height, size_t width );
|
||||
static int rgb32_to_rgb24( libspectrum_byte *rgb24_data, size_t rgb24_stride,
|
||||
libspectrum_byte *rgb32_data, size_t rgb32_stride,
|
||||
size_t height, size_t width );
|
||||
static void fill_rgb32_margin( libspectrum_byte *rgb32_data, size_t stride,
|
||||
size_t height, size_t width );
|
||||
|
||||
/* The biggest size screen (in units of DISPLAY_ASPECT_WIDTH x
|
||||
DISPLAY_SCREEN_HEIGHT ie a Timex screen is size 2) we will be
|
||||
creating via the scalers */
|
||||
#define MAX_SIZE 4
|
||||
|
||||
/* Pixels arround the RGB image for scalers that "smear" the screen */
|
||||
#define K_MARGIN 2
|
||||
|
||||
/* The space used for drawing the screen image on */
|
||||
static libspectrum_byte *rgb_data = NULL;
|
||||
static libspectrum_byte *scaled_data;
|
||||
static libspectrum_byte *png_data = NULL;
|
||||
|
||||
int
|
||||
screenshot_write( const char *filename, scaler_type scaler )
|
||||
{
|
||||
RFILE *f;
|
||||
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
libspectrum_byte *row_pointers[ MAX_SIZE * DISPLAY_SCREEN_HEIGHT ];
|
||||
libspectrum_byte *rgb_data_centered;
|
||||
size_t rgb_stride = ( DISPLAY_ASPECT_WIDTH + K_MARGIN * 2 ) * 4,
|
||||
scaled_stride = MAX_SIZE * DISPLAY_ASPECT_WIDTH * 4,
|
||||
png_stride = MAX_SIZE * DISPLAY_ASPECT_WIDTH * 3;
|
||||
size_t y, base_height, base_width, height, width;
|
||||
int error;
|
||||
|
||||
if( machine_current->timex ) {
|
||||
base_height = 2 * DISPLAY_SCREEN_HEIGHT;
|
||||
base_width = DISPLAY_SCREEN_WIDTH;
|
||||
} else {
|
||||
base_height = DISPLAY_SCREEN_HEIGHT;
|
||||
base_width = DISPLAY_ASPECT_WIDTH;
|
||||
}
|
||||
|
||||
/* Allocate buffers on demand */
|
||||
if( !rgb_data )
|
||||
rgb_data =
|
||||
libspectrum_new( libspectrum_byte,
|
||||
( DISPLAY_SCREEN_HEIGHT + K_MARGIN * 2 ) * rgb_stride );
|
||||
|
||||
if( !scaled_data )
|
||||
scaled_data = libspectrum_new( libspectrum_byte,
|
||||
MAX_SIZE * DISPLAY_SCREEN_HEIGHT *
|
||||
scaled_stride );
|
||||
|
||||
if( !png_data )
|
||||
png_data = libspectrum_new( libspectrum_byte,
|
||||
MAX_SIZE * DISPLAY_SCREEN_HEIGHT * png_stride );
|
||||
|
||||
/* first pixel of rgb_data (without margin) */
|
||||
rgb_data_centered = rgb_data + K_MARGIN * rgb_stride + K_MARGIN * 4;
|
||||
|
||||
/* Change from paletted data to RGB data */
|
||||
error = get_rgb32_data( rgb_data_centered, rgb_stride, base_height,
|
||||
base_width );
|
||||
if( error ) return error;
|
||||
|
||||
/* Initialise margin for scalers that "smear" the screen */
|
||||
if( scaler_get_flags( scaler ) & SCALER_FLAGS_EXPAND )
|
||||
fill_rgb32_margin( rgb_data, rgb_stride, base_height, base_width );
|
||||
|
||||
/* Actually scale the data here */
|
||||
scaler_get_proc32( scaler )( rgb_data_centered, rgb_stride, scaled_data,
|
||||
scaled_stride, base_width, base_height );
|
||||
|
||||
height = base_height * scaler_get_scaling_factor( scaler );
|
||||
width = base_width * scaler_get_scaling_factor( scaler );
|
||||
|
||||
/* Reduce from RGB(padding byte) to just RGB */
|
||||
error = rgb32_to_rgb24( png_data, png_stride, scaled_data, scaled_stride,
|
||||
height, width );
|
||||
if( error ) return error;
|
||||
|
||||
for( y = 0; y < height; y++ )
|
||||
row_pointers[y] = &png_data[ y * png_stride ];
|
||||
|
||||
f = filestream_open( filename, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE );
|
||||
if( !f ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't open `%s': %s", filename,
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL );
|
||||
if( !png_ptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't allocate png_ptr" );
|
||||
filestream_close( f );
|
||||
return 1;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct( png_ptr );
|
||||
if( !info_ptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't allocate info_ptr" );
|
||||
png_destroy_write_struct( &png_ptr, NULL );
|
||||
filestream_close( f );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set up the error handling; libpng will return to here if it
|
||||
encounters an error */
|
||||
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "Error from libpng" );
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
filestream_close( f );
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_init_io( png_ptr, f );
|
||||
|
||||
/* Make files as small as possible */
|
||||
png_set_compression_level( png_ptr, Z_BEST_COMPRESSION );
|
||||
|
||||
png_set_IHDR( png_ptr, info_ptr,
|
||||
width, height, 8,
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT );
|
||||
|
||||
png_set_rows( png_ptr, info_ptr, row_pointers );
|
||||
|
||||
png_write_png( png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL );
|
||||
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
if( filestream_close( f ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't close `%s': %s", filename,
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_rgb32_data( libspectrum_byte *rgb32_data, size_t stride,
|
||||
size_t height, size_t width )
|
||||
{
|
||||
size_t i, x, y;
|
||||
|
||||
static const /* R G B */
|
||||
libspectrum_byte palette[16][3] = { { 0, 0, 0 },
|
||||
{ 0, 0, 192 },
|
||||
{ 192, 0, 0 },
|
||||
{ 192, 0, 192 },
|
||||
{ 0, 192, 0 },
|
||||
{ 0, 192, 192 },
|
||||
{ 192, 192, 0 },
|
||||
{ 192, 192, 192 },
|
||||
{ 0, 0, 0 },
|
||||
{ 0, 0, 255 },
|
||||
{ 255, 0, 0 },
|
||||
{ 255, 0, 255 },
|
||||
{ 0, 255, 0 },
|
||||
{ 0, 255, 255 },
|
||||
{ 255, 255, 0 },
|
||||
{ 255, 255, 255 } };
|
||||
|
||||
libspectrum_byte grey_palette[16];
|
||||
|
||||
/* Addition of 0.5 is to avoid rounding errors */
|
||||
for( i = 0; i < 16; i++ )
|
||||
grey_palette[i] = ( 0.299 * palette[i][0] +
|
||||
0.587 * palette[i][1] +
|
||||
0.114 * palette[i][2] ) + 0.5;
|
||||
|
||||
for( y = 0; y < height; y++ ) {
|
||||
for( x = 0; x < width; x++ ) {
|
||||
|
||||
size_t colour;
|
||||
libspectrum_byte red, green, blue;
|
||||
|
||||
colour = display_getpixel( x, y );
|
||||
|
||||
if( settings_current.bw_tv ) {
|
||||
|
||||
red = green = blue = grey_palette[colour];
|
||||
|
||||
} else {
|
||||
|
||||
red = palette[colour][0];
|
||||
green = palette[colour][1];
|
||||
blue = palette[colour][2];
|
||||
|
||||
}
|
||||
|
||||
rgb32_data[ y * stride + 4 * x ] = red;
|
||||
rgb32_data[ y * stride + 4 * x + 1 ] = green;
|
||||
rgb32_data[ y * stride + 4 * x + 2 ] = blue;
|
||||
rgb32_data[ y * stride + 4 * x + 3 ] = 0; /* padding */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fill_rgb32_margin( libspectrum_byte *rgb32_data, size_t stride,
|
||||
size_t height, size_t width )
|
||||
{
|
||||
size_t x_dest, y_dest, x_src, y_src;
|
||||
libspectrum_byte *dest_data, *src_data;
|
||||
|
||||
/* We need to initialise the extra space available for scalers that "smear"
|
||||
the screen by copying the nearest neighbour, so we avoid any blending with
|
||||
black colour or memory garbage at the outer border */
|
||||
|
||||
/* Fill top margin */
|
||||
y_src = K_MARGIN;
|
||||
for( y_dest = 0; y_dest < K_MARGIN; y_dest++ ) {
|
||||
for( x_dest = 0; x_dest < width + K_MARGIN * 2; x_dest++ ) {
|
||||
|
||||
if( x_dest < K_MARGIN )
|
||||
x_src = K_MARGIN;
|
||||
else if( x_dest >= K_MARGIN + width )
|
||||
x_src = K_MARGIN + width - 1;
|
||||
else
|
||||
x_src = x_dest;
|
||||
|
||||
dest_data = rgb32_data + y_dest * stride + 4 * x_dest;
|
||||
src_data = rgb32_data + y_src * stride + 4 * x_src;
|
||||
*(libspectrum_dword*) dest_data = *(libspectrum_dword*) src_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill left margin */
|
||||
x_src = K_MARGIN;
|
||||
for( y_dest = K_MARGIN; y_dest < K_MARGIN + height; y_dest++ ) {
|
||||
for( x_dest = 0; x_dest < K_MARGIN; x_dest++ ) {
|
||||
y_src = y_dest;
|
||||
|
||||
dest_data = rgb32_data + y_dest * stride + 4 * x_dest;
|
||||
src_data = rgb32_data + y_src * stride + 4 * x_src;
|
||||
*(libspectrum_dword*) dest_data = *(libspectrum_dword*) src_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill right margin */
|
||||
x_src = K_MARGIN + width -1;
|
||||
for( y_dest = K_MARGIN; y_dest < K_MARGIN + height; y_dest++ ) {
|
||||
for( x_dest = K_MARGIN + width; x_dest < K_MARGIN * 2 + width; x_dest++ ) {
|
||||
y_src = y_dest;
|
||||
|
||||
dest_data = rgb32_data + y_dest * stride + 4 * x_dest;
|
||||
src_data = rgb32_data + y_src * stride + 4 * x_src;
|
||||
*(libspectrum_dword*) dest_data = *(libspectrum_dword*) src_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill bottom margin */
|
||||
y_src = K_MARGIN + height - 1;
|
||||
for( y_dest = K_MARGIN + height; y_dest < height + K_MARGIN * 2; y_dest++ ) {
|
||||
for( x_dest = 0; x_dest < width + K_MARGIN * 2; x_dest++ ) {
|
||||
|
||||
if( x_dest < K_MARGIN )
|
||||
x_src = K_MARGIN;
|
||||
else if( x_dest >= K_MARGIN + width )
|
||||
x_src = K_MARGIN + width - 1;
|
||||
else
|
||||
x_src = x_dest;
|
||||
|
||||
dest_data = rgb32_data + y_dest * stride + 4 * x_dest;
|
||||
src_data = rgb32_data + y_src * stride + 4 * x_src;
|
||||
*(libspectrum_dword*) dest_data = *(libspectrum_dword*) src_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rgb32_to_rgb24( libspectrum_byte *rgb24_data, size_t rgb24_stride,
|
||||
libspectrum_byte *rgb32_data, size_t rgb32_stride,
|
||||
size_t height, size_t width )
|
||||
{
|
||||
size_t x, y;
|
||||
|
||||
for( y = 0; y < height; y++ ) {
|
||||
for( x = 0; x < width; x++ ) {
|
||||
rgb24_data[y*rgb24_stride +3*x ] = rgb32_data[y*rgb32_stride +4*x ];
|
||||
rgb24_data[y*rgb24_stride +3*x +1] = rgb32_data[y*rgb32_stride +4*x +1];
|
||||
rgb24_data[y*rgb24_stride +3*x +2] = rgb32_data[y*rgb32_stride +4*x +2];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
screenshot_available_scalers( scaler_type scaler )
|
||||
{
|
||||
if( machine_current->timex ) {
|
||||
|
||||
switch( scaler ) {
|
||||
|
||||
case SCALER_HALF: case SCALER_HALFSKIP: case SCALER_NORMAL:
|
||||
case SCALER_TIMEX1_5X: case SCALER_TIMEX2X:
|
||||
case SCALER_TIMEXTV: case SCALER_PALTV:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
switch( scaler ) {
|
||||
|
||||
case SCALER_NORMAL: case SCALER_DOUBLESIZE: case SCALER_TRIPLESIZE: case SCALER_QUADSIZE:
|
||||
case SCALER_2XSAI: case SCALER_SUPER2XSAI: case SCALER_SUPEREAGLE:
|
||||
case SCALER_ADVMAME2X: case SCALER_ADVMAME3X:
|
||||
case SCALER_TV2X: case SCALER_TV3X: case SCALER_TV4X: case SCALER_DOTMATRIX:
|
||||
case SCALER_PALTV: case SCALER_PALTV2X: case SCALER_PALTV3X: case SCALER_PALTV4X:
|
||||
case SCALER_HQ2X: case SCALER_HQ3X: case SCALER_HQ4X:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #ifdef USE_LIBPNG */
|
||||
|
||||
static void
|
||||
screenshot_end( void )
|
||||
{
|
||||
#ifdef USE_LIBPNG
|
||||
libspectrum_free( rgb_data ); rgb_data = NULL;
|
||||
libspectrum_free( scaled_data ); scaled_data = NULL;
|
||||
libspectrum_free( png_data ); png_data = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
screenshot_register_startup( void )
|
||||
{
|
||||
startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID };
|
||||
startup_manager_register( STARTUP_MANAGER_MODULE_SCREENSHOT, dependencies,
|
||||
ARRAY_SIZE( dependencies ), NULL, NULL,
|
||||
screenshot_end );
|
||||
}
|
||||
|
||||
static int
|
||||
screenshot_scr_hires_write( const char *filename )
|
||||
{
|
||||
libspectrum_byte scr_data[ HIRES_SCR_SIZE ];
|
||||
|
||||
memset( scr_data, 0, HIRES_SCR_SIZE );
|
||||
|
||||
memcpy( scr_data,
|
||||
&RAM[ memory_current_screen ][ display_get_addr(0,0) ],
|
||||
MONO_BITMAP_SIZE );
|
||||
memcpy( scr_data + MONO_BITMAP_SIZE,
|
||||
&RAM[ memory_current_screen ][ display_get_addr(0,0) +
|
||||
ALTDFILE_OFFSET], MONO_BITMAP_SIZE );
|
||||
scr_data[ HIRES_ATTR ] = ( scld_last_dec.byte & HIRESCOLMASK ) |
|
||||
scld_last_dec.mask.scrnmode;
|
||||
|
||||
return utils_write_file( filename, scr_data, HIRES_SCR_SIZE );
|
||||
}
|
||||
|
||||
static int
|
||||
get_display_last_screen_index( int x, int y )
|
||||
{
|
||||
int beam_x = x + DISPLAY_BORDER_WIDTH_COLS;
|
||||
int beam_y = y + DISPLAY_BORDER_HEIGHT;
|
||||
|
||||
return beam_x + beam_y * DISPLAY_SCREEN_WIDTH_COLS;
|
||||
}
|
||||
|
||||
static void
|
||||
set_hicolor_pixels_and_attribute( int x, int y, libspectrum_byte* scr_data )
|
||||
{
|
||||
int index = get_display_last_screen_index( x, y );
|
||||
libspectrum_byte pixel_data = display_last_screen[ index ] & 0xff;
|
||||
libspectrum_byte attribute_data = (display_last_screen[ index ] & 0xff00)>>8;
|
||||
|
||||
libspectrum_word offset = display_get_offset( x, y );
|
||||
scr_data[ offset ] = pixel_data;
|
||||
/* write attribute into bitmap order buffer after bitmap */
|
||||
scr_data[ MONO_BITMAP_SIZE + offset ] = attribute_data;
|
||||
}
|
||||
|
||||
typedef void (*data_write_fn)( int x, int y, libspectrum_byte* scr_data );
|
||||
|
||||
static int
|
||||
scr_write( const char *filename, const int data_size,
|
||||
data_write_fn scr_data_write )
|
||||
{
|
||||
libspectrum_byte scr_data[ data_size ];
|
||||
int x, y;
|
||||
|
||||
memset( scr_data, 0, data_size );
|
||||
|
||||
for( y = 0; y < DISPLAY_HEIGHT; y++ ) {
|
||||
for( x = 0; x < DISPLAY_WIDTH_COLS; x++ ) {
|
||||
scr_data_write( x, y, scr_data );
|
||||
}
|
||||
}
|
||||
|
||||
return utils_write_file( filename, scr_data, data_size );
|
||||
}
|
||||
|
||||
static void
|
||||
set_standard_pixels_and_attribute( int x, int y, libspectrum_byte* scr_data )
|
||||
{
|
||||
int index = get_display_last_screen_index( x, y );
|
||||
libspectrum_byte pixel_data = display_last_screen[ index ] & 0xff;
|
||||
libspectrum_byte attribute_data = (display_last_screen[ index ] & 0xff00)>>8;
|
||||
int attribute_offset;
|
||||
|
||||
scr_data[ display_get_offset( x, y ) ] = pixel_data;
|
||||
|
||||
if( y%8 == 0 ) {
|
||||
/* write attribute into standard attribute order buffer after bitmap */
|
||||
attribute_offset = x + (y/8 * DISPLAY_WIDTH_COLS);
|
||||
scr_data[ MONO_BITMAP_SIZE + attribute_offset ] = attribute_data;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
screenshot_scr_write( const char *filename )
|
||||
{
|
||||
if( scld_last_dec.name.hires ) {
|
||||
return screenshot_scr_hires_write( filename );
|
||||
}
|
||||
else if( scld_last_dec.name.b1 ) {
|
||||
return scr_write( filename, HICOLOUR_SCR_SIZE,
|
||||
&set_hicolor_pixels_and_attribute );
|
||||
}
|
||||
else { /* ALTDFILE and default */
|
||||
return scr_write( filename, STANDARD_SCR_SIZE,
|
||||
&set_standard_pixels_and_attribute );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_mlt_pixels_and_attribute( int x, int y, libspectrum_byte* mlt_data )
|
||||
{
|
||||
int index = get_display_last_screen_index( x, y );
|
||||
libspectrum_byte pixel_data = display_last_screen[ index ] & 0xff;
|
||||
libspectrum_byte attribute_data = (display_last_screen[ index ] & 0xff00)>>8;
|
||||
|
||||
mlt_data[ display_get_offset( x, y ) ] = pixel_data;
|
||||
/* write attribute into linear buffer following bitmap */
|
||||
mlt_data[ MONO_BITMAP_SIZE + x + y * DISPLAY_WIDTH_COLS ] = attribute_data;
|
||||
}
|
||||
|
||||
int
|
||||
screenshot_mlt_write( const char *filename )
|
||||
{
|
||||
if( machine_current->timex && scld_last_dec.name.hires ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"MLT format not supported for Timex hi-res screen mode" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return scr_write( filename, MLT_SIZE, &set_mlt_pixels_and_attribute );
|
||||
}
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
typedef struct {
|
||||
unsigned b0 : 1;
|
||||
unsigned b1 : 1;
|
||||
unsigned b2 : 1;
|
||||
unsigned b3 : 1;
|
||||
unsigned b4 : 1;
|
||||
unsigned b5 : 1;
|
||||
unsigned b6 : 1;
|
||||
unsigned b7 : 1;
|
||||
} byte_field_type;
|
||||
|
||||
#else /* #ifdef WORDS_BIGENDIAN */
|
||||
|
||||
typedef struct {
|
||||
unsigned b7 : 1;
|
||||
unsigned b6 : 1;
|
||||
unsigned b5 : 1;
|
||||
unsigned b4 : 1;
|
||||
unsigned b3 : 1;
|
||||
unsigned b2 : 1;
|
||||
unsigned b1 : 1;
|
||||
unsigned b0 : 1;
|
||||
} byte_field_type;
|
||||
|
||||
#endif /* #ifdef WORDS_BIGENDIAN */
|
||||
|
||||
typedef union {
|
||||
libspectrum_byte byte;
|
||||
byte_field_type bit;
|
||||
} bft_union;
|
||||
|
||||
static libspectrum_byte
|
||||
convert_hires_to_lores( libspectrum_byte high, libspectrum_byte low )
|
||||
{
|
||||
bft_union ret, h, l;
|
||||
|
||||
h.byte = high;
|
||||
l.byte = low;
|
||||
|
||||
ret.bit.b0 = l.bit.b0;
|
||||
ret.bit.b1 = l.bit.b2;
|
||||
ret.bit.b2 = l.bit.b4;
|
||||
ret.bit.b3 = l.bit.b6;
|
||||
ret.bit.b4 = h.bit.b0;
|
||||
ret.bit.b5 = h.bit.b2;
|
||||
ret.bit.b6 = h.bit.b4;
|
||||
ret.bit.b7 = h.bit.b6;
|
||||
|
||||
return ret.byte;
|
||||
}
|
||||
|
||||
int
|
||||
screenshot_scr_read( const char *filename )
|
||||
{
|
||||
int error = 0;
|
||||
int i;
|
||||
utils_file screen;
|
||||
|
||||
error = utils_read_file( filename, &screen );
|
||||
if( error ) return error;
|
||||
|
||||
switch( screen.length ) {
|
||||
case STANDARD_SCR_SIZE:
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0)],
|
||||
screen.buffer, screen.length );
|
||||
|
||||
/* If it is a Timex and it is in hi colour or hires mode, switch out of
|
||||
hires or hicolour mode */
|
||||
if( scld_last_dec.name.b1 || scld_last_dec.name.hires )
|
||||
scld_dec_write( 0xff, scld_last_dec.byte & ~HIRES );
|
||||
break;
|
||||
|
||||
case HICOLOUR_SCR_SIZE:
|
||||
/* If it is a Timex and it is not in hi colour mode, copy screen and switch
|
||||
mode if neccesary */
|
||||
/* If it is not a Timex copy the mono bitmap and raise an error */
|
||||
if( machine_current->timex ) {
|
||||
if( !scld_last_dec.name.b1 )
|
||||
scld_dec_write( 0xff, ( scld_last_dec.byte & ~HIRESATTR ) | EXTCOLOUR );
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0) +
|
||||
ALTDFILE_OFFSET], screen.buffer + MONO_BITMAP_SIZE,
|
||||
MONO_BITMAP_SIZE );
|
||||
} else
|
||||
ui_error( UI_ERROR_INFO,
|
||||
"The file contained a TC2048 high-colour screen, loaded as mono");
|
||||
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0)],
|
||||
screen.buffer, MONO_BITMAP_SIZE );
|
||||
break;
|
||||
|
||||
case HIRES_SCR_SIZE:
|
||||
/* If it is a Timex and it is not in hi res mode, copy screen and switch
|
||||
mode if neccesary */
|
||||
/* If it is not a Timex scale the bitmap and raise an error */
|
||||
if( machine_current->timex ) {
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0)],
|
||||
screen.buffer, MONO_BITMAP_SIZE );
|
||||
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0)] +
|
||||
ALTDFILE_OFFSET, screen.buffer + MONO_BITMAP_SIZE,
|
||||
MONO_BITMAP_SIZE );
|
||||
if( !scld_last_dec.name.hires )
|
||||
scld_dec_write( 0xff,
|
||||
( scld_last_dec.byte & ~( HIRESCOLMASK | HIRES ) ) |
|
||||
( *(screen.buffer + HIRES_ATTR) & ( HIRESCOLMASK | HIRES ) ) );
|
||||
} else {
|
||||
libspectrum_byte attr = hires_convert_dec( *(screen.buffer + HIRES_ATTR) );
|
||||
|
||||
for( i = 0; i < MONO_BITMAP_SIZE; i++ )
|
||||
RAM[ memory_current_screen ][display_get_addr(0,0) + i] =
|
||||
convert_hires_to_lores( *(screen.buffer + MONO_BITMAP_SIZE + i),
|
||||
*(screen.buffer + i) );
|
||||
|
||||
/* set attributes based on hires attribute byte */
|
||||
for( i = 0; i < 768; i++ )
|
||||
RAM[ memory_current_screen ][display_get_addr(0,0) +
|
||||
MONO_BITMAP_SIZE + i] = attr;
|
||||
|
||||
ui_error( UI_ERROR_INFO,
|
||||
"The file contained a TC2048 high-res screen, converted to lores");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ui_error( UI_ERROR_ERROR, "'%s' is not a valid scr file", filename );
|
||||
error = 1;
|
||||
}
|
||||
|
||||
utils_close_file( &screen );
|
||||
|
||||
display_refresh_all();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
screenshot_mlt_read( const char *filename )
|
||||
{
|
||||
int error = 0;
|
||||
int x, y;
|
||||
utils_file screen;
|
||||
|
||||
error = utils_read_file( filename, &screen );
|
||||
if( error ) return error;
|
||||
|
||||
if( screen.length != MLT_SIZE ) {
|
||||
ui_error( UI_ERROR_ERROR, "MLT picture ('%s') is not %d bytes long",
|
||||
filename, MLT_SIZE );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If it is a Timex and it is not in hi colour mode, copy screen and switch
|
||||
mode if neccesary */
|
||||
/* If it is not a Timex copy the mono bitmap and raise an error */
|
||||
if( machine_current->timex ) {
|
||||
if( !scld_last_dec.name.b1 )
|
||||
scld_dec_write( 0xff, ( scld_last_dec.byte & ~HIRESATTR ) | EXTCOLOUR );
|
||||
|
||||
for( y = 0; y < DISPLAY_HEIGHT; y++ ) {
|
||||
for( x = 0; x < DISPLAY_WIDTH_COLS; x++ ) {
|
||||
RAM[ memory_current_screen ][display_get_addr(x,y) + ALTDFILE_OFFSET] =
|
||||
*(screen.buffer + MONO_BITMAP_SIZE + x + y * DISPLAY_WIDTH_COLS);
|
||||
}
|
||||
}
|
||||
} else
|
||||
ui_error( UI_ERROR_INFO,
|
||||
"The file contained a MLT high-colour screen, loaded as mono");
|
||||
|
||||
memcpy( &RAM[ memory_current_screen ][display_get_addr(0,0)],
|
||||
screen.buffer, MONO_BITMAP_SIZE );
|
||||
|
||||
utils_close_file( &screen );
|
||||
|
||||
display_refresh_all();
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* screenshot.h: Routines for saving .png screenshots
|
||||
Copyright (c) 2002 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_SCREENSHOT_H
|
||||
#define FUSE_SCREENSHOT_H
|
||||
|
||||
#ifndef SCALER_H
|
||||
#include "ui/scaler/scaler.h"
|
||||
#endif /* #ifndef SCALER_H */
|
||||
|
||||
void screenshot_register_startup( void );
|
||||
|
||||
#ifdef USE_LIBPNG
|
||||
|
||||
int screenshot_write( const char *filename, scaler_type scaler );
|
||||
int screenshot_available_scalers( scaler_type scaler );
|
||||
|
||||
#endif /* #ifdef USE_LIBPNG */
|
||||
|
||||
int screenshot_scr_write( const char *filename );
|
||||
int screenshot_scr_read( const char *filename );
|
||||
|
||||
int screenshot_mlt_write( const char *filename );
|
||||
int screenshot_mlt_read( const char *filename );
|
||||
|
||||
#define STANDARD_SCR_SIZE 6912
|
||||
|
||||
#endif /* #ifndef FUSE_SCREENSHOT_H */
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "fuse.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "machine.h"
|
||||
#include "movie.h"
|
||||
#include "options.h"
|
||||
#include "peripherals/sound/sp0256.h"
|
||||
#include "settings.h"
|
||||
@@ -374,8 +373,6 @@ sound_init( const char *device )
|
||||
sound_framesiz++;
|
||||
|
||||
samples = libspectrum_new0( blip_sample_t, sound_framesiz * sound_channels );
|
||||
/* initialize movie settings... */
|
||||
movie_init_sound( settings_current.sound_freq, sound_stereo_ay );
|
||||
|
||||
}
|
||||
|
||||
@@ -811,8 +808,6 @@ sound_frame( void )
|
||||
if( settings_current.sound )
|
||||
sound_lowlevel_frame( samples, count );
|
||||
|
||||
if( movie_recording )
|
||||
movie_add_sound( samples, count );
|
||||
ay_change_count[0] = ay_change_count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
/* alsasound.c: ALSA (Linux) sound I/O
|
||||
Copyright (c) 2006 Gergely Szasz
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* This is necessary to prevent warnings from the calls to
|
||||
snd_pcm_[hs]w_params_alloca() */
|
||||
#ifndef NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sfifo.h"
|
||||
#include "sound.h"
|
||||
#include "spectrum.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* Number of Spectrum frames audio latency to use */
|
||||
#define NUM_FRAMES 3
|
||||
|
||||
static snd_pcm_t *pcm_handle;
|
||||
static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
|
||||
static int ch, framesize;
|
||||
static const char *pcm_name = NULL;
|
||||
static int verb = 0;
|
||||
static snd_pcm_uframes_t exact_periodsize, exact_bsize;
|
||||
|
||||
static snd_output_t *output = NULL;
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
/* Stop PCM device and drop pending frames */
|
||||
snd_pcm_drop( pcm_handle );
|
||||
snd_pcm_close( pcm_handle );
|
||||
}
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
unsigned int exact_rate, periods;
|
||||
unsigned int val, n;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
snd_pcm_uframes_t avail_min = 0, sound_periodsize, bsize = 0;
|
||||
static int first_init = 1;
|
||||
static int init_running = 0;
|
||||
const char *option;
|
||||
char tmp;
|
||||
int err, dir, nperiods = NUM_FRAMES;
|
||||
|
||||
float hz;
|
||||
|
||||
if( init_running )
|
||||
return 0;
|
||||
|
||||
init_running = 1;
|
||||
/* select a default device if we weren't explicitly given one */
|
||||
|
||||
option = device;
|
||||
while( option && *option ) {
|
||||
tmp = '*';
|
||||
if( ( err = sscanf( option, " buffer=%i %n%c", &val, &n, &tmp ) > 0 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
if( val < 1 ) {
|
||||
fprintf( stderr, "Bad value for ALSA buffer size %i, using default\n",
|
||||
val );
|
||||
} else {
|
||||
bsize = val;
|
||||
}
|
||||
} else if( ( err = sscanf( option, " frames=%i %n%c", &val, &n, &tmp ) > 0 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
if( val < 1 ) {
|
||||
fprintf( stderr, "Bad value for ALSA buffer size %i frames, using default (%d)\n",
|
||||
val, NUM_FRAMES );
|
||||
} else {
|
||||
nperiods = val;
|
||||
}
|
||||
} else if( ( err = sscanf( option, " avail=%i %n%c", &val, &n, &tmp ) > 0 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
if( val < 1 ) {
|
||||
fprintf( stderr, "Bad value for ALSA avail_min size %i frames, using default\n",
|
||||
val );
|
||||
} else {
|
||||
avail_min = val;
|
||||
}
|
||||
} else if( ( err = sscanf( option, " verbose %n%c", &n, &tmp ) == 1 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
verb = 1;
|
||||
} else { /* try as device name */
|
||||
while( isspace(*option) )
|
||||
option++;
|
||||
if( *option == '\'' ) /* force device... */
|
||||
option++;
|
||||
pcm_name = option;
|
||||
n = strlen( pcm_name );
|
||||
}
|
||||
option += n + ( tmp == ',' );
|
||||
}
|
||||
|
||||
/* Open the sound device
|
||||
*/
|
||||
if( pcm_name == NULL || *pcm_name == '\0' )
|
||||
pcm_name = "default";
|
||||
if( snd_pcm_open( &pcm_handle, pcm_name , stream, 0 ) < 0 ) {
|
||||
if( strcmp( pcm_name, "default" ) == 0 ) {
|
||||
/* we try a last one: plughw:0,0 but what a weired ALSA conf.... */
|
||||
if( snd_pcm_open( &pcm_handle, "plughw:0,0", stream, 0 ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"couldn't open sound device 'default' and 'plughw:0,0' check ALSA configuration."
|
||||
);
|
||||
init_running = 0;
|
||||
return 1;
|
||||
} else {
|
||||
if( first_init )
|
||||
fprintf( stderr,
|
||||
"Couldn't open sound device 'default', using 'plughw:0,0' check ALSA configuration.\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't open sound device '%s'.", pcm_name );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate the snd_pcm_hw_params_t structure on the stack. */
|
||||
snd_pcm_hw_params_alloca( &hw_params );
|
||||
|
||||
/* Init hw_params with full configuration space */
|
||||
if( snd_pcm_hw_params_any( pcm_handle, hw_params ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"couldn't get configuration space on sound device '%s'.",
|
||||
pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( snd_pcm_hw_params_set_access( pcm_handle, hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED ) < 0) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set access interleaved on '%s'.",
|
||||
pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set sample format */
|
||||
if( snd_pcm_hw_params_set_format( pcm_handle, hw_params,
|
||||
#if defined WORDS_BIGENDIAN
|
||||
SND_PCM_FORMAT_S16_BE
|
||||
#else
|
||||
SND_PCM_FORMAT_S16_LE
|
||||
#endif
|
||||
) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set format on '%s'.", pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch = *stereoptr ? 2 : 1;
|
||||
|
||||
if( snd_pcm_hw_params_set_channels( pcm_handle, hw_params, ch )
|
||||
< 0 ) {
|
||||
fprintf( stderr, "Couldn't set %s to '%s'.\n", pcm_name,
|
||||
(*stereoptr ? "stereo" : "mono") );
|
||||
ch = *stereoptr ? 1 : 2; /* try with opposite */
|
||||
if( snd_pcm_hw_params_set_channels( pcm_handle, hw_params, ch )
|
||||
< 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set %s to '%s'.", pcm_name,
|
||||
(*stereoptr ? "stereo" : "mono") );
|
||||
settings_current.sound = 0;
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
*stereoptr = *stereoptr ? 0 : 1; /* write back */
|
||||
}
|
||||
|
||||
framesize = ch << 1; /* we always use 16 bit sorry :-( */
|
||||
/* Set sample rate. If the exact rate is not supported */
|
||||
/* by the hardware, use nearest possible rate. */
|
||||
exact_rate = *freqptr;
|
||||
if( snd_pcm_hw_params_set_rate_near( pcm_handle, hw_params, &exact_rate,
|
||||
NULL ) < 0) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set rate %d on '%s'.",
|
||||
*freqptr, pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
if( first_init && *freqptr != exact_rate ) {
|
||||
fprintf( stderr,
|
||||
"The rate %d Hz is not supported by your hardware. "
|
||||
"Using %d Hz instead.\n", *freqptr, exact_rate );
|
||||
*freqptr = exact_rate;
|
||||
}
|
||||
|
||||
if( bsize != 0 ) {
|
||||
exact_periodsize = sound_periodsize = bsize / nperiods;
|
||||
if( bsize < 1 ) {
|
||||
fprintf( stderr,
|
||||
"bad value for ALSA buffer size %i, using default.\n",
|
||||
val );
|
||||
bsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( bsize == 0 ) {
|
||||
/* Adjust relative processor speed to deal with adjusting sound generation
|
||||
frequency against emulation speed (more flexible than adjusting generated
|
||||
sample rate) */
|
||||
hz = (float)sound_get_effective_processor_speed() /
|
||||
machine_current->timings.tstates_per_frame;
|
||||
/* Amount of audio data we will accumulate before yielding back to the OS.
|
||||
Not much point having more than 100Hz playback, we probably get
|
||||
downgraded by the OS as being a hog too (unlimited Hz limits playback
|
||||
speed to about 2000% on my Mac, 100Hz allows up to 5000% for me) */
|
||||
if( hz > 100.0 ) hz = 100.0;
|
||||
exact_periodsize = sound_periodsize = *freqptr / hz;
|
||||
}
|
||||
|
||||
dir = -1;
|
||||
if( snd_pcm_hw_params_set_period_size_near( pcm_handle, hw_params,
|
||||
&exact_periodsize, &dir ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set period size %d on '%s'.",
|
||||
(int)sound_periodsize, pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( first_init && ( exact_periodsize < sound_periodsize / 1.5 ||
|
||||
exact_periodsize > sound_periodsize * 1.5 ) ) {
|
||||
fprintf( stderr,
|
||||
"The period size %d is not supported by your hardware. "
|
||||
"Using %d instead.\n", (int)sound_periodsize,
|
||||
(int)exact_periodsize );
|
||||
}
|
||||
|
||||
periods = nperiods;
|
||||
/* Set number of periods. Periods used to be called fragments. */
|
||||
if( snd_pcm_hw_params_set_periods_near( pcm_handle, hw_params, &periods,
|
||||
NULL ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "couldn't set periods on '%s'.", pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( first_init && periods != nperiods ) {
|
||||
fprintf( stderr, "%d periods is not supported by your hardware. "
|
||||
"Using %d instead.\n", nperiods, periods );
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_get_buffer_size( hw_params, &exact_bsize );
|
||||
|
||||
/* Apply HW parameter settings to */
|
||||
/* PCM device and prepare device */
|
||||
|
||||
if( snd_pcm_hw_params( pcm_handle, hw_params ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,"couldn't set hw_params on %s", pcm_name );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_alloca( &sw_params );
|
||||
if( ( err = snd_pcm_sw_params_current( pcm_handle, sw_params ) ) < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR,"couldn't get sw_params from %s: %s", pcm_name,
|
||||
snd_strerror ( err ) );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( ( err = snd_pcm_sw_params_set_start_threshold( pcm_handle,
|
||||
sw_params, exact_periodsize * ( nperiods - 1 ) ) ) < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR,"couldn't set start_treshold on %s: %s", pcm_name,
|
||||
snd_strerror ( err ) );
|
||||
snd_pcm_close( pcm_handle );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !avail_min )
|
||||
avail_min = exact_periodsize >> 1;
|
||||
if( snd_pcm_sw_params_set_avail_min( pcm_handle,
|
||||
sw_params, avail_min ) < 0 ) {
|
||||
#if SND_LIB_VERSION < 0x10010
|
||||
if( ( err = snd_pcm_sw_params_set_sleep_min( pcm_handle,
|
||||
sw_params, 1 ) ) < 0 ) {
|
||||
fprintf( stderr, "Unable to set minimal sleep 1 for %s: %s\n", pcm_name,
|
||||
snd_strerror ( err ) );
|
||||
}
|
||||
#else
|
||||
fprintf( stderr, "Unable to set avail min %s: %s\n", pcm_name,
|
||||
snd_strerror( err ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SND_LIB_VERSION < 0x10010
|
||||
if( ( err = snd_pcm_sw_params_set_xfer_align( pcm_handle, sw_params, 1 ) ) < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR,"couldn't set xfer_allign on %s: %s", pcm_name,
|
||||
snd_strerror ( err ) );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( ( err = snd_pcm_sw_params( pcm_handle, sw_params ) ) < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR,"couldn't set sw_params on %s: %s", pcm_name,
|
||||
snd_strerror ( err ) );
|
||||
init_running = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( first_init ) snd_output_stdio_attach(&output, stdout, 0);
|
||||
|
||||
first_init = 0;
|
||||
init_running = 0;
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
int ret = 0;
|
||||
len /= ch; /* now in frames */
|
||||
|
||||
/* to measure sound lag :-)
|
||||
snd_pcm_status_t *status;
|
||||
snd_pcm_sframes_t delay;
|
||||
|
||||
snd_pcm_status_alloca( &status );
|
||||
snd_pcm_status( pcm_handle, status );
|
||||
delay = snd_pcm_status_get_delay( status );
|
||||
fprintf( stderr, "%d ", (int)delay );
|
||||
*/
|
||||
|
||||
while( ( ret = snd_pcm_writei( pcm_handle, data, len ) ) != len ) {
|
||||
if( ret < 0 ) {
|
||||
snd_pcm_prepare( pcm_handle );
|
||||
if( verb )
|
||||
fprintf( stderr, "ALSA: *buffer underrun*!\n" );
|
||||
} else {
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/* aosound.c: libao sound I/O
|
||||
Copyright (c) 2004,2013 Gergely Szasz, Philip Kendall
|
||||
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:
|
||||
|
||||
Philip: [email protected]
|
||||
|
||||
Gergely: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <ao/ao.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
static ao_device *dev_for_ao;
|
||||
static int sixteenbit = 1;
|
||||
static char *filename = NULL;
|
||||
static const char * const default_filename = "fuse-sound.ao";
|
||||
static int first_init = 1;
|
||||
|
||||
static void
|
||||
driver_error( void )
|
||||
{
|
||||
switch( errno ) {
|
||||
case AO_ENODRIVER:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: no driver corresponds to driver_id." );
|
||||
break;
|
||||
case AO_ENOTLIVE:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: driver is not a live output device." );
|
||||
break;
|
||||
case AO_ENOTFILE:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: driver is not a file output driver." );
|
||||
break;
|
||||
case AO_EBADOPTION:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: a valid option key has an invalid value." );
|
||||
break;
|
||||
case AO_EOPENDEVICE:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: cannot open output device." );
|
||||
break;
|
||||
case AO_EOPENFILE:
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"ao: cannot open output file '%s'.", filename );
|
||||
break;
|
||||
case AO_EFILEEXISTS:
|
||||
ui_error( UI_ERROR_ERROR, "ao: output file '%s' already exists.",
|
||||
filename );
|
||||
break;
|
||||
case AO_EFAIL:
|
||||
ui_error( UI_ERROR_ERROR, "ao: unspecified error." );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
parse_driver_options( const char *device, int *driver_id, ao_option **options )
|
||||
{
|
||||
char *mutable, *option, *key, *value;
|
||||
|
||||
/* Get a copy of the device string we can modify */
|
||||
if( !device || *device == '\0' )
|
||||
return 1;
|
||||
|
||||
mutable = utils_safe_strdup( device );
|
||||
|
||||
/* First, find the device name */
|
||||
option = strchr( mutable, ':' );
|
||||
if( option ) *option++ = '\0';
|
||||
|
||||
if( *mutable ) /* ! \0 */
|
||||
*driver_id = ao_driver_id( mutable );
|
||||
|
||||
/* Now parse any further options */
|
||||
while( option ) {
|
||||
|
||||
key = option;
|
||||
|
||||
option = strchr( option, ',' );
|
||||
if( option ) *option++ = '\0';
|
||||
|
||||
value = strchr( key, '=' );
|
||||
if( value ) *value++ = '\0';
|
||||
|
||||
if( strcmp( key, "file" ) == 0 ) {
|
||||
filename = utils_safe_strdup( value );
|
||||
} else if( key && value) {
|
||||
ao_append_option( options, key, value );
|
||||
} else if ( first_init ) {
|
||||
ui_error( UI_ERROR_ERROR, "ignoring badly formed libao option (%s%s)",
|
||||
key ? key : "", value ? value : "" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
libspectrum_free( mutable );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
ao_option *options = NULL;
|
||||
ao_info *driver_info = NULL;
|
||||
int driver_id = -1;
|
||||
static ao_sample_format format = { .bits = 0 };
|
||||
|
||||
/* To prevent recursive errors */
|
||||
static int sound_lowlevel_init_in_progress = 0;
|
||||
|
||||
int error;
|
||||
|
||||
if( sound_lowlevel_init_in_progress ) return 0;
|
||||
|
||||
sound_lowlevel_init_in_progress = 1;
|
||||
|
||||
ao_initialize();
|
||||
|
||||
error = parse_driver_options( device, &driver_id, &options );
|
||||
if( error ) {
|
||||
settings_current.sound = 0;
|
||||
sound_lowlevel_init_in_progress = 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( driver_id == -1 )
|
||||
driver_id = ao_default_driver_id();
|
||||
|
||||
if( driver_id == -1 ) {
|
||||
ui_error( UI_ERROR_ERROR, "ao: driver '%s' unknown",
|
||||
device );
|
||||
settings_current.sound = 0;
|
||||
sound_lowlevel_init_in_progress = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
driver_info = ao_driver_info( driver_id );
|
||||
|
||||
if( driver_info->type == AO_TYPE_FILE &&
|
||||
format.bits != 0 ) { /* OK. We not want to trunc the file :-) */
|
||||
ui_error( UI_ERROR_WARNING, "ao: must truncate audio file '%s'",
|
||||
filename );
|
||||
}
|
||||
|
||||
format.channels = *stereoptr ? 2 : 1;
|
||||
format.rate = *freqptr;
|
||||
format.bits = settings_current.sound_force_8bit ? 8 : 16;
|
||||
format.byte_format = AO_FMT_LITTLE;
|
||||
sixteenbit = settings_current.sound_force_8bit ? 0 : 1;
|
||||
|
||||
if( driver_info->type == AO_TYPE_LIVE ) {
|
||||
|
||||
dev_for_ao = ao_open_live( driver_id, &format, options);
|
||||
|
||||
} else {
|
||||
|
||||
if( !filename ) filename = (char *)default_filename;
|
||||
dev_for_ao = ao_open_file( driver_id, filename, 1, &format, options);
|
||||
|
||||
}
|
||||
|
||||
if( !dev_for_ao ) {
|
||||
driver_error();
|
||||
settings_current.sound = 0;
|
||||
sound_lowlevel_init_in_progress = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ao_free_options( options );
|
||||
|
||||
sound_lowlevel_init_in_progress = 0;
|
||||
first_init = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
if( filename != default_filename ) libspectrum_free( filename );
|
||||
ao_close(dev_for_ao);
|
||||
ao_shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
static signed char buf8[4096];
|
||||
void *data8 = data;
|
||||
|
||||
len <<= 1; /* now in bytes */
|
||||
|
||||
if( !sixteenbit ) {
|
||||
libspectrum_signed_word *src;
|
||||
signed char *dst;
|
||||
int f;
|
||||
|
||||
src = data;
|
||||
dst = buf8;
|
||||
len >>= 1;
|
||||
for( f = 0; f < len; f++)
|
||||
*dst++ = ( int )( ( *src++ ) / 256 );
|
||||
|
||||
data8 = buf8;
|
||||
}
|
||||
|
||||
ao_play( dev_for_ao, data8, len );
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
/* coreaudiosound.c: Mac OS X CoreAudio sound I/O
|
||||
Copyright (c) 2006 Fredrick Meunier
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <AssertMacros.h>
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sfifo.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
sfifo_t sound_fifo;
|
||||
|
||||
/* Number of Spectrum frames audio latency to use */
|
||||
#define NUM_FRAMES 2
|
||||
|
||||
static
|
||||
OSStatus coreaudiowrite( void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData );
|
||||
|
||||
/* info about the format used for writing to output unit */
|
||||
static AudioStreamBasicDescription deviceFormat;
|
||||
|
||||
/* converts from Fuse format (signed 16 bit ints) to CoreAudio format (floats)
|
||||
*/
|
||||
static AudioUnit gOutputUnit;
|
||||
|
||||
/* Records sound writer status information */
|
||||
static int audio_output_started;
|
||||
|
||||
/* get the default output device for the HAL */
|
||||
static int
|
||||
get_default_output_device(AudioDeviceID* device)
|
||||
{
|
||||
OSStatus err = kAudioHardwareNoError;
|
||||
UInt32 count;
|
||||
|
||||
AudioObjectPropertyAddress property_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
/* get the default output device for the HAL */
|
||||
count = sizeof( *device );
|
||||
err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property_address,
|
||||
0, NULL, &count, device);
|
||||
if ( err != kAudioHardwareNoError && device != kAudioObjectUnknown ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"get kAudioHardwarePropertyDefaultOutputDevice error %ld",
|
||||
(long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the nominal sample rate used by the supplied device */
|
||||
static int
|
||||
get_default_sample_rate( AudioDeviceID device, Float64 *rate )
|
||||
{
|
||||
OSStatus err = kAudioHardwareNoError;
|
||||
UInt32 count;
|
||||
|
||||
AudioObjectPropertyAddress property_address = {
|
||||
kAudioDevicePropertyNominalSampleRate,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
/* get the default output device for the HAL */
|
||||
count = sizeof( *rate );
|
||||
err = AudioObjectGetPropertyData( device, &property_address, 0, NULL, &count,
|
||||
rate);
|
||||
if ( err != kAudioHardwareNoError ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"get kAudioDevicePropertyNominalSampleRate error %ld",
|
||||
(long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *dev, int *freqptr, int *stereoptr )
|
||||
{
|
||||
OSStatus err = kAudioHardwareNoError;
|
||||
AudioDeviceID device = kAudioObjectUnknown; /* the default device */
|
||||
int error;
|
||||
float hz;
|
||||
int sound_framesiz;
|
||||
|
||||
if( get_default_output_device(&device) ) return 1;
|
||||
if( get_default_sample_rate( device, &deviceFormat.mSampleRate ) ) return 1;
|
||||
|
||||
*freqptr = deviceFormat.mSampleRate;
|
||||
|
||||
deviceFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
| kLinearPCMFormatFlagIsBigEndian
|
||||
#endif /* #ifdef WORDS_BIGENDIAN */
|
||||
| kLinearPCMFormatFlagIsPacked;
|
||||
deviceFormat.mBytesPerPacket = *stereoptr ? 4 : 2;
|
||||
deviceFormat.mFramesPerPacket = 1;
|
||||
deviceFormat.mBytesPerFrame = *stereoptr ? 4 : 2;
|
||||
deviceFormat.mBitsPerChannel = 16;
|
||||
deviceFormat.mChannelsPerFrame = *stereoptr ? 2 : 1;
|
||||
|
||||
/* Open the default output unit */
|
||||
AudioComponentDescription desc;
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
AudioComponent comp = AudioComponentFindNext( NULL, &desc );
|
||||
if( comp == NULL ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioComponentFindNext" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceNew( comp, &gOutputUnit );
|
||||
if( comp == NULL ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioComponentInstanceNew=%ld", (long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set up a callback function to generate output to the output unit */
|
||||
AURenderCallbackStruct input;
|
||||
input.inputProc = coreaudiowrite;
|
||||
input.inputProcRefCon = NULL;
|
||||
|
||||
err = AudioUnitSetProperty( gOutputUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&input,
|
||||
sizeof( input ) );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioUnitSetProperty-CB=%ld", (long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = AudioUnitSetProperty( gOutputUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&deviceFormat,
|
||||
sizeof( AudioStreamBasicDescription ) );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioUnitSetProperty-SF=%4.4s, %ld", (char*)&err,
|
||||
(long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = AudioUnitInitialize( gOutputUnit );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioUnitInitialize=%ld", (long)err );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Adjust relative processor speed to deal with adjusting sound generation
|
||||
frequency against emulation speed (more flexible than adjusting generated
|
||||
sample rate) */
|
||||
hz = (float)sound_get_effective_processor_speed() /
|
||||
machine_current->timings.tstates_per_frame;
|
||||
/* Amount of audio data we will accumulate before yielding back to the OS.
|
||||
Not much point having more than 100Hz playback, we probably get
|
||||
downgraded by the OS as being a hog too (unlimited Hz limits playback
|
||||
speed to about 2000% on my Mac, 100Hz allows up to 5000% for me) */
|
||||
if( hz > 100.0 ) hz = 100.0;
|
||||
sound_framesiz = deviceFormat.mSampleRate / hz;
|
||||
|
||||
if( ( error = sfifo_init( &sound_fifo, NUM_FRAMES
|
||||
* deviceFormat.mBytesPerFrame
|
||||
* deviceFormat.mChannelsPerFrame
|
||||
* sound_framesiz + 1 ) ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "Problem initialising sound fifo: %s",
|
||||
strerror ( error ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* wait to run sound until we have some sound to play */
|
||||
audio_output_started = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Support pre Xcode 9 SDK */
|
||||
#ifndef __Verify_noErr
|
||||
#define __Verify_noErr((a)) verify_noerr((a))
|
||||
#endif
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
OSStatus err;
|
||||
|
||||
if( audio_output_started )
|
||||
__Verify_noErr( AudioOutputUnitStop( gOutputUnit ) );
|
||||
|
||||
err = AudioUnitUninitialize( gOutputUnit );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioUnitUninitialize=%ld", (long)err );
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceDispose( gOutputUnit );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioComponentInstanceDispose=%ld", (long)err );
|
||||
}
|
||||
|
||||
sfifo_flush( &sound_fifo );
|
||||
sfifo_close( &sound_fifo );
|
||||
}
|
||||
|
||||
/* Copy data to fifo */
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* Convert to bytes */
|
||||
libspectrum_signed_byte* bytes = (libspectrum_signed_byte*)data;
|
||||
len <<= 1;
|
||||
|
||||
while( len ) {
|
||||
if( ( i = sfifo_write( &sound_fifo, bytes, len ) ) < 0 ) {
|
||||
break;
|
||||
} else if( !i ) {
|
||||
usleep( 10000 );
|
||||
}
|
||||
bytes += i;
|
||||
len -= i;
|
||||
}
|
||||
if( i < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't write sound fifo: %s",
|
||||
strerror( i ) );
|
||||
}
|
||||
|
||||
if( !audio_output_started ) {
|
||||
/* Start the rendering
|
||||
The DefaultOutputUnit will do any format conversions to the format of the
|
||||
default device */
|
||||
OSStatus err = AudioOutputUnitStart( gOutputUnit );
|
||||
if( err ) {
|
||||
ui_error( UI_ERROR_ERROR, "AudioOutputUnitStart=%ld", (long)err );
|
||||
return;
|
||||
}
|
||||
|
||||
audio_output_started = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* This is the audio processing callback. */
|
||||
OSStatus coreaudiowrite( void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData )
|
||||
{
|
||||
int f;
|
||||
int len = deviceFormat.mBytesPerFrame * inNumberFrames;
|
||||
uint8_t* out = ioData->mBuffers[0].mData;
|
||||
|
||||
/* Try to only read an even number of bytes so as not to fragment a sample */
|
||||
len = MIN( len, sfifo_used( &sound_fifo ) );
|
||||
len &= sound_stereo_ay != SOUND_STEREO_AY_NONE ? 0xfffc : 0xfffe;
|
||||
|
||||
/* Read input_size bytes from fifo into sound stream */
|
||||
while( ( f = sfifo_read( &sound_fifo, out, len ) ) > 0 ) {
|
||||
out += f;
|
||||
len -= f;
|
||||
}
|
||||
|
||||
/* If we ran out of sound, make do with silence :( */
|
||||
if( f < 0 ) {
|
||||
for( f=0; f<len; f++ ) {
|
||||
*out++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
/* dxsound.c: DirectX sound I/O
|
||||
Copyright (c) 2003-2004 Marek Januszewski, Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* same as for SDL Sound */
|
||||
#define MAX_AUDIO_BUFFER 8192*5
|
||||
|
||||
static LPDIRECTSOUND lpDS; /* DirectSound object */
|
||||
static LPDIRECTSOUNDBUFFER lpDSBuffer; /* sound buffer */
|
||||
|
||||
static DWORD nextpos; /* next position in circular buffer */
|
||||
|
||||
static int sixteenbit;
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
|
||||
WAVEFORMATEX pcmwf; /* waveformat struct */
|
||||
DSBUFFERDESC dsbd; /* buffer description */
|
||||
|
||||
/* Initialize COM */
|
||||
CoInitialize(NULL);
|
||||
|
||||
/* create DirectSound object */
|
||||
if( CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IDirectSound, (void**)&lpDS ) != DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't create DirectSound object.");
|
||||
CoUninitialize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* initialize it */
|
||||
if( IDirectSound_Initialize( lpDS, NULL ) != DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't initialize DirectSound." );
|
||||
CoUninitialize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* set normal cooperative level */
|
||||
if( IDirectSound_SetCooperativeLevel( lpDS, GetDesktopWindow(),
|
||||
DSSCL_NORMAL ) != DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't set DirectSound cooperation level." );
|
||||
IDirectSound_Release( lpDS );
|
||||
CoUninitialize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create wave format description */
|
||||
memset( &pcmwf, 0, sizeof( WAVEFORMATEX ) );
|
||||
|
||||
pcmwf.cbSize = 0;
|
||||
if( settings_current.sound_force_8bit ) {
|
||||
pcmwf.wBitsPerSample = 8;
|
||||
sixteenbit = 0;
|
||||
} else {
|
||||
pcmwf.wBitsPerSample = 16;
|
||||
sixteenbit = 1;
|
||||
}
|
||||
pcmwf.nChannels = *stereoptr ? 2 : 1;
|
||||
pcmwf.nBlockAlign = pcmwf.nChannels * ( sixteenbit ? 2 : 1 );
|
||||
pcmwf.nSamplesPerSec = *freqptr;
|
||||
|
||||
pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
|
||||
|
||||
pcmwf.wFormatTag = WAVE_FORMAT_PCM;
|
||||
|
||||
/* create sound buffer description */
|
||||
memset( &dsbd, 0, sizeof( DSBUFFERDESC ) );
|
||||
dsbd.dwBufferBytes = MAX_AUDIO_BUFFER;
|
||||
|
||||
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME |
|
||||
DSBCAPS_CTRLFREQUENCY | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE;
|
||||
|
||||
dsbd.dwSize = sizeof( DSBUFFERDESC );
|
||||
dsbd.lpwfxFormat = &pcmwf;
|
||||
|
||||
/* attempt to create the buffer */
|
||||
if( IDirectSound_CreateSoundBuffer( lpDS, &dsbd, &lpDSBuffer, NULL )
|
||||
!= DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't create DirectSound buffer." );
|
||||
IDirectSound_Release( lpDS );
|
||||
CoUninitialize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* play buffer */
|
||||
if( IDirectSoundBuffer_Play( lpDSBuffer, 0, 0, DSBPLAY_LOOPING ) != DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't play sound." );
|
||||
IDirectSoundBuffer_Release( lpDSBuffer );
|
||||
IDirectSound_Release( lpDS );
|
||||
CoUninitialize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
nextpos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
if( IDirectSoundBuffer_Stop( lpDSBuffer ) != DS_OK ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't stop sound." );
|
||||
}
|
||||
|
||||
IDirectSoundBuffer_Release( lpDSBuffer );
|
||||
IDirectSound_Release( lpDS );
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
/* Copying data to the buffer */
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
HRESULT hres;
|
||||
int i1, i2;
|
||||
int lsb = 1;
|
||||
|
||||
/* two pair because of circular buffer */
|
||||
UCHAR *ucbuffer1, *ucbuffer2;
|
||||
DWORD length1, length2;
|
||||
DWORD playcursor;
|
||||
long cursordiff;
|
||||
|
||||
if( sixteenbit )
|
||||
len *= 2;
|
||||
|
||||
while( len ) {
|
||||
while( 1 ) {
|
||||
IDirectSoundBuffer_GetCurrentPosition( lpDSBuffer, &playcursor, NULL );
|
||||
|
||||
cursordiff = (long)nextpos - (long)playcursor;
|
||||
|
||||
if( playcursor > nextpos )
|
||||
cursordiff += MAX_AUDIO_BUFFER;
|
||||
|
||||
if( cursordiff < len * 6 )
|
||||
break;
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
/* lock the buffer */
|
||||
hres = IDirectSoundBuffer_Lock( lpDSBuffer, nextpos, (DWORD)len,
|
||||
(void **)&ucbuffer1, &length1,
|
||||
(void **)&ucbuffer2, &length2,
|
||||
0 );
|
||||
if( hres != DS_OK ) return; /* couldn't get a lock on the buffer */
|
||||
|
||||
/* write to the first part of buffer */
|
||||
for( i1 = 0; i1 < length1 && len > 0; i1++ ) {
|
||||
if( sixteenbit ) {
|
||||
if( lsb ) {
|
||||
ucbuffer1[ i1 ] = *data & 0xff;
|
||||
} else {
|
||||
ucbuffer1[ i1 ] = *data >> 8;
|
||||
data++;
|
||||
}
|
||||
lsb = !lsb;
|
||||
} else {
|
||||
ucbuffer1[ i1 ] = ( ( *data++ ) >> 8 ) ^ 0x80;
|
||||
}
|
||||
nextpos++;
|
||||
len--;
|
||||
}
|
||||
|
||||
if( ucbuffer2 ) {
|
||||
/* write to the second part of buffer */
|
||||
for( i2 = 0; i2 < length2 && len > 0; i2++ ) {
|
||||
if( sixteenbit ) {
|
||||
if( lsb ) {
|
||||
ucbuffer2[ i2 ] = *data & 0xff;
|
||||
} else {
|
||||
ucbuffer2[ i2 ] = *data >> 8;
|
||||
data++;
|
||||
}
|
||||
lsb = !lsb;
|
||||
} else {
|
||||
ucbuffer2[ i2 ] = ( ( *data++ ) >> 8 ) ^ 0x80;
|
||||
}
|
||||
nextpos++;
|
||||
len--;
|
||||
}
|
||||
} else {
|
||||
i2 = 0;
|
||||
}
|
||||
|
||||
if( nextpos >= MAX_AUDIO_BUFFER ) nextpos -= MAX_AUDIO_BUFFER;
|
||||
|
||||
/* unlock the buffer */
|
||||
IDirectSoundBuffer_Unlock( lpDSBuffer, ucbuffer1, i1, ucbuffer2, i2 );
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/* hpsound.c: HP-UX sound I/O
|
||||
Copyright (c) 2002-2004 Alexander Yurchenko, Russell Marks, Philip Kendall
|
||||
Matan Ziv-Av, Stuart Brady
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/audio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static int soundfd = -1;
|
||||
static int sixteenbit = 1;
|
||||
|
||||
int sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
int flags, tmp, frag;
|
||||
|
||||
/* select a default device if we weren't explicitly given one */
|
||||
if( device == NULL ) device = "/dev/audio";
|
||||
|
||||
/* Open the sound device non-blocking to avoid hangs if it is being
|
||||
* used by something else, but then set it blocking again as that's what
|
||||
* we actually want */
|
||||
if( ( soundfd = open( device, O_WRONLY | O_NONBLOCK ) ) == -1 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't open sound device '%s'", device );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( ( flags = fcntl( soundfd, F_GETFL ) ) == -1 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't fcntl sound device '%s'", device );
|
||||
close( soundfd );
|
||||
return 1;
|
||||
}
|
||||
|
||||
flags &= ~O_NONBLOCK;
|
||||
|
||||
if( fcntl( soundfd, F_SETFL, flags ) == -1 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't set sound device '%s' blocking",
|
||||
device );
|
||||
close( soundfd );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp = AUDIO_FORMAT_LINEAR16BIT;
|
||||
if( settings_current.sound_force_8bit ||
|
||||
ioctl( soundfd, AUDIO_SET_DATA_FORMAT, tmp ) < 0 ) {
|
||||
|
||||
/* try 8-bit - may be an 8-bit only device */
|
||||
tmp = AUDIO_FORMAT_LINEAR8BIT;
|
||||
if( ioctl( soundfd, AUDIO_SET_DATA_FORMAT, tmp ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
|
||||
if( settings_current.sound_force_8bit ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set sound device '%s' into 8-bit mode", device );
|
||||
} else {
|
||||
ui_error(
|
||||
UI_ERROR_ERROR,
|
||||
"Couldn't set sound device '%s' in either 16-bit or 8-bit mode",
|
||||
device
|
||||
);
|
||||
}
|
||||
|
||||
close( soundfd );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sixteenbit = 0;
|
||||
}
|
||||
|
||||
tmp = ( *stereoptr ) ? 2 : 1;
|
||||
if( ioctl( soundfd, AUDIO_SET_CHANNELS, tmp ) < 0 ) {
|
||||
tmp = ( *stereoptr ) ? 1 : 2;
|
||||
if( ioctl( soundfd, AUDIO_SET_CHANNELS, tmp ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error(
|
||||
UI_ERROR_ERROR,
|
||||
"Couldn't set sound device '%s' into either mono or stereo mode",
|
||||
device
|
||||
);
|
||||
close( soundfd );
|
||||
return 1;
|
||||
}
|
||||
*stereoptr = tmp;
|
||||
}
|
||||
|
||||
if( ioctl( soundfd, AUDIO_SET_SAMPLE_RATE, *freqptr ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,"Couldn't set sound device '%s' speed to %d",
|
||||
device, *freqptr );
|
||||
close( soundfd );
|
||||
return 1;
|
||||
}
|
||||
|
||||
frag = 16384;
|
||||
ioctl( soundfd, AUDIO_SET_FRAGMENT, frag );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
if( soundfd != -1 ) close( soundfd );
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
static unsigned char buf8[4096];
|
||||
unsigned char *data8=(unsigned char *)data;
|
||||
int ret=0, ofs=0;
|
||||
|
||||
len <<= 1; /* now in bytes */
|
||||
if( !sixteenbit ) {
|
||||
libspectrum_signed_word *src;
|
||||
unsigned char *dst;
|
||||
int f;
|
||||
|
||||
src = data; dst = buf8;
|
||||
len >>= 1;
|
||||
/* TODO: confirm byteorder on IA64 */
|
||||
for( f = 0; f < len; f++ )
|
||||
*dst++ = 128 + (int)( (*src++) / 256 );
|
||||
|
||||
data8 = buf8;
|
||||
}
|
||||
|
||||
while( len ) {
|
||||
ret = write( soundfd, data8 + ofs, len );
|
||||
if( ret > 0 ) {
|
||||
ofs += ret;
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* nullsound.c: dummy sound routines
|
||||
Copyright (c) 2003-2004 Philip Kendall
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Dummy functions for when we don't have a sound device; should never be
|
||||
called, so just abort if they are */
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
/* Audio driver not initialised */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
fuse_abort();
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( unsigned char *data, int len )
|
||||
{
|
||||
fuse_abort();
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/* osssound.c: OSS (e.g. Linux) sound I/O
|
||||
Copyright (c) 2000-2004 Russell Marks, Matan Ziv-Av, Philip Kendall
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "spectrum.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* using (8) 64 byte frags for 8kHz, scale up for higher */
|
||||
#define BASE_SOUND_FRAG_PWR 6
|
||||
|
||||
|
||||
static int soundfd=-1;
|
||||
static int sixteenbit=1;
|
||||
|
||||
|
||||
/* returns 0 on *success*, and adjusts freq/stereo args to reflect
|
||||
* what we've actually got.
|
||||
*/
|
||||
int sound_lowlevel_init(const char *device,int *freqptr,int *stereoptr)
|
||||
{
|
||||
int frag,tmp,flags;
|
||||
|
||||
/* select a default device if we weren't explicitly given one */
|
||||
if(device==NULL) device = "/dev/dsp";
|
||||
|
||||
/* Open the sound device non-blocking to avoid hangs if it is being
|
||||
* used by something else, but then set it blocking again as that's what
|
||||
* we actually want
|
||||
*/
|
||||
if((soundfd=open(device,O_WRONLY|O_NONBLOCK))==-1)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't open sound device '%s': %s",device,strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if((flags=fcntl(soundfd,F_GETFL))==-1)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't get flags from sound device: %s",strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
flags &= ~O_NONBLOCK;
|
||||
if(fcntl(soundfd,F_SETFL,flags)==-1)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't set sound device non-blocking: %s",strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp=AFMT_S16_NE;
|
||||
sixteenbit=1;
|
||||
if(settings_current.sound_force_8bit || ioctl(soundfd,SNDCTL_DSP_SETFMT,&tmp)==-1)
|
||||
{
|
||||
/* try 8-bit - may be an 8-bit-only device */
|
||||
tmp=AFMT_U8;
|
||||
if((ioctl(soundfd,SNDCTL_DSP_SETFMT,&tmp))==-1)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't set sound device into 8-bit mode: %s",strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sixteenbit=0;
|
||||
}
|
||||
|
||||
/* XXX should it warn if it didn't get the stereoness it wanted? */
|
||||
tmp=(*stereoptr)?1:0;
|
||||
if(ioctl(soundfd,SNDCTL_DSP_STEREO,&tmp)<0)
|
||||
{
|
||||
/* if it failed, make sure the opposite is ok */
|
||||
tmp=(*stereoptr)?0:1;
|
||||
if(ioctl(soundfd,SNDCTL_DSP_STEREO,&tmp)<0)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't set sound device into either mono or stereo mode: %s",strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
*stereoptr=tmp;
|
||||
}
|
||||
|
||||
frag=(0x80000|BASE_SOUND_FRAG_PWR);
|
||||
|
||||
if(ioctl(soundfd,SNDCTL_DSP_SPEED,freqptr)<0)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't set sound device speed to %d: %s",*freqptr, strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(*freqptr>8250) frag++;
|
||||
if(*freqptr>16500) frag++;
|
||||
if(*freqptr>33000) frag++;
|
||||
if(*stereoptr) frag++;
|
||||
if(sixteenbit) frag++;
|
||||
|
||||
/* FIXME: OSS API docs say you should write to the soundcard in frag size
|
||||
blocks */
|
||||
if(ioctl(soundfd,SNDCTL_DSP_SETFRAGMENT,&frag)<0)
|
||||
{
|
||||
settings_current.sound = 0;
|
||||
ui_error(UI_ERROR_ERROR,"couldn't set sound device fragment size to %d: %s",frag,strerror(errno));
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
|
||||
void sound_lowlevel_end(void)
|
||||
{
|
||||
if(soundfd!=-1)
|
||||
close(soundfd);
|
||||
}
|
||||
|
||||
|
||||
void sound_lowlevel_frame(libspectrum_signed_word *data,int len)
|
||||
{
|
||||
static unsigned char buf8[4096];
|
||||
unsigned char *data8=(unsigned char *)data;
|
||||
int ret=0,ofs=0;
|
||||
|
||||
len<<=1; /* now in bytes */
|
||||
|
||||
if(!sixteenbit)
|
||||
{
|
||||
libspectrum_signed_word *src;
|
||||
unsigned char *dst;
|
||||
int f;
|
||||
|
||||
src=data; dst=buf8;
|
||||
len>>=1;
|
||||
for(f=0;f<len;f++)
|
||||
*dst++=128+(int)((*src++)/256);
|
||||
|
||||
data8=buf8;
|
||||
}
|
||||
|
||||
while(len)
|
||||
{
|
||||
ret=write(soundfd,data8+ofs,len);
|
||||
if(ret>0)
|
||||
ofs+=ret,len-=ret;
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/* pulsesound.c: pulseaudio (Linux) sound I/O
|
||||
Copyright (c) 2010-2019 Grzegorz Jablonski, 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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
#include <pulse/error.h>
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/timeval.h>
|
||||
|
||||
#include "sound.h"
|
||||
|
||||
#define PULSEAUDIO_DEBUG 0
|
||||
|
||||
static pa_simple *pulse_s;
|
||||
static int verbose = 0;
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
pa_simple_free( pulse_s );
|
||||
}
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
pa_sample_spec ss;
|
||||
pa_buffer_attr buf;
|
||||
unsigned int n;
|
||||
unsigned int val;
|
||||
const char *option;
|
||||
char tmp;
|
||||
int err;
|
||||
int bsize = 0;
|
||||
int bdelay = 30; /* default to 30ms */
|
||||
|
||||
/* scan explicitly given parameters */
|
||||
option = device;
|
||||
while( option && *option ) {
|
||||
tmp = '*';
|
||||
if( ( err = sscanf( option, " tlength=%ims %n%c", &val, &n, &tmp ) > 0 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
if( val < 1 ) {
|
||||
fprintf( stderr, "Bad value for PULSEAUDIO tlength, using default\n" );
|
||||
} else {
|
||||
bdelay = val;
|
||||
}
|
||||
} else if( ( err = sscanf( option, " tlength=%i %n%c", &val, &n, &tmp ) > 0 ) &&
|
||||
( tmp == ',' || strlen( option ) == n ) ) {
|
||||
if( val < 1 ) {
|
||||
fprintf( stderr, "Bad value for PULSEAUDIO tlength, using default\n" );
|
||||
} else {
|
||||
bsize = val;
|
||||
}
|
||||
} else if( ( err = sscanf( option, " verbose %n%c", &n, &tmp ) == 1 ) &&
|
||||
( tmp == ',' || ( strlen( option ) == n ) ) ) {
|
||||
verbose = 1;
|
||||
}
|
||||
|
||||
option += n + ( tmp == ',' );
|
||||
}
|
||||
|
||||
#if defined WORDS_BIGENDIAN
|
||||
ss.format = PA_SAMPLE_S16BE;
|
||||
#else
|
||||
ss.format = PA_SAMPLE_S16LE;
|
||||
#endif
|
||||
|
||||
ss.channels = ( *stereoptr )? 2 : 1;
|
||||
ss.rate = *freqptr;
|
||||
|
||||
/* Reduce latency to 30ms or the user-defined value. pulseaudio does
|
||||
not guarantee that */
|
||||
buf.tlength = (bsize)? bsize :
|
||||
pa_usec_to_bytes( bdelay * PA_USEC_PER_MSEC, &ss );
|
||||
|
||||
buf.maxlength = buf.tlength * 4;
|
||||
buf.minreq = buf.tlength / 4;
|
||||
buf.prebuf = (uint32_t) -1;
|
||||
buf.fragsize = (uint32_t) -1;
|
||||
|
||||
if( verbose ) {
|
||||
fprintf( stdout, "buffer requested: maxlength=%lu, tlength=%lu, prebuf=%lu,"
|
||||
" minreq=%lu\n",
|
||||
(unsigned long) buf.maxlength,
|
||||
(unsigned long) buf.tlength,
|
||||
(unsigned long) buf.prebuf,
|
||||
(unsigned long) buf.minreq );
|
||||
}
|
||||
|
||||
pulse_s = pa_simple_new( NULL, PACKAGE, PA_STREAM_PLAYBACK, NULL,
|
||||
"Spectrum", &ss, NULL, &buf, &err );
|
||||
if( pulse_s == NULL ) {
|
||||
fprintf( stderr, "pulseaudio: pa_simple_new() failed: %s\n",
|
||||
pa_strerror( err ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
int retval, error;
|
||||
|
||||
/* Measure sound lag */
|
||||
if( PULSEAUDIO_DEBUG ) {
|
||||
pa_usec_t latency;
|
||||
latency = pa_simple_get_latency( pulse_s, &error );
|
||||
if( latency == (pa_usec_t) - 1 ) {
|
||||
fprintf( stderr, "pulseaudio: pa_simple_get_latency() failed: %s\n",
|
||||
pa_strerror( error ) );
|
||||
} else {
|
||||
fprintf( stderr, "%0.0f ", (float)latency / PA_USEC_PER_MSEC );
|
||||
}
|
||||
}
|
||||
|
||||
retval = pa_simple_write( pulse_s, data, 2 * len, &error );
|
||||
|
||||
if( verbose && retval < 0 ) {
|
||||
fprintf( stderr, "pulseaudio: pa_simple_write() failed: %s\n",
|
||||
pa_strerror( error ) );
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/* sdlsound.c: SDL sound I/O
|
||||
Copyright (c) 2002-2015 Alexander Yurchenko, Russell Marks, Philip Kendall,
|
||||
Fredrick Meunier
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sfifo.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static void sdlwrite( void *userdata, Uint8 *stream, int len );
|
||||
|
||||
sfifo_t sound_fifo;
|
||||
|
||||
/* Number of Spectrum frames audio latency to use */
|
||||
#define NUM_FRAMES 2
|
||||
|
||||
/* Records sound writer status information */
|
||||
static int audio_output_started;
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
SDL_AudioSpec requested, received;
|
||||
int error;
|
||||
float hz;
|
||||
int sound_framesiz;
|
||||
|
||||
#ifndef __MORPHOS__
|
||||
/* I'd rather just use setenv, but Windows doesn't have it */
|
||||
if( device ) {
|
||||
const char *environment = "SDL_AUDIODRIVER=";
|
||||
char *command = libspectrum_new( char, strlen( environment ) +
|
||||
strlen( device ) + 1 );
|
||||
strcpy( command, environment );
|
||||
strcat( command, device );
|
||||
error = putenv( command );
|
||||
libspectrum_free( command );
|
||||
if( error ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't set SDL_AUDIODRIVER: %s",
|
||||
strerror ( error ) );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif /* #ifndef __MORPHOS__ */
|
||||
|
||||
SDL_InitSubSystem( SDL_INIT_AUDIO );
|
||||
|
||||
memset( &requested, 0, sizeof( SDL_AudioSpec ) );
|
||||
|
||||
requested.freq = *freqptr;
|
||||
requested.channels = *stereoptr ? 2 : 1;
|
||||
requested.format = AUDIO_S16SYS;
|
||||
requested.callback = sdlwrite;
|
||||
|
||||
/* Adjust relative processor speed to deal with adjusting sound generation
|
||||
frequency against emulation speed (more flexible than adjusting generated
|
||||
sample rate) */
|
||||
hz = (float)sound_get_effective_processor_speed() /
|
||||
machine_current->timings.tstates_per_frame;
|
||||
/* Amount of audio data we will accumulate before yielding back to the OS.
|
||||
Not much point having more than 100Hz playback, we probably get
|
||||
downgraded by the OS as being a hog too (unlimited Hz limits playback
|
||||
speed to about 2000% on my Mac, 100Hz allows up to 5000% for me) */
|
||||
if( hz > 100.0 ) hz = 100.0;
|
||||
sound_framesiz = *freqptr / hz;
|
||||
#ifdef __FreeBSD__
|
||||
requested.samples = pow( 2.0, floor( log2( sound_framesiz ) ) );
|
||||
#else /* #ifdef __FreeBSD__ */
|
||||
requested.samples = sound_framesiz;
|
||||
#endif /* #ifdef __FreeBSD__ */
|
||||
|
||||
if ( SDL_OpenAudio( &requested, &received ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't open sound device: %s",
|
||||
SDL_GetError() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
*freqptr = received.freq;
|
||||
|
||||
if( received.format != AUDIO_S16SYS ) {
|
||||
/* close audio and then just let SDL convert to this wacky format at a
|
||||
supported sample rate */
|
||||
SDL_CloseAudio();
|
||||
|
||||
requested.freq = *freqptr;
|
||||
sound_framesiz = *freqptr / hz;
|
||||
requested.samples = sound_framesiz;
|
||||
|
||||
if( SDL_OpenAudio( &requested, NULL ) < 0 ) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't open sound device: %s",
|
||||
SDL_GetError() );
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
*stereoptr = received.channels == 1 ? 0 : 1;
|
||||
}
|
||||
|
||||
sound_framesiz = *freqptr / hz;
|
||||
sound_framesiz <<= 1;
|
||||
|
||||
if( ( error = sfifo_init( &sound_fifo, NUM_FRAMES
|
||||
* received.channels
|
||||
* sound_framesiz + 1 ) ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "Problem initialising sound fifo: %s",
|
||||
strerror ( error ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* wait to run sound until we have some sound to play */
|
||||
audio_output_started = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
SDL_PauseAudio( 1 );
|
||||
SDL_LockAudio();
|
||||
SDL_CloseAudio();
|
||||
SDL_QuitSubSystem( SDL_INIT_AUDIO );
|
||||
sfifo_flush( &sound_fifo );
|
||||
sfifo_close( &sound_fifo );
|
||||
}
|
||||
|
||||
/* Copy data to fifo */
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* Convert to bytes */
|
||||
libspectrum_signed_byte* bytes = (libspectrum_signed_byte*)data;
|
||||
len <<= 1;
|
||||
|
||||
while( len ) {
|
||||
if( ( i = sfifo_write( &sound_fifo, bytes, len ) ) < 0 ) {
|
||||
break;
|
||||
} else if (!i) {
|
||||
SDL_Delay(10);
|
||||
}
|
||||
bytes += i;
|
||||
len -= i;
|
||||
}
|
||||
if( i < 0 ) {
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't write sound fifo: %s",
|
||||
strerror( i ) );
|
||||
}
|
||||
|
||||
if( !audio_output_started ) {
|
||||
SDL_PauseAudio( 0 );
|
||||
audio_output_started = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* Write len samples from fifo into stream */
|
||||
void
|
||||
sdlwrite( void *userdata, Uint8 *stream, int len )
|
||||
{
|
||||
int f;
|
||||
|
||||
/* Try to only read an even number of bytes so as not to fragment a sample */
|
||||
len = MIN( len, sfifo_used( &sound_fifo ) );
|
||||
len &= sound_stereo_ay ? 0xfffc : 0xfffe;
|
||||
|
||||
/* Read input_size bytes from fifo into sound stream */
|
||||
while( ( f = sfifo_read( &sound_fifo, stream, len ) ) > 0 ) {
|
||||
stream += f;
|
||||
len -= f;
|
||||
}
|
||||
|
||||
/* If we ran out of sound, do nothing else as SDL has prefilled
|
||||
the output buffer with silence :( */
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
------------------------------------------------------------
|
||||
SFIFO 1.3
|
||||
------------------------------------------------------------
|
||||
* Simple portable lock-free FIFO
|
||||
* (c) 2000-2002, David Olofson
|
||||
* Released under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1
|
||||
|
||||
* Modifications by Philip Kendall (c) 2007
|
||||
* This modified version is released under the GNU GENERAL PUBLIC LICENSE
|
||||
* version 2, or any later version
|
||||
-----------------------------------------------------------
|
||||
TODO:
|
||||
* Is there a way to avoid losing one byte of buffer
|
||||
space to avoid extra variables or locking?
|
||||
|
||||
* Test more compilers and environments.
|
||||
-----------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/string.h>
|
||||
# include <asm/uaccess.h>
|
||||
# include <linux/malloc.h>
|
||||
# define malloc(x) kmalloc(x, GFP_KERNEL)
|
||||
# define free(x, y) kfree_s(x, y)
|
||||
#else
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
# define free(x, y) free(x)
|
||||
#endif
|
||||
|
||||
#include "sfifo.h"
|
||||
|
||||
#ifdef _SFIFO_TEST_
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#define DBG(x) /*(x)*/
|
||||
#define TEST_BUFSIZE 10
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Alloc buffer, init FIFO etc...
|
||||
*/
|
||||
int sfifo_init(sfifo_t *f, int size)
|
||||
{
|
||||
memset(f, 0, sizeof(sfifo_t));
|
||||
|
||||
if(size > SFIFO_MAX_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Set sufficient power-of-2 size.
|
||||
*
|
||||
* No, there's no bug. If you need
|
||||
* room for N bytes, the buffer must
|
||||
* be at least N+1 bytes. (The fifo
|
||||
* can't tell 'empty' from 'full'
|
||||
* without unsafe index manipulations
|
||||
* otherwise.)
|
||||
*/
|
||||
f->size = 1;
|
||||
for(; f->size <= size; f->size <<= 1)
|
||||
;
|
||||
|
||||
/* Get buffer */
|
||||
if( 0 == (f->buffer = malloc(f->size)) )
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dealloc buffer etc...
|
||||
*/
|
||||
void sfifo_close(sfifo_t *f)
|
||||
{
|
||||
if(f->buffer)
|
||||
free(f->buffer, f->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Empty FIFO buffer
|
||||
*/
|
||||
void sfifo_flush(sfifo_t *f)
|
||||
{
|
||||
/* Reset positions */
|
||||
f->readpos = 0;
|
||||
f->writepos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write bytes to a FIFO
|
||||
* Return number of bytes written, or an error code
|
||||
*/
|
||||
int sfifo_write(sfifo_t *f, const void *_buf, int len)
|
||||
{
|
||||
int total;
|
||||
int i;
|
||||
const char *buf = (const char *)_buf;
|
||||
|
||||
if(!f->buffer)
|
||||
return -ENODEV; /* No buffer! */
|
||||
|
||||
/* total = len = min(space, len) */
|
||||
total = sfifo_space(f);
|
||||
DBG(printf("sfifo_space() = %d\n",total));
|
||||
if(len > total)
|
||||
len = total;
|
||||
else
|
||||
total = len;
|
||||
|
||||
i = f->writepos;
|
||||
if(i + len > f->size)
|
||||
{
|
||||
memcpy(f->buffer + i, buf, f->size - i);
|
||||
buf += f->size - i;
|
||||
len -= f->size - i;
|
||||
i = 0;
|
||||
}
|
||||
memcpy(f->buffer + i, buf, len);
|
||||
f->writepos = i + len;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* Write bytes to a FIFO from a user space buffer
|
||||
* Return number of bytes written, or an error code
|
||||
*/
|
||||
int sfifo_write_user(sfifo_t *f, const void *buf, int len)
|
||||
{
|
||||
int total;
|
||||
int i;
|
||||
|
||||
if(!f->buffer)
|
||||
return -ENODEV; /* No buffer! */
|
||||
|
||||
/* total = len = min(space, len) */
|
||||
total = sfifo_space(f);
|
||||
if(len > total)
|
||||
len = total;
|
||||
else
|
||||
total = len;
|
||||
|
||||
i = f->writepos;
|
||||
if(i + len > f->size)
|
||||
{
|
||||
if(f->size - i)
|
||||
{
|
||||
if(copy_from_user(f->buffer + i, buf, f->size - i))
|
||||
return -EFAULT;
|
||||
buf += f->size - i;
|
||||
len -= f->size - i;
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
if(copy_from_user(f->buffer + i, buf, len))
|
||||
return -EFAULT;
|
||||
f->writepos = i + len;
|
||||
|
||||
return total;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read bytes from a FIFO
|
||||
* Return number of bytes read, or an error code
|
||||
*/
|
||||
int sfifo_read(sfifo_t *f, void *_buf, int len)
|
||||
{
|
||||
int total;
|
||||
int i;
|
||||
char *buf = (char *)_buf;
|
||||
|
||||
if(!f->buffer)
|
||||
return -ENODEV; /* No buffer! */
|
||||
|
||||
/* total = len = min(used, len) */
|
||||
total = sfifo_used(f);
|
||||
DBG(printf("sfifo_used() = %d\n",total));
|
||||
if(len > total)
|
||||
len = total;
|
||||
else
|
||||
total = len;
|
||||
|
||||
i = f->readpos;
|
||||
if(i + len > f->size)
|
||||
{
|
||||
memcpy(buf, f->buffer + i, f->size - i);
|
||||
buf += f->size - i;
|
||||
len -= f->size - i;
|
||||
i = 0;
|
||||
}
|
||||
memcpy(buf, f->buffer + i, len);
|
||||
f->readpos = i + len;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* Read bytes from a FIFO into a user space buffer
|
||||
* Return number of bytes read, or an error code
|
||||
*/
|
||||
int sfifo_read_user(sfifo_t *f, void *buf, int len)
|
||||
{
|
||||
int total;
|
||||
int i;
|
||||
|
||||
if(!f->buffer)
|
||||
return -ENODEV; /* No buffer! */
|
||||
|
||||
/* total = len = min(used, len) */
|
||||
total = sfifo_used(f);
|
||||
if(len > total)
|
||||
len = total;
|
||||
else
|
||||
total = len;
|
||||
|
||||
i = f->readpos;
|
||||
if(i + len > f->size)
|
||||
{
|
||||
if(f->size - i)
|
||||
{
|
||||
if(copy_to_user(buf, f->buffer + i, f->size - i))
|
||||
return -EFAULT;
|
||||
buf += f->size - i;
|
||||
len -= f->size - i;
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
if(copy_to_user(buf, f->buffer + i, len))
|
||||
return -EFAULT;
|
||||
f->readpos = i + len;
|
||||
|
||||
return total;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _SFIFO_TEST_
|
||||
void *sender(void *arg)
|
||||
{
|
||||
char buf[TEST_BUFSIZE*2];
|
||||
int i,j;
|
||||
int cnt = 0;
|
||||
int res;
|
||||
sfifo_t *sf = (sfifo_t *)arg;
|
||||
while(1)
|
||||
{
|
||||
j = sfifo_space(sf);
|
||||
for(i = 0; i < j; ++i)
|
||||
{
|
||||
++cnt;
|
||||
buf[i] = cnt;
|
||||
}
|
||||
res = sfifo_write(sf, &buf, j);
|
||||
if(res != j)
|
||||
{
|
||||
printf("Write failed!\n");
|
||||
sleep(1);
|
||||
} else if(res)
|
||||
printf("Wrote %d\n", res);
|
||||
}
|
||||
}
|
||||
int main()
|
||||
{
|
||||
sfifo_t sf;
|
||||
char last = 0;
|
||||
pthread_t thread;
|
||||
char buf[100] = "---------------------------------------";
|
||||
|
||||
printf("sfifo_init(&sf, %d) = %d\n",
|
||||
TEST_BUFSIZE, sfifo_init(&sf, TEST_BUFSIZE)
|
||||
);
|
||||
|
||||
#if 0
|
||||
printf("sfifo_write(&sf,\"0123456789\",7) = %d\n",
|
||||
sfifo_write(&sf,"0123456789",7) );
|
||||
|
||||
printf("sfifo_write(&sf,\"abcdefghij\",7) = %d\n",
|
||||
sfifo_write(&sf,"abcdefghij",7) );
|
||||
|
||||
printf("sfifo_read(&sf,buf,8) = %d\n",
|
||||
sfifo_read(&sf,buf,8) );
|
||||
|
||||
buf[20] = 0;
|
||||
printf("buf =\"%s\"\n",buf);
|
||||
|
||||
printf("sfifo_write(&sf,\"0123456789\",7) = %d\n",
|
||||
sfifo_write(&sf,"0123456789",7) );
|
||||
|
||||
printf("sfifo_read(&sf,buf,10) = %d\n",
|
||||
sfifo_read(&sf,buf,10) );
|
||||
|
||||
buf[20] = 0;
|
||||
printf("buf =\"%s\"\n",buf);
|
||||
#else
|
||||
pthread_create(&thread, NULL, sender, &sf);
|
||||
|
||||
while(1)
|
||||
{
|
||||
static int c = 0;
|
||||
++last;
|
||||
while( sfifo_read(&sf,buf,1) != 1 )
|
||||
/*sleep(1)*/;
|
||||
if(last != buf[0])
|
||||
{
|
||||
printf("Error %d!\n", buf[0]-last);
|
||||
last = buf[0];
|
||||
}
|
||||
else
|
||||
printf("Ok. (%d)\n",++c);
|
||||
}
|
||||
#endif
|
||||
|
||||
sfifo_close(&sf);
|
||||
printf("sfifo_close(&sf)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
------------------------------------------------------------
|
||||
SFIFO 1.3
|
||||
------------------------------------------------------------
|
||||
* Simple portable lock-free FIFO
|
||||
* (c) 2000-2002, David Olofson
|
||||
* Released under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1
|
||||
*
|
||||
* Platform support:
|
||||
* gcc / Linux / x86: Works
|
||||
* gcc / Linux / x86 kernel: Works
|
||||
* gcc / FreeBSD / x86: Works
|
||||
* gcc / NetBSD / x86: Works
|
||||
* gcc / Mac OS X / PPC: Works
|
||||
* gcc / Win32 / x86: Works
|
||||
* Borland C++ / DOS / x86RM: Works
|
||||
* Borland C++ / Win32 / x86PM16: Untested
|
||||
* ? / Various Un*ces / ?: Untested
|
||||
* ? / Mac OS / PPC: Untested
|
||||
* gcc / BeOS / x86: Untested
|
||||
* gcc / BeOS / PPC: Untested
|
||||
* ? / ? / Alpha: Untested
|
||||
*
|
||||
* 1.2: Max buffer size halved, to avoid problems with
|
||||
* the sign bit...
|
||||
*
|
||||
* 1.3: Critical buffer allocation bug fixed! For certain
|
||||
* requested buffer sizes, older version would
|
||||
* allocate a buffer of insufficient size, which
|
||||
* would result in memory thrashing. (Amazing that
|
||||
* I've manage to use this to the extent I have
|
||||
* without running into this... *heh*)
|
||||
*/
|
||||
|
||||
#ifndef _SFIFO_H_
|
||||
#define _SFIFO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*------------------------------------------------
|
||||
"Private" stuff
|
||||
------------------------------------------------*/
|
||||
/*
|
||||
* Porting note:
|
||||
* Reads and writes of a variable of this type in memory
|
||||
* must be *atomic*! 'int' is *not* atomic on all platforms.
|
||||
* A safe type should be used, and sfifo should limit the
|
||||
* maximum buffer size accordingly.
|
||||
*/
|
||||
typedef int sfifo_atomic_t;
|
||||
#ifdef __TURBOC__
|
||||
# define SFIFO_MAX_BUFFER_SIZE 0x7fff
|
||||
#else /* Kludge: Assume 32 bit platform */
|
||||
# define SFIFO_MAX_BUFFER_SIZE 0x7fffffff
|
||||
#endif
|
||||
|
||||
typedef struct sfifo_t
|
||||
{
|
||||
char *buffer;
|
||||
int size; /* Number of bytes */
|
||||
sfifo_atomic_t readpos; /* Read position */
|
||||
sfifo_atomic_t writepos; /* Write position */
|
||||
} sfifo_t;
|
||||
|
||||
#define SFIFO_SIZEMASK(x) ((x)->size - 1)
|
||||
|
||||
|
||||
/*------------------------------------------------
|
||||
API
|
||||
------------------------------------------------*/
|
||||
int sfifo_init(sfifo_t *f, int size);
|
||||
void sfifo_close(sfifo_t *f);
|
||||
void sfifo_flush(sfifo_t *f);
|
||||
int sfifo_write(sfifo_t *f, const void *buf, int len);
|
||||
int sfifo_read(sfifo_t *f, void *buf, int len);
|
||||
#define sfifo_used(x) (((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x))
|
||||
#define sfifo_space(x) ((x)->size - 1 - sfifo_used(x))
|
||||
|
||||
|
||||
/*------------------------------------------------
|
||||
Linux kernel space interface
|
||||
------------------------------------------------*/
|
||||
#ifdef __KERNEL__
|
||||
int sfifo_write_user(sfifo_t *f, const void *buf, int len);
|
||||
int sfifo_read_user(sfifo_t *f, void *buf, int len);
|
||||
#else
|
||||
# define sfifo_write_user sfifo_write
|
||||
# define sfifo_read_user sfifo_read
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,203 +0,0 @@
|
||||
/* sunsound.c: OpenBSD sound I/O
|
||||
Copyright (c) 2002-2004 Alexander Yurchenko, Russell Marks, Philip Kendall
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined(__SVR4) && defined(__sun)
|
||||
#define solaris
|
||||
#else
|
||||
#undef solaris
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/audioio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#ifdef solaris
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* using (8) 64 byte frags for 8kHz, scale up for higher */
|
||||
#define BASE_SOUND_FRAG_PWR 6
|
||||
|
||||
static int soundfd = -1;
|
||||
static int sixteenbit = 0;
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
#ifndef solaris
|
||||
int frag;
|
||||
#endif
|
||||
int flags;
|
||||
struct audio_info ai;
|
||||
|
||||
if (device == NULL)
|
||||
device = "/dev/audio";
|
||||
|
||||
/* Open the sound device non-blocking to avoid hangs if it is
|
||||
being used by something else, but then set it blocking
|
||||
again as that's what we actually want */
|
||||
if ((soundfd = open(device, O_WRONLY | O_NONBLOCK )) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't open sound device '%s'",
|
||||
device );
|
||||
return 1;
|
||||
}
|
||||
if ((flags = fcntl(soundfd, F_GETFL)) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR, "Couldn't fcntl sound device '%s'",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
flags &= ~O_NONBLOCK;
|
||||
if (fcntl(soundfd, F_SETFL, flags) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set sound device '%s' blocking",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
AUDIO_INITINFO(&ai);
|
||||
|
||||
ai.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
ai.play.precision = 16;
|
||||
sixteenbit = 1;
|
||||
if (settings_current.sound_force_8bit ||
|
||||
ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
/* try 8-bit */
|
||||
ai.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
ai.play.precision = 8;
|
||||
sixteenbit = 0;
|
||||
if (ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set bit size of sound device '%s'",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
ai.play.channels = *stereoptr ? 2 : 1;
|
||||
if (ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
/* if it failed make sure the opposite is ok */
|
||||
ai.play.channels = *stereoptr ? 1 : 2;
|
||||
if (ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set channels of sound device '%s'",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
/* FIXME: is this line correct? */
|
||||
*stereoptr = *stereoptr ? 1 : 2;
|
||||
}
|
||||
|
||||
ai.play.sample_rate = *freqptr;
|
||||
if (ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set sample rate of sound device '%s'",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef solaris
|
||||
frag = 0x80000 | BASE_SOUND_FRAG_PWR;
|
||||
if (*freqptr > 8250)
|
||||
frag++;
|
||||
if (*freqptr > 16500)
|
||||
frag++;
|
||||
if (*freqptr > 33000)
|
||||
frag++;
|
||||
if (*stereoptr)
|
||||
frag++;
|
||||
if (sixteenbit)
|
||||
frag++;
|
||||
ai.blocksize = 1 << (frag & 0xffff);
|
||||
ai.hiwat = ((unsigned)frag >> 16) & 0x7fff;
|
||||
if (ai.hiwat == 0)
|
||||
ai.hiwat = 65536;
|
||||
if (ioctl(soundfd, AUDIO_SETINFO, &ai) == -1) {
|
||||
settings_current.sound = 0;
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Couldn't set block size of sound device '%s'",
|
||||
device );
|
||||
close(soundfd);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
#ifdef solaris
|
||||
ioctl(soundfd, I_FLUSH, FLUSHW);
|
||||
#else
|
||||
ioctl(soundfd, AUDIO_FLUSH);
|
||||
#endif
|
||||
close(soundfd);
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
static unsigned char buf8[4096];
|
||||
unsigned char *data8=(unsigned char *)data;
|
||||
int ret=0, ofs=0;
|
||||
|
||||
len <<= 1; /* now in bytes */
|
||||
|
||||
if (!sixteenbit) {
|
||||
libspectrum_signed_word *src;
|
||||
unsigned char *dst;
|
||||
int f;
|
||||
|
||||
src = data; dst = buf8;
|
||||
len >>= 1;
|
||||
for (f=0; f < len; f++)
|
||||
*dst++ = 128 + (int)((*src++)/256);
|
||||
|
||||
data8 = buf8;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
ret = write (soundfd, data8 + ofs, len);
|
||||
if (ret > 0) {
|
||||
ofs += ret;
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/* wiisound.c: Wii sound routines
|
||||
Copyright (c) 2008-2009 Bjoern Giesler, Philip Kendall
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "sfifo.h"
|
||||
|
||||
#include <gccore.h>
|
||||
#include <ogc/audio.h>
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
int samplerate;
|
||||
int streamstate;
|
||||
sfifo_t sound_fifo;
|
||||
|
||||
#define BUFSIZE 16384
|
||||
u8 dmabuf[BUFSIZE<<1] ATTRIBUTE_ALIGN(32);
|
||||
int dmalen = BUFSIZE;
|
||||
|
||||
static void
|
||||
sound_dmacallback( void )
|
||||
{
|
||||
if( sfifo_used( &sound_fifo ) < 128) return;
|
||||
|
||||
dmalen = MIN( BUFSIZE, sfifo_used( &sound_fifo ) );
|
||||
sfifo_read( &sound_fifo, dmabuf, dmalen );
|
||||
DCFlushRange( dmabuf, dmalen );
|
||||
AUDIO_InitDMA( (u32)dmabuf, dmalen );
|
||||
AUDIO_StartDMA();
|
||||
}
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
switch(*freqptr) {
|
||||
case 32000:
|
||||
samplerate = AI_SAMPLERATE_32KHZ;
|
||||
break;
|
||||
case 48000:
|
||||
samplerate = AI_SAMPLERATE_48KHZ;
|
||||
break;
|
||||
default:
|
||||
printf("Sample rate %d not supported on Wii\n", *freqptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sfifo_init( &sound_fifo, BUFSIZE );
|
||||
*stereoptr = 1;
|
||||
|
||||
AUDIO_Init( NULL );
|
||||
AUDIO_SetDSPSampleRate( samplerate );
|
||||
|
||||
#ifndef DISPLAY_AUDIO
|
||||
AUDIO_RegisterDMACallback( sound_dmacallback );
|
||||
memset( dmabuf, 0, BUFSIZE );
|
||||
AUDIO_InitDMA( (u32)dmabuf, BUFSIZE );
|
||||
DCFlushRange( dmabuf, dmalen );
|
||||
AUDIO_StartDMA();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
sfifo_flush( &sound_fifo );
|
||||
sfifo_close( &sound_fifo );
|
||||
AUDIO_StopDMA();
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame(libspectrum_signed_word *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
libspectrum_signed_byte *bytes = (libspectrum_signed_byte*)data;
|
||||
len <<= 1;
|
||||
|
||||
while(len) {
|
||||
if( ( i = sfifo_write( &sound_fifo, bytes, len ) ) < 0 )
|
||||
break;
|
||||
else if( !i )
|
||||
usleep(10000);
|
||||
bytes += i;
|
||||
len -= i;
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/* win32sound.c: Win32 sound using MS Windows Multimedia API
|
||||
Copyright (c) 2007 Marek Januszewski
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static HWAVEOUT hwaveout;
|
||||
static WAVEHDR wavehdr[2];
|
||||
static HANDLE sem_sound_done;
|
||||
static CRITICAL_SECTION sound_lock;
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static void *buffers[2];
|
||||
static int buffer_used[2];
|
||||
static int current_buffer;
|
||||
static char buffer1[ BUFFER_SIZE ];
|
||||
static char buffer2[ BUFFER_SIZE ];
|
||||
|
||||
static int sixteenbit;
|
||||
|
||||
static void
|
||||
sound_display_mmresult( const char * const func, MMRESULT result );
|
||||
|
||||
static void CALLBACK
|
||||
sound_callback( HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
|
||||
|
||||
int
|
||||
sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
|
||||
{
|
||||
WAVEFORMATEX pcmwf; /* waveformat struct */
|
||||
MMRESULT result;
|
||||
|
||||
/* create wave format description */
|
||||
memset( &pcmwf, 0, sizeof( WAVEFORMATEX ) );
|
||||
|
||||
pcmwf.cbSize = 0;
|
||||
if( settings_current.sound_force_8bit ) {
|
||||
pcmwf.wBitsPerSample = 8;
|
||||
sixteenbit = 0;
|
||||
} else {
|
||||
pcmwf.wBitsPerSample = 16;
|
||||
sixteenbit = 1;
|
||||
}
|
||||
pcmwf.nChannels = *stereoptr ? 2 : 1;
|
||||
pcmwf.nBlockAlign = pcmwf.nChannels * ( sixteenbit ? 2 : 1 );
|
||||
pcmwf.nSamplesPerSec = *freqptr;
|
||||
pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
|
||||
pcmwf.wFormatTag = WAVE_FORMAT_PCM;
|
||||
|
||||
/* Open sound device for output */
|
||||
result = waveOutOpen( &hwaveout, WAVE_MAPPER,
|
||||
&pcmwf, ( DWORD_PTR ) sound_callback, 0,
|
||||
CALLBACK_FUNCTION );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutOpen", result );
|
||||
|
||||
buffers[0] = buffer1;
|
||||
buffers[1] = buffer2;
|
||||
buffer_used[0] = 0;
|
||||
buffer_used[1] = 0;
|
||||
current_buffer = 0;
|
||||
memset( &wavehdr[0], 0, sizeof( WAVEHDR ) );
|
||||
memset( &wavehdr[1], 0, sizeof( WAVEHDR ) );
|
||||
|
||||
sem_sound_done = CreateSemaphore( NULL, 0, 2, NULL );
|
||||
InitializeCriticalSection( &sound_lock );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_end( void )
|
||||
{
|
||||
MMRESULT result;
|
||||
|
||||
/* reset the sound */
|
||||
result = waveOutReset( hwaveout );
|
||||
if( result != MMSYSERR_NOERROR ) {
|
||||
settings_current.sound = 0;
|
||||
sound_display_mmresult( "Couldn't stop sound (waveOutReset)", result );
|
||||
return;
|
||||
}
|
||||
|
||||
/* unprepare wave headers */
|
||||
if( wavehdr[ 0 ].dwFlags & WHDR_PREPARED ) {
|
||||
result = waveOutUnprepareHeader( hwaveout, &wavehdr[ 0 ], sizeof( WAVEHDR ) );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutUnprepareHeader", result );
|
||||
}
|
||||
|
||||
if( wavehdr[ 1 ].dwFlags & WHDR_PREPARED ) {
|
||||
result = waveOutUnprepareHeader( hwaveout, &wavehdr[ 1 ], sizeof( WAVEHDR ) );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutUnprepareHeader", result );
|
||||
}
|
||||
|
||||
/* close the device */
|
||||
result = waveOutClose( hwaveout );
|
||||
if( result != MMSYSERR_NOERROR ) {
|
||||
settings_current.sound = 0;
|
||||
sound_display_mmresult( "Couldn't stop sound (waveOutClose)", result );
|
||||
}
|
||||
|
||||
CloseHandle( sem_sound_done );
|
||||
DeleteCriticalSection( &sound_lock );
|
||||
}
|
||||
|
||||
void
|
||||
sound_lowlevel_frame( libspectrum_signed_word *data, int len )
|
||||
{
|
||||
static unsigned char buf8[4096];
|
||||
MMRESULT result;
|
||||
|
||||
/* Convert to bytes */
|
||||
unsigned char *bytes = (unsigned char *) data;
|
||||
len <<= 1;
|
||||
if( !sixteenbit ) {
|
||||
libspectrum_signed_word *src;
|
||||
unsigned char *dst;
|
||||
int f;
|
||||
|
||||
src = data; dst = buf8;
|
||||
len >>= 1;
|
||||
/* TODO: confirm byteorder on IA64 */
|
||||
for( f = 0; f < len; f++ )
|
||||
*dst++ = 128 + (int)( (*src++) / 256 );
|
||||
|
||||
bytes = buf8;
|
||||
}
|
||||
|
||||
if( len > BUFFER_SIZE ) {
|
||||
ui_error( UI_ERROR_WARNING, "%s: requested wave size exceeds the buffer size", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
/* wait for the buffer to finish playing */
|
||||
if( buffer_used[ current_buffer ] > 0 )
|
||||
WaitForSingleObject( sem_sound_done, INFINITE );
|
||||
|
||||
/* unprepare the header if it's prepared */
|
||||
if( wavehdr[ current_buffer ].dwFlags & WHDR_PREPARED ) {
|
||||
result = waveOutUnprepareHeader( hwaveout, &wavehdr[ current_buffer ], sizeof( WAVEHDR ) );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutUnprepareHeader", result );
|
||||
}
|
||||
|
||||
/* copy the new wave into the buffer */
|
||||
memcpy( buffers[ current_buffer ], bytes, len );
|
||||
buffer_used[ current_buffer ] = len;
|
||||
|
||||
/* prepare the header */
|
||||
wavehdr[ current_buffer ].lpData = ( LPSTR ) bytes;
|
||||
wavehdr[ current_buffer ].dwBufferLength = len;
|
||||
wavehdr[ current_buffer ].dwLoops |= WHDR_BEGINLOOP | WHDR_ENDLOOP;
|
||||
|
||||
result = waveOutPrepareHeader( hwaveout, &wavehdr[ current_buffer ], sizeof( WAVEHDR ) );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutPrepareHeader", result );
|
||||
|
||||
/* play */
|
||||
result = waveOutWrite( hwaveout, &wavehdr[ current_buffer ], sizeof( WAVEHDR ) );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
sound_display_mmresult( "waveOutWrite", result );
|
||||
|
||||
/* FIXME this could be done way easier */
|
||||
current_buffer++;
|
||||
if( current_buffer == 2 )
|
||||
current_buffer = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sound_display_mmresult( const char * const func, MMRESULT result )
|
||||
{
|
||||
const char *mmresult;
|
||||
|
||||
switch ( result ) {
|
||||
case WAVERR_BADFORMAT: mmresult = "WAVERR_BADFORMAT"; break;
|
||||
case WAVERR_SYNC: mmresult = "WAVERR_SYNC"; break;
|
||||
case WAVERR_STILLPLAYING: mmresult = "WAVERR_STILLPLAYING"; break;
|
||||
case MMSYSERR_NOERROR: mmresult = "MMSYSERR_NOERROR"; break;
|
||||
case MMSYSERR_BADDEVICEID: mmresult = "MMSYSERR_BADDEVICEID"; break;
|
||||
case MMSYSERR_NODRIVER: mmresult = "MMSYSERR_NODRIVER"; break;
|
||||
case MMSYSERR_INVALHANDLE: mmresult = "MMSYSERR_INVALHANDLE"; break;
|
||||
case MMSYSERR_NOMEM: mmresult = "MMSYSERR_NOMEM"; break;
|
||||
case MMSYSERR_ALLOCATED: mmresult = "MMSYSERR_ALLOCATED"; break;
|
||||
default: mmresult = "UNKNOWN"; break;
|
||||
}
|
||||
|
||||
ui_error( UI_ERROR_ERROR, "%s error: %s(error no: %d)", func, mmresult, result );
|
||||
}
|
||||
|
||||
static void CALLBACK
|
||||
sound_callback( HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
|
||||
{
|
||||
if( uMsg == WOM_DONE ) {
|
||||
EnterCriticalSection( &sound_lock );
|
||||
ReleaseSemaphore( sem_sound_done, 1, NULL );
|
||||
LeaveCriticalSection( &sound_lock );
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "peripherals/ula.h"
|
||||
#include "phantom_typist.h"
|
||||
#include "psg.h"
|
||||
#include "profile.h"
|
||||
#include "rzx.h"
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
@@ -157,7 +156,6 @@ spectrum_frame( void )
|
||||
if( sound_enabled ) sound_frame();
|
||||
|
||||
if( display_frame() ) return 1;
|
||||
if( profile_active ) profile_frame( frame_length );
|
||||
printer_frame();
|
||||
|
||||
/* Add an interrupt unless they're being generated by .rzx playback */
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/* sdl.c: SDL speed routines for Fuse
|
||||
Copyright (c) 1999-2008 Philip Kendall, Marek Januszewski, Fredrick Meunier
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
double
|
||||
timer_get_time( void )
|
||||
{
|
||||
return SDL_GetTicks() / 1000.0;
|
||||
}
|
||||
|
||||
void
|
||||
timer_sleep( int ms )
|
||||
{
|
||||
SDL_Delay( ms );
|
||||
}
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "event.h"
|
||||
#include "infrastructure/startup_manager.h"
|
||||
#include "movie.h"
|
||||
#include "phantom_typist.h"
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
@@ -138,32 +137,6 @@ timer_register_startup( void )
|
||||
timer_end );
|
||||
}
|
||||
|
||||
#ifdef SOUND_FIFO
|
||||
|
||||
/* Callback-style sound based timer */
|
||||
#include "sound/sfifo.h"
|
||||
|
||||
extern sfifo_t sound_fifo;
|
||||
|
||||
static void
|
||||
timer_frame_callback_sound( libspectrum_dword last_tstates )
|
||||
{
|
||||
for(;;) {
|
||||
|
||||
/* Sleep while fifo is full */
|
||||
if( sfifo_space( &sound_fifo ) < sound_framesiz ) {
|
||||
timer_sleep( TEN_MS );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
event_add( last_tstates + machine_current->timings.tstates_per_frame,
|
||||
timer_event );
|
||||
}
|
||||
|
||||
#else /* #ifdef SOUND_FIFO */
|
||||
|
||||
/* Blocking socket-style sound based timer */
|
||||
static void
|
||||
@@ -173,7 +146,6 @@ timer_frame_callback_sound( libspectrum_dword last_tstates )
|
||||
timer_event );
|
||||
}
|
||||
|
||||
#endif /* #ifdef SOUND_FIFO */
|
||||
|
||||
void
|
||||
timer_start_fastloading( void )
|
||||
|
||||
@@ -38,13 +38,9 @@
|
||||
#include "tape.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uimedia.h"
|
||||
#include "ui/widget/widget.h"
|
||||
|
||||
#define MESSAGE_MAX_LENGTH 256
|
||||
|
||||
/* We don't start in a widget */
|
||||
int ui_widget_level = -1;
|
||||
|
||||
static char last_message[ MESSAGE_MAX_LENGTH ] = "";
|
||||
static size_t frames_since_last_message = 0;
|
||||
|
||||
@@ -775,19 +771,6 @@ ui_mdr_write( int which, int saveas )
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef USE_WIDGET
|
||||
int
|
||||
ui_widget_init( void )
|
||||
{
|
||||
return widget_init();
|
||||
}
|
||||
|
||||
int
|
||||
ui_widget_end( void )
|
||||
{
|
||||
return widget_end();
|
||||
}
|
||||
#else
|
||||
int
|
||||
ui_widget_init( void )
|
||||
{
|
||||
@@ -809,4 +792,3 @@ void
|
||||
ui_widget_keyhandler( int native_key )
|
||||
{
|
||||
}
|
||||
#endif /* #ifndef USE_WIDGET */
|
||||
@@ -1,49 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
## Copyright (c) 2001-2004 Philip Kendall
|
||||
## 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: [email protected]
|
||||
|
||||
if UI_FB
|
||||
|
||||
fuse_SOURCES += $(ui_fb_files)
|
||||
|
||||
BUILT_SOURCES += $(ui_fb_built)
|
||||
|
||||
endif
|
||||
|
||||
|
||||
CLEANFILES += $(ui_fb_built)
|
||||
|
||||
ui_fb_files = \
|
||||
ui/fb/fbdisplay.c \
|
||||
ui/fb/fbdisplay.h \
|
||||
ui/fb/fbjoystick.c \
|
||||
ui/fb/fbkeyboard.c \
|
||||
ui/fb/fbkeyboard.h \
|
||||
ui/fb/fbmouse.c \
|
||||
ui/fb/fbmouse.h \
|
||||
ui/fb/fbui.c \
|
||||
ui/fb/keysyms.c
|
||||
|
||||
ui_fb_built = ui/fb/keysyms.c
|
||||
|
||||
ui/fb/keysyms.c: $(srcdir)/keysyms.pl $(srcdir)/keysyms.dat
|
||||
@$(MKDIR_P) ui/fb
|
||||
$(AM_V_GEN)$(PERL) -I$(srcdir)/perl $(srcdir)/keysyms.pl fb $(srcdir)/keysyms.dat > $@.tmp && mv $@.tmp $@
|
||||
@@ -1,549 +0,0 @@
|
||||
/* fbdisplay.c: Routines for dealing with the linux fbdev display
|
||||
Copyright (c) 2000-2003 Philip Kendall, Matan Ziv-Av, Darren Salt,
|
||||
Witold Filipczyk
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fb.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fbdisplay.h"
|
||||
#include "fuse.h"
|
||||
#include "display.h"
|
||||
#include "screenshot.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uidisplay.h"
|
||||
#include "settings.h"
|
||||
|
||||
/* A copy of every pixel on the screen */
|
||||
libspectrum_word
|
||||
fbdisplay_image[ 2 * DISPLAY_SCREEN_HEIGHT ][ DISPLAY_SCREEN_WIDTH ];
|
||||
ptrdiff_t fbdisplay_pitch = DISPLAY_SCREEN_WIDTH * sizeof( libspectrum_word );
|
||||
|
||||
/* The environment variable specifying which device to use */
|
||||
static const char * const DEVICE_VARIABLE = "FRAMEBUFFER";
|
||||
|
||||
/* The device we will use if device_env_variable is not specified */
|
||||
static const char * const DEFAULT_DEVICE = "/dev/fb0";
|
||||
|
||||
/* The size of a 1x1 image in units of
|
||||
DISPLAY_ASPECT WIDTH x DISPLAY_SCREEN_HEIGHT */
|
||||
int image_scale;
|
||||
|
||||
/* The height and width of a 1x1 image in pixels */
|
||||
int image_width, image_height;
|
||||
|
||||
/* Are we in a Timex display mode? */
|
||||
static int hires;
|
||||
|
||||
static void register_scalers( void );
|
||||
|
||||
/* probably 0rrrrrgggggbbbbb */
|
||||
static short rgbs[16], greys[16];
|
||||
|
||||
static int fb_fd = -1; /* The framebuffer's file descriptor */
|
||||
static libspectrum_word *gm = 0;
|
||||
|
||||
static struct fb_fix_screeninfo fixed;
|
||||
static struct fb_var_screeninfo orig_display, display;
|
||||
static int got_orig_display = 0;
|
||||
static int changed_palette = 0;
|
||||
|
||||
unsigned long fb_resolution; /* == xres << 16 | yres */
|
||||
#define FB_RES(X,Y) ((X) << 16 | (Y))
|
||||
#define FB_WIDTH (fb_resolution >> 16)
|
||||
#define IF_FB_WIDTH(X) ((fb_resolution >> 16) == (X))
|
||||
#define IF_FB_HEIGHT(Y) ((fb_resolution & 0xFFFF) == (Y))
|
||||
#define IF_FB_RES(X, Y) (fb_resolution == FB_RES ((X), (Y)))
|
||||
|
||||
typedef struct {
|
||||
int xres, yres, pixclock;
|
||||
int left_margin, right_margin, upper_margin, lower_margin;
|
||||
int hsync_len, vsync_len;
|
||||
int sync, doublescan;
|
||||
} fuse_fb_mode_t;
|
||||
|
||||
/* Test monitor is a Compaq V50, default settings
|
||||
* A = working on ATI RV100 (Radeon 7000) (2.6.x, old radeonfb)
|
||||
* M = working on Matrox MGA2064W (Millennium I) (2.6.x, matroxfb)
|
||||
* Large/tall/wide = size indication (overscan)
|
||||
* The 640x480 modes are from /etc/fb.modes and should work anywhere.
|
||||
*
|
||||
* x y clock lm rm tm bm hl vl s d
|
||||
*/
|
||||
static const fuse_fb_mode_t fb_modes_singlescan[] = {
|
||||
{ 640, 480, 32052, 96, 56, 28, 9, 40, 3, 0, 0 }, /* 640x480-72 72.114 */
|
||||
{ 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, 0 }, /* 640x480-60 59.940 std */
|
||||
{ 0 } /* end of list */
|
||||
};
|
||||
static const fuse_fb_mode_t fb_modes_doublescan[] = {
|
||||
{ 640, 240, 32052, 92, 56, 14, 4, 40, 3, 0, 1 }, /* 640x240-72 72.185 */
|
||||
{ 640, 480, 32052, 96, 56, 28, 9, 40, 3, 0, 0 }, /* 640x480-72 72.114 */
|
||||
{ 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, 0 }, /* 640x480-60 59.940 std */
|
||||
{ 320, 240, 64104, 46, 28, 14, 4, 20, 3, 3, 1 }, /* 320x240-72 72.185 M wide */
|
||||
{ 0 } /* end of list */
|
||||
};
|
||||
static const fuse_fb_mode_t fb_modes_doublescan_alt[] = {
|
||||
{ 640, 240, 39722, 36, 12, 18, 7, 96, 2, 1, 1 }, /* 640x240-60 60.133 AM large */
|
||||
{ 640, 480, 32052, 96, 56, 28, 9, 40, 3, 0, 0 }, /* 640x480-72 72.114 */
|
||||
{ 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, 0 }, /* 640x480-60 59.940 std */
|
||||
{ 320, 240, 79444, 18, 6, 18, 7, 48, 2, 1, 1 }, /* 320x240-60 60.133 AM large */
|
||||
{ 0 } /* end of list */
|
||||
};
|
||||
/* Modes not used but which may work are listed here.
|
||||
* x y clock lm rm tm bm hl vl s d
|
||||
{ 640, 480, 22272, 48, 32, 17, 22, 128, 12, 0, 0 }, ** 640x480-100 99.713
|
||||
{ 640, 480, 25057, 120, 32, 14, 25, 40, 14, 0, 0 }, ** 640x480-90 89.995
|
||||
{ 640, 480, 31747, 120, 16, 16, 1, 64, 3, 0, 0 }, ** 640x480-75 74.998
|
||||
{ 320, 240, 80000, 40, 28, 9, 2, 20, 3, 0, 1 }, ** 320x240-60 60.310 M tall
|
||||
{ 320, 240, 55555, 52, 16, 12, 0, 28, 2, 0, 1 }, ** 320x240-85 85.177 M
|
||||
*/
|
||||
|
||||
static unsigned short red16[256], green16[256], blue16[256], transp16[256];
|
||||
static struct fb_cmap orig_cmap = {0, 256, red16, green16, blue16, transp16};
|
||||
|
||||
static int fb_set_mode( void );
|
||||
|
||||
int uidisplay_init( int width, int height )
|
||||
{
|
||||
hires = ( width == 640 ? 1 : 0 );
|
||||
|
||||
image_width = width; image_height = height;
|
||||
image_scale = width / DISPLAY_ASPECT_WIDTH;
|
||||
|
||||
register_scalers();
|
||||
|
||||
display_ui_initialised = 1;
|
||||
|
||||
display_refresh_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
register_scalers( void )
|
||||
{
|
||||
scaler_register_clear();
|
||||
scaler_select_bitformat( 565 ); /* 16bit always */
|
||||
scaler_register( SCALER_NORMAL );
|
||||
}
|
||||
|
||||
static void
|
||||
linear_palette(struct fb_cmap *p_cmap)
|
||||
{
|
||||
int i;
|
||||
int rcols = 1 << display.red.length;
|
||||
int gcols = 1 << display.green.length;
|
||||
int bcols = 1 << display.blue.length;
|
||||
|
||||
for (i = 0; i < rcols; i++)
|
||||
p_cmap->red[i] = (65535 / (rcols - 1)) * i;
|
||||
|
||||
for (i = 0; i < gcols; i++)
|
||||
p_cmap->green[i] = (65535 / (gcols - 1)) * i;
|
||||
|
||||
for (i = 0; i < bcols; i++)
|
||||
p_cmap->blue[i] = (65535 / (bcols - 1)) * i;
|
||||
}
|
||||
|
||||
int fbdisplay_init(void)
|
||||
{
|
||||
int i;
|
||||
const char *dev;
|
||||
|
||||
static libspectrum_word r16[256], g16[256], b16[256];
|
||||
static struct fb_cmap fb_cmap = {
|
||||
0, 256, r16, g16, b16, NULL
|
||||
};
|
||||
|
||||
dev = getenv( DEVICE_VARIABLE );
|
||||
if( !dev || !*dev ) dev = DEFAULT_DEVICE;
|
||||
|
||||
fb_fd = open( dev, O_RDWR | O_EXCL );
|
||||
if( fb_fd == -1 ) {
|
||||
fprintf( stderr, "%s: couldn't open framebuffer device '%s'\n",
|
||||
fuse_progname, dev );
|
||||
return 1;
|
||||
}
|
||||
if( ioctl( fb_fd, FBIOGET_FSCREENINFO, &fixed ) ||
|
||||
ioctl( fb_fd, FBIOGET_VSCREENINFO, &orig_display ) ) {
|
||||
fprintf( stderr, "%s: couldn't read info from framebuffer device '%s'\n",
|
||||
fuse_progname, dev );
|
||||
return 1;
|
||||
}
|
||||
got_orig_display = 1;
|
||||
|
||||
if( fb_set_mode() ) return 1;
|
||||
|
||||
fputs( "\x1B[H\x1B[J", stdout ); /* clear tty */
|
||||
memset( gm, 0, display.xres_virtual * display.yres_virtual * 2 );
|
||||
|
||||
|
||||
display.activate = FB_ACTIVATE_NOW;
|
||||
if( ioctl( fb_fd, FBIOPUT_VSCREENINFO, &display ) ) {
|
||||
fprintf( stderr, "%s: couldn't set mode for framebuffer device '%s'\n",
|
||||
fuse_progname, dev );
|
||||
return 1;
|
||||
}
|
||||
|
||||
ioctl( fb_fd, FBIOGET_VSCREENINFO, &display);
|
||||
for( i = 0; i < 16; i++ ) {
|
||||
int v = ( i & 8 ) ? 0xff : 0xbf;
|
||||
int c;
|
||||
rgbs[i] = ( ( i & 1 ) ? (v >> (8 - display.blue.length)) << display.blue.offset : 0 )
|
||||
| ( ( i & 2 ) ? (v >> (8 - display.red.length)) << display.red.offset : 0 )
|
||||
| ( ( i & 4 ) ? (v >> (8 - display.green.length)) << display.green.offset : 0 );
|
||||
|
||||
c = (( i & 1 ) ? (v * 0.114) : 0.0) +
|
||||
(( i & 2) ? (v * 0.299) : 0.0) +
|
||||
(( i & 4) ? (v * 0.587) : 0.0) + 0.5;
|
||||
greys[i] = (c >> (8 - display.red.length) << display.red.offset)
|
||||
| (c >> (8 - display.green.length) << display.green.offset)
|
||||
| (c >> (8 - display.blue.length) << display.blue.offset);
|
||||
}
|
||||
linear_palette(&fb_cmap);
|
||||
|
||||
if (orig_display.bits_per_pixel == 8 || fixed.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
ioctl( fb_fd, FBIOGETCMAP, &orig_cmap);
|
||||
changed_palette = 1;
|
||||
}
|
||||
ioctl( fb_fd, FBIOGET_FSCREENINFO, &fixed);
|
||||
if ( fixed.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
ioctl( fb_fd, FBIOPUTCMAP, &fb_cmap );
|
||||
}
|
||||
sleep( 1 ); /* give the monitor time to sync before we start emulating */
|
||||
|
||||
fputs( "\x1B[?25l", stdout ); /* hide cursor */
|
||||
fflush( stdout );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fb_select_mode( const fuse_fb_mode_t *fb_mode )
|
||||
{
|
||||
memset (&display, 0, sizeof (struct fb_var_screeninfo));
|
||||
display.xres_virtual = display.xres = fb_mode->xres;
|
||||
display.yres_virtual = display.yres = fb_mode->yres;
|
||||
display.xoffset = display.yoffset = 0;
|
||||
display.grayscale = 0;
|
||||
display.nonstd = 0;
|
||||
display.accel_flags = 0;
|
||||
display.pixclock = fb_mode->pixclock;
|
||||
display.left_margin = fb_mode->left_margin;
|
||||
display.right_margin = fb_mode->right_margin;
|
||||
display.upper_margin = fb_mode->upper_margin;
|
||||
display.lower_margin = fb_mode->lower_margin;
|
||||
display.hsync_len = fb_mode->hsync_len;
|
||||
display.vsync_len = fb_mode->vsync_len;
|
||||
display.sync = fb_mode->sync;
|
||||
display.vmode &= ~FB_VMODE_MASK;
|
||||
if( fb_mode->doublescan ) display.vmode |= FB_VMODE_DOUBLE;
|
||||
display.vmode |= FB_VMODE_CONUPDATE;
|
||||
|
||||
display.bits_per_pixel = 16;
|
||||
display.red.length = display.green.length = display.blue.length = 5;
|
||||
|
||||
display.red.offset = 0;
|
||||
display.green.offset = 5;
|
||||
display.blue.offset = 10;
|
||||
|
||||
gm = mmap( 0, fixed.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0 );
|
||||
if( gm == (void*)-1 ) {
|
||||
fprintf (stderr, "%s: couldn't mmap for framebuffer: %s\n",
|
||||
fuse_progname, strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
display.activate = FB_ACTIVATE_TEST;
|
||||
if( ioctl( fb_fd, FBIOPUT_VSCREENINFO, &display ) ) {
|
||||
munmap( gm, fixed.smem_len );
|
||||
return 1;
|
||||
}
|
||||
|
||||
fb_resolution = FB_RES( display.xres, display.yres );
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static int
|
||||
fb_set_mode( void )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
const fuse_fb_mode_t *fb_modes =
|
||||
(settings_current.doublescan_mode == 0) ? fb_modes_singlescan :
|
||||
(settings_current.doublescan_mode == 1) ? fb_modes_doublescan :
|
||||
fb_modes_doublescan_alt;
|
||||
|
||||
/* First, try to use our preferred mode */
|
||||
for( i=0; fb_modes[i].xres; i++ )
|
||||
if( fb_modes[i].xres == settings_current.fb_mode )
|
||||
if( !fb_select_mode( fb_modes + i ) )
|
||||
return 0;
|
||||
|
||||
/* If that failed, try to use the first available mode */
|
||||
for( i=0; fb_modes[i].xres; i++ )
|
||||
if( !fb_select_mode( fb_modes + i ) )
|
||||
return 0;
|
||||
|
||||
/* If that failed, we can't continue :-( */
|
||||
fprintf( stderr, "%s: couldn't find a framebuffer mode to start in\n",
|
||||
fuse_progname );
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_hotswap_gfx_mode( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_frame_end( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_area( int x, int start, int width, int height)
|
||||
{
|
||||
int y;
|
||||
const short *colours = settings_current.bw_tv ? greys : rgbs;
|
||||
|
||||
switch( fb_resolution ) {
|
||||
case FB_RES( 640, 480 ):
|
||||
for( y = start; y < start + height; y++ )
|
||||
{
|
||||
int i;
|
||||
libspectrum_word *point;
|
||||
|
||||
if( hires ) {
|
||||
|
||||
for( i = 0, point = gm + y * display.xres_virtual + x;
|
||||
i < width;
|
||||
i++, point++ )
|
||||
*point = colours[fbdisplay_image[y][x+i]];
|
||||
|
||||
} else {
|
||||
|
||||
for( i = 0, point = gm + 2 * y * display.xres_virtual + x * 2;
|
||||
i < width;
|
||||
i++, point += 2 )
|
||||
* point = *( point + display.xres_virtual ) =
|
||||
*( point + 1 ) = *( point + 1 + display.xres_virtual ) =
|
||||
colours[fbdisplay_image[y][x+i]];
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FB_RES( 640, 240 ):
|
||||
if( hires ) { start >>= 1; height >>= 1; }
|
||||
for( y = start; y < start + height; y++ )
|
||||
{
|
||||
int i;
|
||||
libspectrum_word *point;
|
||||
|
||||
if( hires ) {
|
||||
|
||||
for ( i = 0, point = gm + y * display.xres_virtual + x;
|
||||
i < width;
|
||||
i++, point++ )
|
||||
*point = colours[fbdisplay_image[y*2][x+i]];
|
||||
|
||||
} else {
|
||||
|
||||
for( i = 0, point = gm + y * display.xres_virtual + x * 2;
|
||||
i < width;
|
||||
i++, point+=2 )
|
||||
*point = *(point+1) = colours[fbdisplay_image[y][x+i]];
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FB_RES( 320, 240 ):
|
||||
if( hires ) { start >>= 1; height >>= 1; x >>= 1; width >>= 1; }
|
||||
for( y = start; y < start + height; y++ )
|
||||
{
|
||||
int i;
|
||||
libspectrum_word *point;
|
||||
|
||||
if( hires ) {
|
||||
|
||||
/* Drop every second pixel */
|
||||
for ( i = 0, point = gm + y * display.xres_virtual + x;
|
||||
i < width;
|
||||
i++, point++ )
|
||||
*point = colours[fbdisplay_image[y*2][(x+i)*2]];
|
||||
|
||||
} else {
|
||||
|
||||
for( i = 0, point = gm + y * display.xres_virtual + x;
|
||||
i < width;
|
||||
i++, point++ )
|
||||
*point = colours[fbdisplay_image[y][x+i]];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:; /* Shut gcc up */
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_end( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fbdisplay_end( void )
|
||||
{
|
||||
if( fb_fd != -1 ) {
|
||||
if( got_orig_display ) {
|
||||
ioctl( fb_fd, FBIOPUT_VSCREENINFO, &orig_display );
|
||||
if (changed_palette) {
|
||||
ioctl( fb_fd, FBIOPUTCMAP, &orig_cmap);
|
||||
changed_palette = 0;
|
||||
}
|
||||
}
|
||||
close( fb_fd );
|
||||
fb_fd = -1;
|
||||
fputs( "\x1B[H\x1B[J\x1B[?25h", stdout ); /* clear screen, show cursor */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set one pixel in the display */
|
||||
void
|
||||
uidisplay_putpixel( int x, int y, int colour )
|
||||
{
|
||||
if( machine_current->timex ) {
|
||||
x <<= 1; y <<= 1;
|
||||
fbdisplay_image[y ][x ] = colour;
|
||||
fbdisplay_image[y ][x+1] = colour;
|
||||
fbdisplay_image[y+1][x ] = colour;
|
||||
fbdisplay_image[y+1][x+1] = colour;
|
||||
} else {
|
||||
fbdisplay_image[y][x] = colour;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
x <<= 3;
|
||||
|
||||
if( machine_current->timex ) {
|
||||
int i;
|
||||
|
||||
x <<= 1; y <<= 1;
|
||||
for( i=0; i<2; i++,y++ ) {
|
||||
fbdisplay_image[y][x+ 0] = ( data & 0x80 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 1] = ( data & 0x80 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 2] = ( data & 0x40 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 3] = ( data & 0x40 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 4] = ( data & 0x20 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 5] = ( data & 0x20 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 6] = ( data & 0x10 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 7] = ( data & 0x10 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 8] = ( data & 0x08 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 9] = ( data & 0x08 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+10] = ( data & 0x04 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+11] = ( data & 0x04 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+12] = ( data & 0x02 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+13] = ( data & 0x02 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+14] = ( data & 0x01 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+15] = ( data & 0x01 ) ? ink : paper;
|
||||
}
|
||||
} else {
|
||||
fbdisplay_image[y][x+ 0] = ( data & 0x80 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 1] = ( data & 0x40 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 2] = ( data & 0x20 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 3] = ( data & 0x10 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 4] = ( data & 0x08 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 5] = ( data & 0x04 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 6] = ( data & 0x02 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 7] = ( data & 0x01 ) ? ink : paper;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
int i;
|
||||
x <<= 4; y <<= 1;
|
||||
|
||||
for( i=0; i<2; i++,y++ ) {
|
||||
fbdisplay_image[y][x+ 0] = ( data & 0x8000 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 1] = ( data & 0x4000 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 2] = ( data & 0x2000 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 3] = ( data & 0x1000 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 4] = ( data & 0x0800 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 5] = ( data & 0x0400 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 6] = ( data & 0x0200 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 7] = ( data & 0x0100 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 8] = ( data & 0x0080 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+ 9] = ( data & 0x0040 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+10] = ( data & 0x0020 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+11] = ( data & 0x0010 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+12] = ( data & 0x0008 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+13] = ( data & 0x0004 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+14] = ( data & 0x0002 ) ? ink : paper;
|
||||
fbdisplay_image[y][x+15] = ( data & 0x0001 ) ? ink : paper;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_frame_save( void )
|
||||
{
|
||||
/* FIXME: Save current framebuffer state as the widget UI wants to scribble
|
||||
in here */
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_frame_restore( void )
|
||||
{
|
||||
/* FIXME: Restore saved framebuffer state as the widget UI wants to draw a
|
||||
new menu */
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/* fbdisplay.h: Routines for dealing with the framebuffer display
|
||||
Copyright (c) 2003 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_FBDISPLAY_H
|
||||
#define FUSE_FBDISPLAY_H
|
||||
|
||||
int fbdisplay_init( void );
|
||||
int fbdisplay_end( void );
|
||||
|
||||
#endif /* #ifndef FUSE_FBDISPLAY_H */
|
||||
@@ -1,29 +0,0 @@
|
||||
/* fbjoystick.c: Joystick emulation
|
||||
Copyright (c) 2003 Darren Salt
|
||||
|
||||
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: [email protected]
|
||||
|
||||
Darren: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "peripherals/joystick.h"
|
||||
#include "../uijoystick.c"
|
||||
@@ -1,147 +0,0 @@
|
||||
/* fbkeyboard.c: routines for dealing with the linux fbdev display
|
||||
Copyright (c) 2000-2004 Philip Kendall, Matan Ziv-Av, Darren Salt
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <linux/kd.h>
|
||||
#include <termios.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "fuse.h"
|
||||
#include "keyboard.h"
|
||||
#include "machine.h"
|
||||
#include "settings.h"
|
||||
#include "snapshot.h"
|
||||
#include "spectrum.h"
|
||||
#include "tape.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "input.h"
|
||||
|
||||
static struct termios old_ts;
|
||||
static int old_kbmode = K_XLATE;
|
||||
static int got_old_ts = 0;
|
||||
|
||||
int fbkeyboard_init(void)
|
||||
{
|
||||
struct termios ts;
|
||||
struct stat st;
|
||||
int i = 1;
|
||||
|
||||
/* First, set up the keyboard */
|
||||
if( fstat( STDIN_FILENO, &st ) ) {
|
||||
fprintf( stderr, "%s: couldn't stat stdin: %s\n", fuse_progname,
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check for character special, major 4, minor 0..63 */
|
||||
if( !isatty(STDIN_FILENO) || !S_ISCHR(st.st_mode) ||
|
||||
( st.st_rdev & ~63 ) != 0x0400 ) {
|
||||
fprintf( stderr, "%s: stdin isn't a local tty\n", fuse_progname );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tcgetattr( STDIN_FILENO, &old_ts );
|
||||
ioctl( STDIN_FILENO, KDGKBMODE, &old_kbmode );
|
||||
got_old_ts = 1;
|
||||
|
||||
/* We need non-blocking semi-cooked keyboard input */
|
||||
if( ioctl( STDIN_FILENO, FIONBIO, &i ) ) {
|
||||
fprintf( stderr, "%s: can't set stdin nonblocking: %s\n", fuse_progname,
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
if( ioctl( STDIN_FILENO, KDSKBMODE, K_MEDIUMRAW ) ) {
|
||||
fprintf( stderr, "%s: can't set keyboard into medium-raw mode: %s\n",
|
||||
fuse_progname, strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Add in a bit of voodoo... */
|
||||
ts = old_ts;
|
||||
ts.c_cc[VTIME] = 0;
|
||||
ts.c_cc[VMIN] = 1;
|
||||
ts.c_lflag &= ~(ICANON | ECHO | ISIG);
|
||||
ts.c_iflag = 0;
|
||||
tcsetattr( STDIN_FILENO, TCSAFLUSH, &ts );
|
||||
tcsetpgrp( STDIN_FILENO, getpgrp() );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fbkeyboard_end(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
ioctl( STDIN_FILENO, FIONBIO, &i );
|
||||
if( got_old_ts ) {
|
||||
tcsetattr( STDIN_FILENO, TCSAFLUSH, &old_ts );
|
||||
ioctl( STDIN_FILENO, KDSKBMODE, old_kbmode );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
keyboard_update( void )
|
||||
{
|
||||
unsigned char keybuf[64];
|
||||
static int ignore = 0;
|
||||
|
||||
while( 1 ) {
|
||||
ssize_t available, i;
|
||||
|
||||
available = read( STDIN_FILENO, &keybuf, sizeof( keybuf ) );
|
||||
if( available <= 0 ) return;
|
||||
|
||||
for( i = 0; i < available; i++ )
|
||||
if( ignore ) {
|
||||
ignore--;
|
||||
} else if( ( keybuf[i] & 0x7f ) == 0 ) {
|
||||
ignore = 2; /* ignore extended keysyms */
|
||||
} else {
|
||||
input_key fuse_keysym;
|
||||
input_event_t fuse_event;
|
||||
|
||||
fuse_keysym = keysyms_remap( keybuf[i] & 0x7f );
|
||||
|
||||
if( fuse_keysym == INPUT_KEY_NONE ) continue;
|
||||
|
||||
fuse_event.type = ( keybuf[i] & 0x80 ) ?
|
||||
INPUT_EVENT_KEYRELEASE :
|
||||
INPUT_EVENT_KEYPRESS;
|
||||
fuse_event.types.key.native_key = fuse_keysym;
|
||||
fuse_event.types.key.spectrum_key = fuse_keysym;
|
||||
|
||||
input_event( &fuse_event );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* fbkeyboard.h: routines for dealing with the buttons interface
|
||||
Copyright (c) 2000-2001 Philip Kendall, Matan Ziv-Av
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_FBKEYBOARD_H
|
||||
#define FUSE_FBKEYBOARD_H
|
||||
|
||||
int fbkeyboard_init(void);
|
||||
|
||||
int fbkeyboard_keypress(int keysym);
|
||||
void fbkeyboard_keyrelease(int keysym);
|
||||
|
||||
int fbkeyboard_end(void);
|
||||
|
||||
void keyboard_update(void);
|
||||
|
||||
#endif /* #ifndef FUSE_FBKEYBOARD_H */
|
||||
@@ -1,264 +0,0 @@
|
||||
/* fbmouse.c: Linux mouse handling code (requires kernel input layer)
|
||||
Copyright (c) 2004 Darren Salt
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fbmouse.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef HAVE_GPM_H
|
||||
#include <gpm.h>
|
||||
static void *libgpm = NULL;
|
||||
static typeof (Gpm_GetEvent) *gpm_getevent = 0;
|
||||
static typeof (gpm_fd) gpmfd = 0;
|
||||
static const typeof (gpm_flag) zero = 0;
|
||||
static typeof (&zero) gpmflag = &zero;
|
||||
#endif
|
||||
|
||||
static int mouse_fd = -1;
|
||||
static enum { PS2, ImPS2, ExPS2 } mouse_mode = PS2;
|
||||
static int packet_size = 3;
|
||||
|
||||
static int try_open( const char * );
|
||||
static int try_mouse_mode( unsigned char, unsigned char );
|
||||
|
||||
int
|
||||
fbmouse_init(void)
|
||||
{
|
||||
int i;
|
||||
#ifdef HAVE_GPM_H
|
||||
static Gpm_Connect conn = { ~0/*GPM_MOVE | GPM_DOWN | GPM_UP*/, 0, 0, ~0 };
|
||||
|
||||
libgpm = dlopen( "libgpm.so", RTLD_LAZY );
|
||||
|
||||
if( libgpm ) {
|
||||
typeof (Gpm_Open) *gpm_open = dlsym( libgpm, "Gpm_Open" );
|
||||
i = 1;
|
||||
if( gpm_open( &conn, 0 ) != -1 ) {
|
||||
gpmfd = *( typeof( &gpm_fd ) ) dlsym( libgpm, "gpm_fd" );
|
||||
gpmflag = ( typeof( &gpm_flag ) ) dlsym( libgpm, "gpm_flag" );
|
||||
gpm_getevent = dlsym( libgpm, "Gpm_GetEvent" );
|
||||
ui_mouse_present = 1;
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
fprintf( stderr, "%s: couldn't init libgpm: %s\n", fuse_progname,
|
||||
dlerror() );
|
||||
#endif
|
||||
|
||||
/* No libgpm support, no libgpm or gpm is not running - use our own code */
|
||||
if( try_open ("/dev/input/mice") &&
|
||||
try_open ("/dev/mouse") &&
|
||||
try_open ("/dev/psaux") ) {
|
||||
if( errno == ENOENT || errno == ENODEV || errno == EACCES ) {
|
||||
fprintf( stderr, "%s: warning: no mouse, or not accessible\n",
|
||||
fuse_progname );
|
||||
return 0;
|
||||
}
|
||||
fprintf( stderr, "%s: couldn't open mouse device: %s\n",
|
||||
fuse_progname, strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Try to select plain PS/2 (just in case it's not already selected).
|
||||
* Ignore any error.
|
||||
*/
|
||||
i = -1; /* account for big-endian */
|
||||
write( mouse_fd, &i, 1 );
|
||||
|
||||
/* Try to set the mouse mode. We prefer ExPS/2 but will accept ImPS/2.
|
||||
* The main reason is so that the side buttons don't appear as duplicate
|
||||
* buttons 2 and 3, and so that the wheel is available should it be wanted
|
||||
* somewhere.
|
||||
*/
|
||||
if( !try_mouse_mode( 100, 3 ) ) {
|
||||
packet_size = 4;
|
||||
mouse_mode = try_mouse_mode( 200, 4 ) ? ImPS2 : ExPS2;
|
||||
}
|
||||
|
||||
/* *Now* we want non-blocking I/O... */
|
||||
i = 1;
|
||||
if( ioctl( mouse_fd, FIONBIO, &i ) )
|
||||
return fbmouse_end(); /* couldn't get it */
|
||||
|
||||
ui_mouse_present = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fbmouse_end(void)
|
||||
{
|
||||
#ifdef HAVE_GPM_H
|
||||
if( *gpmflag ) {
|
||||
typeof (Gpm_Close) *gpm_close = dlsym( libgpm, "Gpm_Close" );
|
||||
gpm_close();
|
||||
}
|
||||
#endif
|
||||
if( mouse_fd != -1 ) {
|
||||
close( mouse_fd );
|
||||
mouse_fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GPM_H
|
||||
static void mouse_update_gpm( void );
|
||||
static void mouse_update_ps2( void );
|
||||
|
||||
void
|
||||
mouse_update( void )
|
||||
{
|
||||
if( *gpmflag )
|
||||
mouse_update_gpm();
|
||||
else
|
||||
mouse_update_ps2();
|
||||
}
|
||||
|
||||
static void
|
||||
mouse_update_gpm( void )
|
||||
{
|
||||
Gpm_Event event;
|
||||
int db;
|
||||
static int oldbuttons = 0;
|
||||
|
||||
if( gpmfd < 0 ) {
|
||||
static int t = 0;
|
||||
if (!t) fprintf (stderr, "gpm not there?\n");
|
||||
t = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct pollfd ufd = { gpmfd, POLLIN | POLLPRI };
|
||||
if( poll( &ufd, 1, 0 ) < 1 ) break;
|
||||
|
||||
gpm_getevent( &event );
|
||||
|
||||
if( !ui_mouse_grabbed ) continue;
|
||||
|
||||
if( event.dx | event.dy ) ui_mouse_motion( event.dx, event.dy );
|
||||
|
||||
db = event.buttons ^ oldbuttons;
|
||||
if( db ) {
|
||||
oldbuttons = event.buttons;
|
||||
if( db & GPM_B_LEFT ) ui_mouse_button( 1, event.buttons & GPM_B_LEFT );
|
||||
if( db & GPM_B_RIGHT ) ui_mouse_button( 3, event.buttons & GPM_B_RIGHT );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Simple PS/2 mouse handler.
|
||||
* Should cope with any similar 3- and 4-byte protocol which is a superset of
|
||||
* plain PS/2: e.g. ImPS/2, ExPS/2 but not PS2++.
|
||||
*/
|
||||
#ifdef HAVE_GPM_H
|
||||
static void
|
||||
mouse_update_ps2( void )
|
||||
#else
|
||||
void
|
||||
mouse_update( void )
|
||||
#endif
|
||||
{
|
||||
static int btn_state = 0;
|
||||
unsigned char mousebuf[60]; /* max. events: 20 (PS/2) or 15 (other) */
|
||||
ssize_t xoff = 0, yoff = 0;
|
||||
int btn_changed = 0, btn_new = btn_state, btn_mod = btn_state;
|
||||
|
||||
if( mouse_fd == -1 ) return;
|
||||
|
||||
while( 1 ) {
|
||||
ssize_t available;
|
||||
const unsigned char *i;
|
||||
|
||||
available = read( mouse_fd, &mousebuf, sizeof( mousebuf ) );
|
||||
if( available <= 0 ) break;
|
||||
|
||||
if( !ui_mouse_grabbed ) continue;
|
||||
|
||||
for( i = mousebuf; i < mousebuf + available; i += packet_size ) {
|
||||
btn_changed |= btn_mod ^= btn_new = i[0] & 7;
|
||||
btn_changed |= btn_mod ^= btn_new = i[0] & 7;
|
||||
xoff += i[1]; if( i[0] & 16 ) xoff -= 256;
|
||||
yoff += i[2]; if( i[0] & 32 ) yoff -= 256;
|
||||
}
|
||||
}
|
||||
|
||||
btn_state = btn_new;
|
||||
if( btn_changed & 1 ) ui_mouse_button( 1, btn_new & 1 );
|
||||
if( btn_changed & 2 ) ui_mouse_button( 3, btn_new & 2 );
|
||||
|
||||
if( xoff || yoff ) ui_mouse_motion( xoff, -yoff );
|
||||
}
|
||||
|
||||
/* Try to open the specified device. Return 0 on success. */
|
||||
static int
|
||||
try_open( const char *dev )
|
||||
{
|
||||
mouse_fd = open( dev, O_RDWR );
|
||||
if( mouse_fd == -1 && errno == EACCES )
|
||||
mouse_fd = open( dev, O_RDONLY );
|
||||
return mouse_fd == -1;
|
||||
}
|
||||
|
||||
/* Try to set the mouse to the requested mode (100 = ImPS/2, 200 = ExPS/2).
|
||||
* Return 0 on success.
|
||||
*/
|
||||
static int
|
||||
try_mouse_mode( unsigned char mode, unsigned char response )
|
||||
{
|
||||
struct pollfd ufd = { mouse_fd, POLLIN | POLLPRI };
|
||||
int ret = 0;
|
||||
unsigned char buf[] = { 0xF3, 200, 0xF3, mode, 0xF3, 80, 0xF2 };
|
||||
if( write( mouse_fd, buf, sizeof( buf ) ) < sizeof( buf ) )
|
||||
return 1;
|
||||
while( poll( &ufd, 1, 10 ) == 1)
|
||||
ret = read( mouse_fd, buf, sizeof( buf ) );
|
||||
return ret < 1 || buf[ret - 1] != response;
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_grab( int startup GCC_UNUSED )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_release( int suspend )
|
||||
{
|
||||
return !suspend;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/* fbmouse.h: Linux mouse handling code (requires kernel input layer)
|
||||
Copyright (c) 2004 Darren Salt
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_FBMOUSE_H
|
||||
#define FUSE_FBMOUSE_H
|
||||
|
||||
int fbmouse_init(void);
|
||||
int fbmouse_end(void);
|
||||
void mouse_update(void);
|
||||
|
||||
#endif /* #ifndef FUSE_FBMOUSE_H */
|
||||
@@ -1,114 +0,0 @@
|
||||
/* fbui.c: Routines for dealing with the linux fbdev user interface
|
||||
Copyright (c) 2000-2004 Philip Kendall, Matan Ziv-Av, Witold Filipczyk
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fbdisplay.h"
|
||||
#include "fbkeyboard.h"
|
||||
#include "fbmouse.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uidisplay.h"
|
||||
|
||||
static void end_handler( int signo );
|
||||
static void fb_end( void );
|
||||
|
||||
int
|
||||
ui_init( int *argc, char ***argv )
|
||||
{
|
||||
struct sigaction handler;
|
||||
int error;
|
||||
|
||||
if( ui_widget_init() ) return 1;
|
||||
|
||||
error = atexit( fb_end );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "ui_init: couldn't set atexit function" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
handler.sa_handler = end_handler;
|
||||
|
||||
error = sigaction( SIGTERM, &handler, NULL );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "ui_init: couldn't set SIGTERM handler: %s",
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
error = sigaction( SIGHUP, &handler, NULL );
|
||||
if( error ) {
|
||||
ui_error( UI_ERROR_ERROR, "ui_init: couldn't set SIGHUP handler: %s",
|
||||
strerror( errno ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
error = fbkeyboard_init();
|
||||
if( error ) return error;
|
||||
|
||||
error = fbmouse_init();
|
||||
if( error ) return error;
|
||||
|
||||
error = fbdisplay_init();
|
||||
if( error ) return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ui_event( void )
|
||||
{
|
||||
keyboard_update();
|
||||
mouse_update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ui_end( void )
|
||||
{
|
||||
/* Cleanup handled by atexit function */
|
||||
int error;
|
||||
|
||||
error = fbkeyboard_end(); if( error ) return error;
|
||||
error = fbdisplay_end(); if( error ) return error;
|
||||
|
||||
ui_widget_end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
end_handler( int signo )
|
||||
{
|
||||
fb_end();
|
||||
}
|
||||
|
||||
static void
|
||||
fb_end( void )
|
||||
{
|
||||
fbkeyboard_end();
|
||||
uidisplay_end();
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
## Copyright (c) 2001-2004 Philip Kendall
|
||||
## 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: [email protected]
|
||||
|
||||
if UI_GTK
|
||||
|
||||
fuse_SOURCES += $(ui_gtk_files)
|
||||
|
||||
BUILT_SOURCES += $(ui_gtk_built)
|
||||
|
||||
pkgdata_DATA += ui/gtk/menu_data.ui
|
||||
|
||||
endif
|
||||
|
||||
|
||||
CLEANFILES += $(ui_gtk_built)
|
||||
|
||||
ui_gtk_files = \
|
||||
ui/gtk/binary.c \
|
||||
ui/gtk/browse.c \
|
||||
ui/gtk/confirm.c \
|
||||
ui/gtk/debugger.c \
|
||||
ui/gtk/fileselector.c \
|
||||
ui/gtk/gtkcompat.h \
|
||||
ui/gtk/gtkdisplay.c \
|
||||
ui/gtk/gtkinternals.h \
|
||||
ui/gtk/gtkjoystick.c \
|
||||
ui/gtk/gtkkeyboard.c \
|
||||
ui/gtk/gtkmouse.c \
|
||||
ui/gtk/gtkui.c \
|
||||
ui/gtk/keysyms.c \
|
||||
ui/gtk/menu_data.c \
|
||||
ui/gtk/options.c \
|
||||
ui/gtk/picture.c \
|
||||
ui/gtk/pixmaps.c \
|
||||
ui/gtk/pokefinder.c \
|
||||
ui/gtk/pokemem.c \
|
||||
ui/gtk/rollback.c \
|
||||
ui/gtk/roms.c \
|
||||
ui/gtk/statusbar.c \
|
||||
ui/gtk/stock.c
|
||||
|
||||
if HAVE_GTK2
|
||||
|
||||
ui_gtk_files += ui/gtk/gtkcompat.c \
|
||||
ui/gtk/memory_gtk2.c
|
||||
|
||||
else
|
||||
|
||||
ui_gtk_files += ui/gtk/memory.c
|
||||
|
||||
endif
|
||||
|
||||
ui_gtk_built = \
|
||||
ui/gtk/keysyms.c \
|
||||
ui/gtk/menu_data.c \
|
||||
ui/gtk/menu_data.ui \
|
||||
ui/gtk/options.c \
|
||||
ui/gtk/options_internals.h
|
||||
|
||||
EXTRA_DIST += \
|
||||
ui/gtk/options.pl \
|
||||
ui/gtk/options-header.pl
|
||||
|
||||
ui/gtk/keysyms.c: $(srcdir)/keysyms.pl $(srcdir)/keysyms.dat
|
||||
@$(MKDIR_P) ui/gtk
|
||||
$(AM_V_GEN)$(PERL) -I$(srcdir)/perl $(srcdir)/keysyms.pl gtk $(srcdir)/keysyms.dat > $@.tmp && mv $@.tmp $@
|
||||
|
||||
ui/gtk/menu_data.c: $(srcdir)/perl/cpp-perl.pl $(srcdir)/menu_data.pl $(srcdir)/menu_data.dat config.h
|
||||
@$(MKDIR_P) ui/gtk
|
||||
$(AM_V_GEN)$(PERL) $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/menu_data.dat | $(PERL) -I$(srcdir)/perl $(srcdir)/menu_data.pl gtk c > $@.tmp && mv $@.tmp $@
|
||||
|
||||
ui/gtk/menu_data.ui: $(srcdir)/perl/cpp-perl.pl $(srcdir)/menu_data.pl $(srcdir)/menu_data.dat config.h
|
||||
@$(MKDIR_P) ui/gtk
|
||||
$(AM_V_GEN)$(PERL) $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/menu_data.dat | $(PERL) -I$(srcdir)/perl $(srcdir)/menu_data.pl gtk ui > $@.tmp && mv $@.tmp $@
|
||||
|
||||
ui/gtk/options.c: $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/gtk/options.pl $(srcdir)/ui/options.dat $(srcdir)/perl/Fuse.pm $(srcdir)/perl/Fuse/Dialog.pm
|
||||
@$(MKDIR_P) ui/gtk
|
||||
$(AM_V_GEN)$(PERL) $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/options.dat | $(PERL) -I$(srcdir)/perl $(srcdir)/ui/gtk/options.pl - > $@.tmp && mv $@.tmp $@
|
||||
|
||||
ui/gtk/options_internals.h: $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/gtk/options-header.pl $(srcdir)/ui/options.dat $(srcdir)/perl/Fuse.pm $(srcdir)/perl/Fuse/Dialog.pm
|
||||
@$(MKDIR_P) ui/gtk
|
||||
$(AM_V_GEN)$(PERL) $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/options.dat | $(PERL) -I$(srcdir)/perl $(srcdir)/ui/gtk/options-header.pl - > $@.tmp && mv $@.tmp $@
|
||||
@@ -1,447 +0,0 @@
|
||||
/* binary.c: GTK routines to load/save chunks of binary data
|
||||
Copyright (c) 2003-2013 Philip Kendall
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libspectrum.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "memory_pages.h"
|
||||
#include "menu.h"
|
||||
#include "ui/ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct binary_info {
|
||||
|
||||
char *filename;
|
||||
utils_file file;
|
||||
|
||||
GCallback activate_data;
|
||||
GCallback change_filename;
|
||||
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *filename_widget, *start_widget, *length_widget;
|
||||
|
||||
long start;
|
||||
long length;
|
||||
};
|
||||
|
||||
static struct binary_info save_info = { 0 };
|
||||
static struct binary_info load_info = { 0 };
|
||||
|
||||
static void change_load_filename( GtkButton *button, gpointer user_data );
|
||||
static void load_data( GtkEntry *entry, gpointer user_data );
|
||||
|
||||
static void change_save_filename( GtkButton *button, gpointer user_data );
|
||||
static void save_data( GtkEntry *entry, gpointer user_data );
|
||||
|
||||
|
||||
/*
|
||||
* This is the main dialog for the load/save binary data interface. It's
|
||||
* shared between the 2 functions.
|
||||
*
|
||||
* It's passed the binary_info structure, which contains the start address
|
||||
* and length values of the block to either load or save. These are populated
|
||||
* into the GUI.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BINARY_DIALOG_MODE_LOAD,
|
||||
BINARY_DIALOG_MODE_SAVE
|
||||
} BINARY_DIALOG_MODE;
|
||||
|
||||
static void
|
||||
create_shared_binary_dialog( struct binary_info *info, const char *title,
|
||||
BINARY_DIALOG_MODE mode )
|
||||
{
|
||||
GtkWidget *table, *button, *content_area;
|
||||
GtkWidget *label_filename, *label_start, *label_length;
|
||||
|
||||
char buffer[80];
|
||||
|
||||
info->dialog = gtkstock_dialog_new( title, NULL );
|
||||
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( info->dialog ) );
|
||||
|
||||
label_filename = gtk_label_new( "Filename" );
|
||||
|
||||
info->filename_widget = gtk_label_new( info->filename );
|
||||
#if GTK_CHECK_VERSION( 3, 16, 0 )
|
||||
gtk_label_set_xalign( GTK_LABEL(info->filename_widget), 0.0);
|
||||
#else
|
||||
gtk_misc_set_alignment( GTK_MISC(info->filename_widget), 0.0, 0.5 );
|
||||
#endif
|
||||
|
||||
button = gtk_button_new_with_label( "Browse..." );
|
||||
g_signal_connect( G_OBJECT( button ), "clicked", info->change_filename,
|
||||
info );
|
||||
|
||||
label_start = gtk_label_new( "Start" );
|
||||
|
||||
info->start_widget = gtk_entry_new();
|
||||
snprintf( buffer, 80, "%lu", info->start );
|
||||
gtk_entry_set_text( GTK_ENTRY( info->start_widget ), buffer );
|
||||
g_signal_connect( G_OBJECT( info->start_widget ), "activate",
|
||||
info->activate_data, info );
|
||||
|
||||
label_length = gtk_label_new( "Length" );
|
||||
|
||||
info->length_widget = gtk_entry_new();
|
||||
if( mode == BINARY_DIALOG_MODE_LOAD ) {
|
||||
snprintf( buffer, 80, "%lu", (unsigned long)info->file.length );
|
||||
} else {
|
||||
snprintf( buffer, 80, "%lu", info->length );
|
||||
}
|
||||
gtk_entry_set_text( GTK_ENTRY( info->length_widget ), buffer );
|
||||
|
||||
g_signal_connect( G_OBJECT( info->length_widget ), "activate",
|
||||
info->activate_data, info );
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
table = gtk_grid_new();
|
||||
gtk_grid_set_column_homogeneous( GTK_GRID( table ), FALSE );
|
||||
gtk_grid_set_column_spacing( GTK_GRID( table ), 6 );
|
||||
gtk_grid_set_row_spacing( GTK_GRID( table ), 6 );
|
||||
gtk_container_set_border_width( GTK_CONTAINER( table ), 6 );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), table );
|
||||
|
||||
gtk_grid_attach( GTK_GRID( table ), label_filename, 0, 0, 1, 1 );
|
||||
gtk_widget_set_hexpand( info->filename_widget, TRUE );
|
||||
gtk_grid_attach( GTK_GRID( table ), info->filename_widget,
|
||||
1, 0, 1, 1 );
|
||||
gtk_grid_attach( GTK_GRID( table ), button, 2, 0, 1, 1 );
|
||||
gtk_grid_attach( GTK_GRID( table ), label_start, 0, 1, 1, 1 );
|
||||
gtk_grid_attach( GTK_GRID( table ), info->start_widget, 1, 1, 2, 1 );
|
||||
gtk_grid_attach( GTK_GRID( table ), label_length, 0, 2, 1, 1 );
|
||||
gtk_grid_attach( GTK_GRID( table ), info->length_widget, 1, 2, 2, 1 );
|
||||
|
||||
#else /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
table = gtk_table_new( 3, 3, FALSE );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), table, TRUE, TRUE, 0 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), label_filename, 0, 1, 0, 1,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), info->filename_widget, 1, 2, 0, 1,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), button, 2, 3, 0, 1,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), label_start, 0, 1, 1, 2, 0, 0, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), info->start_widget, 1, 3, 1, 2,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), label_length, 0, 1, 2, 3,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
gtk_table_attach( GTK_TABLE( table ), info->length_widget, 1, 3, 2, 3,
|
||||
GTK_FILL, GTK_FILL, 3, 3 );
|
||||
|
||||
#endif
|
||||
|
||||
GtkAccelGroup *accel_group;
|
||||
accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group( GTK_WINDOW( info->dialog ), accel_group );
|
||||
|
||||
/* Command buttons */
|
||||
gtkstock_create_ok_cancel( info->dialog, NULL, info->activate_data, info,
|
||||
DEFAULT_DESTROY, DEFAULT_DESTROY );
|
||||
|
||||
gtk_widget_grab_focus( info->start_widget );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is the entry point for the load binary data command. It's called from
|
||||
* the GUI menu. It pops the file name selector dialog if the user hasn't
|
||||
* already specified a filename, then pops the dialog requesting start address
|
||||
* and length.
|
||||
*/
|
||||
void
|
||||
menu_file_loadbinarydata( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
int error;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
/*
|
||||
* If the filename is blank it's not been selected previously.
|
||||
* Pop up the file selector.
|
||||
*/
|
||||
if( !load_info.filename )
|
||||
{
|
||||
load_info.filename = ui_get_open_filename( "Fuse - Load Binary Data" );
|
||||
if( !load_info.filename )
|
||||
{
|
||||
fuse_emulation_unpause();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error = utils_read_file( load_info.filename, &load_info.file );
|
||||
if( error )
|
||||
{
|
||||
fuse_emulation_unpause();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Information display */
|
||||
load_info.activate_data = G_CALLBACK( load_data );
|
||||
load_info.change_filename = G_CALLBACK( change_load_filename );
|
||||
|
||||
create_shared_binary_dialog( &load_info, "Fuse - Load Binary Data",
|
||||
BINARY_DIALOG_MODE_LOAD );
|
||||
|
||||
/* Process the dialog */
|
||||
gtk_widget_show_all( load_info.dialog );
|
||||
gtk_main();
|
||||
|
||||
utils_close_file( &load_info.file );
|
||||
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Change the name of the file the user is going to load into the Spectrum's
|
||||
* memory. This pops a file selector dialog and stores the name of the file
|
||||
* chosen (releasing the memory of the previously chosen name).
|
||||
*/
|
||||
static void
|
||||
change_load_filename( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
struct binary_info *info = user_data;
|
||||
|
||||
char *new_filename;
|
||||
utils_file new_file;
|
||||
|
||||
char buffer[80];
|
||||
int error;
|
||||
|
||||
new_filename = ui_get_open_filename( "Fuse - Load Binary Data" );
|
||||
if( !new_filename ) return;
|
||||
|
||||
error = utils_read_file( new_filename, &new_file );
|
||||
if( error ) { free( new_filename ); return; }
|
||||
|
||||
/* Remove the data for the old file */
|
||||
utils_close_file( &info->file );
|
||||
|
||||
free( info->filename );
|
||||
|
||||
/* Put the new data in */
|
||||
info->filename = new_filename; info->file = new_file;
|
||||
|
||||
/* And update the displayed information */
|
||||
gtk_label_set_text( GTK_LABEL( info->filename_widget ), new_filename );
|
||||
|
||||
snprintf( buffer, 80, "%lu", (unsigned long)info->file.length );
|
||||
gtk_entry_set_text( GTK_ENTRY( info->length_widget ), buffer );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load a chunk of binary data into the Spectrum's memory. It's called as a
|
||||
* callback from the GUI when the user hits the OK button.
|
||||
*
|
||||
* The given user_data is a binary_info structure which contains the user's
|
||||
* selected filename, plus the start/length widgets the user will have filled
|
||||
* in.
|
||||
*
|
||||
* This is called in a separate GTK loop, so it returns to code which tidies
|
||||
* up and unpauses the Spectrum.
|
||||
*/
|
||||
static void
|
||||
load_data( GtkEntry *entry GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
struct binary_info *info = user_data;
|
||||
|
||||
long start, length; size_t i;
|
||||
const gchar *nptr;
|
||||
char *endptr;
|
||||
int base;
|
||||
|
||||
errno = 0;
|
||||
nptr = gtk_entry_get_text( GTK_ENTRY( info->length_widget ) );
|
||||
base = ( g_str_has_prefix( nptr, "0x" ) )? 16 : 10;
|
||||
length = strtol( nptr, &endptr, base );
|
||||
|
||||
if( errno || length < 1 || length > 0x10000 || endptr == nptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Length must be between 1 and 65536" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( length > info->file.length ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"'%s' contains only %lu bytes",
|
||||
info->filename, (unsigned long)info->file.length );
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
nptr = gtk_entry_get_text( GTK_ENTRY( info->start_widget ) );
|
||||
base = ( g_str_has_prefix( nptr, "0x" ) )? 16 : 10;
|
||||
start = strtol( nptr, &endptr, base );
|
||||
|
||||
if( errno || start < 0 || start > 0xffff || endptr == nptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Start must be between 0 and 65535" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( start + length > 0x10000 ) {
|
||||
ui_error( UI_ERROR_ERROR, "Block ends after address 65535" );
|
||||
return;
|
||||
}
|
||||
|
||||
for( i = 0; i < length; i++ )
|
||||
writebyte_internal( start + i, info->file.buffer[ i ] );
|
||||
|
||||
gtkui_destroy_widget_and_quit( info->dialog, NULL );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is the start point for the save binary data GUI command. It pops up
|
||||
* the file selector if a selected file isn't already in the static structure,
|
||||
* then pops up the filename/start/length dialog.
|
||||
*/
|
||||
void
|
||||
menu_file_savebinarydata( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
fuse_emulation_pause();
|
||||
|
||||
/*
|
||||
* If the filename is blank it's not been selected previously.
|
||||
* Pop up the file selector.
|
||||
*/
|
||||
if( !save_info.filename )
|
||||
{
|
||||
save_info.filename = ui_get_save_filename( "Fuse - Save Binary Data" );
|
||||
if( !save_info.filename )
|
||||
{
|
||||
fuse_emulation_unpause();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
save_info.activate_data = G_CALLBACK( save_data );
|
||||
save_info.change_filename = G_CALLBACK( change_save_filename );
|
||||
|
||||
create_shared_binary_dialog( &save_info, "Fuse - Save Binary Data",
|
||||
BINARY_DIALOG_MODE_SAVE );
|
||||
|
||||
/* Process the dialog */
|
||||
gtk_widget_show_all( save_info.dialog );
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Change the name of the file the save binary command will write
|
||||
* out to. The previously specified filename is removed (and the memory
|
||||
* for it reclaimed) and a new filename is stored in its place. The
|
||||
* GUI entry field is updated.
|
||||
*
|
||||
* This is only called after the user has already specified a filename,
|
||||
* so it's safe to free the old name.
|
||||
*/
|
||||
static void
|
||||
change_save_filename( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
struct binary_info *info = user_data;
|
||||
char *new_filename;
|
||||
|
||||
new_filename = ui_get_save_filename( "Fuse - Save Binary Data" );
|
||||
if( !new_filename ) return;
|
||||
|
||||
free( info->filename );
|
||||
|
||||
info->filename = new_filename;
|
||||
|
||||
gtk_label_set_text( GTK_LABEL( info->filename_widget ), new_filename );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write out a chunk of binary data, as described by the given binary_info
|
||||
* structure passed in.
|
||||
*
|
||||
* This writes the given number of data bytes from the given start point in
|
||||
* the Spectrum's memory out to the given file.
|
||||
*/
|
||||
static void
|
||||
save_data( GtkEntry *entry GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
struct binary_info *info = user_data;
|
||||
|
||||
const gchar *nptr;
|
||||
char *endptr;
|
||||
int base;
|
||||
|
||||
int error;
|
||||
|
||||
errno = 0;
|
||||
nptr = gtk_entry_get_text( GTK_ENTRY( info->length_widget ) );
|
||||
base = ( g_str_has_prefix( nptr, "0x" ) )? 16 : 10;
|
||||
info->length = strtol( nptr, &endptr, base );
|
||||
|
||||
if( errno || info->length < 1 || info->length > 0x10000 || endptr == nptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Length must be between 1 and 65536" );
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
nptr = gtk_entry_get_text( GTK_ENTRY( info->start_widget ) );
|
||||
base = ( g_str_has_prefix( nptr, "0x" ) )? 16 : 10;
|
||||
info->start = strtol( nptr, &endptr, base );
|
||||
|
||||
if( errno || info->start < 0 || info->start > 0xffff || endptr == nptr ) {
|
||||
ui_error( UI_ERROR_ERROR, "Start must be between 0 and 65535" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( info->start + info->length > 0x10000 ) {
|
||||
ui_error( UI_ERROR_ERROR, "Block ends after address 65535" );
|
||||
return;
|
||||
}
|
||||
|
||||
error = utils_save_binary( info->start, info->length, info->filename );
|
||||
if( error ) return;
|
||||
|
||||
gtkui_destroy_widget_and_quit( info->dialog, NULL );
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
/* browse.c: tape browser dialog box
|
||||
Copyright (c) 2002-2004 Philip Kendall
|
||||
Copyright (c) 2015 Sergio Baldoví
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "menu.h"
|
||||
#include "tape.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static int create_dialog( void );
|
||||
static void add_block_details( libspectrum_tape_block *block,
|
||||
void *user_data );
|
||||
static void select_row( GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer user_data );
|
||||
void mark_row( GtkTreeModel *model, int row );
|
||||
static void browse_done( GtkWidget *widget, gpointer data );
|
||||
static gboolean delete_dialog( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer user_data );
|
||||
|
||||
static GdkPixbuf *tape_marker_pixbuf;
|
||||
|
||||
static GtkWidget
|
||||
*dialog, /* The dialog box itself */
|
||||
*blocks, /* The list of blocks */
|
||||
*modified_label; /* The label saying if the tape has been
|
||||
modified */
|
||||
|
||||
static int dialog_created; /* Have we created the dialog box yet? */
|
||||
|
||||
/* List columns */
|
||||
enum
|
||||
{
|
||||
COL_PIX = 0, /* Pixmap */
|
||||
COL_BLOCK, /* Block type */
|
||||
COL_DATA, /* Data detail */
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
void
|
||||
menu_media_tape_browse( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
/* Firstly, stop emulation */
|
||||
fuse_emulation_pause();
|
||||
|
||||
if( !dialog_created )
|
||||
if( create_dialog() ) { fuse_emulation_unpause(); return; }
|
||||
|
||||
if( ui_tape_browser_update( UI_TAPE_BROWSER_NEW_TAPE, NULL ) ) {
|
||||
fuse_emulation_unpause();
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
|
||||
/* Carry on with emulation */
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_block_list( void )
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeModel *model;
|
||||
GtkListStore *store;
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
/* Add columns */
|
||||
renderer = gtk_cell_renderer_pixbuf_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
NULL,
|
||||
renderer,
|
||||
"pixbuf", COL_PIX,
|
||||
NULL );
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Block type",
|
||||
renderer,
|
||||
"text", COL_BLOCK,
|
||||
NULL );
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Data",
|
||||
renderer,
|
||||
"text", COL_DATA,
|
||||
NULL );
|
||||
|
||||
/* Create data model */
|
||||
store = gtk_list_store_new( NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
|
||||
G_TYPE_STRING );
|
||||
|
||||
model = GTK_TREE_MODEL( store );
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW( view ), model );
|
||||
g_object_unref( model );
|
||||
|
||||
/* Fast move tape */
|
||||
g_signal_connect( G_OBJECT( view ), "row-activated",
|
||||
G_CALLBACK( select_row ), model );
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
static int
|
||||
create_dialog( void )
|
||||
{
|
||||
GtkWidget *scrolled_window, *content_area;
|
||||
|
||||
/* Give me a new dialog box */
|
||||
dialog = gtkstock_dialog_new( "Fuse - Browse Tape",
|
||||
G_CALLBACK( delete_dialog ) );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
/* And a scrolled window to pack the list into */
|
||||
scrolled_window = gtk_scrolled_window_new( NULL, NULL );
|
||||
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), scrolled_window, TRUE, TRUE, 0 );
|
||||
|
||||
/* The tape marker pixbuf */
|
||||
tape_marker_pixbuf = gdk_pixbuf_new_from_xpm_data( gtkpixmap_tape_marker );
|
||||
/* FIXME: unref this at exit */
|
||||
|
||||
/* And the list itself */
|
||||
blocks = create_block_list();
|
||||
gtk_container_add( GTK_CONTAINER( scrolled_window ), GTK_WIDGET( blocks ) );
|
||||
|
||||
/* And the "tape modified" label */
|
||||
modified_label = gtk_label_new( "" );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), modified_label, FALSE, FALSE, 0 );
|
||||
|
||||
/* Create the OK button */
|
||||
gtkstock_create_close( dialog, NULL, G_CALLBACK( browse_done ), FALSE );
|
||||
|
||||
/* Make the window big enough to show at least some data */
|
||||
gtk_window_set_default_size( GTK_WINDOW( dialog ), -1, 250 );
|
||||
|
||||
dialog_created = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_tape_browser_update( ui_tape_browser_update_type change GCC_UNUSED,
|
||||
libspectrum_tape_block *block GCC_UNUSED )
|
||||
{
|
||||
int error, current_block;
|
||||
GtkTreeModel *model;
|
||||
|
||||
if( !dialog_created ) return 0;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
model = gtk_tree_view_get_model( GTK_TREE_VIEW( blocks ) );
|
||||
gtk_list_store_clear( GTK_LIST_STORE( model ) );
|
||||
|
||||
error = tape_foreach( add_block_details, model );
|
||||
if( error ) {
|
||||
fuse_emulation_unpause();
|
||||
return 1;
|
||||
}
|
||||
|
||||
current_block = tape_get_current_block();
|
||||
|
||||
if( current_block != -1 )
|
||||
mark_row( model, current_block );
|
||||
|
||||
if( tape_modified ) {
|
||||
gtk_label_set_text( GTK_LABEL( modified_label ), "Tape modified" );
|
||||
} else {
|
||||
gtk_label_set_text( GTK_LABEL( modified_label ), "Tape not modified" );
|
||||
}
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
add_block_details( libspectrum_tape_block *block, void *user_data )
|
||||
{
|
||||
gchar block_type[80];
|
||||
gchar data_detail[80];
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model = user_data;
|
||||
|
||||
libspectrum_tape_block_description( block_type, 80, block );
|
||||
tape_block_details( data_detail, 80, block );
|
||||
|
||||
/* Append a new row and fill data */
|
||||
gtk_list_store_append( GTK_LIST_STORE( model ), &iter );
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_PIX, NULL,
|
||||
COL_BLOCK, block_type,
|
||||
COL_DATA, data_detail,
|
||||
-1 );
|
||||
}
|
||||
|
||||
/* Called when a row is selected */
|
||||
static void
|
||||
select_row( GtkTreeView *treeview GCC_UNUSED, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
int current_block;
|
||||
int *indices;
|
||||
int row;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model = user_data;
|
||||
|
||||
/* Get selected row */
|
||||
row = -1;
|
||||
indices = gtk_tree_path_get_indices( path );
|
||||
if( indices ) row = indices[0];
|
||||
|
||||
/* Don't do anything if the current block was clicked on */
|
||||
current_block = tape_get_current_block();
|
||||
if( row == current_block ) return;
|
||||
|
||||
/* Otherwise, select the new block */
|
||||
tape_select_block_no_update( row );
|
||||
|
||||
/* Mark selected block */
|
||||
if( current_block != -1 ) {
|
||||
gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path );
|
||||
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_PIX, tape_marker_pixbuf,
|
||||
-1 );
|
||||
}
|
||||
|
||||
/* Unmark former block */
|
||||
if( current_block != -1 ) {
|
||||
path = gtk_tree_path_new_from_indices( current_block, -1 );
|
||||
|
||||
if( !gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path ) ) {
|
||||
gtk_tree_path_free( path );
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_tree_path_free( path );
|
||||
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_PIX, NULL,
|
||||
-1 );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mark_row( GtkTreeModel *model, int row )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
|
||||
path = gtk_tree_path_new_from_indices( row, -1 );
|
||||
|
||||
if( !gtk_tree_model_get_iter( model, &iter, path ) ) {
|
||||
gtk_tree_path_free( path );
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_tree_path_free( path );
|
||||
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_PIX, tape_marker_pixbuf,
|
||||
-1 );
|
||||
}
|
||||
|
||||
/* Called if the OK button is clicked */
|
||||
static void
|
||||
browse_done( GtkWidget *widget GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
dialog_created = 0;
|
||||
gtk_widget_destroy( dialog );
|
||||
}
|
||||
|
||||
/* Catch attempts to delete the window and just hide it instead */
|
||||
static gboolean
|
||||
delete_dialog( GtkWidget *widget, GdkEvent *event GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
dialog_created = 0;
|
||||
gtk_widget_destroy( dialog );
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/* confirm.c: Confirmation dialog box
|
||||
Copyright (c) 2000-2015 Philip Kendall, Russell Marks
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static void set_confirmed( GtkButton *button, gpointer user_data );
|
||||
static void set_save( GtkButton *button, gpointer user_data );
|
||||
static void set_dont_save( GtkButton *button, gpointer user_data );
|
||||
|
||||
int
|
||||
gtkui_confirm( const char *string )
|
||||
{
|
||||
GtkWidget *dialog, *label, *content_area;
|
||||
int confirm;
|
||||
|
||||
/* Return value isn't an error code, but signifies whether to undertake
|
||||
the action */
|
||||
if( !settings_current.confirm_actions ) return 1;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
confirm = 0;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Confirm", NULL );
|
||||
|
||||
label = gtk_label_new( string );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), label, TRUE, TRUE, 5 );
|
||||
|
||||
gtkstock_create_ok_cancel( dialog, NULL, G_CALLBACK( set_confirmed ),
|
||||
&confirm, DEFAULT_DESTROY, DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog ), GTK_RESPONSE_CANCEL );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return confirm;
|
||||
}
|
||||
|
||||
static void
|
||||
set_confirmed( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
int *ptr = user_data;
|
||||
|
||||
*ptr = 1;
|
||||
}
|
||||
|
||||
ui_confirm_save_t
|
||||
ui_confirm_save_specific( const char *message )
|
||||
{
|
||||
GtkWidget *dialog, *label, *content_area;
|
||||
ui_confirm_save_t confirm;
|
||||
|
||||
if( !settings_current.confirm_actions ) return UI_CONFIRM_SAVE_DONTSAVE;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
confirm = UI_CONFIRM_SAVE_CANCEL;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Confirm", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
label = gtk_label_new( message );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), label, TRUE, TRUE, 5 );
|
||||
|
||||
{
|
||||
static gtkstock_button btn[] = {
|
||||
{ "_No", G_CALLBACK( set_dont_save ), NULL, DEFAULT_DESTROY, 0, 0, 0, 0, GTK_RESPONSE_NO },
|
||||
{ "_Cancel", NULL, NULL, DEFAULT_DESTROY, GDK_KEY_Escape, 0, 0, 0, GTK_RESPONSE_CANCEL },
|
||||
{ "_Save", G_CALLBACK( set_save ), NULL, DEFAULT_DESTROY, 0, 0, 0, 0, GTK_RESPONSE_YES }
|
||||
};
|
||||
btn[0].actiondata = btn[2].actiondata = &confirm;
|
||||
gtkstock_create_buttons( dialog, NULL, btn, ARRAY_SIZE( btn ) );
|
||||
}
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog ), GTK_RESPONSE_CANCEL );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return confirm;
|
||||
}
|
||||
|
||||
int
|
||||
ui_query( const char *message )
|
||||
{
|
||||
return gtkui_confirm( message );
|
||||
}
|
||||
|
||||
static void
|
||||
set_save( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
ui_confirm_save_t *ptr = user_data;
|
||||
|
||||
*ptr = UI_CONFIRM_SAVE_SAVE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_dont_save( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
ui_confirm_save_t *ptr = user_data;
|
||||
|
||||
*ptr = UI_CONFIRM_SAVE_DONTSAVE;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,357 +0,0 @@
|
||||
/* fileselector.c: GTK fileselector routines
|
||||
Copyright (c) 2000-2007 Philip Kendall
|
||||
Copyright (c) 2015-2016 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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkinternals.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static gchar *current_folder;
|
||||
|
||||
static void add_filter_defaults( GtkWidget *file_chooser );
|
||||
static void add_filter_auxiliary_files( GtkFileFilter *filter );
|
||||
static void add_filter_disk_files( GtkFileFilter *filter );
|
||||
static void add_filter_dock_files( GtkFileFilter *filter );
|
||||
static void add_filter_harddisk_files( GtkFileFilter *filter );
|
||||
static void add_filter_microdrive_files( GtkFileFilter *filter );
|
||||
static void add_filter_movie_files( GtkFileFilter *filter );
|
||||
static void add_filter_recording_files( GtkFileFilter *filter );
|
||||
static void add_filter_snapshot_files( GtkFileFilter *filter );
|
||||
static void add_filter_tape_files( GtkFileFilter *filter );
|
||||
|
||||
#if defined LIBSPECTRUM_SUPPORTS_BZ2_COMPRESSION || \
|
||||
defined LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
||||
static void add_filter_compressed_files( GtkFileFilter *filter );
|
||||
#endif
|
||||
|
||||
static char*
|
||||
run_dialog( const char *title, GtkFileChooserAction action )
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
char *filename = NULL;
|
||||
const char *button;
|
||||
|
||||
if( action == GTK_FILE_CHOOSER_ACTION_SAVE ) {
|
||||
button = "_Save";
|
||||
} else {
|
||||
button = "_Open";
|
||||
}
|
||||
|
||||
dialog =
|
||||
gtk_file_chooser_dialog_new( title, GTK_WINDOW( gtkui_window ),
|
||||
action, "_Cancel", GTK_RESPONSE_CANCEL,
|
||||
button, GTK_RESPONSE_ACCEPT,
|
||||
NULL );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT );
|
||||
|
||||
/* TODO: select filter based on UI operation (snapshot, recording, screenshot) */
|
||||
/* TODO: custom filter based file action (open, save) */
|
||||
add_filter_defaults( dialog );
|
||||
|
||||
if( current_folder )
|
||||
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( dialog ), current_folder );
|
||||
|
||||
if( gtk_dialog_run( GTK_DIALOG( dialog ) ) == GTK_RESPONSE_ACCEPT ) {
|
||||
gchar *new_folder = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog ) );
|
||||
if( new_folder ) {
|
||||
g_free( current_folder );
|
||||
current_folder = new_folder;
|
||||
}
|
||||
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( dialog ) );
|
||||
}
|
||||
|
||||
gtk_widget_destroy( dialog );
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
ui_get_open_filename( const char *title )
|
||||
{
|
||||
return run_dialog( title, GTK_FILE_CHOOSER_ACTION_OPEN );
|
||||
}
|
||||
|
||||
char*
|
||||
ui_get_save_filename( const char *title )
|
||||
{
|
||||
return run_dialog( title, GTK_FILE_CHOOSER_ACTION_SAVE );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_defaults( GtkWidget *file_chooser )
|
||||
{
|
||||
GtkFileFilter *filter;
|
||||
|
||||
/* TODO: poll libspectrum for supported file extensions and avoid duplication
|
||||
between UIs */
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Supported Files" );
|
||||
add_filter_auxiliary_files( filter );
|
||||
#if defined LIBSPECTRUM_SUPPORTS_BZ2_COMPRESSION || \
|
||||
defined LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
||||
add_filter_compressed_files( filter );
|
||||
#endif
|
||||
add_filter_disk_files( filter );
|
||||
add_filter_dock_files( filter );
|
||||
add_filter_harddisk_files( filter );
|
||||
add_filter_microdrive_files( filter );
|
||||
add_filter_movie_files( filter );
|
||||
add_filter_recording_files( filter );
|
||||
add_filter_snapshot_files( filter );
|
||||
add_filter_tape_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "All Files" );
|
||||
gtk_file_filter_add_pattern( filter, "*" );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Auxiliary Files" );
|
||||
add_filter_auxiliary_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Cartridge Files" );
|
||||
add_filter_dock_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
#if defined LIBSPECTRUM_SUPPORTS_BZ2_COMPRESSION || \
|
||||
defined LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Compressed Files" );
|
||||
add_filter_compressed_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
#endif
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Disk Files" );
|
||||
add_filter_disk_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Harddisk Files" );
|
||||
add_filter_harddisk_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Microdrive Files" );
|
||||
add_filter_microdrive_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Movie Files" );
|
||||
add_filter_movie_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Recording Files" );
|
||||
add_filter_recording_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Snapshot Files" );
|
||||
add_filter_snapshot_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "Tape Files" );
|
||||
add_filter_tape_files( filter );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_chooser ), filter );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_auxiliary_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.bin" );
|
||||
gtk_file_filter_add_pattern( filter, "*.log" );
|
||||
gtk_file_filter_add_pattern( filter, "*.pok" );
|
||||
gtk_file_filter_add_pattern( filter, "*.scr" );
|
||||
gtk_file_filter_add_pattern( filter, "*.mlt" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.BIN" );
|
||||
gtk_file_filter_add_pattern( filter, "*.LOG" );
|
||||
gtk_file_filter_add_pattern( filter, "*.POK" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SCR" );
|
||||
gtk_file_filter_add_pattern( filter, "*.MLT" );
|
||||
|
||||
#ifdef USE_LIBPNG
|
||||
gtk_file_filter_add_pattern( filter, "*.png" );
|
||||
gtk_file_filter_add_pattern( filter, "*.PNG" );
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIB_XML2
|
||||
gtk_file_filter_add_pattern( filter, "*.svg" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SVG" );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if defined LIBSPECTRUM_SUPPORTS_BZ2_COMPRESSION || \
|
||||
defined LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
||||
|
||||
static void
|
||||
add_filter_compressed_files( GtkFileFilter *filter )
|
||||
{
|
||||
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
||||
gtk_file_filter_add_pattern( filter, "*.gz" );
|
||||
gtk_file_filter_add_pattern( filter, "*.GZ" );
|
||||
gtk_file_filter_add_pattern( filter, "*.zip" );
|
||||
gtk_file_filter_add_pattern( filter, "*.ZIP" );
|
||||
#endif
|
||||
|
||||
#ifdef LIBSPECTRUM_SUPPORTS_BZ2_COMPRESSION
|
||||
gtk_file_filter_add_pattern( filter, "*.bz2" );
|
||||
gtk_file_filter_add_pattern( filter, "*.BZ2" );
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
add_filter_disk_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.d40" );
|
||||
gtk_file_filter_add_pattern( filter, "*.d80" );
|
||||
gtk_file_filter_add_pattern( filter, "*.dsk" );
|
||||
gtk_file_filter_add_pattern( filter, "*.fdi" );
|
||||
gtk_file_filter_add_pattern( filter, "*.img" );
|
||||
gtk_file_filter_add_pattern( filter, "*.mgt" );
|
||||
gtk_file_filter_add_pattern( filter, "*.opd" );
|
||||
gtk_file_filter_add_pattern( filter, "*.opu" );
|
||||
gtk_file_filter_add_pattern( filter, "*.sad" );
|
||||
gtk_file_filter_add_pattern( filter, "*.scl" );
|
||||
gtk_file_filter_add_pattern( filter, "*.td0" );
|
||||
gtk_file_filter_add_pattern( filter, "*.trd" );
|
||||
gtk_file_filter_add_pattern( filter, "*.udi" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.D40" );
|
||||
gtk_file_filter_add_pattern( filter, "*.D80" );
|
||||
gtk_file_filter_add_pattern( filter, "*.DSK" );
|
||||
gtk_file_filter_add_pattern( filter, "*.FDI" );
|
||||
gtk_file_filter_add_pattern( filter, "*.IMG" );
|
||||
gtk_file_filter_add_pattern( filter, "*.MGT" );
|
||||
gtk_file_filter_add_pattern( filter, "*.OPD" );
|
||||
gtk_file_filter_add_pattern( filter, "*.OPU" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SAD" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SCL" );
|
||||
gtk_file_filter_add_pattern( filter, "*.TD0" );
|
||||
gtk_file_filter_add_pattern( filter, "*.TRD" );
|
||||
gtk_file_filter_add_pattern( filter, "*.UDI" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_dock_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.dck" );
|
||||
gtk_file_filter_add_pattern( filter, "*.rom" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.DCK" );
|
||||
gtk_file_filter_add_pattern( filter, "*.ROM" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_harddisk_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.hdf" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.HDF" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_microdrive_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.mdr" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.MDR" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_movie_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.fmf" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.FMF" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_recording_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.rzx" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.RZX" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_snapshot_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.mgtsnp" );
|
||||
gtk_file_filter_add_pattern( filter, "*.slt" );
|
||||
gtk_file_filter_add_pattern( filter, "*.sna" );
|
||||
gtk_file_filter_add_pattern( filter, "*.snapshot" );
|
||||
gtk_file_filter_add_pattern( filter, "*.snp" );
|
||||
gtk_file_filter_add_pattern( filter, "*.sp" );
|
||||
gtk_file_filter_add_pattern( filter, "*.szx" );
|
||||
gtk_file_filter_add_pattern( filter, "*.z80" );
|
||||
gtk_file_filter_add_pattern( filter, "*.zx-state" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.MGTSNP" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SLT" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SNA" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SNAPSHOT" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SNP" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SP" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SZX" );
|
||||
gtk_file_filter_add_pattern( filter, "*.Z80" );
|
||||
gtk_file_filter_add_pattern( filter, "*.ZX-STATE" );
|
||||
}
|
||||
|
||||
static void
|
||||
add_filter_tape_files( GtkFileFilter *filter )
|
||||
{
|
||||
gtk_file_filter_add_pattern( filter, "*.csw" );
|
||||
gtk_file_filter_add_pattern( filter, "*.ltp" );
|
||||
gtk_file_filter_add_pattern( filter, "*.pzx" );
|
||||
gtk_file_filter_add_pattern( filter, "*.raw" );
|
||||
gtk_file_filter_add_pattern( filter, "*.spc" );
|
||||
gtk_file_filter_add_pattern( filter, "*.sta" );
|
||||
gtk_file_filter_add_pattern( filter, "*.tzx" );
|
||||
gtk_file_filter_add_pattern( filter, "*.tap" );
|
||||
gtk_file_filter_add_pattern( filter, "*.wav" );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, "*.CSW" );
|
||||
gtk_file_filter_add_pattern( filter, "*.LTP" );
|
||||
gtk_file_filter_add_pattern( filter, "*.PZX" );
|
||||
gtk_file_filter_add_pattern( filter, "*.RAW" );
|
||||
gtk_file_filter_add_pattern( filter, "*.SPC" );
|
||||
gtk_file_filter_add_pattern( filter, "*.STA" );
|
||||
gtk_file_filter_add_pattern( filter, "*.TZX" );
|
||||
gtk_file_filter_add_pattern( filter, "*.TAP" );
|
||||
gtk_file_filter_add_pattern( filter, "*.WAV" );
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/* gtkcompat.c: various compatibility bits between GTK versions
|
||||
Copyright (c) 2012-2014 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkcompat.h"
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
GtkWidget *
|
||||
gtk_box_new( GtkOrientation orientation, gint spacing )
|
||||
{
|
||||
return ( orientation == GTK_ORIENTATION_HORIZONTAL )?
|
||||
gtk_hbox_new( FALSE, spacing ) :
|
||||
gtk_vbox_new( FALSE, spacing );
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_separator_new( GtkOrientation orientation )
|
||||
{
|
||||
return ( orientation == GTK_ORIENTATION_HORIZONTAL )?
|
||||
gtk_hseparator_new() :
|
||||
gtk_vseparator_new();
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_scrollbar_new( GtkOrientation orientation, GtkAdjustment *adjustment )
|
||||
{
|
||||
return ( orientation == GTK_ORIENTATION_HORIZONTAL )?
|
||||
gtk_hscrollbar_new( adjustment ) :
|
||||
gtk_vscrollbar_new( adjustment );
|
||||
}
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
@@ -1,198 +0,0 @@
|
||||
/* gtkcompat.h: various compatibility bits between GTK versions
|
||||
Copyright (c) 2012 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_GTKCOMPAT_H
|
||||
#define FUSE_GTKCOMPAT_H
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifndef GTK_TYPE_COMBO_BOX_TEXT
|
||||
|
||||
#define GTK_COMBO_BOX_TEXT( X ) GTK_COMBO_BOX( X )
|
||||
#define gtk_combo_box_text_new() gtk_combo_box_new_text()
|
||||
#define gtk_combo_box_text_append_text( X, Y ) gtk_combo_box_append_text( X, Y )
|
||||
|
||||
#endif /* #ifndef GTK_TYPE_COMBO_BOX_TEXT */
|
||||
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
GtkWidget *
|
||||
gtk_box_new( GtkOrientation orientation, gint spacing );
|
||||
|
||||
GtkWidget *
|
||||
gtk_separator_new( GtkOrientation orientation );
|
||||
|
||||
GtkWidget *
|
||||
gtk_scrollbar_new( GtkOrientation orientation, GtkAdjustment *adjustment );
|
||||
|
||||
#define gtk_widget_override_font( X, Y ) gtk_widget_modify_font( X, Y )
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !GTK_CHECK_VERSION( 2, 22, 0 )
|
||||
|
||||
#define GDK_KEY_VoidSymbol GDK_VoidSymbol
|
||||
#define GDK_KEY_Tab GDK_Tab
|
||||
#define GDK_KEY_Return GDK_Return
|
||||
#define GDK_KEY_Escape GDK_Escape
|
||||
#define GDK_KEY_space GDK_space
|
||||
#define GDK_KEY_quotedbl GDK_quotedbl
|
||||
#define GDK_KEY_exclam GDK_exclam
|
||||
#define GDK_KEY_numbersign GDK_numbersign
|
||||
#define GDK_KEY_dollar GDK_dollar
|
||||
#define GDK_KEY_percent GDK_percent
|
||||
#define GDK_KEY_ampersand GDK_ampersand
|
||||
#define GDK_KEY_apostrophe GDK_apostrophe
|
||||
#define GDK_KEY_parenleft GDK_parenleft
|
||||
#define GDK_KEY_parenright GDK_parenright
|
||||
#define GDK_KEY_asterisk GDK_asterisk
|
||||
#define GDK_KEY_plus GDK_plus
|
||||
#define GDK_KEY_comma GDK_comma
|
||||
#define GDK_KEY_minus GDK_minus
|
||||
#define GDK_KEY_period GDK_period
|
||||
#define GDK_KEY_slash GDK_slash
|
||||
#define GDK_KEY_0 GDK_0
|
||||
#define GDK_KEY_1 GDK_1
|
||||
#define GDK_KEY_2 GDK_2
|
||||
#define GDK_KEY_3 GDK_3
|
||||
#define GDK_KEY_4 GDK_4
|
||||
#define GDK_KEY_5 GDK_5
|
||||
#define GDK_KEY_6 GDK_6
|
||||
#define GDK_KEY_7 GDK_7
|
||||
#define GDK_KEY_8 GDK_8
|
||||
#define GDK_KEY_9 GDK_9
|
||||
#define GDK_KEY_colon GDK_colon
|
||||
#define GDK_KEY_semicolon GDK_semicolon
|
||||
#define GDK_KEY_less GDK_less
|
||||
#define GDK_KEY_equal GDK_equal
|
||||
#define GDK_KEY_greater GDK_greater
|
||||
#define GDK_KEY_question GDK_question
|
||||
#define GDK_KEY_at GDK_at
|
||||
#define GDK_KEY_A GDK_A
|
||||
#define GDK_KEY_B GDK_B
|
||||
#define GDK_KEY_C GDK_C
|
||||
#define GDK_KEY_D GDK_D
|
||||
#define GDK_KEY_E GDK_E
|
||||
#define GDK_KEY_F GDK_F
|
||||
#define GDK_KEY_G GDK_G
|
||||
#define GDK_KEY_H GDK_H
|
||||
#define GDK_KEY_I GDK_I
|
||||
#define GDK_KEY_J GDK_J
|
||||
#define GDK_KEY_K GDK_K
|
||||
#define GDK_KEY_L GDK_L
|
||||
#define GDK_KEY_M GDK_M
|
||||
#define GDK_KEY_N GDK_N
|
||||
#define GDK_KEY_O GDK_O
|
||||
#define GDK_KEY_P GDK_P
|
||||
#define GDK_KEY_Q GDK_Q
|
||||
#define GDK_KEY_R GDK_R
|
||||
#define GDK_KEY_S GDK_S
|
||||
#define GDK_KEY_T GDK_T
|
||||
#define GDK_KEY_U GDK_U
|
||||
#define GDK_KEY_V GDK_V
|
||||
#define GDK_KEY_W GDK_W
|
||||
#define GDK_KEY_X GDK_X
|
||||
#define GDK_KEY_Y GDK_Y
|
||||
#define GDK_KEY_Z GDK_Z
|
||||
#define GDK_KEY_bracketleft GDK_bracketleft
|
||||
#define GDK_KEY_backslash GDK_backslash
|
||||
#define GDK_KEY_bracketright GDK_bracketright
|
||||
#define GDK_KEY_asciicircum GDK_asciicircum
|
||||
#define GDK_KEY_underscore GDK_underscore
|
||||
#define GDK_KEY_dead_circumflex GDK_dead_circumflex
|
||||
#define GDK_KEY_a GDK_a
|
||||
#define GDK_KEY_b GDK_b
|
||||
#define GDK_KEY_c GDK_c
|
||||
#define GDK_KEY_d GDK_d
|
||||
#define GDK_KEY_e GDK_e
|
||||
#define GDK_KEY_f GDK_f
|
||||
#define GDK_KEY_g GDK_g
|
||||
#define GDK_KEY_h GDK_h
|
||||
#define GDK_KEY_i GDK_i
|
||||
#define GDK_KEY_j GDK_j
|
||||
#define GDK_KEY_k GDK_k
|
||||
#define GDK_KEY_l GDK_l
|
||||
#define GDK_KEY_m GDK_m
|
||||
#define GDK_KEY_n GDK_n
|
||||
#define GDK_KEY_o GDK_o
|
||||
#define GDK_KEY_p GDK_p
|
||||
#define GDK_KEY_q GDK_q
|
||||
#define GDK_KEY_r GDK_r
|
||||
#define GDK_KEY_s GDK_s
|
||||
#define GDK_KEY_t GDK_t
|
||||
#define GDK_KEY_u GDK_u
|
||||
#define GDK_KEY_v GDK_v
|
||||
#define GDK_KEY_w GDK_w
|
||||
#define GDK_KEY_x GDK_x
|
||||
#define GDK_KEY_y GDK_y
|
||||
#define GDK_KEY_z GDK_z
|
||||
#define GDK_KEY_braceleft GDK_braceleft
|
||||
#define GDK_KEY_bar GDK_bar
|
||||
#define GDK_KEY_braceright GDK_braceright
|
||||
#define GDK_KEY_asciitilde GDK_asciitilde
|
||||
#define GDK_KEY_BackSpace GDK_BackSpace
|
||||
#define GDK_KEY_KP_Enter GDK_KP_Enter
|
||||
#define GDK_KEY_Up GDK_Up
|
||||
#define GDK_KEY_Down GDK_Down
|
||||
#define GDK_KEY_Left GDK_Left
|
||||
#define GDK_KEY_Right GDK_Right
|
||||
#define GDK_KEY_Insert GDK_Insert
|
||||
#define GDK_KEY_Delete GDK_Delete
|
||||
#define GDK_KEY_Home GDK_Home
|
||||
#define GDK_KEY_End GDK_End
|
||||
#define GDK_KEY_Page_Up GDK_Page_Up
|
||||
#define GDK_KEY_Page_Down GDK_Page_Down
|
||||
#define GDK_KEY_Caps_Lock GDK_Caps_Lock
|
||||
#define GDK_KEY_F1 GDK_F1
|
||||
#define GDK_KEY_F2 GDK_F2
|
||||
#define GDK_KEY_F3 GDK_F3
|
||||
#define GDK_KEY_F4 GDK_F4
|
||||
#define GDK_KEY_F5 GDK_F5
|
||||
#define GDK_KEY_F6 GDK_F6
|
||||
#define GDK_KEY_F7 GDK_F7
|
||||
#define GDK_KEY_F8 GDK_F8
|
||||
#define GDK_KEY_F9 GDK_F9
|
||||
#define GDK_KEY_F10 GDK_F10
|
||||
#define GDK_KEY_F11 GDK_F11
|
||||
#define GDK_KEY_F12 GDK_F12
|
||||
#define GDK_KEY_Shift_L GDK_Shift_L
|
||||
#define GDK_KEY_Shift_R GDK_Shift_R
|
||||
#define GDK_KEY_Control_L GDK_Control_L
|
||||
#define GDK_KEY_Control_R GDK_Control_R
|
||||
#define GDK_KEY_Alt_L GDK_Alt_L
|
||||
#define GDK_KEY_Alt_R GDK_Alt_R
|
||||
#define GDK_KEY_Meta_L GDK_Meta_L
|
||||
#define GDK_KEY_Meta_R GDK_Meta_R
|
||||
#define GDK_KEY_Super_L GDK_Super_L
|
||||
#define GDK_KEY_Super_R GDK_Super_R
|
||||
#define GDK_KEY_Hyper_L GDK_Hyper_L
|
||||
#define GDK_KEY_Hyper_R GDK_Hyper_R
|
||||
#define GDK_KEY_Mode_switch GDK_Mode_switch
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 2, 22, 0 ) */
|
||||
|
||||
#endif /* #ifndef FUSE_GTKCOMPAT_H */
|
||||
@@ -1,687 +0,0 @@
|
||||
/* gtkdisplay.c: GTK routines for dealing with the Speccy screen
|
||||
Copyright (c) 2000-2005 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include <gdk/gdkwayland.h>
|
||||
#endif
|
||||
|
||||
#include "display.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "screenshot.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uidisplay.h"
|
||||
#include "ui/scaler/scaler.h"
|
||||
#include "settings.h"
|
||||
|
||||
/* The biggest size screen (in units of DISPLAY_ASPECT_WIDTH x
|
||||
DISPLAY_SCREEN_HEIGHT ie a Timex screen is size 2) we will be
|
||||
creating via the scalers */
|
||||
#define MAX_SCALE 4
|
||||
|
||||
/* The size of a 1x1 image in units of
|
||||
DISPLAY_ASPECT WIDTH x DISPLAY_SCREEN_HEIGHT */
|
||||
int image_scale;
|
||||
|
||||
/* The height and width of a 1x1 image in pixels */
|
||||
int image_width, image_height;
|
||||
|
||||
/* A copy of every pixel on the screen, replaceable by plotting directly into
|
||||
rgb_image below */
|
||||
libspectrum_word
|
||||
gtkdisplay_image[ 2 * DISPLAY_SCREEN_HEIGHT ][ DISPLAY_SCREEN_WIDTH ];
|
||||
ptrdiff_t gtkdisplay_pitch = DISPLAY_SCREEN_WIDTH * sizeof( libspectrum_word );
|
||||
|
||||
/* An RGB image of the Spectrum screen; slightly bigger than the real
|
||||
screen to handle the smoothing filters which read around each pixel */
|
||||
static guchar rgb_image[ 4 * 2 * ( DISPLAY_SCREEN_HEIGHT + 4 ) *
|
||||
( DISPLAY_SCREEN_WIDTH + 3 ) ];
|
||||
static const gint rgb_pitch = ( DISPLAY_SCREEN_WIDTH + 3 ) * 4;
|
||||
|
||||
/* The scaled image */
|
||||
static guchar scaled_image[ MAX_SCALE * DISPLAY_SCREEN_HEIGHT *
|
||||
MAX_SCALE * DISPLAY_SCREEN_WIDTH * 2 ];
|
||||
static const ptrdiff_t scaled_pitch = MAX_SCALE * DISPLAY_SCREEN_WIDTH * 2;
|
||||
|
||||
/* The colour palette */
|
||||
static const guchar rgb_colours[16][3] = {
|
||||
|
||||
{ 0, 0, 0 },
|
||||
{ 0, 0, 192 },
|
||||
{ 192, 0, 0 },
|
||||
{ 192, 0, 192 },
|
||||
{ 0, 192, 0 },
|
||||
{ 0, 192, 192 },
|
||||
{ 192, 192, 0 },
|
||||
{ 192, 192, 192 },
|
||||
{ 0, 0, 0 },
|
||||
{ 0, 0, 255 },
|
||||
{ 255, 0, 0 },
|
||||
{ 255, 0, 255 },
|
||||
{ 0, 255, 0 },
|
||||
{ 0, 255, 255 },
|
||||
{ 255, 255, 0 },
|
||||
{ 255, 255, 255 },
|
||||
|
||||
};
|
||||
|
||||
/* And the colours (and black and white 'colours') in 32-bit format */
|
||||
libspectrum_dword gtkdisplay_colours[16];
|
||||
static libspectrum_dword bw_colours[16];
|
||||
|
||||
/* Colour format for the back buffer in endianess-order */
|
||||
typedef enum {
|
||||
FORMAT_x8r8g8b8, /* Cairo (GTK3) */
|
||||
FORMAT_x8b8g8r8 /* GdkRGB (GTK2) */
|
||||
} colour_format_t;
|
||||
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
static int display_updated = 0;
|
||||
|
||||
static cairo_surface_t *surface = NULL;
|
||||
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
/* The current size of the window (in units of DISPLAY_SCREEN_*) */
|
||||
static int gtkdisplay_current_size=1;
|
||||
|
||||
/* Extra height used for menu and status bars */
|
||||
static int extra_height = 0;
|
||||
|
||||
static int init_colours( colour_format_t format );
|
||||
static void gtkdisplay_area(int x, int y, int width, int height);
|
||||
static void register_scalers( int force_scaler );
|
||||
static void gtkdisplay_load_gfx_mode( void );
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
static gint gtkdisplay_expose(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data);
|
||||
#else
|
||||
static gboolean gtkdisplay_draw( GtkWidget *widget, cairo_t *cr,
|
||||
gpointer user_data );
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
static gint drawing_area_resize_callback( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data );
|
||||
|
||||
static int
|
||||
init_colours( colour_format_t format )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < 16; i++ ) {
|
||||
|
||||
|
||||
guchar red, green, blue, grey;
|
||||
|
||||
red = rgb_colours[i][0];
|
||||
green = rgb_colours[i][1];
|
||||
blue = rgb_colours[i][2];
|
||||
|
||||
/* Addition of 0.5 is to avoid rounding errors */
|
||||
grey = ( 0.299 * red + 0.587 * green + 0.114 * blue ) + 0.5;
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
switch( format ) {
|
||||
case FORMAT_x8b8g8r8:
|
||||
gtkdisplay_colours[i] = red << 24 | green << 16 | blue << 8;
|
||||
break;
|
||||
case FORMAT_x8r8g8b8:
|
||||
gtkdisplay_colours[i] = blue << 24 | green << 16 | red << 8;
|
||||
break;
|
||||
}
|
||||
|
||||
bw_colours[i] = grey << 24 | grey << 16 | grey << 8;
|
||||
|
||||
#else /* #ifdef WORDS_BIGENDIAN */
|
||||
|
||||
switch( format ) {
|
||||
case FORMAT_x8b8g8r8:
|
||||
gtkdisplay_colours[i] = red | green << 8 | blue << 16;
|
||||
break;
|
||||
case FORMAT_x8r8g8b8:
|
||||
gtkdisplay_colours[i] = blue | green << 8 | red << 16;
|
||||
break;
|
||||
}
|
||||
|
||||
bw_colours[i] = grey | grey << 8 | grey << 16;
|
||||
|
||||
#endif /* #ifdef WORDS_BIGENDIAN */
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_init( int width, int height )
|
||||
{
|
||||
int x, y, error;
|
||||
libspectrum_dword black;
|
||||
const char *machine_name;
|
||||
colour_format_t colour_format;
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
g_signal_connect( G_OBJECT( gtkui_drawing_area ), "expose_event",
|
||||
G_CALLBACK( gtkdisplay_expose ), NULL );
|
||||
|
||||
colour_format = FORMAT_x8b8g8r8;
|
||||
|
||||
g_signal_connect( G_OBJECT( gtkui_drawing_area ), "configure_event",
|
||||
G_CALLBACK( drawing_area_resize_callback ), NULL );
|
||||
|
||||
#else
|
||||
|
||||
g_signal_connect( G_OBJECT( gtkui_drawing_area ), "draw",
|
||||
G_CALLBACK( gtkdisplay_draw ), NULL );
|
||||
|
||||
colour_format = FORMAT_x8r8g8b8;
|
||||
|
||||
g_signal_connect( G_OBJECT( gtkui_window ), "configure_event",
|
||||
G_CALLBACK( drawing_area_resize_callback ), NULL );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
error = init_colours( colour_format ); if( error ) return error;
|
||||
|
||||
black = settings_current.bw_tv ? bw_colours[0] : gtkdisplay_colours[0];
|
||||
|
||||
for( y = 0; y < DISPLAY_SCREEN_HEIGHT + 4; y++ )
|
||||
for( x = 0; x < DISPLAY_SCREEN_WIDTH + 3; x++ )
|
||||
*(libspectrum_dword*)( rgb_image + y * rgb_pitch + 4 * x ) = black;
|
||||
|
||||
image_width = width; image_height = height;
|
||||
image_scale = width / DISPLAY_ASPECT_WIDTH;
|
||||
|
||||
register_scalers( 0 );
|
||||
|
||||
display_refresh_all();
|
||||
|
||||
if ( scaler_select_scaler( current_scaler ) )
|
||||
scaler_select_scaler( SCALER_NORMAL );
|
||||
|
||||
gtkdisplay_load_gfx_mode();
|
||||
|
||||
machine_name = libspectrum_machine_name( machine_current->machine );
|
||||
gtkstatusbar_update_machine( machine_name );
|
||||
|
||||
display_ui_initialised = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure that an appropriate Cairo surface exists */
|
||||
static void
|
||||
ensure_appropriate_surface( void )
|
||||
{
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
/* Create a bigger surface for the new display size */
|
||||
float scale = (float)gtkdisplay_current_size / image_scale;
|
||||
if( surface ) cairo_surface_destroy( surface );
|
||||
|
||||
surface =
|
||||
cairo_image_surface_create_for_data( scaled_image,
|
||||
CAIRO_FORMAT_RGB24,
|
||||
scale * image_width,
|
||||
scale * image_height,
|
||||
scaled_pitch );
|
||||
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
}
|
||||
|
||||
static int
|
||||
drawing_area_resize( int width, int height, int force_scaler )
|
||||
{
|
||||
int size;
|
||||
|
||||
size = width / DISPLAY_ASPECT_WIDTH;
|
||||
if( size > height / DISPLAY_SCREEN_HEIGHT )
|
||||
size = height / DISPLAY_SCREEN_HEIGHT;
|
||||
|
||||
/* If we're the same size as before, no need to do anything else */
|
||||
if( size == gtkdisplay_current_size ) return 0;
|
||||
|
||||
gtkdisplay_current_size = size;
|
||||
|
||||
register_scalers( force_scaler );
|
||||
|
||||
memset( scaled_image, 0, sizeof( scaled_image ) );
|
||||
|
||||
ensure_appropriate_surface();
|
||||
|
||||
display_refresh_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
register_scalers( int force_scaler )
|
||||
{
|
||||
scaler_type scaler;
|
||||
float drawing_area_scale, scaling_factor;
|
||||
|
||||
scaler_register_clear();
|
||||
|
||||
if( machine_current->timex ) {
|
||||
scaler_register( SCALER_HALF );
|
||||
scaler_register( SCALER_HALFSKIP );
|
||||
scaler_register( SCALER_TIMEXTV );
|
||||
scaler_register( SCALER_TIMEX1_5X );
|
||||
scaler_register( SCALER_TIMEX2X );
|
||||
} else {
|
||||
scaler_register( SCALER_DOUBLESIZE );
|
||||
scaler_register( SCALER_TRIPLESIZE );
|
||||
scaler_register( SCALER_QUADSIZE );
|
||||
scaler_register( SCALER_TV2X );
|
||||
scaler_register( SCALER_TV3X );
|
||||
scaler_register( SCALER_TV4X );
|
||||
scaler_register( SCALER_PALTV2X );
|
||||
scaler_register( SCALER_PALTV3X );
|
||||
scaler_register( SCALER_PALTV4X );
|
||||
scaler_register( SCALER_HQ2X );
|
||||
scaler_register( SCALER_HQ3X );
|
||||
scaler_register( SCALER_HQ4X );
|
||||
scaler_register( SCALER_ADVMAME2X );
|
||||
scaler_register( SCALER_ADVMAME3X );
|
||||
scaler_register( SCALER_2XSAI );
|
||||
scaler_register( SCALER_SUPER2XSAI );
|
||||
scaler_register( SCALER_SUPEREAGLE );
|
||||
scaler_register( SCALER_DOTMATRIX );
|
||||
}
|
||||
scaler_register( SCALER_NORMAL );
|
||||
scaler_register( SCALER_PALTV );
|
||||
|
||||
scaler =
|
||||
scaler_is_supported( current_scaler ) ? current_scaler : SCALER_NORMAL;
|
||||
|
||||
drawing_area_scale = (float)gtkdisplay_current_size / image_scale;
|
||||
scaling_factor = scaler_get_scaling_factor( current_scaler );
|
||||
|
||||
/* Override scaler if the image doesn't fit well in the drawing area */
|
||||
if( force_scaler && drawing_area_scale != scaling_factor ) {
|
||||
|
||||
switch( gtkdisplay_current_size ) {
|
||||
case 1: scaler = machine_current->timex ? SCALER_HALF : SCALER_NORMAL;
|
||||
break;
|
||||
case 2: scaler = machine_current->timex ? SCALER_NORMAL : SCALER_DOUBLESIZE;
|
||||
break;
|
||||
case 3: scaler = machine_current->timex ? SCALER_TIMEX1_5X :
|
||||
SCALER_TRIPLESIZE;
|
||||
break;
|
||||
case 4: scaler = machine_current->timex ? SCALER_TIMEX2X :
|
||||
SCALER_QUADSIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scaler_select_scaler( scaler );
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_frame_end( void )
|
||||
{
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
if( display_updated ) {
|
||||
gdk_window_process_updates( gtk_widget_get_window( gtkui_drawing_area ),
|
||||
FALSE );
|
||||
display_updated = 0;
|
||||
}
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_area( int x, int y, int w, int h )
|
||||
{
|
||||
float scale = (float)gtkdisplay_current_size / image_scale;
|
||||
int scaled_x, scaled_y, i, yy;
|
||||
libspectrum_dword *palette;
|
||||
|
||||
/* 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 );
|
||||
|
||||
scaled_x = scale * x; scaled_y = scale * y;
|
||||
|
||||
palette = settings_current.bw_tv ? bw_colours : gtkdisplay_colours;
|
||||
|
||||
/* Create the RGB image */
|
||||
for( yy = y; yy < y + h; yy++ ) {
|
||||
|
||||
libspectrum_dword *rgb; libspectrum_word *display;
|
||||
|
||||
rgb = (libspectrum_dword*)( rgb_image + ( yy + 2 ) * rgb_pitch );
|
||||
rgb += x + 1;
|
||||
|
||||
display = >kdisplay_image[yy][x];
|
||||
|
||||
for( i = 0; i < w; i++, rgb++, display++ ) *rgb = palette[ *display ];
|
||||
}
|
||||
|
||||
/* Create scaled image */
|
||||
scaler_proc32( &rgb_image[ ( y + 2 ) * rgb_pitch + 4 * ( x + 1 ) ],
|
||||
rgb_pitch,
|
||||
&scaled_image[ scaled_y * scaled_pitch + 4 * scaled_x ],
|
||||
scaled_pitch, w, h );
|
||||
|
||||
w *= scale; h *= scale;
|
||||
|
||||
/* Blit to the real screen */
|
||||
gtkdisplay_area( scaled_x, scaled_y, w, h );
|
||||
}
|
||||
|
||||
static void gtkdisplay_area(int x, int y, int width, int height)
|
||||
{
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
gdk_draw_rgb_32_image( gtkui_drawing_area->window,
|
||||
gtkui_drawing_area->style->fg_gc[GTK_STATE_NORMAL],
|
||||
x, y, width, height, GDK_RGB_DITHER_NONE,
|
||||
&scaled_image[ y * scaled_pitch + 4 * x ],
|
||||
scaled_pitch );
|
||||
|
||||
#else
|
||||
display_updated = 1;
|
||||
|
||||
gtk_widget_queue_draw_area( gtkui_drawing_area, x, y, width, height );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_hotswap_gfx_mode( void )
|
||||
{
|
||||
fuse_emulation_pause();
|
||||
|
||||
/* Setup the new GFX mode */
|
||||
gtkdisplay_load_gfx_mode();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_end( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set one pixel in the display */
|
||||
void
|
||||
uidisplay_putpixel( int x, int y, int colour )
|
||||
{
|
||||
if( machine_current->timex ) {
|
||||
x <<= 1; y <<= 1;
|
||||
gtkdisplay_image[y ][x ] = colour;
|
||||
gtkdisplay_image[y ][x+1] = colour;
|
||||
gtkdisplay_image[y+1][x ] = colour;
|
||||
gtkdisplay_image[y+1][x+1] = colour;
|
||||
} else {
|
||||
gtkdisplay_image[y][x] = colour;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
x <<= 3;
|
||||
|
||||
if( machine_current->timex ) {
|
||||
int i;
|
||||
|
||||
x <<= 1; y <<= 1;
|
||||
for( i=0; i<2; i++,y++ ) {
|
||||
gtkdisplay_image[y][x+ 0] = ( data & 0x80 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 1] = ( data & 0x80 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 2] = ( data & 0x40 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 3] = ( data & 0x40 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 4] = ( data & 0x20 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 5] = ( data & 0x20 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 6] = ( data & 0x10 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 7] = ( data & 0x10 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 8] = ( data & 0x08 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 9] = ( data & 0x08 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+10] = ( data & 0x04 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+11] = ( data & 0x04 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+12] = ( data & 0x02 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+13] = ( data & 0x02 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+14] = ( data & 0x01 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+15] = ( data & 0x01 ) ? ink : paper;
|
||||
}
|
||||
} else {
|
||||
gtkdisplay_image[y][x+ 0] = ( data & 0x80 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 1] = ( data & 0x40 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 2] = ( data & 0x20 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 3] = ( data & 0x10 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 4] = ( data & 0x08 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 5] = ( data & 0x04 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 6] = ( data & 0x02 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 7] = ( data & 0x01 ) ? ink : paper;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
int i;
|
||||
x <<= 4; y <<= 1;
|
||||
|
||||
for( i=0; i<2; i++,y++ ) {
|
||||
gtkdisplay_image[y][x+ 0] = ( data & 0x8000 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 1] = ( data & 0x4000 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 2] = ( data & 0x2000 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 3] = ( data & 0x1000 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 4] = ( data & 0x0800 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 5] = ( data & 0x0400 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 6] = ( data & 0x0200 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 7] = ( data & 0x0100 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 8] = ( data & 0x0080 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+ 9] = ( data & 0x0040 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+10] = ( data & 0x0020 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+11] = ( data & 0x0010 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+12] = ( data & 0x0008 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+13] = ( data & 0x0004 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+14] = ( data & 0x0002 ) ? ink : paper;
|
||||
gtkdisplay_image[y][x+15] = ( data & 0x0001 ) ? ink : paper;
|
||||
}
|
||||
}
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
/* Called by gtkui_drawing_area on "expose_event" */
|
||||
static gint
|
||||
gtkdisplay_expose( GtkWidget *widget GCC_UNUSED, GdkEvent *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
gtkdisplay_area(event->expose.area.x, event->expose.area.y,
|
||||
event->expose.area.width, event->expose.area.height);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Called by gtkui_drawing_area on "configure_event".
|
||||
On GTK 2 the drawing_area determines the size of the window */
|
||||
static gint
|
||||
drawing_area_resize_callback( GtkWidget *widget GCC_UNUSED, GdkEvent *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
drawing_area_resize( event->configure.width, event->configure.height, 1 );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#else /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
/* Called by gtkui_drawing_area on "draw" event */
|
||||
static gboolean
|
||||
gtkdisplay_draw( GtkWidget *widget, cairo_t *cr, gpointer user_data )
|
||||
{
|
||||
/* Create a new surface for this gfx mode */
|
||||
if( !surface ) ensure_appropriate_surface();
|
||||
|
||||
/* Repaint the drawing area */
|
||||
cairo_set_source_surface( cr, surface, 0, 0 );
|
||||
cairo_set_operator( cr, CAIRO_OPERATOR_SOURCE );
|
||||
cairo_paint( cr );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called by gtkui_window on "configure_event".
|
||||
On GTK 3 the window determines the size of the drawing area */
|
||||
static gint
|
||||
drawing_area_resize_callback( GtkWidget *widget GCC_UNUSED, GdkEvent *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
drawing_area_resize( event->configure.width,
|
||||
event->configure.height - extra_height, 1 );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
void
|
||||
gtkdisplay_update_geometry( void )
|
||||
{
|
||||
GdkGeometry geometry;
|
||||
GdkWindowHints hints;
|
||||
GtkWidget *geometry_widget;
|
||||
float scale;
|
||||
|
||||
if( !scalers_registered ) return;
|
||||
|
||||
scale = scaler_get_scaling_factor( current_scaler );
|
||||
|
||||
hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE |
|
||||
GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC;
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
/* Since GTK 3.20 it is intended that gtk_window_set_geometry_hints
|
||||
don't set geometry of widgets. See [bugs:#344] */
|
||||
geometry_widget = NULL;
|
||||
|
||||
/* Add extra space for menu bar */
|
||||
extra_height = gtkui_menubar_get_height();
|
||||
|
||||
/* Add extra space for status bar + padding */
|
||||
if( settings_current.statusbar ) {
|
||||
extra_height += gtkstatusbar_get_height();
|
||||
}
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
/* We don't calculate the window size enough accurately on wayland
|
||||
backend to force the window geometry (bug #367) */
|
||||
GdkDisplay *display = gdk_display_get_default();
|
||||
|
||||
if( GDK_IS_WAYLAND_DISPLAY( display ) ) {
|
||||
hints &= ~GDK_HINT_RESIZE_INC;
|
||||
}
|
||||
#endif /* #ifdef GDK_WINDOWING_WAYLAND */
|
||||
|
||||
#else /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
geometry_widget = gtkui_drawing_area;
|
||||
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
|
||||
geometry.min_width = DISPLAY_ASPECT_WIDTH;
|
||||
geometry.min_height = DISPLAY_SCREEN_HEIGHT + extra_height;
|
||||
geometry.max_width = MAX_SCALE * DISPLAY_ASPECT_WIDTH;
|
||||
geometry.max_height = MAX_SCALE * DISPLAY_SCREEN_HEIGHT + extra_height;
|
||||
geometry.base_width = scale * image_width;
|
||||
geometry.base_height = scale * image_height + extra_height;
|
||||
geometry.width_inc = DISPLAY_ASPECT_WIDTH;
|
||||
geometry.height_inc = DISPLAY_SCREEN_HEIGHT;
|
||||
|
||||
if( settings_current.aspect_hint ) {
|
||||
hints |= GDK_HINT_ASPECT;
|
||||
|
||||
geometry.min_aspect = geometry.max_aspect =
|
||||
( scale * DISPLAY_ASPECT_WIDTH ) /
|
||||
( scale * DISPLAY_SCREEN_HEIGHT + extra_height );
|
||||
|
||||
if( !settings_current.strict_aspect_hint ) {
|
||||
geometry.min_aspect *= 0.9;
|
||||
geometry.max_aspect *= 1.125;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_window_set_geometry_hints( GTK_WINDOW( gtkui_window ),
|
||||
geometry_widget,
|
||||
&geometry, hints );
|
||||
}
|
||||
|
||||
static void
|
||||
gtkdisplay_load_gfx_mode( void )
|
||||
{
|
||||
float scale;
|
||||
|
||||
scale = scaler_get_scaling_factor( current_scaler );
|
||||
|
||||
gtkdisplay_update_geometry();
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
/* This function should be innocuous when the main window is shown */
|
||||
gtk_window_set_default_size( GTK_WINDOW( gtkui_window ),
|
||||
scale * image_width,
|
||||
scale * image_height + extra_height );
|
||||
|
||||
drawing_area_resize( scale * image_width, scale * image_height, 0 );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
gtk_window_resize( GTK_WINDOW( gtkui_window ), scale * image_width,
|
||||
scale * image_height + extra_height );
|
||||
|
||||
/* Redraw the entire screen... */
|
||||
display_refresh_all();
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/* gtkinternals.h: stuff internal to the GTK UI
|
||||
Copyright (c) 2003-2015 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FUSE_GTKINTERNALS_H
|
||||
#define FUSE_GTKINTERNALS_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libspectrum.h>
|
||||
|
||||
/*
|
||||
* Display routines (gtkdisplay.c)
|
||||
*/
|
||||
|
||||
/* The colour palette in use */
|
||||
extern libspectrum_dword gtkdisplay_colours[ 16 ];
|
||||
|
||||
void gtkdisplay_update_geometry( void );
|
||||
|
||||
/*
|
||||
* Keyboard routines (gtkkeyboard.c)
|
||||
*/
|
||||
|
||||
int gtkkeyboard_keypress( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data);
|
||||
int gtkkeyboard_keyrelease( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data);
|
||||
int gtkkeyboard_release_all( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data );
|
||||
|
||||
/*
|
||||
* Mouse routines (gtkmouse.c)
|
||||
*/
|
||||
|
||||
void gtkmouse_init( void );
|
||||
|
||||
/*
|
||||
* General user interface routines (gtkui.c)
|
||||
*/
|
||||
|
||||
extern GtkWidget *gtkui_window;
|
||||
extern GtkWidget *gtkui_drawing_area;
|
||||
|
||||
void gtkui_destroy_widget_and_quit( GtkWidget *widget, gpointer data );
|
||||
|
||||
int gtkui_confirm( const char *string );
|
||||
|
||||
int gtkui_picture( const char *filename, int border );
|
||||
|
||||
extern void gtkui_popup_menu(void);
|
||||
|
||||
GtkAccelGroup* gtkstock_add_accel_group( GtkWidget *widget );
|
||||
|
||||
/* Set modifier=0 to use the first default accel key.
|
||||
* Set modifier_alt=0 to use the second default accel key.
|
||||
* For either, GDK_KEY_VoidSymbol means "no accel key".
|
||||
*/
|
||||
typedef struct gtkstock_button {
|
||||
const gchar *label;
|
||||
GCallback action; /* "clicked" func; data is actiondata. */
|
||||
gpointer actiondata;
|
||||
GCallback destroy; /* "clicked" func; data is parent widget */
|
||||
guint shortcut;
|
||||
GdkModifierType modifier; /* primary shortcut */
|
||||
guint shortcut_alt;
|
||||
GdkModifierType modifier_alt; /* secondary shortcut */
|
||||
gint response_id; /* response id for dialog */
|
||||
} gtkstock_button;
|
||||
|
||||
/* GTK1: create a simple button with the given label.
|
||||
* "gtk-" prefixes are stripped and are used to select default accel keys.
|
||||
* GTK2: chooses between stock and normal based on "gtk-" prefix.
|
||||
* GTK3: GtkStock has been deprecated, we should not use "gtk-" prefix.
|
||||
*
|
||||
* If the target widget is a GtkDialog, then created buttons are put in its
|
||||
* action area.
|
||||
*
|
||||
* If the label begins with "!", then g_signal_connect_swapped, rather than
|
||||
* g_signal_connect, is used to connect the action function.
|
||||
*/
|
||||
GtkWidget* gtkstock_create_button( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
const gtkstock_button *btn );
|
||||
GtkAccelGroup*
|
||||
gtkstock_create_buttons( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
const gtkstock_button *buttons, size_t count );
|
||||
GtkAccelGroup* gtkstock_create_ok_cancel( GtkWidget *widget,
|
||||
GtkAccelGroup *accel,
|
||||
/* for OK button -> */ GCallback action,
|
||||
gpointer actiondata,
|
||||
/* for both buttons -> */ GCallback destroy_ok,
|
||||
GCallback destroy_cancel );
|
||||
GtkAccelGroup* gtkstock_create_close( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
GCallback destroy,
|
||||
gboolean esconly );
|
||||
/* destroy==NULL => use DEFAULT_DESTROY */
|
||||
|
||||
#define DEFAULT_DESTROY ( G_CALLBACK( gtkui_destroy_widget_and_quit ) )
|
||||
|
||||
GtkWidget *gtkstock_dialog_new( const gchar *title, GCallback destroy );
|
||||
|
||||
int gtkui_get_monospaced_font( PangoFontDescription **font );
|
||||
void gtkui_free_font( PangoFontDescription *font );
|
||||
|
||||
int gtkui_menubar_get_height( void );
|
||||
|
||||
/*
|
||||
* The menu data (menu_data.c)
|
||||
*/
|
||||
|
||||
extern GtkActionEntry gtkui_menu_data[];
|
||||
extern guint gtkui_menu_data_size;
|
||||
|
||||
/*
|
||||
* The icon pixmaps (pixmaps.c)
|
||||
*/
|
||||
extern const char *gtkpixmap_tape_inactive[];
|
||||
extern const char *gtkpixmap_tape_active[];
|
||||
extern const char *gtkpixmap_mdr_inactive[];
|
||||
extern const char *gtkpixmap_mdr_active[];
|
||||
extern const char *gtkpixmap_disk_inactive[];
|
||||
extern const char *gtkpixmap_disk_active[];
|
||||
extern const char *gtkpixmap_pause_inactive[];
|
||||
extern const char *gtkpixmap_pause_active[];
|
||||
extern const char *gtkpixmap_tape_marker[];
|
||||
extern const char *gtkpixmap_mouse_inactive[];
|
||||
extern const char *gtkpixmap_mouse_active[];
|
||||
|
||||
/*
|
||||
* Statusbar routines (statusbar.c)
|
||||
*/
|
||||
|
||||
|
||||
int gtkstatusbar_create( GtkBox *parent );
|
||||
int gtkstatusbar_get_height( void );
|
||||
int gtkstatusbar_set_visibility( int visible );
|
||||
void gtkstatusbar_update_machine( const char *name );
|
||||
|
||||
/*
|
||||
* Routines for list widgets
|
||||
*/
|
||||
|
||||
void gtkui_scroll_connect( GtkTreeView *list, GtkAdjustment *adj );
|
||||
void gtkui_list_set_cursor( GtkTreeView *list, int row );
|
||||
int gtkui_list_get_cursor( GtkTreeView *list );
|
||||
|
||||
/*
|
||||
* Dialog box reset
|
||||
*/
|
||||
|
||||
void gtkui_pokefinder_clear( void );
|
||||
|
||||
#endif /* #ifndef FUSE_GTKINTERNALS_H */
|
||||
@@ -1,466 +0,0 @@
|
||||
/* gtkjoystick.c: Joystick emulation
|
||||
Copyright (c) 2003-2015 Darren Salt, Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
Darren: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "keyboard.h"
|
||||
#include "peripherals/joystick.h"
|
||||
#include "menu.h"
|
||||
#include "settings.h"
|
||||
|
||||
#if !defined USE_JOYSTICK || defined HAVE_JSW_H
|
||||
/* Fake joystick, or override UI-specific handling */
|
||||
#include "../uijoystick.c"
|
||||
|
||||
#else /* #if !defined USE_JOYSTICK || defined HAVE_JSW_H */
|
||||
|
||||
#include "../sdl/sdljoystick.c"
|
||||
|
||||
#endif /* #if !defined USE_JOYSTICK || defined HAVE_JSW_H */
|
||||
|
||||
enum
|
||||
{
|
||||
COL_TEXT = 0,
|
||||
COL_KEY,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
struct button_info {
|
||||
int *setting;
|
||||
char name[80];
|
||||
GtkWidget *label;
|
||||
keyboard_key_name key;
|
||||
};
|
||||
|
||||
struct joystick_info {
|
||||
|
||||
int *type;
|
||||
GtkWidget *radio[ JOYSTICK_TYPE_COUNT ];
|
||||
|
||||
struct button_info button[NUM_JOY_BUTTONS];
|
||||
};
|
||||
|
||||
typedef enum key_item_t {
|
||||
ITEM = 0,
|
||||
KEY_GROUP,
|
||||
SUBITEM,
|
||||
} key_item_t;
|
||||
|
||||
typedef struct key_menu_t {
|
||||
key_item_t item;
|
||||
const gchar *text;
|
||||
keyboard_key_name key;
|
||||
} key_menu_t;
|
||||
|
||||
static void setup_info( struct joystick_info *info, int callback_action );
|
||||
static void create_joystick_type_selector( struct joystick_info *info,
|
||||
GtkBox *parent );
|
||||
static void
|
||||
create_fire_button_selector( const char *title, struct button_info *info,
|
||||
GtkBox *parent, GtkTreeModel *model );
|
||||
static void set_key_text( GtkWidget *label, keyboard_key_name key );
|
||||
|
||||
static void key_callback( GtkComboBox *widget, gpointer user_data );
|
||||
static void joystick_done( GtkButton *button, gpointer user_data );
|
||||
|
||||
static key_menu_t key_menu[] = {
|
||||
|
||||
{ ITEM, "Joystick Fire", KEYBOARD_JOYSTICK_FIRE },
|
||||
|
||||
{ KEY_GROUP, "Numbers", KEYBOARD_NONE },
|
||||
{ SUBITEM, "0", KEYBOARD_0 },
|
||||
{ SUBITEM, "1", KEYBOARD_1 },
|
||||
{ SUBITEM, "2", KEYBOARD_2 },
|
||||
{ SUBITEM, "3", KEYBOARD_3 },
|
||||
{ SUBITEM, "4", KEYBOARD_4 },
|
||||
{ SUBITEM, "5", KEYBOARD_5 },
|
||||
{ SUBITEM, "6", KEYBOARD_6 },
|
||||
{ SUBITEM, "7", KEYBOARD_7 },
|
||||
{ SUBITEM, "8", KEYBOARD_8 },
|
||||
{ SUBITEM, "9", KEYBOARD_9 },
|
||||
|
||||
{ KEY_GROUP, "A - M", KEYBOARD_NONE },
|
||||
{ SUBITEM, "A", KEYBOARD_a },
|
||||
{ SUBITEM, "B", KEYBOARD_b },
|
||||
{ SUBITEM, "C", KEYBOARD_c },
|
||||
{ SUBITEM, "D", KEYBOARD_d },
|
||||
{ SUBITEM, "E", KEYBOARD_e },
|
||||
{ SUBITEM, "F", KEYBOARD_f },
|
||||
{ SUBITEM, "G", KEYBOARD_g },
|
||||
{ SUBITEM, "H", KEYBOARD_h },
|
||||
{ SUBITEM, "I", KEYBOARD_i },
|
||||
{ SUBITEM, "J", KEYBOARD_j },
|
||||
{ SUBITEM, "K", KEYBOARD_k },
|
||||
{ SUBITEM, "L", KEYBOARD_l },
|
||||
{ SUBITEM, "M", KEYBOARD_m },
|
||||
|
||||
{ KEY_GROUP, "N - Z", KEYBOARD_NONE },
|
||||
{ SUBITEM, "N", KEYBOARD_n },
|
||||
{ SUBITEM, "O", KEYBOARD_o },
|
||||
{ SUBITEM, "P", KEYBOARD_p },
|
||||
{ SUBITEM, "Q", KEYBOARD_q },
|
||||
{ SUBITEM, "R", KEYBOARD_r },
|
||||
{ SUBITEM, "S", KEYBOARD_s },
|
||||
{ SUBITEM, "T", KEYBOARD_t },
|
||||
{ SUBITEM, "U", KEYBOARD_u },
|
||||
{ SUBITEM, "V", KEYBOARD_v },
|
||||
{ SUBITEM, "W", KEYBOARD_w },
|
||||
{ SUBITEM, "X", KEYBOARD_x },
|
||||
{ SUBITEM, "Y", KEYBOARD_y },
|
||||
{ SUBITEM, "Z", KEYBOARD_z },
|
||||
|
||||
{ ITEM, "Space", KEYBOARD_space },
|
||||
{ ITEM, "Enter", KEYBOARD_Enter },
|
||||
{ ITEM, "Caps Shift", KEYBOARD_Caps },
|
||||
{ ITEM, "Symbol Shift", KEYBOARD_Symbol },
|
||||
{ ITEM, "Nothing", KEYBOARD_NONE },
|
||||
|
||||
};
|
||||
|
||||
static GtkTreeModel *
|
||||
create_joystick_options_store( void )
|
||||
{
|
||||
GtkTreeIter iter, iter2;
|
||||
GtkTreeStore *store;
|
||||
guint i;
|
||||
|
||||
store = gtk_tree_store_new( NUM_COLS, G_TYPE_STRING, G_TYPE_INT );
|
||||
|
||||
for( i = 0; i < ARRAY_SIZE( key_menu ); i++ ) {
|
||||
|
||||
switch( key_menu[i].item ) {
|
||||
|
||||
case ITEM:
|
||||
case KEY_GROUP:
|
||||
gtk_tree_store_append( store, &iter, NULL );
|
||||
gtk_tree_store_set( store, &iter,
|
||||
COL_TEXT, key_menu[i].text,
|
||||
COL_KEY, key_menu[i].key,
|
||||
-1 );
|
||||
break;
|
||||
|
||||
case SUBITEM:
|
||||
gtk_tree_store_append( store, &iter2, &iter );
|
||||
gtk_tree_store_set( store, &iter2,
|
||||
COL_TEXT, key_menu[i].text,
|
||||
COL_KEY, key_menu[i].key,
|
||||
-1 );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return GTK_TREE_MODEL( store );
|
||||
}
|
||||
|
||||
void
|
||||
menu_options_joysticks_select( GtkAction *gtk_action GCC_UNUSED,
|
||||
guint callback_action )
|
||||
{
|
||||
GtkWidget *dialog, *hbox, *vbox, *content_area;
|
||||
GtkTreeModel *model;
|
||||
struct joystick_info info;
|
||||
int i;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
setup_info( &info, callback_action );
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Configure Joystick", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 4 );
|
||||
gtk_container_set_border_width( GTK_CONTAINER( hbox ), 4 );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), hbox, FALSE, FALSE, 0 );
|
||||
|
||||
create_joystick_type_selector( &info, GTK_BOX( hbox ) );
|
||||
|
||||
vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 2 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), vbox, TRUE, TRUE, 0 );
|
||||
|
||||
model = create_joystick_options_store();
|
||||
|
||||
for( i = 0; i < NUM_JOY_BUTTONS; i += 5 ) {
|
||||
|
||||
int j;
|
||||
|
||||
vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 2 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), vbox, TRUE, TRUE, 0 );
|
||||
|
||||
for( j = i; j < i + 5; j++ )
|
||||
if( info.button[j].setting ) {
|
||||
create_fire_button_selector( info.button[j].name, &( info.button[j] ),
|
||||
GTK_BOX( vbox ), model );
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref( model );
|
||||
|
||||
gtkstock_create_ok_cancel( dialog, NULL, G_CALLBACK( joystick_done ),
|
||||
&info, DEFAULT_DESTROY, DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog ), GTK_RESPONSE_OK );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
static void
|
||||
setup_info( struct joystick_info *info, int callback_action )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
switch( callback_action ) {
|
||||
|
||||
case 1:
|
||||
info->type = &( settings_current.joystick_1_output );
|
||||
info->button[0].setting = &( settings_current.joystick_1_fire_1 );
|
||||
info->button[1].setting = &( settings_current.joystick_1_fire_2 );
|
||||
info->button[2].setting = &( settings_current.joystick_1_fire_3 );
|
||||
info->button[3].setting = &( settings_current.joystick_1_fire_4 );
|
||||
info->button[4].setting = &( settings_current.joystick_1_fire_5 );
|
||||
info->button[5].setting = &( settings_current.joystick_1_fire_6 );
|
||||
info->button[6].setting = &( settings_current.joystick_1_fire_7 );
|
||||
info->button[7].setting = &( settings_current.joystick_1_fire_8 );
|
||||
info->button[8].setting = &( settings_current.joystick_1_fire_9 );
|
||||
info->button[9].setting = &( settings_current.joystick_1_fire_10 );
|
||||
info->button[10].setting = &( settings_current.joystick_1_fire_11 );
|
||||
info->button[11].setting = &( settings_current.joystick_1_fire_12 );
|
||||
info->button[12].setting = &( settings_current.joystick_1_fire_13 );
|
||||
info->button[13].setting = &( settings_current.joystick_1_fire_14 );
|
||||
info->button[14].setting = &( settings_current.joystick_1_fire_15 );
|
||||
for( i = 0; i < NUM_JOY_BUTTONS; i++ )
|
||||
snprintf( info->button[i].name, 80, "Button %lu", (unsigned long)i + 1 );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->type = &( settings_current.joystick_2_output );
|
||||
info->button[0].setting = &( settings_current.joystick_2_fire_1 );
|
||||
info->button[1].setting = &( settings_current.joystick_2_fire_2 );
|
||||
info->button[2].setting = &( settings_current.joystick_2_fire_3 );
|
||||
info->button[3].setting = &( settings_current.joystick_2_fire_4 );
|
||||
info->button[4].setting = &( settings_current.joystick_2_fire_5 );
|
||||
info->button[5].setting = &( settings_current.joystick_2_fire_6 );
|
||||
info->button[6].setting = &( settings_current.joystick_2_fire_7 );
|
||||
info->button[7].setting = &( settings_current.joystick_2_fire_8 );
|
||||
info->button[8].setting = &( settings_current.joystick_2_fire_9 );
|
||||
info->button[9].setting = &( settings_current.joystick_2_fire_10 );
|
||||
info->button[10].setting = &( settings_current.joystick_2_fire_10 );
|
||||
info->button[11].setting = &( settings_current.joystick_2_fire_11 );
|
||||
info->button[12].setting = &( settings_current.joystick_2_fire_12 );
|
||||
info->button[13].setting = &( settings_current.joystick_2_fire_13 );
|
||||
info->button[14].setting = &( settings_current.joystick_2_fire_14 );
|
||||
for( i = 0; i < NUM_JOY_BUTTONS; i++ )
|
||||
snprintf( info->button[i].name, 80, "Button %lu", (unsigned long)i + 1 );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
info->type = &( settings_current.joystick_keyboard_output );
|
||||
info->button[0].setting = &( settings_current.joystick_keyboard_up );
|
||||
snprintf( info->button[0].name, 80, "Button for UP" );
|
||||
info->button[1].setting = &( settings_current.joystick_keyboard_down );
|
||||
snprintf( info->button[1].name, 80, "Button for DOWN" );
|
||||
info->button[2].setting = &( settings_current.joystick_keyboard_left );
|
||||
snprintf( info->button[2].name, 80, "Button for LEFT" );
|
||||
info->button[3].setting = &( settings_current.joystick_keyboard_right );
|
||||
snprintf( info->button[3].name, 80, "Button for RIGHT" );
|
||||
info->button[4].setting = &( settings_current.joystick_keyboard_fire );
|
||||
snprintf( info->button[4].name, 80, "Button for FIRE" );
|
||||
for( i = 5; i < NUM_JOY_BUTTONS; i++ ) info->button[i].setting = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_joystick_type_selector( struct joystick_info *info, GtkBox *parent )
|
||||
{
|
||||
GtkWidget *frame, *box;
|
||||
GSList *button_group;
|
||||
int i;
|
||||
|
||||
frame = gtk_frame_new( "Joystick type" );
|
||||
gtk_box_pack_start( parent, frame, FALSE, FALSE, 0 );
|
||||
|
||||
box = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_container_add( GTK_CONTAINER( frame ), box );
|
||||
|
||||
button_group = NULL;
|
||||
|
||||
for( i = 0; i < JOYSTICK_TYPE_COUNT; i++ ) {
|
||||
|
||||
info->radio[ i ] =
|
||||
gtk_radio_button_new_with_label( button_group, joystick_name[ i ] );
|
||||
button_group =
|
||||
gtk_radio_button_get_group( GTK_RADIO_BUTTON( info->radio[ i ] ) );
|
||||
gtk_box_pack_start( GTK_BOX( box ), info->radio[ i ], FALSE, FALSE, 0 );
|
||||
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( info->radio[ i ] ),
|
||||
i == *( info->type ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
set_entry_properties( GtkCellLayout *cell_layout GCC_UNUSED,
|
||||
GtkCellRenderer *cell, GtkTreeModel *tree_model,
|
||||
GtkTreeIter *iter, gpointer data GCC_UNUSED )
|
||||
{
|
||||
gboolean sensitive;
|
||||
|
||||
sensitive = !gtk_tree_model_iter_has_child( tree_model, iter );
|
||||
|
||||
g_object_set( cell, "sensitive", sensitive, NULL );
|
||||
|
||||
g_object_set( cell, "xpad", 10, NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
create_fire_button_selector( const char *title, struct button_info *info,
|
||||
GtkBox *parent, GtkTreeModel *model )
|
||||
{
|
||||
GtkWidget *frame, *box, *combo;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
size_t i;
|
||||
|
||||
frame = gtk_frame_new( title );
|
||||
gtk_box_pack_start( parent, frame, TRUE, TRUE, 0 );
|
||||
|
||||
box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 4 );
|
||||
gtk_container_set_border_width( GTK_CONTAINER( box ), 2 );
|
||||
gtk_container_add( GTK_CONTAINER( frame ), box );
|
||||
|
||||
/* Create label */
|
||||
info->key = *info->setting;
|
||||
info->label = gtk_label_new( "" );
|
||||
|
||||
for( i = 0; i < ARRAY_SIZE( key_menu ); i++ ) {
|
||||
|
||||
keyboard_key_name key;
|
||||
|
||||
key = key_menu[i].key;
|
||||
|
||||
if( key_menu[i].item != KEY_GROUP && key == (unsigned int)*info->setting ) {
|
||||
set_key_text( info->label, key );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( box ), info->label, TRUE, TRUE, 0 );
|
||||
|
||||
/* Create combobox */
|
||||
combo = gtk_combo_box_new_with_model( model );
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( combo ), renderer, TRUE );
|
||||
gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( combo ), renderer,
|
||||
"text", 0, NULL );
|
||||
gtk_cell_layout_set_cell_data_func( GTK_CELL_LAYOUT( combo ), renderer,
|
||||
set_entry_properties, NULL, NULL );
|
||||
|
||||
/* Select first item */
|
||||
path = gtk_tree_path_new_from_indices( 0, -1 );
|
||||
gtk_tree_model_get_iter( model, &iter, path );
|
||||
gtk_tree_path_free( path );
|
||||
gtk_combo_box_set_active_iter( GTK_COMBO_BOX( combo ), &iter );
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( box ), combo, TRUE, TRUE, 0 );
|
||||
|
||||
g_signal_connect( G_OBJECT( combo ), "changed", G_CALLBACK( key_callback ),
|
||||
info );
|
||||
}
|
||||
|
||||
static void
|
||||
set_key_text( GtkWidget *label, keyboard_key_name key )
|
||||
{
|
||||
const char *text;
|
||||
char buffer[40];
|
||||
|
||||
text = keyboard_key_text( key );
|
||||
|
||||
snprintf( buffer, 40, "%s", text );
|
||||
|
||||
gtk_label_set_text( GTK_LABEL( label ), buffer );
|
||||
}
|
||||
|
||||
static void
|
||||
key_callback( GtkComboBox *widget, gpointer user_data )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GValue value;
|
||||
struct button_info *info = user_data;
|
||||
keyboard_key_name key;
|
||||
GtkTreeModel *model;
|
||||
|
||||
/* Get current selection */
|
||||
gtk_combo_box_get_active_iter( GTK_COMBO_BOX( widget ), &iter );
|
||||
memset( &value, 0, sizeof( value ) );
|
||||
model = gtk_combo_box_get_model( GTK_COMBO_BOX( widget ) );
|
||||
gtk_tree_model_get_value( model, &iter, COL_KEY, &value );
|
||||
key = g_value_get_int( &value );
|
||||
g_value_unset( &value );
|
||||
|
||||
/* Store and display selection */
|
||||
info->key = key;
|
||||
set_key_text( info->label, info->key );
|
||||
}
|
||||
|
||||
static void
|
||||
joystick_done( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
struct joystick_info *info = user_data;
|
||||
|
||||
int i;
|
||||
GtkToggleButton *toggle;
|
||||
|
||||
for( i = 0; i < NUM_JOY_BUTTONS; i++ )
|
||||
if( info->button[i].setting )
|
||||
*info->button[i].setting = info->button[i].key;
|
||||
|
||||
for( i = 0; i < JOYSTICK_TYPE_COUNT; i++ ) {
|
||||
|
||||
toggle = GTK_TOGGLE_BUTTON( info->radio[ i ] );
|
||||
|
||||
if( gtk_toggle_button_get_active( toggle ) ) {
|
||||
*( info->type ) = i;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/* gtkkeyboard.c: GTK routines for dealing with the keyboard
|
||||
Copyright (c) 2000-2008 Philip Kendall, Russell Marks
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "input.h"
|
||||
#include "keyboard.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
/* Given a hardware keycode, return the keyval which would have been returned if
|
||||
the key were unshifted */
|
||||
static guint
|
||||
unshift_keysym( guint keycode, gint group )
|
||||
{
|
||||
GdkKeymapKey *maps;
|
||||
guint *keyvals, i, r = GDK_KEY_VoidSymbol, r2 = GDK_KEY_VoidSymbol;
|
||||
gint count;
|
||||
|
||||
gdk_keymap_get_entries_for_keycode( gdk_keymap_get_default(), keycode,
|
||||
&maps, &keyvals, &count );
|
||||
|
||||
for( i = 0; i < count; i++ ) {
|
||||
if( maps[i].group == group && maps[i].level == 0 ) {
|
||||
r = keyvals[i];
|
||||
break;
|
||||
}
|
||||
if( maps[i].group == 0 && maps[i].level == 0 ) {
|
||||
r2 = keyvals[i];
|
||||
}
|
||||
}
|
||||
if (i == count)
|
||||
r = r2;
|
||||
|
||||
g_free( keyvals ); g_free( maps );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
get_keysyms( input_event_t *event, guint keycode, guint keysym, gint group )
|
||||
{
|
||||
guint unshifted;
|
||||
|
||||
/* The GTK UI doesn't actually use the native keysym for anything,
|
||||
but we may as well set it up anyway as we've got it */
|
||||
event->types.key.native_key = keysyms_remap( keysym );
|
||||
|
||||
unshifted = unshift_keysym( keycode, group );
|
||||
event->types.key.spectrum_key = keysyms_remap( unshifted );
|
||||
}
|
||||
|
||||
int
|
||||
gtkkeyboard_keypress( GtkWidget *widget GCC_UNUSED, GdkEvent *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
input_event_t fuse_event;
|
||||
|
||||
if( event->key.keyval == GDK_KEY_F1 && event->key.state == 0 )
|
||||
ui_mouse_suspend();
|
||||
|
||||
fuse_event.type = INPUT_EVENT_KEYPRESS;
|
||||
get_keysyms( &fuse_event, event->key.hardware_keycode, event->key.keyval, event->key.group );
|
||||
|
||||
return input_event( &fuse_event );
|
||||
|
||||
/* FIXME: handle F1 to deal with the pop-up menu */
|
||||
}
|
||||
|
||||
int
|
||||
gtkkeyboard_keyrelease( GtkWidget *widget GCC_UNUSED, GdkEvent *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
input_event_t fuse_event;
|
||||
|
||||
fuse_event.type = INPUT_EVENT_KEYRELEASE;
|
||||
get_keysyms( &fuse_event, event->key.hardware_keycode, event->key.keyval, event->key.group );
|
||||
|
||||
return input_event( &fuse_event );
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/* gtkmouse.c: GTK routines for emulating Spectrum mice
|
||||
Copyright (c) 2004 Darren Salt
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#include "gtkinternals.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include <gdk/gdkwayland.h>
|
||||
#endif
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
/* For XWarpPointer *only* - see below */
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
static GdkCursor *nullpointer = NULL;
|
||||
|
||||
/* The widget we base our events, grabs, warping etc on */
|
||||
static GtkWidget *mouse_widget = NULL;
|
||||
|
||||
/* Translate absolute pointer coordinate to relative movement */
|
||||
static void (*mouse_motion_fn)( gdouble x, gdouble y, int *rel_x, int *rel_y );
|
||||
|
||||
#if defined GDK_WINDOWING_WAYLAND || defined GDK_WINDOWING_WIN32
|
||||
|
||||
/* On Wayland we can't warp the pointer so we keep the last position */
|
||||
static gdouble last_pos_x = 0;
|
||||
static gdouble last_pos_y = 0;
|
||||
static int have_last_position = 0;
|
||||
|
||||
static void
|
||||
mouse_motion_relative( gdouble x, gdouble y, int *rel_x, int *rel_y )
|
||||
{
|
||||
if( have_last_position ) {
|
||||
*rel_x = x - last_pos_x;
|
||||
*rel_y = y - last_pos_y;
|
||||
} else {
|
||||
*rel_x = 0;
|
||||
*rel_y = 0;
|
||||
have_last_position = 1;
|
||||
}
|
||||
|
||||
last_pos_x = x;
|
||||
last_pos_y = y;
|
||||
}
|
||||
|
||||
#endif /* if defined GDK_WINDOWING_WAYLAND ||
|
||||
defined GDK_WINDOWING_WIN32 */
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
|
||||
static void
|
||||
mouse_motion_x11( gdouble x, gdouble y, int *rel_x, int *rel_y )
|
||||
{
|
||||
*rel_x = x - 128;
|
||||
*rel_y = y - 128;
|
||||
|
||||
if( x != 128 || y != 128 ) {
|
||||
GdkWindow *window = gtk_widget_get_window( mouse_widget );
|
||||
XWarpPointer( GDK_WINDOW_XDISPLAY( window ), None,
|
||||
GDK_WINDOW_XID( window ), 0, 0, 0, 0, 128, 128 );
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #ifdef GDK_WINDOWING_X11 */
|
||||
|
||||
#ifdef GDK_WINDOWING_WIN32
|
||||
|
||||
static void
|
||||
mouse_motion_win32( gdouble x, gdouble y, int *rel_x, int *rel_y )
|
||||
{
|
||||
mouse_motion_relative( x, y, rel_x, rel_y );
|
||||
|
||||
/* Keep pointer hidden */
|
||||
SetCursor( NULL );
|
||||
}
|
||||
|
||||
#endif /* #ifdef GDK_WINDOWING_WIN32 */
|
||||
|
||||
static void
|
||||
gtkmouse_reset_pointer( void )
|
||||
{
|
||||
/* Ugh. GDK doesn't have its own move-pointer function :-|
|
||||
|
||||
The logic here is a bit hairy:
|
||||
|
||||
* On GTK 2.x, we warp relative to the drawing area
|
||||
* On GTK 3.x on X11, we warp relative to the top-level window
|
||||
* On GTK 3.x on Wayland, we don't warp at all because it causes a
|
||||
segfault (see bug #435)
|
||||
* On GTK 3.x on win32, we don't warp at all
|
||||
*/
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
|
||||
GdkDisplay *display = gdk_display_get_default();
|
||||
if( GDK_IS_WAYLAND_DISPLAY( display ) ) {
|
||||
mouse_motion_fn = mouse_motion_relative;
|
||||
have_last_position = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* #ifdef GDK_WINDOWING_WAYLAND */
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
|
||||
mouse_motion_fn = mouse_motion_x11;
|
||||
|
||||
/* Force initial position */
|
||||
GdkWindow *window = gtk_widget_get_window( mouse_widget );
|
||||
XWarpPointer( GDK_WINDOW_XDISPLAY( window ), None,
|
||||
GDK_WINDOW_XID( window ), 0, 0, 0, 0, 128, 128 );
|
||||
return;
|
||||
|
||||
#endif /* #ifdef GDK_WINDOWING_WAYLAND */
|
||||
|
||||
#ifdef GDK_WINDOWING_WIN32
|
||||
|
||||
mouse_motion_fn = mouse_motion_win32;
|
||||
have_last_position = 0;
|
||||
return;
|
||||
|
||||
#endif /* #ifdef GDK_WINDOWING_WIN32 */
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
motion_event( GtkWidget *widget GCC_UNUSED, GdkEventMotion *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
int rel_x, rel_y;
|
||||
|
||||
if( !ui_mouse_grabbed ) return FALSE;
|
||||
|
||||
/* Get relative movement from last position */
|
||||
(*mouse_motion_fn)( event->x, event->y, &rel_x, &rel_y );
|
||||
ui_mouse_motion( rel_x, rel_y );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
button_event( GtkWidget *widget GCC_UNUSED, GdkEventButton *event,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
if( event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS
|
||||
|| event->type == GDK_3BUTTON_PRESS )
|
||||
ui_mouse_button( event->button, 1 );
|
||||
else
|
||||
ui_mouse_button( event->button, 0 );
|
||||
|
||||
/* Stop other handlers only if we've grabbed the mouse */
|
||||
return ui_mouse_grabbed? TRUE : FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtkmouse_init( void )
|
||||
{
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
mouse_widget = gtkui_window;
|
||||
#else /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
mouse_widget = gtkui_drawing_area;
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
gtk_widget_add_events( GTK_WIDGET( mouse_widget ),
|
||||
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK );
|
||||
g_signal_connect( G_OBJECT( mouse_widget ), "motion-notify-event",
|
||||
G_CALLBACK( motion_event ), NULL );
|
||||
g_signal_connect( G_OBJECT( mouse_widget ), "button-press-event",
|
||||
G_CALLBACK( button_event ), NULL );
|
||||
g_signal_connect( G_OBJECT( mouse_widget ), "button-release-event",
|
||||
G_CALLBACK( button_event ), NULL );
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_grab( int startup )
|
||||
{
|
||||
GdkWindow *window;
|
||||
GdkGrabStatus status;
|
||||
|
||||
if( startup ) return 0;
|
||||
|
||||
window = gtk_widget_get_window( mouse_widget );
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 20, 0 )
|
||||
|
||||
if( !nullpointer ) {
|
||||
nullpointer = gdk_cursor_new( GDK_BLANK_CURSOR );
|
||||
}
|
||||
|
||||
status = gdk_pointer_grab( window, FALSE,
|
||||
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK,
|
||||
window, nullpointer, GDK_CURRENT_TIME );
|
||||
|
||||
#else
|
||||
|
||||
GdkDisplay *display;
|
||||
GdkSeat *seat;
|
||||
|
||||
display = gdk_window_get_display( window );
|
||||
|
||||
if( !nullpointer ) {
|
||||
nullpointer = gdk_cursor_new_for_display( display, GDK_BLANK_CURSOR );
|
||||
}
|
||||
|
||||
seat = gdk_display_get_default_seat( display );
|
||||
status = gdk_seat_grab( seat, window, GDK_SEAT_CAPABILITY_ALL_POINTING,
|
||||
FALSE, nullpointer, NULL, NULL, NULL );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 20, 0 ) */
|
||||
|
||||
if( status == GDK_GRAB_SUCCESS ) {
|
||||
gtkmouse_reset_pointer();
|
||||
ui_statusbar_update( UI_STATUSBAR_ITEM_MOUSE, UI_STATUSBAR_STATE_ACTIVE );
|
||||
return 1;
|
||||
}
|
||||
|
||||
ui_error( UI_ERROR_WARNING, "Mouse grab failed" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_release( int suspend GCC_UNUSED )
|
||||
{
|
||||
#if !GTK_CHECK_VERSION( 3, 20, 0 )
|
||||
|
||||
gdk_pointer_ungrab( GDK_CURRENT_TIME );
|
||||
|
||||
#else
|
||||
|
||||
GdkDisplay *display;
|
||||
GdkSeat *seat;
|
||||
|
||||
display = gtk_widget_get_display( mouse_widget );
|
||||
seat = gdk_display_get_default_seat( display );
|
||||
gdk_seat_ungrab( seat );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 20, 0 ) */
|
||||
|
||||
ui_statusbar_update( UI_STATUSBAR_ITEM_MOUSE, UI_STATUSBAR_STATE_INACTIVE );
|
||||
return 0;
|
||||
}
|
||||
-1000
@@ -1,1000 +0,0 @@
|
||||
/* gtkui.c: GTK routines for dealing with the user interface
|
||||
Copyright (c) 2000-2015 Philip Kendall, Russell Marks, 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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "keyboard.h"
|
||||
#include "machine.h"
|
||||
#include "machines/specplus3.h"
|
||||
#include "menu.h"
|
||||
#include "peripherals/ide/simpleide.h"
|
||||
#include "peripherals/ide/zxatasp.h"
|
||||
#include "peripherals/ide/zxcf.h"
|
||||
#include "peripherals/joystick.h"
|
||||
#include "psg.h"
|
||||
#include "rzx.h"
|
||||
#include "screenshot.h"
|
||||
#include "settings.h"
|
||||
#include "snapshot.h"
|
||||
#include "timer/timer.h"
|
||||
#include "ui/ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* The main Fuse window */
|
||||
GtkWidget *gtkui_window;
|
||||
|
||||
/* The area into which the screen will be drawn */
|
||||
GtkWidget *gtkui_drawing_area;
|
||||
|
||||
static GtkWidget *menu_bar;
|
||||
|
||||
/* The UIManager used to create the menu bar */
|
||||
GtkUIManager *ui_manager_menu = NULL;
|
||||
|
||||
/* True if we were paused via the Machine/Pause menu item */
|
||||
static int paused = 0;
|
||||
|
||||
/* Structure used by the radio button selection widgets (eg the
|
||||
graphics filter selectors and Machine/Select) */
|
||||
typedef struct gtkui_select_info {
|
||||
|
||||
GtkWidget *dialog;
|
||||
GtkWidget **buttons;
|
||||
|
||||
/* Used by the graphics filter selectors */
|
||||
scaler_available_fn selector;
|
||||
scaler_type selected;
|
||||
|
||||
/* Used by the joystick confirmation */
|
||||
ui_confirm_joystick_t joystick;
|
||||
|
||||
} gtkui_select_info;
|
||||
|
||||
static gboolean gtkui_make_menu(GtkAccelGroup **accel_group,
|
||||
GtkWidget **menu_bar,
|
||||
GtkActionEntry *menu_data,
|
||||
guint menu_data_size);
|
||||
|
||||
static gboolean gtkui_lose_focus( GtkWidget*, GdkEvent*, gpointer );
|
||||
static gboolean gtkui_gain_focus( GtkWidget*, GdkEvent*, gpointer );
|
||||
|
||||
static gboolean gtkui_delete( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data );
|
||||
|
||||
static void menu_options_filter_done( GtkWidget *widget, gpointer user_data );
|
||||
static void menu_machine_select_done( GtkWidget *widget, gpointer user_data );
|
||||
|
||||
static const GtkTargetEntry drag_types[] =
|
||||
{
|
||||
{ (gchar *)"text/uri-list", GTK_TARGET_OTHER_APP, 0 }
|
||||
};
|
||||
|
||||
static void gtkui_drag_data_received( GtkWidget *widget GCC_UNUSED,
|
||||
GdkDragContext *drag_context,
|
||||
gint x GCC_UNUSED, gint y GCC_UNUSED,
|
||||
GtkSelectionData *data,
|
||||
guint info GCC_UNUSED, guint timestamp )
|
||||
{
|
||||
static char uri_prefix[] = "file://";
|
||||
char *filename, *selection_filename;
|
||||
const guchar *selection_data, *data_begin, *data_end, *p;
|
||||
gint selection_length;
|
||||
|
||||
selection_length = gtk_selection_data_get_length( data );
|
||||
|
||||
if ( data && selection_length > (gint) sizeof( uri_prefix ) ) {
|
||||
selection_data = gtk_selection_data_get_data( data );
|
||||
data_begin = selection_data + sizeof( uri_prefix ) - 1;
|
||||
data_end = selection_data + selection_length;
|
||||
p = data_begin;
|
||||
do {
|
||||
if ( *p == '\r' || *p == '\n' ) {
|
||||
data_end = p;
|
||||
break;
|
||||
}
|
||||
} while ( p++ != data_end );
|
||||
|
||||
selection_filename = g_strndup( (const gchar *)data_begin,
|
||||
data_end - data_begin );
|
||||
|
||||
filename = g_uri_unescape_string( selection_filename, NULL );
|
||||
if ( filename ) {
|
||||
fuse_emulation_pause();
|
||||
utils_open_file( filename, settings_current.auto_load, NULL );
|
||||
free( filename );
|
||||
display_refresh_all();
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
g_free( selection_filename );
|
||||
}
|
||||
gtk_drag_finish( drag_context, FALSE, FALSE, timestamp );
|
||||
}
|
||||
|
||||
int
|
||||
ui_init( int *argc, char ***argv )
|
||||
{
|
||||
GtkWidget *box;
|
||||
GtkAccelGroup *accel_group;
|
||||
GtkSettings *settings;
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 10, 0 )
|
||||
/* The Wayland output is buggy, see #367 */
|
||||
gdk_set_allowed_backends( "quartz,win32,mir,x11,*" );
|
||||
#endif
|
||||
|
||||
gtk_init(argc,argv);
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
gdk_rgb_init();
|
||||
gdk_rgb_set_install( TRUE );
|
||||
gtk_widget_set_default_colormap( gdk_rgb_get_cmap() );
|
||||
gtk_widget_set_default_visual( gdk_rgb_get_visual() );
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
gtkui_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
#ifdef FUSE_ICON_AVAILABLE
|
||||
gtk_window_set_icon_name( GTK_WINDOW( gtkui_window ), "fuse" );
|
||||
#endif
|
||||
|
||||
settings = gtk_widget_get_settings( GTK_WIDGET( gtkui_window ) );
|
||||
g_object_set( settings, "gtk-menu-bar-accel", "F1", NULL );
|
||||
gtk_window_set_title( GTK_WINDOW(gtkui_window), "Fuse" );
|
||||
|
||||
g_signal_connect(G_OBJECT(gtkui_window), "delete-event",
|
||||
G_CALLBACK(gtkui_delete), NULL);
|
||||
g_signal_connect(G_OBJECT(gtkui_window), "key-press-event",
|
||||
G_CALLBACK(gtkkeyboard_keypress), NULL);
|
||||
gtk_widget_add_events( gtkui_window, GDK_KEY_RELEASE_MASK );
|
||||
g_signal_connect(G_OBJECT(gtkui_window), "key-release-event",
|
||||
G_CALLBACK(gtkkeyboard_keyrelease), NULL);
|
||||
|
||||
/* If we lose the focus, disable all keys */
|
||||
g_signal_connect( G_OBJECT( gtkui_window ), "focus-out-event",
|
||||
G_CALLBACK( gtkui_lose_focus ), NULL );
|
||||
g_signal_connect( G_OBJECT( gtkui_window ), "focus-in-event",
|
||||
G_CALLBACK( gtkui_gain_focus ), NULL );
|
||||
|
||||
gtk_drag_dest_set( GTK_WIDGET( gtkui_window ),
|
||||
GTK_DEST_DEFAULT_ALL,
|
||||
drag_types,
|
||||
ARRAY_SIZE( drag_types ),
|
||||
GDK_ACTION_COPY | GDK_ACTION_PRIVATE | GDK_ACTION_MOVE );
|
||||
/* GDK_ACTION_PRIVATE alone DNW with ROX-Filer,
|
||||
GDK_ACTION_MOVE allow DnD from KDE */
|
||||
|
||||
g_signal_connect( G_OBJECT( gtkui_window ), "drag-data-received",
|
||||
G_CALLBACK( gtkui_drag_data_received ), NULL );
|
||||
|
||||
box = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_container_add(GTK_CONTAINER(gtkui_window), box);
|
||||
|
||||
if( gtkui_make_menu( &accel_group, &menu_bar, gtkui_menu_data,
|
||||
gtkui_menu_data_size ) ) {
|
||||
fprintf(stderr,"%s: couldn't make menus %s:%d\n",
|
||||
fuse_progname,__FILE__,__LINE__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gtk_window_add_accel_group( GTK_WINDOW(gtkui_window), accel_group );
|
||||
gtk_box_pack_start( GTK_BOX(box), menu_bar, FALSE, FALSE, 0 );
|
||||
|
||||
gtkui_drawing_area = gtk_drawing_area_new();
|
||||
if(!gtkui_drawing_area) {
|
||||
fprintf(stderr,"%s: couldn't create drawing area at %s:%d\n",
|
||||
fuse_progname,__FILE__,__LINE__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set minimum size for drawing area */
|
||||
gtk_widget_set_size_request( gtkui_drawing_area, DISPLAY_ASPECT_WIDTH,
|
||||
DISPLAY_SCREEN_HEIGHT );
|
||||
|
||||
gtkmouse_init();
|
||||
|
||||
gtk_box_pack_start( GTK_BOX(box), gtkui_drawing_area, TRUE, TRUE, 0 );
|
||||
|
||||
/* Create the statusbar */
|
||||
gtkstatusbar_create( GTK_BOX( box ) );
|
||||
|
||||
gtk_widget_show_all( gtkui_window );
|
||||
gtkstatusbar_set_visibility( settings_current.statusbar );
|
||||
|
||||
ui_mouse_present = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_menu_deactivate( GtkMenuShell *menu GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
ui_mouse_resume();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtkui_make_menu(GtkAccelGroup **accel_group,
|
||||
GtkWidget **menu_bar,
|
||||
GtkActionEntry *menu_data,
|
||||
guint menu_data_size)
|
||||
{
|
||||
*accel_group = NULL;
|
||||
*menu_bar = NULL;
|
||||
GError *error = NULL;
|
||||
char ui_file[ PATH_MAX ];
|
||||
|
||||
ui_manager_menu = gtk_ui_manager_new();
|
||||
|
||||
/* Load actions */
|
||||
GtkActionGroup *menu_action_group = gtk_action_group_new( "MenuActionGroup" );
|
||||
gtk_action_group_add_actions( menu_action_group, menu_data, menu_data_size,
|
||||
NULL );
|
||||
gtk_ui_manager_insert_action_group( ui_manager_menu, menu_action_group, 0 );
|
||||
g_object_unref( menu_action_group );
|
||||
|
||||
/* Load the UI */
|
||||
if( utils_find_file_path( "menu_data.ui", ui_file, UTILS_AUXILIARY_GTK ) ) {
|
||||
fprintf( stderr, "%s: Error getting path for menu_data.ui\n",
|
||||
fuse_progname );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
guint ui_menu_id = gtk_ui_manager_add_ui_from_file( ui_manager_menu, ui_file,
|
||||
&error );
|
||||
if( error ) {
|
||||
g_error_free( error );
|
||||
return TRUE;
|
||||
}
|
||||
else if( !ui_menu_id ) return TRUE;
|
||||
|
||||
*accel_group = gtk_ui_manager_get_accel_group( ui_manager_menu );
|
||||
|
||||
*menu_bar = gtk_ui_manager_get_widget( ui_manager_menu, "/MainMenu" );
|
||||
g_signal_connect( G_OBJECT( *menu_bar ), "deactivate",
|
||||
G_CALLBACK( gtkui_menu_deactivate ), NULL );
|
||||
|
||||
/* Start various menus in the 'off' state */
|
||||
ui_menu_activate( UI_MENU_ITEM_AY_LOGGING, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_MOVIE_RECORDING, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_MACHINE_PROFILER, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_RECORDING, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_RECORDING_ROLLBACK, 0 );
|
||||
ui_menu_activate( UI_MENU_ITEM_TAPE_RECORDING, 0 );
|
||||
#ifdef HAVE_LIB_XML2
|
||||
ui_menu_activate( UI_MENU_ITEM_FILE_SVG_CAPTURE, 0 );
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
ui_event(void)
|
||||
{
|
||||
while(gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_end(void)
|
||||
{
|
||||
/* Don't display the window whilst doing all this! */
|
||||
gtk_widget_hide( gtkui_window );
|
||||
|
||||
g_object_unref( ui_manager_menu );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a dialog box with the given error message */
|
||||
int
|
||||
ui_error_specific( ui_error_level severity, const char *message )
|
||||
{
|
||||
GtkWidget *dialog, *label, *vbox, *content_area;
|
||||
const gchar *title;
|
||||
|
||||
/* If we don't have a UI yet, we can't output widgets */
|
||||
if( !display_ui_initialised ) return 0;
|
||||
|
||||
/* Set the appropriate title */
|
||||
switch( severity ) {
|
||||
case UI_ERROR_INFO: title = "Fuse - Info"; break;
|
||||
case UI_ERROR_WARNING: title = "Fuse - Warning"; break;
|
||||
case UI_ERROR_ERROR: title = "Fuse - Error"; break;
|
||||
default: title = "Fuse - (Unknown Error Level)"; break;
|
||||
}
|
||||
|
||||
/* Create the dialog box */
|
||||
dialog = gtkstock_dialog_new( title, G_CALLBACK( gtk_widget_destroy ) );
|
||||
|
||||
/* Add the OK button into the lower half */
|
||||
gtkstock_create_close( dialog, NULL, G_CALLBACK (gtk_widget_destroy),
|
||||
FALSE );
|
||||
|
||||
/* Create a label with that message */
|
||||
label = gtk_label_new( message );
|
||||
|
||||
/* Make a new vbox for the top part for saner spacing */
|
||||
vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), vbox, TRUE, TRUE, 0 );
|
||||
gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
|
||||
|
||||
/* Put the label in it */
|
||||
gtk_container_add( GTK_CONTAINER( vbox ), label );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The callbacks used by various routines */
|
||||
|
||||
static gboolean
|
||||
gtkui_lose_focus( GtkWidget *widget GCC_UNUSED,
|
||||
GdkEvent *event GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
keyboard_release_all();
|
||||
ui_mouse_suspend();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtkui_gain_focus( GtkWidget *widget GCC_UNUSED,
|
||||
GdkEvent *event GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
ui_mouse_resume();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Called by the main window on a "delete-event" */
|
||||
static gboolean
|
||||
gtkui_delete( GtkWidget *widget GCC_UNUSED, GdkEvent *event GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
menu_file_exit( NULL, NULL );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Called by the menu when File/Exit selected */
|
||||
void
|
||||
menu_file_exit( GtkAction *gtk_action GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
if( gtkui_confirm( "Exit Fuse?" ) ) {
|
||||
|
||||
if( menu_check_media_changed() ) return;
|
||||
|
||||
fuse_exiting = 1;
|
||||
|
||||
/* Stop the paused state to allow us to exit (occurs from main
|
||||
emulation loop) */
|
||||
if( paused ) menu_machine_pause( NULL, NULL );
|
||||
|
||||
/* Ensure we break out of the main Z80 loop, there could be active
|
||||
breakpoints before the next event */
|
||||
debugger_exit_emulator( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/* Select a graphics filter from those for which `available' returns
|
||||
true */
|
||||
scaler_type
|
||||
menu_get_scaler( scaler_available_fn selector )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
gtkui_select_info dialog;
|
||||
GSList *button_group = NULL;
|
||||
|
||||
int count;
|
||||
scaler_type scaler;
|
||||
|
||||
/* Store the function which tells us which scalers are currently
|
||||
available */
|
||||
dialog.selector = selector;
|
||||
|
||||
/* No scaler currently selected */
|
||||
dialog.selected = SCALER_NUM;
|
||||
|
||||
/* Some space to store the radio buttons in */
|
||||
dialog.buttons = malloc( SCALER_NUM * sizeof(GtkWidget* ) );
|
||||
if( dialog.buttons == NULL ) {
|
||||
ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
|
||||
return SCALER_NUM;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
/* Create the necessary widgets */
|
||||
dialog.dialog = gtkstock_dialog_new( "Fuse - Select Scaler", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog.dialog ) );
|
||||
|
||||
for( scaler = 0; scaler < SCALER_NUM; scaler++ ) {
|
||||
|
||||
if( !selector( scaler ) ) continue;
|
||||
|
||||
dialog.buttons[ count ] =
|
||||
gtk_radio_button_new_with_label( button_group, scaler_name( scaler ) );
|
||||
button_group =
|
||||
gtk_radio_button_get_group( GTK_RADIO_BUTTON( dialog.buttons[ count ] ) );
|
||||
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dialog.buttons[ count ] ),
|
||||
current_scaler == scaler );
|
||||
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), dialog.buttons[ count ] );
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Create and add the actions buttons to the dialog box */
|
||||
gtkstock_create_ok_cancel( dialog.dialog, NULL,
|
||||
G_CALLBACK( menu_options_filter_done ),
|
||||
(gpointer) &dialog, DEFAULT_DESTROY,
|
||||
DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog.dialog ),
|
||||
GTK_RESPONSE_OK );
|
||||
|
||||
gtk_widget_show_all( dialog.dialog );
|
||||
|
||||
/* Process events until the window is done with */
|
||||
gtk_main();
|
||||
|
||||
return dialog.selected;
|
||||
}
|
||||
|
||||
/* Callback used by the filter selection dialog */
|
||||
static void
|
||||
menu_options_filter_done( GtkWidget *widget GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
int i, count;
|
||||
gtkui_select_info *ptr = (gtkui_select_info*)user_data;
|
||||
|
||||
count = 0;
|
||||
|
||||
for( i = 0; i < SCALER_NUM; i++ ) {
|
||||
|
||||
if( !ptr->selector( i ) ) continue;
|
||||
|
||||
if( gtk_toggle_button_get_active(
|
||||
GTK_TOGGLE_BUTTON( ptr->buttons[ count ] )
|
||||
)
|
||||
) {
|
||||
ptr->selected = i;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
gtk_widget_destroy( ptr->dialog );
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtkui_run_main_loop( gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
gtk_main();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Machine/Pause */
|
||||
void
|
||||
menu_machine_pause( GtkAction *gtk_action GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
int error;
|
||||
|
||||
if( paused ) {
|
||||
paused = 0;
|
||||
ui_statusbar_update( UI_STATUSBAR_ITEM_PAUSED,
|
||||
UI_STATUSBAR_STATE_INACTIVE );
|
||||
timer_estimate_reset();
|
||||
gtk_main_quit();
|
||||
} else {
|
||||
|
||||
/* Stop recording any competition mode RZX file */
|
||||
if( rzx_recording && rzx_competition_mode ) {
|
||||
ui_error( UI_ERROR_INFO, "Stopping competition mode RZX recording" );
|
||||
error = rzx_stop_recording(); if( error ) return;
|
||||
}
|
||||
|
||||
paused = 1;
|
||||
ui_statusbar_update( UI_STATUSBAR_ITEM_PAUSED, UI_STATUSBAR_STATE_ACTIVE );
|
||||
|
||||
/* Create nested main loop outside this callback to allow unpause */
|
||||
g_idle_add( (GSourceFunc)gtkui_run_main_loop, NULL );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Called by the menu when Machine/Reset selected */
|
||||
void
|
||||
menu_machine_reset( GtkAction *gtk_action GCC_UNUSED, guint action )
|
||||
{
|
||||
int hard_reset = action;
|
||||
const char *message = "Reset?";
|
||||
|
||||
if( hard_reset )
|
||||
message = "Hard reset?";
|
||||
|
||||
if( !gtkui_confirm( message ) )
|
||||
return;
|
||||
|
||||
/* Stop any ongoing RZX */
|
||||
rzx_stop_recording();
|
||||
rzx_stop_playback( 1 );
|
||||
|
||||
if( machine_reset( hard_reset ) ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't reset machine: giving up!" );
|
||||
|
||||
/* FIXME: abort() seems a bit extreme here, but it'll do for now */
|
||||
fuse_abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Called by the menu when Machine/Select selected */
|
||||
void
|
||||
menu_machine_select( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
gtkui_select_info dialog;
|
||||
|
||||
int i;
|
||||
|
||||
/* Some space to store the radio buttons in */
|
||||
dialog.buttons = malloc( machine_count * sizeof(GtkWidget* ) );
|
||||
if( dialog.buttons == NULL ) {
|
||||
ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop emulation */
|
||||
fuse_emulation_pause();
|
||||
|
||||
/* Create the necessary widgets */
|
||||
dialog.dialog = gtkstock_dialog_new( "Fuse - Select Machine", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog.dialog ) );
|
||||
|
||||
dialog.buttons[0] =
|
||||
gtk_radio_button_new_with_label(
|
||||
NULL, libspectrum_machine_name( machine_types[0]->machine )
|
||||
);
|
||||
|
||||
for( i=1; i<machine_count; i++ ) {
|
||||
dialog.buttons[i] =
|
||||
gtk_radio_button_new_with_label(
|
||||
gtk_radio_button_get_group( GTK_RADIO_BUTTON( dialog.buttons[i-1] ) ),
|
||||
libspectrum_machine_name( machine_types[i]->machine )
|
||||
);
|
||||
}
|
||||
|
||||
for( i=0; i<machine_count; i++ ) {
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dialog.buttons[i] ),
|
||||
machine_current == machine_types[i] );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), dialog.buttons[i] );
|
||||
}
|
||||
|
||||
/* Create and add the actions buttons to the dialog box */
|
||||
gtkstock_create_ok_cancel( dialog.dialog, NULL,
|
||||
G_CALLBACK( menu_machine_select_done ),
|
||||
(gpointer) &dialog, DEFAULT_DESTROY,
|
||||
DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog.dialog ),
|
||||
GTK_RESPONSE_OK );
|
||||
|
||||
gtk_widget_show_all( dialog.dialog );
|
||||
|
||||
/* Process events until the window is done with */
|
||||
gtk_main();
|
||||
|
||||
/* And then carry on with emulation again */
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
/* Callback used by the machine selection dialog */
|
||||
static void
|
||||
menu_machine_select_done( GtkWidget *widget GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
int i;
|
||||
gtkui_select_info *ptr = user_data;
|
||||
|
||||
for( i=0; i<machine_count; i++ ) {
|
||||
if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(ptr->buttons[i]) ) &&
|
||||
machine_current != machine_types[i]
|
||||
)
|
||||
{
|
||||
machine_select( machine_types[i]->machine );
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_destroy( ptr->dialog );
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
void
|
||||
menu_machine_debugger( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
debugger_mode = DEBUGGER_MODE_HALTED;
|
||||
if( paused ) ui_debugger_activate();
|
||||
}
|
||||
|
||||
/* Called on machine selection */
|
||||
int
|
||||
ui_widgets_reset( void )
|
||||
{
|
||||
gtkui_pokefinder_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
menu_help_keyboard( GtkAction *gtk_action GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
gtkui_picture( "keyboard.png", 0 );
|
||||
}
|
||||
|
||||
void
|
||||
menu_help_about( GtkAction *gtk_action GCC_UNUSED, gpointer data GCC_UNUSED )
|
||||
{
|
||||
gtk_show_about_dialog( GTK_WINDOW( gtkui_window ),
|
||||
"program-name", "Fuse",
|
||||
"comments", "The Free Unix Spectrum Emulator",
|
||||
"copyright", FUSE_COPYRIGHT,
|
||||
#ifdef FUSE_ICON_AVAILABLE
|
||||
"logo-icon-name", "fuse",
|
||||
#else
|
||||
"logo-icon-name", NULL,
|
||||
#endif
|
||||
"version", VERSION,
|
||||
"website", PACKAGE_URL,
|
||||
NULL );
|
||||
}
|
||||
|
||||
/* Generic `tidy-up' callback */
|
||||
void
|
||||
gtkui_destroy_widget_and_quit( GtkWidget *widget, gpointer data GCC_UNUSED )
|
||||
{
|
||||
gtk_widget_destroy( widget );
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
/* Functions to activate and deactivate certain menu items */
|
||||
|
||||
int
|
||||
ui_menu_item_set_active( const char *path, int active )
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
|
||||
/* Translate UI-indepentment path to GTK UI path */
|
||||
gchar *full_path = g_strdup_printf ("/MainMenu%s", path );
|
||||
|
||||
menu_item = gtk_ui_manager_get_widget( ui_manager_menu, full_path );
|
||||
g_free( full_path );
|
||||
|
||||
if( !menu_item ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't get menu item '%s' from menu_factory",
|
||||
path );
|
||||
return 1;
|
||||
}
|
||||
gtk_widget_set_sensitive( menu_item, active );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
confirm_joystick_done( GtkWidget *widget GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
int i;
|
||||
gtkui_select_info *ptr = user_data;
|
||||
|
||||
for( i = 0; i < JOYSTICK_CONN_COUNT; i++ ) {
|
||||
|
||||
GtkToggleButton *button = GTK_TOGGLE_BUTTON( ptr->buttons[ i ] );
|
||||
|
||||
if( gtk_toggle_button_get_active( button ) ) {
|
||||
ptr->joystick = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_destroy( ptr->dialog );
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
ui_confirm_joystick_t
|
||||
ui_confirm_joystick( libspectrum_joystick libspectrum_type,
|
||||
int inputs GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
gtkui_select_info dialog;
|
||||
char title[ 80 ];
|
||||
int i;
|
||||
GSList *group = NULL;
|
||||
|
||||
if( !settings_current.joy_prompt ) return UI_CONFIRM_JOYSTICK_NONE;
|
||||
|
||||
/* Some space to store the radio buttons in */
|
||||
dialog.buttons =
|
||||
malloc( JOYSTICK_CONN_COUNT * sizeof( *dialog.buttons ) );
|
||||
if( !dialog.buttons ) {
|
||||
ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
|
||||
return UI_CONFIRM_JOYSTICK_NONE;
|
||||
}
|
||||
|
||||
/* Stop emulation */
|
||||
fuse_emulation_pause();
|
||||
|
||||
/* Create the necessary widgets */
|
||||
snprintf( title, sizeof( title ), "Fuse - Configure %s Joystick",
|
||||
libspectrum_joystick_name( libspectrum_type ) );
|
||||
dialog.dialog = gtkstock_dialog_new( title, NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog.dialog ) );
|
||||
|
||||
for( i = 0; i < JOYSTICK_CONN_COUNT; i++ ) {
|
||||
|
||||
GtkWidget **button = &( dialog.buttons[ i ] );
|
||||
|
||||
*button =
|
||||
gtk_radio_button_new_with_label( group, joystick_connection[ i ] );
|
||||
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( *button ) );
|
||||
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( *button ), i == 0 );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), *button );
|
||||
}
|
||||
|
||||
/* Create and add the actions buttons to the dialog box */
|
||||
gtkstock_create_ok_cancel( dialog.dialog, NULL,
|
||||
G_CALLBACK( confirm_joystick_done ),
|
||||
(gpointer) &dialog, DEFAULT_DESTROY,
|
||||
DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog.dialog ),
|
||||
GTK_RESPONSE_OK );
|
||||
|
||||
gtk_widget_show_all( dialog.dialog );
|
||||
|
||||
/* Process events until the window is done with */
|
||||
dialog.joystick = UI_CONFIRM_JOYSTICK_NONE;
|
||||
gtk_main();
|
||||
|
||||
/* And then carry on with emulation again */
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return dialog.joystick;
|
||||
}
|
||||
|
||||
/*
|
||||
* Font code
|
||||
*/
|
||||
|
||||
int
|
||||
gtkui_get_monospaced_font( PangoFontDescription **font )
|
||||
{
|
||||
*font = pango_font_description_from_string( "Monospace 10" );
|
||||
if( !(*font) ) {
|
||||
ui_error( UI_ERROR_ERROR, "couldn't find a monospaced font" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gtkui_free_font( PangoFontDescription *font )
|
||||
{
|
||||
pango_font_description_free( font );
|
||||
}
|
||||
|
||||
void
|
||||
gtkui_list_set_cursor( GtkTreeView *list, int row )
|
||||
{
|
||||
GtkTreePath *path;
|
||||
|
||||
if( row >= 0 ) {
|
||||
path = gtk_tree_path_new_from_indices( row, -1 );
|
||||
gtk_tree_view_set_cursor( list, path, NULL, FALSE );
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
gtkui_list_get_cursor( GtkTreeView *list )
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeViewColumn *focus_column;
|
||||
int *indices;
|
||||
int row = -1;
|
||||
|
||||
/* Get selected row */
|
||||
gtk_tree_view_get_cursor( list, &path, &focus_column );
|
||||
if( path ) {
|
||||
indices = gtk_tree_path_get_indices( path );
|
||||
if( indices ) row = indices[0];
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_press( GtkTreeView *list, GdkEventKey *event, gpointer user_data )
|
||||
{
|
||||
GtkAdjustment *adjustment = user_data;
|
||||
gdouble base, oldbase, base_limit;
|
||||
gdouble page_size, step_increment;
|
||||
int cursor_row;
|
||||
int num_rows;
|
||||
|
||||
base = oldbase = gtk_adjustment_get_value( adjustment );
|
||||
page_size = gtk_adjustment_get_page_size( adjustment );
|
||||
step_increment = gtk_adjustment_get_step_increment( adjustment );
|
||||
num_rows = ( page_size + 1 ) / step_increment;
|
||||
|
||||
/* Get selected row */
|
||||
cursor_row = gtkui_list_get_cursor( list );
|
||||
|
||||
switch( event->keyval )
|
||||
{
|
||||
case GDK_KEY_Up:
|
||||
if( cursor_row == 0 )
|
||||
base -= step_increment;
|
||||
break;
|
||||
|
||||
case GDK_KEY_Down:
|
||||
if( cursor_row == num_rows - 1 )
|
||||
base += step_increment;
|
||||
break;
|
||||
|
||||
case GDK_KEY_Page_Up:
|
||||
base -= gtk_adjustment_get_page_increment( adjustment );
|
||||
break;
|
||||
|
||||
case GDK_KEY_Page_Down:
|
||||
base += gtk_adjustment_get_page_increment( adjustment );
|
||||
break;
|
||||
|
||||
case GDK_KEY_Home:
|
||||
cursor_row = 0;
|
||||
base = gtk_adjustment_get_lower( adjustment );
|
||||
break;
|
||||
|
||||
case GDK_KEY_End:
|
||||
cursor_row = num_rows - 1;
|
||||
base = gtk_adjustment_get_upper( adjustment ) - page_size;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( base < 0 ) {
|
||||
base = 0;
|
||||
} else {
|
||||
base_limit = gtk_adjustment_get_upper( adjustment ) - page_size;
|
||||
if( base > base_limit ) base = base_limit;
|
||||
}
|
||||
|
||||
if( base != oldbase ) {
|
||||
gtk_adjustment_set_value( adjustment, base );
|
||||
|
||||
/* Mark selected row */
|
||||
gtkui_list_set_cursor( list, cursor_row );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wheel_scroll_event( GtkTreeView *list, GdkEvent *event, gpointer user_data )
|
||||
{
|
||||
GtkAdjustment *adjustment = user_data;
|
||||
gdouble base, oldbase, base_limit;
|
||||
int cursor_row;
|
||||
|
||||
base = oldbase = gtk_adjustment_get_value( adjustment );
|
||||
|
||||
switch( event->scroll.direction )
|
||||
{
|
||||
case GDK_SCROLL_UP:
|
||||
base -= gtk_adjustment_get_page_increment( adjustment ) / 2;
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
base += gtk_adjustment_get_page_increment( adjustment ) / 2;
|
||||
break;
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 4, 0 )
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
{
|
||||
static gdouble total_dy = 0;
|
||||
gdouble dx, dy, page_size;
|
||||
int delta;
|
||||
|
||||
if( gdk_event_get_scroll_deltas( event, &dx, &dy ) ) {
|
||||
total_dy += dy;
|
||||
page_size = gtk_adjustment_get_page_size( adjustment );
|
||||
delta = total_dy * pow( page_size, 2.0 / 3.0 );
|
||||
|
||||
/* Is movement significative? */
|
||||
if( delta ) {
|
||||
base += delta;
|
||||
total_dy = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( base < 0 ) {
|
||||
base = 0;
|
||||
} else {
|
||||
base_limit = gtk_adjustment_get_upper( adjustment ) -
|
||||
gtk_adjustment_get_page_size( adjustment );
|
||||
if( base > base_limit ) base = base_limit;
|
||||
}
|
||||
|
||||
if( base != oldbase ) {
|
||||
cursor_row = gtkui_list_get_cursor( list );
|
||||
gtk_adjustment_set_value( adjustment, base );
|
||||
gtkui_list_set_cursor( list, cursor_row );
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtkui_scroll_connect( GtkTreeView *list, GtkAdjustment *adj )
|
||||
{
|
||||
g_signal_connect( list, "key-press-event",
|
||||
G_CALLBACK( key_press ), adj );
|
||||
g_signal_connect( list, "scroll-event",
|
||||
G_CALLBACK( wheel_scroll_event ), adj );
|
||||
}
|
||||
|
||||
int
|
||||
gtkui_menubar_get_height( void )
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
|
||||
gtk_widget_get_allocation( menu_bar, &alloc );
|
||||
|
||||
return alloc.height;
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
/* memory.c: the GTK memory browser
|
||||
Copyright (c) 2004-2005 Philip Kendall
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "memory_pages.h"
|
||||
#include "menu.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#define VIEW_NUM_ROWS 20
|
||||
#define VIEW_NUM_COLS 16
|
||||
|
||||
static libspectrum_word memaddr = 0x0000;
|
||||
|
||||
static libspectrum_dword mark_offset = 0xffffffff;
|
||||
|
||||
static int cursor_line_number = -1, cursor_char_offset;
|
||||
static GtkTextBuffer *cursor_buffer;
|
||||
|
||||
static GtkTextBuffer *buffer_address, *buffer_hex, *buffer_data;
|
||||
static GtkAdjustment *adjustment;
|
||||
|
||||
static gboolean
|
||||
textview_wheel_scroll_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
|
||||
{
|
||||
GtkAdjustment *adjustment = user_data;
|
||||
gdouble base, oldbase, base_limit;
|
||||
|
||||
base = oldbase = gtk_adjustment_get_value( adjustment );
|
||||
|
||||
switch( event->scroll.direction )
|
||||
{
|
||||
case GDK_SCROLL_UP:
|
||||
base -= gtk_adjustment_get_page_increment( adjustment ) / 2;
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
base += gtk_adjustment_get_page_increment( adjustment ) / 2;
|
||||
break;
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 4, 0 )
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
{
|
||||
static gdouble total_dy = 0;
|
||||
gdouble dx, dy, page_size;
|
||||
int delta;
|
||||
|
||||
if( gdk_event_get_scroll_deltas( event, &dx, &dy ) ) {
|
||||
total_dy += dy;
|
||||
page_size = gtk_adjustment_get_page_size( adjustment );
|
||||
delta = total_dy * pow( page_size, 2.0 / 3.0 );
|
||||
|
||||
/* Is movement significative? */
|
||||
if( delta ) {
|
||||
base += delta;
|
||||
total_dy = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( base < 0 ) {
|
||||
base = 0;
|
||||
} else {
|
||||
base_limit = gtk_adjustment_get_upper( adjustment ) -
|
||||
gtk_adjustment_get_page_size( adjustment );
|
||||
if( base > base_limit ) base = base_limit;
|
||||
}
|
||||
|
||||
if( base != oldbase ) {
|
||||
gtk_adjustment_set_value( adjustment, base );
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
textview_key_press_event( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
|
||||
{
|
||||
GtkAdjustment *adjustment = user_data;
|
||||
GtkTextBuffer *text_buffer;
|
||||
GtkTextIter iter;
|
||||
GtkTextMark *mark;
|
||||
gdouble base, oldbase, base_limit;
|
||||
gdouble page_size, step_increment;
|
||||
gint line, line_offset;
|
||||
int num_rows, line_width;
|
||||
|
||||
base = oldbase = gtk_adjustment_get_value( adjustment );
|
||||
page_size = gtk_adjustment_get_page_size( adjustment );
|
||||
step_increment = gtk_adjustment_get_step_increment( adjustment );
|
||||
num_rows = ( page_size + 1 ) / step_increment;
|
||||
|
||||
text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( widget ) );
|
||||
|
||||
/* Get line width (includes CR/LF) */
|
||||
line_width = gtk_text_buffer_get_char_count( text_buffer ) /
|
||||
gtk_text_buffer_get_line_count( text_buffer );
|
||||
|
||||
/* Get row and offset of cursor */
|
||||
mark = gtk_text_buffer_get_insert( text_buffer );
|
||||
gtk_text_buffer_get_iter_at_mark( text_buffer, &iter, mark );
|
||||
line = gtk_text_iter_get_line( &iter );
|
||||
line_offset = gtk_text_iter_get_line_offset( &iter );
|
||||
|
||||
switch( event->keyval )
|
||||
{
|
||||
|
||||
case GDK_KEY_Left:
|
||||
if( line == 0 && line_offset == 0 ) {
|
||||
base -= step_increment;
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = line;
|
||||
cursor_char_offset = line_width;
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_KEY_Right:
|
||||
if( line == num_rows - 1 && line_offset == line_width ) {
|
||||
base += step_increment;
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = line;
|
||||
cursor_char_offset = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_KEY_Up:
|
||||
if( line == 0 ) {
|
||||
base -= step_increment;
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = 0;
|
||||
cursor_char_offset = line_offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_KEY_Down:
|
||||
if( line == num_rows - 1 ) {
|
||||
base += step_increment;
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = line;
|
||||
cursor_char_offset = line_offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_KEY_Page_Up:
|
||||
base -= gtk_adjustment_get_page_increment( adjustment );
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = line;
|
||||
cursor_char_offset = line_offset;
|
||||
break;
|
||||
|
||||
case GDK_KEY_Page_Down:
|
||||
base += gtk_adjustment_get_page_increment( adjustment );
|
||||
cursor_buffer = text_buffer;
|
||||
cursor_line_number = line;
|
||||
cursor_char_offset = line_offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( base < 0 ) {
|
||||
base = 0;
|
||||
} else {
|
||||
base_limit = gtk_adjustment_get_upper( adjustment ) - page_size;
|
||||
if( base > base_limit ) base = base_limit;
|
||||
}
|
||||
|
||||
if( base != oldbase ) {
|
||||
gtk_adjustment_set_value( adjustment, base );
|
||||
|
||||
/* As we are simulating a full-filled text view, we need to move the cursor
|
||||
position after page movement/redraw */
|
||||
if( cursor_line_number >= 0 && cursor_line_number < VIEW_NUM_ROWS ) {
|
||||
gtk_text_buffer_get_iter_at_line_offset( cursor_buffer, &iter,
|
||||
cursor_line_number,
|
||||
cursor_char_offset );
|
||||
gtk_text_buffer_place_cursor( cursor_buffer, &iter );
|
||||
cursor_line_number = -1;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_display( libspectrum_word base )
|
||||
{
|
||||
size_t i, j;
|
||||
char buffer2[ 8 ];
|
||||
char buffer3;
|
||||
GtkTextIter iter_address, iter_hex, iter_data, start, end;
|
||||
|
||||
memaddr = base;
|
||||
|
||||
gtk_text_buffer_get_bounds( buffer_address, &start, &end );
|
||||
gtk_text_buffer_delete( buffer_address, &start, &end );
|
||||
gtk_text_buffer_get_bounds( buffer_hex, &start, &end );
|
||||
gtk_text_buffer_delete( buffer_hex, &start, &end );
|
||||
gtk_text_buffer_get_bounds( buffer_data, &start, &end );
|
||||
gtk_text_buffer_delete( buffer_data, &start, &end );
|
||||
|
||||
gtk_text_buffer_get_start_iter( buffer_address, &iter_address );
|
||||
gtk_text_buffer_get_start_iter( buffer_hex, &iter_hex );
|
||||
gtk_text_buffer_get_start_iter( buffer_data, &iter_data );
|
||||
|
||||
for( i = 0; i < VIEW_NUM_ROWS; i++ ) {
|
||||
if( i > 0 ) {
|
||||
gtk_text_buffer_insert( buffer_address, &iter_address, "\n", -1 );
|
||||
gtk_text_buffer_insert( buffer_hex, &iter_hex, "\n", -1 );
|
||||
gtk_text_buffer_insert( buffer_data, &iter_data, "\n", -1 );
|
||||
}
|
||||
|
||||
snprintf( buffer2, 8, "%04X", base );
|
||||
gtk_text_buffer_insert( buffer_address, &iter_address, buffer2, -1 );
|
||||
|
||||
for( j = 0; j < VIEW_NUM_COLS; j++, base++ ) {
|
||||
if( j > 0 )
|
||||
gtk_text_buffer_insert( buffer_hex, &iter_hex, " ", -1 );
|
||||
|
||||
libspectrum_byte b = readbyte_internal( base );
|
||||
snprintf( buffer2, 4, "%02X", b );
|
||||
|
||||
buffer3 = ( b >= 32 && b < 127 ) ? b : '.';
|
||||
|
||||
if( base != mark_offset ) {
|
||||
gtk_text_buffer_insert( buffer_hex, &iter_hex, buffer2, -1 );
|
||||
gtk_text_buffer_insert( buffer_data, &iter_data, &buffer3, 1 );
|
||||
} else {
|
||||
gtk_text_buffer_insert_with_tags_by_name( buffer_hex, &iter_hex,
|
||||
buffer2, -1, "background_yellow", NULL );
|
||||
gtk_text_buffer_insert_with_tags_by_name( buffer_data, &iter_data,
|
||||
&buffer3, 1, "background_yellow", NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gtk_text_buffer_get_bounds( buffer_address, &start, &end );
|
||||
gtk_text_buffer_apply_tag_by_name( buffer_address, "monospace", &start, &end );
|
||||
gtk_text_buffer_get_bounds( buffer_hex, &start, &end );
|
||||
gtk_text_buffer_apply_tag_by_name( buffer_hex, "monospace", &start, &end );
|
||||
gtk_text_buffer_get_bounds( buffer_data, &start, &end );
|
||||
gtk_text_buffer_apply_tag_by_name( buffer_data, "monospace", &start, &end );
|
||||
}
|
||||
|
||||
static void
|
||||
scroller( GtkAdjustment *adjustment, gpointer user_data )
|
||||
{
|
||||
libspectrum_word base;
|
||||
|
||||
/* Drop the low bits before displaying anything */
|
||||
base = gtk_adjustment_get_value( adjustment );
|
||||
base &= 0xfff0;
|
||||
|
||||
update_display( base );
|
||||
}
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 6, 0 )
|
||||
static void
|
||||
goto_offset( GtkWidget *widget GCC_UNUSED, gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
long offset;
|
||||
const gchar *entry;
|
||||
char *endptr;
|
||||
int base_num;
|
||||
|
||||
if( gtk_entry_get_text_length( GTK_ENTRY( widget ) ) == 0 )
|
||||
return;
|
||||
|
||||
/* Parse address */
|
||||
entry = gtk_entry_get_text( GTK_ENTRY( widget ) );
|
||||
errno = 0;
|
||||
base_num = ( g_str_has_prefix( entry, "0x" ) )? 16 : 10;
|
||||
offset = strtol( entry, &endptr, base_num );
|
||||
|
||||
/* Validate address */
|
||||
if( errno || offset < 0 || offset > 65535 || endptr == entry ||
|
||||
*endptr != '\0' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
mark_offset = offset;
|
||||
gtk_adjustment_set_value( adjustment, offset );
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
menu_machine_memorybrowser( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *dialog, *content_area, *scrollbar, *label, *offset;
|
||||
GtkWidget *box, *box_address, *box_hex, *box_data, *box_data_horizontal;
|
||||
GtkAccelGroup *accel_group;
|
||||
GtkWidget *view_address, *view_hex, *view_data;
|
||||
GtkTextTagTable *tag_table;
|
||||
GtkTextTag *tag;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Memory Browser", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
/* Keyboard shortcuts */
|
||||
accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group( GTK_WINDOW( dialog ), accel_group );
|
||||
|
||||
gtkstock_create_close( dialog, accel_group, NULL, TRUE );
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 6, 0 )
|
||||
/* Go to offset */
|
||||
box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 8 );
|
||||
offset = gtk_search_entry_new();
|
||||
/*
|
||||
* Entry is max 6 chars wide ("0xXXXX") but the GTK widget adds
|
||||
* a search icon and "clear contents" icon, which between them
|
||||
* take up about 6 char's widths. So it needs to be wider.
|
||||
*/
|
||||
gtk_entry_set_width_chars( GTK_ENTRY( offset ), 15 );
|
||||
gtk_entry_set_max_length( GTK_ENTRY( offset ), 6 );
|
||||
gtk_box_pack_end( GTK_BOX( box ), offset, FALSE, FALSE, 0 );
|
||||
|
||||
label = gtk_label_new( "Go to offset" );
|
||||
gtk_box_pack_end( GTK_BOX( box ), label, FALSE, FALSE, 0 );
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), box, FALSE, FALSE, 8 );
|
||||
|
||||
g_signal_connect( G_OBJECT( offset ), "activate",
|
||||
G_CALLBACK( goto_offset ), NULL );
|
||||
#endif
|
||||
|
||||
/* Create text buffers */
|
||||
tag_table = gtk_text_tag_table_new();
|
||||
tag = gtk_text_tag_new( "monospace" );
|
||||
g_object_set( tag, "family", "monospace", NULL );
|
||||
gtk_text_tag_table_add( tag_table, tag );
|
||||
|
||||
tag = gtk_text_tag_new( "background_yellow" );
|
||||
g_object_set( tag, "background", "yellow",
|
||||
"background-full-height", TRUE, NULL );
|
||||
gtk_text_tag_table_add( tag_table, tag );
|
||||
|
||||
buffer_address = gtk_text_buffer_new( tag_table );
|
||||
buffer_hex = gtk_text_buffer_new( tag_table );
|
||||
buffer_data = gtk_text_buffer_new( tag_table );
|
||||
|
||||
/* Labels */
|
||||
box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 8 );
|
||||
box_address = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box ), box_address, FALSE, FALSE, 0 );
|
||||
box_hex = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box ), box_hex, FALSE, FALSE, 0 );
|
||||
box_data = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box ), box_data, FALSE, FALSE, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), box, FALSE, FALSE, 0 );
|
||||
|
||||
label = gtk_label_new( "Address" );
|
||||
gtk_box_pack_start( GTK_BOX( box_address ), label, FALSE, FALSE, 0 );
|
||||
label = gtk_label_new( "Hex" );
|
||||
gtk_box_pack_start( GTK_BOX( box_hex ), label, FALSE, FALSE, 0 );
|
||||
label = gtk_label_new( "Data" );
|
||||
gtk_box_pack_start( GTK_BOX( box_data ), label, FALSE, FALSE, 0 );
|
||||
|
||||
/* Add views */
|
||||
view_address = gtk_text_view_new_with_buffer( buffer_address );
|
||||
gtk_text_view_set_editable( GTK_TEXT_VIEW( view_address ), FALSE );
|
||||
gtk_text_view_set_left_margin( GTK_TEXT_VIEW( view_address ), 1 );
|
||||
gtk_text_view_set_right_margin( GTK_TEXT_VIEW( view_address ), 1 );
|
||||
view_hex = gtk_text_view_new_with_buffer( buffer_hex );
|
||||
gtk_text_view_set_editable( GTK_TEXT_VIEW( view_hex ), FALSE );
|
||||
gtk_text_view_set_left_margin( GTK_TEXT_VIEW( view_hex ), 1 );
|
||||
gtk_text_view_set_right_margin( GTK_TEXT_VIEW( view_hex ), 1 );
|
||||
view_data = gtk_text_view_new_with_buffer( buffer_data );
|
||||
gtk_text_view_set_editable( GTK_TEXT_VIEW( view_data ), FALSE );
|
||||
gtk_text_view_set_left_margin( GTK_TEXT_VIEW( view_data ), 1 );
|
||||
gtk_text_view_set_right_margin( GTK_TEXT_VIEW( view_data ), 1 );
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 16, 0 )
|
||||
gtk_text_view_set_monospace( GTK_TEXT_VIEW( view_address ), TRUE );
|
||||
gtk_text_view_set_monospace( GTK_TEXT_VIEW( view_hex ), TRUE );
|
||||
gtk_text_view_set_monospace( GTK_TEXT_VIEW( view_data ), TRUE );
|
||||
#endif
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( box_address ), view_address, FALSE, FALSE, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box_hex ), view_hex, FALSE, FALSE, 0 );
|
||||
box_data_horizontal = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box_data ), box_data_horizontal, FALSE, FALSE, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( box_data_horizontal ), view_data, FALSE, FALSE, 0 );
|
||||
|
||||
/* Scroll */
|
||||
adjustment = GTK_ADJUSTMENT(
|
||||
gtk_adjustment_new( memaddr, 0x0000, 0xffff, VIEW_NUM_COLS,
|
||||
( VIEW_NUM_ROWS * VIEW_NUM_COLS ) / 2,
|
||||
( VIEW_NUM_ROWS * VIEW_NUM_COLS ) - 1 ) );
|
||||
g_signal_connect( adjustment, "value-changed", G_CALLBACK( scroller ), NULL );
|
||||
scrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL, adjustment );
|
||||
gtk_box_pack_start( GTK_BOX( box_data_horizontal ), scrollbar, FALSE, FALSE, 0 );
|
||||
|
||||
/* Allow scroll on text views */
|
||||
g_signal_connect( view_address, "scroll-event",
|
||||
G_CALLBACK( textview_wheel_scroll_event ), adjustment );
|
||||
g_signal_connect( view_hex, "scroll-event",
|
||||
G_CALLBACK( textview_wheel_scroll_event ), adjustment );
|
||||
g_signal_connect( view_data, "scroll-event",
|
||||
G_CALLBACK( textview_wheel_scroll_event ), adjustment );
|
||||
g_signal_connect( view_address, "key-press-event",
|
||||
G_CALLBACK( textview_key_press_event ), adjustment );
|
||||
g_signal_connect( view_hex, "key-press-event",
|
||||
G_CALLBACK( textview_key_press_event ), adjustment );
|
||||
g_signal_connect( view_data, "key-press-event",
|
||||
G_CALLBACK( textview_key_press_event ), adjustment );
|
||||
|
||||
update_display( memaddr );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/* memory.c: the GTK memory browser
|
||||
Copyright (c) 2004-2005 Philip Kendall
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "memory_pages.h"
|
||||
#include "menu.h"
|
||||
|
||||
static libspectrum_word memaddr = 0x0000;
|
||||
|
||||
/* List columns */
|
||||
enum
|
||||
{
|
||||
COL_ADDRESS = 0,
|
||||
COL_HEX,
|
||||
COL_DATA,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
static void
|
||||
update_display( GtkTreeModel *model, libspectrum_word base )
|
||||
{
|
||||
size_t i, j;
|
||||
GtkTreeIter iter;
|
||||
|
||||
gchar buffer[ 8 + 64 + 20 ];
|
||||
gchar *text[] = { &buffer[0], &buffer[ 8 ], &buffer[ 8 + 64 ] };
|
||||
char buffer2[ 4 ];
|
||||
|
||||
memaddr = base;
|
||||
gtk_list_store_clear( GTK_LIST_STORE( model ) );
|
||||
|
||||
for( i = 0; i < 20; i++ ) {
|
||||
snprintf( text[0], 8, "%04X", base );
|
||||
|
||||
text[1][0] = '\0';
|
||||
for( j = 0; j < 0x10; j++, base++ ) {
|
||||
|
||||
libspectrum_byte b = readbyte_internal( base );
|
||||
|
||||
snprintf( buffer2, 4, "%02X ", b );
|
||||
strncat( text[1], buffer2, 4 );
|
||||
|
||||
text[2][j] = ( b >= 32 && b < 127 ) ? b : '.';
|
||||
}
|
||||
text[2][ 0x10 ] = '\0';
|
||||
|
||||
/* Append a new row and fill data */
|
||||
gtk_list_store_append( GTK_LIST_STORE( model ), &iter );
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_ADDRESS, text[0],
|
||||
COL_HEX, text[1],
|
||||
COL_DATA, text[2],
|
||||
-1 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
scroller( GtkAdjustment *adjustment, gpointer user_data )
|
||||
{
|
||||
libspectrum_word base;
|
||||
GtkTreeModel *model = user_data;
|
||||
|
||||
/* Drop the low bits before displaying anything */
|
||||
base = gtk_adjustment_get_value( adjustment );
|
||||
base &= 0xfff0;
|
||||
|
||||
update_display( model, base );
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_mem_list( void )
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeModel *model;
|
||||
GtkListStore *store;
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
/* Add columns */
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Address",
|
||||
renderer,
|
||||
"text", COL_ADDRESS,
|
||||
NULL );
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Hex",
|
||||
renderer,
|
||||
"text", COL_HEX,
|
||||
NULL );
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Data",
|
||||
renderer,
|
||||
"text", COL_DATA,
|
||||
NULL );
|
||||
|
||||
/* Create data model */
|
||||
store = gtk_list_store_new( NUM_COLS, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING );
|
||||
|
||||
model = GTK_TREE_MODEL( store );
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW( view ), model );
|
||||
g_object_unref( model );
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
void
|
||||
menu_machine_memorybrowser( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *dialog, *box, *content_area, *list, *scrollbar;
|
||||
GtkAdjustment *adjustment;
|
||||
GtkTreeModel *model;
|
||||
int error;
|
||||
PangoFontDescription *font;
|
||||
|
||||
fuse_emulation_pause();
|
||||
|
||||
error = gtkui_get_monospaced_font( &font ); if( error ) return;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Memory Browser", NULL );
|
||||
|
||||
gtkstock_create_close( dialog, NULL, NULL, FALSE );
|
||||
|
||||
box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), box, TRUE, TRUE, 0 );
|
||||
|
||||
/* The list itself */
|
||||
list = create_mem_list();
|
||||
gtk_widget_override_font( list, font );
|
||||
model = gtk_tree_view_get_model( GTK_TREE_VIEW( list ) );
|
||||
update_display( GTK_TREE_MODEL( model ), memaddr );
|
||||
gtk_box_pack_start( GTK_BOX( box ), list, TRUE, TRUE, 0 );
|
||||
|
||||
adjustment = GTK_ADJUSTMENT(
|
||||
gtk_adjustment_new( memaddr, 0x0000, 0xffff, 0x10, 0xa0, 0x13f ) );
|
||||
g_signal_connect( adjustment, "value-changed", G_CALLBACK( scroller ),
|
||||
model );
|
||||
|
||||
gtkui_scroll_connect( GTK_TREE_VIEW( list ), adjustment );
|
||||
|
||||
scrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL, adjustment );
|
||||
gtk_box_pack_start( GTK_BOX( box ), scrollbar, FALSE, FALSE, 0 );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
|
||||
gtkui_free_font( font );
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# options-header.pl: generate options dialog boxes
|
||||
|
||||
# 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: [email protected]
|
||||
|
||||
use strict;
|
||||
|
||||
use Fuse;
|
||||
use Fuse::Dialog;
|
||||
|
||||
die "No data file specified" unless @ARGV;
|
||||
|
||||
my @dialogs = Fuse::Dialog::read( shift @ARGV );
|
||||
my $internal = 1;
|
||||
|
||||
$internal = 0 if( @ARGV && shift( @ARGV ) eq 'public' );
|
||||
|
||||
if( $internal ) {
|
||||
print Fuse::GPL( 'options_internals.h: options dialog boxes',
|
||||
'2001-2002 Philip Kendall' );
|
||||
} else {
|
||||
print Fuse::GPL( 'options.h: options dialog boxes public declarations',
|
||||
'2001-2002 Philip Kendall' );
|
||||
}
|
||||
|
||||
print <<"CODE";
|
||||
|
||||
/* This file is autogenerated from options.dat by options-header.pl.
|
||||
Do not edit unless you know what you\'re doing! */
|
||||
|
||||
CODE
|
||||
|
||||
if( $internal ) {
|
||||
print << "CODE";
|
||||
#ifndef FUSE_GTKOPTIONS_INTERNALS_H
|
||||
#define FUSE_GTKOPTIONS_INTERNALS_H
|
||||
|
||||
#ifndef __GTK_H__
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
CODE
|
||||
} else {
|
||||
print << "CODE";
|
||||
#ifndef FUSE_OPTIONS_H
|
||||
#define FUSE_OPTIONS_H
|
||||
|
||||
CODE
|
||||
}
|
||||
|
||||
if( $internal ) {
|
||||
foreach( @dialogs ) {
|
||||
|
||||
print << "CODE";
|
||||
void menu_options_$_->{name}( GtkWidget *widget, gpointer data );
|
||||
|
||||
typedef struct menu_options_$_->{name}_t \{
|
||||
|
||||
GtkWidget *dialog;
|
||||
|
||||
CODE
|
||||
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
|
||||
if( $widget->{type} eq "Checkbox" or
|
||||
$widget->{type} eq "Combo" or
|
||||
$widget->{type} eq "Entry" ) {
|
||||
print " GtkWidget *$widget->{value};\n";
|
||||
} else {
|
||||
die "Unknown type `$widget->{type}'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
print "\n} menu_options_$_->{name}_t;\n\n"
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
foreach( @dialogs ) {
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
if( $widget->{type} eq "Combo" ) {
|
||||
print <<"CODE";
|
||||
int option_enumerate_$_->{name}_$widget->{value}( void );
|
||||
|
||||
CODE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $internal ) {
|
||||
print << "CODE";
|
||||
#endif /* #ifndef FUSE_GTKOPTIONS_ITERNALS_H */
|
||||
CODE
|
||||
} else {
|
||||
print << "CODE";
|
||||
#endif /* #ifndef FUSE_OPTIONS_H */
|
||||
CODE
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# options.pl: generate options dialog boxes
|
||||
|
||||
# Copyright (c) 2002-2015 Philip Kendall
|
||||
|
||||
# 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: [email protected]
|
||||
|
||||
use strict;
|
||||
|
||||
use Fuse;
|
||||
use Fuse::Dialog;
|
||||
|
||||
die "No data file specified" unless @ARGV;
|
||||
|
||||
my @dialogs = Fuse::Dialog::read( shift @ARGV );
|
||||
|
||||
my %combo_sets;
|
||||
my %combo_default;
|
||||
|
||||
print Fuse::GPL( 'options.c: options dialog boxes',
|
||||
'2001-2013 Philip Kendall' ) . << "CODE";
|
||||
|
||||
/* This file is autogenerated from options.dat by options.pl.
|
||||
Do not edit unless you know what you\'re doing! */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef UI_GTK /* Use this file if we're using GTK */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "display.h"
|
||||
#include "fuse.h"
|
||||
#include "options.h"
|
||||
#include "options_internals.h"
|
||||
#include "periph.h"
|
||||
#include "settings.h"
|
||||
#include "ui/gtk/gtkcompat.h"
|
||||
#include "ui/gtk/gtkinternals.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int
|
||||
option_enumerate_combo( const char * const *options, char *value, guint count,
|
||||
int def )
|
||||
{
|
||||
guint i;
|
||||
if( value != NULL ) {
|
||||
for( i = 0; i < count; i++) {
|
||||
if( !strcmp( value, options[ i ] ) )
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
CODE
|
||||
|
||||
foreach( @dialogs ) {
|
||||
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
|
||||
foreach my $type ( $widget->{type} ) {
|
||||
|
||||
my $text = $widget->{text}; $text =~ tr/()//d;
|
||||
|
||||
if( $type eq "Combo" ) {
|
||||
my $n = 0;
|
||||
|
||||
foreach( split( /\|/, $widget->{data1} ) ) {
|
||||
if( /^\*/ ) {
|
||||
$combo_default{$widget->{value}} = $n;
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$n = 0;
|
||||
$widget->{data1} =~ s/^\*//;
|
||||
$widget->{data1} =~ s/\|\*/|/;
|
||||
if( not exists( $combo_sets{$widget->{data1}} ) ) {
|
||||
$combo_sets{$widget->{data1}} = "$_->{name}_$widget->{value}_combo";
|
||||
|
||||
print << "CODE";
|
||||
|
||||
static const char * const $_->{name}_$widget->{value}_combo[] = {
|
||||
CODE
|
||||
foreach( split( /\|/, $widget->{data1} ) ) {
|
||||
print << "CODE";
|
||||
"$_",
|
||||
CODE
|
||||
$n++;
|
||||
}
|
||||
print << "CODE";
|
||||
};
|
||||
|
||||
static const guint $_->{name}_$widget->{value}_combo_count = $n;
|
||||
|
||||
CODE
|
||||
} else {
|
||||
print << "CODE";
|
||||
\#define $_->{name}_$widget->{value}_combo $combo_sets{$widget->{data1}}
|
||||
\#define $_->{name}_$widget->{value}_combo_count $combo_sets{$widget->{data1}}_count
|
||||
|
||||
CODE
|
||||
}
|
||||
print << "CODE";
|
||||
int
|
||||
option_enumerate_$_->{name}_$widget->{value}( void )
|
||||
{
|
||||
return option_enumerate_combo( $_->{name}_$widget->{value}_combo,
|
||||
settings_current.$widget->{value},
|
||||
$_->{name}_$widget->{value}_combo_count,
|
||||
$combo_default{$widget->{value}} );
|
||||
}
|
||||
|
||||
CODE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print << "CODE";
|
||||
static void menu_options_$_->{name}_done( GtkWidget *widget,
|
||||
gpointer user_data );
|
||||
|
||||
void
|
||||
menu_options_$_->{name}( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
menu_options_$_->{name}_t dialog;
|
||||
|
||||
/* Firstly, stop emulation */
|
||||
fuse_emulation_pause();
|
||||
|
||||
/* Create the necessary widgets */
|
||||
dialog.dialog = gtkstock_dialog_new( "Fuse - $_->{title}", NULL );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog.dialog ) );
|
||||
|
||||
/* Create the various widgets */
|
||||
CODE
|
||||
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
|
||||
foreach my $type ( $widget->{type} ) {
|
||||
|
||||
my $text = $widget->{text}; $text =~ tr/()//d;
|
||||
|
||||
if( $type eq "Checkbox" ) {
|
||||
|
||||
print << "CODE";
|
||||
dialog.$widget->{value} =
|
||||
gtk_check_button_new_with_label( "$text" );
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dialog.$widget->{value} ),
|
||||
settings_current.$widget->{value} );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), dialog.$widget->{value} );
|
||||
|
||||
CODE
|
||||
} elsif( $type eq "Entry" ) {
|
||||
|
||||
# FIXME: Make the entry widget resize sensibly
|
||||
|
||||
print << "CODE";
|
||||
{
|
||||
GtkWidget *frame = gtk_frame_new( "$text" );
|
||||
GtkWidget *hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
GtkWidget *text = gtk_label_new( "$widget->{data2}" );
|
||||
gchar buffer[80];
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), frame, TRUE, TRUE, 0 );
|
||||
|
||||
gtk_container_set_border_width( GTK_CONTAINER( hbox ), 4 );
|
||||
gtk_container_add( GTK_CONTAINER( frame ), hbox );
|
||||
|
||||
dialog.$widget->{value} = gtk_entry_new();
|
||||
gtk_entry_set_max_length( GTK_ENTRY( dialog.$widget->{value} ),
|
||||
$widget->{data1} );
|
||||
snprintf( buffer, 80, "%d", settings_current.$widget->{value} );
|
||||
gtk_entry_set_text( GTK_ENTRY( dialog.$widget->{value} ), buffer );
|
||||
gtk_entry_set_activates_default( GTK_ENTRY( dialog.$widget->{value} ), TRUE );
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), dialog.$widget->{value}, TRUE, TRUE, 0 );
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), text, FALSE, FALSE, 5 );
|
||||
}
|
||||
|
||||
CODE
|
||||
} elsif( $type eq "Combo" ) {
|
||||
|
||||
print << "CODE";
|
||||
{
|
||||
GtkWidget *hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
GtkWidget *combo = gtk_combo_box_text_new();
|
||||
GtkWidget *text = gtk_label_new( "$text" );
|
||||
guint i;
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), text, FALSE, FALSE, 5 );
|
||||
text = gtk_label_new( " " );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), text, TRUE, FALSE, 5 );
|
||||
|
||||
for( i = 0; i < $_->{name}_$widget->{value}_combo_count; i++ ) {
|
||||
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT( combo ), $_->{name}_$widget->{value}_combo[i] );
|
||||
}
|
||||
gtk_combo_box_set_active( GTK_COMBO_BOX( combo ), $combo_default{$widget->{value}} );
|
||||
if( settings_current.$widget->{value} != NULL ) {
|
||||
for( i = 0; i < $_->{name}_$widget->{value}_combo_count; i++ ) {
|
||||
if( !strcmp( settings_current.$widget->{value}, $_->{name}_$widget->{value}_combo[i] ) ) {
|
||||
gtk_combo_box_set_active( GTK_COMBO_BOX( combo ), i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dialog.$widget->{value} = combo;
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), dialog.$widget->{value}, FALSE, FALSE, 5 );
|
||||
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), hbox, TRUE, TRUE, 0 );
|
||||
}
|
||||
|
||||
CODE
|
||||
} else {
|
||||
die "Unknown type `$type'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print << "CODE";
|
||||
/* Create the OK and Cancel buttons */
|
||||
gtkstock_create_ok_cancel( dialog.dialog, NULL,
|
||||
G_CALLBACK( menu_options_$_->{name}_done ),
|
||||
(gpointer) &dialog, NULL, DEFAULT_DESTROY );
|
||||
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog.dialog ),
|
||||
GTK_RESPONSE_OK );
|
||||
|
||||
/* Display the window */
|
||||
gtk_widget_show_all( dialog.dialog );
|
||||
|
||||
/* Process events until the window is done with */
|
||||
gtk_main();
|
||||
|
||||
/* And then carry on with emulation again */
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
static void
|
||||
menu_options_$_->{name}_done( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data )
|
||||
{
|
||||
menu_options_$_->{name}_t *ptr = user_data;
|
||||
|
||||
CODE
|
||||
|
||||
if( $_->{postcheck} ) {
|
||||
|
||||
print << "CODE";
|
||||
/* Get a copy of current settings */
|
||||
settings_info original_settings;
|
||||
memset( &original_settings, 0, sizeof( settings_info ) );
|
||||
settings_copy( &original_settings, &settings_current );
|
||||
|
||||
CODE
|
||||
|
||||
}
|
||||
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
|
||||
if( $widget->{type} eq "Checkbox" ) {
|
||||
|
||||
print << "CODE";
|
||||
settings_current.$widget->{value} =
|
||||
gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( ptr->$widget->{value} ) );
|
||||
|
||||
CODE
|
||||
} elsif( $widget->{type} eq "Entry" ) {
|
||||
|
||||
print << "CODE";
|
||||
settings_current.$widget->{value} =
|
||||
atoi( gtk_entry_get_text( GTK_ENTRY( ptr->$widget->{value} ) ) );
|
||||
|
||||
CODE
|
||||
} elsif( $widget->{type} eq "Combo" ) {
|
||||
|
||||
print << "CODE";
|
||||
libspectrum_free( settings_current.$widget->{value} );
|
||||
settings_current.$widget->{value} = utils_safe_strdup( $_->{name}_$widget->{value}_combo[
|
||||
gtk_combo_box_get_active( GTK_COMBO_BOX( ptr->$widget->{value} ) ) ] );
|
||||
|
||||
CODE
|
||||
} else {
|
||||
die "Unknown type `$widget->{type}'";
|
||||
}
|
||||
}
|
||||
|
||||
if( $_->{postcheck} ) {
|
||||
|
||||
print << "CODE";
|
||||
int needs_hard_reset = $_->{postcheck}();
|
||||
|
||||
/* Confirm reset */
|
||||
if( needs_hard_reset && !gtkui_confirm("Some options need to reset the machine. Reset?" ) ) {
|
||||
|
||||
/* Cancel new settings */
|
||||
settings_copy( &settings_current, &original_settings );
|
||||
settings_free( &original_settings );
|
||||
return;
|
||||
}
|
||||
|
||||
settings_free( &original_settings );
|
||||
|
||||
CODE
|
||||
|
||||
}
|
||||
|
||||
print << "CODE";
|
||||
gtk_widget_destroy( ptr->dialog );
|
||||
|
||||
CODE
|
||||
|
||||
print " $_->{posthook}();\n\n" if $_->{posthook};
|
||||
|
||||
print << "CODE";
|
||||
gtkstatusbar_set_visibility( settings_current.statusbar );
|
||||
display_refresh_all();
|
||||
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
CODE
|
||||
|
||||
}
|
||||
|
||||
print << "CODE"
|
||||
|
||||
#endif /* #ifdef UI_GTK */
|
||||
CODE
|
||||
@@ -1,146 +0,0 @@
|
||||
/* picture.c: GTK routines to draw the keyboard picture
|
||||
Copyright (c) 2002-2005 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkinternals.h"
|
||||
#include "ui/ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int dialog_created = 0;
|
||||
|
||||
static GtkWidget *dialog;
|
||||
static GdkPixbuf *pixbuf = NULL;
|
||||
|
||||
#if GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
static GdkPixbuf *scaled_pixbuf = NULL;
|
||||
|
||||
static gint
|
||||
picture_size_allocate( GtkWidget *widget GCC_UNUSED, GdkRectangle *allocation,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
float scale_x, scale_y, scale_factor;
|
||||
int scaled_width, scaled_height;
|
||||
|
||||
scale_x = (float)allocation->width / gdk_pixbuf_get_width( pixbuf );
|
||||
scale_y = (float)allocation->height / gdk_pixbuf_get_height( pixbuf );
|
||||
scale_factor = scale_x < scale_y ? scale_x : scale_y;
|
||||
|
||||
scaled_width = gdk_pixbuf_get_width( pixbuf ) * scale_factor;
|
||||
scaled_height = gdk_pixbuf_get_height( pixbuf ) * scale_factor;
|
||||
|
||||
if( scaled_pixbuf != NULL ) g_object_unref( scaled_pixbuf );
|
||||
scaled_pixbuf = gdk_pixbuf_scale_simple( pixbuf, scaled_width, scaled_height,
|
||||
GDK_INTERP_BILINEAR );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
picture_draw( GtkWidget *widget, cairo_t *cr, gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
int offset_x, offset_y;
|
||||
int scaled_width, scaled_height;
|
||||
int allocated_width, allocated_height;
|
||||
|
||||
allocated_width = gtk_widget_get_allocated_width( widget );
|
||||
allocated_height = gtk_widget_get_allocated_height( widget );
|
||||
|
||||
scaled_width = gdk_pixbuf_get_width( scaled_pixbuf );
|
||||
scaled_height = gdk_pixbuf_get_height( scaled_pixbuf );
|
||||
|
||||
offset_x = ( allocated_width - scaled_width ) / 2;
|
||||
offset_y = ( allocated_height - scaled_height ) / 2;
|
||||
|
||||
gdk_cairo_set_source_pixbuf( cr, scaled_pixbuf, offset_x, offset_y );
|
||||
cairo_paint(cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
int
|
||||
gtkui_picture( const char *filename, int border GCC_UNUSED )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
|
||||
if( !dialog_created ) {
|
||||
|
||||
char path[PATH_MAX];
|
||||
|
||||
if( utils_find_file_path( filename, path, UTILS_AUXILIARY_LIB ) ) return 1;
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_file( path, NULL );
|
||||
if( pixbuf == NULL ) {
|
||||
ui_error( UI_ERROR_ERROR, "Failed to load keyboard image" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Keyboard",
|
||||
G_CALLBACK( gtk_widget_hide ) );
|
||||
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
|
||||
GtkWidget *image = gtk_image_new();
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( image ), pixbuf );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), image );
|
||||
|
||||
/* Stop users resizing this window */
|
||||
gtk_window_set_resizable( GTK_WINDOW( dialog ), FALSE );
|
||||
|
||||
#else
|
||||
|
||||
GtkWidget *box = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_container_add( GTK_CONTAINER( content_area ), box );
|
||||
|
||||
GtkWidget *drawing_area = gtk_drawing_area_new();
|
||||
gtk_widget_set_vexpand( drawing_area, TRUE );
|
||||
gtk_box_pack_start( GTK_BOX( box ), drawing_area, TRUE, TRUE, 0 );
|
||||
|
||||
g_signal_connect( G_OBJECT( drawing_area ), "draw",
|
||||
G_CALLBACK( picture_draw ), NULL );
|
||||
g_signal_connect( G_OBJECT( drawing_area ), "size-allocate",
|
||||
G_CALLBACK( picture_size_allocate ), NULL );
|
||||
|
||||
gtk_widget_set_size_request( GTK_WIDGET( drawing_area ),
|
||||
gdk_pixbuf_get_width( pixbuf ),
|
||||
gdk_pixbuf_get_height( pixbuf ) );
|
||||
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
gtkstock_create_close( dialog, NULL, G_CALLBACK( gtk_widget_hide ),
|
||||
FALSE );
|
||||
|
||||
dialog_created = 1;
|
||||
}
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
/* pixmaps.c: icon pixmaps
|
||||
Copyright (c) 2003-2005 Fredrick Meunier, Philip Kendall, Darren Salt
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "gtkinternals.h"
|
||||
|
||||
const char *gtkpixmap_tape_inactive[] = {
|
||||
"27 17 13 1",
|
||||
" c None",
|
||||
". c #D5D5D5",
|
||||
"+ c #7C7C7C",
|
||||
"@ c #A5A5A5",
|
||||
"# c #7F7F7F",
|
||||
"$ c #979797",
|
||||
"% c #DEDEDE",
|
||||
"& c #F9F9F9",
|
||||
"* c #626262",
|
||||
"= c #F8F8F8",
|
||||
"- c #636363",
|
||||
"; c #616161",
|
||||
"> c #6A6A6A",
|
||||
"...........................",
|
||||
".+.......................+.",
|
||||
"..@@@@@@@@@@@@@@@@@@@@@@@..",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@###############@@@@@.",
|
||||
".@@@@$#%&%@*****@%&%#$@@@@.",
|
||||
".@@@@#@&&=@-****@&&=@#@@@@.",
|
||||
".@@@@$#%=%@*;***@%=%#$@@@@.",
|
||||
".@@@@@###############@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".....>>>>>>>>>>>>>>>>>.....",
|
||||
"....$>...............>$....",
|
||||
"....>$...............$>....",
|
||||
".+..>.................>..+.",
|
||||
"....>.................>...."};
|
||||
|
||||
const char *gtkpixmap_tape_active[] = {
|
||||
"27 17 13 1",
|
||||
" c None",
|
||||
". c #00D500",
|
||||
"+ c #007C00",
|
||||
"@ c #00A500",
|
||||
"# c #007F00",
|
||||
"$ c #009700",
|
||||
"% c #00DE00",
|
||||
"& c #00F900",
|
||||
"* c #006200",
|
||||
"= c #00F800",
|
||||
"- c #006300",
|
||||
"; c #006100",
|
||||
"> c #006A00",
|
||||
"...........................",
|
||||
".+.......................+.",
|
||||
"..@@@@@@@@@@@@@@@@@@@@@@@..",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@###############@@@@@.",
|
||||
".@@@@$#%&%@*****@%&%#$@@@@.",
|
||||
".@@@@#@&&=@-****@&&=@#@@@@.",
|
||||
".@@@@$#%=%@*;***@%=%#$@@@@.",
|
||||
".@@@@@###############@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".@@@@@@@@@@@@@@@@@@@@@@@@@.",
|
||||
".....>>>>>>>>>>>>>>>>>.....",
|
||||
"....$>...............>$....",
|
||||
"....>$...............$>....",
|
||||
".+..>.................>..+.",
|
||||
"....>.................>...."};
|
||||
|
||||
const char *gtkpixmap_mdr_inactive[] = {
|
||||
"15 20 6 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"+ c #686868",
|
||||
"@ c #888888",
|
||||
"# c #AFAFAF",
|
||||
"$ c #D8D8D8",
|
||||
"##+$$$$$$$$####",
|
||||
"###+++..+++####",
|
||||
"+##++++++++####",
|
||||
"$+##+++++++####",
|
||||
"$+#############",
|
||||
"$+@##@@@@@##@@#",
|
||||
"$+############@",
|
||||
"+#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"#@@@#######@@@#",
|
||||
"###############",
|
||||
"###############",
|
||||
"+++++++++++++++",
|
||||
"+++++++++++++++",
|
||||
"+++++++++++++++"};
|
||||
|
||||
const char *gtkpixmap_mdr_active[] = {
|
||||
"15 20 6 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"+ c #686868",
|
||||
"@ c #008800",
|
||||
"# c #00AF00",
|
||||
"$ c #00D800",
|
||||
"##+$$$$$$$$####",
|
||||
"###+++..+++####",
|
||||
"+##++++++++####",
|
||||
"$+##+++++++####",
|
||||
"$+#############",
|
||||
"$+@##@@@@@##@@#",
|
||||
"$+############@",
|
||||
"+#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"@#############@",
|
||||
"#@@@#######@@@#",
|
||||
"###############",
|
||||
"###############",
|
||||
"+++++++++++++++",
|
||||
"+++++++++++++++",
|
||||
"+++++++++++++++"};
|
||||
|
||||
const char *gtkpixmap_disk_inactive[] = {
|
||||
"15 20 7 1",
|
||||
" c None",
|
||||
". c #A5A5A5",
|
||||
"+ c #A6A6A6",
|
||||
"@ c #6A6A6A",
|
||||
"# c #696969",
|
||||
"$ c #DFDFDF",
|
||||
"% c #F9F9F9",
|
||||
"..+.+++++..++++",
|
||||
"+++++.+@.+.+.+.",
|
||||
"+++.+..@..+.+.+",
|
||||
"++.++++@+.++..+",
|
||||
"...++..#+++..+.",
|
||||
"++.+++.@++.+.+.",
|
||||
"+.++.+.#.+++.++",
|
||||
"+......+....+++",
|
||||
"+.++..+..+.++++",
|
||||
".++.++$%$+.+.+.",
|
||||
".+.++.%%%+..++.",
|
||||
"..+...$%$..+++.",
|
||||
"+..+..+++++++..",
|
||||
"++..+...++.+...",
|
||||
"+++.++.++......",
|
||||
"..@#@@###@#@#++",
|
||||
"++@#@@@@@@##@..",
|
||||
"+.@#@#@@@##@@.+",
|
||||
"++@@@@@@@#@@#..",
|
||||
"..##@#@@@@#@#.+"};
|
||||
|
||||
const char *gtkpixmap_disk_active[] = {
|
||||
"15 20 7 1",
|
||||
" c None",
|
||||
". c #00A500",
|
||||
"+ c #00A600",
|
||||
"@ c #006A00",
|
||||
"# c #006900",
|
||||
"$ c #00DF00",
|
||||
"% c #00F900",
|
||||
"..+.+++++..++++",
|
||||
"+++++.+@.+.+.+.",
|
||||
"+++.+..@..+.+.+",
|
||||
"++.++++@+.++..+",
|
||||
"...++..#+++..+.",
|
||||
"++.+++.@++.+.+.",
|
||||
"+.++.+.#.+++.++",
|
||||
"+......+....+++",
|
||||
"+.++..+..+.++++",
|
||||
".++.++$%$+.+.+.",
|
||||
".+.++.%%%+..++.",
|
||||
"..+...$%$..+++.",
|
||||
"+..+..+++++++..",
|
||||
"++..+...++.+...",
|
||||
"+++.++.++......",
|
||||
"..@#@@###@#@#++",
|
||||
"++@#@@@@@@##@..",
|
||||
"+.@#@#@@@##@@.+",
|
||||
"++@@@@@@@#@@#..",
|
||||
"..##@#@@@@#@#.+"};
|
||||
|
||||
const char *gtkpixmap_pause_inactive[] = {
|
||||
"30 21 7 1",
|
||||
" c None",
|
||||
". c #6C6C6C",
|
||||
"+ c #6A6A6A",
|
||||
"@ c #6D6D6D",
|
||||
"# c #6E6E6E",
|
||||
"$ c #696969",
|
||||
"% c #6B6B6B",
|
||||
" .+ @# ",
|
||||
" #$$+ %$$# ",
|
||||
" +$# #$$ ",
|
||||
" @$# .+ ",
|
||||
" $$$ #$$+ .+ @# ",
|
||||
" .$@ #+$. %$# #$$+ %$$# ",
|
||||
" #$$$$+ +$# #$$ ",
|
||||
" $$$$$$. @$# .+ ",
|
||||
" @$$$$$$$ $$$ #$$+",
|
||||
" +$$$$$$$ .$@ #+$. %$#",
|
||||
" $$$$$$$$# #$$$$+ ",
|
||||
" $$$$$$$$# $$$$$$. ",
|
||||
" +$$$$$$$ @$$$$$$$ ",
|
||||
" #$$$$$$$ +$$$$$$$ ",
|
||||
" $$$$$$@ $$$$$$$$# ",
|
||||
" #$$$$+ $$$$$$$$# ",
|
||||
" %$@ +$$$$$$$ ",
|
||||
" #$$$$$$$ ",
|
||||
" $$$$$$@ ",
|
||||
" #$$$$+ ",
|
||||
" %$@ "};
|
||||
|
||||
const char *gtkpixmap_pause_active[] = {
|
||||
"30 21 7 1",
|
||||
" c None",
|
||||
". c #D80000",
|
||||
"+ c #D40000",
|
||||
"@ c #DA0000",
|
||||
"# c #DC0000",
|
||||
"$ c #D20000",
|
||||
"% c #D60000",
|
||||
" .+ @# ",
|
||||
" #$$+ %$$# ",
|
||||
" +$# #$$ ",
|
||||
" @$# .+ ",
|
||||
" $$$ #$$+ .+ @# ",
|
||||
" .$@ #+$. %$# #$$+ %$$# ",
|
||||
" #$$$$+ +$# #$$ ",
|
||||
" $$$$$$. @$# .+ ",
|
||||
" @$$$$$$$ $$$ #$$+",
|
||||
" +$$$$$$$ .$@ #+$. %$#",
|
||||
" $$$$$$$$# #$$$$+ ",
|
||||
" $$$$$$$$# $$$$$$. ",
|
||||
" +$$$$$$$ @$$$$$$$ ",
|
||||
" #$$$$$$$ +$$$$$$$ ",
|
||||
" $$$$$$@ $$$$$$$$# ",
|
||||
" #$$$$+ $$$$$$$$# ",
|
||||
" %$@ +$$$$$$$ ",
|
||||
" #$$$$$$$ ",
|
||||
" $$$$$$@ ",
|
||||
" #$$$$+ ",
|
||||
" %$@ "};
|
||||
|
||||
const char *gtkpixmap_tape_marker[] = {
|
||||
"8 16 2 1",
|
||||
" c None",
|
||||
". c #212FC6",
|
||||
". ",
|
||||
".. ",
|
||||
"... ",
|
||||
".... ",
|
||||
"..... ",
|
||||
"...... ",
|
||||
"....... ",
|
||||
"........",
|
||||
"........",
|
||||
"....... ",
|
||||
"...... ",
|
||||
"..... ",
|
||||
".... ",
|
||||
"... ",
|
||||
".. ",
|
||||
". "};
|
||||
|
||||
const char *gtkpixmap_mouse_inactive[] = {
|
||||
"14 21 4 1",
|
||||
" c None",
|
||||
"# c #6C6C6C",
|
||||
"+ c #A0A0A0",
|
||||
". c #FFFFFF",
|
||||
" .. ",
|
||||
" .. ",
|
||||
" +####++####+ ",
|
||||
" #####++##### ",
|
||||
" ####++++#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ++++++++++++ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" +##########+ ",
|
||||
" +##########+ ",
|
||||
" ++######++ "};
|
||||
|
||||
const char *gtkpixmap_mouse_active[] = {
|
||||
"14 21 4 1",
|
||||
" c None",
|
||||
"# c #0000D0",
|
||||
"+ c #6C6CD0",
|
||||
". c #FFFFFF",
|
||||
" .. ",
|
||||
" .. ",
|
||||
" +####++####+ ",
|
||||
" #####++##### ",
|
||||
" ####++++#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ####+##+#### ",
|
||||
" ++++++++++++ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" ############ ",
|
||||
" +##########+ ",
|
||||
" +##########+ ",
|
||||
" ++######++ "};
|
||||
@@ -1,332 +0,0 @@
|
||||
/* pokefinder.c: GTK interface to the poke finder
|
||||
Copyright (c) 2003-2015 Philip Kendall
|
||||
Copyright (c) 2014 Sergio Baldoví
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "debugger/debugger.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "menu.h"
|
||||
#include "pokefinder/pokefinder.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static int create_dialog( void );
|
||||
static void gtkui_pokefinder_incremented( GtkWidget *widget,
|
||||
gpointer user_data GCC_UNUSED );
|
||||
static void gtkui_pokefinder_decremented( GtkWidget *widget,
|
||||
gpointer user_data GCC_UNUSED );
|
||||
static void gtkui_pokefinder_search( GtkWidget *widget, gpointer user_data );
|
||||
static void gtkui_pokefinder_reset( GtkWidget *widget, gpointer user_data );
|
||||
static void gtkui_pokefinder_close( GtkWidget *widget, gpointer user_data );
|
||||
static gboolean delete_dialog( GtkWidget *widget, GdkEvent *event,
|
||||
gpointer user_data );
|
||||
static void update_pokefinder( void );
|
||||
static void possible_click( GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer user_data );
|
||||
|
||||
static int dialog_created = 0;
|
||||
|
||||
static GtkWidget
|
||||
*dialog, /* The dialog box itself */
|
||||
*count_label, /* The number of possible locations */
|
||||
*location_list; /* The list view of possible locations */
|
||||
|
||||
static GtkTreeModel *location_model; /* The data of possible locations */
|
||||
|
||||
/* The possible locations */
|
||||
|
||||
#define MAX_POSSIBLE 20
|
||||
|
||||
int possible_page[ MAX_POSSIBLE ];
|
||||
libspectrum_word possible_offset[ MAX_POSSIBLE ];
|
||||
|
||||
/* List columns */
|
||||
enum
|
||||
{
|
||||
COL_PAGE = 0,
|
||||
COL_OFFSET,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
void
|
||||
menu_machine_pokefinder( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
int error;
|
||||
|
||||
if( !dialog_created ) {
|
||||
error = create_dialog(); if( error ) return;
|
||||
}
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
update_pokefinder();
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_location_list( void )
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkListStore *store;
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
/* Add columns */
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Page",
|
||||
renderer,
|
||||
"text", COL_PAGE,
|
||||
NULL );
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Offset",
|
||||
renderer,
|
||||
"text", COL_OFFSET,
|
||||
NULL );
|
||||
|
||||
/* Create data model */
|
||||
store = gtk_list_store_new( NUM_COLS, G_TYPE_STRING, G_TYPE_STRING );
|
||||
|
||||
location_model = GTK_TREE_MODEL( store );
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW( view ), location_model );
|
||||
g_object_unref( location_model );
|
||||
|
||||
/* Activate breakpoints */
|
||||
g_signal_connect( G_OBJECT( view ), "row-activated",
|
||||
G_CALLBACK( possible_click ), NULL );
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
static int
|
||||
create_dialog( void )
|
||||
{
|
||||
GtkWidget *hbox, *vbox, *label, *entry, *content_area;
|
||||
GtkAccelGroup *accel_group;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Poke Finder",
|
||||
G_CALLBACK( delete_dialog ) );
|
||||
|
||||
hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), hbox, TRUE, TRUE, 0 );
|
||||
|
||||
label = gtk_label_new( "Search for:" );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), label, TRUE, TRUE, 5 );
|
||||
|
||||
entry = gtk_entry_new();
|
||||
g_signal_connect( G_OBJECT( entry ), "activate",
|
||||
G_CALLBACK( gtkui_pokefinder_search ), NULL );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), entry, TRUE, TRUE, 5 );
|
||||
|
||||
vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), vbox, TRUE, TRUE, 5 );
|
||||
|
||||
count_label = gtk_label_new( "" );
|
||||
gtk_box_pack_start( GTK_BOX( vbox ), count_label, TRUE, TRUE, 5 );
|
||||
|
||||
location_list = create_location_list();
|
||||
gtk_box_pack_start( GTK_BOX( vbox ), location_list, TRUE, TRUE, 5 );
|
||||
|
||||
{
|
||||
static gtkstock_button btn[] = {
|
||||
{ "Incremented", G_CALLBACK( gtkui_pokefinder_incremented ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE },
|
||||
{ "Decremented", G_CALLBACK( gtkui_pokefinder_decremented ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE },
|
||||
{ "!Search", G_CALLBACK( gtkui_pokefinder_search ), NULL, NULL, GDK_KEY_Return, 0, 0, 0, GTK_RESPONSE_NONE },
|
||||
{ "Reset", G_CALLBACK( gtkui_pokefinder_reset ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE }
|
||||
};
|
||||
btn[2].actiondata = G_OBJECT( entry );
|
||||
accel_group = gtkstock_create_buttons( dialog, NULL, btn,
|
||||
ARRAY_SIZE( btn ) );
|
||||
gtkstock_create_close( dialog, accel_group,
|
||||
G_CALLBACK( gtkui_pokefinder_close ), TRUE );
|
||||
}
|
||||
|
||||
/* Users shouldn't be able to resize this window */
|
||||
gtk_window_set_resizable( GTK_WINDOW( dialog ), FALSE );
|
||||
|
||||
dialog_created = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_pokefinder_incremented( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
pokefinder_incremented();
|
||||
update_pokefinder();
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_pokefinder_decremented( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
pokefinder_decremented();
|
||||
update_pokefinder();
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_pokefinder_search( GtkWidget *widget, gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
long value;
|
||||
const gchar *entry;
|
||||
char *endptr;
|
||||
int base;
|
||||
|
||||
errno = 0;
|
||||
entry = gtk_entry_get_text( GTK_ENTRY( widget ) );
|
||||
base = ( g_str_has_prefix( entry, "0x" ) )? 16 : 10;
|
||||
value = strtol( entry, &endptr, base );
|
||||
|
||||
if( errno != 0 || value < 0 || value > 255 || endptr == entry ) {
|
||||
ui_error( UI_ERROR_ERROR, "Invalid value: use an integer from 0 to 255" );
|
||||
return;
|
||||
}
|
||||
|
||||
pokefinder_search( value );
|
||||
update_pokefinder();
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_pokefinder_reset( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED)
|
||||
{
|
||||
pokefinder_clear();
|
||||
update_pokefinder();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
delete_dialog( GtkWidget *widget, GdkEvent *event GCC_UNUSED,
|
||||
gpointer user_data )
|
||||
{
|
||||
gtkui_pokefinder_close( widget, user_data );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtkui_pokefinder_close( GtkWidget *widget, gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
gtk_widget_hide( widget );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
widget_delayed_show( GtkWidget *item )
|
||||
{
|
||||
gtk_widget_show( item );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_pokefinder( void )
|
||||
{
|
||||
size_t page, offset, bank, bank_offset;
|
||||
gchar buffer[256], *possible_text[2] = { &buffer[0], &buffer[128] };
|
||||
GtkTreeIter iter;
|
||||
|
||||
gtk_list_store_clear( GTK_LIST_STORE( location_model ) );
|
||||
|
||||
if( pokefinder_count && pokefinder_count <= MAX_POSSIBLE ) {
|
||||
|
||||
size_t which;
|
||||
|
||||
which = 0;
|
||||
|
||||
for( page = 0; page < MEMORY_PAGES_IN_16K * SPECTRUM_RAM_PAGES; page++ ) {
|
||||
memory_page *mapping = &memory_map_ram[page];
|
||||
bank = mapping->page_num;
|
||||
|
||||
for( offset = 0; offset < MEMORY_PAGE_SIZE; offset++ )
|
||||
if( ! (pokefinder_impossible[page][offset/8] & 1 << (offset & 7)) ) {
|
||||
bank_offset = mapping->offset + offset;
|
||||
|
||||
possible_page[ which ] = bank;
|
||||
possible_offset[ which ] = bank_offset;
|
||||
which++;
|
||||
|
||||
snprintf( possible_text[0], 128, "%lu", (unsigned long)bank );
|
||||
snprintf( possible_text[1], 128, "0x%04X", (unsigned)bank_offset );
|
||||
|
||||
/* Append a new row and fill data */
|
||||
gtk_list_store_append( GTK_LIST_STORE( location_model ), &iter );
|
||||
gtk_list_store_set( GTK_LIST_STORE( location_model ), &iter,
|
||||
COL_PAGE, possible_text[0],
|
||||
COL_OFFSET, possible_text[1],
|
||||
-1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Show widget when the GtkTreeView has been filled with data. Fix an empty
|
||||
list on GTK 3.10 (and maybe other versions) */
|
||||
g_idle_add( (GSourceFunc)widget_delayed_show, location_list );
|
||||
|
||||
} else {
|
||||
gtk_widget_hide( location_list );
|
||||
}
|
||||
|
||||
snprintf( buffer, 256, "Possible locations: %lu",
|
||||
(unsigned long)pokefinder_count );
|
||||
gtk_label_set_text( GTK_LABEL( count_label ), buffer );
|
||||
}
|
||||
|
||||
static void
|
||||
possible_click( GtkTreeView *treeview GCC_UNUSED, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
int error, *indices, row;
|
||||
|
||||
/* Get selected row via double-clicks or keyboard */
|
||||
indices = gtk_tree_path_get_indices( path );
|
||||
if( !indices ) return;
|
||||
row = indices[0];
|
||||
|
||||
error = debugger_breakpoint_add_address(
|
||||
DEBUGGER_BREAKPOINT_TYPE_WRITE, memory_source_ram, possible_page[ row ],
|
||||
possible_offset[ row ], 0, DEBUGGER_BREAKPOINT_LIFE_PERMANENT, NULL
|
||||
);
|
||||
if( error ) return;
|
||||
|
||||
ui_debugger_update();
|
||||
}
|
||||
|
||||
void
|
||||
gtkui_pokefinder_clear( void )
|
||||
{
|
||||
pokefinder_clear();
|
||||
if( dialog_created ) update_pokefinder();
|
||||
}
|
||||
@@ -1,599 +0,0 @@
|
||||
/* pokemem.c: GTK interface that handles pok files
|
||||
Copyright (c) 2011-2015 Philip Kendall, 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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "menu.h"
|
||||
#include "pokefinder/pokemem.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
enum
|
||||
{
|
||||
COL_CHECK = 0,
|
||||
COL_NAME,
|
||||
COL_VALUE,
|
||||
COL_TRAINER,
|
||||
COL_ENABLED,
|
||||
COL_EDITABLE,
|
||||
COL_INCONSISTENT,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
static GtkWidget
|
||||
*dialog, /* The dialog box itself */
|
||||
*poke_list, /* The list of possible pokes */
|
||||
*bank, /* Entries for custom poke */
|
||||
*address,
|
||||
*value;
|
||||
|
||||
void create_dialog( void );
|
||||
void create_and_fill_treeview( void );
|
||||
static void pokemem_close( GtkWidget *widget, gpointer user_data );
|
||||
static void pokemem_update_list( GtkWidget *widget, gpointer user_data );
|
||||
gboolean pokemem_update_trainer( GtkTreeModel *model, GtkTreePath *path,
|
||||
GtkTreeIter *iter, gpointer data );
|
||||
static void pokemem_add_custom_poke( GtkWidget *widget, gpointer user_data );
|
||||
static void trainer_add( gpointer data, gpointer user_data );
|
||||
|
||||
void entry_validate_digit( GtkEntry *entry, const gchar *text, gint length,
|
||||
gint *position, gpointer data );
|
||||
void entry_validate_address( GtkEntry *entry, const gchar *text, gint length,
|
||||
gint *position, gpointer data );
|
||||
|
||||
void row_toggled_callback( GtkCellRendererToggle *cell, gchar *path_string,
|
||||
gpointer user_data );
|
||||
|
||||
static gboolean custom_value_edit( GtkTreeView *tree );
|
||||
void custom_value_changed( GtkCellRendererText *cell, gchar *path_string,
|
||||
gchar *new_text, gpointer user_data );
|
||||
|
||||
void
|
||||
menu_machine_pokememory( GtkAction *gtk_action GCC_UNUSED,
|
||||
gpointer data GCC_UNUSED )
|
||||
{
|
||||
fuse_emulation_pause();
|
||||
|
||||
pokemem_autoload_pokfile();
|
||||
create_dialog();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
void
|
||||
ui_pokemem_selector( const char *filename )
|
||||
{
|
||||
fuse_emulation_pause();
|
||||
|
||||
pokemem_read_from_file( filename );
|
||||
create_dialog();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
}
|
||||
|
||||
void
|
||||
create_dialog( void )
|
||||
{
|
||||
GtkWidget *hbox, *vbox, *label, *scroll;
|
||||
GtkAccelGroup *accel_group;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Poke Memory",
|
||||
G_CALLBACK( pokemem_close ) );
|
||||
|
||||
vbox = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
|
||||
/* Keyboard shortcuts */
|
||||
accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group( GTK_WINDOW( dialog ), accel_group );
|
||||
|
||||
hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
|
||||
gtk_box_pack_start( GTK_BOX( vbox ), hbox, TRUE, TRUE, 5 );
|
||||
|
||||
/* Bank */
|
||||
label = gtk_label_new( "Bank:" );
|
||||
bank = gtk_entry_new();
|
||||
gtk_entry_set_width_chars( GTK_ENTRY( bank ), 7 );
|
||||
gtk_entry_set_max_length( GTK_ENTRY( bank ), 1 );
|
||||
g_signal_connect( G_OBJECT( bank ), "activate",
|
||||
G_CALLBACK( pokemem_add_custom_poke ), NULL );
|
||||
g_signal_connect( G_OBJECT( bank ), "insert_text",
|
||||
G_CALLBACK( entry_validate_digit ), NULL );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), label, TRUE, TRUE, 5 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), bank, TRUE, TRUE, 5 );
|
||||
|
||||
/* Address */
|
||||
label = gtk_label_new( "Address:" );
|
||||
address = gtk_entry_new();
|
||||
gtk_entry_set_width_chars( GTK_ENTRY( address ), 7 );
|
||||
gtk_entry_set_max_length( GTK_ENTRY( address ), 6 );
|
||||
g_signal_connect( G_OBJECT( address ), "activate",
|
||||
G_CALLBACK( pokemem_add_custom_poke ), NULL );
|
||||
g_signal_connect( G_OBJECT( address ), "insert_text",
|
||||
G_CALLBACK( entry_validate_address ), NULL );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), label, TRUE, TRUE, 5 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), address, TRUE, TRUE, 5 );
|
||||
|
||||
/* Value */
|
||||
label = gtk_label_new( "Value:" );
|
||||
value = gtk_entry_new();
|
||||
gtk_entry_set_width_chars( GTK_ENTRY( value ), 7 );
|
||||
gtk_entry_set_max_length( GTK_ENTRY( value ), 3 );
|
||||
g_signal_connect( G_OBJECT( value ), "activate",
|
||||
G_CALLBACK( pokemem_add_custom_poke ), NULL );
|
||||
g_signal_connect( G_OBJECT( value ), "insert_text",
|
||||
G_CALLBACK( entry_validate_digit ), NULL );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), label, TRUE, TRUE, 5 );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), value, TRUE, TRUE, 5 );
|
||||
|
||||
/* Create Add button for custom pokes */
|
||||
static const gtkstock_button
|
||||
add = { "_Add", G_CALLBACK( pokemem_add_custom_poke ), NULL, NULL,
|
||||
0, 0, 0, 0, GTK_RESPONSE_NONE };
|
||||
gtkstock_create_button( GTK_WIDGET( hbox ), accel_group, &add );
|
||||
|
||||
label = gtk_label_new( "Choose active POKES:" );
|
||||
gtk_box_pack_start( GTK_BOX( vbox ), label, TRUE, TRUE, 5 );
|
||||
|
||||
/* Create list widget */
|
||||
create_and_fill_treeview();
|
||||
scroll = gtk_scrolled_window_new( NULL, NULL );
|
||||
|
||||
/* Adjust size for list */
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
gtk_widget_set_size_request( GTK_WIDGET( poke_list ), -1, 250 );
|
||||
#else
|
||||
gtk_scrolled_window_set_min_content_height( GTK_SCROLLED_WINDOW( scroll ), 250 );
|
||||
#endif /* #if !GTK_CHECK_VERSION( 3, 0, 0 ) */
|
||||
|
||||
gtk_container_add( GTK_CONTAINER( scroll ), GTK_WIDGET( poke_list ) );
|
||||
gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scroll ), TRUE, TRUE, 5 );
|
||||
|
||||
/* Create and add the actions buttons to the dialog box */
|
||||
gtkstock_create_ok_cancel( dialog, accel_group,
|
||||
G_CALLBACK( pokemem_update_list ),
|
||||
(gpointer) &dialog,
|
||||
G_CALLBACK( pokemem_close ),
|
||||
G_CALLBACK( pokemem_close ) );
|
||||
|
||||
/* Users shouldn't be able to resize this window */
|
||||
gtk_window_set_resizable( GTK_WINDOW( dialog ), FALSE );
|
||||
|
||||
/* Process the dialog */
|
||||
gtk_widget_show_all( dialog );
|
||||
gtk_main();
|
||||
}
|
||||
|
||||
void
|
||||
create_and_fill_treeview( void )
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeModel *model;
|
||||
|
||||
poke_list = gtk_tree_view_new();
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( poke_list ), TRUE );
|
||||
#endif
|
||||
|
||||
store = gtk_list_store_new( NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING,
|
||||
G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN );
|
||||
|
||||
renderer = gtk_cell_renderer_toggle_new();
|
||||
|
||||
/* First column, checkbox */
|
||||
g_signal_connect( renderer, "toggled",
|
||||
(GCallback) row_toggled_callback,
|
||||
(gpointer) GTK_TREE_MODEL( store ) );
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( poke_list ),
|
||||
-1,
|
||||
"Active",
|
||||
renderer,
|
||||
"active", COL_CHECK,
|
||||
"activatable", COL_ENABLED,
|
||||
"inconsistent", COL_INCONSISTENT,
|
||||
NULL );
|
||||
|
||||
/* Second column, name */
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( poke_list ),
|
||||
-1,
|
||||
"Trainer",
|
||||
renderer,
|
||||
"text", COL_NAME,
|
||||
NULL );
|
||||
|
||||
/* Third column, optional value */
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
g_signal_connect( renderer, "edited", (GCallback)custom_value_changed,
|
||||
(gpointer) GTK_TREE_MODEL( store ) );
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( poke_list ),
|
||||
-1,
|
||||
"Value",
|
||||
renderer,
|
||||
"text", COL_VALUE,
|
||||
"editable", COL_EDITABLE,
|
||||
NULL);
|
||||
|
||||
/* Create and fill model */
|
||||
if( trainer_list ) {
|
||||
g_slist_foreach( trainer_list, trainer_add, store );
|
||||
}
|
||||
model = GTK_TREE_MODEL( store );
|
||||
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW( poke_list ), model );
|
||||
|
||||
g_object_unref( model );
|
||||
}
|
||||
|
||||
static void
|
||||
trainer_add( gpointer data, gpointer user_data )
|
||||
{
|
||||
gchar *val = NULL;
|
||||
GtkListStore *store = user_data;
|
||||
GtkTreeIter iter;
|
||||
trainer_t *trainer = data;
|
||||
|
||||
if( !trainer ) return;
|
||||
|
||||
if( trainer->ask_value )
|
||||
val = g_strdup_printf( "%d", trainer->value );
|
||||
|
||||
/* Append a new row and fill data */
|
||||
gtk_list_store_append( store, &iter );
|
||||
gtk_list_store_set( store, &iter,
|
||||
COL_CHECK, trainer->active,
|
||||
COL_NAME, trainer->name,
|
||||
COL_VALUE, ( trainer->ask_value )? val : NULL,
|
||||
COL_TRAINER, trainer,
|
||||
COL_ENABLED, !trainer->disabled,
|
||||
COL_EDITABLE, ( !trainer->disabled && !trainer->active
|
||||
&& trainer->ask_value ),
|
||||
COL_INCONSISTENT, ( trainer->disabled && !trainer->active ),
|
||||
-1);
|
||||
|
||||
g_free( val );
|
||||
}
|
||||
|
||||
static void
|
||||
pokemem_close( GtkWidget *widget, gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
gtkui_destroy_widget_and_quit( widget, NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
pokemem_update_list( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
|
||||
model = gtk_tree_view_get_model( GTK_TREE_VIEW( poke_list ) );
|
||||
if( model ) {
|
||||
gtk_tree_model_foreach( model, pokemem_update_trainer, NULL );
|
||||
}
|
||||
|
||||
gtkui_destroy_widget_and_quit( dialog, NULL );
|
||||
}
|
||||
|
||||
gboolean
|
||||
pokemem_update_trainer( GtkTreeModel *model, GtkTreePath *path GCC_UNUSED,
|
||||
GtkTreeIter *iter, gpointer data GCC_UNUSED )
|
||||
{
|
||||
gboolean selected;
|
||||
trainer_t *trainer;
|
||||
|
||||
gtk_tree_model_get( model, iter,
|
||||
COL_CHECK, &selected,
|
||||
COL_TRAINER, &trainer,
|
||||
-1);
|
||||
|
||||
if( selected ) {
|
||||
pokemem_trainer_activate( trainer );
|
||||
} else {
|
||||
pokemem_trainer_deactivate( trainer );
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
pokemem_add_custom_poke( GtkWidget *widget GCC_UNUSED,
|
||||
gpointer user_data GCC_UNUSED )
|
||||
{
|
||||
long b, a, v;
|
||||
const gchar *entry;
|
||||
char *endptr;
|
||||
int base;
|
||||
trainer_t *trainer;
|
||||
GtkListStore *store;
|
||||
|
||||
if( gtk_entry_get_text_length( GTK_ENTRY( address ) ) == 0 &&
|
||||
gtk_entry_get_text_length( GTK_ENTRY( value ) ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse bank */
|
||||
entry = gtk_entry_get_text( GTK_ENTRY( bank ) );
|
||||
errno = 0;
|
||||
b = strtol( entry, &endptr, 10 );
|
||||
if( errno || b < 0 || b > 8 ) {
|
||||
ui_error( UI_ERROR_ERROR, "Invalid bank: use an integer from 0 to 8" );
|
||||
gtk_widget_grab_focus( bank );
|
||||
return;
|
||||
}
|
||||
|
||||
if( endptr == entry ) b = 8; /* ignore bank by default */
|
||||
|
||||
/* Parse address */
|
||||
entry = gtk_entry_get_text( GTK_ENTRY( address ) );
|
||||
errno = 0;
|
||||
base = ( g_str_has_prefix( entry, "0x" ) )? 16 : 10;
|
||||
a = strtol( entry, &endptr, base );
|
||||
|
||||
if( errno || a < 0 || a > 65535 || endptr == entry ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Invalid address: use an integer from 0 to 65535" );
|
||||
gtk_widget_grab_focus( address );
|
||||
return;
|
||||
}
|
||||
|
||||
if( b == 8 && a < 16384 ) {
|
||||
ui_error( UI_ERROR_ERROR,
|
||||
"Invalid address: use an integer from 16384 to 65535" );
|
||||
gtk_widget_grab_focus( address );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse value */
|
||||
entry = gtk_entry_get_text( GTK_ENTRY( value ) );
|
||||
errno = 0;
|
||||
v = strtol( entry, &endptr, 10 );
|
||||
|
||||
if( errno || v < 0 || v > 256 || endptr == entry ) {
|
||||
ui_error( UI_ERROR_ERROR, "Invalid value: use an integer from 0 to 256" );
|
||||
gtk_widget_grab_focus( value );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update store and view */
|
||||
trainer = pokemem_trainer_list_add( b, a, v );
|
||||
if( !trainer ) {
|
||||
ui_error( UI_ERROR_ERROR, "Cannot add trainer" );
|
||||
return;
|
||||
}
|
||||
|
||||
store = GTK_LIST_STORE( gtk_tree_view_get_model(
|
||||
GTK_TREE_VIEW( poke_list ) ) );
|
||||
trainer_add( trainer, store );
|
||||
|
||||
/* Mark custom trainer for activate */
|
||||
GtkTreeModel *model;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
int rows;
|
||||
|
||||
if( !trainer->active && !trainer->disabled ) {
|
||||
model = gtk_tree_view_get_model( GTK_TREE_VIEW( poke_list ) );
|
||||
rows = gtk_tree_model_iter_n_children( model, NULL );
|
||||
path = gtk_tree_path_new_from_indices( rows - 1, -1 );
|
||||
gtk_tree_model_get_iter( model, &iter, path );
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter, COL_CHECK, TRUE, -1 );
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
|
||||
/* Clear custom fields */
|
||||
gtk_editable_delete_text( GTK_EDITABLE( bank ), 0, -1 );
|
||||
gtk_editable_delete_text( GTK_EDITABLE( address ), 0, -1 );
|
||||
gtk_editable_delete_text( GTK_EDITABLE( value ), 0, -1 );
|
||||
gtk_widget_grab_focus( address );
|
||||
}
|
||||
|
||||
void
|
||||
entry_validate_digit( GtkEntry *entry, const gchar *text, gint length,
|
||||
gint *position, gpointer data )
|
||||
{
|
||||
int i, count = 0;
|
||||
GtkEditable *editable = GTK_EDITABLE( entry );
|
||||
gchar *result = g_new( gchar, length );
|
||||
|
||||
/* Validate decimal and fill buffer to insert */
|
||||
for( i = 0; i < length; i++ ) {
|
||||
if( !isdigit( text[i] ) ) continue;
|
||||
|
||||
result[ count++ ] = text[i];
|
||||
}
|
||||
|
||||
/* Insert only validated text */
|
||||
if( count > 0 ) {
|
||||
g_signal_handlers_block_by_func( G_OBJECT( editable ),
|
||||
G_CALLBACK( entry_validate_digit ),
|
||||
data );
|
||||
gtk_editable_insert_text( editable, result, count, position );
|
||||
g_signal_handlers_unblock_by_func( G_OBJECT( editable ),
|
||||
G_CALLBACK( entry_validate_digit ),
|
||||
data );
|
||||
}
|
||||
g_signal_stop_emission_by_name( G_OBJECT( editable ), "insert_text" );
|
||||
|
||||
g_free( result );
|
||||
}
|
||||
|
||||
void
|
||||
entry_validate_address( GtkEntry *entry, const gchar *text, gint length,
|
||||
gint *position, gpointer data )
|
||||
{
|
||||
int i, is_valid, is_hex;
|
||||
size_t n, prev_length, max_length, count = 0;
|
||||
GtkEditable *editable = GTK_EDITABLE( entry );
|
||||
GString *full_text;
|
||||
gchar *result = NULL;
|
||||
|
||||
/* Validate hex or decimal number */
|
||||
full_text = g_string_new( gtk_entry_get_text( entry ) );
|
||||
prev_length = full_text->len;
|
||||
full_text = g_string_insert_len( full_text, *position, text, length );
|
||||
is_valid = 1;
|
||||
is_hex = 0;
|
||||
|
||||
for( n = 0; n < full_text->len && is_valid; n++ ) {
|
||||
|
||||
switch( n ) {
|
||||
case 0:
|
||||
is_valid = isdigit( full_text->str[n] );
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if( full_text->str[n] == 'x' ) {
|
||||
if( full_text->str[0] == '0' ) is_hex = 1;
|
||||
is_valid = is_hex;
|
||||
} else {
|
||||
is_valid = isdigit( full_text->str[n] );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
is_valid = ( is_hex )? isxdigit( full_text->str[n] ) :
|
||||
isdigit( full_text->str[n] );
|
||||
}
|
||||
|
||||
}
|
||||
g_string_free( full_text, TRUE );
|
||||
|
||||
/* Fill buffer to insert */
|
||||
if( is_valid ) {
|
||||
max_length = ( is_hex )? 6 : 5;
|
||||
result = g_new( gchar, length );
|
||||
|
||||
for( i = 0; i < length && i + prev_length < max_length; i++ ) {
|
||||
result[ count++ ] = text[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert only validated text */
|
||||
if( count > 0 ) {
|
||||
g_signal_handlers_block_by_func( G_OBJECT( editable ),
|
||||
G_CALLBACK( entry_validate_address ),
|
||||
data );
|
||||
gtk_editable_insert_text( editable, result, count, position );
|
||||
g_signal_handlers_unblock_by_func( G_OBJECT( editable ),
|
||||
G_CALLBACK( entry_validate_address ),
|
||||
data );
|
||||
}
|
||||
|
||||
g_signal_stop_emission_by_name( G_OBJECT( editable ), "insert_text" );
|
||||
g_free( result );
|
||||
}
|
||||
|
||||
void
|
||||
row_toggled_callback( GtkCellRendererToggle *cell GCC_UNUSED,
|
||||
gchar *path_string, gpointer user_data )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model = user_data;
|
||||
GValue val, gtrainer;
|
||||
trainer_t *trainer;
|
||||
gboolean flag;
|
||||
|
||||
/* Toggle current selection */
|
||||
gtk_tree_model_get_iter_from_string( model, &iter, path_string );
|
||||
memset( &val, 0, sizeof( val ) );
|
||||
gtk_tree_model_get_value( model, &iter, COL_CHECK, &val );
|
||||
flag = !g_value_get_boolean( &val );
|
||||
g_value_unset( &val );
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter, COL_CHECK, flag, -1 );
|
||||
|
||||
/* Request user for custom value */
|
||||
if( flag ) {
|
||||
memset( >rainer, 0, sizeof( gtrainer ) );
|
||||
gtk_tree_model_get_value( model, &iter, COL_TRAINER, >rainer );
|
||||
trainer = (trainer_t *)g_value_get_pointer( >rainer );
|
||||
g_value_unset( >rainer );
|
||||
|
||||
if( trainer->ask_value ) {
|
||||
g_idle_add( (GSourceFunc)custom_value_edit, GTK_TREE_VIEW( poke_list ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_value_edit( GtkTreeView *tree )
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeViewColumn *col, *col_current;
|
||||
|
||||
gtk_tree_view_get_cursor( tree, &path, &col_current );
|
||||
if( !path || !col_current ) return FALSE;
|
||||
|
||||
col = gtk_tree_view_get_column( tree, COL_VALUE );
|
||||
gtk_tree_view_set_cursor( tree, path, col, TRUE );
|
||||
gtk_tree_path_free( path );
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
custom_value_changed( GtkCellRendererText *cell GCC_UNUSED, gchar *path_string,
|
||||
gchar *new_text, gpointer user_data )
|
||||
{
|
||||
GtkTreeModel *model = (GtkTreeModel *)user_data;
|
||||
GtkTreeIter iter;
|
||||
GValue val;
|
||||
gchar *str_value;
|
||||
trainer_t *trainer;
|
||||
long v;
|
||||
char *endptr;
|
||||
|
||||
if( gtk_tree_model_get_iter_from_string( model, &iter, path_string ) ) {
|
||||
memset( &val, 0, sizeof( val ) );
|
||||
|
||||
gtk_tree_model_get_value( model, &iter, COL_TRAINER, &val );
|
||||
trainer = (trainer_t *)g_value_get_pointer( &val );
|
||||
g_value_unset( &val );
|
||||
|
||||
errno = 0;
|
||||
v = strtol( new_text, &endptr, 10 );
|
||||
|
||||
if( errno || v < 0 || v > 255 || endptr == new_text ) {
|
||||
ui_error( UI_ERROR_ERROR, "Invalid value: use an integer from 0 to 255" );
|
||||
return;
|
||||
}
|
||||
|
||||
trainer->value = v;
|
||||
str_value = g_strdup_printf( "%d", trainer->value );
|
||||
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_VALUE, str_value, -1);
|
||||
g_free( str_value );
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/* rollback.c: select a rollback point
|
||||
Copyright (c) 2004 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "fuse.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
GtkWidget *dialog, *list;
|
||||
|
||||
static int dialog_created = 0;
|
||||
static int current_block;
|
||||
|
||||
/* List columns */
|
||||
enum
|
||||
{
|
||||
COL_SECONDS = 0,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
static void
|
||||
select_row( GtkButton *button GCC_UNUSED, gpointer user_data )
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeView *view = user_data;
|
||||
GtkTreeViewColumn *focus_column;
|
||||
|
||||
current_block = -1;
|
||||
|
||||
/* Get selected row */
|
||||
gtk_tree_view_get_cursor( GTK_TREE_VIEW( view ), &path, &focus_column );
|
||||
if( path ) {
|
||||
int *indices = gtk_tree_path_get_indices( path );
|
||||
if( indices ) current_block = indices[0];
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_rollback_list( void )
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeModel *model;
|
||||
GtkListStore *store;
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
/* Add columns */
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( view ),
|
||||
-1,
|
||||
"Seconds",
|
||||
renderer,
|
||||
"text", COL_SECONDS,
|
||||
NULL );
|
||||
|
||||
/* Create data model */
|
||||
store = gtk_list_store_new( NUM_COLS, G_TYPE_STRING );
|
||||
|
||||
model = GTK_TREE_MODEL( store );
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW( view ), model );
|
||||
g_object_unref( model );
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
static int
|
||||
create_dialog( void )
|
||||
{
|
||||
GtkWidget *content_area;
|
||||
|
||||
dialog = gtkstock_dialog_new( "Fuse - Select Rollback Point", NULL );
|
||||
|
||||
list = create_rollback_list();
|
||||
|
||||
gtkstock_create_ok_cancel( dialog, NULL, G_CALLBACK( select_row ), list,
|
||||
DEFAULT_DESTROY, DEFAULT_DESTROY );
|
||||
|
||||
content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
|
||||
gtk_box_pack_start( GTK_BOX( content_area ), list, TRUE, TRUE, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
update_list( GSList *points )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model;
|
||||
|
||||
model = gtk_tree_view_get_model( GTK_TREE_VIEW( list ) );
|
||||
|
||||
gtk_list_store_clear( GTK_LIST_STORE( model ) );
|
||||
|
||||
while( points ) {
|
||||
gchar buffer[256];
|
||||
|
||||
snprintf( buffer, 256, "%.2f", GPOINTER_TO_INT( points->data ) / 50.0 );
|
||||
|
||||
/* Append a new row and fill data */
|
||||
gtk_list_store_append( GTK_LIST_STORE( model ), &iter );
|
||||
gtk_list_store_set( GTK_LIST_STORE( model ), &iter,
|
||||
COL_SECONDS, buffer,
|
||||
-1 );
|
||||
|
||||
points = points->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_get_rollback_point( GSList *points )
|
||||
{
|
||||
fuse_emulation_pause();
|
||||
|
||||
if( !dialog_created )
|
||||
if( create_dialog() ) { fuse_emulation_unpause(); return -1; }
|
||||
|
||||
if( update_list( points ) ) { fuse_emulation_unpause(); return -1; }
|
||||
|
||||
current_block = -1;
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
|
||||
gtk_main();
|
||||
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return current_block;
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/* roms.c: ROM selector dialog box
|
||||
Copyright (c) 2003-2004 Philip Kendall
|
||||
Copyright (c) 2015 Stuart Brady
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "fuse.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "menu.h"
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static void add_rom( GtkBox *parent, size_t start, gint row,
|
||||
int is_peripheral );
|
||||
static void select_new_rom( GtkWidget *widget, gpointer data );
|
||||
static void roms_done( GtkButton *button, gpointer data );
|
||||
|
||||
/* The labels used to display the current ROMs */
|
||||
static GtkWidget *rom[ SETTINGS_ROM_COUNT ];
|
||||
|
||||
struct callback_info {
|
||||
|
||||
size_t start, n;
|
||||
int is_peripheral;
|
||||
|
||||
};
|
||||
|
||||
int
|
||||
menu_select_roms_with_title( const char *title, size_t start, size_t n,
|
||||
int is_peripheral )
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkBox *vbox;
|
||||
|
||||
struct callback_info info;
|
||||
|
||||
char buffer[ 256 ];
|
||||
size_t i;
|
||||
|
||||
/* Firstly, stop emulation */
|
||||
fuse_emulation_pause();
|
||||
|
||||
/* Give me a new dialog box */
|
||||
snprintf( buffer, 256, "Fuse - Select ROMs - %s", title );
|
||||
dialog = gtkstock_dialog_new( buffer, NULL );
|
||||
|
||||
info.start = start;
|
||||
info.n = n;
|
||||
info.is_peripheral = is_peripheral;
|
||||
|
||||
/* Create the OK and Cancel buttons */
|
||||
gtkstock_create_ok_cancel( dialog, NULL, G_CALLBACK( roms_done ), &info,
|
||||
DEFAULT_DESTROY, DEFAULT_DESTROY );
|
||||
|
||||
/* And the current values of each of the ROMs */
|
||||
vbox = GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG( dialog ) ) );
|
||||
|
||||
gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
|
||||
for( i = 0; i < n; i++ ) add_rom( vbox, start, i, is_peripheral );
|
||||
|
||||
/* Users shouldn't be able to resize this window */
|
||||
gtk_window_set_resizable( GTK_WINDOW( dialog ), FALSE );
|
||||
|
||||
/* Display the window */
|
||||
gtk_widget_show_all( dialog );
|
||||
|
||||
/* Process events until the window is done with */
|
||||
gtk_main();
|
||||
|
||||
/* And then carry on with emulation again */
|
||||
fuse_emulation_unpause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
add_rom( GtkBox *parent, size_t start, gint row, int is_peripheral )
|
||||
{
|
||||
GtkWidget *frame, *hbox, *change_button;
|
||||
char buffer[ 80 ], **setting;
|
||||
|
||||
snprintf( buffer, 80, "ROM %d", row );
|
||||
frame = gtk_frame_new( buffer );
|
||||
gtk_box_pack_start( parent, frame, FALSE, FALSE, 2 );
|
||||
|
||||
hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 4 );
|
||||
gtk_container_set_border_width( GTK_CONTAINER( hbox ), 4 );
|
||||
gtk_container_add( GTK_CONTAINER( frame ), hbox );
|
||||
|
||||
setting = settings_get_rom_setting( &settings_current, start + row,
|
||||
is_peripheral );
|
||||
rom[ row ] = gtk_entry_new();
|
||||
gtk_entry_set_text( GTK_ENTRY( rom[ row ] ), *setting );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), rom[ row ], FALSE, FALSE, 2 );
|
||||
|
||||
change_button = gtk_button_new_with_label( "Select..." );
|
||||
g_signal_connect( G_OBJECT( change_button ), "clicked",
|
||||
G_CALLBACK( select_new_rom ),
|
||||
rom[ row ] );
|
||||
gtk_box_pack_start( GTK_BOX( hbox ), change_button, FALSE, FALSE, 2 );
|
||||
}
|
||||
|
||||
static void
|
||||
select_new_rom( GtkWidget *widget GCC_UNUSED, gpointer data )
|
||||
{
|
||||
char *filename;
|
||||
|
||||
GtkWidget *entry = data;
|
||||
|
||||
filename = ui_get_open_filename( "Fuse - Select ROM" );
|
||||
if( !filename ) return;
|
||||
|
||||
gtk_entry_set_text( GTK_ENTRY( entry ), filename );
|
||||
}
|
||||
|
||||
static void
|
||||
roms_done( GtkButton *button GCC_UNUSED, gpointer data )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
char **setting; const char *string;
|
||||
|
||||
struct callback_info *info = data;
|
||||
|
||||
for( i = 0; i < info->n; i++ ) {
|
||||
|
||||
setting = settings_get_rom_setting( &settings_current, info->start + i,
|
||||
info->is_peripheral );
|
||||
string = gtk_entry_get_text( GTK_ENTRY( rom[i] ) );
|
||||
|
||||
settings_set_string( setting, string );
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/* statusbar.c: routines for updating the status bar
|
||||
Copyright (c) 2003-2004 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static GtkWidget *status_bar;
|
||||
|
||||
static GdkPixbuf
|
||||
*pixbuf_tape_inactive, *pixbuf_tape_active,
|
||||
*pixbuf_mdr_inactive, *pixbuf_mdr_active,
|
||||
*pixbuf_disk_inactive, *pixbuf_disk_active,
|
||||
*pixbuf_pause_inactive, *pixbuf_pause_active,
|
||||
*pixbuf_mouse_inactive, *pixbuf_mouse_active;
|
||||
|
||||
static GtkWidget
|
||||
*microdrive_status, /* Is any microdrive motor running? */
|
||||
*disk_status, /* Is the disk motor running? */
|
||||
*mouse_status, /* Have we grabbed the mouse? */
|
||||
*pause_status, /* Is emulation paused (via the menu option)? */
|
||||
*tape_status, /* Is the tape running? */
|
||||
*speed_status, /* How fast are we running? */
|
||||
*machine_name; /* What machine is being emulated? */
|
||||
|
||||
int
|
||||
gtkstatusbar_create( GtkBox *parent )
|
||||
{
|
||||
GtkWidget *separator;
|
||||
|
||||
status_bar = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 5 );
|
||||
gtk_box_pack_start( parent, status_bar, FALSE, FALSE, 3 );
|
||||
|
||||
/* FIXME: unref these pixbuf on statusbar destroy */
|
||||
pixbuf_tape_inactive =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_tape_inactive );
|
||||
pixbuf_tape_active =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_tape_active );
|
||||
|
||||
pixbuf_mdr_inactive =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_mdr_inactive );
|
||||
pixbuf_mdr_active =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_mdr_active );
|
||||
|
||||
pixbuf_disk_inactive =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_disk_inactive );
|
||||
pixbuf_disk_active =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_disk_active );
|
||||
|
||||
pixbuf_pause_inactive =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_pause_inactive );
|
||||
pixbuf_pause_active =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_pause_active );
|
||||
|
||||
pixbuf_mouse_inactive =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_mouse_inactive );
|
||||
pixbuf_mouse_active =
|
||||
gdk_pixbuf_new_from_xpm_data( gtkpixmap_mouse_active );
|
||||
|
||||
speed_status = gtk_label_new( "100%" );
|
||||
gtk_label_set_width_chars( GTK_LABEL( speed_status ), 8 );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), speed_status, FALSE, FALSE, 0 );
|
||||
|
||||
separator = gtk_separator_new( GTK_ORIENTATION_VERTICAL );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), separator, FALSE, FALSE, 0 );
|
||||
|
||||
tape_status = gtk_image_new_from_pixbuf( pixbuf_tape_inactive );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), tape_status, FALSE, FALSE, 0 );
|
||||
|
||||
microdrive_status = gtk_image_new_from_pixbuf( pixbuf_mdr_inactive );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), microdrive_status, FALSE, FALSE,
|
||||
0 );
|
||||
|
||||
disk_status = gtk_image_new_from_pixbuf( pixbuf_disk_inactive );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), disk_status, FALSE, FALSE, 0 );
|
||||
|
||||
pause_status = gtk_image_new_from_pixbuf( pixbuf_pause_inactive );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), pause_status, FALSE, FALSE, 0 );
|
||||
|
||||
mouse_status = gtk_image_new_from_pixbuf( pixbuf_mouse_inactive );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), mouse_status, FALSE, FALSE, 0 );
|
||||
|
||||
separator = gtk_separator_new( GTK_ORIENTATION_VERTICAL );
|
||||
gtk_box_pack_end( GTK_BOX( status_bar ), separator, FALSE, FALSE, 0 );
|
||||
|
||||
machine_name = gtk_label_new( NULL );
|
||||
gtk_box_pack_start( GTK_BOX( status_bar ), machine_name, FALSE, FALSE, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gtkstatusbar_get_height( void )
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
|
||||
gtk_widget_get_allocation( status_bar, &alloc );
|
||||
|
||||
return alloc.height + 6; /* status bar + vbox padding */
|
||||
}
|
||||
|
||||
int
|
||||
gtkstatusbar_set_visibility( int visible )
|
||||
{
|
||||
gtkdisplay_update_geometry();
|
||||
|
||||
if( visible ) {
|
||||
gtk_widget_show( status_bar );
|
||||
} else {
|
||||
gtk_widget_hide( status_bar );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gtkstatusbar_update_machine( const char *name )
|
||||
{
|
||||
gtk_label_set_text( GTK_LABEL( machine_name ), name );
|
||||
}
|
||||
|
||||
int
|
||||
ui_statusbar_update( ui_statusbar_item item, ui_statusbar_state state )
|
||||
{
|
||||
GdkPixbuf *which;
|
||||
|
||||
switch( item ) {
|
||||
|
||||
case UI_STATUSBAR_ITEM_DISK:
|
||||
switch( state ) {
|
||||
case UI_STATUSBAR_STATE_NOT_AVAILABLE:
|
||||
gtk_widget_hide( disk_status ); break;
|
||||
case UI_STATUSBAR_STATE_ACTIVE:
|
||||
gtk_widget_show( disk_status );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( disk_status ), pixbuf_disk_active );
|
||||
break;
|
||||
default:
|
||||
gtk_widget_show( disk_status );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( disk_status ),
|
||||
pixbuf_disk_inactive );
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case UI_STATUSBAR_ITEM_MOUSE:
|
||||
which = ( state == UI_STATUSBAR_STATE_ACTIVE ?
|
||||
pixbuf_mouse_active : pixbuf_mouse_inactive );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( mouse_status ), which );
|
||||
return 0;
|
||||
|
||||
case UI_STATUSBAR_ITEM_PAUSED:
|
||||
which = ( state == UI_STATUSBAR_STATE_ACTIVE ?
|
||||
pixbuf_pause_active : pixbuf_pause_inactive );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( pause_status ), which );
|
||||
return 0;
|
||||
|
||||
case UI_STATUSBAR_ITEM_MICRODRIVE:
|
||||
switch( state ) {
|
||||
case UI_STATUSBAR_STATE_NOT_AVAILABLE:
|
||||
gtk_widget_hide( microdrive_status ); break;
|
||||
case UI_STATUSBAR_STATE_ACTIVE:
|
||||
gtk_widget_show( microdrive_status );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( microdrive_status ),
|
||||
pixbuf_mdr_active );
|
||||
break;
|
||||
default:
|
||||
gtk_widget_show( microdrive_status );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( microdrive_status ),
|
||||
pixbuf_mdr_inactive );
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case UI_STATUSBAR_ITEM_TAPE:
|
||||
which = ( state == UI_STATUSBAR_STATE_ACTIVE ?
|
||||
pixbuf_tape_active : pixbuf_tape_inactive );
|
||||
gtk_image_set_from_pixbuf( GTK_IMAGE( tape_status ), which );
|
||||
|
||||
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 buffer[8];
|
||||
|
||||
snprintf( buffer, 8, "%3.0f%%", speed );
|
||||
gtk_label_set_text( GTK_LABEL( speed_status ), buffer );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/* stock.c: 'standard' GTK widgets etc
|
||||
Copyright (c) 2004 Darren Salt, Philip Kendall
|
||||
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:
|
||||
|
||||
Philip Kendall <[email protected]>
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkinternals.h"
|
||||
|
||||
GtkAccelGroup*
|
||||
gtkstock_add_accel_group( GtkWidget *widget )
|
||||
{
|
||||
GtkAccelGroup *accel = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group( GTK_WINDOW( widget ), accel );
|
||||
return accel;
|
||||
}
|
||||
|
||||
static void
|
||||
add_click( GtkWidget *btn, const gtkstock_button *button, GtkAccelGroup *accel,
|
||||
guint key1, GdkModifierType mod1, guint key2, GdkModifierType mod2 )
|
||||
{
|
||||
if( button->shortcut ) {
|
||||
if( button->shortcut != GDK_KEY_VoidSymbol )
|
||||
gtk_widget_add_accelerator( btn, "clicked", accel, button->shortcut,
|
||||
button->modifier, 0 );
|
||||
} else if( key1 ) {
|
||||
gtk_widget_add_accelerator( btn, "clicked", accel, key1, mod1, 0 );
|
||||
}
|
||||
|
||||
if( button->shortcut_alt ) {
|
||||
if( button->shortcut_alt != GDK_KEY_VoidSymbol )
|
||||
gtk_widget_add_accelerator( btn, "clicked", accel, button->shortcut_alt,
|
||||
button->modifier_alt, 0 );
|
||||
}
|
||||
else if( key2 ) {
|
||||
gtk_widget_add_accelerator( btn, "clicked", accel, key2, mod2, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gtkstock_create_button( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
const gtkstock_button *button )
|
||||
{
|
||||
GtkWidget *btn;
|
||||
gboolean link_object = ( button->label[0] == '!' );
|
||||
const gchar *button_label = button->label + link_object;
|
||||
|
||||
if( !accel ) accel = gtkstock_add_accel_group (widget);
|
||||
|
||||
if( GTK_IS_DIALOG( widget ) ) {
|
||||
btn = gtk_dialog_add_button( GTK_DIALOG( widget ), button_label,
|
||||
button->response_id );
|
||||
} else {
|
||||
btn = gtk_button_new_with_mnemonic( button_label );
|
||||
gtk_container_add( GTK_CONTAINER( widget ), btn );
|
||||
}
|
||||
|
||||
if( button->action ) {
|
||||
if( link_object ) {
|
||||
g_signal_connect_swapped( G_OBJECT( btn ), "clicked",
|
||||
button->action,
|
||||
G_OBJECT( button->actiondata ) );
|
||||
} else {
|
||||
g_signal_connect( G_OBJECT( btn ), "clicked", button->action,
|
||||
button->actiondata );
|
||||
}
|
||||
}
|
||||
|
||||
if( button->destroy )
|
||||
g_signal_connect_swapped( G_OBJECT( btn ), "clicked", button->destroy,
|
||||
G_OBJECT( widget ) );
|
||||
|
||||
GtkWidget *icon = NULL;
|
||||
if( !strcmp( button->label, "_Close" ) )
|
||||
icon = gtk_image_new_from_icon_name( "window-close", GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_Open" ) )
|
||||
icon = gtk_image_new_from_icon_name( "document-open",GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_Save" ) )
|
||||
icon = gtk_image_new_from_icon_name( "document-save",GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_Add" ) )
|
||||
icon = gtk_image_new_from_icon_name( "list-add", GTK_ICON_SIZE_BUTTON );
|
||||
|
||||
#if !GTK_CHECK_VERSION( 3, 0, 0 )
|
||||
/* Note: ok/cancel/yes/no icons are not covered by freedesktop's Icon Naming
|
||||
Specification 0.8.90 */
|
||||
else if( !strcmp( button->label, "_OK" ) )
|
||||
icon = gtk_image_new_from_icon_name( GTK_STOCK_OK, GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_Cancel" ) )
|
||||
icon = gtk_image_new_from_icon_name( GTK_STOCK_CANCEL,
|
||||
GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_Yes" ) )
|
||||
icon = gtk_image_new_from_icon_name( GTK_STOCK_YES, GTK_ICON_SIZE_BUTTON );
|
||||
else if( !strcmp( button->label, "_No" ) )
|
||||
icon = gtk_image_new_from_icon_name( GTK_STOCK_NO, GTK_ICON_SIZE_BUTTON );
|
||||
#endif
|
||||
|
||||
if( icon ) gtk_button_set_image( GTK_BUTTON( btn ), icon );
|
||||
|
||||
add_click( btn, button, accel, 0, 0, 0, 0 );
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
GtkAccelGroup*
|
||||
gtkstock_create_buttons( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
const gtkstock_button *buttons, size_t count )
|
||||
{
|
||||
if( !accel ) accel = gtkstock_add_accel_group( widget );
|
||||
|
||||
for( ; count; buttons++, count-- )
|
||||
gtkstock_create_button( widget, accel, buttons );
|
||||
|
||||
return accel;
|
||||
}
|
||||
|
||||
GtkAccelGroup*
|
||||
gtkstock_create_ok_cancel( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
GCallback action, gpointer actiondata,
|
||||
GCallback destroy_ok, GCallback destroy_cancel )
|
||||
{
|
||||
gtkstock_button btn[] = {
|
||||
{ "_Cancel", NULL, NULL, NULL, GDK_KEY_Escape, 0, 0, 0, GTK_RESPONSE_CANCEL},
|
||||
{ "_OK", NULL, NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_OK},
|
||||
};
|
||||
btn[0].destroy = destroy_cancel ? destroy_cancel : NULL;
|
||||
btn[1].destroy = destroy_ok ? destroy_ok : NULL;
|
||||
btn[1].action = action;
|
||||
btn[1].actiondata = actiondata;
|
||||
|
||||
return gtkstock_create_buttons( widget, accel, btn,
|
||||
ARRAY_SIZE( btn ) );
|
||||
}
|
||||
|
||||
GtkAccelGroup*
|
||||
gtkstock_create_close( GtkWidget *widget, GtkAccelGroup *accel,
|
||||
GCallback destroy, gboolean esconly )
|
||||
{
|
||||
gtkstock_button btn = {
|
||||
"_Close", NULL, NULL, (destroy ? destroy : DEFAULT_DESTROY),
|
||||
(esconly ? GDK_KEY_VoidSymbol : GDK_KEY_Return), 0, GDK_KEY_Escape, 0,
|
||||
GTK_RESPONSE_CLOSE
|
||||
};
|
||||
return gtkstock_create_buttons( widget, accel, &btn, 1 );
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gtkstock_dialog_new( const gchar *title, GCallback destroy )
|
||||
{
|
||||
GtkWidget *dialog = gtk_dialog_new();
|
||||
if( title ) gtk_window_set_title( GTK_WINDOW( dialog ), title );
|
||||
/* TODO: allow to keep the dialog after closing for gtk_dialog_run() */
|
||||
g_signal_connect( G_OBJECT( dialog ), "delete-event",
|
||||
destroy ? destroy : DEFAULT_DESTROY, NULL );
|
||||
if( destroy == NULL ) gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
|
||||
gtk_window_set_transient_for( GTK_WINDOW( dialog ),
|
||||
GTK_WINDOW( gtkui_window ) );
|
||||
|
||||
return dialog;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
## Copyright (c) 2001-2017 Philip Kendall, 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: [email protected]
|
||||
|
||||
if UI_NULL
|
||||
|
||||
fuse_SOURCES += $(ui_null_files)
|
||||
|
||||
BUILT_SOURCES += $(ui_null_built)
|
||||
|
||||
endif
|
||||
|
||||
CLEANFILES += $(ui_null_built)
|
||||
|
||||
ui_null_files = \
|
||||
ui/null/null_ui.c \
|
||||
ui/null/options.c
|
||||
|
||||
ui_null_built = \
|
||||
ui/null/options.c
|
||||
|
||||
EXTRA_DIST += \
|
||||
ui/null/options.pl \
|
||||
ui/null/options-header.pl
|
||||
|
||||
ui/null/options.c: $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/null/options.pl $(srcdir)/ui/options.dat $(srcdir)/perl/Fuse.pm $(srcdir)/perl/Fuse/Dialog.pm
|
||||
@$(MKDIR_P) ui/null
|
||||
$(AM_V_GEN)$(PERL) $(srcdir)/perl/cpp-perl.pl config.h $(srcdir)/ui/options.dat | $(PERL) -I$(srcdir)/perl $(srcdir)/ui/null/options.pl - > $@.tmp && mv $@.tmp $@
|
||||
@@ -1,259 +0,0 @@
|
||||
/* null_ui.c: Routines for dealing with the null user interface
|
||||
Copyright (c) 2017 Philip Kendall
|
||||
|
||||
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: [email protected]
|
||||
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "../uijoystick.c"
|
||||
|
||||
keysyms_map_t keysyms_map[] = {
|
||||
{ 0, 0 } /* End marker */
|
||||
};
|
||||
|
||||
scaler_type
|
||||
menu_get_scaler( scaler_available_fn selector )
|
||||
{
|
||||
/* No scaler selected */
|
||||
return SCALER_NUM;
|
||||
}
|
||||
|
||||
int
|
||||
menu_select_roms_with_title( const char *title, size_t start, size_t count,
|
||||
int is_peripheral )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ui_breakpoints_updated( void )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
ui_confirm_save_t
|
||||
ui_confirm_save_specific( const char *message )
|
||||
{
|
||||
return UI_CONFIRM_SAVE_DONTSAVE;
|
||||
}
|
||||
|
||||
ui_confirm_joystick_t
|
||||
ui_confirm_joystick( libspectrum_joystick libspectrum_type, int inputs )
|
||||
{
|
||||
return UI_CONFIRM_JOYSTICK_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
ui_debugger_activate( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_debugger_deactivate( int interruptable )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_debugger_disassemble( libspectrum_word addr )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_debugger_update( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_end( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_error_specific( ui_error_level severity, const char *message )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_event( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
char*
|
||||
ui_get_open_filename( const char *title )
|
||||
{
|
||||
/* No filename */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
ui_get_rollback_point( GSList *points )
|
||||
{
|
||||
/* No rollback point */
|
||||
return -1;
|
||||
}
|
||||
|
||||
char*
|
||||
ui_get_save_filename( const char *title )
|
||||
{
|
||||
/* No filename */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
ui_init( int *argc, char ***argv )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_menu_item_set_active( const char *path, int active )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_grab( int startup )
|
||||
{
|
||||
/* Successful grab */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ui_mouse_release( int suspend )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ui_pokemem_selector( const char *filename )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
int
|
||||
ui_query( const char *message )
|
||||
{
|
||||
/* Query confirmed */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ui_statusbar_update( ui_statusbar_item item, ui_statusbar_state state )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_statusbar_update_speed( float speed )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_tape_browser_update( ui_tape_browser_update_type change,
|
||||
libspectrum_tape_block *block )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ui_widgets_reset( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_area( int x, int y, int w, int h )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_end( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_frame_end( void )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_hotswap_gfx_mode( void )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
uidisplay_init( int width, int height )
|
||||
{
|
||||
/* No error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_plot16( int x, int y, libspectrum_word data,
|
||||
libspectrum_byte ink, libspectrum_byte paper )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_plot8( int x, int y, libspectrum_byte data,
|
||||
libspectrum_byte ink, libspectrum_byte paper )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
void
|
||||
uidisplay_putpixel( int x, int y, int colour )
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# options-header.pl: generate options dialog boxes
|
||||
# Copyright (c) 2001-2017 Philip Kendall
|
||||
|
||||
# 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: [email protected]
|
||||
|
||||
use strict;
|
||||
|
||||
use Fuse;
|
||||
use Fuse::Dialog;
|
||||
|
||||
die "No data file specified" unless @ARGV;
|
||||
|
||||
my @dialogs = Fuse::Dialog::read( shift @ARGV );
|
||||
|
||||
print Fuse::GPL( 'options.h: options dialog boxes public declarations',
|
||||
'2001-2017 Philip Kendall' );
|
||||
|
||||
print << "CODE";
|
||||
|
||||
/* This file is autogenerated from options.dat by options-header.pl.
|
||||
Do not edit unless you know what you\'re doing! */
|
||||
|
||||
#ifndef FUSE_OPTIONS_H
|
||||
#define FUSE_OPTIONS_H
|
||||
|
||||
CODE
|
||||
|
||||
foreach( @dialogs ) {
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
if( $widget->{type} eq "Combo" ) {
|
||||
print <<"CODE";
|
||||
int option_enumerate_$_->{name}_$widget->{value}( void );
|
||||
|
||||
CODE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print << "CODE";
|
||||
#endif /* #ifndef FUSE_OPTIONS_H */
|
||||
CODE
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# options.pl: generate options dialog boxes
|
||||
# Copyright (c) 2001-2015 Philip Kendall, Fredrick Meunier
|
||||
|
||||
# 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: [email protected]
|
||||
|
||||
use strict;
|
||||
|
||||
use lib '../perl';
|
||||
|
||||
use Fuse;
|
||||
use Fuse::Dialog;
|
||||
|
||||
die "No data file specified" unless @ARGV;
|
||||
|
||||
my %combo_sets;
|
||||
my %combo_default;
|
||||
my @dialogs = Fuse::Dialog::read( shift @ARGV );
|
||||
|
||||
print Fuse::GPL( 'options.c: options dialog boxes',
|
||||
'2001-2017 Philip Kendall' ) . << "CODE";
|
||||
|
||||
/* This file is autogenerated from options.dat by options.pl.
|
||||
Do not edit unless you know what you\'re doing! */
|
||||
|
||||
#include <config.h>
|
||||
CODE
|
||||
|
||||
foreach( @dialogs ) {
|
||||
foreach my $widget ( @{ $_->{widgets} } ) {
|
||||
if( $widget->{type} eq "Combo" ) {
|
||||
print << "CODE";
|
||||
|
||||
int
|
||||
option_enumerate_$_->{name}_$widget->{value}( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
CODE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
## Copyright (c) 2001 Philip Kendall
|
||||
## 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: [email protected]
|
||||
|
||||
fuse_SOURCES += ui/scaler/scaler.c
|
||||
|
||||
fuse_LDADD += \
|
||||
ui/scaler/scalers16.o \
|
||||
ui/scaler/scalers32.o
|
||||
|
||||
fuse_DEPENDENCIES += \
|
||||
ui/scaler/scalers16.o \
|
||||
ui/scaler/scalers32.o
|
||||
|
||||
ui/scaler/scalers16.o: $(srcdir)/ui/scaler/scalers.c
|
||||
$(AM_V_CC)$(COMPILE) -DSCALER_DATA_SIZE=2 -c $(srcdir)/ui/scaler/scalers.c -o $@
|
||||
|
||||
ui/scaler/scalers32.o: $(srcdir)/ui/scaler/scalers.c
|
||||
$(AM_V_CC)$(COMPILE) -DSCALER_DATA_SIZE=4 -c $(srcdir)/ui/scaler/scalers.c -o $@
|
||||
|
||||
noinst_HEADERS += \
|
||||
ui/scaler/scaler.h \
|
||||
ui/scaler/scaler_internals.h
|
||||
|
||||
EXTRA_DIST += \
|
||||
ui/scaler/scalers.c \
|
||||
ui/scaler/scaler_hq2x.c \
|
||||
ui/scaler/scaler_hq3x.c \
|
||||
ui/scaler/scaler_hq4x.c
|
||||
|
||||
CLEANFILES += \
|
||||
ui/scaler/scalers16.o \
|
||||
ui/scaler/scalers32.o
|
||||
@@ -1,284 +0,0 @@
|
||||
/* scaler.c: code for selecting (etc) scalers
|
||||
* Copyright (C) 2003 Fredrick Meunier, Philip Kendall
|
||||
* Copyright (c) 2015 Sergio Baldoví
|
||||
*
|
||||
* Originally taken from ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2001 Ludvig Strigeus
|
||||
* Copyright (C) 2001/2002 The ScummVM project
|
||||
*
|
||||
* HQ2x and HQ3x scalers taken from HiEnd3D Demos (http://www.hiend3d.com)
|
||||
* Copyright (C) 2003 MaxSt ( [email protected] )
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libspectrum.h>
|
||||
|
||||
#include "scaler.h"
|
||||
#include "scaler_internals.h"
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/uidisplay.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int scaler_supported[ SCALER_NUM ] = {0};
|
||||
|
||||
int scalers_registered = 0;
|
||||
|
||||
struct scaler_info {
|
||||
|
||||
const char *name;
|
||||
const char *id;
|
||||
enum scaler_flags_t flags;
|
||||
float scaling_factor;
|
||||
ScalerProc *scaler16, *scaler32;
|
||||
scaler_expand_fn *expander;
|
||||
|
||||
};
|
||||
|
||||
/* The expander functions */
|
||||
|
||||
/* Clip back to screen size after expansion */
|
||||
static void clip( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
|
||||
static void expand_1( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
static void expand_sai( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
static void expand_pal1( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
static void expand_pal( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
static void expand_dotmatrix( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
|
||||
/* Information on each of the available scalers. Make sure this array stays
|
||||
in the same order as scaler.h:scaler_type */
|
||||
static const struct scaler_info available_scalers[] = {
|
||||
|
||||
{ "Timex Half (smoothed)", "half", SCALER_FLAGS_NONE, 0.5,
|
||||
scaler_Half_16, scaler_Half_32, NULL },
|
||||
{ "Timex Half (skipping)", "halfskip", SCALER_FLAGS_NONE, 0.5,
|
||||
scaler_HalfSkip_16, scaler_HalfSkip_32, NULL },
|
||||
{ "Normal", "normal", SCALER_FLAGS_NONE, 1.0,
|
||||
scaler_Normal1x_16, scaler_Normal1x_32, NULL },
|
||||
{ "Double size", "2x", SCALER_FLAGS_NONE, 2.0,
|
||||
scaler_Normal2x_16, scaler_Normal2x_32, NULL },
|
||||
{ "Triple size", "3x", SCALER_FLAGS_NONE, 3.0,
|
||||
scaler_Normal3x_16, scaler_Normal3x_32, NULL },
|
||||
{ "Quadruple size", "4x", SCALER_FLAGS_NONE, 4.0,
|
||||
scaler_Normal4x_16, scaler_Normal4x_32, NULL },
|
||||
{ "2xSaI", "2xsai", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_2xSaI_16, scaler_2xSaI_32, expand_sai },
|
||||
{ "Super 2xSaI", "super2xsai", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_Super2xSaI_16, scaler_Super2xSaI_32, expand_sai },
|
||||
{ "SuperEagle", "supereagle", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_SuperEagle_16, scaler_SuperEagle_32, expand_sai },
|
||||
{ "AdvMAME 2x", "advmame2x", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_AdvMame2x_16, scaler_AdvMame2x_32, expand_1 },
|
||||
{ "AdvMAME 3x", "advmame3x", SCALER_FLAGS_EXPAND, 3.0,
|
||||
scaler_AdvMame3x_16, scaler_AdvMame3x_32, expand_1 },
|
||||
{ "TV 2x", "tv2x", SCALER_FLAGS_NONE, 2.0,
|
||||
scaler_TV2x_16, scaler_TV2x_32, NULL },
|
||||
{ "TV 3x", "tv3x", SCALER_FLAGS_NONE, 3.0,
|
||||
scaler_TV3x_16, scaler_TV3x_32, NULL },
|
||||
{ "TV 4x", "tv4x", SCALER_FLAGS_NONE, 4.0,
|
||||
scaler_TV4x_16, scaler_TV4x_32, NULL },
|
||||
{ "Timex TV", "timextv", SCALER_FLAGS_NONE, 1.0,
|
||||
scaler_TimexTV_16, scaler_TimexTV_32, NULL },
|
||||
{ "Dot Matrix", "dotmatrix", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_DotMatrix_16, scaler_DotMatrix_32, expand_dotmatrix },
|
||||
{ "Timex 1.5x", "timex15x", SCALER_FLAGS_NONE, 1.5,
|
||||
scaler_Timex1_5x_16, scaler_Timex1_5x_32, NULL },
|
||||
{ "Timex 2x", "timex2x", SCALER_FLAGS_NONE, 2.0,
|
||||
scaler_Normal2x_16, scaler_Normal2x_32, NULL },
|
||||
{ "PAL TV", "paltv", SCALER_FLAGS_EXPAND, 1.0,
|
||||
scaler_PalTV_16, scaler_PalTV_32, expand_pal1 },
|
||||
{ "PAL TV 2x", "paltv2x", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_PalTV2x_16, scaler_PalTV2x_32, expand_pal },
|
||||
{ "PAL TV 3x", "paltv3x", SCALER_FLAGS_EXPAND, 3.0,
|
||||
scaler_PalTV3x_16, scaler_PalTV3x_32, expand_pal },
|
||||
{ "PAL TV 4x", "paltv4x", SCALER_FLAGS_EXPAND, 4.0,
|
||||
scaler_PalTV4x_16, scaler_PalTV4x_32, expand_pal },
|
||||
{ "HQ 2x", "hq2x", SCALER_FLAGS_EXPAND, 2.0,
|
||||
scaler_HQ2x_16, scaler_HQ2x_32, expand_1 },
|
||||
{ "HQ 3x", "hq3x", SCALER_FLAGS_EXPAND, 3.0,
|
||||
scaler_HQ3x_16, scaler_HQ3x_32, expand_1 },
|
||||
{ "HQ 4x", "hq4x", SCALER_FLAGS_EXPAND, 4.0,
|
||||
scaler_HQ4x_16, scaler_HQ4x_32, expand_1 },
|
||||
};
|
||||
|
||||
scaler_type current_scaler = SCALER_NUM;
|
||||
ScalerProc *scaler_proc16, *scaler_proc32;
|
||||
scaler_flags_t scaler_flags;
|
||||
scaler_expand_fn *scaler_expander;
|
||||
|
||||
int
|
||||
scaler_select_scaler( scaler_type scaler )
|
||||
{
|
||||
if( !scaler_is_supported( scaler ) ) return 1;
|
||||
|
||||
if( current_scaler == scaler ) return 0;
|
||||
|
||||
current_scaler = scaler;
|
||||
|
||||
if( settings_current.start_scaler_mode ) libspectrum_free( settings_current.start_scaler_mode );
|
||||
settings_current.start_scaler_mode =
|
||||
utils_safe_strdup( available_scalers[current_scaler].id );
|
||||
|
||||
scaler_proc16 = scaler_get_proc16( current_scaler );
|
||||
scaler_proc32 = scaler_get_proc32( current_scaler );
|
||||
scaler_flags = scaler_get_flags( current_scaler );
|
||||
scaler_expander = scaler_get_expander( current_scaler );
|
||||
|
||||
return uidisplay_hotswap_gfx_mode();
|
||||
}
|
||||
|
||||
int
|
||||
scaler_select_id( const char *id )
|
||||
{
|
||||
scaler_type i;
|
||||
|
||||
for( i=0; i < SCALER_NUM; i++ ) {
|
||||
if( ! strcmp( available_scalers[i].id, id ) ) {
|
||||
scaler_select_scaler( i );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ui_error( UI_ERROR_ERROR, "Scaler id '%s' unknown", id );
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
scaler_register_clear( void )
|
||||
{
|
||||
scalers_registered = 0;
|
||||
memset( scaler_supported, 0, sizeof(int) * SCALER_NUM );
|
||||
}
|
||||
|
||||
void
|
||||
scaler_register( scaler_type scaler )
|
||||
{
|
||||
if( scaler_supported[scaler] == 1 ) return;
|
||||
scalers_registered++;
|
||||
scaler_supported[scaler] = 1;
|
||||
}
|
||||
|
||||
int
|
||||
scaler_is_supported( scaler_type scaler )
|
||||
{
|
||||
return ( scaler >= SCALER_NUM ? 0 : scaler_supported[scaler] );
|
||||
}
|
||||
|
||||
const char *
|
||||
scaler_name( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].name;
|
||||
}
|
||||
|
||||
ScalerProc*
|
||||
scaler_get_proc16( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].scaler16;
|
||||
}
|
||||
|
||||
ScalerProc*
|
||||
scaler_get_proc32( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].scaler32;
|
||||
}
|
||||
|
||||
scaler_flags_t
|
||||
scaler_get_flags( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].flags;
|
||||
}
|
||||
|
||||
float
|
||||
scaler_get_scaling_factor( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].scaling_factor;
|
||||
}
|
||||
|
||||
scaler_expand_fn*
|
||||
scaler_get_expander( scaler_type scaler )
|
||||
{
|
||||
return available_scalers[scaler].expander;
|
||||
}
|
||||
|
||||
/* The expansion functions */
|
||||
|
||||
/* Clip after expansion */
|
||||
static inline void
|
||||
clip( int *x, int *y, int *w, int *h, int image_width, int image_height )
|
||||
{
|
||||
if ( *x < 0 ) { *w += *x; *x=0; }
|
||||
if ( *y < 0 ) { *h += *y; *y=0; }
|
||||
if ( *w > image_width - *x ) *w = image_width - *x;
|
||||
if ( *h > image_height - *y ) *h = image_height - *y;
|
||||
}
|
||||
|
||||
/* Expand one pixel in all directions */
|
||||
static void
|
||||
expand_1( int *x, int *y, int *w, int *h, int image_width, int image_height )
|
||||
{
|
||||
(*x)--; (*y)--; (*w)+=2; (*h)+=2;
|
||||
clip( x, y, w, h, image_width, image_height );
|
||||
}
|
||||
|
||||
/* Expand two pixels up and left and one pixel down and right */
|
||||
static void
|
||||
expand_sai( int *x, int *y, int *w, int *h, int image_width, int image_height )
|
||||
{
|
||||
(*x)-=2; (*y)-=2; (*w)+=3; (*h)+=3;
|
||||
clip( x, y, w, h, image_width, image_height );
|
||||
}
|
||||
|
||||
/* Expand two pixels left and right */
|
||||
static void
|
||||
expand_pal1( int *x, int *y, int *w, int *h, int image_width, int image_height )
|
||||
{
|
||||
int w_mod = (*w) % 2;
|
||||
(*x)-=2; (*w)+=4;
|
||||
(*w)+=w_mod; /* expand to even*/
|
||||
clip( x, y, w, h, image_width, image_height );
|
||||
}
|
||||
|
||||
/* Expand one pixels left and right */
|
||||
static void
|
||||
expand_pal( int *x, int *y, int *w, int *h, int image_width, int image_height )
|
||||
{
|
||||
(*x)-=1; (*w)+=2;
|
||||
clip( x, y, w, h, image_width, image_height );
|
||||
}
|
||||
|
||||
/* Expand to a even y co-ord */
|
||||
static void
|
||||
expand_dotmatrix( int *x GCC_UNUSED, int *y GCC_UNUSED, int *w GCC_UNUSED,
|
||||
int *h, int image_width GCC_UNUSED,
|
||||
int image_height GCC_UNUSED )
|
||||
{
|
||||
int y_mod = (*y) % 2;
|
||||
|
||||
(*y)-=y_mod;
|
||||
(*h)+=y_mod;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2002-2003 The ScummVM project, Fredrick Meunier and
|
||||
* Philip Kendall
|
||||
*
|
||||
* HQ2x and HQ3x scalers taken from HiEnd3D Demos (http://www.hiend3d.com)
|
||||
* Copyright (C) 2003 MaxSt ( [email protected] )
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Header$
|
||||
*/
|
||||
|
||||
#ifndef SCALER_H
|
||||
#define SCALER_H
|
||||
|
||||
#include <libspectrum.h>
|
||||
|
||||
typedef enum scaler_type {
|
||||
SCALER_HALF = 0,
|
||||
SCALER_HALFSKIP,
|
||||
SCALER_NORMAL,
|
||||
SCALER_DOUBLESIZE,
|
||||
SCALER_TRIPLESIZE,
|
||||
SCALER_QUADSIZE,
|
||||
SCALER_2XSAI,
|
||||
SCALER_SUPER2XSAI,
|
||||
SCALER_SUPEREAGLE,
|
||||
SCALER_ADVMAME2X,
|
||||
SCALER_ADVMAME3X,
|
||||
SCALER_TV2X,
|
||||
SCALER_TV3X,
|
||||
SCALER_TV4X,
|
||||
SCALER_TIMEXTV,
|
||||
SCALER_DOTMATRIX,
|
||||
SCALER_TIMEX1_5X,
|
||||
SCALER_TIMEX2X,
|
||||
SCALER_PALTV,
|
||||
SCALER_PALTV2X,
|
||||
SCALER_PALTV3X,
|
||||
SCALER_PALTV4X,
|
||||
SCALER_HQ2X,
|
||||
SCALER_HQ3X,
|
||||
SCALER_HQ4X,
|
||||
|
||||
SCALER_NUM /* End marker; do not remove */
|
||||
} scaler_type;
|
||||
|
||||
typedef enum scaler_flags_t {
|
||||
SCALER_FLAGS_NONE = 0,
|
||||
SCALER_FLAGS_EXPAND = 1 << 0,
|
||||
} scaler_flags_t;
|
||||
|
||||
typedef void ScalerProc( const libspectrum_byte *srcPtr,
|
||||
libspectrum_dword srcPitch,
|
||||
libspectrum_byte *dstPtr, libspectrum_dword dstPitch,
|
||||
int width, int height );
|
||||
|
||||
/* The type of function used to expand the area dirtied by a scaler */
|
||||
typedef void scaler_expand_fn( int *x, int *y, int *w, int *h,
|
||||
int image_width, int image_height );
|
||||
|
||||
extern scaler_type current_scaler;
|
||||
extern ScalerProc *scaler_proc16, *scaler_proc32;
|
||||
extern scaler_flags_t scaler_flags;
|
||||
extern scaler_expand_fn *scaler_expander;
|
||||
extern int scalers_registered;
|
||||
|
||||
typedef int (*scaler_available_fn)( scaler_type scaler );
|
||||
|
||||
int scaler_select_id( const char *scaler_mode );
|
||||
void scaler_register_clear( void );
|
||||
int scaler_select_scaler( scaler_type scaler );
|
||||
void scaler_register( scaler_type scaler );
|
||||
int scaler_is_supported( scaler_type scaler );
|
||||
const char *scaler_name( scaler_type scaler );
|
||||
ScalerProc *scaler_get_proc16( scaler_type scaler );
|
||||
ScalerProc *scaler_get_proc32( scaler_type scaler );
|
||||
scaler_flags_t scaler_get_flags( scaler_type scaler );
|
||||
float scaler_get_scaling_factor( scaler_type scaler );
|
||||
scaler_expand_fn* scaler_get_expander( scaler_type scaler );
|
||||
|
||||
int scaler_select_bitformat( libspectrum_dword BitFormat );
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Loaded 100 of 296 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user