all current vba-m Wx patches applied to trunk.

This commit is contained in:
squall-leonhart 2011-05-25 11:55:46 +00:00
parent f3b7ead629
commit 8e6a51211e
64 changed files with 28757 additions and 580 deletions

View File

@ -8,6 +8,7 @@ if( COMMAND cmake_policy )
endif( COMMAND cmake_policy )
SET( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeScripts )
option( ENABLE_WX "Build the wxWidgets port" ON )
option( ENABLE_SDL "Build the SDL port" ON )
option( ENABLE_GTK "Build the GTK+ GUI" ON )
option( ENABLE_DEBUGGER "Enable the debugger" ON )
@ -63,6 +64,17 @@ FIND_PACKAGE ( SDL REQUIRED )
if( ENABLE_LINK )
FIND_PACKAGE ( SFML REQUIRED )
endif( ENABLE_LINK )
# set the standard libraries all ports use
SET(VBAMCORE_LIBS
vbamcore
fex
${SDL_LIBRARY}
${SFML_LIBRARY}
${OPENGL_LIBRARIES}
${ZLIB_LIBRARY}
${PNG_LIBRARY})
# Disable looking for GTK if not going to build the GTK frontend
# so that pkg-config is not required
@ -111,9 +123,20 @@ ENDIF( NOT SYSCONFDIR )
ADD_DEFINITIONS (-DHAVE_NETINET_IN_H -DHAVE_ARPA_INET_H -DHAVE_ZLIB_H -DFINAL_VERSION -DSDL -DUSE_OPENGL -DSYSCONFDIR='"${SYSCONFDIR}"' -DWITH_LIRC='${WITHLIRC}')
ADD_DEFINITIONS (-DVERSION='"${VERSION}"' -DPKGDATADIR='"${PKGDATADIR}"' -DPACKAGE='')
if( NOT ENABLE_LINK )
if( ENABLE_LINK )
FIND_LIBRARY(RT_LIB rt)
IF(RT_LIB)
SET(CMAKE_REQUIRED_LIBRARIES ${RT_LIB})
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
ENDIF(RT_LIB)
INCLUDE(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(sem_timedwait SEM_TIMEDWAIT)
IF( SEM_TIMEDWAIT)
ADD_DEFINITIONS (-DHAVE_SEM_TIMEDWAIT)
ENDIF( SEM_TIMEDWAIT)
else( ENABLE_LINK )
ADD_DEFINITIONS (-DNO_LINK)
endif( NOT ENABLE_LINK )
endif( ENABLE_LINK )
# The debugger is enabled by default
if( NOT ENABLE_DEBUGGER )
@ -132,6 +155,21 @@ if( ENABLE_NLS )
SET( LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale )
ADD_DEFINITIONS ( -DENABLE_NLS )
ADD_DEFINITIONS ( -DLOCALEDIR=\\\"${LOCALEDIR}\\\" )
# for now, only GBALink.cpp uses gettext() directly
IF(ENABLE_LINK)
FIND_PATH(LIBINTL_INC libintl.h )
FIND_LIBRARY(LIBINTL_LIB intl )
IF(LIBINTL_LIB)
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBINTL_LIB})
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${LIBINTL_LIB})
ENDIF(LIBINTL_LIB)
INCLUDE(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(gettext GETTEXT_FN)
IF(NOT LIBINTL_INC OR NOT GETTEXT_FN)
message( SEND_ERROR "NLS requires libintl" )
ENDIF(NOT LIBINTL_INC OR NOT GETTEXT_FN)
INCLUDE_DIRECTORIES(${LIBINTL_INC})
ENDIF(ENABLE_LINK)
endif( ENABLE_NLS )
# Compiler flags
@ -256,6 +294,7 @@ SET(SRC_FILTERS
src/filters/interframe.cpp
src/filters/pixel.cpp
src/filters/scanline.cpp
src/filters/simpleFilter.cpp
)
SET(SRC_HQ_C
@ -270,6 +309,13 @@ SET(SRC_HQ_ASM
src/filters/hq/asm/hq3x32.cpp
)
if( ENABLE_ASM_SCALERS )
SET(SRC_FILTERS ${SRC_FILTERS} ${SRC_HQ_ASM})
else( ENABLE_ASM_SCALERS )
SET(SRC_FILTERS ${SRC_FILTERS} ${SRC_HQ_C})
ADD_DEFINITIONS ( -DNO_ASM )
endif( ENABLE_ASM_SCALERS )
SET(SRC_GTK
src/gtk/configfile.cpp
src/gtk/main.cpp
@ -303,12 +349,6 @@ if( ENABLE_DEBUGGER )
)
endif( ENABLE_DEBUGGER )
if( ENABLE_ASM_SCALERS )
SET(SRC_HQ ${SRC_HQ_ASM})
else( ENABLE_ASM_SCALERS )
SET(SRC_HQ ${SRC_HQ_C})
endif( ENABLE_ASM_SCALERS )
INCLUDE_DIRECTORIES(
${ZLIB_INCLUDE_DIR}
fex
@ -354,7 +394,6 @@ IF( ENABLE_SDL )
vbam
WIN32
${SRC_SDL}
${SRC_HQ}
)
IF( WIN32 )
@ -367,15 +406,9 @@ IF( ENABLE_SDL )
TARGET_LINK_LIBRARIES (
vbam
vbamcore
${SDL_LIBRARY}
${ZLIB_LIBRARY}
${PNG_LIBRARY}
${OPENGL_LIBRARY}
${VBAMCORE_LIBS}
${WIN32_LIBRARIES}
${LIRC_CLIENT_LIBRARY}
${SFML_LIBRARY}
fex
)
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin)
@ -394,14 +427,9 @@ IF( ENABLE_GTK )
TARGET_LINK_LIBRARIES (
gvbam
vbamcore
${ZLIB_LIBRARY}
${PNG_LIBRARY}
${SDL_LIBRARY}
${VBAMCORE_LIBS}
${GTKMM_LIBRARIES}
${GTKGLMM_LIBRARIES}
${SFML_LIBRARY}
fex
)
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/gvbam DESTINATION bin)
@ -410,8 +438,20 @@ IF( ENABLE_GTK )
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/gtk/ui DESTINATION ${DATA_INSTALL_DIR} PATTERN ".svn" EXCLUDE)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/vba-over.ini DESTINATION ${DATA_INSTALL_DIR})
ENDIF( ENABLE_GTK )
IF( ENABLE_WX )
# since this has generated source files, it's easier to just
# make from the subdir
# otherwise out-of-tree builds have trouble
add_subdirectory(src/wx)
ENDIF( ENABLE_WX )
if( ENABLE_GTK OR ENABLE_WX )
# Native Language Support
if( ENABLE_NLS )
add_subdirectory(po)
endif( ENABLE_NLS )
ENDIF( ENABLE_GTK )
endif( ENABLE_GTK OR ENABLE_WX )

View File

@ -0,0 +1,48 @@
# -*- cmake -*-
# mostly taken from emeraldviewer
if (WIN32)
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
"$ENV{DXSDK_DIR}/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2008)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (November 2007)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2007)/Include"
"C:/DX90SDK/Include"
"$ENV{PROGRAMFILES}/DX90SDK/Include"
)
if (DIRECTX_INCLUDE_DIR)
include_directories(${DIRECTX_INCLUDE_DIR})
if (DIRECTX_FIND_QUIETLY)
message(STATUS "Found DirectX include: ${DIRECTX_INCLUDE_DIR}")
endif (DIRECTX_FIND_QUIETLY)
else (DIRECTX_INCLUDE_DIR)
message(FATAL_ERROR "Could not find DirectX SDK Include")
endif (DIRECTX_INCLUDE_DIR)
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
"$ENV{DXSDK_DIR}/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2008)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (November 2007)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2007)/Lib/x86"
"C:/DX90SDK/Lib"
"$ENV{PROGRAMFILES}/DX90SDK/Lib"
)
if (DIRECTX_LIBRARY_DIR)
if (DIRECTX_FIND_QUIETLY)
message(STATUS "Found DirectX include: ${DIRECTX_LIBRARY_DIR}")
endif (DIRECTX_FIND_QUIETLY)
else (DIRECTX_LIBRARY_DIR)
message(FATAL_ERROR "Could not find DirectX SDK Libraries")
endif (DIRECTX_LIBRARY_DIR)
endif (WIN32)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(DirectX DEFAULT_MSG DIRECTX_LIBRARY_DIR DIRECTX_INCLUDE_DIR)

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
vbam (1.8.0.1016-1) unstable; urgency=low
* Use new version from upstream, with wxwidgets patches
-- Thomas J. Moore <darktjm@gmail.com> Sun, 22 May 2011 14:08:05 -0500
vbam (1.8.0.1001-1) unstable; urgency=low
* Use new version from upstream

17
debian/control vendored
View File

@ -2,7 +2,7 @@ Source: vbam
Section: otherosfs
Priority: optional
Maintainer: Fernando Tarlá Cardoso Lemos <fernandotcl@gmail.com>
Build-Depends: debhelper (>= 7), cmake, nasm [i386 amd64], libsdl1.2-dev, libgl-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev
Build-Depends: debhelper (>= 7), cmake, nasm [i386 amd64], libsdl1.2-dev, libgl-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev, libavcodec-dev, libavformat-dev, libswscale-dev, libavutil-dev, wx-common, libcairo2-dev, liblircclient-dev, libopenal-dev, libwxgtk2.8-dev
Standards-Version: 3.8.1
Homepage: http://www.vbam.com
@ -17,6 +17,7 @@ Description: Nintendo Game Boy Advance emulator
.
This package does not provide a GUI version of VisualBoyAdvance-M.
See the vbam-gtk package for the GTK+ version of this program.
See the vbam-wx package for the wxWidgets version of this program.
Package: vbam-gtk
Architecture: any
@ -27,4 +28,16 @@ Description: Nintendo Game Boy Advance emulator (GTK+ frontend)
Boy Advance handheld console, in addition to the original Game Boy
handhelds and its Super and Color variants.
.
This package provides the GUI version of VisualBoyAdvance-M.
This package provides the GTK+ GUI version of VisualBoyAdvance-M.
Package: vbam-wx
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Nintendo Game Boy Advance emulator (GTK+ frontend)
VisualBoyAdvance-M is a Nintendo Game Boy Emulator with high
compatibility with commercial games. It emulates the Nintendo Game
Boy Advance handheld console, in addition to the original Game Boy
handhelds and its Super and Color variants.
.
This package provides the wxWidgets GUI version of VisualBoyAdvance-M.

12
debian/rules vendored
View File

@ -23,16 +23,23 @@ endif
builddir/Makefile:
dh_testdir
mkdir -p builddir
mkdir -p builddir builddir-wx
cd builddir && \
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="$(CFLAGS)" \
-DCMAKE_LD_FLAGS="-Wl,-z,defs" -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" \
-DENABLE_LIRC=ON -DENABLE_WX=OFF \
-DCMAKE_SKIP_RPATH=ON $(EXTRA_CMAKE_FLAGS)
cd builddir-wx && \
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="$(CFLAGS)" \
-DCMAKE_LD_FLAGS="-Wl,-z,defs" -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" \
-DENABLE_LINK=ON -DENABLE_WX=ON -DENABLE_SDL=OFF -DENABLE_GTK=OFF\
-DCMAKE_SKIP_RPATH=ON $(EXTRA_CMAKE_FLAGS)
build: build-stamp
build-stamp: builddir/Makefile
dh_testdir
$(MAKE) -C builddir-wx
$(MAKE) -C builddir
touch $@
@ -40,7 +47,7 @@ clean:
dh_testdir
dh_testroot
rm -f build-stamp
rm -rf builddir
rm -rf builddir builddir-wx
dh_clean
install: build
@ -49,6 +56,7 @@ install: build
dh_prep
dh_installdirs
$(MAKE) -C builddir DESTDIR=$(CURDIR)/debian/tmp install
$(MAKE) -C builddir-wx DESTDIR=$(CURDIR)/debian/tmp install
binary-indep: install

View File

@ -1,4 +1,5 @@
usr/bin/gvbam
usr/share/icons
usr/share/vbam
usr/share/applications
usr/share/locale/*/*/gvbam.*
usr/share/applications/gvbam.desktop

2
debian/vbam-wx.docs vendored Normal file
View File

@ -0,0 +1,2 @@
doc/DevInfo.txt
doc/ips.htm

4
debian/vbam-wx.install vendored Normal file
View File

@ -0,0 +1,4 @@
usr/bin/wxvbam
usr/share/icons
usr/share/locale/*/*/wxvbam.*
usr/share/applications/wxvbam.desktop

2
debian/vbam-wx.menu vendored Normal file
View File

@ -0,0 +1,2 @@
?package(vbam-wx):needs="X11" section="Applications/Emulators"\
title="VisualBoyAdvance-M" command="/usr/bin/wxvbam"

View File

@ -1,2 +1,47 @@
GETTEXT_CREATE_TRANSLATIONS(gvbam.pot gvbam/cs.po gvbam/de.po gvbam/en.po gvbam/es_ES.po gvbam/fr.po gvbam/pt_BR.po gvbam/zh_CN.po)
ADD_DEPENDENCIES(gvbam translations)
IF(ENABLE_GTK)
GETTEXT_CREATE_TRANSLATIONS(gvbam.pot gvbam/cs.po gvbam/de.po gvbam/en.po gvbam/es_ES.po gvbam/fr.po gvbam/pt_BR.po gvbam/zh_CN.po)
ADD_DEPENDENCIES(gvbam translations)
ENDIF(ENABLE_GTK)
IF(ENABLE_WX)
# Extract message strings from xrc and source files (NLS only for 2nd cmd)
ADD_CUSTOM_COMMAND(OUTPUT wx-xrc-strings.h
COMMAND wxrc -g ../src/wx/wxvbam.xrc -o ${CMAKE_CURRENT_BINARY_DIR}/wx-xrc-strings.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ../src/wx/wxvbam.xrc)
SET(XGETTEXTCMD ${XGETTEXT} -k_ -kN_ -kwxTRANSLATE -s --copyright-holder=\"VBA-M development team\" --package-name=VBA-M --package-version=${VERSION})
# note that GLOB wil return absolute paths unless otherwise requested
# and it runs in CMAKE_CURRENT_SOURCE_DIR
FILE(GLOB PO_SRC_FILES ../src/wx/*.cpp ../src/wx/*.h ../src/wx/widgets/*.cpp
../src/Util.cpp ../src/gba/*.cpp ../src/gb/*.cpp)
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.pot
COMMAND ${XGETTEXTCMD} -o wxvbam.pot ${PO_SRC_FILES}
COMMAND ${XGETTEXTCMD} -o wxvbam.pot ../src/wx/cmdtab.cpp
COMMAND ${XGETTEXTCMD} --from-code=utf-8 -j -o wxvbam.pot wx-xrc-strings.h
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS wx-xrc-strings.h) # there is no direct cmdtab.cpp rule
# is there really any point in auto-generating en?
ADD_CUSTOM_COMMAND(OUTPUT wxvbam/en.po
COMMAND ${CMAKE_COMMAND} -E make_directory wxvbam
COMMAND ${MSGINIT} -i wxvbam.pot -o wxvbam/en.po --no-translator -l en_US.utf-8
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS wxvbam.pot)
FILE(GLOB POFILES wxvbam/*.po)
IF(NOT POFILES)
SET(POFILES ${CMAKE_CURRENT_BINARY_DIR}/wxvbam/en.po)
ENDIF(NOT POFILES)
# only one GETTEXT_CREATE_TRANSLATIONS call can be made
# probably need to simulate effects of call or force spearate cmake
# invocations
IF(NOT ENABLE_GTK)
GETTEXT_CREATE_TRANSLATIONS(${CMAKE_CURRENT_BINARY_DIR}/wxvbam.pot ALL ${POFILES})
# try to build wxvbam first so cmdtab is generated
ADD_DEPENDENCIES(translations wxvbam)
ELSE(NOT ENABLE_GTK)
MESSAGE(WARNING "wxWidgets translations overridden by GTK.\nBuild again without GTK to correct.")
ADD_CUSTOM_TARGET(extra_translations ALL DEPENDS ${POFILES})
ADD_DEPENDENCIES(extra_translations wxvbam)
ENDIF(NOT ENABLE_GTK)
ENDIF(ENABLE_WX)

View File

@ -68,7 +68,11 @@ extern void Sm60FPS_Init();
extern bool Sm60FPS_CanSkipFrame();
extern void Sm60FPS_Sleep();
extern void DbgMsg(const char *msg, ...);
#ifdef SDL
#define winlog log
#else
extern void winlog(const char *,...);
#endif
extern void (*dbgOutput)(const char *s, u32 addr);
extern void (*dbgSignal)(int sig,int number);

View File

@ -436,12 +436,17 @@ static bool utilIsImage(const char *file)
}
#ifdef WIN32
#include <Windows.h>
#include <windows.h>
#endif
IMAGE_TYPE utilFindType(const char *file)
{
char buffer [2048];
utilFindType(file, buffer);
}
IMAGE_TYPE utilFindType(const char *file, char (&buffer)[2048])
{
#ifdef WIN32
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, file, -1, NULL, 0);
wchar_t *pwText;
@ -453,24 +458,24 @@ IMAGE_TYPE utilFindType(const char *file)
MultiByteToWideChar (CP_ACP, 0, file, -1, pwText, dwNum );
char* file_conv = fex_wide_to_path( pwText);
delete []pwText;
if ( !utilIsImage( file_conv ) ) // TODO: utilIsArchive() instead?
{
// if ( !utilIsImage( file_conv ) ) // TODO: utilIsArchive() instead?
// {
fex_t* fe = scan_arc(file_conv,utilIsImage,buffer);
if(!fe)
return IMAGE_UNKNOWN;
fex_close(fe);
file = buffer;
}
// }
free(file_conv);
#else
if ( !utilIsImage( file ) ) // TODO: utilIsArchive() instead?
{
// if ( !utilIsImage( file ) ) // TODO: utilIsArchive() instead?
// {
fex_t* fe = scan_arc(file,utilIsImage,buffer);
if(!fe)
return IMAGE_UNKNOWN;
fex_close(fe);
file = buffer;
}
// }
#endif
return utilIsGBAImage(file) ? IMAGE_GBA : IMAGE_GB;
}

View File

@ -23,6 +23,7 @@ bool utilIsGBImage(const char *);
bool utilIsGzipFile(const char *);
void utilStripDoubleExtension(const char *, char *);
IMAGE_TYPE utilFindType(const char *);
IMAGE_TYPE utilFindType(const char *, char (&)[2048]);
u8 *utilLoad(const char *, bool (*)(const char*), u8 *, int &);
void utilPutDword(u8 *, u32);

View File

@ -41,12 +41,12 @@ void InterframeCleanup()
}
#ifdef MMX
static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
u16 *src0 = (u16 *)srcPtr;
u16 *src1 = (u16 *)frm1;
u16 *src2 = (u16 *)frm2;
u16 *src3 = (u16 *)frm3;
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
u16 *src1 = (u16 *)frm1 + srcPitch * starty / 2;
u16 *src2 = (u16 *)frm2 + srcPitch * starty / 2;
u16 *src3 = (u16 *)frm3 + srcPitch * starty / 2;
int count = width >> 2;
@ -159,24 +159,24 @@ static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
}
#endif
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
if(frm1 == NULL) {
Init();
}
#ifdef MMX
if(cpu_mmx) {
SmartIB_MMX(srcPtr, srcPitch, width, height);
SmartIB_MMX(srcPtr, srcPitch, width, starty, height);
return;
}
#endif
u16 colorMask = ~RGB_LOW_BITS_MASK;
u16 *src0 = (u16 *)srcPtr;
u16 *src1 = (u16 *)frm1;
u16 *src2 = (u16 *)frm2;
u16 *src3 = (u16 *)frm3;
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
u16 *src1 = (u16 *)frm1 + srcPitch * starty / 2;
u16 *src2 = (u16 *)frm2 + srcPitch * starty / 2;
u16 *src3 = (u16 *)frm3 + srcPitch * starty / 2;
int sPitch = srcPitch >> 1;
@ -201,13 +201,18 @@ void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
frm2 = temp;
}
#ifdef MMX
static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
{
u32 *src0 = (u32 *)srcPtr;
u32 *src1 = (u32 *)frm1;
u32 *src2 = (u32 *)frm2;
u32 *src3 = (u32 *)frm3;
SmartIB(srcPtr, srcPitch, width, 0, height);
}
#ifdef MMX
static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
u32 *src2 = (u32 *)frm2 + starty * srcPitch / 4;
u32 *src3 = (u32 *)frm3 + starty * srcPitch / 4;
int count = width >> 1;
@ -320,22 +325,22 @@ static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
}
#endif
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
if(frm1 == NULL) {
Init();
}
#ifdef MMX
if(cpu_mmx) {
SmartIB32_MMX(srcPtr, srcPitch, width, height);
SmartIB32_MMX(srcPtr, srcPitch, width, starty, height);
return;
}
#endif
u32 *src0 = (u32 *)srcPtr;
u32 *src1 = (u32 *)frm1;
u32 *src2 = (u32 *)frm2;
u32 *src3 = (u32 *)frm3;
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
u32 *src2 = (u32 *)frm2 + starty * srcPitch / 4;
u32 *src3 = (u32 *)frm3 + starty * srcPitch / 4;
u32 colorMask = 0xfefefe;
@ -362,11 +367,16 @@ void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
frm2 = temp;
}
#ifdef MMX
static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
{
u16 *src0 = (u16 *)srcPtr;
u16 *src1 = (u16 *)frm1;
SmartIB32(srcPtr, srcPitch, width, 0, height);
}
#ifdef MMX
static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
u16 *src1 = (u16 *)frm1 + starty * srcPitch / 2;
int count = width >> 2;
@ -431,7 +441,7 @@ static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
}
#endif
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
if(frm1 == NULL) {
Init();
@ -439,15 +449,15 @@ void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
#ifdef MMX
if(cpu_mmx) {
MotionBlurIB_MMX(srcPtr, srcPitch, width, height);
MotionBlurIB_MMX(srcPtr, srcPitch, width, starty, height);
return;
}
#endif
u16 colorMask = ~RGB_LOW_BITS_MASK;
u16 *src0 = (u16 *)srcPtr;
u16 *src1 = (u16 *)frm1;
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
u16 *src1 = (u16 *)frm1 + starty * srcPitch / 2;
int sPitch = srcPitch >> 1;
@ -462,11 +472,16 @@ void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
}
}
#ifdef MMX
static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
{
u32 *src0 = (u32 *)srcPtr;
u32 *src1 = (u32 *)frm1;
MotionBlurIB(srcPtr, srcPitch, width, 0, height);
}
#ifdef MMX
static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
int count = width >> 1;
@ -531,7 +546,7 @@ static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
}
#endif
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
{
if(frm1 == NULL) {
Init();
@ -539,13 +554,13 @@ void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
#ifdef MMX
if(cpu_mmx) {
MotionBlurIB32_MMX(srcPtr, srcPitch, width, height);
MotionBlurIB32_MMX(srcPtr, srcPitch, width, starty, height);
return;
}
#endif
u32 *src0 = (u32 *)srcPtr;
u32 *src1 = (u32 *)frm1;
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
u32 colorMask = 0xfefefe;
@ -561,3 +576,8 @@ void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
pos++;
}
}
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
{
MotionBlurIB32(srcPtr, srcPitch, width, 0, height);
}

View File

@ -21,7 +21,6 @@
extern u8 *pix;
extern bool speedup;
bool gbUpdateSizes();
bool inBios = false;
// debugging

View File

@ -17,7 +17,12 @@ typedef union {
u16 W;
} gbRegister;
extern gbRegister AF, BC, DE, HL, SP, PC;
extern u16 IFF;
int gbDis(char *, u16);
bool gbLoadRom(const char *);
bool gbUpdateSizes();
void gbEmulate(int);
void gbWriteMemory(register u16, register u8);
void gbDrawLine();

View File

@ -27,11 +27,11 @@ bool genericflashcardEnable = false;
int gbCgbMode = 0;
u16 gbColorFilter[32768];
int gbColorOption = 0;
bool gbColorOption = false;
int gbPaletteOption = 0;
int gbEmulatorType = 0;
int gbBorderOn = 1;
int gbBorderAutomatic = 0;
bool gbBorderOn = true;
bool gbBorderAutomatic = false;
int gbBorderLineSkip = 160;
int gbBorderRowSkip = 0;
int gbBorderColumnSkip = 0;

View File

@ -25,11 +25,11 @@ extern u8 *gbMemoryMap[16];
extern int gbFrameSkip;
extern u16 gbColorFilter[32768];
extern int gbColorOption;
extern bool gbColorOption;
extern int gbPaletteOption;
extern int gbEmulatorType;
extern int gbBorderOn;
extern int gbBorderAutomatic;
extern bool gbBorderOn;
extern bool gbBorderAutomatic;
extern int gbCgbMode;
extern int gbSgbMode;
extern int gbWindowLine;

View File

@ -113,7 +113,6 @@ bool fxOn = false;
bool windowOn = false;
int frameCount = 0;
char buffer[1024];
FILE *out = NULL;
u32 lastTime = 0;
int count = 0;
@ -2826,27 +2825,12 @@ void CPUUpdateRegister(u32 address, u16 value)
case COMM_SIOCNT:
StartLink(value);
/*
// old code path for no linking...
{
if (value & 0x80) {
value &= 0xff7f;
if ((value & 1) && (value & 0x4000)) {
UPDATE_REG(COMM_SIODATA8, 0xFF);
IF |= 0x80;
UPDATE_REG(0x202, IF);
value &= 0x7f7f;
}
}
UPDATE_REG(COMM_SIOCNT, value);
}
*/
break;
case COMM_SIODATA8:
if (gba_link_enabled)
LinkSSend(value);
UPDATE_REG(COMM_RCNT, value);
UPDATE_REG(COMM_SIODATA8, value);
break;
case 0x130:
@ -3452,27 +3436,6 @@ void CPUInterrupt()
biosProtected[3] = 0xe5;
}
#ifdef SDL
void log(const char *defaultMsg, ...)
{
char buffer[2048];
va_list valist;
va_start(valist, defaultMsg);
vsprintf(buffer, defaultMsg, valist);
if(out == NULL) {
out = fopen("trace.log","w");
}
fputs(buffer, out);
va_end(valist);
}
#else
extern void winlog(const char *, ...);
#endif
void CPULoop(int ticks)
{
int clockTicks;
@ -3498,30 +3461,20 @@ void CPULoop(int ticks)
#ifdef BKPT_SUPPORT
if (debugger_last)
{
sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
winlog("R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5],
oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11],
oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16],
oldreg[17]);
}
#endif
sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
winlog("R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I,
reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I,
reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I,
reg[17].I);
#ifdef SDL
log(buffer);
#else
winlog(buffer);
#endif
} else if(!holdState) {
sprintf(buffer, "PC=%08x\n", armNextPC);
#ifdef SDL
log(buffer);
#else
winlog(buffer);
#endif
winlog("PC=%08x\n", armNextPC);
}
}
#endif /* FINAL_VERSION */

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
#ifndef GBA_GBALINK_H
#define GBA_GBALINK_H
#pragma once
#ifndef NO_LINK
#include <SFML/Network.hpp>
#endif
// register definitions; these are always present
#define LINK_PARENTLOST 0x80
#define UNSUPPORTED -1
#define MULTIPLAYER 0
#define NORMAL8 1
@ -12,6 +12,7 @@
#define UART 3
#define JOYBUS 4
#define GP 5
#define RFU_INIT 0
#define RFU_COMM 1
#define RFU_SEND 2
@ -21,6 +22,11 @@
#define COMM_SIODATA32_H 0x122
#define COMM_SIOCNT 0x128
#define COMM_SIODATA8 0x12a
#define COMM_SIOMLT_SEND 0x12a
#define COMM_SIOMULTI0 0x120
#define COMM_SIOMULTI1 0x122
#define COMM_SIOMULTI2 0x124
#define COMM_SIOMULTI3 0x126
#define COMM_RCNT 0x134
#define COMM_JOYCNT 0x140
#define COMM_JOY_RECV_L 0x150
@ -45,13 +51,27 @@ enum
JOY_CMD_WRITE = 0x15
};
#ifdef _MSC_VER
// Link implementation; only present if enabled
#ifndef NO_LINK
#include <SFML/System.hpp>
#include <SFML/Network.hpp>
class ServerInfoDisplay
{
public:
virtual void ShowServerIP(sf::IPAddress addr) = 0;
virtual void ShowConnect(int player) = 0;
virtual void Ping() = 0;
virtual void Connected() = 0;
};
typedef struct {
u16 linkdata[4];
u16 linkcmd[4];
u16 linkdata[5];
u16 linkcmd;
u16 numtransfers;
int lastlinktime;
u8 numgbas;
u8 trgbas;
u8 linkflags;
int rfu_q[4];
u8 rfu_request[4];
@ -62,72 +82,77 @@ typedef struct {
class lserver{
int numbytes;
fd_set fdset;
timeval wsocktimeout;
sf::Selector<sf::SocketTCP> fdset;
//timeval udptimeout;
char inbuffer[256], outbuffer[256];
int *intinbuffer;
s32 *intinbuffer;
u16 *u16inbuffer;
int *intoutbuffer;
s32 *intoutbuffer;
u16 *u16outbuffer;
int counter;
int done;
public:
int howmanytimes;
SOCKET tcpsocket[4];
SOCKADDR_IN udpaddr[4];
sf::SocketTCP tcpsocket[4];
sf::IPAddress udpaddr[4];
lserver(void);
int Init(void*);
bool Init(ServerInfoDisplay *);
void Send(void);
void Recv(void);
};
class ClientInfoDisplay {
public:
virtual void ConnectStart(sf::IPAddress addr) = 0;
virtual void Ping() = 0;
virtual void ShowConnect(int player, int togo) = 0;
virtual void Connected() = 0;
};
class lclient{
fd_set fdset;
timeval wsocktimeout;
sf::Selector<sf::SocketTCP> fdset;
char inbuffer[256], outbuffer[256];
int *intinbuffer;
s32 *intinbuffer;
u16 *u16inbuffer;
int *intoutbuffer;
s32 *intoutbuffer;
u16 *u16outbuffer;
int numbytes;
public:
bool oncesend;
SOCKADDR_IN serverinfo;
SOCKET noblock;
sf::IPAddress serveraddr;
unsigned short serverport;
sf::SocketTCP noblock;
int numtransfers;
lclient(void);
int Init(LPHOSTENT, void*);
bool Init(sf::IPAddress, ClientInfoDisplay *);
void Send(void);
void Recv(void);
void CheckConn(void);
};
typedef struct {
SOCKET tcpsocket;
//SOCKET udpsocket;
int numgbas;
HANDLE thread;
u8 type;
u8 server;
sf::SocketTCP tcpsocket;
//sf::SocketUDP udpsocket;
int numslaves;
sf::Thread *thread;
int type;
bool server;
bool terminate;
bool connected;
bool speed;
bool active;
} LANLINKDATA;
#endif
extern bool gba_joybus_enabled;
#ifndef NO_LINK
extern sf::IPAddress joybusHostAddr;
#endif
extern void JoyBusConnect();
extern void JoyBusShutdown();
extern void JoyBusUpdate(int ticks);
extern bool gba_link_enabled;
#ifdef _MSC_VER
extern bool InitLink();
extern void CloseLink();
extern void StartLink(u16);
extern void StartGPLink(u16);
extern void LinkSSend(u16);
@ -135,16 +160,28 @@ extern void LinkUpdate(int);
extern void LinkChildStop();
extern void LinkChildSend(u16);
extern void CloseLanLink();
extern char *MakeInstanceFilename(const char *Input);
extern void CleanLocalLink();
extern const char *MakeInstanceFilename(const char *Input);
extern LANLINKDATA lanlink;
extern int vbaid;
extern bool rfu_enabled;
extern int linktimeout;
extern lclient lc;
extern lserver ls;
extern int linkid;
#else // These are stubbed for now
inline void StartLink(u16){}
inline void StartGPLink(u16){}
inline void LinkSSend(u16){}
inline void LinkUpdate(int){}
#else
// stubs to keep #ifdef's out of mainline
#define StartLink(x)
#define StartGPLink(x)
#define LinkSSend(x)
#define LinkUpdate(x)
#define JoyBusUpdate(x)
#define InitLink() false
#define CloseLink()
#define gba_link_enabled false
#define gba_joybus_enabled false
#endif
#endif /* GBA_GBALINK_H */

View File

@ -234,7 +234,8 @@ void remotePutPacket(const char *packet)
char c = 0;
while(c != '+'){
remoteSendFnc(buffer, (int)count + 4);
remoteRecvFnc(&c, 1);
if(remoteRecvFnc(&c, 1) < 0)
return;
// fprintf(stderr,"sent:%s recieved:%c\n",buffer,c);
}
}
@ -571,7 +572,8 @@ void remoteStubMain()
#endif
debugger = false;
break;
}
} else if(res == -2)
break;
if(res < 1024){
buffer[res] = 0;
}else{

View File

@ -175,3 +175,18 @@ void debuggerBreakOnWrite(u32 address, u32 oldvalue, u32 value, int size, int t)
void (*dbgMain)() = debuggerMain;
void (*dbgSignal)(int, int) = debuggerSignal;
void (*dbgOutput)(const char *, u32) = debuggerOutput;
void log(const char *defaultMsg, ...)
{
static FILE *out = NULL;
if(out == NULL) {
out = fopen("trace.log","w");
}
va_list valist;
va_start(valist, defaultMsg);
vfprintf(out, defaultMsg, valist);
va_end(valist);
}

View File

@ -2697,3 +2697,18 @@ void systemOnSoundShutdown()
void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length)
{
}
void log(const char *defaultMsg, ...)
{
static FILE *out = NULL;
if(out == NULL) {
out = fopen("trace.log","w");
}
va_list valist;
va_start(valist, defaultMsg);
vfprintf(out, defaultMsg, valist);
va_end(valist);
}

View File

@ -195,7 +195,7 @@ BOOL LinkServer::OnInitDialog()
{
CDialog::OnInitDialog();
m_numplayers = lanlink.numgbas;
m_numplayers = lanlink.numslaves;
m_prottype = lanlink.type;
m_speed = lanlink.speed;
@ -490,6 +490,25 @@ void LinkOptions::OnCancel()
return;
}
class Win32ServerInfoDisplay : public ServerInfoDisplay
{
Win32ServerInfoDisplay(ServerWait _dlg) : dlg(_dlg) {}
void ShowServerIP(sf::IPAddress addr) {
dlg->m_serveraddress.Format("Server IP address is: %s", addr.ToString);
}
void ShowConnect(int player) {
dlg->m_plconn[i].Format("Player %d connected", player);
dlg->UpdateData(false);
}
void Ping() { dlg->m_prgctrl.StepIt(); }
void Connected() {
MessageBox(NULL, "All players connected", "Link", MB_OK);
dlg->SendMessage(WM_CLOSE, 0, 0);
}
private:
ServerWait dlg;
}
void LinkServer::OnServerStart()
{
int errorcode;
@ -497,15 +516,15 @@ void LinkServer::OnServerStart()
UpdateData(TRUE);
lanlink.numgbas = m_numplayers+1;
lanlink.numslaves = m_numplayers+1;
lanlink.type = m_prottype;
lanlink.server = 1;
lanlink.server = true;
lanlink.speed = m_speed==1 ? true : false;
sf::IPAddress addr;
if((errorcode=ls.Init(&dlg))!=0){
char message[50];
sprintf(message, "Error %d occured.\nPlease try again.", errorcode);
MessageBox(message, "Error", MB_OK);
Win32ServerInfoDisplay dlginfo(dlg);
if(!ls.Init(&dlginfo)){
MessageBox("Error occured.\nPlease try again.", "Error", MB_OK);
return;
}
@ -526,6 +545,26 @@ BOOL LinkClient::OnInitDialog()
return TRUE;
}
class Win32ClientInfoDisplay : public ClientInfoDisplay
{
Win32ClientInfoDisplay(ServerWait _dlg) : dlg(_dlg) {}
void ConnectStart(sf::IPAddress addr) {
dlg->SetWindowText("Connecting...");
}
void ShowConnect(int player, int togo) {
dlg->m_serveraddress.Format("Connected as #%d", player);
if(togo) dlg->m_plconn[0].Format("Waiting for %d players to join", togo);
else dlg->m_plconn[0].Format("All players joined.");
}
void Ping() { dlg->m_prgctrl.StepIt(); }
void Connected() {
MessageBox(NULL, "Connected.", "Link", MB_OK);
dlg->SendMessage(WM_CLOSE, 0, 0);
}
private:
ServerWait dlg;
}
void LinkClient::OnLinkConnect()
{
char ipaddress[31];
@ -535,12 +574,13 @@ void LinkClient::OnLinkConnect()
UpdateData(TRUE);
lanlink.type = m_prottype;
lanlink.server = 0;
lanlink.server = false;
lanlink.speed = m_hacks==1 ? true : false;
m_serverip.GetWindowText(ipaddress, 30);
if((errorcode=lc.Init(gethostbyname(ipaddress), &dlg))!=0){
Win32ClientInfoDisplay dlginfo(dlg);
if((errorcode=lc.Init(sf::IPAddress(std::string(ipaddress)), &dlginfo))!=0){
char message[50];
sprintf(message, "Error %d occured.\nPlease try again.", errorcode);
MessageBox(message, "Error", MB_OK);

View File

@ -7,7 +7,11 @@
// http://www.hiend3d.com/hq2x.html
// Modified by suanyuan
//---------------------------------------------------------------------------------------------------------------------------
#ifdef __MSW__
#include <windows.h>
#else
#define HMODULE void *
#endif
//---------------------------------------------------------------------------------------------------------------------------
typedef struct
@ -54,10 +58,16 @@ typedef RENDER_PLUGIN_INFO *(*RENDPLUG_GetInfo)(void);
#define RPI_565_SUPP 0x000000800
#define RPI_888_SUPP 0x000001000
#define RPI_DST_WIDE 0x000008000
#define RPI_OUT_SCL1 0x000010000
#define RPI_OUT_SCL2 0x000020000
#define RPI_OUT_SCL3 0x000030000
#define RPI_OUT_SCL4 0x000040000
#define RPI_OUT_SCLMSK 0x0000f0000
#define RPI_OUT_SCLSH 16
//---------------------------------------------------------------------------------------------------------------------------
int rpiScaleFactor();

229
src/wx/CMakeLists.txt Normal file
View File

@ -0,0 +1,229 @@
# This build is much easier if we just do it here.
# not yet implemented
option( ENABLE_CAIRO "Enable Cairo rendering for the wxWidgets port" ON )
if( WIN32 )
# not yet implemented
option( ENABLE_DIRECT3D "Enable Direct3D rendering for the wxWidgets port" ON )
endif( WIN32 )
option( ENABLE_OPENAL "Enable OpenAL for the wxWidgets port" ON )
if( NOT ENABLE_CAIRO )
ADD_DEFINITIONS (-DNO_CAIRO)
endif( NOT ENABLE_CAIRO )
if(NOT ENABLE_DIRECT3D)
ADD_DEFINITIONS(-DNO_D3D)
endif(NOT ENABLE_DIRECT3D)
if(ENABLE_OPENAL)
FIND_PACKAGE(OpenAL REQUIRED)
INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR})
else(ENABLE_OPENAL)
ADD_DEFINITIONS (-DNO_OAL)
endif(ENABLE_OPENAL)
# adv is for wxAboutBox
SET( wxWidgets_USE_LIBS core base xrc gl adv net )
FIND_PACKAGE ( wxWidgets REQUIRED )
EXECUTE_PROCESS(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --cxxflags
OUTPUT_VARIABLE CXF)
INCLUDE( ${wxWidgets_USE_FILE} )
FIND_PACKAGE ( Gettext REQUIRED )
FIND_PROGRAM(XGETTEXT xgettext)
FIND_PROGRAM(MSGINIT msginit)
if(ENABLE_NLS AND (NOT XGETTEXT OR NOT MSGINIT))
message(SEND_ERROR "Cannot find gettext ${XGETTEXT} ${MSGINIT}")
endif(ENABLE_NLS AND (NOT XGETTEXT OR NOT MSGINIT))
IF(ENABLE_CAIRO)
FIND_PACKAGE ( PkgConfig REQUIRED )
PKG_CHECK_MODULES(CAIRO REQUIRED cairo)
include_directories(${CAIRO_INCLUDE_DIRS})
IF(WIN32)
# need gdiplus to extract hdc for cairo context
SET(CAIRO_LIBRARIES ${CAIRO_LIBRARIES} -lgdiplus)
ENDIF(WIN32)
# SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${CAIRO_CFLAGS})
ELSE(ENABLE_CAIRO)
ADD_DEFINITIONS (-DNO_CAIRO)
SET(CAIRO_LIBRARIES )
ENDIF(ENABLE_CAIRO)
IF(WIN32 AND ENABLE_DIRECTX)
FIND_PACKGE ( DirectX REQUIRED )
ENDIF(WIN32 AND ENABLE_DIRECTX)
# contrib widgets
include_directories(widgets)
# for out-of-tree builds, grab includes from both target and source dirs
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# external deps
SET(ICO_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../win32/res/VBA.ico)
SET(ICO_PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../gtk)
SET(WX_APP_ICON ${ICO_PARENT_DIR}/icons/32x32/apps/vbam.png)
#SET(WX_APP_ICON VBA_4_32x32x24.png)
SET(ICOX_FILES VBA_4_32x32x24.png)
#wxvbam.xrc now uses gvbam icon as well
#SET(ICOX_FILES ${ICOX_FILES} VBA_9_48x48x32.png)
# Extract icons using icoutils (http://www.nongnu.org/icoutils/)
# Used for main prog. icon and about dialog (in xrc file)
# or, just use the icons already extracted for gtk
ADD_CUSTOM_COMMAND(OUTPUT ${ICOX_FILES}
COMMAND icotool -x ${ICO_FILE}
DEPENDS ${ICO_FILE})
# Convert to xpm using ImageMagick (http://www.imagemagick.org)
# not executed on win32
IF( NOT WIN32 )
FIND_PACKAGE(ImageMagick REQUIRED convert)
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xpm
COMMAND ${ImageMagick_convert_EXECUTABLE} ${WX_APP_ICON} wxvbam.xpm
# following is done using #define in wxvbam.cpp
# so there is no dependency on sed
# COMMAND sed -i 's/wxvbam\\[/wxvbam_xpm[/;s/char \\*/const char */' wxvbam.xpm
DEPENDS ${WX_APP_ICON})
ENDIF( NOT WIN32 )
# wxrc does not support xrs files in -c output (> 10x compression)
# so do it manually using slow but portable bin2c.cmake script
SET(WX_XRC_ICON icons/32x32/apps/vbam.png)
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xrs
# doing this in its own dir prevents name prefixes
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/wxvbam.xrc wxvbam.xrc
COMMAND ${CMAKE_COMMAND} -E copy ${ICO_PARENT_DIR}/${WX_XRC_ICON} ${WX_XRC_ICON}
COMMAND wxrc wxvbam.xrc -o wxvbam.xrs
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${ICO_PARENT_DIR}/${XRC_ITEM}
DEPENDS wxvbam.xrc)
ADD_CUSTOM_COMMAND(OUTPUT builtin-xrc.h
COMMAND ${CMAKE_COMMAND} -DINFILE=wxvbam.xrs -DOUTFILE=builtin-xrc.h -DVARNAME=builtin_xrs -P ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.cmake
DEPENDS wxvbam.xrs)
# use a built-in vba-over.ini if no config file present
ADD_CUSTOM_COMMAND(OUTPUT builtin-over.h
COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_CURRENT_SOURCE_DIR}/../vba-over.ini -DOUTFILE=builtin-over.h -DVARNAME=builtin_over -P ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.cmake
DEPENDS ../vba-over.ini)
# I don't like duplicating/triplicating code, so I only declare
# event handlers once, and copy them in other places they are needed
# all using portable cmake code
ADD_CUSTOM_COMMAND(OUTPUT cmdtab.cpp cmdhandlers.h cmd-evtable.h
COMMAND
${CMAKE_COMMAND} -D OUTDIR=${CMAKE_CURRENT_BINARY_DIR} -P copy-events.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS cmdevents.cpp)
#
# the following should be in the main file for consistency with
# other front ends, but can't due to cmake issues
# then again, the main file should be split up into separate dirs anyway
#
SET( SRC_WX
wxvbam.cpp
guiinit.cpp
viewers.cpp
gfxviewers.cpp
cmdevents.cpp
opts.cpp
sys.cpp
panel.cpp
viewsupt.cpp
widgets/keyedit.cpp
widgets/joyedit.cpp
widgets/sdljoy.cpp
widgets/wxmisc.cpp
# common, but not in lib, apparently
../common/SoundSDL.cpp
# probably ought to be in common
../sdl/text.cpp
# from external source with minor modifications
widgets/checkedlistctrl.cpp
# generated
cmdtab.cpp
# generated includes must be explicitly listed
builtin-xrc.h
builtin-over.h
cmdhandlers.h
cmd-evtable.h
)
IF(ENABLE_OPENAL)
SET( SRC_WX ${SRC_WX} openal.cpp )
ENDIF(ENABLE_OPENAL)
IF( WIN32 )
SET( SRC_WX ${SRC_WX} wxvbam.rc dsound.cpp xaudio2.cpp )
SET(DIRECTX_LIBRARIES -ldxguid -ldsound)
# not strictly directx, but win32-related
IF(ENABLE_DEBUGGER)
SET(DIRECTX_LIBRARIES ${DIRECTX_LIBRARIES} -lwsock32)
ENDIF(ENABLE_DEBUGGER)
ELSE( WIN32 )
SET(DIRECTX_LIBRARIES )
# generated file must be explicitly listed
SET( SRC_WX ${SRC_WX} wxvbam.xpm )
ENDIF( WIN32 )
IF(APPLE)
# icon must be generated manually
SET( SRC_WX ${SRC_WX} wxvbam.icns )
# png2icns is provided with libicns (http://icns.sourceforge.net/)
FIND_PROGRAM(PNG2ICNS png2icns)
# note: could add more icons, if available and proper size
SET(WX_APP_ICONS
${WX_APP_ICON}
${ICO_PARENT_DIR}/icons/16x16/apps/vbam.png)
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.icns
COMMAND ${PNG2ICNS} wxvbam.icns ${WX_APP_ICONS})
SET(MACOSX_BUNDLE_ICON_FILE wxvbam.icns)
SET_SOURCE_FILES_PROPERTIES(wxvbam.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
ENDIF(APPLE)
link_directories( ${CMAKE_BINARY_DIR} )
ADD_EXECUTABLE (
wxvbam
WIN32
MACOSX_BUNDLE
${SRC_WX}
)
TARGET_LINK_LIBRARIES (
wxvbam
${VBAMCORE_LIBS}
${wxWidgets_LIBRARIES}
${FFMPEG_LIBRARIES}
${DIRECTX_LIBRARIES}
${CAIRO_LIBRARIES}
)
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/wxvbam DESTINATION bin)
IF(NOT WIN32 AND NOT APPLE)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wxvbam.desktop DESTINATION share/applications)
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../gtk/icons/ DESTINATION share/icons/hicolor PATTERN ".svn" EXCLUDE)
ENDIF(NOT WIN32 AND NOT APPLE)
# for consistency with others, copy exe to top-level dir
if(WIN32)
SET(WX_EXE_NAME wxvbam${CMAKE_EXECUTABLE_SUFFIX})
ADD_CUSTOM_COMMAND(TARGET wxvbam POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${WX_EXE_NAME} ../../${WX_EXE_NAME})
else(WIN32)
if(APPLE)
SET(WX_EXE_NAME wxvbam.app)
# this should set ROM file types correctly
SET_PROPERTY(TARGET wxvbam APPEND PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/wxplist.in)
else(APPLE)
SET(WX_EXE_NAME wxvbam${CMAKE_EXECUTABLE_SUFFIX})
endif(APPLE)
ADD_CUSTOM_COMMAND(TARGET wxvbam POST_BUILD
# I'd rather make this link relative, but it's too hard
COMMAND rm -rf ../../${WX_EXE_NAME}
COMMAND ln -s ${CMAKE_CURRENT_BINARY_DIR}/${WX_EXE_NAME} ../../${WX_EXE_NAME})
endif(WIN32)

24
src/wx/bin2c.cmake Normal file
View File

@ -0,0 +1,24 @@
# portably convert binary file to header
FUNCTION(FILE2C INFILE VARNAME OUTFILE)
FILE(READ ${INFILE} HEXFILE HEX)
STRING(LENGTH ${HEXFILE} XRSLEN)
SET(HEXPOS 0)
FILE(WRITE ${OUTFILE}
"/* generated from ${INFILE}; do not edit */\n"
"const unsigned char ${VARNAME}[] = {")
WHILE(${HEXPOS} LESS ${XRSLEN})
MATH(EXPR LPOS "${HEXPOS} % 32")
IF(NOT ${LPOS})
FILE(APPEND ${OUTFILE} "\n")
ENDIF(NOT ${LPOS})
STRING(SUBSTRING ${HEXFILE} ${HEXPOS} 2 HEXBYTE)
FILE(APPEND ${OUTFILE} "0x${HEXBYTE}")
MATH(EXPR HEXPOS "${HEXPOS} + 2")
IF(${HEXPOS} LESS ${XRSLEN})
FILE(APPEND ${OUTFILE} ",")
ENDIF(${HEXPOS} LESS ${XRSLEN})
ENDWHILE(${HEXPOS} LESS ${XRSLEN})
FILE(APPEND ${OUTFILE} "};\n")
ENDFUNCTION(FILE2C)
FILE2C(${INFILE} ${VARNAME} ${OUTFILE})

2316
src/wx/cmdevents.cpp Normal file

File diff suppressed because it is too large Load Diff

56
src/wx/copy-events.cmake Normal file
View File

@ -0,0 +1,56 @@
# Create cmdtab.cpp, cmdhandlers.h, cmd-evtable.h from cmdevents.cpp
IF(NOT OUTDIR)
SET(OUTDIR ".")
ENDIF(NOT OUTDIR)
SET(CMDTAB "${OUTDIR}/cmdtab.cpp")
SET(EVPROTO "${OUTDIR}/cmdhandlers.h")
SET(EVTABLE "${OUTDIR}/cmd-evtable.h")
FILE(READ cmdevents.cpp MW)
STRING(REGEX MATCHALL "\nEVT_HANDLER([^\")]|\"[^\"]*\")*\\)" MW "${MW}")
# cmdtab.cpp is a table of cmd-id-name/cmd-name pairs
# sorted for binary searching
FILE(WRITE "${CMDTAB}" "// Generated from cmdevents.cpp; do not edit\n#include \"wxvbam.h\"\n\nstruct cmditem cmdtab[] = {\n")
SET(EVLINES )
FOREACH(EV ${MW})
# stripping the wxID_ makes it look better, but it's still all-caps
STRING(REGEX REPLACE "^[^\"]*\\((wxID_|)([^,]*),[^\"]*(\"[^\"]*\")[^,)]*(,[^)]*|).*"
" {wxT(\"\\2\"), wxTRANSLATE(\\3), XRCID(\"\\1\\2\")\\4 }"
EV "${EV}")
STRING(REGEX REPLACE "XRCID\\(\"(wxID_[^\"]*)\"\\)" "\\1" EV ${EV})
LIST(APPEND EVLINES "${EV},\n")
ENDFOREACH(EV)
LIST(SORT EVLINES)
STRING(REGEX REPLACE ",\n\$" "\n" EVLINES "${EVLINES}")
FILE(APPEND "${CMDTAB}" ${EVLINES})
FILE(APPEND "${CMDTAB}" "};\nconst int ncmds = sizeof(cmdtab) / sizeof(cmdtab[0]);\n")
# cmdhandlers.h contains prototypes for all handlers
FILE(WRITE "${EVPROTO}" "// Generated from cmdevents.cpp; do not edit\n")
FOREACH(EV ${MW})
STRING(REGEX REPLACE "^[^\"]*\\(" "void On" P1 "${EV}")
STRING(REGEX REPLACE ",.*" "(wxCommandEvent&);\n" P1 "${P1}")
FILE(APPEND "${EVPROTO}" "${P1}")
IF(EV MATCHES "EVT_HANDLER_MASK")
STRING(REGEX REPLACE "^[^\"]*\\(" "void Do" P1 "${EV}")
STRING(REGEX REPLACE ",.*" "();\n" P1 "${P1}")
FILE(APPEND "${EVPROTO}" "${P1}")
ENDIF(EV MATCHES "EVT_HANDLER_MASK")
ENDFOREACH(EV)
# cmd-evtable.h has the event table entries for all handlers
FILE(WRITE "${EVTABLE}" "// Generated from cmdevents.cpp; do not edit\n")
FOREACH(EV ${MW})
FILE(APPEND "${EVTABLE}" "EVT_MENU(")
STRING(REGEX REPLACE "[^\"]*\\(" "" EV "${EV}")
STRING(REGEX REPLACE ",.*" "" EV "${EV}")
IF("${EV}" MATCHES "wx.*")
FILE(APPEND "${EVTABLE}" "${EV}")
ELSE("${EV}" MATCHES "wx.*")
FILE(APPEND "${EVTABLE}" "XRCID(\"${EV}\")")
ENDIF("${EV}" MATCHES "wx.*")
FILE(APPEND "${EVTABLE}" ", MainFrame::On${EV})\n")
ENDFOREACH(EV)

87
src/wx/drawing.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef GAME_DRAWING_H
#define GAME_DRAWING_H
class BasicDrawingPanel : public DrawingPanel, public wxPanel
{
public:
BasicDrawingPanel(wxWindow *parent, int _width, int _height);
wxWindow *GetWindow() { return this; }
void Delete() { Destroy(); }
protected:
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
void DrawArea(wxWindowDC &dc);
DECLARE_CLASS()
DECLARE_EVENT_TABLE()
};
#ifndef NO_OGL
#include <wx/glcanvas.h>
class GLDrawingPanel : public DrawingPanel, public wxGLCanvas
{
public:
GLDrawingPanel(wxWindow *parent, int _width, int _height);
virtual ~GLDrawingPanel();
wxWindow *GetWindow() { return this; }
void Delete() { Destroy(); }
protected:
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
void OnSize(wxSizeEvent &);
void DrawArea(wxWindowDC &dc);
#if wxCHECK_VERSION(2,9,0) || !defined(__WXMAC__)
wxGLContext ctx;
#endif
bool did_init;
void Init();
GLuint texid, vlist;
int texsize;
DECLARE_CLASS()
DECLARE_EVENT_TABLE()
};
#endif
#if defined(__WXMSW__) && !defined(NO_D3D)
class DXDrawingPanel : public DrawingPanel, public wxPanel
{
public:
DXDrawingPanel(wxWindow *parent, int _width, int _height);
wxWindow *GetWindow() { return this; }
void Delete() { Destroy(); }
protected:
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
void DrawArea(wxWindowDC&);
bool did_init;
void Init();
DECLARE_CLASS()
DECLARE_EVENT_TABLE()
};
#endif
#ifndef NO_CAIRO
#include <cairo.h>
class CairoDrawingPanel : public DrawingPanel, public wxPanel
{
public:
CairoDrawingPanel(wxWindow *parent, int _width, int _height);
~CairoDrawingPanel();
wxWindow *GetWindow() { return this; }
void Delete() { Destroy(); }
protected:
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
void DrawArea(wxWindowDC&);
cairo_surface_t *conv_surf;
DECLARE_CLASS()
DECLARE_EVENT_TABLE()
};
#endif
#endif /* GAME_DRAWING_H */

334
src/wx/dsound.cpp Normal file
View File

@ -0,0 +1,334 @@
// Application
#include "wxvbam.h"
// Internals
#include "../System.h"
#include "../gba/GBA.h"
#include "../gba/Globals.h"
#include "../gba/Sound.h"
#include "../common/SoundDriver.h"
// DirectSound8
#define DIRECTSOUND_VERSION 0x0800
#include <dsound.h>
extern bool soundBufferLow;
class DirectSound : public SoundDriver
{
private:
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
LPDIRECTSOUNDNOTIFY8 dsbNotify;
HANDLE dsbEvent;
WAVEFORMATEX wfx; // Primary buffer wave format
int soundBufferLen;
int soundBufferTotalLen;
unsigned int soundNextPosition;
public:
DirectSound();
virtual ~DirectSound();
bool init(long sampleRate); // initialize the primary and secondary sound buffer
void pause(); // pause the secondary sound buffer
void reset(); // stop and reset the secondary sound buffer
void resume(); // resume the secondary sound buffer
void write(u16 * finalWave, int length); // write the emulated sound to the secondary sound buffer
};
DirectSound::DirectSound()
{
pDirectSound = NULL;
dsbPrimary = NULL;
dsbSecondary = NULL;
dsbNotify = NULL;
dsbEvent = NULL;
soundBufferTotalLen = 14700;
soundNextPosition = 0;
}
DirectSound::~DirectSound()
{
if(dsbNotify) {
dsbNotify->Release();
dsbNotify = NULL;
}
if(dsbEvent) {
CloseHandle(dsbEvent);
dsbEvent = NULL;
}
if(pDirectSound) {
if(dsbPrimary) {
dsbPrimary->Release();
dsbPrimary = NULL;
}
if(dsbSecondary) {
dsbSecondary->Release();
dsbSecondary = NULL;
}
pDirectSound->Release();
pDirectSound = NULL;
}
}
bool DirectSound::init(long sampleRate)
{
HRESULT hr;
DWORD freq;
DSBUFFERDESC dsbdesc;
int i;
hr = CoCreateInstance( CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (LPVOID *)&pDirectSound );
if( hr != S_OK ) {
wxLogError(_("Cannot create DirectSound %08x"), hr);
return false;
}
GUID dev;
if(gopts.audio_dev.empty())
dev = DSDEVID_DefaultPlayback;
else
CLSIDFromString(const_cast<wxChar *>(gopts.audio_dev.c_str()), &dev);
pDirectSound->Initialize( &dev );
if( hr != DS_OK ) {
wxLogError(_("Cannot create DirectSound %08x"), hr);
return false;
}
if( FAILED( hr = pDirectSound->SetCooperativeLevel( (HWND)wxGetApp().frame->GetHandle(), DSSCL_EXCLUSIVE ) ) ) {
wxLogError(_("Cannot SetCooperativeLevel %08x"), hr);
return false;
}
// Create primary sound buffer
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
if( !gopts.dsound_hw_accel ) {
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
}
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbPrimary, NULL ) ) ) {
wxLogError(_("Cannot CreateSoundBuffer %08x"), hr);
return false;
}
freq = sampleRate;
// calculate the number of samples per frame first
// then multiply it with the size of a sample frame (16 bit * stereo)
soundBufferLen = ( freq / 60 ) * 4;
soundBufferTotalLen = soundBufferLen * 10;
soundNextPosition = 0;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = freq;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
if( FAILED( hr = dsbPrimary->SetFormat( &wfx ) ) ) {
wxLogError( _("CreateSoundBuffer(primary) failed %08x"), hr );
return false;
}
// Create secondary sound buffer
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
if( !gopts.dsound_hw_accel ) {
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
}
dsbdesc.dwBufferBytes = soundBufferTotalLen;
dsbdesc.lpwfxFormat = &wfx;
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbSecondary, NULL ) ) ) {
wxLogError(_("CreateSoundBuffer(secondary) failed %08x"), hr );
return false;
}
if( FAILED( hr = dsbSecondary->SetCurrentPosition( 0 ) ) ) {
wxLogError(_("dsbSecondary->SetCurrentPosition failed %08x"), hr );
return false;
}
if( SUCCEEDED( hr = dsbSecondary->QueryInterface( IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify ) ) ) {
dsbEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
DSBPOSITIONNOTIFY notify[10];
for( i = 0; i < 10; i++ ) {
notify[i].dwOffset = i * soundBufferLen;
notify[i].hEventNotify = dsbEvent;
}
if( FAILED( dsbNotify->SetNotificationPositions( 10, notify ) ) ) {
dsbNotify->Release();
dsbNotify = NULL;
CloseHandle(dsbEvent);
dsbEvent = NULL;
}
}
// Play primary buffer
if( FAILED( hr = dsbPrimary->Play( 0, 0, DSBPLAY_LOOPING ) ) ) {
wxLogError( _("Cannot Play primary %08x"), hr );
return false;
}
return true;
}
void DirectSound::pause()
{
if( dsbSecondary == NULL ) return;
DWORD status;
dsbSecondary->GetStatus( &status );
if( status & DSBSTATUS_PLAYING ) dsbSecondary->Stop();
}
void DirectSound::reset()
{
if( dsbSecondary == NULL ) return;
dsbSecondary->Stop();
dsbSecondary->SetCurrentPosition( 0 );
soundNextPosition = 0;
}
void DirectSound::resume()
{
if( dsbSecondary == NULL ) return;
dsbSecondary->Play( 0, 0, DSBPLAY_LOOPING );
}
void DirectSound::write(u16 * finalWave, int length)
{
if(!pDirectSound) return;
HRESULT hr;
DWORD status = 0;
DWORD play = 0;
LPVOID lpvPtr1;
DWORD dwBytes1 = 0;
LPVOID lpvPtr2;
DWORD dwBytes2 = 0;
if( !speedup && synchronize && !gopts.throttle ) {
hr = dsbSecondary->GetStatus(&status);
if( status & DSBSTATUS_PLAYING ) {
if( !soundPaused ) {
while( true ) {
dsbSecondary->GetCurrentPosition(&play, NULL);
int BufferLeft = ((soundNextPosition <= play) ?
play - soundNextPosition :
soundBufferTotalLen - soundNextPosition + play);
if(BufferLeft > soundBufferLen)
{
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
soundBufferLow = true;
break;
}
soundBufferLow = false;
if(dsbEvent) {
WaitForSingleObject(dsbEvent, 50);
}
}
}
}/* else {
// TODO: remove?
setsoundPaused(true);
}*/
}
// Obtain memory address of write block.
// This will be in two parts if the block wraps around.
if( DSERR_BUFFERLOST == ( hr = dsbSecondary->Lock(
soundNextPosition,
soundBufferLen,
&lpvPtr1,
&dwBytes1,
&lpvPtr2,
&dwBytes2,
0 ) ) ) {
// If DSERR_BUFFERLOST is returned, restore and retry lock.
dsbSecondary->Restore();
hr = dsbSecondary->Lock(
soundNextPosition,
soundBufferLen,
&lpvPtr1,
&dwBytes1,
&lpvPtr2,
&dwBytes2,
0 );
}
soundNextPosition += soundBufferLen;
soundNextPosition = soundNextPosition % soundBufferTotalLen;
if( SUCCEEDED( hr ) ) {
// Write to pointers.
CopyMemory( lpvPtr1, finalWave, dwBytes1 );
if ( lpvPtr2 ) {
CopyMemory( lpvPtr2, finalWave + dwBytes1, dwBytes2 );
}
// Release the data back to DirectSound.
hr = dsbSecondary->Unlock( lpvPtr1, dwBytes1, lpvPtr2, dwBytes2 );
} else {
wxLogError(_("dsbSecondary->Lock() failed: %08x"), hr );
return;
}
}
SoundDriver *newDirectSound()
{
return new DirectSound();
}
struct devnames {
wxArrayString *names, *ids;
};
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR drvnam, LPVOID user)
{
devnames *dn = (devnames *)user;
dn->names->push_back(desc);
WCHAR buf[32 + 4 + 2 + 1]; // hex digits + "-" + "{}" + \0
StringFromGUID2(*guid, buf, sizeof(buf));
dn->ids->push_back(buf);
return TRUE;
}
bool GetDSDevices(wxArrayString &names, wxArrayString &ids)
{
devnames dn = { &names, &ids };
return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK;
}

77
src/wx/filters.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef FILTERS_H
#define FILTERS_H
// FIXME: these should be in a system header included by all users and all
// files which define these functions
// most 16-bit filters require space in src rounded up to u32
// those that take delta take 1 src line of pixels, rounded up to u32 size
// initial value appears to be all-0xff
void Pixelate32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Pixelate(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
// next 3*2 use Init_2xSaI(555|565) and do not take into account
int Init_2xSaI(u32 BitFormat);
// endianness or bit shift variables in init.
// next 4*2 may be MMX-accelerated
void _2xSaI32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
void _2xSaI(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
// void Scale_2xSaI(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Super2xSaI32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
void Super2xSaI(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
void SuperEagle32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
void SuperEagle(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
void AdMame2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void AdMame2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// next 4 convert to rgb24 in internal buffers first, and then back again
void Bilinear32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Bilinear(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void BilinearPlus32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void BilinearPlus(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Scanlines32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Scanlines(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void ScanlinesTV32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// "TV" here means each pixel is faded horizontally & vertically rather than
// inserting black scanlines
// this depends on RGB_LOW_BITS_MASK
extern int RGB_LOW_BITS_MASK;
void ScanlinesTV(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// next 2 require calling hq2x_init first and whenever bpp changes
void hq2x_init(unsigned bpp);
void hq2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void hq2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void lq2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void lq2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// the simple ones could greatly benefit from correct usage of preprocessor..
// in any case, they are worthless, since all renderers do "simple" or
// better by default
void Simple2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Simple2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Simple3x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Simple3x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Simple4x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void Simple4x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// note: 16-bit input for asm version only!
void hq3x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// this takes 32-bit input
// (by converting to 16-bit first in asm version)
void hq3x32_32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void hq3x16(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// note: 16-bit input for asm version only!
void hq4x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// this takes 32-bit input
// (by converting to 16-bit first in asm version)
void hq4x32_32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
void hq4x16(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
// call ifc to ignore previous frame / when starting new
void InterframeCleanup();
// all 4 are MMX-accelerated if enabled
void SmartIB(u8 *src, u32 spitch, int width, int height);
void SmartIB32(u8 *src, u32 spitch, int width, int height);
void MotionBlurIB(u8 *src, u32 spitch, int width, int height);
void MotionBlurIB32(u8 *src, u32 spitch, int width, int height);
void SmartIB(u8 *src, u32 spitch, int width, int starty, int height);
void SmartIB32(u8 *src, u32 spitch, int width, int starty, int height);
void MotionBlurIB(u8 *src, u32 spitch, int width, int starty, int height);
void MotionBlurIB32(u8 *src, u32 spitch, int width, int starty, int height);
#endif /* FILTERS_H */

1565
src/wx/gfxviewers.cpp Normal file

File diff suppressed because it is too large Load Diff

3309
src/wx/guiinit.cpp Normal file

File diff suppressed because it is too large Load Diff

2072
src/wx/ioregs.h Normal file

File diff suppressed because it is too large Load Diff

490
src/wx/openal.cpp Normal file
View File

@ -0,0 +1,490 @@
// === LOGALL writes very detailed informations to vba-trace.log ===
//#define LOGALL
#ifndef NO_OAL
// for gopts
// also, wx-related
#include "wxvbam.h"
// Interface
#include "../common/SoundDriver.h"
// OpenAL
#include "openal.h"
// Internals
#include "../gba/Sound.h"
#include "../gba/Globals.h" // for 'speedup' and 'synchronize'
// Debug
#include <assert.h>
#define ASSERT_SUCCESS assert( AL_NO_ERROR == ALFunction.alGetError() )
#ifndef LOGALL
// replace logging functions with comments
#ifdef winlog
#undef winlog
#endif
#define winlog //
#define debugState() //
#endif
struct OPENALFNTABLE;
class OpenAL : public SoundDriver
{
public:
OpenAL();
virtual ~OpenAL();
static wxDynamicLibrary Lib;
static bool LoadOAL();
static bool GetDevices(wxArrayString &names, wxArrayString &ids);
bool init(long sampleRate); // initialize the sound buffer queue
void pause(); // pause the secondary sound buffer
void reset(); // stop and reset the secondary sound buffer
void resume(); // play/resume the secondary sound buffer
void write(u16 * finalWave, int length); // write the emulated sound to a sound buffer
private:
static OPENALFNTABLE ALFunction;
bool initialized;
bool buffersLoaded;
ALCdevice *device;
ALCcontext *context;
ALuint *buffer;
ALuint tempBuffer;
ALuint source;
int freq;
int soundBufferLen;
#ifdef LOGALL
void debugState();
#endif
};
OpenAL::OpenAL()
{
initialized = false;
buffersLoaded = false;
device = NULL;
context = NULL;
buffer = (ALuint*)malloc( gopts.audio_buffers * sizeof( ALuint ) );
memset( buffer, 0, gopts.audio_buffers * sizeof( ALuint ) );
tempBuffer = 0;
source = 0;
}
OpenAL::~OpenAL()
{
if( !initialized ) return;
ALFunction.alSourceStop( source );
ASSERT_SUCCESS;
ALFunction.alSourcei( source, AL_BUFFER, 0 );
ASSERT_SUCCESS;
ALFunction.alDeleteSources( 1, &source );
ASSERT_SUCCESS;
ALFunction.alDeleteBuffers( gopts.audio_buffers, buffer );
ASSERT_SUCCESS;
free( buffer );
ALFunction.alcMakeContextCurrent( NULL );
// Wine incorrectly returns ALC_INVALID_VALUE
// and then fails the rest of these functions as well
// so there will be a leak under Wine, but that's a bug in Wine, not
// this code
//ASSERT_SUCCESS;
ALFunction.alcDestroyContext( context );
//ASSERT_SUCCESS;
ALFunction.alcCloseDevice( device );
//ASSERT_SUCCESS;
ALFunction.alGetError(); // reset error state
}
#ifdef LOGALL
void OpenAL::debugState()
{
ALint value = 0;
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &value );
ASSERT_SUCCESS;
winlog( " soundPaused = %i\n", soundPaused );
winlog( " Source:\n" );
winlog( " State: " );
switch( value )
{
case AL_INITIAL:
winlog( "AL_INITIAL\n" );
break;
case AL_PLAYING:
winlog( "AL_PLAYING\n" );
break;
case AL_PAUSED:
winlog( "AL_PAUSED\n" );
break;
case AL_STOPPED:
winlog( "AL_STOPPED\n" );
break;
default:
winlog( "!unknown!\n" );
break;
}
ALFunction.alGetSourcei( source, AL_BUFFERS_QUEUED, &value );
ASSERT_SUCCESS;
winlog( " Buffers in queue: %i\n", value );
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &value );
ASSERT_SUCCESS;
winlog( " Buffers processed: %i\n", value );
}
#endif
bool OpenAL::init(long sampleRate)
{
winlog( "OpenAL::init\n" );
assert( initialized == false );
if( !LoadOAL() ) {
wxLogError( _("OpenAL library could not be found on your system. Please install the runtime from http://openal.org") );
return false;
}
if( !gopts.audio_dev.empty() ) {
device = ALFunction.alcOpenDevice( gopts.audio_dev.mb_str() );
} else {
device = ALFunction.alcOpenDevice( NULL );
}
assert( device != NULL );
context = ALFunction.alcCreateContext( device, NULL );
assert( context != NULL );
ALCboolean retVal = ALFunction.alcMakeContextCurrent( context );
assert( ALC_TRUE == retVal );
ALFunction.alGenBuffers( gopts.audio_buffers, buffer );
ASSERT_SUCCESS;
ALFunction.alGenSources( 1, &source );
ASSERT_SUCCESS;
freq = sampleRate;
// calculate the number of samples per frame first
// then multiply it with the size of a sample frame (16 bit * stereo)
soundBufferLen = ( freq / 60 ) * 4;
initialized = true;
return true;
}
void OpenAL::resume()
{
if( !initialized ) return;
winlog( "OpenAL::resume\n" );
if( !buffersLoaded ) return;
debugState();
ALint sourceState = 0;
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
ASSERT_SUCCESS;
if( sourceState != AL_PLAYING ) {
ALFunction.alSourcePlay( source );
ASSERT_SUCCESS;
}
debugState();
}
void OpenAL::pause()
{
if( !initialized ) return;
winlog( "OpenAL::pause\n" );
if( !buffersLoaded ) return;
debugState();
ALint sourceState = 0;
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
ASSERT_SUCCESS;
if( sourceState == AL_PLAYING ) {
ALFunction.alSourcePause( source );
ASSERT_SUCCESS;
}
debugState();
}
void OpenAL::reset()
{
if( !initialized ) return;
winlog( "OpenAL::reset\n" );
if( !buffersLoaded ) return;
debugState();
ALint sourceState = 0;
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
ASSERT_SUCCESS;
if( sourceState != AL_STOPPED ) {
ALFunction.alSourceStop( source );
ASSERT_SUCCESS;
}
debugState();
}
void OpenAL::write(u16 * finalWave, int length)
{
if( !initialized ) return;
winlog( "OpenAL::write\n" );
debugState();
ALint sourceState = 0;
ALint nBuffersProcessed = 0;
if( !buffersLoaded ) {
// ==initial buffer filling==
winlog( " initial buffer filling\n" );
for( int i = 0 ; i < gopts.audio_buffers ; i++ ) {
// Filling the buffers explicitly with silence would be cleaner,
// but the very first sample is usually silence anyway.
ALFunction.alBufferData( buffer[i], AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq );
ASSERT_SUCCESS;
}
ALFunction.alSourceQueueBuffers( source, gopts.audio_buffers, buffer );
ASSERT_SUCCESS;
buffersLoaded = true;
} else {
// ==normal buffer refreshing==
nBuffersProcessed = 0;
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &nBuffersProcessed );
ASSERT_SUCCESS;
if( nBuffersProcessed == gopts.audio_buffers ) {
// we only want to know about it when we are emulating at full speed or faster:
if( ( gopts.throttle >= 100 ) || ( gopts.throttle == 0 ) ) {
if( systemVerbose & VERBOSE_SOUNDOUTPUT ) {
static unsigned int i = 0;
log( "OpenAL: Buffers were not refilled fast enough (i=%i)\n", i++ );
}
}
}
if( !speedup && synchronize && !gopts.throttle ) {
// wait until at least one buffer has finished
while( nBuffersProcessed == 0 ) {
winlog( " waiting...\n" );
// wait for about half the time one buffer needs to finish
// unoptimized: ( sourceBufferLen * 1000 ) / ( freq * 2 * 2 ) * 1/2
wxMilliSleep( soundBufferLen / ( freq >> 7 ) );
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &nBuffersProcessed );
ASSERT_SUCCESS;
}
} else {
if( nBuffersProcessed == 0 ) return;
}
assert( nBuffersProcessed > 0 );
// unqueue buffer
tempBuffer = 0;
ALFunction.alSourceUnqueueBuffers( source, 1, &tempBuffer );
ASSERT_SUCCESS;
// refill buffer
ALFunction.alBufferData( tempBuffer, AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq );
ASSERT_SUCCESS;
// requeue buffer
ALFunction.alSourceQueueBuffers( source, 1, &tempBuffer );
ASSERT_SUCCESS;
}
// start playing the source if necessary
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
ASSERT_SUCCESS;
if( !soundPaused && ( sourceState != AL_PLAYING ) ) {
ALFunction.alSourcePlay( source );
ASSERT_SUCCESS;
}
}
SoundDriver *newOpenAL()
{
winlog( "newOpenAL\n" );
return new OpenAL();
}
// no more use of copyrighted OpenAL code just to load the stupid library
// this is for compatibility with MFC version
// positive: make an OpenAL-capable binary which does not require OpenAL
// negative: openal lib may not be in library path
// openal lib name is OS-dependent, and may even change based
// on where it was installed from
// On UNIX, it would probably be better to just hard link with libopenal
OPENALFNTABLE OpenAL::ALFunction = {NULL};
wxDynamicLibrary OpenAL::Lib;
bool OpenAL::LoadOAL()
{
if(!Lib.IsLoaded() &&
#ifdef __WXMSW__
// on win32, it's openal32.dll
!Lib.Load(wxT("openal32")) &&
#else
#ifdef __WXMAC__
// on macosx, it's just plain OpenAL
!Lib.Load(wxT("OpenAL"), wxDL_NOW|wxDL_VERBATIM) &&
#endif
#endif
// on linux, it's libopenal.so
// try standard name on all platforms
!Lib.Load(wxDynamicLibrary::CanonicalizeName(wxT("openal"))))
return false;
#define loadfn(t, n) do { \
if(!(ALFunction.n = (t)Lib.GetSymbol(wxT(#n)))) \
return false; \
} while(0)
//loadfn(LPALENABLE, alEnable);
//loadfn(LPALDISABLE, alDisable);
//loadfn(LPALISENABLED, alIsEnabled);
//loadfn(LPALGETSTRING, alGetString);
//loadfn(LPALGETBOOLEANV, alGetBooleanv);
//loadfn(LPALGETINTEGERV, alGetIntegerv);
//loadfn(LPALGETFLOATV, alGetFloatv);
//loadfn(LPALGETDOUBLEV, alGetDoublev);
//loadfn(LPALGETBOOLEAN, alGetBoolean);
//loadfn(LPALGETINTEGER, alGetInteger);
//loadfn(LPALGETFLOAT, alGetFloat);
//loadfn(LPALGETDOUBLE, alGetDouble);
loadfn(LPALGETERROR, alGetError);
//loadfn(LPALISEXTENSIONPRESENT, alIsExtensionPresent);
//loadfn(LPALGETPROCADDRESS, alGetProcAddress);
//loadfn(LPALGETENUMVALUE, alGetEnumValue);
//loadfn(LPALLISTENERF, alListenerf);
//loadfn(LPALLISTENER3F, alListener3f);
//loadfn(LPALLISTENERFV, alListenerfv);
//loadfn(LPALLISTENERI, alListeneri);
//loadfn(LPALLISTENER3I, alListener3i);
//loadfn(LPALLISTENERIV, alListeneriv);
//loadfn(LPALGETLISTENERF, alGetListenerf);
//loadfn(LPALGETLISTENER3F, alGetListener3f);
//loadfn(LPALGETLISTENERFV, alGetListenerfv);
//loadfn(LPALGETLISTENERI, alGetListeneri);
//loadfn(LPALGETLISTENER3I, alGetListener3i);
//loadfn(LPALGETLISTENERIV, alGetListeneriv);
loadfn(LPALGENSOURCES, alGenSources);
loadfn(LPALDELETESOURCES, alDeleteSources);
//loadfn(LPALISSOURCE, alIsSource);
//loadfn(LPALSOURCEF, alSourcef);
//loadfn(LPALSOURCE3F, alSource3f);
//loadfn(LPALSOURCEFV, alSourcefv);
loadfn(LPALSOURCEI, alSourcei);
//loadfn(LPALSOURCE3I, alSource3i);
//loadfn(LPALSOURCEIV, alSourceiv);
//loadfn(LPALGETSOURCEF, alGetSourcef);
//loadfn(LPALGETSOURCE3F, alGetSource3f);
//loadfn(LPALGETSOURCEFV, alGetSourcefv);
loadfn(LPALGETSOURCEI, alGetSourcei);
//loadfn(LPALGETSOURCE3I, alGetSource3i);
//loadfn(LPALGETSOURCEIV, alGetSourceiv);
//loadfn(LPALSOURCEPLAYV, alSourcePlayv);
//loadfn(LPALSOURCESTOPV, alSourceStopv);
//loadfn(LPALSOURCEREWINDV, alSourceRewindv);
//loadfn(LPALSOURCEPAUSEV, alSourcePausev);
loadfn(LPALSOURCEPLAY, alSourcePlay);
loadfn(LPALSOURCESTOP, alSourceStop);
//loadfn(LPALSOURCEREWIND, alSourceRewind);
loadfn(LPALSOURCEPAUSE, alSourcePause);
loadfn(LPALSOURCEQUEUEBUFFERS, alSourceQueueBuffers);
loadfn(LPALSOURCEUNQUEUEBUFFERS, alSourceUnqueueBuffers);
loadfn(LPALGENBUFFERS, alGenBuffers);
loadfn(LPALDELETEBUFFERS, alDeleteBuffers);
//loadfn(LPALISBUFFER, alIsBuffer);
loadfn(LPALBUFFERDATA, alBufferData);
//loadfn(LPALBUFFERF, alBufferf);
//loadfn(LPALBUFFER3F, alBuffer3f);
//loadfn(LPALBUFFERFV, alBufferfv);
//loadfn(LPALBUFFERI, alBufferi);
//loadfn(LPALBUFFER3I, alBuffer3i);
//loadfn(LPALBUFFERIV, alBufferiv);
//loadfn(LPALGETBUFFERF, alGetBufferf);
//loadfn(LPALGETBUFFER3F, alGetBuffer3f);
//loadfn(LPALGETBUFFERFV, alGetBufferfv);
//loadfn(LPALGETBUFFERI, alGetBufferi);
//loadfn(LPALGETBUFFER3I, alGetBuffer3i);
//loadfn(LPALGETBUFFERIV, alGetBufferiv);
//loadfn(LPALDOPPLERFACTOR, alDopplerFactor);
//loadfn(LPALDOPPLERVELOCITY, alDopplerVelocity);
//loadfn(LPALSPEEDOFSOUND, alSpeedOfSound);
//loadfn(LPALDISTANCEMODEL, alDistanceModel);
loadfn(LPALCCREATECONTEXT, alcCreateContext);
loadfn(LPALCMAKECONTEXTCURRENT, alcMakeContextCurrent);
//loadfn(LPALCPROCESSCONTEXT, alcProcessContext);
//loadfn(LPALCSUSPENDCONTEXT, alcSuspendContext);
loadfn(LPALCDESTROYCONTEXT, alcDestroyContext);
//loadfn(LPALCGETCURRENTCONTEXT, alcGetCurrentContext);
//loadfn(LPALCGETCONTEXTSDEVICE, alcGetContextsDevice);
loadfn(LPALCOPENDEVICE, alcOpenDevice);
loadfn(LPALCCLOSEDEVICE, alcCloseDevice);
//loadfn(LPALCGETERROR, alcGetError);
loadfn(LPALCISEXTENSIONPRESENT, alcIsExtensionPresent);
//loadfn(LPALCGETPROCADDRESS, alcGetProcAddress);
//loadfn(LPALCGETENUMVALUE, alcGetEnumValue);
loadfn(LPALCGETSTRING, alcGetString);
//loadfn(LPALCGETINTEGERV, alcGetIntegerv);
//loadfn(LPALCCAPTUREOPENDEVICE, alcCaptureOpenDevice);
//loadfn(LPALCCAPTURECLOSEDEVICE, alcCaptureCloseDevice);
//loadfn(LPALCCAPTURESTART, alcCaptureStart);
//loadfn(LPALCCAPTURESTOP, alcCaptureStop);
//loadfn(LPALCCAPTURESAMPLES, alcCaptureSamples);
return true;
}
bool GetOALDevices(wxArrayString &names, wxArrayString &ids)
{
return OpenAL::GetDevices(names, ids);
}
bool OpenAL::GetDevices(wxArrayString &names, wxArrayString &ids)
{
if(!OpenAL::LoadOAL())
return false;
#ifdef ALC_DEVICE_SPECIFIER
if(ALFunction.alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_FALSE)
// this extension isn't critical to OpenAL operating
return true;
const char *devs = ALFunction.alcGetString(NULL, ALC_DEVICE_SPECIFIER);
while(*devs) {
names.push_back(wxString(devs, wxConvLibc));
ids.push_back(names[names.size() - 1]);
devs += strlen(devs) + 1;
}
#else
// should work anyway, but must always use default driver
return true;
#endif
}
#endif

123
src/wx/openal.h Normal file
View File

@ -0,0 +1,123 @@
// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES
// on mac, ALC_NO_PROTOTYPES as well
#define AL_NO_PROTOTYPES 1
// on mac, alc pointer typedefs ony happen for ALC if ALC_NO_PROTOTYPES
// unfortunately, there is a bug in the system headers (use of ALCvoid when
// void should be used; shame on Apple for introducing this error, and shame
// on Creative for making a typedef to void in the first place)
//#define ALC_NO_PROTOTYPES 1
#include <al.h>
#include <alc.h>
// since the ALC typedefs are broken on Mac:
#ifdef __WXMAC__
typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist);
typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context );
typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context );
typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename );
typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device );
typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname );
typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param );
#endif
// no more use of copyrighted OpenAL code just to load the stupid library
struct OPENALFNTABLE {
//LPALENABLE alEnable;
//LPALDISABLE alDisable;
//LPALISENABLED alIsEnabled;
//LPALGETSTRING alGetString;
//LPALGETBOOLEANV alGetBooleanv;
//LPALGETINTEGERV alGetIntegerv;
//LPALGETFLOATV alGetFloatv;
//LPALGETDOUBLEV alGetDoublev;
//LPALGETBOOLEAN alGetBoolean;
//LPALGETINTEGER alGetInteger;
//LPALGETFLOAT alGetFloat;
//LPALGETDOUBLE alGetDouble;
LPALGETERROR alGetError;
LPALISEXTENSIONPRESENT alIsExtensionPresent;
//LPALGETPROCADDRESS alGetProcAddress;
//LPALGETENUMVALUE alGetEnumValue;
//LPALLISTENERF alListenerf;
//LPALLISTENER3F alListener3f;
//LPALLISTENERFV alListenerfv;
//LPALLISTENERI alListeneri;
//LPALLISTENER3I alListener3i;
//LPALLISTENERIV alListeneriv;
//LPALGETLISTENERF alGetListenerf;
//LPALGETLISTENER3F alGetListener3f;
//LPALGETLISTENERFV alGetListenerfv;
//LPALGETLISTENERI alGetListeneri;
//LPALGETLISTENER3I alGetListener3i;
//LPALGETLISTENERIV alGetListeneriv;
LPALGENSOURCES alGenSources;
LPALDELETESOURCES alDeleteSources;
//LPALISSOURCE alIsSource;
//LPALSOURCEF alSourcef;
//LPALSOURCE3F alSource3f;
//LPALSOURCEFV alSourcefv;
LPALSOURCEI alSourcei;
//LPALSOURCE3I alSource3i;
//LPALSOURCEIV alSourceiv;
//LPALGETSOURCEF alGetSourcef;
//LPALGETSOURCE3F alGetSource3f;
//LPALGETSOURCEFV alGetSourcefv;
LPALGETSOURCEI alGetSourcei;
//LPALGETSOURCE3I alGetSource3i;
//LPALGETSOURCEIV alGetSourceiv;
//LPALSOURCEPLAYV alSourcePlayv;
//LPALSOURCESTOPV alSourceStopv;
//LPALSOURCEREWINDV alSourceRewindv;
//LPALSOURCEPAUSEV alSourcePausev;
LPALSOURCEPLAY alSourcePlay;
LPALSOURCESTOP alSourceStop;
//LPALSOURCEREWIND alSourceRewind;
LPALSOURCEPAUSE alSourcePause;
LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers;
LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers;
LPALGENBUFFERS alGenBuffers;
LPALDELETEBUFFERS alDeleteBuffers;
//LPALISBUFFER alIsBuffer;
LPALBUFFERDATA alBufferData;
//LPALBUFFERF alBufferf;
//LPALBUFFER3F alBuffer3f;
//LPALBUFFERFV alBufferfv;
//LPALBUFFERI alBufferi;
//LPALBUFFER3I alBuffer3i;
//LPALBUFFERIV alBufferiv;
//LPALGETBUFFERF alGetBufferf;
//LPALGETBUFFER3F alGetBuffer3f;
//LPALGETBUFFERFV alGetBufferfv;
//LPALGETBUFFERI alGetBufferi;
//LPALGETBUFFER3I alGetBuffer3i;
//LPALGETBUFFERIV alGetBufferiv;
//LPALDOPPLERFACTOR alDopplerFactor;
//LPALDOPPLERVELOCITY alDopplerVelocity;
//LPALSPEEDOFSOUND alSpeedOfSound;
//LPALDISTANCEMODEL alDistanceModel;
LPALCCREATECONTEXT alcCreateContext;
LPALCMAKECONTEXTCURRENT alcMakeContextCurrent;
//LPALCPROCESSCONTEXT alcProcessContext;
//LPALCSUSPENDCONTEXT alcSuspendContext;
LPALCDESTROYCONTEXT alcDestroyContext;
//LPALCGETCURRENTCONTEXT alcGetCurrentContext;
//LPALCGETCONTEXTSDEVICE alcGetContextsDevice;
LPALCOPENDEVICE alcOpenDevice;
LPALCCLOSEDEVICE alcCloseDevice;
//LPALCGETERROR alcGetError;
LPALCISEXTENSIONPRESENT alcIsExtensionPresent;
//LPALCGETPROCADDRESS alcGetProcAddress;
//LPALCGETENUMVALUE alcGetEnumValue;
LPALCGETSTRING alcGetString;
//LPALCGETINTEGERV alcGetIntegerv;
//LPALCCAPTUREOPENDEVICE alcCaptureOpenDevice;
//LPALCCAPTURECLOSEDEVICE alcCaptureCloseDevice;
//LPALCCAPTURESTART alcCaptureStart;
//LPALCCAPTURESTOP alcCaptureStop;
//LPALCCAPTURESAMPLES alcCaptureSamples;
};

773
src/wx/opts.cpp Normal file
View File

@ -0,0 +1,773 @@
#include "wxvbam.h"
#include <algorithm>
#include <wx/display.h>
/*
* disableSfx(F) -> cpuDisableSfx
* priority(2) -> threadPriority
* saveMoreCPU(F) -> Sm60FPS
*
* SDL:
* -p/--profile=hz
*/
/* not sure how well other compilers support field-init syntax */
#define STROPT(n, d, v) {wxT(n), d, &v}
#define INTOPT(n, d, v, max, min) {wxT(n), d, NULL, &v, NULL, max, min}
#define BOOLOPT(n, d, v) {wxT(n), d, NULL, NULL, NULL, 0, 0, &v}
#define ENUMOPT(n, d, v, e) {wxT(n), d, NULL, &v, e}
opts_t gopts;
// having the standard menu accels here means they will work even without menus
const wxAcceleratorEntry default_accels[] = {
wxAcceleratorEntry(wxMOD_CMD, wxT('C'), XRCID("CheatsList")),
wxAcceleratorEntry(wxMOD_CMD, wxT('N'), XRCID("NextFrame")),
// some ports add ctrl-q anyway, so may as well make it official
// maybe make alt-f4 universal as well...
// FIXME: ctrl-Q does not work on wxMSW
// FIXME: esc does not work on wxMSW
wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, wxID_EXIT),
wxAcceleratorEntry(wxMOD_CMD, wxT('X'), wxID_EXIT),
wxAcceleratorEntry(wxMOD_CMD, wxT('Q'), wxID_EXIT),
// FIXME: ctrl-W does not work on wxMSW
wxAcceleratorEntry(wxMOD_CMD, wxT('W'), wxID_CLOSE),
// load most recent is more commonly used than load other
//wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("Load")),
wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("LoadGameRecent")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F1, XRCID("LoadGame01")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F2, XRCID("LoadGame02")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F3, XRCID("LoadGame03")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F4, XRCID("LoadGame04")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F5, XRCID("LoadGame05")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F6, XRCID("LoadGame06")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F7, XRCID("LoadGame07")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F8, XRCID("LoadGame08")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F9, XRCID("LoadGame09")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F10, XRCID("LoadGame10")),
wxAcceleratorEntry(wxMOD_NONE, WXK_PAUSE, XRCID("Pause")),
wxAcceleratorEntry(wxMOD_CMD, wxT('P'), XRCID("Pause")),
wxAcceleratorEntry(wxMOD_CMD, wxT('R'), XRCID("Reset")),
// save oldest is more commonly used than save other
//wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("Save")),
wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("SaveGameOldest")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F1, XRCID("SaveGame01")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F2, XRCID("SaveGame02")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F3, XRCID("SaveGame03")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F4, XRCID("SaveGame04")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F5, XRCID("SaveGame05")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F6, XRCID("SaveGame06")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F7, XRCID("SaveGame07")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F8, XRCID("SaveGame08")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F9, XRCID("SaveGame09")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F10, XRCID("SaveGame10")),
// I prefer the SDL ESC key binding
//wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, XRCID("ToggleFullscreen"),
// alt-enter is more standard anyway
wxAcceleratorEntry(wxMOD_ALT, WXK_RETURN, XRCID("ToggleFullscreen")),
wxAcceleratorEntry(wxMOD_ALT, wxT('1'), XRCID("JoypadAutofireA")),
wxAcceleratorEntry(wxMOD_ALT, wxT('2'), XRCID("JoypadAutofireB")),
wxAcceleratorEntry(wxMOD_ALT, wxT('3'), XRCID("JoypadAutofireL")),
wxAcceleratorEntry(wxMOD_ALT, wxT('4'), XRCID("JoypadAutofireR")),
wxAcceleratorEntry(wxMOD_CMD, wxT('1'), XRCID("VideoLayersBG0")),
wxAcceleratorEntry(wxMOD_CMD, wxT('2'), XRCID("VideoLayersBG1")),
wxAcceleratorEntry(wxMOD_CMD, wxT('3'), XRCID("VideoLayersBG2")),
wxAcceleratorEntry(wxMOD_CMD, wxT('4'), XRCID("VideoLayersBG3")),
wxAcceleratorEntry(wxMOD_CMD, wxT('5'), XRCID("VideoLayersOBJ")),
wxAcceleratorEntry(wxMOD_CMD, wxT('6'), XRCID("VideoLayersWIN0")),
wxAcceleratorEntry(wxMOD_CMD, wxT('7'), XRCID("VideoLayersWIN1")),
wxAcceleratorEntry(wxMOD_CMD, wxT('8'), XRCID("VideoLayersOBJWIN")),
wxAcceleratorEntry(wxMOD_CMD, wxT('B'), XRCID("Rewind")),
// following are not in standard menus
// FILExx are filled in when recent menu is filled
wxAcceleratorEntry(wxMOD_CMD, WXK_F1, wxID_FILE1),
wxAcceleratorEntry(wxMOD_CMD, WXK_F2, wxID_FILE2),
wxAcceleratorEntry(wxMOD_CMD, WXK_F3, wxID_FILE3),
wxAcceleratorEntry(wxMOD_CMD, WXK_F4, wxID_FILE4),
wxAcceleratorEntry(wxMOD_CMD, WXK_F5, wxID_FILE5),
wxAcceleratorEntry(wxMOD_CMD, WXK_F6, wxID_FILE6),
wxAcceleratorEntry(wxMOD_CMD, WXK_F7, wxID_FILE7),
wxAcceleratorEntry(wxMOD_CMD, WXK_F8, wxID_FILE8),
wxAcceleratorEntry(wxMOD_CMD, WXK_F9, wxID_FILE9),
wxAcceleratorEntry(wxMOD_CMD, WXK_F10, wxID_FILE10),
wxAcceleratorEntry(wxMOD_CMD, wxT('0'), XRCID("VideoLayersReset")),
wxAcceleratorEntry(wxMOD_CMD, wxT('G'), XRCID("ChangeFilter")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ADD, XRCID("IncreaseVolume")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_SUBTRACT, XRCID("DecreaseVolume")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ENTER, XRCID("ToggleSound"))
};
const int num_def_accels = sizeof(default_accels)/sizeof(default_accels[0]);
// Note: this must match GUI widget names or GUI won't work
// This table's order determines tab order as well
const wxChar * const joynames[NUM_KEYS] = {
wxT("Up"), wxT("Down"), wxT("Left"), wxT("Right"),
wxT("A"), wxT("B"), wxT("L"), wxT("R"),
wxT("Select"), wxT("Start"),
wxT("MotionUp"), wxT("MotionDown"), wxT("MotionLeft"), wxT("MotionRight"),
wxT("AutoA"), wxT("AutoB"),
wxT("Speed"), wxT("Capture"), wxT("GS")
};
wxJoyKeyBinding defkeys[NUM_KEYS * 2] = {
{ WXK_UP }, { 1, WXJB_AXIS_MINUS, 1 }, { WXK_DOWN }, { 1, WXJB_AXIS_PLUS, 1 },
{ WXK_LEFT }, { 0, WXJB_AXIS_MINUS, 1 }, { WXK_RIGHT }, { 0, WXJB_AXIS_PLUS, 1 },
{ wxT('X') }, { 0, WXJB_BUTTON, 1 }, { wxT('Z') }, { 1, WXJB_BUTTON, 1 },
{ wxT('A') }, { 2, WXJB_BUTTON, 1 }, { wxT('S') }, { 3, WXJB_BUTTON, 1 },
{ WXK_BACK }, { 4, WXJB_BUTTON, 1 }, { WXK_RETURN }, { 5, WXJB_BUTTON, 1 },
{ WXK_NUMPAD_UP }, { 2, WXJB_AXIS_PLUS, 1 }, { WXK_NUMPAD_DOWN }, { 2, WXJB_AXIS_MINUS, 1 },
{ WXK_NUMPAD_LEFT }, { 3, WXJB_AXIS_MINUS, 1 }, { WXK_NUMPAD_RIGHT }, { 3, WXJB_AXIS_PLUS, 1 },
{ wxT('W') }, { 0 }, { wxT('Q') }, { 0 },
{ WXK_SPACE }, { 0 }, { WXK_F11 }, { 0 },
{ 0 } , { 0 }
};
wxAcceleratorEntry_v sys_accels;
// Note: this table must be sorted in option name order
// Both for better user display and for (fast) searching by name
opt_desc opts[] = {
BOOLOPT("Display/Bilinear", wxTRANSLATE("Use bilinear filter with 3d renderer"), gopts.bilinear),
BOOLOPT("Display/DisableStatus", wxTRANSLATE("Disable on-screen status messages"), gopts.no_osd_status),
#ifdef MMX
BOOLOPT("Display/EnableMMX", wxTRANSLATE("Enable MMX"), cpu_mmx),
#endif
ENUMOPT("Display/Filter", wxTRANSLATE("Full-screen filter to apply"), gopts.filter,
wxTRANSLATE("none|2xsai|super2xsai|supereagle|pixelate|advmame|"
"bilinear|bilinearplus|scanlines|tvmode|hq2x|lq2x|"
"simple2x|simple3x|hq3x|simple4x|hq4x|plugin")),
STROPT ("Display/FilterPlugin", wxTRANSLATE("Filter plugin library"), gopts.filter_plugin),
BOOLOPT("Display/Fullscreen", wxTRANSLATE("Enter fullscreen mode at startup"), gopts.fullscreen),
INTOPT ("Display/FullscreenDepth", wxTRANSLATE("Fullscreen mode color depth (0 = any)"), gopts.fs_mode.bpp, 0, 999),
INTOPT ("Display/FullscreenFreq", wxTRANSLATE("Fullscreen mode frequency (0 = any)"), gopts.fs_mode.refresh, 0, 999),
INTOPT ("Display/FullscreenHeight", wxTRANSLATE("Fullscreen mode height (0 = desktop)"), gopts.fs_mode.h, 0, 99999),
INTOPT ("Display/FullscreenWidth", wxTRANSLATE("Fullscreen mode width (0 = desktop)"), gopts.fs_mode.w, 0, 99999),
ENUMOPT("Display/IFB", wxTRANSLATE("Interframe blending function"), gopts.ifb, wxTRANSLATE("none|smart|motionblur")),
INTOPT ("Display/MaxScale", wxTRANSLATE("Maximum scale factor (0 = no limit)"), gopts.max_scale, 0, 100),
INTOPT ("Display/MaxThreads", wxTRANSLATE("Maximum number of threads to run filters in"), gopts.max_threads, 1, 8),
ENUMOPT("Display/RenderMethod", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method,
#ifdef __WXMSW__
// try to keep variations to a minimum to ease translation
// since the config file stores strings rather than numbers, the
// ordering does not have to stay fixed
// but the numbers need to match the usage in code
wxTRANSLATE("simple|opengl|cairo|direct3d")
#else
wxTRANSLATE("simple|opengl|cairo")
#endif
),
INTOPT ("Display/Scale", wxTRANSLATE("Default scale factor"), gopts.video_scale, 1, 6),
ENUMOPT("Display/ShowSpeed", wxTRANSLATE("Show speed indicator"), gopts.osd_speed, wxTRANSLATE("no|percent|detailed")),
BOOLOPT("Display/Stretch", wxTRANSLATE("Retain aspect ratio when resizing"), gopts.retain_aspect),
BOOLOPT("Display/Transparent", wxTRANSLATE("Draw on-screen messages transparently"), gopts.osd_transparent),
BOOLOPT("Display/Vsync", wxTRANSLATE("Wait for vertical sync"), gopts.vsync),
BOOLOPT("GB/AutomaticBorder", wxTRANSLATE("Automatically enable border for Super GameBoy games"), gbBorderAutomatic),
STROPT ("GB/BiosFile", wxTRANSLATE("BIOS file to use for GB, if enabled"), gopts.gb_bios),
BOOLOPT("GB/Border", wxTRANSLATE("Always enable border"), gbBorderOn),
ENUMOPT("GB/EmulatorType", wxTRANSLATE("Type of system to emulate"), gbEmulatorType, wxTRANSLATE("auto|gba|gbc|sgb|sgb2|gb")),
BOOLOPT("GB/EnablePrinter", wxTRANSLATE("Enable printer emulation"), gopts.gbprint),
INTOPT ("GB/FrameSkip", wxTRANSLATE("Skip frames. Values are 0-9 or -1 to skip automatically based on time."), gopts.gb_frameskip, -1, 9),
STROPT ("GB/GBCBiosFile", wxTRANSLATE("BIOS file to use for GBC, if enabled"), gopts.gbc_bios),
BOOLOPT("GB/GBCUseBiosFile", wxTRANSLATE("Use the specified BIOS file for GBC"), gopts.gbc_use_bios),
BOOLOPT("GB/LCDColor", wxTRANSLATE("Emulate washed colors of LCD"), gbColorOption),
ENUMOPT("GB/Palette", wxTRANSLATE("The palette to use"), gbPaletteOption, wxTRANSLATE("default|user1|user2")),
{ wxT("GB/Palette0"), wxTRANSLATE("The default palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
{ wxT("GB/Palette1"), wxTRANSLATE("The first user palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
{ wxT("GB/Palette2"), wxTRANSLATE("The second user palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
BOOLOPT("GB/PrintAutoPage", wxTRANSLATE("Automatically gather a full page before printing"), gopts.print_auto_page),
BOOLOPT("GB/PrintScreenCap", wxTRANSLATE("Automatically save printouts as screen captures with -print suffix"), gopts.print_screen_cap),
STROPT ("GB/ROMDir", wxTRANSLATE("Directory to look for ROM files"), gopts.gb_rom_dir),
BOOLOPT("GB/UseBiosFile", wxTRANSLATE("Use the specified BIOS file for GB"), gopts.gb_use_bios),
BOOLOPT("GBA/AGBPrinter", wxTRANSLATE("Enable AGB printer"), gopts.agbprint),
STROPT ("GBA/BiosFile", wxTRANSLATE("BIOS file to use, if enabled"), gopts.gba_bios),
BOOLOPT("GBA/EnableRTC", wxTRANSLATE("Enable RTC (vba-over.ini override is rtcEnabled"), gopts.rtc),
ENUMOPT("GBA/FlashSize", wxTRANSLATE("Flash size (kb) (vba-over.ini override is flashSize in bytes)"), gopts.flash_size, wxTRANSLATE("64|128")),
INTOPT ("GBA/FrameSkip", wxTRANSLATE("Skip frames. Values are 0-9 or -1 to skip automatically based on time."), gopts.gba_frameskip, -1, 9),
#ifndef NO_LINK
BOOLOPT("GBA/Joybus", wxTRANSLATE("Enable joybus"), gba_joybus_enabled),
STROPT ("GBA/JoybusHost", wxTRANSLATE("Joybus host address"), gopts.joybus_host),
BOOLOPT("GBA/Link", wxTRANSLATE("Enable link cable"), gba_link_enabled),
BOOLOPT("GBA/LinkFast", wxTRANSLATE("Enable faster network protocol by default"), lanlink.speed),
STROPT ("GBA/LinkHost", wxTRANSLATE("Default network link client host"), gopts.link_host),
ENUMOPT("GBA/LinkProto", wxTRANSLATE("Default network protocol"), gopts.link_proto, wxTRANSLATE("tcp|udp")),
BOOLOPT("GBA/LinkRFU", wxTRANSLATE("Enable RFU for link"), rfu_enabled),
INTOPT ("GBA/LinkTimeout", wxTRANSLATE("Link timeout (ms)"), linktimeout, 0, 9999999),
#endif
STROPT ("GBA/ROMDir", wxTRANSLATE("Directory to look for ROM files"), gopts.gba_rom_dir),
ENUMOPT("GBA/SaveType", wxTRANSLATE("Native save (\"battery\") hardware type (vba-over.ini override is saveType integer 0-5)"), gopts.save_type, wxTRANSLATE("auto|eeprom|sram|flash|eeprom+sensor|none")),
#if 0 // currently disabled
BOOLOPT("GBA/SkipIntro", wxTRANSLATE("Skip intro"), gopts.skip_intro),
#endif
BOOLOPT("GBA/UseBiosFile", wxTRANSLATE("Use the specified BIOS file"), gopts.gba_use_bios),
BOOLOPT("General/ApplyPatches", wxTRANSLATE("Apply IPS/UPS/IPF patches if found"), gopts.apply_patches),
BOOLOPT("General/AutoLoadLastState", wxTRANSLATE("Automatically load last saved state"), gopts.autoload_state),
BOOLOPT("General/AutoSaveCheatList", wxTRANSLATE("Automatically save and load cheat list"), gopts.autoload_cheats),
STROPT ("General/BatteryDir", wxTRANSLATE("Directory to store game save files (relative paths are relative to ROM; blank is config dir)"), gopts.battery_dir),
ENUMOPT("General/CaptureFormat", wxTRANSLATE("Screen capture file format"), gopts.cap_format, wxTRANSLATE("png|bmp")),
BOOLOPT("General/EnableCheats", wxTRANSLATE("Enable cheats"), cheatsEnabled),
BOOLOPT("General/FreezeRecent", wxTRANSLATE("Freeze recent load list"), gopts.recent_freeze),
BOOLOPT("General/PauseWhenInactive", wxTRANSLATE("Pause game when main window loses focus"), gopts.defocus_pause),
STROPT ("General/RecordingDir", wxTRANSLATE("Directory to store A/V and game recordings (relative paths are relative to ROM)"), gopts.recording_dir),
INTOPT ("General/RewindInterval", wxTRANSLATE("Number of seconds between rewind snapshots (0 to disable)"), gopts.rewind_interval, 0, 600),
STROPT ("General/ScreenshotDir", wxTRANSLATE("Directory to store screenshots (relative paths are relative to ROM)"), gopts.scrshot_dir),
BOOLOPT("General/SkipBios", wxTRANSLATE("Skip BIOS initialization"), skipBios),
STROPT ("General/StateDir", wxTRANSLATE("Directory to store saved state files (relative paths are relative to BatteryDir)"), gopts.state_dir),
BOOLOPT("General/StateLoadNoBattery", wxTRANSLATE("Do not overwrite native (battery) save when loading state"), skipSaveGameBattery),
BOOLOPT("General/StateLoadNoCheat", wxTRANSLATE("Do not overwrite cheat list when loading state"), skipSaveGameCheats),
INTOPT ("General/Throttle", wxTRANSLATE("Throttle game speed, even when accelerated (0-1000%, 0 = disabled)"), gopts.throttle, 0, 1000),
{ wxT("Joypad/*/*"), wxTRANSLATE("The parameter Joypad/<n>/<button> contains a comma-separated list of key names which map to joypad #<n> button <button>. Button is one of Up, Down, Left, Right, A, B, L, R, Select, Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, AutoB, Speed, Capture, GS") },
INTOPT ("Joypad/AutofireThrottle", wxTRANSLATE("The autofire toggle period, in frames (1/60 s)"), gopts.autofire_rate, 1, 1000),
INTOPT ("Joypad/Default", wxTRANSLATE("The number of the stick to use in single-player mode"), gopts.default_stick, 1, 4),
{ wxT("Keyboard/*"), wxTRANSLATE("The parameter Keyboard/<cmd> contains a comma-separated list of key names (e.g. Alt-Shift-F1). When the named key is pressed, the command <cmd> is executed.") },
ENUMOPT("Sound/AudioAPI", wxTRANSLATE("Sound API; if unsupported, default API will be used"), gopts.audio_api,
#ifdef __WXMSW__
// see comment on Display/RenderMethod
wxTRANSLATE("sdl|openal|directsound|xaudio2")
#else
wxTRANSLATE("sdl|openal")
#endif
),
INTOPT ("Sound/Buffers", wxTRANSLATE("Number of sound buffers"), gopts.audio_buffers, 2, 10),
INTOPT ("Sound/Enable", wxTRANSLATE("Bit mask of sound channels to enable"), gopts.sound_en, 0, 0x30f),
INTOPT ("Sound/GBAFiltering", wxTRANSLATE("GBA sound filtering (%)"), gopts.gba_sound_filter, 0, 100),
BOOLOPT("Sound/GBAInterpolation", wxTRANSLATE("GBA sound interpolation"), soundInterpolation),
BOOLOPT("Sound/GBDeclicking", wxTRANSLATE("GB sound declicking"), gopts.gb_declick),
INTOPT ("Sound/GBEcho", wxTRANSLATE("GB echo effect (%)"), gopts.gb_echo, 0, 100),
BOOLOPT("Sound/GBEnableEffects", wxTRANSLATE("Enable GB sound effects"), gb_effects_config.enabled),
INTOPT ("Sound/GBStereo", wxTRANSLATE("GB stereo effect (%)"), gopts.gb_stereo, 0, 100),
BOOLOPT("Sound/GBSurround", wxTRANSLATE("GB surround sound effect (%)"), gb_effects_config.surround),
ENUMOPT("Sound/Quality", wxTRANSLATE("Sound sample rate (kHz)"), gopts.sound_qual, wxTRANSLATE("48|44|22|11")),
BOOLOPT("Sound/Synchronize", wxTRANSLATE("Synchronize game to audio"), synchronize),
INTOPT ("Sound/Volume", wxTRANSLATE("Sound volume (%)"), gopts.sound_vol, 0, 200),
};
const int num_opts = sizeof(opts)/sizeof(opts[0]);
// This constructor only works with globally allocated gopts. It relies on
// the default value of every non-object to be 0.
opts_t::opts_t()
{
gba_frameskip = -1;
gb_frameskip = -1;
#ifdef __WXMSW__
audio_api = AUD_DIRECTSOUND;
#endif
video_scale = 3;
retain_aspect = true;
max_threads = wxThread::GetCPUCount();
if(max_threads > 8)
max_threads = 8;
if(max_threads < 0)
max_threads = 2;
audio_buffers = 5;
sound_en = 0x30f;
sound_vol = 100;
sound_qual = 1;
gb_echo = 20;
gb_stereo = 15;
gb_declick = true;
gba_sound_filter = 50;
bilinear = true;
default_stick = 1;
for(int i = 0; i < NUM_KEYS; i++) {
if(defkeys[i*2].key)
joykey_bindings[0][i].push_back(defkeys[i*2]);
if(defkeys[i*2+1].joy)
joykey_bindings[0][i].push_back(defkeys[i*2+1]);
}
recent = new wxFileHistory(10);
autofire_rate = 1;
gbprint = print_auto_page = true;
apply_patches = true;
}
// for binary_search() and friends
bool opt_lt(const opt_desc &opt1, const opt_desc &opt2)
{
return wxStrcmp(opt1.opt, opt2.opt) < 0;
}
// FIXME: simulate MakeInstanceFilename(VBA.ini) using subkeys (Slave%d/*)
void load_opts()
{
// just for sanity...
bool did_init = false;
if(did_init)
return;
did_init = true;
// Translations can't be initialized in static structures (before locale
// is initialized), so do them now
for(int i = 0; i < num_opts; i++)
opts[i].desc = wxGetTranslation(opts[i].desc);
// enumvals should not be translated, since they would cause config file
// change after lang change
// instead, translate when presented to user
wxConfig *cfg = wxGetApp().cfg;
cfg->SetPath(wxT("/"));
// enure there are no unknown options present
// note that items cannot be deleted until after loop or loop will fail
wxArrayString item_del, grp_del;
long grp_idx;
wxString s;
bool cont;
for(cont = cfg->GetFirstEntry(s, grp_idx); cont;
cont = cfg->GetNextEntry(s, grp_idx)) {
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
}
for(cont = cfg->GetFirstGroup(s, grp_idx); cont;
cont = cfg->GetNextGroup(s, grp_idx)) {
// ignore wxWidgets-managed global library settings
if(s == wxT("wxWindows"))
continue;
// ignore file history
if(s == wxT("Recent"))
continue;
cfg->SetPath(s);
int poff = s.size();
long entry_idx;
wxString e;
for(cont = cfg->GetFirstGroup(e, entry_idx); cont;
cont = cfg->GetNextGroup(e, entry_idx)) {
// the only one with subgroups
if(s == wxT("Joypad") && e.size() == 1 && e[0] >= wxT('1') && e[0] <= wxT('4')) {
s.append(wxT('/'));
s.append(e);
s.append(wxT('/'));
int poff2 = s.size();
cfg->SetPath(e);
long key_idx;
for(cont = cfg->GetFirstGroup(e, key_idx); cont;
cont = cfg->GetNextGroup(e, key_idx)) {
s.append(e);
wxLogWarning(_("Invalid option group %s present; removing if possible"), s.c_str());
grp_del.push_back(s);
s.resize(poff2);
}
for(cont = cfg->GetFirstEntry(e, key_idx); cont;
cont = cfg->GetNextEntry(e, key_idx)) {
int i;
for(i = 0; i < NUM_KEYS; i++)
if(e == joynames[i])
break;
if(i == NUM_KEYS) {
s.append(e);
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
s.resize(poff2);
}
}
s.resize(poff);
cfg->SetPath(wxT("/"));
cfg->SetPath(s);
} else {
s.append(wxT('/'));
s.append(e);
wxLogWarning(_("Invalid option group %s present; removing if possible"), s.c_str());
grp_del.push_back(s);
s.resize(poff);
}
}
for(cont = cfg->GetFirstEntry(e, entry_idx); cont;
cont = cfg->GetNextEntry(e, entry_idx)) {
// kb options come from a different list
if(s == wxT("Keyboard")) {
const cmditem dummy = { e.c_str() };
if(!std::binary_search(&cmdtab[0], &cmdtab[ncmds], dummy, cmditem_lt)) {
s.append(wxT('/'));
s.append(e);
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
s.resize(poff);
}
} else {
s.append(wxT('/'));
s.append(e);
const opt_desc dummy = { s.c_str() };
if(!std::binary_search(&opts[0], &opts[num_opts], dummy, opt_lt)) {
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
}
s.resize(poff);
}
}
cfg->SetPath(wxT("/"));
}
for(int i = 0; i < item_del.size(); i++)
cfg->DeleteEntry(item_del[i]);
for(int i = 0; i < grp_del.size(); i++)
cfg->DeleteGroup(grp_del[i]);
// now read actual values and set to default if unset
// config file will be updated with unset options
cfg->SetRecordDefaults();
for(int i = 0; i < num_opts; i++) {
opt_desc &opt = opts[i];
if(opt.stropt) {
bool gotit = cfg->Read(opt.opt, opt.stropt, *opt.stropt);
opt.curstr = *opt.stropt;
} else if(opt.enumvals) {
opt.curint = *opt.intopt;
bool gotit = cfg->Read(opt.opt, &s);
const wxChar *ev = opt.enumvals;
if(gotit && s.size()) {
// wx provides no case-insensitive Find()
s.MakeLower();
for( ; (ev = wxStrstr(ev, (const wxChar *)s.c_str())); ev++) {
if(ev != opt.enumvals && ev[-1] != wxT('|'))
continue;
if(!ev[s.size()] || ev[s.size()] == wxT('|'))
break;
}
if(!ev) {
opt.curint = 0;
ev = opt.enumvals;
const wxChar *evx = wxGetTranslation(ev);
bool isx = wxStrcmp(ev, evx) != 0;
// technically, the translation for this string could incorproate
// the equals sign if necessary instead of doing it this way
wxLogWarning(_("Invalid value %s for option %s; valid values are %s%s%s"),
s.c_str(), opt.opt, ev,
isx ? wxT(" = ") : wxT(""),
isx ? evx : wxT(""));
s = wxString(ev, wxStrchr(ev, wxT('|')) - ev);
cfg->Write(opt.opt, s);
} else {
const wxChar *ev2;
for(ev2 = opt.enumvals, opt.curint = 0; ev2 != ev; opt.curint++)
ev2 = wxStrchr(ev2, wxT('|')) + 1;
}
*opt.intopt = opt.curint;
} else {
for(int i = 0; i != opt.curint; i++)
ev = wxStrchr(ev, wxT('|')) + 1;
const wxChar *ev2 = wxStrchr(ev, wxT('|'));
s = ev2 ? wxString(ev, ev2 - ev) : wxString(ev);
cfg->Write(opt.opt, s);
}
} else if(opt.intopt) {
cfg->Read(opt.opt, &opt.curint, *opt.intopt);
if(opt.curint < opt.min || opt.curint > opt.max) {
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), opt.curint, opt.opt, opt.min, opt.max);
} else
*opt.intopt = opt.curint;
} else if(opt.boolopt) {
cfg->Read(opt.opt, opt.boolopt, *opt.boolopt);
opt.curbool = *opt.boolopt;
}
}
// GB/Palette[0-2] is special
for(int i = 0; i < 3; i++) {
wxString optn;
optn.Printf(wxT("GB/Palette%d"), i);
wxString val;
const opt_desc dummy = { optn.c_str() };
opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
wxString entry;
for(int j = 0; j < 8; j++) {
// stupid wxString.Printf doesn't support printing at offset
entry.Printf(wxT("%04X,"), (int)systemGbPalette[i * 8 + j]);
val.append(entry);
}
val.resize(val.size() - 1);
cfg->Read(optn, &val, val);
opt->curstr = val;
for(int j = 0, cpos = 0; j < 8; j++) {
int start = cpos;
cpos = val.find(wxT(','), cpos);
if(cpos == wxString::npos)
cpos = val.size();
long ival;
// ignoring errors; if the value is bad, palette will be corrupt
// -- tough.
// stupid wxString.ToLong doesn't support start @ offset
entry = val.substr(start, cpos - start);
entry.ToLong(&ival, 16);
systemGbPalette[i * 8 + j] = ival;
if(cpos != val.size())
cpos++;
}
}
// joypad is special
for(int i = 0; i < 4; i++) {
for(int j = 0; j < NUM_KEYS; j++) {
wxString optname;
optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j]);
bool gotit = cfg->Read(optname, &s);
if(gotit) {
gopts.joykey_bindings[i][j] = wxJoyKeyTextCtrl::FromString(s);
if(s.size() && !gopts.joykey_bindings[i][j].size())
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), optname.c_str());
} else {
s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j]);
cfg->Write(optname, s);
}
}
}
// keyboard is special
// Keyboard does not get written with defaults
wxString kbopt(wxT("Keyboard/"));
int kboff = kbopt.size();
for(int i = 0; i < ncmds; i++) {
kbopt.resize(kboff);
kbopt.append(cmdtab[i].cmd);
if(cfg->Read(kbopt, &s) && s.size()) {
wxAcceleratorEntry_v val = wxKeyTextCtrl::FromString(s);
if(!val.size())
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), kbopt.c_str());
else {
for(int j = 0; j < val.size(); j++)
val[j].Set(val[j].GetFlags(), val[j].GetKeyCode(),
cmdtab[i].cmd_id);
gopts.accels.insert(gopts.accels.end(),
val.begin(), val.end());
}
}
}
// recent is special
// Recent does not get written with defaults
cfg->SetPath(wxT("/Recent"));
gopts.recent->Load(*cfg);
cfg->SetPath(wxT("/"));
cfg->Flush();
}
// Note: run load_opts() first to guarantee all config opts exist
void update_opts()
{
wxConfig *cfg = wxGetApp().cfg;
for(int i = 0; i < num_opts; i++) {
opt_desc &opt = opts[i];
if(opt.stropt) {
if(opt.curstr != *opt.stropt) {
opt.curstr = *opt.stropt;
cfg->Write(opt.opt, opt.curstr);
}
} else if(opt.enumvals) {
if(*opt.intopt != opt.curint) {
opt.curint = *opt.intopt;
const wxChar *ev = opt.enumvals;
for(int i = 0; i != opt.curint; i++)
ev = wxStrchr(ev, wxT('|')) + 1;
const wxChar *ev2 = wxStrchr(ev, wxT('|'));
wxString s = ev2 ? wxString(ev, ev2 - ev) : wxString(ev);
cfg->Write(opt.opt, s);
}
} else if(opt.intopt) {
if(*opt.intopt != opt.curint)
cfg->Write(opt.opt, (opt.curint = *opt.intopt));
} else if(opt.boolopt) {
if(*opt.boolopt != opt.curbool)
cfg->Write(opt.opt, (opt.curbool = *opt.boolopt));
}
}
// gbpalette requires doing the conversion to string over
// it may trigger a write even with no changes if # of digits changes
for(int i = 0; i < 3; i++) {
wxString optn;
optn.Printf(wxT("GB/Palette%d"), i);
const opt_desc dummy = { optn.c_str() };
opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
wxString val;
wxString entry;
for(int j = 0; j < 8; j++) {
// stupid wxString.Printf doesn't support printing at offset
entry.Printf(wxT("%04X,"), (int)systemGbPalette[i * 8 + j]);
val.append(entry);
}
val.resize(val.size() - 1);
if(val != opt->curstr) {
opt->curstr = val;
cfg->Write(optn, val);
}
}
// for joypad, use ToString comparisons. It may trigger changes
// even when there are none (e.g. multi-binding ordering changes)
// not worth worrying about
for(int i = 0; i < 4; i++) {
for(int j = 0; j < NUM_KEYS; j++) {
wxString s, o;
wxString optname;
optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j]);
s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j]);
cfg->Read(optname, &o);
if(o != s)
cfg->Write(optname, s);
}
}
// for keyboard, first remove any commands that aren't bound at all
if(cfg->HasGroup(wxT("/Keyboard"))) {
cfg->SetPath(wxT("/Keyboard"));
wxString s;
long entry_idx;
wxArrayString item_del;
for(bool cont = cfg->GetFirstEntry(s, entry_idx); cont;
cont = cfg->GetNextEntry(s, entry_idx)) {
const cmditem dummy = { s.c_str() };
cmditem *cmd = std::lower_bound(&cmdtab[0], &cmdtab[ncmds], dummy, cmditem_lt);
int i;
for(i = 0; i < gopts.accels.size(); i++)
if(gopts.accels[i].GetCommand() == cmd->cmd_id)
break;
if(i == gopts.accels.size())
item_del.push_back(s);
}
for(int i = 0; i < item_del.size(); i++)
cfg->DeleteEntry(item_del[i]);
}
// then, add/update the commands that are bound
// even if only ordering changed, a write will be triggered.
// nothing to worry about...
if(gopts.accels.size())
cfg->SetPath(wxT("/Keyboard"));
for(wxAcceleratorEntry_v::iterator i = gopts.accels.begin();
i < gopts.accels.end(); i++) {
int cmd_id = i->GetCommand();
int cmd;
for(cmd = 0; cmd < ncmds; cmd++)
if(cmdtab[cmd].cmd_id == cmd_id)
break;
wxAcceleratorEntry_v::iterator j;
for(j = i + 1; j < gopts.accels.end(); j++)
if(j->GetCommand() != cmd_id)
break;
wxAcceleratorEntry_v nv(i, j);
wxString nvs = wxKeyTextCtrl::ToString(nv);
if(nvs != cfg->Read(cmdtab[cmd].cmd))
cfg->Write(cmdtab[cmd].cmd, nvs);
}
cfg->SetPath(wxT("/"));
// recent items are updated separately
cfg->Flush();
}
bool opt_set(const wxChar *name, const wxChar *val)
{
const opt_desc dummy = { name };
const opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
if(!wxStrcmp(name, opt->opt)) {
if(opt->stropt)
*opt->stropt = wxString(val);
else if(opt->boolopt) {
if(!*val || val[1] || (*val != wxT('0') && *val != wxT('1')))
wxLogWarning(_("Invalid flag option %s - %s ignored"),
name, val);
else
*opt->boolopt = *val == wxT('1');
} else if(opt->enumvals) {
wxString s(val);
s.MakeLower();
const wxChar *ev;
for(ev = opt->enumvals; (ev = wxStrstr(ev, (const wxChar *)s.c_str())); ev++) {
if(ev != opt->enumvals && ev[-1] != wxT('|'))
continue;
if(!ev[s.size()] || ev[s.size()] == wxT('|'))
break;
}
if(!ev) {
const wxChar *evx = wxGetTranslation(opt->enumvals);
bool isx = wxStrcmp(opt->enumvals, evx) != 0;
// technically, the translation for this string could incorproate
// the equals sign if necessary instead of doing it this way
wxLogWarning(_("Invalid value %s for option %s; valid values are %s%s%s"),
s.c_str(), opt->opt, opt->enumvals,
isx ? wxT(" = ") : wxT(""),
isx ? evx : wxT(""));
} else {
const wxChar *ev2;
int val;
for(ev2 = opt->enumvals, val = 0; ev2 != ev; val++)
ev2 = wxStrchr(ev2, wxT('|')) + 1;
*opt->intopt = val;
}
} else if(opt->intopt) {
const wxString s(val);
long ival;
if(!s.ToLong(&ival) || ival < opt->min || ival > opt->max)
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), ival, name, opt->min, opt->max);
else
*opt->intopt = ival;
} else {
// GB/Palette[0-2] is virtual
for(int i = 0; i < 3; i++) {
wxString optn;
optn.Printf(wxT("GB/Palette%d"), i);
if(optn != name)
continue;
wxString vals(val);
for(int j = 0, cpos = 0; j < 8; j++) {
int start = cpos;
cpos = vals.find(wxT(','), cpos);
if(cpos == wxString::npos)
cpos = vals.size();
long ival;
// ignoring errors; if the value is bad, palette will be corrupt
// -- tough.
// stupid wxString.ToLong doesn't support start @ offset
wxString entry = vals.substr(start, cpos - start);
entry.ToLong(&ival, 16);
systemGbPalette[i * 8 + j] = ival;
if(cpos != vals.size())
cpos++;
}
}
}
return true;
} else {
const wxChar *slat = wxStrchr(name, wxT('/'));
if(!slat)
return false;
if(!wxStrncmp(name, wxT("Keyboard"), (int)(slat - name))) {
const cmditem dummy2 = { slat + 1 };
cmditem *cmd = std::lower_bound(&cmdtab[0], &cmdtab[ncmds], dummy2, cmditem_lt);
if(cmd == &cmdtab[ncmds] || wxStrcmp(slat + 1, cmd->cmd))
return false;
for(wxAcceleratorEntry_v::iterator i = gopts.accels.begin();
i < gopts.accels.end(); i++)
if(i->GetCommand() == cmd->cmd_id) {
wxAcceleratorEntry_v::iterator j;
for(j = i; j < gopts.accels.end(); j++)
if(j->GetCommand() != cmd->cmd_id)
break;
gopts.accels.erase(i, j);
break;
}
if(*val) {
wxAcceleratorEntry_v aval = wxKeyTextCtrl::FromString(val);
for(int i = 0; i < aval.size(); i++)
aval[i].Set(aval[i].GetFlags(), aval[i].GetKeyCode(),
cmd->cmd_id);
if(!aval.size())
wxLogWarning(_("Invalid key binding %s for %s"), val, name);
else
gopts.accels.insert(gopts.accels.end(), aval.begin(), aval.end());
}
return true;
} else if(!wxStrncmp(name, wxT("Joypad"), (int)(slat - name))) {
if(slat[1] < wxT('1') || slat[1] > wxT('4') || slat[2] != wxT('/'))
return false;
int jno = slat[1] - wxT('1');
int kno;
for(kno = 0; kno < NUM_KEYS; kno++)
if(!wxStrcmp(joynames[kno], slat + 3))
break;
if(kno == NUM_KEYS)
return false;
if(!*val)
gopts.joykey_bindings[jno][kno].clear();
else {
wxJoyKeyBinding_v b = wxJoyKeyTextCtrl::FromString(val);
if(!b.size())
wxLogWarning(_("Invalid key binding %s for %s"), val, name);
else
gopts.joykey_bindings[jno][kno] = b;
}
return true;
} else
return false;
}
}

142
src/wx/opts.h Normal file
View File

@ -0,0 +1,142 @@
#ifndef WX_OPTS_H
#define WX_OPTS_H
#define NUM_KEYS 19
extern const wxChar * const joynames[NUM_KEYS];
extern wxJoyKeyBinding defkeys[NUM_KEYS * 2]; // keyboard + joystick defaults
extern struct opts_t {
opts_t();
// while I would normally put large objects in front to reduce gaps,
// I instead organized this by opts.cpp table order
/// Display
bool bilinear;
// bool cpu_mmx;
bool no_osd_status;
int filter;
wxString filter_plugin;
int ifb;
bool fullscreen;
wxVideoMode fs_mode;
int max_scale;
int max_threads;
int render_method;
int video_scale;
int osd_speed;
bool retain_aspect;
bool osd_transparent;
bool vsync;
/// GB
// bool gbBorderAutomatic;
wxString gb_bios;
// bool gbBorderOn;
// int gbEmulatorType;
bool gbprint; // gbSerialFunction
int gb_frameskip; // systemFrameSkip
wxString gbc_bios;
bool gbc_use_bios;
// bool gbcColorOption;
// bool gbPaletteOption;
// u16 systemGbPalette[8*3];
bool print_auto_page, print_screen_cap;
wxString gb_rom_dir;
bool gb_use_bios;
/// GBA
bool agbprint; // AGBPrintEnable()
wxString gba_bios;
bool rtc; // rtcEnable // has per-game override
int flash_size; // flashSetSize // has per-game override
int gba_frameskip; // systemFrameSkip
// bool gba_joybus_enabled;
wxString joybus_host;
// bool gba_link_enabled;
// bool lanlink.speed;
wxString link_host;
int link_proto;
// bool rfu_enabled;
// int linktimeout;
wxString gba_rom_dir;
bool skip_intro;
int save_type; // cpuSaveType
bool gba_use_bios;
/// General
bool apply_patches;
bool autoload_state, autoload_cheats;
wxString battery_dir;
int cap_format;
// bool cheatsEnabled;
bool recent_freeze;
bool defocus_pause;
wxString recording_dir;
int rewind_interval;
wxString scrshot_dir;
// bool skipBios;
wxString state_dir;
// bool skipSaveGameBattery, skipSaveGameCheats;
int throttle; // updateThrottle()
/// Joypad
wxJoyKeyBinding_v joykey_bindings[4][NUM_KEYS];
int autofire_rate;
int default_stick;
/// Keyboard
wxAcceleratorEntry_v accels;
/// Sound
int audio_api;
int audio_buffers;
wxString audio_dev;
int sound_en; // soundSetEnable()
int gba_sound_filter;
// int soundInterpolation;
bool gb_declick;
int gb_echo;
// bool gb_effects_config.enabled;
bool dsound_hw_accel;
int gb_stereo;
// int gb_effects_config.surround;
int sound_qual; // soundSetSampleRate() / gbSoundSetSampleRate()
// bool synchronize;
int sound_vol; // soundSetVolume()
bool upmix; // xa2 only
/// Recent
wxFileHistory *recent;
/// wxWindows
// wxWidgets-generated options (opaque)
} gopts;
extern struct opt_desc {
const wxChar *opt, *desc;
wxString *stropt;
int *intopt;
const wxChar *enumvals;
int min, max;
bool *boolopt;
// current configured value
wxString curstr;
int curint;
#define curbool curint
} opts[];
extern const int num_opts;
extern const wxAcceleratorEntry default_accels[];
extern const int num_def_accels;
// call to load config (once)
// will write defaults for options not present and delete bad opts
// will also initialize opts[] array translations
void load_opts();
// call whenever opt vars change
// will detect changes and write config if necessary
void update_opts();
// returns true if option name correct; prints error if val invalid
bool opt_set(const wxChar *name, const wxChar *val);
#endif /* WX_OPTS_H */

2019
src/wx/panel.cpp Normal file

File diff suppressed because it is too large Load Diff

1080
src/wx/sys.cpp Normal file

File diff suppressed because it is too large Load Diff

899
src/wx/viewers.cpp Normal file
View File

@ -0,0 +1,899 @@
// these are all the viewer dialogs except for the ones with graphical areas
// they can be instantiated multiple times
#include "wxvbam.h"
#include "viewsupt.h"
#include "../gba/armdis.h"
#include <wx/vlbox.h>
#include <wx/ffile.h>
// avoid exporting classes
namespace Viewers
{
#define gethex(v, n, l) do { \
v = XRCCTRL(*this, n, wxTextCtrl); \
if(!v) \
baddialog(); \
wxTextValidator hv(wxFILTER_INCLUDE_CHAR_LIST); \
hv.SetIncludes(val_hexdigits); \
v->SetValidator(hv); \
v->SetMaxLength(l); \
} while(0)
class DisassembleViewer : public Viewer
{
public:
DisassembleViewer() : Viewer(wxT("Disassemble"))
{
gethex(goto_addr, "GotoAddress", 8);
goto_addr->SetFocus();
for(int i = 0; i < 17; i++) {
// it is unfortunately impossible to reliably assign
// number ranges to XRC IDs, so the string name has to be
// used every time
wxString n;
n.Printf(wxT("R%d"), i);
regv[i] = XRCCTRL_D(*this, n, wxControl);
if(!regv[i])
baddialog();
}
#define flagctrl(n) do { \
n = XRCCTRL(*this, #n, wxCheckBox); \
if(!n) \
baddialog(); \
} while(0)
#define regctrl(n) do { \
n##v = XRCCTRL(*this, #n, wxControl); \
if(!n##v) \
baddialog(); \
} while(0)
flagctrl(N);
flagctrl(Z);
flagctrl(C);
flagctrl(V);
flagctrl(I);
flagctrl(F);
flagctrl(T);
regctrl(Mode);
dis = XRCCTRL(*this, "Disassembly", DisList);
if(!dis)
baddialog();
// refit listing for longest line
dis->Refit(70);
Fit();
SetMinSize(GetSize());
dis->maxaddr = (u32)~0;
dismode = 0;
GotoPC();
}
void Update()
{
GotoPC();
}
void Next(wxCommandEvent &ev)
{
CPULoop(1);
GotoPC();
}
void Goto(wxCommandEvent &ev)
{
wxString as = goto_addr->GetValue();
if(!as.size())
return;
long a;
as.ToLong(&a, 16);
dis->SetSel(a);
UpdateDis();
}
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
void GotoPCEv(wxCommandEvent &ev)
{
GotoPC();
}
void GotoPC()
{
#if 0
// this is what the win32 interface used
if(armState)
dis->SetSel(armNextPC - 16);
else
dis->SetSel(armNextPC - 8);
// doesn't make sense, though. Maybe it's just trying to keep the
// sel 4 instructions below top...
#endif
dis->SetSel(armNextPC);
UpdateDis();
}
void RefreshCmd(wxCommandEvent &ev)
{
UpdateDis();
}
void UpdateDis()
{
N->SetValue(reg[16].I & 0x80000000);
Z->SetValue(reg[16].I & 0x40000000);
C->SetValue(reg[16].I & 0x20000000);
V->SetValue(reg[16].I & 0x10000000);
I->SetValue(reg[16].I & 0x00000080);
F->SetValue(reg[16].I & 0x00000040);
T->SetValue(reg[16].I & 0x00000020);
wxString s;
s.Printf(wxT("%02X"), reg[16].I & 0x1f);
Modev->SetLabel(s);
for(int i = 0; i < 17; i++) {
s.Printf(wxT("%08X"), reg[i].I);
regv[i]->SetLabel(s);
}
}
void RefillListEv(wxCommandEvent &ev)
{
// what an unsafe calling convention
// examination of disArm shows that max len is 69 chars
// (e.g. 0x081cb6db), and I assume disThumb is shorter
char buf[80];
dis->strings.clear();
dis->addrs.clear();
u32 addr = dis->topaddr;
bool arm = dismode == 1 || (armState && dismode != 2);
dis->back_size = arm ? 4 : 2;
for(int i = 0; i < dis->nlines; i++) {
dis->addrs.push_back(addr);
if(arm)
addr += disArm(addr, buf, DIS_VIEW_CODE|DIS_VIEW_ADDRESS);
else
addr += disThumb(addr, buf, DIS_VIEW_CODE|DIS_VIEW_ADDRESS);
dis->strings.push_back(wxString(buf, wxConvLibc));
}
dis->Refill();
}
DisList *dis;
wxTextCtrl *goto_addr;
wxCheckBox *N, *Z, *C, *V, *I, *F, *T;
int dismode;
wxControl *regv[17], *Modev;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(DisassembleViewer, Viewer)
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, DisassembleViewer::RefillListEv)
EVT_BUTTON(XRCID("Goto"), DisassembleViewer::Goto)
EVT_TEXT_ENTER(XRCID("GotoAddress"), DisassembleViewer::Goto)
EVT_BUTTON(XRCID("GotoPC"), DisassembleViewer::GotoPCEv)
EVT_BUTTON(XRCID("Next"), DisassembleViewer::Next)
EVT_BUTTON(XRCID("Refresh"), DisassembleViewer::RefreshCmd)
END_EVENT_TABLE()
class GBDisassembleViewer : public Viewer
{
public:
GBDisassembleViewer() : Viewer(wxT("GBDisassemble"))
{
gethex(goto_addr, "GotoAddress", 4);
goto_addr->SetFocus();
regctrl(AF);
regctrl(BC);
regctrl(DE);
regctrl(HL);
regctrl(SP);
regctrl(PC);
regctrl(LY);
regctrl(IFF);
flagctrl(Z);
flagctrl(N);
flagctrl(H);
flagctrl(C);
dis = XRCCTRL(*this, "Disassembly", DisList);
if(!dis)
baddialog();
// refit listing for longest line
dis->Refit(26);
Fit();
SetMinSize(GetSize());
dis->maxaddr = (u32)~0;
GotoPC();
}
void Update()
{
GotoPC();
}
void Next(wxCommandEvent &ev)
{
gbEmulate(1);
GotoPC();
}
void Goto(wxCommandEvent &ev)
{
wxString as = goto_addr->GetValue();
if(!as.size())
return;
long a;
as.ToLong(&a, 16);
dis->SetSel(a);
UpdateDis();
}
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
void GotoPCEv(wxCommandEvent &ev)
{
GotoPC();
}
void GotoPC()
{
dis->SetSel(PC.W);
UpdateDis();
}
void RefreshCmd(wxCommandEvent &ev)
{
UpdateDis();
}
void UpdateDis()
{
Z->SetValue(AF.B.B0 & Z_FLAG);
N->SetValue(AF.B.B0 & N_FLAG);
H->SetValue(AF.B.B0 & H_FLAG);
C->SetValue(AF.B.B0 & C_FLAG);
#define grv16(n, val) do { \
wxString s; \
s.Printf(wxT("%04X"), (int)val); \
n##v->SetLabel(s); \
} while(0)
#define rv16(n) grv16(n, n.W)
#define srv16(n) grv16(n, n)
rv16(AF);
rv16(BC);
rv16(DE);
rv16(HL);
rv16(SP);
rv16(PC);
srv16(IFF);
wxString s;
s.Printf(wxT("%02X"), register_LY);
LYv->SetLabel(s);
}
void RefillListEv(wxCommandEvent &ev)
{
// what an unsafe calling convention
// examination of gbDis shows that max len is 26 chars
// (e.g. 0xe2)
char buf[30];
u16 addr = dis->topaddr;
dis->strings.clear();
dis->addrs.clear();
dis->back_size = 1;
for(int i = 0; i < dis->nlines; i++) {
dis->addrs.push_back(addr);
addr += gbDis(buf, addr);
dis->strings.push_back(wxString(buf, wxConvLibc));
}
dis->Refill();
}
DisList *dis;
wxTextCtrl *goto_addr;
wxControl *AFv, *BCv, *DEv, *HLv, *SPv, *PCv, *LYv, *IFFv;
wxCheckBox *Z, *N, *H, *C;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(GBDisassembleViewer, Viewer)
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, GBDisassembleViewer::RefillListEv)
EVT_BUTTON(XRCID("Goto"), GBDisassembleViewer::Goto)
EVT_TEXT_ENTER(XRCID("GotoAddress"), GBDisassembleViewer::Goto)
EVT_BUTTON(XRCID("GotoPC"), GBDisassembleViewer::GotoPCEv)
EVT_BUTTON(XRCID("Next"), GBDisassembleViewer::Next)
EVT_BUTTON(XRCID("Refresh"), GBDisassembleViewer::RefreshCmd)
END_EVENT_TABLE()
}
void MainFrame::Disassemble(void)
{
switch(panel->game_type()) {
case IMAGE_GBA:
LoadXRCViewer(Disassemble);
break;
case IMAGE_GB:
LoadXRCViewer(GBDisassemble);
break;
}
}
// for CPUWriteHalfWord
// and CPURead... below
#include "../gba/GBAinline.h"
namespace Viewers {
#include "ioregs.h"
class IOViewer : public Viewer
{
public:
IOViewer() : Viewer(wxT("IOViewer"))
{
for(int i = 0; i < 16; i++) {
wxString s;
s.Printf(wxT("B%d"), i);
bit[i] = XRCCTRL_D(*this, s, wxCheckBox);
s.append(wxT("lab"));
bitlab[i] = XRCCTRL_D(*this, s, wxControl);
if(!bit[i] || !bitlab[i])
baddialog();
}
addr = XRCCTRL(*this, "Address", wxChoice);
val = XRCCTRL(*this, "Value", wxControl);
if(!addr || !val)
baddialog();
addr->Clear();
const wxChar *longline = lline;
int lwidth = 0;
for(int i = 0; i < NUM_IOREGS; i++) {
addr->Append(wxGetTranslation(ioregs[i].name));
// find longest label
// this is probably horribly expensive
// cache for entire app...
// and while at it, translate all the strings
if(!lline) {
for(int j = 0; j < 16; j++) {
if(ioregs[i].bits[j][0]) {
ioregs[i].bits[j] = wxGetTranslation(ioregs[i].bits[j]);
int w, h;
bitlab[0]->GetTextExtent(ioregs[i].bits[j], &w, &h);
if(w > lwidth) {
lwidth = w;
longline = ioregs[i].bits[j];
}
}
}
}
}
if(!lline)
lline = longline;
bitlab[0]->SetLabel(lline);
Fit();
addr->SetSelection(0);
Select(0);
}
void SelectEv(wxCommandEvent &ev)
{
Select(addr->GetSelection());
}
void Select(int sel)
{
int i;
u16 mask;
for(mask = 1, i = 0; mask; mask <<= 1, i++) {
bit[i]->Enable(mask & ioregs[sel].write);
bitlab[i]->SetLabel(ioregs[sel].bits[i]);
}
Update(sel);
}
void Update()
{
Update(addr->GetSelection());
}
void Update(int sel)
{
u16 *addr = ioregs[sel].address ? ioregs[sel].address :
(u16 *)&ioMem[ioregs[sel].offset];
u16 mask, reg = *addr;
int i;
for(mask = 1, i = 0; mask; mask <<= 1, i++)
bit[i]->SetValue(mask & reg);
wxString s;
s.Printf(wxT("%04X"), reg);
val->SetLabel(s);
}
void CheckBit(wxCommandEvent &ev)
{
for(int i = 0; i < 16; i++)
if(ev.GetEventObject() == bit[i]) {
// it'd be faster to store the value and just flip
// the bit, but it's easier this way
u16 mask, reg = 0;
int j;
for(mask = 1, j = 0; mask; mask <<= 1, j++)
if(bit[j]->GetValue())
reg |= mask;
wxString s;
s.Printf(wxT("%04X"), reg);
val->SetLabel(s);
return;
}
ev.Skip();
}
void RefreshEv(wxCommandEvent &ev)
{
Update();
}
void Apply(wxCommandEvent &ev)
{
int sel = addr->GetSelection();
u16 *addr = ioregs[sel].address ? ioregs[sel].address :
(u16 *)&ioMem[ioregs[sel].offset];
u16 mask, reg = *addr;
reg &= ~ioregs[sel].write;
int i;
for(mask = 1, i = 0; mask; mask <<= 1, i++) {
if((mask & ioregs[sel].write) && bit[i]->GetValue())
reg |= mask;
}
CPUWriteHalfWord(0x4000000+ioregs[sel].offset, reg);
Update(sel);
}
static const wxChar *lline;
wxChoice *addr;
wxControl *val;
wxCheckBox *bit[16];
wxControl *bitlab[16];
DECLARE_EVENT_TABLE()
};
const wxChar *IOViewer::lline = NULL;
BEGIN_EVENT_TABLE(IOViewer, Viewer)
EVT_BUTTON(XRCID("Refresh"), IOViewer::RefreshEv)
EVT_BUTTON(wxID_APPLY, IOViewer::Apply)
EVT_CHOICE(XRCID("Address"), IOViewer::SelectEv)
EVT_CHECKBOX(wxID_ANY, IOViewer::CheckBit)
END_EVENT_TABLE()
}
void MainFrame::IOViewer()
{
LoadXRCViewer(IO);
}
#define getlogf(n, val) do { \
wxCheckBox *cb = XRCCTRL(*this, n, wxCheckBox); \
if(!cb) \
baddialog(); \
cb->SetValidator(wxBoolIntValidator(&systemVerbose, val, val)); \
} while(0)
LogDialog::LogDialog()
{
const wxChar *dname = wxT("Logging");
if(!wxXmlResource::Get()->LoadDialog(this, wxGetApp().frame, dname))
baddialog();
SetEscapeId(wxID_OK);
getlogf("SWI", VERBOSE_SWI);
getlogf("UnalignedMemory", VERBOSE_UNALIGNED_MEMORY);
getlogf("IllWrite", VERBOSE_ILLEGAL_WRITE);
getlogf("IllRead", VERBOSE_ILLEGAL_READ);
getlogf("DMA0", VERBOSE_DMA0);
getlogf("DMA1", VERBOSE_DMA1);
getlogf("DMA2", VERBOSE_DMA2);
getlogf("DMA3", VERBOSE_DMA3);
getlogf("UndefInstruction", VERBOSE_UNDEFINED);
getlogf("AGBPrint", VERBOSE_AGBPRINT);
getlogf("SoundOut", VERBOSE_SOUNDOUTPUT);
log = XRCCTRL(*this, "Log", wxTextCtrl);
if(!log)
baddialog();
Fit();
}
void LogDialog::Update()
{
wxString l = wxGetApp().log;
log->SetValue(l);
log->ShowPosition(l.size() > 2 ? l.size() - 2 : 0);
}
void LogDialog::Save(wxCommandEvent &ev)
{
static wxString logdir = wxEmptyString, def_name = wxEmptyString;
if(def_name.empty())
def_name = wxGetApp().frame->GetPanel()->game_name() + wxT(".log");
wxString pats = _("Text files (*.txt;*.log)|*.txt;*.log|");
pats.append(wxALL_FILES);
wxFileDialog dlg(this, _("Select output file"), logdir, def_name,
pats, wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
int ret = dlg.ShowModal();
def_name = dlg.GetPath();
logdir = dlg.GetDirectory();
if(ret != wxID_OK)
return;
wxFFile f(def_name, wxT("w"));
if(f.IsOpened()) {
f.Write(wxGetApp().log);
f.Close();
}
}
void LogDialog::Clear(wxCommandEvent &ev)
{
wxGetApp().log.clear();
Update();
}
BEGIN_EVENT_TABLE(LogDialog, wxDialog)
EVT_BUTTON(wxID_SAVE, LogDialog::Save)
EVT_BUTTON(XRCID("Clear"), LogDialog::Clear)
EVT_CHECKBOX(wxID_ANY, Viewers::Viewer::ActiveCtrl)
END_EVENT_TABLE()
// These are what mfc interface used. Maybe it would be safer
// to avoid the Quick() routines in favor of the long ones...
#define CPUWriteByteQuick(addr, b) \
::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask] = (b)
#define CPUWriteHalfWordQuick(addr, b) \
WRITE16LE((u16 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask], b)
#define CPUWriteMemoryQuick(addr, b) \
WRITE32LE((u32 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask], b)
#define GBWriteByteQuick(addr, b) \
*((u8 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff]) = (b)
#define GBWriteHalfWordQuick(addr, b) \
WRITE16LE((u16 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff], b)
#define GBWriteMemoryQuick(addr, b) \
WRITE32LE((u32 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff], b)
#define GBReadMemoryQuick(addr) \
READ32LE((u32 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff])
namespace Viewers {
static wxString memsave_dir = wxEmptyString;
class MemViewerBase : public Viewer
{
public:
MemViewerBase(u32 max) : Viewer(wxT("MemViewer"))
{
if(!(mv = XRCCTRL(*this, "MemView", MemView)))
baddialog();
if(!(bs = XRCCTRL(*this, "BlockStart", wxChoice)))
baddialog();
bs->Append(wxT(""));
bs->SetFocus();
mv->fmt = max > 0xffff ? 2 : 1;
getradio(,"Fmt8", mv->fmt, 0);
getradio(,"Fmt16", mv->fmt, 1);
getradio(,"Fmt32", mv->fmt, 2);
mv->maxaddr = max;
addrlen = max > 0xffff ? 8 : 4;
gethex(goto_addr, "GotoAddress", addrlen);
wxControl *addr = XRCCTRL(*this, "CurAddress", wxControl);
if(!addr)
baddialog();
addr->SetLabel(addrlen == 8 ? wxT("0xWWWWWWWW") : wxT("0xWWWW"));
// refit listing for longest line
mv->Refit();
Fit();
// don't let address display resize when window size changes
addr->SetMinSize(addr->GetSize());
mv->addrlab = addr;
SetMinSize(GetSize());
Goto(0);
// initialize load/save support dialog already
{
const wxChar *dname = wxT("MemSelRegion");
selregion = wxXmlResource::Get()->LoadDialog(this, dname);
if(!selregion)
baddialog();
#define this selregion // for gethex()
gethex(selreg_addr, "Address", addrlen);
gethex(selreg_len, "Size", addrlen);
#undef this
selreg_lenlab = XRCCTRL(*selregion, "SizeLab", wxControl);
selregion->Fit();
}
}
void BlockStart(wxCommandEvent &ev)
{
unsigned long l;
bs->GetStringSelection().ToULong(&l, 0);
Goto(l);
}
void GotoEv(wxCommandEvent &ev)
{
unsigned long l;
wxString v = goto_addr->GetValue();
if(v.empty())
return;
v.ToULong(&l, 16);
Goto(l);
}
void Goto(u32 addr)
{
mv->Show(addr, true);
}
void RefreshCmd(wxCommandEvent &ev)
{
Update();
}
virtual void Update() {}
void Load(wxCommandEvent &ev)
{
if(memsave_fn.empty())
memsave_fn = wxGetApp().frame->GetPanel()->game_name() + wxT(".dmp");
wxString pats = _("Memory dumps (*.dmp;*.bin)|*.dmp;*.bin|");
pats.append(wxALL_FILES);
wxFileDialog dlg(this, _("Select memory dump file"), memsave_dir, memsave_fn,
pats, wxFD_OPEN|wxFD_FILE_MUST_EXIST);
int ret = dlg.ShowModal();
memsave_fn = dlg.GetPath();
memsave_dir = dlg.GetDirectory();
if(ret != wxID_OK)
return;
wxFileName fn(memsave_fn);
if(!fn.IsFileReadable()) {
wxLogError(wxT("Can't open file %s"), memsave_fn.c_str());
return;
}
unsigned long addr, len = fn.GetSize().ToULong();
if(!len)
return;
wxString s;
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), mv->GetAddr());
selreg_addr->SetValue(s);
selreg_len->Disable();
selreg_lenlab->Disable();
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), len);
selreg_len->SetValue(s);
if(selregion->ShowModal() != wxID_OK)
return;
selreg_addr->GetValue().ToULong(&addr, 16);
MemLoad(memsave_fn, addr, len);
}
virtual void MemLoad(wxString &name, u32 addr, u32 len) = 0;
void Save(wxCommandEvent &ev)
{
wxString s;
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), mv->GetAddr());
selreg_addr->SetValue(s);
selreg_len->Enable();
selreg_lenlab->Enable();
selreg_len->SetValue(wxEmptyString);
if(selregion->ShowModal() != wxID_OK)
return;
unsigned long addr, len;
selreg_addr->GetValue().ToULong(&addr, 16);
selreg_len->GetValue().ToULong(&len, 16);
if(memsave_fn.empty())
memsave_fn = wxGetApp().frame->GetPanel()->game_name() + wxT(".dmp");
wxString pats = _("Memory dumps (*.dmp;*.bin)|*.dmp;*.bin|");
pats.append(wxALL_FILES);
wxFileDialog dlg(this, _("Select output file"), memsave_dir, memsave_fn,
pats, wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
int ret = dlg.ShowModal();
memsave_dir = dlg.GetDirectory();
memsave_fn = dlg.GetPath();
if(ret != wxID_OK)
return;
MemSave(memsave_fn, addr, len);
}
virtual void MemSave(wxString &name, u32 addr, u32 len) = 0;
protected:
int addrlen;
wxChoice *bs;
wxTextCtrl *goto_addr;
MemView *mv;
wxDialog *selregion;
wxTextCtrl *selreg_addr, *selreg_len;
wxControl *selreg_lenlab;
wxString memsave_fn;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MemViewerBase, Viewer)
EVT_CHOICE(XRCID("BlockStart"), MemViewerBase::BlockStart)
EVT_BUTTON(XRCID("Goto"), MemViewerBase::GotoEv)
EVT_TEXT_ENTER(XRCID("GotoAddress"), MemViewerBase::GotoEv)
EVT_BUTTON(XRCID("Refresh"), MemViewerBase::RefreshCmd)
EVT_BUTTON(wxID_SAVE, MemViewerBase::Save)
EVT_BUTTON(wxID_OPEN, MemViewerBase::Load)
END_EVENT_TABLE()
class MemViewer : public MemViewerBase
{
public:
MemViewer() : MemViewerBase(~0)
{
bs->Append(_("0x00000000 - BIOS"));
bs->Append(_("0x02000000 - WRAM"));
bs->Append(_("0x03000000 - IRAM"));
bs->Append(_("0x04000000 - I/O"));
bs->Append(_("0x05000000 - PALETTE"));
bs->Append(_("0x06000000 - VRAM"));
bs->Append(_("0x07000000 - OAM"));
bs->Append(_("0x08000000 - ROM"));
bs->SetSelection(1);
Fit();
}
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
void RefillListEv(wxCommandEvent &ev)
{
Update();
}
void Update()
{
u32 addr = mv->topaddr;
mv->words.resize(mv->nlines * 4);
for(int i = 0; i < mv->nlines; i++) {
if(i && !addr)
break;
for(int j = 0; j < 4; j++, addr += 4)
mv->words[i * 4 + j] = CPUReadMemoryQuick(addr);
}
mv->Refill();
}
void WriteVal(wxCommandEvent &ev)
{
switch(mv->fmt) {
case 0:
CPUWriteByteQuick(mv->writeaddr, mv->writeval);
break;
case 1:
CPUWriteHalfWordQuick(mv->writeaddr, mv->writeval);
break;
case 2:
CPUWriteMemoryQuick(mv->writeaddr, mv->writeval);
break;
}
}
void MemLoad(wxString &name, u32 addr, u32 len)
{
wxFFile f(name, wxT("rb"));
if(!f.IsOpened())
return;
// this does the equivalent of the CPUWriteMemoryQuick()
while(len > 0) {
memoryMap m = map[addr >> 24];
u32 off = addr & m.mask;
u32 wlen = (off + len) > m.mask ? m.mask + 1 - off : len;
wlen = f.Read(m.address + off, wlen);
if(wlen < 0)
return; // FIXME: give error
len -= wlen;
addr += wlen;
}
}
void MemSave(wxString &name, u32 addr, u32 len)
{
wxFFile f(name, wxT("wb"));
if(!f.IsOpened())
return;
// this does the equivalent of the CPUReadMemoryQuick()
while(len > 0) {
memoryMap m = map[addr >> 24];
u32 off = addr & m.mask;
u32 wlen = (off + len) > m.mask ? m.mask + 1 - off : len;
wlen = f.Write(m.address + off, wlen);
if(wlen < 0)
return; // FIXME: give error
len -= wlen;
addr += wlen;
}
}
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MemViewer, MemViewerBase)
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, MemViewer::RefillListEv)
EVT_COMMAND(wxID_ANY, EVT_WRITEVAL, MemViewer::WriteVal)
END_EVENT_TABLE()
class GBMemViewer : public MemViewerBase
{
public:
GBMemViewer() : MemViewerBase((u16)~0)
{
bs->Append(_("0x0000 - ROM"));
bs->Append(_("0x4000 - ROM"));
bs->Append(_("0x8000 - VRAM"));
bs->Append(_("0xA000 - SRAM"));
bs->Append(_("0xC000 - RAM"));
bs->Append(_("0xD000 - WRAM"));
bs->Append(_("0xFF00 - I/O"));
bs->Append(_("0xFF80 - RAM"));
bs->SetSelection(1);
Fit();
}
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
void RefillListEv(wxCommandEvent &ev)
{
Update();
}
void Update()
{
u32 addr = mv->topaddr;
mv->words.resize(mv->nlines * 4);
for(int i = 0; i < mv->nlines; i++) {
if(i && !(u16)addr)
break;
for(int j = 0; j < 4; j++, addr += 4)
mv->words[i * 4 + j] = GBReadMemoryQuick(addr);
}
mv->Refill();
}
void WriteVal(wxCommandEvent &ev)
{
switch(mv->fmt) {
case 0:
GBWriteByteQuick(mv->writeaddr, mv->writeval);
break;
case 1:
GBWriteHalfWordQuick(mv->writeaddr, mv->writeval);
break;
case 2:
GBWriteMemoryQuick(mv->writeaddr, mv->writeval);
break;
}
}
void MemLoad(wxString &name, u32 addr, u32 len)
{
wxFFile f(name, wxT("rb"));
if(!f.IsOpened())
return;
// this does the equivalent of the GBWriteMemoryQuick()
while(len > 0) {
u8 *maddr = gbMemoryMap[addr >> 12];
u32 off = addr & 0xfff;
u32 wlen = (off + len) > 0xfff ? 0x1000 - off : len;
wlen = f.Read(maddr + off, wlen);
if(wlen < 0)
return; // FIXME: give error
len -= wlen;
addr += wlen;
}
}
void MemSave(wxString &name, u32 addr, u32 len)
{
wxFFile f(name, wxT("wb"));
if(!f.IsOpened())
return;
// this does the equivalent of the GBReadMemoryQuick()
while(len > 0) {
u8 *maddr = gbMemoryMap[addr >> 12];
u32 off = addr & 0xfff;
u32 wlen = (off + len) > 0xfff ? 0x1000 - off : len;
wlen = f.Write(maddr + off, wlen);
if(wlen < 0)
return; // FIXME: give error
len -= wlen;
addr += wlen;
}
}
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(GBMemViewer, MemViewerBase)
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, GBMemViewer::RefillListEv)
EVT_COMMAND(wxID_ANY, EVT_WRITEVAL, GBMemViewer::WriteVal)
END_EVENT_TABLE()
}
void MainFrame::MemViewer()
{
switch(panel->game_type()) {
case IMAGE_GBA:
LoadXRCViewer(Mem);
break;
case IMAGE_GB:
LoadXRCViewer(GBMem);
break;
}
}

1061
src/wx/viewsupt.cpp Normal file

File diff suppressed because it is too large Load Diff

363
src/wx/viewsupt.h Normal file
View File

@ -0,0 +1,363 @@
#ifndef WX_VIEWSUPT_H
#define WX_VIEWSUPT_H
#include <wx/spinctrl.h>
#include <wx/caret.h>
// avoid exporting too much stuff
namespace Viewers {
// common to all viewers:
// - track in MainFrame::popups
// - wxID_CLOSE button closes window
// - AutoUpdate checkbox toggles calling Update() every screen refresh
class Viewer : public wxDialog
{
public:
void CloseDlg(wxCloseEvent &ev);
Viewer(const wxChar *name);
virtual ~Viewer() {}
virtual void Update() = 0;
bool auto_update;
// A lot of viewers have GUI elements to set parameters. Almost all
// of them just read back the value and update the display. This
// event handler does that with validators.
void ActiveCtrl(wxCommandEvent &ev);
void ActiveCtrlScr(wxScrollEvent &ev) { ActiveCtrl(ev); }
void ActiveCtrlSpin(wxSpinEvent &ev) { ActiveCtrl(ev); }
protected:
const wxChar *dname;
void SetAutoUpdate(wxCommandEvent &ev) { auto_update = ev.IsChecked(); }
DECLARE_EVENT_TABLE()
};
// on errors, abort program
#define baddialog() do { \
wxLogError(_("Unable to load dialog %s from resources"), dname); \
wxGetApp().frame->Close(true); \
return; \
} while(0)
// widgets to use with auto validator
#define getvfld(sv, n, t, v) do { \
t *_w = sv XRCCTRL(*this, n, t); \
if(!_w) \
baddialog(); \
_w->SetValidator(v); \
} while(0)
#define getradio(sv, n, var, val) \
getvfld(sv, n, wxRadioButton, wxBoolIntValidator(&var, val))
#define getslider(sv, n, var) \
getvfld(sv, n, wxSlider, wxGenericValidator(&var))
#define getspin(sv, n, var) \
getvfld(sv, n, wxSpinCtrl, wxGenericValidator(&var))
#define LoadXRCViewer(t) do { \
wxDialog *d = new Viewers::t##Viewer; \
if(d) \
d->Show(); \
} while(0)
// a list box with no horizontal scrollbar and a funky vertical scrollbar:
// range = 1 - 500
// but/pagesz = # of lines shown/# of lines shown - 1
// 1-100 = normal
// 101-200 = 10x semi-stationary
// 201-300 = stationary @ center
// 301-400 = 10x semi-stationary
// 401-500 = normal
// note that since listboxes' size is impossible to control correctly (at
// least with wxGTK), this uses a textctrl to display the items.
class DisList : public wxPanel
{
public:
DisList();
// called after init to create subcontrols and size panel
void Refit(int cols);
void MoveSB();
void MoveView(wxScrollEvent &ev);
private:
void RefillNeeded();
public:
// called by parent's refill handler or any other time strings have
// changed
void Refill();
private:
void Resize(wxSizeEvent &ev);
void SetSel();
public:
// make addr visible and then select it
void SetSel(u32 addr);
void UnSel() { issel = false; }
// currently visible lines
int nlines;
// at least nlines strings to display
wxArrayString strings;
// and their starting addrs (mostly for scrollbar)
wxArrayInt addrs;
// how far back to scroll for single line
int back_size;
// address of top line
u32 topaddr;
// max address for scrollbar
u32 maxaddr;
protected:
// assigned to textctrl to avoid mouse input
void MouseEvent(wxMouseEvent &ev) {}
// the subwidgets
wxTextCtrl tc;
wxScrollBar sb;
// cached computed line tc size
int lineheight, extraheight;
// need to know if tc/sb have been Create()d yet
bool didinit;
// selection info
u32 seladdr;
bool issel;
DECLARE_DYNAMIC_CLASS() // for xrc
DECLARE_EVENT_TABLE()
};
BEGIN_DECLARE_EVENT_TYPES()
// event generated when fewer lines available than needed
DECLARE_LOCAL_EVENT_TYPE(EVT_REFILL_NEEDED, 0)
END_DECLARE_EVENT_TYPES()
// a hex editor with a funky scrollbar like above
// since it's impossible to exercise this much control over a text
// control, it uses a panel into which text is drawn.
// Maybe some day the above will be changed to do that as well, since
// it allows better mouse control as well.
class MemView : public wxPanel
{
public:
MemView();
// called after init to create subcontrols and size panel
void Refit();
void MoveSB();
void MoveView(wxScrollEvent &ev);
private:
void RefillNeeded();
public:
// called by parent's refill handler or any other time strings have
// changed
void Refill();
private:
void Refill(wxDC &dc);
void RepaintEv(wxPaintEvent &ev);
void Repaint();
void Resize(wxSizeEvent &ev);
public:
// make addr visible
void Show(u32 addr, bool force_update = false);
// current selection, or topaddr if none
u32 GetAddr();
// currently visible lines
int nlines;
// at least nlines * 4 words to display
wxArrayInt words;
// address of top line
u32 topaddr;
// max address for scrollbar
u32 maxaddr;
// bytes per word == (1 << fmt)
int fmt;
// after write, these contain write addr and val
u32 writeaddr, writeval;
// when selection is made, this widget is updated w/ addr
wxControl *addrlab;
protected:
// easier than checking maxaddr
int addrlen;
void MouseEvent(wxMouseEvent &ev);
void KeyEvent(wxKeyEvent &ev);
// the subwidgets
wxPanel disp;
wxScrollBar sb;
wxCaret *caret;
// cached text size
int charheight, charwidth;
// need to know if tc/sb have been Create()d yet
bool didinit;
// selection info
int selnib, seladdr;
bool isasc;
void ShowCaret();
DECLARE_DYNAMIC_CLASS() // for xrc
DECLARE_EVENT_TABLE()
};
BEGIN_DECLARE_EVENT_TYPES()
// event generated when write occurs
// check writeaddr/writeval/fmt
DECLARE_LOCAL_EVENT_TYPE(EVT_WRITEVAL, 0)
END_DECLARE_EVENT_TYPES()
// Display a color in a square, with the RGB value to its right.
// this is too hard to integrate into xrc (impossible to initialize
// correctly) so no accomodations are made for this
class ColorView : public wxControl
{
public:
ColorView(wxWindow *parent, wxWindowID id);
void SetRGB(int r, int g, int b);
void GetRGB(int &_r, int &_g, int &_b) { _r = r; _g = g; _b = b; }
protected:
int r, g, b;
wxPanel *cp;
wxStaticText *rt, *gt, *bt;
};
#define unkctrl(n, v) do { \
if(!wxXmlResource::Get()->AttachUnknownControl(wxT(n), v, this)) \
baddialog(); \
} while(0)
#define colorctrl(v, n) do { \
v = new ColorView(this, XRCID(n)); \
if(!v) \
baddialog(); \
unkctrl(n, v); \
} while(0)
// Display a small bitmap in jumbopixel style. If a pixel is selected, it
// is highlighted with a border. For wxvbam, no event is generated.
// Instead, a ColorView can be assigned to it, and on selection, that
// widget will be updated to the selected color.
// The whole class can also be derived to add more functionality to the
// button click.
// It must be intialized in 3 phases: 2-phase xrc-style (new + Create()),
// and then InitBMP()
class PixView : public wxPanel
{
public:
PixView() : wxPanel(), bm(0) {}
bool InitBMP(int w = 8, int h = 8, ColorView *cv = NULL);
// stride is in pixels
// format is rgb24 (aka wxImage format)
// x/y is added to data and returned coords
// if data == NULL, bitmap will be reset to default (all-black)
virtual void SetData(const unsigned char *data, int stride, int x = 0,
int y = 0);
// desel if out of displayed range
void SetSel(int x, int y, bool dsel_cview_update = true);
// -1, -1 = no sel
void GetSel(int &x, int &y) {
x = selx < 0 ? -1 : ox + selx;
y = sely < 0 ? -1 : oy + sely;
}
ColorView *cview;
protected:
wxImage im;
wxBitmap *bm;
void Redraw(wxPaintEvent &ev);
virtual void SelPoint(wxMouseEvent &ev);
int ox, oy, selx, sely;
DECLARE_EVENT_TABLE()
DECLARE_DYNAMIC_CLASS()
};
#define pixview(v, n, w, h, cv) do { \
v = XRCCTRL(*this, n, PixView); \
if(!v) \
baddialog(); \
v->InitBMP(w, h, cv); \
} while(0)
// a graphics viewer panel; expected to be inside of a wxScrollWindow
class GfxPanel : public wxPanel
{
public:
GfxPanel() : wxPanel(), bm(0), selx(-1), sely(-1) {}
int bmw, bmh;
wxBitmap *bm;
wxImage *im;
PixView *pv;
protected:
void DrawBitmap(wxPaintEvent &ev);
void Click(wxMouseEvent &ev);
void MouseMove(wxMouseEvent &ev);
void DoSel(wxMouseEvent &ev, bool force = false);
private:
int selx, sely;
DECLARE_DYNAMIC_CLASS()
DECLARE_EVENT_TABLE()
};
BEGIN_DECLARE_EVENT_TYPES()
// event generated on mouse click
// generates wxMouseEvent with coords adjusted to original bitmap
// size regardless of scaling
DECLARE_LOCAL_EVENT_TYPE(EVT_COMMAND_GFX_CLICK, 0)
END_DECLARE_EVENT_TYPES()
#define EVT_GFX_CLICK(id, fun) \
DECLARE_EVENT_TABLE_ENTRY(EVT_COMMAND_GFX_CLICK, id, wxID_ANY, wxMouseEventHandler(fun), NULL),
// like Viewer, common stuff to all gfx viewers
// this is what actually manages the GfxPanel
class GfxViewer : public Viewer
{
public:
GfxViewer(const wxChar *dname, int maxw, int maxh);
void ChangeBMP();
void BMPSize(int w, int h);
protected:
void StretchTog(wxCommandEvent &ev);
void RefreshEv(wxCommandEvent &ev);
void SaveBMP(wxCommandEvent &ev);
wxImage image;
GfxPanel *gv;
private:
static wxString bmp_save_dir;
wxScrolledWindow *gvs;
wxCheckBox *str;
DECLARE_EVENT_TABLE()
};
// if the jumbopixel view is all there is, maybe send a GFX_CLICK..
class PixViewEvt : public PixView
{
// generates a GFX_CLICK if a point is selected
void SetData(const unsigned char *data, int stride, int x = 0,
int y = 0);
protected:
// always generates a GFX_CLICK
void SelPoint(wxMouseEvent &ev);
void click();
DECLARE_DYNAMIC_CLASS()
};
// a display-only checkbox which does not look like it's disabled
class DispCheckBox : public wxCheckBox
{
public:
bool AcceptsFocus() { return false; }
void MouseEvent(wxMouseEvent &ev) {}
DECLARE_EVENT_TABLE()
DECLARE_DYNAMIC_CLASS()
};
// standard widgets in graphical viewers
}
#endif /* WX_VIEWSUPT_H */

View File

@ -0,0 +1,488 @@
/////////////////////////////////////////////////////////////////////////////
// Name: checkedlistctrl.cpp
// Purpose: wxCheckedListCtrl
// Author: Uknown ? (found at http://wiki.wxwidgets.org/wiki.pl?WxListCtrl)
// Modified by: Francesco Montorsi
// Created: 2005/06/29
// RCS-ID: $Id$
// Copyright: (c) 2005 Francesco Montorsi
// Licence: wxWidgets licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// includes
#include "wx/checkedlistctrl.h"
#include <wx/icon.h>
#include <wx/settings.h>
#if wxUSE_CHECKEDLISTCTRL
#if !CLC_VBAM_USAGE || !CLC_USE_SYSICONS
// resources
#include "wx/checked.xpm"
#include "wx/unchecked.xpm"
#include "wx/checked_dis.xpm"
#include "wx/unchecked_dis.xpm"
#else
#include <wx/dcbuffer.h>
#include <wx/renderer.h>
#include <wx/checkbox.h>
#endif
#if CLC_VBAM_USAGE
IMPLEMENT_DYNAMIC_CLASS(wxCheckedListCtrl, wxListCtrl)
#else
IMPLEMENT_CLASS(wxCheckedListCtrl, wxListCtrl)
#endif
BEGIN_EVENT_TABLE(wxCheckedListCtrl, wxListCtrl)
EVT_LEFT_DOWN(wxCheckedListCtrl::OnMouseEvent)
END_EVENT_TABLE()
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_CHECKED);
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
// ------------------
// wxCHECKEDLISTCTRL
// ------------------
bool wxCheckedListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pt,
const wxSize& sz, long style, const wxValidator& validator, const wxString& name)
{
if (!wxListCtrl::Create(parent, id, pt, sz, style, validator, name))
return FALSE;
#if CLC_VBAM_USAGE
// support xrc by making init separate
return Init();
}
bool wxCheckedListCtrl::Init()
{
#if CLC_USE_SYSICONS
// use native size images instead of 16x16
wxCheckBox *cb = new wxCheckBox(GetParent(), wxID_ANY, wxEmptyString);
wxSize cbsz = cb->GetBestSize();
delete cb;
m_imageList.Create(cbsz.GetWidth(), cbsz.GetHeight(), TRUE);
#else
m_imageList.Create(16, 16, TRUE);
#endif
#endif
SetImageList(&m_imageList, wxIMAGE_LIST_SMALL);
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
// pasted from wxWiki
// but with native size instead of 16x16
// constructor only takes wxSize in 2.9+, apparently
wxBitmap unchecked_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
checked_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
unchecked_disabled_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
checked_disabled_bmp(cbsz.GetWidth(), cbsz.GetHeight());
// Bitmaps must not be selected by a DC for addition to the image list but I don't see
// a way of diselecting them in wxMemoryDC so let's just use a code block to end the scope
{
wxMemoryDC renderer_dc;
// Unchecked
renderer_dc.SelectObject(unchecked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), 0);
// Checked
renderer_dc.SelectObject(checked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), wxCONTROL_CHECKED);
// Unchecked and Disabled
renderer_dc.SelectObject(unchecked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), 0 | wxCONTROL_DISABLED);
// Checked and Disabled
renderer_dc.SelectObject(checked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), wxCONTROL_CHECKED | wxCONTROL_DISABLED);
}
// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
m_imageList.Add(unchecked_bmp);
m_imageList.Add(checked_bmp);
m_imageList.Add(unchecked_disabled_bmp);
m_imageList.Add(checked_disabled_bmp);
#else
// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
m_imageList.Add(wxIcon(unchecked_xpm));
m_imageList.Add(wxIcon(checked_xpm));
m_imageList.Add(wxIcon(unchecked_dis_xpm));
m_imageList.Add(wxIcon(checked_dis_xpm));
#endif
return TRUE;
}
/* static */
int wxCheckedListCtrl::GetItemImageFromAdditionalState(int addstate)
{
bool checked = (addstate & wxLIST_STATE_CHECKED) != 0;
bool enabled = (addstate & wxLIST_STATE_ENABLED) != 0;
if (checked && enabled)
return wxCLC_CHECKED_IMGIDX;
else if (checked && !enabled)
return wxCLC_DISABLED_CHECKED_IMGIDX;
else if (!checked && enabled)
return wxCLC_UNCHECKED_IMGIDX;
wxASSERT(!checked && !enabled); // this is the last possibility
return wxCLC_DISABLED_UNCHECKED_IMGIDX;
}
wxColour wxCheckedListCtrl::GetBgColourFromAdditionalState(int additionalstate)
{
if ((additionalstate & wxLIST_STATE_ENABLED) &&
this->IsEnabled())
return *wxWHITE;
#ifdef __WXMSW__
return wxColour(212, 208, 200);
#else
return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
#endif
}
/* static */
int wxCheckedListCtrl::GetAndRemoveAdditionalState(long *state, int statemask)
{
int additionalstate = 0;
if (!state) return -1;
// extract the bits we are interested in
bool checked = (*state & wxLIST_STATE_CHECKED) != 0;
bool enabled = (*state & wxLIST_STATE_ENABLED) != 0;
// and set them in a different variable if they are included in the statemask
if (checked && (statemask & wxLIST_STATE_CHECKED)) additionalstate |= wxLIST_STATE_CHECKED;
if (enabled && (statemask & wxLIST_STATE_ENABLED)) additionalstate |= wxLIST_STATE_ENABLED;
// remove them from the original state var...
*state &= ~wxLIST_STATE_CHECKED;
*state &= ~wxLIST_STATE_ENABLED;
return additionalstate;
}
bool wxCheckedListCtrl::GetItem(wxListItem& info) const
{
// wx internal wxListCtrl::GetItem remove from the state mask the
// wxLIST_STATE_CHECKED & wxLIST_STATE_ENABLED bits since they
// are not part of wx standard flags... so we need to check those
// flags against the original wxListItem's statemask...
wxListItem original(info);
#ifdef __WXDEBUG__
// we always want to retrieve also the image state for checking purposes...
info.m_mask |= wxLIST_MASK_IMAGE;
#endif
if (!wxListCtrl::GetItem(info))
return FALSE;
// these are our additional supported states: read them from m_stateList
bool checked = (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED) != 0;
bool enabled = (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED) != 0;
// now intercept state requests about enable or check mode
if ((original.m_mask & wxLIST_MASK_STATE) &&
(original.m_stateMask & wxLIST_STATE_CHECKED)) {
info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
info.m_stateMask |= wxLIST_STATE_CHECKED;
info.m_mask |= wxLIST_MASK_STATE; // contains valid info !
}
if ((original.m_mask & wxLIST_MASK_STATE) &&
(original.m_stateMask & wxLIST_STATE_ENABLED)) {
info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
info.m_stateMask |= wxLIST_STATE_ENABLED;
info.m_mask |= wxLIST_MASK_STATE; // contains valid info !
}
// check that state & image are synch
#if CLC_VBAM_USAGE
if(info.GetColumn())
return TRUE;
#endif
#ifdef __WXDEBUG__
wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
wxT("Something wrong ! See InsertItem()"));
// read info by image index
bool imagecheck = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
(info.m_image == wxCLC_DISABLED_CHECKED_IMGIDX);
bool imageenabled = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
(info.m_image == wxCLC_UNCHECKED_IMGIDX);
wxASSERT_MSG((checked && imagecheck) || (!checked && !imagecheck),
wxT("This is item has checked state but it's shown as unchecked (or viceversa)"));
wxASSERT_MSG((enabled && imageenabled) || (!enabled && !imageenabled),
wxT("This is item has enabled state but it's shown as disabled (or viceversa)"));
#endif
return TRUE;
}
bool wxCheckedListCtrl::SetItem(wxListItem& info)
{
#if CLC_VBAM_USAGE
// only col 0 gets a checkbox
if(info.GetColumn())
return wxListCtrl::SetItem(info);
#endif
// remove the checked & enabled states from the state flag:
// we'll store them in our separate array
int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
// set image index
// we will ignore the info.m_image field since we need
// to overwrite it...
if (info.m_mask & wxLIST_MASK_STATE) {
// if some state is not included in the state mask, then get the state info
// from our internal state array
if (!(info.m_stateMask & wxLIST_STATE_ENABLED))
additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
if (!(info.m_stateMask & wxLIST_STATE_CHECKED))
additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
// state is valid: use it to determine the image to set...
info.m_mask |= wxLIST_MASK_IMAGE;
info.m_image = GetItemImageFromAdditionalState(additionalstate);
// since when changing the background color, also the foreground color
// and the font of the item are changed, we try to respect the user
// choices of such attributes
info.SetTextColour(this->GetItemTextColour(info.GetId()));
#if wxCHECK_VERSION(2, 6, 2)
// before wx 2.6.2 the wxListCtrl::SetItemFont function is missing
info.SetFont(this->GetItemFont(info.GetId()));
#endif
// change the background color to respect the enabled/disabled status...
info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
m_stateList[info.m_itemId] = additionalstate;
} else {
// state is invalid; don't change image
info.m_mask &= ~wxLIST_MASK_IMAGE;
}
// save the changes
return wxListCtrl::SetItem(info);
}
long wxCheckedListCtrl::InsertItem(wxListItem &info)
{
int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
if (!(info.m_mask & wxLIST_MASK_STATE) ||
!(info.m_stateMask & wxLIST_STATE_ENABLED)) {
// if not specified, the default additional state is ENABLED
additionalstate = wxLIST_STATE_ENABLED;
}
// we always want to insert items with images...
info.m_mask |= wxLIST_MASK_IMAGE;
info.m_image = GetItemImageFromAdditionalState(additionalstate);
info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
int itemcount = GetItemCount();
wxASSERT_MSG(info.m_itemId <= itemcount, wxT("Invalid index !"));
wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
wxT("Something wrong !"));
if (info.m_itemId == itemcount) {
// we are adding a new item at the end of the list
m_stateList.Add(additionalstate);
} else {
// we must shift all following items
m_stateList.Add(m_stateList[itemcount - 1]);
for (int i=itemcount - 1; i > info.m_itemId; i--)
m_stateList[i] = m_stateList[i-1];
m_stateList[info.m_itemId] = additionalstate;
}
return wxListCtrl::InsertItem(info);
}
bool wxCheckedListCtrl::SetItemState(long item, long state, long stateMask)
{
wxListItem li;
li.SetId(item);
li.SetMask(wxLIST_MASK_STATE);
li.SetState(state);
li.SetStateMask(stateMask);
// so we are sure to use wxCheckedListCtrl::SetItem
// (and not wxListCtrl::SetItem)
return SetItem(li);
}
int wxCheckedListCtrl::GetItemState(long item, long stateMask) const
{
wxListItem li;
li.SetId(item);
li.SetMask(wxLIST_MASK_STATE);
li.SetStateMask(stateMask);
// so we are sure to use wxCheckedListCtrl::GetItem
// (and not wxListCtrl::GetItem)
if (!GetItem(li))
return -1;
return li.GetState();
}
long wxCheckedListCtrl::SetItem(long index, int col, const wxString& label, int WXUNUSED(imageId))
{
wxListItem li;
li.SetId(index);
li.SetColumn(col);
li.SetText(label);
li.SetMask(wxLIST_MASK_TEXT);
// so we are sure to use wxCheckedListCtrl::SetItem
// (and not wxListCtrl::SetItem)
return SetItem(li);
}
long wxCheckedListCtrl::InsertItem( long index, const wxString& label, int WXUNUSED(imageIndex) )
{
wxListItem info;
info.m_text = label;
info.m_mask = wxLIST_MASK_TEXT;
info.m_itemId = index;
return InsertItem(info);
}
void wxCheckedListCtrl::Check(long item, bool checked)
{
// NB: the "statemask" is not the "mask" of a list item;
// in the "mask" you use the wxLIST_MASK_XXXX defines;
// in the "statemask" you use the wxLIST_STATE_XXX defines
// to set a specific bit of the wxListInfo::m_state var
if (checked)
// the 2nd parameter says: activate the STATE bit relative to CHECK feature
// the 3rd parameter says: set only *that* bit
SetItemState(item, wxLIST_STATE_CHECKED, wxLIST_STATE_CHECKED);
else
SetItemState(item, 0, wxLIST_STATE_CHECKED);
}
void wxCheckedListCtrl::Enable(long item, bool enable)
{
if (enable)
// the 2nd parameter says: activate the STATE bit relative to ENABLE feature
// the 3rd parameter says: set only *that* bit
SetItemState(item, wxLIST_STATE_ENABLED, wxLIST_STATE_ENABLED);
else
SetItemState(item, 0, wxLIST_STATE_ENABLED);
}
void wxCheckedListCtrl::EnableAll(bool enable)
{
for (int i=0; i < GetItemCount(); i++)
Enable(i, enable);
}
void wxCheckedListCtrl::CheckAll(bool check)
{
for (int i=0; i < GetItemCount(); i++)
Check(i, check);
}
bool wxCheckedListCtrl::DeleteItem(long item)
{
// shift our additional state array
//for (int i=item,max=GetItemCount(); i < max-1; i++)
// m_stateList[i] = m_stateList[i+1];
m_stateList.RemoveAt(item, 1);
return wxListCtrl::DeleteItem(item);
}
int wxCheckedListCtrl::GetCheckedItemCount() const
{
int res = 0;
for (int i=0; i<GetItemCount(); i++)
if (IsChecked(i))
res++;
return res;
}
// event handlers
void wxCheckedListCtrl::OnMouseEvent(wxMouseEvent& event)
{
if (!event.LeftDown()) {
event.Skip();
return;
}
int flags;
long item = HitTest(event.GetPosition(), flags);
if (item == wxNOT_FOUND || !IsEnabled(item)) {
// skip this item
event.Skip();
return;
}
// user clicked exactly on the checkbox or on the item row ?
bool processcheck = (flags & wxLIST_HITTEST_ONITEMICON) ||
((GetWindowStyle() & wxCLC_CHECK_WHEN_SELECTING) &&
(flags & wxLIST_HITTEST_ONITEM));
if (processcheck) {
wxListEvent ev(wxEVT_NULL, GetId());
ev.m_itemIndex = item;
// send the check event
if (IsChecked(item)) {
ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
Check(item, FALSE);
AddPendingEvent(ev);
} else {
ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_CHECKED);
Check(item, TRUE);
AddPendingEvent(ev);
}
}
event.Skip();
}
#endif // wxUSE_CHECKEDLISTCTRL

272
src/wx/widgets/joyedit.cpp Normal file
View File

@ -0,0 +1,272 @@
#include "wx/joyedit.h"
// FIXME: suppport analog/digital flag on per-axis basis
IMPLEMENT_DYNAMIC_CLASS(wxJoyKeyTextCtrl, wxKeyTextCtrl)
BEGIN_EVENT_TABLE(wxJoyKeyTextCtrl, wxKeyTextCtrl)
EVT_SDLJOY(wxJoyKeyTextCtrl::OnJoy)
END_EVENT_TABLE()
int wxJoyKeyTextCtrl::DigitalButton(wxSDLJoyEvent &event)
{
int sdlval = event.GetControlValue();
int sdltype = event.GetControlType();
switch(sdltype) {
case WXSDLJOY_AXIS:
// for val = 0 return arbitrary direction; val means "off"
return sdlval > 0 ? WXJB_AXIS_PLUS : WXJB_AXIS_MINUS;
case WXSDLJOY_HAT:
/* URDL = 1248 */
switch(sdlval) {
case 1:
return WXJB_HAT_N;
case 2:
return WXJB_HAT_E;
case 3:
return WXJB_HAT_NE;
case 4:
return WXJB_HAT_S;
case 6:
return WXJB_HAT_SE;
case 8:
return WXJB_HAT_W;
case 9:
return WXJB_HAT_NW;
case 12:
return WXJB_HAT_SW;
default:
return WXJB_HAT_N; // arbitrary direction; val = 0 means "off"
}
case WXSDLJOY_BUTTON:
return WXJB_BUTTON;
default:
// unknown ctrl type
return -1;
}
}
void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent &event)
{
short val = event.GetControlValue();
int mod = DigitalButton(event);
int key = event.GetControlIndex(), joy = event.GetJoy() + 1;
if(!val || mod < 0)
return;
wxString nv = ToString(mod, key, joy);
if(nv.empty())
return;
if(multikey) {
wxString ov = GetValue();
if(!ov.empty())
nv = ov + multikey + nv;
}
SetValue(nv);
if(keyenter)
Navigate();
}
wxString wxJoyKeyTextCtrl::ToString(int mod, int key, int joy)
{
if(!joy)
return wxKeyTextCtrl::ToString(mod, key);
wxString s;
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
// So any strings added below must also be translated unconditionally
s.Printf(_("Joy%d-"), joy);
wxString mk;
switch(mod) {
case WXJB_AXIS_PLUS:
mk.Printf(_("Axis%d+"), key);
break;
case WXJB_AXIS_MINUS:
mk.Printf(_("Axis%d-"), key);
break;
case WXJB_BUTTON:
mk.Printf(_("Button%d"), key);
break;
case WXJB_HAT_N:
mk.Printf(_("Hat%dN"), key);
break;
case WXJB_HAT_S:
mk.Printf(_("Hat%dS"), key);
break;
case WXJB_HAT_W:
mk.Printf(_("Hat%dW"), key);
break;
case WXJB_HAT_E:
mk.Printf(_("Hat%dE"), key);
break;
case WXJB_HAT_NW:
mk.Printf(_("Hat%dNW"), key);
break;
case WXJB_HAT_NE:
mk.Printf(_("Hat%dNE"), key);
break;
case WXJB_HAT_SW:
mk.Printf(_("Hat%dSW"), key);
break;
case WXJB_HAT_SE:
mk.Printf(_("Hat%dSE"), key);
break;
}
s += mk;
return s;
}
wxString wxJoyKeyTextCtrl::ToString(wxJoyKeyBinding_v keys, wxChar sep)
{
wxString ret;
for(int i = 0; i < keys.size(); i++) {
if(i > 0)
ret += sep;
wxString key = ToString(keys[i].mod, keys[i].key, keys[i].joy);
if(key.empty())
return wxEmptyString;
ret += key;
}
return ret;
}
#include <wx/regex.h>
// only parse regex once
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
// So any strings added below must also be translated unconditionally
// \1 is joy #
static wxRegEx joyre(_("^Joy([0-9]+)[-+]"), wxRE_EXTENDED|wxRE_ICASE);
// \1 is axis# and \2 is + or -
static wxRegEx axre(_("Axis([0-9]+)([+-])"), wxRE_EXTENDED|wxRE_ICASE);
// \1 is button#
static wxRegEx butre(_("Button([0-9]+)"), wxRE_EXTENDED|wxRE_ICASE);
// \1 is hat#, \3 is N, \4 is S, \5 is E, \6 is W, \7 is NE, \8 is SE,
// \9 is SW, \10 is NW
static wxRegEx hatre(_("Hat([0-9]+)((N|North|U|Up)|(S|South|D|Down)|"
"(E|East|R|Right)|(W|West|L|Left)|"
"(NE|NorthEast|UR|UpRight)|(SE|SouthEast|DR|DownRight)|"
"(SW|SouthWest|DL|DownLeft)|(NW|NorthWest|UL|UpLeft))"),
wxRE_EXTENDED|wxRE_ICASE);
// use of static wxRegeEx is not thread-safe
static wxCriticalSection recs;
// wx provides no atoi for wxChar
// this is not a universal function; assumes valid number
static int simple_atoi(const wxChar *s, int len)
{
int ret = 0;
for(int i = 0; i < len; i++)
ret = ret * 10 + s[i] - wxT('0');
return ret;
}
static bool ParseJoy(const wxChar *s, int len, int &mod, int &key, int &joy)
{
mod = key = joy = 0;
if(!len)
return false;
wxCriticalSectionLocker lk(recs);
size_t b, l;
if(!joyre.Matches(s) || !joyre.GetMatch(&b, &l) || b)
return false;
const wxChar *p = s + l;
int alen = len - l;
joyre.GetMatch(&b, &l, 1);
joy = simple_atoi(s + b, l);
#define is_ctrl(re) re.Matches(p) && re.GetMatch(&b, &l) && l == alen && !b
if(is_ctrl(axre)) {
axre.GetMatch(&b, &l, 1);
key = simple_atoi(p + b, l);
axre.GetMatch(&b, &l, 2);
mod = p[b] == wxT('+') ? WXJB_AXIS_PLUS : WXJB_AXIS_MINUS;
} else if(is_ctrl(butre)) {
butre.GetMatch(&b, &l, 1);
key = simple_atoi(p + b, l);
mod = WXJB_BUTTON;
} else if(is_ctrl(hatre)) {
hatre.GetMatch(&b, &l, 1);
key = simple_atoi(p + b, l);
#define check_dir(n, d) else if(hatre.GetMatch(&b, &l, n) && l > 0) mod = WXJB_HAT_##d
if(0);
check_dir(3, N);
check_dir(4, S);
check_dir(5, E);
check_dir(6, W);
check_dir(7, NE);
check_dir(8, SE);
check_dir(9, SW);
check_dir(10, NW);
} else {
joy = 0;
return false;
}
return true;
}
bool wxJoyKeyTextCtrl::ParseString(const wxChar *s, int len, int &mod, int &key, int &joy)
{
if(ParseJoy(s, len, mod, key, joy))
return true;
return wxKeyTextCtrl::ParseString(s, len, mod, key);
}
bool wxJoyKeyTextCtrl::FromString(const wxString &s, int &mod, int &key, int &joy)
{
return ParseString(s.c_str(), s.size(), mod, key, joy);
}
wxJoyKeyBinding_v wxJoyKeyTextCtrl::FromString(const wxString &s, wxChar sep)
{
wxJoyKeyBinding_v ret, empty;
int mod, key, joy;
int len = s.size();
if(!len)
return empty;
for(int lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) {
if(lastkey == len - 1) {
// sep as accel
if(!lastkey)
break;
if(s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') ||
s[lastkey - 1] == sep)
continue;
}
if(!ParseString(s.c_str() + lastkey + 1, len - lastkey - 1, mod, key, joy))
return empty;
wxJoyKeyBinding jb = { key, mod, joy };
ret.insert(ret.begin(), jb);
len = lastkey;
}
if(!ParseString(s.c_str(), len, mod, key, joy))
return empty;
wxJoyKeyBinding jb = { key, mod, joy };
ret.insert(ret.begin(), jb);
return ret;
}
IMPLEMENT_CLASS(wxJoyKeyValidator, wxValidator)
bool wxJoyKeyValidator::TransferToWindow()
{
if(!val)
return false;
wxJoyKeyTextCtrl *jk = wxDynamicCast(GetWindow(), wxJoyKeyTextCtrl);
if(!jk)
return false;
jk->SetValue(wxJoyKeyTextCtrl::ToString(*val));
return true;
}
bool wxJoyKeyValidator::TransferFromWindow()
{
if(!val)
return false;
wxJoyKeyTextCtrl *jk = wxDynamicCast(GetWindow(), wxJoyKeyTextCtrl);
if(!jk)
return false;
*val = wxJoyKeyTextCtrl::FromString(jk->GetValue());
return true;
}

228
src/wx/widgets/keyedit.cpp Normal file
View File

@ -0,0 +1,228 @@
#include "wx/keyedit.h"
IMPLEMENT_DYNAMIC_CLASS(wxKeyTextCtrl, wxTextCtrl)
BEGIN_EVENT_TABLE(wxKeyTextCtrl, wxTextCtrl)
EVT_KEY_DOWN(wxKeyTextCtrl::OnKeyDown)
EVT_KEY_UP(wxKeyTextCtrl::OnKeyUp)
END_EVENT_TABLE()
void wxKeyTextCtrl::OnKeyDown(wxKeyEvent &event)
{
lastmod = event.GetModifiers();
lastkey = event.GetKeyCode();
}
void wxKeyTextCtrl::OnKeyUp(wxKeyEvent &event)
{
int mod = lastmod;
int key = lastkey;
lastmod = lastkey = 0;
// key is only 0 if we missed the keydown event
// or if we are being shipped pseudo keypress events
// either way, just ignore
if(!key)
return;
// use unmodified backspace to clear last key, if enabled
// if blank or backspace is modified, add normally instead
if(clearable && !mod && key == WXK_BACK && !GetValue().empty())
{
wxString val = GetValue();
int lastkey = val.rfind(multikey);
if(lastkey && lastkey != wxString::npos) {
// if this was actually a ,-accel, delete instead
if(lastkey == val.size() - 1) {
lastkey = val.rfind(multikey, lastkey-1);
if(lastkey == wxString::npos)
lastkey = 0;
}
val.resize(lastkey);
SetValue(val);
} else
Clear();
} else {
wxString nv = ToString(mod, key);
if(nv.empty())
return;
if(multikey) {
wxString ov = GetValue();
if(!ov.empty())
nv = ov + multikey + nv;
}
SetValue(nv);
}
if(keyenter)
Navigate();
}
wxString wxKeyTextCtrl::ToString(int mod, int key)
{
// wx ignores non-alnum printable chars
// actually, wx gives an assertion error, so it's best to filter out
// before passing to ToString()
bool char_override = key > 32 && key < WXK_START && !wxIsalnum(key);
// wx also ignores modifiers (and does not report meta at all)
bool mod_override = key == WXK_SHIFT || key == WXK_CONTROL || key == WXK_ALT;
wxAcceleratorEntry ae(mod, char_override || mod_override ? WXK_F1 : key);
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
// So any strings added below must also be translated unconditionally
wxString s = ae.ToString();
if(char_override || mod_override) {
int l = s.rfind(wxT('-'));
if(l == wxString::npos)
l = 0;
else l++;
s.erase(l);
switch(key) {
case WXK_SHIFT:
s.append(_("SHIFT"));
break;
case WXK_ALT:
s.append(_("ALT"));
break;
case WXK_CONTROL:
s.append(_("CTRL"));
break;
default:
s.append((wxChar)key);
}
}
// on Mac, ctrl/meta become xctrl/cmd
// on other, meta is ignored
#ifndef __WXMAC__
if(mod & wxMOD_META) {
s.insert(0, _("Meta-"));
}
#endif
if(s.empty() || (key != wxT('-') && s[s.size()-1] == wxT('-')))
// bad key combo; probably also generates an assertion in wx
return wxEmptyString;
return s;
}
wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep)
{
wxString ret;
for(int i = 0; i < keys.size(); i++) {
if(i > 0)
ret += sep;
wxString key = ToString(keys[i].GetFlags(), keys[i].GetKeyCode());
if(key.empty())
return wxEmptyString;
ret += key;
}
return ret;
}
bool wxKeyTextCtrl::ParseString(const wxChar *s, int len, int &mod, int &key)
{
mod = key = 0;
if(!s || !len)
return false;
wxString a = wxT('\t');
a.append(s, len);
wxAcceleratorEntry ae;
#ifndef __WXMAC__
#define check_meta(str) do { \
wxString meta = str; \
for(int ml = 0; (ml = a.find(meta, ml)) != wxString::npos; ml++) { \
if(!ml || a[ml-1] == wxT('-') || a[ml-1] == wxT('+')) { \
mod |= wxMOD_META; \
a.erase(ml, meta.size()); \
ml = -1; \
} \
} \
} while(0)
check_meta(wxT("Meta-"));
check_meta(wxT("Meta+"));
check_meta(_("Meta-"));
check_meta(_("Meta+"));
#endif
// wx disallows standalone modifiers
// unlike ToString(), this generates a debug message rather than
// an assertion error, so it's easy to ignore and expensive to avoid
// beforehand. Instead, check for them on failure
if(!ae.FromString(a)) {
a.MakeUpper();
#define chk_str(n, k) do { \
wxString t = n; \
if(a.size() > t.size() && a.substr(a.size() - t.size()) == t) { \
a.replace(a.size() - t.size(), t.size(), wxT("F1")); \
wxString ss(s); \
if(ae.FromString(a)) { \
mod |= ae.GetFlags(); \
key = k; \
return true; \
} \
a.replace(a.size() - 2, 2, n); \
} \
} while(0)
chk_str(wxT("ALT"), WXK_ALT);
chk_str(wxT("SHIFT"), WXK_SHIFT);
chk_str(wxT("CTRL"), WXK_CONTROL);
chk_str(wxT("CONTROL"), WXK_CONTROL);
chk_str(_("ALT"), WXK_ALT);
chk_str(_("SHIFT"), WXK_SHIFT);
chk_str(_("CTRL"), WXK_CONTROL);
chk_str(_("CONTROL"), WXK_CONTROL);
return false;
}
mod |= ae.GetFlags();
key = ae.GetKeyCode();
// wx makes key lower-case, but key events return upper case
if(key < WXK_START && wxIslower(key))
key = wxToupper(key);
return true;
}
bool wxKeyTextCtrl::FromString(const wxString &s, int &mod, int &key)
{
return ParseString(s.c_str(), s.size(), mod, key);
}
wxAcceleratorEntry_v wxKeyTextCtrl::FromString(const wxString &s, wxChar sep)
{
wxAcceleratorEntry_v ret, empty;
int mod, key;
int len = s.size();
for(int lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) {
if(lastkey == len - 1) {
// sep as accel
if(!lastkey)
break;
if(s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') ||
s[lastkey - 1] == sep)
continue;
}
if(!ParseString(s.c_str() + lastkey + 1, len - lastkey - 1, mod, key))
return empty;
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
len = lastkey;
}
if(!ParseString(s.c_str(), len, mod, key))
return empty;
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
return ret;
}
IMPLEMENT_CLASS(wxKeyValidator, wxValidator)
bool wxKeyValidator::TransferToWindow()
{
wxKeyTextCtrl *k = wxDynamicCast(GetWindow(), wxKeyTextCtrl);
if(!k)
return false;
k->SetValue(wxKeyTextCtrl::ToString(*val));
return true;
}
bool wxKeyValidator::TransferFromWindow()
{
wxKeyTextCtrl *k = wxDynamicCast(GetWindow(), wxKeyTextCtrl);
if(!k)
return false;
*val = wxKeyTextCtrl::FromString(k->GetValue());
return true;
}

159
src/wx/widgets/sdljoy.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "wx/sdljoy.h"
#include <SDL/SDL.h>
#include <SDL/SDL_joystick.h>
#include <wx/window.h>
DEFINE_EVENT_TYPE(wxEVT_SDLJOY)
struct wxSDLJoyState {
SDL_Joystick *dev;
int nax, nhat, nbut;
short *curval;
~wxSDLJoyState() { if(dev) SDL_JoystickClose(dev); }
};
wxSDLJoy::wxSDLJoy(bool analog) : wxTimer(), digital(!analog),
evthandler(0), joystate(0), nosticks(true)
{
// Start up joystick if not already started
// FIXME: check for errors
SDL_Init(SDL_INIT_JOYSTICK);
// but we'll have to manage it manually
SDL_JoystickEventState(SDL_IGNORE);
// now query joystick config and open joysticks
// there is no way to reread this later (e.g. if joystick plugged in),
// since SDL won't
njoy = SDL_NumJoysticks();
if(!njoy)
return;
joystate = new wxSDLJoyState_t[njoy];
memset(joystate, 0, njoy * sizeof(*joystate));
for(int i = 0; i < njoy; i++) {
SDL_Joystick *dev = joystate[i].dev = SDL_JoystickOpen(i);
int nctrl = 0;
nctrl += joystate[i].nax = SDL_JoystickNumAxes(dev);
nctrl += joystate[i].nhat = SDL_JoystickNumHats(dev);
nctrl += joystate[i].nbut = SDL_JoystickNumButtons(dev);
joystate[i].curval = new short[nctrl];
memset(joystate[i].curval, 0, sizeof(short) * nctrl);
}
}
wxSDLJoy::~wxSDLJoy()
{
delete[] joystate;
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
wxEvtHandler *wxSDLJoy::Attach(wxEvtHandler *handler)
{
wxEvtHandler *prev = evthandler;
evthandler = handler;
return prev;
}
void wxSDLJoy::Add(int joy)
{
if(joy >= njoy || !njoy)
return;
if(joy < 0) {
for(int i = 0; i < njoy; i++)
Add(i);
return;
}
if(!joystate[joy].dev)
joystate[joy].dev = SDL_JoystickOpen(joy);
if(nosticks && joystate[joy].dev) {
Start(50);
nosticks = false;
}
}
void wxSDLJoy::Remove(int joy)
{
if(joy >= njoy || !njoy)
return;
if(joy < 0) {
for(int i = 0; i < njoy; i++)
if(joystate[i].dev) {
SDL_JoystickClose(joystate[i].dev);
joystate[i].dev = NULL;
}
Stop();
nosticks = true;
return;
}
if(!joystate[joy].dev)
return;
SDL_JoystickClose(joystate[joy].dev);
joystate[joy].dev = NULL;
for(int i = 0; i < njoy; i++)
if(joystate[i].dev)
return;
Stop();
nosticks = true;
}
void wxSDLJoy::Notify()
{
if(nosticks)
return;
SDL_JoystickUpdate();
wxEvtHandler *handler = evthandler ? evthandler : wxWindow::FindFocus();
for(int i = 0; i < njoy; i++) {
SDL_Joystick *dev = joystate[i].dev;
if(dev) {
int nax = joystate[i].nax, nhat = joystate[i].nhat,
nbut = joystate[i].nbut;
int ctrl = 0;
short val;
for(int j = 0; j < nax; j++) {
val = SDL_JoystickGetAxis(dev, j);
if(digital) {
if(val > 0x3fff)
val = 0x7fff;
else if(val <= -0x3fff)
val = -0x7fff;
else
val = 0;
}
if(handler && val != joystate[i].curval[j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_AXIS;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[j];
handler->ProcessEvent(ev);
}
joystate[i].curval[j] = val;
}
for(int j = 0; j < nhat; j++) {
val = SDL_JoystickGetHat(dev, j);
if(handler && val != joystate[i].curval[nax + j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_HAT;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[nax + j];
handler->ProcessEvent(ev);
}
joystate[i].curval[nax + j] = val;
}
for(int j = 0; j < nbut; j++) {
val = SDL_JoystickGetButton(dev, j);
if(handler && val != joystate[i].curval[nax + nhat + j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_BUTTON;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[nax + nhat + j];
handler->ProcessEvent(ev);
}
joystate[i].curval[nax + nhat + j] = val;
}
}
}
}

View File

@ -0,0 +1,182 @@
/////////////////////////////////////////////////////////////////////////////
// Name: checkedlistctrl.h
// Purpose: wxCheckedListCtrl
// Author: Uknown ? (found at http://wiki.wxwidgets.org/wiki.pl?WxListCtrl)
// Modified by: Francesco Montorsi
// Created: 2005/06/29
// RCS-ID: $Id$
// Copyright: (c) 2005 Francesco Montorsi
// Licence: wxWidgets licence
/////////////////////////////////////////////////////////////////////////////
#ifndef _WX_CHECKEDLISTCTRL_H_
#define _WX_CHECKEDLISTCTRL_H_
// wxWidgets headers
#include "wx/webupdatedef.h" // for the WXDLLIMPEXP_WEBUPDATE macro
#include <wx/listctrl.h>
#include <wx/imaglist.h>
#if wxUSE_CHECKEDLISTCTRL
// image indexes (used internally by wxCheckedListCtrl)
#define wxCLC_UNCHECKED_IMGIDX 0 // unchecked & enabled
#define wxCLC_CHECKED_IMGIDX 1 // checked & enabled
#define wxCLC_DISABLED_UNCHECKED_IMGIDX 2 // unchecked & disabled
#define wxCLC_DISABLED_CHECKED_IMGIDX 3 // checked & disabled
// additional state flags (wx's defines should end at 0x0100; see listbase.h)
#define wxLIST_STATE_CHECKED 0x010000
#define wxLIST_STATE_ENABLED 0x100000
// additional wxCheckedListCtrl style flags
// (wx's defines should at 0x8000; see listbase.h)
#define wxCLC_CHECK_WHEN_SELECTING 0x10000
// -------------------------
// wxCheckedListCtrl events
// -------------------------
DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_WEBUPDATE, wxEVT_COMMAND_LIST_ITEM_CHECKED, -1);
DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_WEBUPDATE, wxEVT_COMMAND_LIST_ITEM_UNCHECKED, -1);
#define EVT_LIST_ITEM_CHECKED(id, fn) \
DECLARE_EVENT_TABLE_ENTRY( \
wxEVT_COMMAND_LIST_ITEM_CHECKED, id, -1, \
(wxObjectEventFunction)(wxEventFunction)(wxListEventFunction)&fn, \
(wxObject *) NULL \
),
#define EVT_LIST_ITEM_UNCHECKED(id, fn) \
DECLARE_EVENT_TABLE_ENTRY( \
wxEVT_COMMAND_LIST_ITEM_UNCHECKED, id, -1, \
(wxObjectEventFunction)(wxEventFunction)(wxListEventFunction)&fn, \
(wxObject *) NULL \
),
//! This is the class which performs all transactions with the server.
//! It uses the wxSocket facilities.
class WXDLLIMPEXP_WEBUPDATE wxCheckedListCtrl : public wxListCtrl
{
protected:
// we have to keep a different array to keep track of the additional
// states we support....
wxArrayInt m_stateList;
// our set of checkbox images...
wxImageList m_imageList;
public:
wxCheckedListCtrl()
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
: wxListCtrl(), m_imageList() {}
#else
: wxListCtrl(), m_imageList(16, 16, TRUE) {}
#endif
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = -1,
const wxPoint& pt = wxDefaultPosition,
const wxSize& sz = wxDefaultSize,
long style = wxCLC_CHECK_WHEN_SELECTING,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxListCtrlNameStr)
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
: wxListCtrl(), m_imageList()
#else
: wxListCtrl(), m_imageList(16, 16, TRUE)
#endif
{ Create(parent, id, pt, sz, style, validator, name); }
bool Create(wxWindow *parent, wxWindowID id = -1,
const wxPoint& pt = wxDefaultPosition,
const wxSize& sz = wxDefaultSize,
long style = wxCLC_CHECK_WHEN_SELECTING,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxListCtrlNameStr);
#ifdef CLC_VBAM_USAGE
// for xrc usage, a separate init function is needed.
bool Init();
#endif
virtual ~wxCheckedListCtrl() {}
public: // utilities
// core overloads (i.e. the most generic overloads)
bool GetItem(wxListItem& info) const;
bool SetItem(wxListItem& info);
long InsertItem(wxListItem& info);
bool DeleteItem(long item);
bool DeleteAllItems()
{ m_stateList.Clear(); return wxListCtrl::DeleteAllItems(); }
bool SortItems(wxListCtrlCompare, long)
{ wxASSERT_MSG(0, wxT("Not implemented yet ! sorry... ")); return FALSE; }
// shortcuts to the SetItemState function
void Check(long item, bool checked);
void Enable(long item, bool enable);
void CheckAll(bool checked = true);
void EnableAll(bool enable = true);
// this needs to be redeclared otherwise it's hidden by our other Enable() function.
// However you should use #EnableAll instead of this function if you want to get
// good graphics (try to understand)
virtual bool Enable(bool enable = true)
{ return wxListCtrl::Enable(enable); }
// shortcuts to the GetItemState function
bool IsChecked(long item) const
{ return GetItemState(item, wxLIST_STATE_CHECKED) != 0; }
bool IsEnabled(long item) const
{ return GetItemState(item, wxLIST_STATE_ENABLED) != 0; }
// this needs to be redeclared otherwise it's hidden by our other IsEnabled() function.
bool IsEnabled() const
{ return wxWindow::IsEnabled(); }
//! Returns the number of checked items in the control.
int GetCheckedItemCount() const;
// we overload these so we are sure they will use our
// #GetItem and #SetItem functions...
bool SetItemState(long item, long state, long stateMask);
int GetItemState(long item, long stateMask) const;
long InsertItem( long index, const wxString& label, int imageIndex = -1);
long SetItem(long index, int col, const wxString& label, int imageId = -1);
// the image associated with an element is already in used by wxCheckedListCtrl
// itself to show the checkbox and it cannot be handled by the user !
bool SetItemImage(long, int)
{ wxASSERT_MSG(0, wxT("This function cannot be used with wxCheckedListCtrl !")); return FALSE; }
protected: // event handlers
void OnMouseEvent(wxMouseEvent& event);
protected: // internal utilities
static int GetItemImageFromAdditionalState(int addstate);
static int GetAndRemoveAdditionalState(long *state, int statemask);
wxColour GetBgColourFromAdditionalState(int additionalstate);
private:
#if CLC_VBAM_USAGE
DECLARE_DYNAMIC_CLASS(wxCheckedListCtrl)
#else
DECLARE_CLASS(wxCheckedListCtrl)
#endif
DECLARE_EVENT_TABLE()
};
#endif // wxUSE_CHECKEDLISTCTRL
#endif // _WX_CHECKEDLISTCTRL_H_

View File

@ -0,0 +1,75 @@
#ifndef _WX_JOYKEYTEXT_H
#define _WX_JOYKEYTEXT_H
// wxJoyKeyTextCtrl: a wxTextCtrl which stores/acts on key presses and joystick
// The value is the symbolic name of the key pressed
// Supports manual clearing (bs), multiple keys in widget, automatic tab on key
#include "wx/keyedit.h"
#include "wx/sdljoy.h"
typedef struct wxJoyKeyBinding {
int key; // key code; listed first for easy static init
int mod; // modifier flags
int joy; // joystick # (starting at 1)
// if joy is non-0, key = control number, and mod = control type
} wxJoyKeyBinding;
typedef std::vector<wxJoyKeyBinding> wxJoyKeyBinding_v;
// joystick control types
// mod for joysticks
enum {
WXJB_AXIS_PLUS, WXJB_AXIS_MINUS, WXJB_BUTTON, WXJB_HAT_FIRST,
WXJB_HAT_N = WXJB_HAT_FIRST, WXJB_HAT_S, WXJB_HAT_W, WXJB_HAT_E, WXJB_HAT_NW,
WXJB_HAT_NE, WXJB_HAT_SW, WXJB_HAT_SE, WXJB_HAT_LAST = WXJB_HAT_SE
};
class wxJoyKeyTextCtrl : public wxKeyTextCtrl
{
public:
// default constructor; required for use with xrc
// FIXME: clearable and keyenter should be style flags
wxJoyKeyTextCtrl() : wxKeyTextCtrl() {}
virtual ~wxJoyKeyTextCtrl() {};
// key is event.GetControlIndex(), and joy is event.GetJoy() + 1
// mod is derived from GetControlValue() and GetControlType():
// convert wxSDLJoyEvent's type+val into mod (WXJB_*)
static int DigitalButton(wxSDLJoyEvent &event);
// convert mod+key to accel string, separated by -
static wxString ToString(int mod, int key, int joy);
// convert multiple keys, separated by multikey
static wxString ToString(wxJoyKeyBinding_v keys, wxChar sep = wxT(','));
// parses single key string into mod+key
static bool FromString(const wxString &s, int &mod, int &key, int &joy);
// parse multi-key string into array
// returns empty array on parse errors
static wxJoyKeyBinding_v FromString(const wxString &s, wxChar sep = wxT(','));
// parse a single key in given wxChar array up to given len
static bool ParseString(const wxChar *s, int len, int &mod, int &key, int &joy);
protected:
void OnJoy(wxSDLJoyEvent &);
DECLARE_DYNAMIC_CLASS();
DECLARE_EVENT_TABLE();
};
// A simple copy-only validator
class wxJoyKeyValidator : public wxValidator
{
public:
wxJoyKeyValidator(wxJoyKeyBinding_v *v) : wxValidator(), val(v) {}
wxJoyKeyValidator(const wxJoyKeyValidator &v) : wxValidator(), val(v.val) {}
wxObject *Clone() const { return new wxJoyKeyValidator(val); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
wxJoyKeyBinding_v *val;
DECLARE_CLASS(wxJoyKeyValidator)
};
#endif /* WX_JOYKEYTEXT_H */

View File

@ -0,0 +1,75 @@
#ifndef _WX_KEYTEXT_H
#define _WX_KEYTEXT_H
// wxKeyTextCtrl: a wxTextCtrl which stores/acts on key presses
// The value is the symbolic name of the key pressed
// Supports manual clearing (bs), multiple keys in widget, automatic tab on key
#include <wx/textctrl.h>
#include <wx/accel.h>
#include <vector>
typedef std::vector<wxAcceleratorEntry> wxAcceleratorEntry_v;
class wxKeyTextCtrl : public wxTextCtrl
{
public:
// default constructor; required for use with xrc
// FIXME: clearable and keyenter should be style flags
wxKeyTextCtrl() : wxTextCtrl(), clearable(true), multikey(wxT(',')),
keyenter(true), lastmod(0), lastkey(0) {};
virtual ~wxKeyTextCtrl() {};
void SetClearable(bool set = true) { clearable = set; }
void SetMultikey(wxChar c = wxT(',')) { multikey = c; }
void SetKeyEnter(bool set = true) { keyenter = set; }
bool GetClearable() { return clearable; }
wxChar GetMultikey() { return multikey; }
bool GetKeyEnter() { return keyenter; }
// convert mod+key to accel string, separated by -
static wxString ToString(int mod, int key);
// convert multiple keys, separated by multikey
static wxString ToString(wxAcceleratorEntry_v keys, wxChar sep = wxT(','));
// parses single key string into mod+key
static bool FromString(const wxString &s, int &mod, int &key);
// parse multi-key string into accelentry array
// note that meta flag may be set in accelentry array item even
// where not supported for accelerators (i.e. non-mac)
// returns empty array on parse errors
static wxAcceleratorEntry_v FromString(const wxString &s, wxChar sep = wxT(','));
// parse a single key in given wxChar array up to given len
static bool ParseString(const wxChar *s, int len, int &mod, int &key);
protected:
void OnKeyDown(wxKeyEvent &);
void OnKeyUp(wxKeyEvent &);
bool clearable;
wxChar multikey;
bool keyenter;
// the last keydown event received; this is processed on next keyup
int lastmod, lastkey;
DECLARE_DYNAMIC_CLASS();
DECLARE_EVENT_TABLE();
};
// A simple copy-only validator
class wxKeyValidator : public wxValidator
{
public:
wxKeyValidator(wxAcceleratorEntry_v *v) : wxValidator(), val(v) {}
wxKeyValidator(const wxKeyValidator &v) : wxValidator(), val(v.val) {}
wxObject *Clone() const { return new wxKeyValidator(val); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
wxAcceleratorEntry_v *val;
DECLARE_CLASS(wxKeyValidator)
};
#endif /* WX_KEYTEXT_H */

102
src/wx/widgets/wx/sdljoy.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef JOYEVT_H
#define JOYEVT_H
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
// It's geared towards keyboard emulation
// To use, create a wxSDLJoy object Add() the joysticks you want to monitor.
// wxSDLJoy derives from wxTimer, so you can pause it with Stop() and
// resume with Start().
//
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
#include <wx/event.h>
#include <wx/timer.h>
typedef struct wxSDLJoyState wxSDLJoyState_t;
class wxSDLJoy : public wxTimer
{
public:
// if analog, send events for all axis movement
// otherwise, only send events when values cross the 50% mark
// and max out values
wxSDLJoy(bool analog = false);
// but flag can be set later
void SetAnalog(bool analog = true) { digital = !analog; };
// send events to this handler
// If NULL (default), send to window with keyboard focus
wxEvtHandler *Attach(wxEvtHandler *);
// add another joystick to the list of polled sticks
// -1 == add all
// If joy > # of joysticks, it is ignored
// This will start polling if a valid joystick is selected
void Add(int joy = -1);
// remove a joystick from the polled sticks
// -1 == remove all
// If joy > # of joysticks, it is ignored
// This will stop polling if all joysticks are disabled
void Remove(int joy = -1);
// query if a stick is being polled
bool IsPolling(int joy);
// query # of joysticks
int GetNumJoysticks() { return njoy; }
// query # of axes on given joystick
// 0 is returned if joy is invalid
int GetNumAxes(int joy);
// query # of hats on given joystick
// 0 is returned if joy is invalid
int GetNumHats(int joy);
// query # of buttons on given joystick
// 0 is returned if joy is invalid
int GetNumButtons(int joy);
virtual ~wxSDLJoy();
protected:
bool digital;
int njoy;
wxSDLJoyState_t *joystate;
wxEvtHandler *evthandler;
bool nosticks;
void Notify();
};
enum {
// The types of supported controls
// values are signed-16 for axis, 0/1 for button
// hat is bitmask NESW/URDL
WXSDLJOY_AXIS, WXSDLJOY_HAT, WXSDLJOY_BUTTON
};
class wxSDLJoyEvent : public wxCommandEvent
{
friend class wxSDLJoy;
public:
// Default constructor
wxSDLJoyEvent(wxEventType commandType = wxEVT_NULL, int id = 0) :
wxCommandEvent(commandType, id) {}
// accessors
unsigned short GetJoy() { return joy; }
unsigned short GetControlType() { return ctrl_type; }
unsigned short GetControlIndex() { return ctrl_idx; }
short GetControlValue() { return ctrl_val; }
short GetControlPrevValue() { return prev_val; }
// required for PostEvent, apparently
wxEvent *Clone();
protected:
unsigned short joy;
unsigned short ctrl_type;
unsigned short ctrl_idx;
short ctrl_val;
short prev_val;
};
// Note: this means sdljoy can't be part of a library w/o extra work
DECLARE_LOCAL_EVENT_TYPE(wxEVT_SDLJOY, -1)
typedef void (wxEvtHandler::*wxSDLJoyEventFunction)(wxSDLJoyEvent &);
#define EVT_SDLJOY(fn) \
DECLARE_EVENT_TABLE_ENTRY(wxEVT_SDLJOY, wxID_ANY, wxID_ANY, \
(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) \
wxStaticCastEvent(wxSDLJoyEventFunction, &fn), (wxObject *)NULL), \
#endif /* JOYEVT_H */

View File

@ -0,0 +1,14 @@
// dummy webupdatedef.h for checklistctrl
#ifndef WXDLLIMPEXP_WEBUPDATE
// this will never be part of a DLL
#define WXDLLIMPEXP_WEBUPDATE
// why would I include it and not enable it?
#define wxUSE_CHECKEDLISTCTRL 1
// enable customizations:
// - include wx/settings.h (bugfix; always enabled)
// - make a dynamic class so it can be subclass in xrc
// - only make col0 checkable
// - use "native" checkbox (also requires CLC_USE_SYSICONS)
#define CLC_VBAM_USAGE 1
#define CLC_USE_SYSICONS 1
#endif

195
src/wx/widgets/wx/wxmisc.h Normal file
View File

@ -0,0 +1,195 @@
#ifndef WX_MISC_H
#define WX_MISC_H
// utility widgets
// simple radio button not under the same parent window
// note that it must be checkbox, as wx radio buttons have rigid behavior
class wxFarRadio : public wxCheckBox
{
public:
wxFarRadio();
virtual ~wxFarRadio();
void SetValue(bool val);
// join this group with widget(s) in grp
void SetGroup(class wxFarRadio *grp);
// turn into a singleton
void BreakGroup();
// iterate over members in group (ring)
wxFarRadio *GetNext();
protected:
void UpdatedValue();
void UpdateEvt(wxCommandEvent &ev);
wxFarRadio *Next;
DECLARE_DYNAMIC_CLASS()
DECLARE_EVENT_TABLE()
};
// boolean copy-only validator that uses a constant int
// may be attached to radio button or checkbox
class wxBoolIntValidator : public wxValidator
{
public:
wxBoolIntValidator(int *_vptr, int _val, int _mask = ~0) : wxValidator(),
vptr(_vptr), val(_val), mask(_mask) {}
wxBoolIntValidator(const wxBoolIntValidator &v) : wxValidator(),
vptr(v.vptr), val(v.val), mask(v.mask) {}
wxObject *Clone() const { return new wxBoolIntValidator(vptr, val, mask); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
int val, mask, *vptr;
};
// boolean copy-only validator with reversed value
// may be attached to radio button or checkbox
class wxBoolRevValidator : public wxValidator
{
public:
wxBoolRevValidator(bool *_vptr) : wxValidator(), vptr(_vptr) {}
wxBoolRevValidator(const wxBoolRevValidator &v) : wxValidator(), vptr(v.vptr) {}
wxObject *Clone() const { return new wxBoolRevValidator(vptr); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
bool *vptr;
};
// wxFilePickerCtrl/wxDirPickerCtrl copy-only vvalidator
class wxFileDirPickerValidator : public wxValidator
{
public:
wxFileDirPickerValidator(wxString *_vptr) : wxValidator(), vptr(_vptr) {}
wxFileDirPickerValidator(const wxFileDirPickerValidator &v) : wxValidator(),
vptr(v.vptr) {}
wxObject *Clone() const { return new wxFileDirPickerValidator(vptr); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
wxString *vptr;
};
// color copy-only validator that supports either 32-bit or 16-bit color
// value (but not endianness..)
// FIXME: only supported formats are RGB888 and BGR555
#include <stdint.h>
class wxColorValidator : public wxValidator
{
public:
wxColorValidator(uint32_t *vptr) : wxValidator(), ptr32(vptr), ptr16(0) {}
wxColorValidator(uint16_t *vptr) : wxValidator(), ptr16(vptr), ptr32(0) {}
wxColorValidator(const wxColorValidator &v) : wxValidator(),
ptr32(v.ptr32), ptr16(v.ptr16) {}
wxObject *Clone() const { return new wxColorValidator(*this); }
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow *p) { return true; }
protected:
uint32_t *ptr32;
uint16_t *ptr16;
};
// Copy-only validators for checkboxes and radio buttons that enables a set
// of dependent widgets
// Requires an event handler during run-time
#include <vector>
#include <wx/valgen.h>
// there's probably a standard wxWindowList or some such, but it's
// undocumented and I prefer arrays
typedef std::vector<wxWindow *>wxWindow_v;
class wxBoolEnValidator : public wxGenericValidator
{
public:
wxBoolEnValidator(bool *vptr) : wxGenericValidator(vptr) {}
wxBoolEnValidator(bool *vptr, wxWindow_v &cnt, std::vector<bool>rev = std::vector<bool>()) :
wxGenericValidator(vptr), controls(cnt), reverse(rev) {}
wxBoolEnValidator(const wxBoolEnValidator &v) : wxGenericValidator(v),
controls(v.controls), reverse(v.reverse) {}
wxObject *Clone() const { return new wxBoolEnValidator(*this); }
// set these after init, rather than in constructor
wxWindow_v controls;
// set reverse entries to true if disabled when checkbox checked
// controls past the end of the reverse array are not reversed
std::vector<bool> reverse;
// inherit validate, xferfrom from parent
bool TransferToWindow();
};
class wxBoolIntEnValidator : public wxBoolIntValidator
{
public:
wxBoolIntEnValidator(int *vptr, int val, int mask = ~0) :
wxBoolIntValidator(vptr, val, mask) {}
wxBoolIntEnValidator(int *vptr, int val, int mask, wxWindow_v &cnt,
std::vector<bool>rev = std::vector<bool>()) :
wxBoolIntValidator(vptr, val, mask), controls(cnt), reverse(rev) {}
wxBoolIntEnValidator(const wxBoolIntEnValidator &v) :
wxBoolIntValidator(v), controls(v.controls), reverse(v.reverse) {}
wxObject *Clone() const { return new wxBoolIntEnValidator(*this); }
// set these after init, rather than in constructor
wxWindow_v controls;
// set reverse entries to true if disabled when checkbox checked
// controls past the end of the reverse array are not reversed
std::vector<bool> reverse;
// inherit validate, xferfrom from parent
bool TransferToWindow();
};
class wxBoolRevEnValidator : public wxBoolRevValidator
{
public:
wxBoolRevEnValidator(bool *vptr) : wxBoolRevValidator(vptr) {}
wxBoolRevEnValidator(bool *vptr, wxWindow_v &cnt, std::vector<bool>rev = std::vector<bool>()) :
wxBoolRevValidator(vptr), controls(cnt), reverse(rev) {}
wxBoolRevEnValidator(const wxBoolRevEnValidator &v) : wxBoolRevValidator(v),
controls(v.controls), reverse(v.reverse) {}
wxObject *Clone() const { return new wxBoolRevEnValidator(*this); }
// set these after init, rather than in constructor
wxWindow_v controls;
// set reverse entries to true if disabled when checkbox checked
// controls past the end of the reverse array are not reversed
std::vector<bool> reverse;
// inherit validate, xferfrom from parent
bool TransferToWindow();
};
// and here's an event handler that can be attached to the widget or a parent
// of the widget
// It is a descendent of wxEvtHandler so its methods can be used as
// event handlers
class wxBoolEnHandler : public wxEvtHandler
{
public:
wxWindow_v controls;
std::vector<bool> reverse;
void ToggleCheck(wxCommandEvent &ev);
void Enable(wxCommandEvent &ev);
void Disable(wxCommandEvent &ev);
};
// use this to connect to the handler of id in win to obj
#define wxCBBoolEnHandlerConnect(win, id, obj) \
(win)->Connect(id, wxEVT_COMMAND_CHECKBOX_CLICKED, \
wxCommandEventHandler(wxBoolEnHandler::ToggleCheck), NULL, \
&obj)
#define wxRBBoolEnHandlerConnect(win, id, obj, f) \
(win)->Connect(id, wxEVT_COMMAND_RADIOBUTTON_SELECTED, \
wxCommandEventHandler(wxBoolEnHandler::f), NULL, \
&obj)
#define wxRBEBoolEnHandlerConnect(win, id, obj) wxRBBoolEnHandlerConnect(win, id, obj, Enable)
#define wxRBDBoolEnHandlerConnect(win, id, obj) wxRBBoolEnHandlerConnect(win, id, obj, Disable)
// for wxTextValidator include lists
extern const wxArrayString val_hexdigits, val_sigdigits, val_unsdigits;
#endif /* WX_MISC_H */

322
src/wx/widgets/wxmisc.cpp Normal file
View File

@ -0,0 +1,322 @@
// utility widgets
#include <wx/wx.h>
#include "wx/wxmisc.h"
wxFarRadio::wxFarRadio()
: wxCheckBox()
{
Next = this;
}
wxFarRadio::~wxFarRadio()
{
BreakGroup();
}
void wxFarRadio::SetValue(bool val)
{
wxCheckBox::SetValue(val);
UpdatedValue();
}
void wxFarRadio::SetGroup(class wxFarRadio *grp)
{
if(grp == this)
return;
wxFarRadio *checked = GetValue() ? this : NULL;
for(wxFarRadio *gp = Next; gp != this; gp = gp->Next) {
if(gp = grp)
return;
if(gp->GetValue())
checked = gp;
}
wxFarRadio *link = Next;
Next = grp;
bool clear_checked = false;
wxFarRadio *gp;
for(gp = grp; gp->Next != grp; gp = gp->Next) {
if(checked && GetValue())
clear_checked = true;
}
gp->Next = link;
if(checked && gp->GetValue())
clear_checked = true;
if(clear_checked)
checked->SetValue(false);
int l;
for(l = 1, gp = Next; gp != this; gp = gp->Next, l++);
}
void wxFarRadio::BreakGroup() {
wxFarRadio **gp;
for(gp = &Next; *gp != this; gp = &(*gp)->Next);
*gp = Next;
Next = this;
}
wxFarRadio *wxFarRadio::GetNext()
{
return Next;
}
void wxFarRadio::UpdatedValue()
{
if(!GetValue()) {
wxFarRadio *gp;
// just like system wx, ensure at least one always checked
for(gp = Next; gp != this; gp = gp->Next)
if(gp->GetValue())
break;
if(gp == this) {
SetValue(true);
return;
}
} else
for(wxFarRadio *gp = Next; gp != this; gp = gp->Next)
gp->SetValue(false);
}
void wxFarRadio::UpdateEvt(wxCommandEvent &ev)
{
UpdatedValue();
ev.Skip();
}
IMPLEMENT_DYNAMIC_CLASS(wxFarRadio, wxCheckBox);
BEGIN_EVENT_TABLE(wxFarRadio, wxCheckBox)
EVT_CHECKBOX(wxID_ANY, wxFarRadio::UpdateEvt)
END_EVENT_TABLE()
bool wxBoolIntValidator::TransferToWindow()
{
if(!vptr)
return false;
bool checked = (*vptr & mask) == val;
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
if(cb) {
cb->SetValue(checked);
return true;
}
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
if(rb) {
rb->SetValue(checked);
return true;
}
return false;
}
bool wxBoolIntValidator::TransferFromWindow()
{
if(!vptr)
return false;
bool nv = false;
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
if(cb)
nv = cb->GetValue();
else {
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
if(rb)
nv = rb->GetValue();
else
return false;
}
if(mask == ~0 && !nv && *vptr != val)
return true;
*vptr = (*vptr & ~mask) | (nv ? val : 0);
return true;
}
bool wxBoolRevValidator::TransferToWindow()
{
if(!vptr)
return false;
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
if(cb) {
cb->SetValue(!*vptr);
return true;
}
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
if(rb) {
rb->SetValue(!*vptr);
return true;
}
return false;
}
bool wxBoolRevValidator::TransferFromWindow()
{
if(!vptr)
return false;
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
if(cb)
*vptr = !cb->GetValue();
else {
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
if(rb)
*vptr = !rb->GetValue();
else
return false;
}
return true;
}
#include <wx/filepicker.h>
bool wxFileDirPickerValidator::TransferToWindow()
{
if(!vptr)
return false;
wxFilePickerCtrl *fp = wxDynamicCast(GetWindow(), wxFilePickerCtrl);
if(fp) {
fp->SetPath(*vptr);
return true;
}
wxDirPickerCtrl *dp = wxDynamicCast(GetWindow(), wxDirPickerCtrl);
if(dp) {
dp->SetPath(*vptr);
return true;
}
return false;
}
bool wxFileDirPickerValidator::TransferFromWindow()
{
if(!vptr)
return false;
wxFilePickerCtrl *fp = wxDynamicCast(GetWindow(), wxFilePickerCtrl);
if(fp) {
*vptr = fp->GetPath();
return true;
}
wxDirPickerCtrl *dp = wxDynamicCast(GetWindow(), wxDirPickerCtrl);
if(dp) {
*vptr = dp->GetPath();
return true;
}
return false;
}
#include <wx/clrpicker.h>
bool wxColorValidator::TransferToWindow()
{
if(!ptr32 && !ptr16)
return false;
wxColourPickerCtrl *cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
if(!cp)
return false;
if(ptr32)
cp->SetColour(wxColor(((*ptr32 >> 16) & 0xff),
((*ptr32 >> 8) & 0xff),
((*ptr32) & 0xff)));
else
cp->SetColour(wxColor(((*ptr16 << 3) & 0xf8),
((*ptr16 >> 2) & 0xf8),
((*ptr16 >> 7) & 0xf8)));
return true;
}
bool wxColorValidator::TransferFromWindow()
{
if(!ptr32 && !ptr16)
return false;
wxColourPickerCtrl *cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
if(!cp)
return false;
wxColor c = cp->GetColour();
if(ptr32)
*ptr32 = ((c.Red() & 0xff) << 16) +
((c.Green() & 0xff) << 8) +
((c.Blue() & 0xff));
else
*ptr16 = ((c.Red() & 0xf8) >> 3) +
((c.Green() & 0xf8) << 2) +
((c.Blue() & 0xf8) << 7);
return true;
}
static void enable(wxWindow_v controls, std::vector<bool>reverse, bool en)
{
for(int i = 0; i < controls.size(); i++)
controls[i]->Enable(reverse.size() <= i || !reverse[i] ? en : !en);
}
#define boolen(r) do { \
bool en; \
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox); \
if(cb) \
en = cb->GetValue(); \
else { \
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton); \
if(!rb) \
return false; \
en = rb->GetValue(); \
} \
enable(controls, reverse, r ? !en : en); \
return true; \
} while(0)
bool wxBoolEnValidator::TransferToWindow()
{
if(!wxGenericValidator::TransferToWindow())
return false;
boolen(false);
}
bool wxBoolIntEnValidator::TransferToWindow()
{
if(!wxBoolIntValidator::TransferToWindow())
return false;
boolen(false);
}
bool wxBoolRevEnValidator::TransferToWindow()
{
if(!wxBoolRevValidator::TransferToWindow())
return false;
boolen(true);
}
void wxBoolEnHandler::ToggleCheck(wxCommandEvent &ev)
{
enable(controls, reverse, ev.IsChecked());
ev.Skip();
}
void wxBoolEnHandler::Enable(wxCommandEvent &ev)
{
enable(controls, reverse, true);
ev.Skip();
}
void wxBoolEnHandler::Disable(wxCommandEvent &ev)
{
enable(controls, reverse, false);
ev.Skip();
}
static const wxChar * /* const */ val_hexdigits_s[] = {
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
wxT("7"), wxT("8"), wxT("9"), wxT("A"), wxT("B"), wxT("C"), wxT("D"),
wxT("E"), wxT("F"), wxT("a"), wxT("b"), wxT("c"), wxT("d"), wxT("e"),
wxT("f")
};
const wxArrayString val_hexdigits(sizeof(val_hexdigits_s)/sizeof(val_hexdigits_s[0]),
val_hexdigits_s);
static const wxChar * /* const */ val_sigdigits_s[] = {
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
wxT("7"), wxT("8"), wxT("9"), wxT("-"), wxT("+")
};
const wxArrayString val_sigdigits(sizeof(val_sigdigits_s)/sizeof(val_sigdigits_s[0]),
val_sigdigits_s);
static const wxChar * /* const */ val_unsdigits_s[] = {
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
wxT("7"), wxT("8"), wxT("9")
};
const wxArrayString val_unsdigits(sizeof(val_unsdigits_s)/sizeof(val_unsdigits_s[0]),
val_unsdigits_s);

115
src/wx/wxhead.h Normal file
View File

@ -0,0 +1,115 @@
#ifndef WX_WXHEAD_H
#define WX_WXHEAD_H
// For compilers that support precompilation, includes <wx/wx.h>.
#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
// The following are not pulled in by wx.h
// for some reason, mingw32 wx.h doesn't pull in listctrl by default
#include <wx/listctrl.h>
#include <wx/treectrl.h>
#include <wx/xrc/xmlres.h>
#include <wx/config.h>
#include <wx/fileconf.h>
#include <wx/display.h>
#include <wx/stdpaths.h>
// filehistory.h is separate only in 2.9+
#include <wx/docview.h>
#ifndef NO_OGL
// glcanvas must be included before SFML for MacOSX
// originally, this was confined to drawing.h.
#include <wx/glcanvas.h>
// besides that, other crap gets #defined
#ifdef Status
#undef Status
#endif
#ifdef BadRequest
#undef BadRequest
#endif
#endif
// compatibility with MacOSX 10.5
#if !wxCHECK_VERSION(2,8,8)
#define AddFileWithMimeType(a, b, c, m) AddFile(a, b, c)
#define GetItemLabel GetText
#define SetItemLabel SetText
#define GetMenuLabel GetLabelTop
#define GetItemLabelText GetLabel
#endif
// compatibility with wx-2.9
// The only reason I use wxTRANSLATE at all is to get wxT as a side effect.
#if wxCHECK_VERSION(2,9,0)
#undef wxTRANSLATE
#define wxTRANSLATE wxT
#endif
// wxGTK (2.8.8+, at least) doesn't store the actual menu item text in m_text.
// This breaks GetText, SetText, GetAccel, SetAccel, and GetLabel;
// GetItemLabel() works, though.
// GetText, SetText, and GetLabel are deprecated, so that's not a problem
// GetAccel is inefficent anyway (often I don't want to convert to wxAccEnt)
// This is a working replacement for SetAccel, at least.
#include "wx/keyedit.h"
static inline void DoSetAccel(wxMenuItem *mi, wxAcceleratorEntry *acc)
{
wxString lab = mi->GetItemLabel();
size_t tab = lab.find(wxT('\t'));
// following short circuit returns are to avoid UI update on no change
if(tab == wxString::npos && !acc)
return;
wxString accs;
if(acc)
// actually, use keyedit's ToString(), as it is more reliable
// and doesn't generate wx assertions
// accs = acc->ToString();
accs = wxKeyTextCtrl::ToString(acc->GetFlags(), acc->GetKeyCode());
if(tab != wxString::npos && accs == lab.substr(tab + 1))
return;
if(tab != wxString::npos)
lab.resize(tab);
if(acc) {
lab.append(wxT('\t'));
lab.append(accs);
}
mi->SetItemLabel(lab);
}
// wxrc helpers (for dynamic strings instead of constant)
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
//#define XRCCTRL_I(win, id, type) (wxStaticCast((win).FindWindow(id), type))
// XRCCTRL is broken.
// In debug mode, it uses wxDynamicCast, which numerous wx classes fail on
// due to not correctly specifying parents in CLASS() declarations
// In standard mode, it does a static cast, which is unsafe for user input
// So instead I'll always do a (slow, possibly unportable) dynamic_cast().
// If your compiler doesn't support rtti, there are other pieces of code where
// I bypassed wx's stuff to use real dynamic_cast as well, so get a better
// compiler.
#undef XRCCTRL
#define XRCCTRL_I(win, id, type) (dynamic_cast<type *>((win).FindWindow(id)))
#define XRCCTRL(win, id, type) XRCCTRL_I(win, XRCID(id), type)
#define XRCCTRL_D(win, id, type) XRCCTRL_I(win, XRCID_D(id), type)
// wxWidgets provides fn_str(), but no mb_fn_str() or equiv.
#define mb_fn_str() mb_str(wxConvFile)
// by default, only 9 recent items
#define wxID_FILE10 (wxID_FILE9 + 1)
#endif /* WX_WXHEAD_H */

122
src/wx/wxplist.in Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>public.gbarom</string>
<key>UTTypeReferenceURL</key>
<string>http://www.vba-m.com/</string>
<key>UTTypeDescription</key>
<string>GameBoy Advance ROM</string>
<key>UTTypeIconFile</key>
<string>wxvbam.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>agb</string>
<string>agb.gz</string>
<string>agb.z</string>
<string>gba</string>
<string>gba.gz</string>
<string>gba.z</string>
<!-- this is probably too generic
<string>bin</string>
<string>bin.gz</string>
<string>bin.z</string> -->
<!-- slightly less generic, but still..
<string>elf</string>
<string>elf.gz</string>
<string>elf.z</string>
-->
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>public.gbrom</string>
<key>UTTypeReferenceURL</key>
<string>http://www.vba-m.com/</string>
<key>UTTypeDescription</key>
<string>GameBoy ROM</string>
<key>UTTypeIconFile</key>
<string>wxvbam.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gb</string>
<string>gb.gz</string>
<string>gb.z</string>
<string>cgb</string>
<string>cgb.gz</string>
<string>cgb.z</string>
<string>gbc</string>
<string>gbc.gz</string>
<string>gbc.z</string>
<string>sgb</string>
<string>sgb.gz</string>
<string>sgb.z</string>
</array>
</dict>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Cartridge</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.gbarom</string>
<string>public.gbrom</string>
</array>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
</dict>
</plist>

669
src/wx/wxvbam.cpp Normal file
View File

@ -0,0 +1,669 @@
// mainline:
// parse cmd line
// load xrc file (guiinit.cpp does most of instantiation)
// create & display main frame
#include "wxvbam.h"
#include <wx/filesys.h>
#include <wx/fs_mem.h>
#include <wx/fs_arc.h>
#include <wx/file.h>
#include <wx/wfstream.h>
#include <wx/mstream.h>
#include <wx/sstream.h>
#include <wx/cmdline.h>
#include <wx/regex.h>
// The built-in xrc file
#include "builtin-xrc.h"
// The built-in vba-over.ini
#include "builtin-over.h"
IMPLEMENT_APP(wxvbamApp)
IMPLEMENT_DYNAMIC_CLASS(MainFrame, wxFrame)
// generate config file path
static void get_config_path(wxPathList &path, bool exists = true)
{
// local config dir first, then global
// locale-specific res first, then main
wxStandardPathsBase& stdp = wxStandardPaths::Get();
#define add_path(p) do { \
const wxString& s = stdp.p; \
if(!exists || wxDirExists(s)) \
path.Add(s); \
} while(0)
// NOTE: this does not support XDG (freedesktop.org) paths
add_path(GetUserLocalDataDir());
add_path(GetUserDataDir());
add_path(GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()));
add_path(GetResourcesDir());
add_path(GetDataDir());
add_path(GetLocalDataDir());
}
static void tack_full_path(wxString &s, const wxString &app = wxEmptyString)
{
// regenerate path, including nonexistent dirs this time
wxPathList full_config_path;
get_config_path(full_config_path, false);
for(int i = 0; i < full_config_path.size(); i++)
s += wxT("\n\t") + full_config_path[i] + app;
}
bool wxvbamApp::OnInit()
{
// use consistent names for config
SetAppName(_T("wxvbam"));
// load system default locale, if available
locale.Init();
locale.AddCatalog(_T("wxvbam"));
// make built-in xrc file available
// this has to be done before parent OnInit() so xrc dump works
wxFileSystem::AddHandler(new wxMemoryFSHandler);
wxFileSystem::AddHandler(new wxArchiveFSHandler);
wxMemoryFSHandler::AddFileWithMimeType(wxT("wxvbam.xrs"), builtin_xrs, sizeof(builtin_xrs), wxT("application/zip"));
if ( !wxApp::OnInit() )
return false;
// prepare for loading xrc files
wxXmlResource* xr = wxXmlResource::Get();
// note: if linking statically, next 2 pull in lot of unused code
// maybe in future if not wxSHARED, load only builtin-needed handlers
xr->InitAllHandlers();
wxInitAllImageHandlers();
get_config_path(config_path);
// first, load override xrcs
// this can only override entire root nodes
// 2.9 has LoadAllFiles(), but this is 2.8, so we'll do it manually
wxString cwd = wxGetCwd();
for(int i = 0; i < config_path.size(); i++)
if(wxSetWorkingDirectory(config_path[i])) {
// *.xr[cs] doesn't work (double the number of scans)
// 2.9 gives errors for no files found, so manual precheck needed
// (yet another double the number of scans)
if(!wxFindFirstFile(wxT("*.xrc")).empty())
xr->Load(wxT("*.xrc"));
if(!wxFindFirstFile(wxT("*.xrs")).empty())
xr->Load(wxT("*.xrs"));
}
wxSetWorkingDirectory(cwd);
// finally, load built-in xrc
xr->Load(wxT("memory:wxvbam.xrs"));
// set up config file
// this needs to be in a subdir to support other config as well
// but subdir flag behaves differently 2.8 vs. 2.9. Oh well.
// NOTE: this does not support XDG (freedesktop.org) paths
cfg = new wxConfig(wxEmptyString, wxEmptyString, wxEmptyString,
wxEmptyString,
// style =
wxCONFIG_USE_GLOBAL_FILE|wxCONFIG_USE_LOCAL_FILE|
wxCONFIG_USE_SUBDIR);
// set global config for e.g. Windows font mapping
wxConfig::Set(cfg);
// yet another bug/deficiency in wxConfig: dirs are not created if needed
// since a default config is always written, dirs are always needed
// Can't figure out statically if using wxFileConfig w/o duplicating wx's
// logic, so do it at run-time
// wxFileConfig *f = wxDynamicCast(cfg, wxFileConfig);
// wxConfigBase does not derive from wxObject!!! so no wxDynamicCast
wxFileConfig *f = dynamic_cast<wxFileConfig *>(cfg);
if(f) {
wxFileName s(wxFileConfig::GetLocalFileName(GetAppName()));
// at least up to 2.8.12, GetLocalFileName returns the dir if
// SUBDIR is specified instead of actual file name
// and SUBDIR only affects UNIX
#if defined(__UNIX__) && !wxCHECK_VERSION(2,9,0)
s.AppendDir(s.GetFullName());
#endif
// only the path part gets created
// note that 0777 is default (assumes umask will do og-w)
s.Mkdir(0777, wxPATH_MKDIR_FULL);
}
load_opts();
// process command-line options
for(int i = 0; i < pending_optset.size(); i++) {
wxString p = pending_optset[i];
size_t eqat = p.find(wxT('='));
p[eqat] = 0;
opt_set(p.c_str(), p.c_str() + eqat + 1);
}
pending_optset.clear();
// load vba-over.ini
// rather than dealing with wxConfig's broken search path, just use
// the same one that the xrc overrides use
// this also allows us to override a group at a time, add commments, and
// add the file from which the group came
wxMemoryInputStream mis(builtin_over, sizeof(builtin_over));
overrides = new wxFileConfig(mis);
wxRegEx cmtre;
// not the most efficient thing to do: read entire file into a string
// just to parse the comments out
wxString bovs((const char *)builtin_over, wxConvUTF8, sizeof(builtin_over));
bool cont;
wxString s;
long grp_idx;
for(cont = overrides->GetFirstGroup(s, grp_idx); cont;
cont = overrides->GetNextGroup(s, grp_idx)) {
// apparently even MacOSX sometimes uses the old \r by itself
#define CMT_RE_START wxT("(^|[\n\r])# ?([^\n\r]*)(\r?\n|\r)\\[")
wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]");
if(cmtre.Compile(cmt) && cmtre.Matches(bovs))
cmt = cmtre.GetMatch(bovs, 2);
else
cmt = wxEmptyString;
overrides->Write(s + wxT("/comment"), cmt);
}
for(int i = config_path.size() - 1; i >= 0 ; i--) {
wxFileName fn(config_path[i], wxT("vba-over.ini"));
if(!fn.IsFileReadable())
continue;
wxStringOutputStream sos;
wxFileInputStream fis(fn.GetFullPath());
// not the most efficient thing to do: read entire file into a string
// just to parse the comments out
fis.Read(sos);
// rather than assuming the file is seekable, use the string we just
// read as an input stream
wxStringInputStream sis(sos.GetString());
wxFileConfig ov(sis);
for(cont = ov.GetFirstGroup(s, grp_idx); cont;
cont = ov.GetNextGroup(s, grp_idx)) {
overrides->DeleteGroup(s);
overrides->SetPath(s);
ov.SetPath(s);
overrides->Write(wxT("path"), config_path[i]);
// apparently even MacOSX sometimes uses \r by itself
wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]");
if(cmtre.Compile(cmt) && cmtre.Matches(sos.GetString()))
cmt = cmtre.GetMatch(sos.GetString(), 2);
else
cmt = wxEmptyString;
overrides->Write(wxT("comment"), cmt);
long ent_idx;
for(cont = ov.GetFirstEntry(s, ent_idx); cont;
cont = ov.GetNextEntry(s, ent_idx))
overrides->Write(s, ov.Read(s, wxEmptyString));
ov.SetPath(wxT("/"));
overrides->SetPath(wxT("/"));
}
}
// create the main window
frame = wxDynamicCast(xr->LoadFrame(NULL, wxT("MainFrame")), MainFrame);
if(!frame) {
wxLogError(_("Could not create main window"));
return false;
}
// Create() cannot be overridden easily
if(!frame->InitMore())
return false;
frame->Show(true);
return true;
}
void wxvbamApp::OnInitCmdLine(wxCmdLineParser &cl)
{
wxApp::OnInitCmdLine(cl);
cl.SetLogo(wxT("VisualBoyAdvance-M " VERSION "\n"));
// 2.9 decided to change all of these from wxChar to char for some
// reason
#if wxCHECK_VERSION(2,9,0)
// N_(x) returns x
#define t(x) x
#else
// N_(x) returns wxT(x)
#define t(x) wxT(x)
#endif
// while I would rather the long options be translated, there is merit
// to the idea that command-line syntax should not change based on
// locale
static wxCmdLineEntryDesc opttab[] = {
{ wxCMD_LINE_OPTION, NULL, t("save-xrc"),
N_("Save built-in XRC file and exit") },
{ wxCMD_LINE_OPTION, NULL, t("save-over"),
N_("Save built-in vba-over.ini and exit") },
{ wxCMD_LINE_SWITCH, NULL, t("print-cfg-path"),
N_("Print configuration path and exit") },
{ wxCMD_LINE_SWITCH, t("f"), t("fullscreen"),
N_("Start in full-screen mode") },
#if !defined(NO_LINK) && !defined(__WXMSW__)
{ wxCMD_LINE_SWITCH, t("s"), t("delete-shared-state"),
N_("Delete shared link state first, if it exists") },
#endif
// stupid wx cmd line parser doesn't support duplicate options
// { wxCMD_LINE_OPTION, t("o"), t("option"),
// _("Set configuration option; <opt>=<value> or help for list"),
{ wxCMD_LINE_SWITCH, t("o"), t("list-options"),
N_("List all settable options and exit") },
{ wxCMD_LINE_PARAM, NULL, NULL,
N_("ROM file"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_PARAM, NULL, NULL,
N_("<config>=<value>"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE|wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_NONE }
};
// 2.9 automatically translates desc, but 2.8 doesn't
#if !wxCHECK_VERSION(2,9,0)
for(int i = 0; opttab[i].kind != wxCMD_LINE_NONE; i++)
opttab[i].description = wxGetTranslation(opttab[i].description);
#endif
// note that "SetDesc" actually Adds rather than replaces
// so system standard (port-dependent) options are still included:
// -h/--help --verbose --theme --mode
cl.SetDesc(opttab);
}
bool wxvbamApp::OnCmdLineParsed(wxCmdLineParser &cl)
{
if(!wxApp::OnCmdLineParsed(cl))
return false;
wxString s;
if(cl.Found(_("save-xrc"), &s)) {
// This was most likely done on a command line, so use
// stderr instead of gui for messages
wxLog::SetActiveTarget(new wxLogStderr);
wxFileSystem fs;
wxFSFile *f = fs.OpenFile(wxT("memory:wxvbam.xrs#zip:wxvbam.xrs$wxvbam.xrc"));
if(!f) {
wxLogError(_("Configuration/build error: can't find built-in xrc"));
return false;
}
wxFileOutputStream os(s);
os.Write(*f->GetStream());
delete f;
wxString lm;
lm.Printf(_("Wrote built-in configuration to %s.\n"
"To override, remove all but changed root node(s). "
"First found root node of correct name in any .xrc or "
".xrs files in following search path overrides built-in:"),
s.c_str());
tack_full_path(lm);
wxLogMessage(lm);
return false;
}
if(cl.Found(_("print-cfg-path"))) {
// This was most likely done on a command line, so use
// stderr instead of gui for messages
wxLog::SetActiveTarget(new wxLogStderr);
wxString lm(_("Configuration is read from, in order:"));
tack_full_path(lm);
wxLogMessage(lm);
return false;
}
if(cl.Found(_("save-over"), &s)) {
// This was most likely done on a command line, so use
// stderr instead of gui for messages
wxLog::SetActiveTarget(new wxLogStderr);
wxFileOutputStream os(s);
os.Write(builtin_over, sizeof(builtin_over));
wxString lm;
lm.Printf(_("Wrote built-in override file to %s\n"
"To override, delete all but changed section. First found section is used from search path:"), s.c_str());
wxString oi = wxFileName::GetPathSeparator();
oi += wxT("vba-over.ini");
tack_full_path(lm, oi);
lm.append(_("\n\tbuilt-in"));
wxLogMessage(lm);
return false;
}
if(cl.Found(wxT("f"))) {
pending_fullscreen = true;
}
if(cl.Found(wxT("o"))) {
wxPrintf(_("Options set from the command line are saved if any"
" configuration changes are made in the user interface.\n\n"
"For flag options, true and false are specified as 1 and 0, respectively.\n\n"));
for(int i = 0; i < num_opts; i++) {
wxPrintf(wxT("%s (%s"), opts[i].opt,
opts[i].boolopt ? (const wxChar *)_("flag") :
opts[i].stropt ? (const wxChar *)_("string") :
opts[i].enumvals ? opts[i].enumvals :
opts[i].intopt ? (const wxChar *)_("int") :
(const wxChar *)_("string"));
if(opts[i].enumvals) {
const wxChar *evx = wxGetTranslation(opts[i].enumvals);
if(wxStrcmp(evx, opts[i].enumvals))
wxPrintf(wxT(" = %s"), evx);
}
wxPrintf(wxT(")\n\t%s\n\n"), opts[i].desc);
if(opts[i].enumvals)
opts[i].enumvals = wxGetTranslation(opts[i].enumvals);
}
wxPrintf(_("The commands available for the Keyboard/* option are:\n\n"));
for(int i = 0; i < ncmds; i++)
wxPrintf(wxT("%s (%s)\n"), cmdtab[i].cmd, cmdtab[i].name);
return false;
}
#if !defined(NO_LINK) && !defined(__WXMSW__)
if(cl.Found(wxT("s"))) {
CleanLocalLink();
}
#endif
int nparm = cl.GetParamCount();
bool complained = false, gotfile = false;
for(int i = 0; i < nparm; i++) {
wxString p = cl.GetParam(i);
size_t eqat = p.find(wxT('='));
if(eqat != wxString::npos) {
p[eqat] = 0;
if(!opt_set(p.c_str(), p.c_str() + eqat + 1)) {
p[eqat] = wxT('=');
eqat = wxString::npos;
} else
p[eqat] = wxT('=');
pending_optset.push_back(p);
}
if(eqat == wxString::npos) {
if(!gotfile) {
pending_load = p;
gotfile = true;
} else {
if(!complained) {
wxFprintf(stderr, _("Bad configuration option or multiple ROM files given:\n"));
wxFprintf(stderr, wxT("%s\n"), pending_load.c_str());
complained = true;
}
wxFprintf(stderr, wxT("%s\n"), p.c_str());
}
}
}
return true;
}
MainFrame::MainFrame() : wxFrame(), did_link_init(false), focused(false),
paused(false), menus_opened(0), dialog_opened(0) {}
MainFrame::~MainFrame()
{
if(did_link_init)
CloseLink();
}
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
#include "cmd-evtable.h"
EVT_CONTEXT_MENU(MainFrame::OnMenu)
// this is the main window focus? Or EVT_SET_FOCUS/EVT_KILL_FOCUS?
EVT_ACTIVATE(MainFrame::OnActivate)
// requires DragAcceptFiles(true); even then may not do anything
EVT_DROP_FILES(MainFrame::OnDropFile)
// pause game if menu pops up
EVT_MENU_OPEN(MainFrame::MenuPopped)
EVT_MENU_CLOSE(MainFrame::MenuPopped)
END_EVENT_TABLE()
void MainFrame::OnActivate(wxActivateEvent &event)
{
focused = event.GetActive();
if(panel && focused)
panel->SetFocus();
}
void MainFrame::OnDropFile(wxDropFilesEvent &event)
{
int n = event.GetNumberOfFiles();
wxString *f = event.GetFiles();
// ignore all but last
wxGetApp().pending_load = f[event.GetNumberOfFiles() - 1];
}
void MainFrame::OnMenu(wxContextMenuEvent &event)
{
if(IsFullScreen() && ctx_menu) {
wxPoint p(event.GetPosition());
#if 0 // wx actually recommends ignoring the position
if(p != wxDefaultPosition)
p = ScreenToClient(p);
#endif
PopupMenu(ctx_menu, p);
}
}
void MainFrame::SetJoystick()
{
bool anyjoy = false;
joy.Remove();
if(!emulating)
return;
for(int i = 0; i < 4; i++)
for(int j = 0; j < NUM_KEYS; j++) {
wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j];
for(int k = 0; k < b.size(); k++) {
int jn = b[k].joy;
if(jn) {
if(!anyjoy) {
anyjoy = true;
joy.Attach(panel);
}
joy.Add(jn - 1);
}
}
}
}
void MainFrame::enable_menus()
{
for(int i = 0; i < ncmds; i++)
if(cmdtab[i].mask_flags && cmdtab[i].mi)
cmdtab[i].mi->Enable((cmdtab[i].mask_flags & cmd_enable) != 0);
if(cmd_enable & CMDEN_SAVST)
for(int i = 0; i < 10; i++)
if(loadst_mi[i])
loadst_mi[i]->Enable(state_ts[i].IsValid());
}
void MainFrame::SetRecentAccels()
{
for(int i = 0; i <= 10; i++) {
wxMenuItem *mi = recent->FindItem(i + wxID_FILE1);
if(!mi)
break;
// if command is 0, there is no accel
DoSetAccel(mi, recent_accel[i].GetCommand() ? &recent_accel[i] : NULL);
}
}
void MainFrame::update_state_ts(bool force)
{
bool any_states = false;
for(int i = 0; i < 10; i++) {
if(force)
state_ts[i] = wxInvalidDateTime;
if(panel->game_type() != IMAGE_UNKNOWN) {
wxString fn;
fn.Printf(SAVESLOT_FMT, panel->game_name().c_str(), i + 1);
wxFileName fp(panel->state_dir(), fn);
wxDateTime ts; // = wxInvalidDateTime
if(fp.IsFileReadable()) {
ts = fp.GetModificationTime();
any_states = true;
}
// if(ts != state_ts[i] || (force && !ts.IsValid())) {
// to prevent assertions (stupid wx):
if(ts.IsValid() != state_ts[i].IsValid() ||
(ts.IsValid() && ts != state_ts[i]) ||
(force && !ts.IsValid())) {
// wx has no easy way of doing the -- bit independent
// of locale
// so use a real date and substitute all digits
wxDateTime fts = ts.IsValid() ? ts : wxDateTime::Now();
wxString df = fts.Format(wxT("0&0 %x %X"));
if(!ts.IsValid())
for(int j = 0; j < df.size(); j++)
if(wxIsdigit(df[j]))
df[j] = wxT('-');
df[0] = i == 9 ? wxT('1') : wxT(' ');
df[2] = wxT('0') + (i + 1) % 10;
if(loadst_mi[i]) {
wxString lab = loadst_mi[i]->GetItemLabel();
size_t tab = lab.find(wxT('\t')), dflen = df.size();
if(tab != wxString::npos)
df.append(lab.substr(tab));
loadst_mi[i]->SetItemLabel(df);
loadst_mi[i]->Enable(ts.IsValid());
if(tab != wxString::npos)
df.resize(dflen);
}
if(savest_mi[i]) {
wxString lab = savest_mi[i]->GetItemLabel();
size_t tab = lab.find(wxT('\t'));
if(tab != wxString::npos)
df.append(lab.substr(tab));
savest_mi[i]->SetItemLabel(df);
}
}
state_ts[i] = ts;
}
}
int cmd_flg = any_states ? CMDEN_SAVST : 0;
if((cmd_enable & CMDEN_SAVST) != cmd_flg) {
cmd_enable = (cmd_enable & ~CMDEN_SAVST) | cmd_flg;
enable_menus();
}
}
int MainFrame::oldest_state_slot()
{
wxDateTime ot;
int os = -1;
for(int i = 0; i < 10; i++) {
if(!state_ts[i].IsValid())
return i + 1;
if(os < 0 || state_ts[i] < ot) {
os = i;
ot = state_ts[i];
}
}
return os + 1;
}
int MainFrame::newest_state_slot()
{
wxDateTime nt;
int ns = -1;
for(int i = 0; i < 10; i++) {
if(!state_ts[i].IsValid())
continue;
if(ns < 0 || state_ts[i] > nt) {
ns = i;
nt = state_ts[i];
}
}
return ns + 1;
}
// disable emulator loop while menus are popped up
// not sure how to match up w/ down other than counting...
// only msw is guaranteed to only give one up & one down event for entire
// menu browsing
// if there is ever a mismatch, the game will freeze and there is no way
// to detect if it's doing that
// FIXME: this does not work.
// Not all open events are followed by close events.
// Removing the nesting counter may help, but on wxGTK I still get lockups.
void MainFrame::MenuPopped(wxMenuEvent &evt)
{
bool popped = evt.GetEventType() == wxEVT_MENU_OPEN;
#if 0
if(popped)
++menus_opened;
else
--menus_opened;
if(menus_opened < 0) // how could this ever be???
menus_opened = 0;
#else
if(popped)
menus_opened = 1;
else
menus_opened = 0;
#endif
// workaround for lack of wxGTK mouse motion events: unblank
// pointer when menu popped up
// of course this does not help in finding the menus in the first place
// the user is better off clicking in the window or entering/
// exiting the window (which does generate a mouse event)
// it will auto-hide again once game resumes
if(popped)
panel->ShowPointer();
if(menus_opened)
panel->Pause();
else if(!IsPaused())
panel->Resume();
}
// ShowModal that also disables emulator loop
// uses dialog_opened as a nesting counter
int MainFrame::ShowModal(wxDialog *dlg)
{
StartModal();
int ret = dlg->ShowModal();
StopModal();
return ret;
}
void MainFrame::StartModal()
{
// workaround for lack of wxGTK mouse motion events: unblank
// pointer when dialog popped up
// it will auto-hide again once game resumes
panel->ShowPointer();
panel->Pause();
++dialog_opened;
}
void MainFrame::StopModal()
{
if(!dialog_opened) // technically an error in the caller
return;
--dialog_opened;
if(!IsPaused())
panel->Resume();
}
// global event filter
// apparently required for win32; just setting accel table still misses
// a few keys (e.g. only ctrl-x works for exit, but not esc & ctrl-q;
// ctrl-w does not work for close). It's possible another entity is
// grabbing those keys, but I can't track it down.
// FIXME: move this to MainFrame
int wxvbamApp::FilterEvent(wxEvent &event)
{
if(frame && frame->IsPaused(true))
return -1;
if(event.GetEventType() == wxEVT_KEY_DOWN) {
wxKeyEvent &ke = (wxKeyEvent &)event;
for(int i = 0; i < accels.size(); i++) {
if(accels[i].GetKeyCode() == ke.GetKeyCode() &&
accels[i].GetFlags() == ke.GetModifiers()) {
wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, accels[i].GetCommand());
ev.SetEventObject(this);
frame->GetEventHandler()->ProcessEvent(ev);
return 1;
}
}
}
return -1;
}

10
src/wx/wxvbam.desktop Normal file
View File

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Encoding=UTF-8
Type=Application
Name=VBA-M
GenericName=GameBoy Advance Emulator
Comment=Nindendo GameBoy Advance Emulator
Exec=wxvbam
Icon=vbam
Categories=Application;Game;Emulator

566
src/wx/wxvbam.h Normal file
View File

@ -0,0 +1,566 @@
#ifndef WX_WXVBAM_H
#define WX_WXVBAM_H
#include "wxhead.h"
#include "wx/sdljoy.h"
#include "wx/joyedit.h"
#include "wx/keyedit.h"
#include "wx/wxmisc.h"
#ifndef NO_FFMPEG
#include "../common/ffmpeg.h"
#endif
#include <list>
/* yeah, they aren't needed globally, but I'm too lazy to limit where needed */
#include "../System.h"
#include "../gba/Globals.h"
#include "../gb/gbGlobals.h"
#include "../gb/gbSound.h"
#include "../gb/gb.h"
#include "../gba/Sound.h"
#include "../Util.h"
#include "../gba/GBALink.h"
#include "../gb/gbCheats.h"
#include "../gba/Cheats.h"
class MainFrame;
class wxvbamApp : public wxApp
{
public:
wxvbamApp() : wxApp(), pending_fullscreen(false) {}
virtual bool OnInit();
virtual void OnInitCmdLine(wxCmdLineParser &);
virtual bool OnCmdLineParsed(wxCmdLineParser &);
// name of a file to load at earliest opportunity
wxString pending_load;
// list of options to set after config file loaded
wxArrayString pending_optset;
// set fullscreen mode after init
bool pending_fullscreen;
#if __WXMAC__
// I suppose making this work will require tweaking the bundle
void MacOpenFile(const wxString&f) { pending_load = f; };
#endif
// without this, global accels don't always work
int FilterEvent(wxEvent &event);
wxAcceleratorEntry_v accels;
// the main configuration
wxConfig *cfg;
// vba-over.ini
wxFileConfig *overrides;
MainFrame *frame;
// use this to get ms since program lauch
wxStopWatch timer;
// append log messages to this for the log viewer
wxString log;
// there's no way to retrieve "current" locale, so this is public
wxLocale locale;
private:
wxPathList config_path;
};
DECLARE_APP(wxvbamApp);
// menu event handlers are declared with these macros in mainwind.cpp
// and mainwind-handlers.h, mainwind-cmd.cpp, and mainwind-evt.cpp are
// auto-generated from them
#define EVT_HANDLER(n, x) \
void MainFrame::On##n(wxCommandEvent& WXUNUSED(event))
// some commands are masked under some conditions
#define EVT_HANDLER_MASK(n, x, m) \
void MainFrame::On##n(wxCommandEvent& WXUNUSED(event)) \
{ \
if(!(cmd_enable & (m))) \
return; \
Do##n(); \
} \
void MainFrame::Do##n() \
// here are those conditions
enum {
CMDEN_GB = (1<<0), // GB ROM loaded
CMDEN_GBA = (1<<1), // GBA ROM loaded
// the rest imply the above, unless:
// _ANY -> does not imply either
// _GBA -> only implies GBA
CMDEN_REWIND = (1<<2), // rewind states available
CMDEN_SREC = (1<<3), // sound recording in progress
CMDEN_NSREC = (1<<4), // no sound recording
CMDEN_VREC = (1<<5), // video recording
CMDEN_NVREC = (1<<6), // no video recording
CMDEN_GREC = (1<<7), // game recording
CMDEN_NGREC = (1<<8), // no game recording
CMDEN_GPLAY = (1<<9), // game playback
CMDEN_NGPLAY = (1<<10), // no game playback
CMDEN_SAVST = (1<<11), // any save states
CMDEN_GDB = (1<<12), // gdb connected
CMDEN_NGDB_GBA = (1<<13), // gdb not connected
CMDEN_NGDB_ANY = (1<<14), // gdb not connected
CMDEN_NREC_ANY = (1<<15), // not a/v recording
CMDEN_LINK_ANY = (1<<16), // link enabled
CMDEN_NEVER = (1<<31) // never (for NOOP)
};
#define ONLOAD_CMDEN (CMDEN_NSREC|CMDEN_NVREC|CMDEN_NGREC|CMDEN_NGPLAY)
#define UNLOAD_CMDEN_KEEP (CMDEN_NGDB_ANY|CMDEN_NREC_ANY|CMDEN_LINK_ANY)
struct checkable_mi_t {
int cmd;
wxMenuItem *mi;
bool *boolopt;
int *intopt, mask, val;
};
// wxArray is only for small types
typedef std::vector<checkable_mi_t> checkable_mi_array_t;
typedef std::list<wxDialog *> dialog_list_t;
class GameArea;
class LogDialog;
// true if pause should happen at next frame
extern bool pause_next;
class MainFrame : public wxFrame
{
public:
MainFrame();
~MainFrame();
bool InitMore(void);
void SetJoystick(void);
GameArea *GetPanel() { return panel; }
// wxMSW pauses the game for menu popups and modal dialogs, but wxGTK
// does not. It's probably desirable to pause the game. To do this for
// dialogs, use this function instead of dlg->ShowModal()
int ShowModal(wxDialog *dlg);
// and here are the wrapper functions for use when ShowModal() isn't
// possible
void StartModal();
void StopModal();
// however, adding a handler for open/close menu to do the same is unsafe.
// there is no guarantee every show will be matched by a hide.
void MenuPopped(wxMenuEvent &evt);
// flags for enabling commands
int cmd_enable;
// adjust menus based on current cmd_enable
void enable_menus();
// adjust menus based on available save game states
void update_state_ts(bool force = false);
// retrieve oldest/newest slot
// returns lowest-numbered slot on ties
int oldest_state_slot(); // or empty slot if available
int newest_state_slot(); // or 0 if none
// system-defined accelerators
wxAcceleratorEntry_v sys_accels;
// adjust recent menu with accelerators
void SetRecentAccels();
// merge sys accels with user-defined accels (user overrides sys)
wxAcceleratorEntry_v get_accels(wxAcceleratorEntry_v user_accels);
// update menu and global accelerator table with sys+user accel defs
// probably not the quickest way to add/remove individual accels
void set_global_accels();
// 2.8 has no HasFocus(), and FindFocus() doesn't work right
bool HasFocus() { return focused; }
// The various viewer popups; these can be popped up as often as
// desired
void Disassemble();
void IOViewer();
void MapViewer();
void MemViewer();
void OAMViewer();
void PaletteViewer();
void TileViewer();
// since they will all have to be destroyed on game unload:
dialog_list_t popups;
// this won't actually be destroyed, but it needs to be tracked so only
// one is ever up and it needs to be pinged when new messages arrive
LogDialog *logdlg;
// the cheat search dialog isn't destroyed or tracked, but it needs
// to be cleared between games
void ResetCheatSearch();
// call this to update the viewers once a frame:
void UpdateViewers();
// required for building from xrc
DECLARE_DYNAMIC_CLASS();
// required for event handling
DECLARE_EVENT_TABLE();
private:
bool did_link_init;
GameArea *panel;
// the various reasons the game might be paused
bool paused;
int menus_opened, dialog_opened;
public:
bool IsPaused(bool incendental = false)
{
return (paused && !pause_next && !incendental) || menus_opened || dialog_opened;
}
private:
bool autoLoadMostRecent;
// copy of top-level menu bar as a context menu
wxMenu *ctx_menu;
// load/save states menu items
wxMenuItem *loadst_mi[10];
wxMenuItem *savest_mi[10];
wxDateTime state_ts[10];
// checkable menu items
checkable_mi_array_t checkable_mi;
// recent menu item accels
wxMenu *recent;
wxAcceleratorEntry recent_accel[10];
// joystick reader
wxSDLJoy joy;
// helper function for adding menu to accel editor
void add_menu_accels(wxTreeCtrl *tc, wxTreeItemId &parent, wxMenu *menu);
// for detecting window focus
void OnActivate(wxActivateEvent&);
// quicker & more accurate than FindFocus() != NULL
bool focused;
// may work, may not... if so, load dropped file
void OnDropFile(wxDropFilesEvent&);
// pop up menu in fullscreen mode
void OnMenu(wxContextMenuEvent &);
#include "cmdhandlers.h"
};
// a helper class to avoid forgetting StopModal()
class ModalPause
{
public:
ModalPause() { wxGetApp().frame->StartModal(); }
~ModalPause() { wxGetApp().frame->StopModal(); }
};
enum showspeed {
// this order must match order of option enum and selector widget
SS_NONE, SS_PERCENT, SS_DETAILED
};
enum filtfunc {
// this order must match order of option enum and selector widget
FF_NONE, FF_2XSAI, FF_SUPER2XSAI, FF_SUPEREAGLE, FF_PIXELATE,
FF_ADVMAME, FF_BILINEAR, FF_BILINEARPLUS, FF_SCANLINES, FF_TV,
FF_HQ2X, FF_LQ2X, FF_SIMPLE2X, FF_SIMPLE3X, FF_HQ3X, FF_SIMPLE4X,
FF_HQ4X, FF_PLUGIN // plugin must always be last
};
#define builtin_ff_scale(x) \
((x == FF_HQ4X || x == FF_SIMPLE4X) ? 4 : \
(x == FF_HQ3X || x == FF_SIMPLE3X) ? 3 : \
x == FF_PLUGIN ? 0 : x == FF_NONE ? 1 : 2)
enum ifbfunc {
// this order must match order of option enum and selector widget
IFB_NONE, IFB_SMART, IFB_MOTION_BLUR
};
// make sure and keep this in sync with opts.cpp!
enum renderer {
RND_SIMPLE, RND_OPENGL, RND_CAIRO, RND_DIRECT3D
};
// likewise
enum audioapi {
AUD_SDL, AUD_OPENAL, AUD_DIRECTSOUND, AUD_XAUDIO2
};
// an unfortunate legacy default; should have a non-digit preceding %d
// the only reason to keep it is that user can set slotdir to old dir
// otoh, we already make some name changes (double ext strip), and
// rename is not that hard (esp. under UNIX), so I'm going ahead with a new
// name.
//#define SAVESLOT_FMT wxT("%s%d.sgm")
#define SAVESLOT_FMT wxT("%s-%02d.sgm")
// display time, in ms
#define OSD_TIME 3000
class DrawingPanel;
class GameArea : public wxPanel
{
public:
GameArea();
virtual ~GameArea();
// set to game title + link info
void SetFrameTitle();
void LoadGame(const wxString &name);
void UnloadGame(bool destruct = false);
IMAGE_TYPE game_type() { return loaded; }
u32 game_size() { return rom_size; }
wxString game_dir() { return loaded_game.GetPath(); }
wxString game_name() { return loaded_game.GetFullName(); }
wxString bat_dir() { return batdir; }
wxString state_dir() { return statedir; }
void recompute_dirs();
bool LoadState();
bool LoadState(int slot);
bool LoadState(const wxFileName &fname);
bool SaveState();
bool SaveState(int slot);
bool SaveState(const wxFileName &fname);
// save to default location
void SaveBattery(bool quiet = false);
// true if file at default location may not match memory
bool cheats_dirty;
static const int GBWidth = 160, GBHeight = 144,
SGBWidth = 256, SGBHeight = 224,
GBAWidth = 240, GBAHeight = 160;
void AddBorder();
void DelBorder();
// Delete() & set to NULL to force reinit
DrawingPanel *panel;
struct EmulatedSystem *emusys;
// pause game or signal a long operation, similar to pausing
void Pause();
void Resume();
// true if paused since last reset of flag
bool was_paused;
// osdstat is always displayed at top-left of screen
wxString osdstat;
// osdtext is displayed for 3 seconds after osdtime, and then cleared
wxString osdtext;
u32 osdtime;
// Rewind: count down to 0 and rewind
u32 rewind_time;
// Rewind: flag to OnIdle to do a rewind
bool do_rewind;
// Rewind: rewind states
char *rewind_mem; // should be u8, really
int num_rewind_states;
int next_rewind_state;
// FIXME: size this properly
#define REWIND_SIZE 400000
// FIXME: make this a config option
#define NUM_REWINDS 8
void ShowFullScreen(bool full);
bool IsFullScreen() { return fullscreen; }
// set size of frame & panel to scaled screen size
void AdjustSize(bool force);
#ifndef NO_FFMPEG
void StartSoundRecording(const wxString &fname);
void StopSoundRecording();
void StartVidRecording(const wxString &fname);
void StopVidRecording();
void AddFrame(const u8 *data); // video
void AddFrame(const u16 *data, int length); // audio
bool IsRecording() { return snd_rec.IsRecording() || vid_rec.IsRecording(); }
#endif
void StartGameRecording(const wxString &fname);
void StopGameRecording();
void StartGamePlayback(const wxString &fname);
void StopGamePlayback();
protected:
// set minsize of frame & panel to unscaled screen size
void LowerMinSize();
// set minsize of frame & panel to scaled screen size
void AdjustMinSize();
IMAGE_TYPE loaded;
u32 rom_size;
wxFileName loaded_game;
wxString batdir, statedir;
int basic_width, basic_height;
bool fullscreen;
bool paused;
void OnIdle(wxIdleEvent &);
void OnKeyDown(wxKeyEvent &ev);
void OnKeyUp(wxKeyEvent &ev);
void OnSDLJoy(wxSDLJoyEvent &ev);
#ifndef NO_FFMPEG
MediaRecorder snd_rec, vid_rec;
#endif
public:
void ShowPointer();
void HidePointer();
protected:
void MouseEvent(wxMouseEvent &) { ShowPointer(); }
bool pointer_blanked;
u32 mouse_active_time;
DECLARE_DYNAMIC_CLASS()
DECLARE_EVENT_TABLE()
};
// wxString version of OSD message
void systemScreenMessage(const wxString &msg);
// List of all commands with their descriptions
// sorted by cmd field for binary searching
// filled in by copy-events.cmake
extern struct cmditem {
const wxChar *cmd, *name;
int cmd_id;
int mask_flags; // if non-0, one of the flags must be turned on in win
// to enable this command
wxMenuItem *mi; // the menu item to invoke command, if present
} cmdtab[];
extern const int ncmds;
// for binary search
extern bool cmditem_lt(const struct cmditem &cmd1, const struct cmditem &cmd2);
#include <wx/dynlib.h>
#include "../win32/rpi.h"
class FilterThread;
class DrawingPanel : public wxObject
{
public:
DrawingPanel(int _width, int _height);
~DrawingPanel();
void DrawArea(u8 **pixels);
// the following wouldn't be necessary with virtual inheritance
virtual wxWindow *GetWindow() = 0;
virtual void Delete() = 0;
protected:
virtual void DrawArea(wxWindowDC&) = 0;
virtual void DrawOSD(wxWindowDC&);
int width, height, scale;
u8 *todraw;
u8 *pixbuf1, *pixbuf2;
FilterThread *threads;
int nthreads;
wxSemaphore filt_done;
wxDynamicLibrary filt_plugin;
const RENDER_PLUGIN_INFO *rpi; // also flag indicating plugin loaded
// largest buffer required is 32-bit * (max width + 1) * (max height + 2)
u8 delta[257 * 4 * 226];
// following can't work in 2.8 as intended
// inheriting from wxEvtHandler is required, but also breaks subclasses
// due to lack of virtual inheritance (2.9 drops wxEvtHandler req)
// so each child must have a paint event handler (not override of this,
// but it's not virtual anyway, so that won't happen) that calls this
void PaintEv(wxPaintEvent &ev);
DECLARE_ABSTRACT_CLASS()
};
class LogDialog : public wxDialog
{
public:
LogDialog();
void Update();
private:
wxTextCtrl *log;
void Save(wxCommandEvent &ev);
void Clear(wxCommandEvent &ev);
DECLARE_EVENT_TABLE()
};
#include "opts.h"
class SoundDriver;
extern SoundDriver *newOpenAL();
// I should add this to SoundDriver, but wxArrayString is wx-specific
// I suppose I could make subclass wxSoundDriver. maybe later.
extern bool GetOALDevices(wxArrayString &names, wxArrayString &ids);
#ifdef __WXMSW__
extern SoundDriver *newDirectSound();
extern bool GetDSDevices(wxArrayString &names, wxArrayString &ids);
extern SoundDriver *newXAudio2_Output();
extern bool GetXA2Devices(wxArrayString &names, wxArrayString &ids);
#endif
extern bool debugger;
extern void (*dbgMain)();
extern void (*dbgSignal)(int, int);
extern void (*dbgOutput)(const char *, u32);
extern void remoteStubMain();
extern void remoteCleanUp();
extern void remoteStubSignal(int, int);
extern void remoteOutput(const char *, u32);
extern bool debugOpenPty();
extern const wxString &debugGetSlavePty();
extern bool debugWaitPty();
extern bool debugStartListen(int port);
extern bool debugWaitSocket();
// perhaps these functions should not be called systemXXX
// perhaps they should move to panel.cpp/GameArea
// but they must integrate with systemReadJoypad
void systemStartGameRecording(const wxString &fname);
void systemStopGameRecording();
void systemStartGamePlayback(const wxString &fname);
void systemStopGamePlayback();
// true if turbo mode (like pressing turbo button constantly)
extern bool turbo;
// mask of key press flags; see below
extern int joypress[4], autofire;
// FIXME: these defines should be global to project and used instead of raw numbers
#define KEYM_A (1<<0)
#define KEYM_B (1<<1)
#define KEYM_SELECT (1<<2)
#define KEYM_START (1<<3)
#define KEYM_RIGHT (1<<4)
#define KEYM_LEFT (1<<5)
#define KEYM_UP (1<<6)
#define KEYM_DOWN (1<<7)
#define KEYM_R (1<<8)
#define KEYM_L (1<<9)
#define KEYM_SPEED (1<<10)
#define KEYM_CAPTURE (1<<11)
#define KEYM_GS (1<<12)
// actually, the wx port adds the following, which could be local:
#define REALKEY_MASK ((1<<13)-1)
#define KEYM_AUTO_A (1<<13)
#define KEYM_AUTO_B (1<<14)
#define KEYM_MOTION_UP (1<<15)
#define KEYM_MOTION_DOWN (1<<16)
#define KEYM_MOTION_LEFT (1<<17)
#define KEYM_MOTION_RIGHT (1<<18)
#include "filters.h"
#endif /* WX_WXVBAM_H */

42
src/wx/wxvbam.rc Normal file
View File

@ -0,0 +1,42 @@
IDI_MAINICON ICON "../win32/res/VBA.ico"
#include "wx/msw/wx.rc"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "VBA-M comes with NO WARRANTY. Use it at your own risk."
VALUE "CompanyName", "http://vba-m.com/"
VALUE "FileDescription", "VisualBoyAdvance-M"
VALUE "FileVersion", "1, 8, 0, 0"
VALUE "InternalName", "VBA-M"
VALUE "LegalCopyright", "Copyright © 2008-2009 VBA-M development team"
VALUE "OriginalFilename", "VisualBoyAdvance-M.exe"
VALUE "ProductName", "GB/C/A emulator for Windows"
VALUE "ProductVersion", "1, 8, 0, 0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

6503
src/wx/wxvbam.xrc Normal file

File diff suppressed because it is too large Load Diff

439
src/wx/xaudio2.cpp Normal file
View File

@ -0,0 +1,439 @@
#ifndef NO_XAUDIO2
// Application
#include "wxvbam.h"
#include <stdio.h>
// Interface
#include "../common/SoundDriver.h"
// XAudio2
#include <XAudio2.h>
// Internals
#include "../System.h" // for systemMessage()
#include "../gba/Globals.h"
// Synchronization Event
class XAudio2_BufferNotify : public IXAudio2VoiceCallback
{
public:
HANDLE hBufferEndEvent;
XAudio2_BufferNotify() {
hBufferEndEvent = NULL;
hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
assert( hBufferEndEvent != NULL );
}
~XAudio2_BufferNotify() {
CloseHandle( hBufferEndEvent );
hBufferEndEvent = NULL;
}
STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) {
assert( hBufferEndEvent != NULL );
SetEvent( hBufferEndEvent );
}
// dummies:
STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {}
STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {}
STDMETHOD_( void, OnStreamEnd ) () {}
STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {}
STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {}
STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {};
};
// Class Declaration
class XAudio2_Output
: public SoundDriver
{
public:
XAudio2_Output();
~XAudio2_Output();
// Initialization
bool init(long sampleRate);
// Sound Data Feed
void write(u16 * finalWave, int length);
// Play Control
void pause();
void resume();
void reset();
// Configuration Changes
void setThrottle( unsigned short throttle );
private:
bool failed;
bool initialized;
bool playing;
UINT32 freq;
UINT32 bufferCount;
BYTE *buffers;
int currentBuffer;
int soundBufferLen;
IXAudio2 *xaud;
IXAudio2MasteringVoice *mVoice; // listener
IXAudio2SourceVoice *sVoice; // sound source
XAUDIO2_BUFFER buf;
XAUDIO2_VOICE_STATE vState;
XAudio2_BufferNotify notify; // buffer end notification
};
// Class Implementation
XAudio2_Output::XAudio2_Output()
{
failed = false;
initialized = false;
playing = false;
freq = 0;
bufferCount = gopts.audio_buffers;
buffers = NULL;
currentBuffer = 0;
xaud = NULL;
mVoice = NULL;
sVoice = NULL;
ZeroMemory( &buf, sizeof( buf ) );
ZeroMemory( &vState, sizeof( vState ) );
}
XAudio2_Output::~XAudio2_Output()
{
initialized = false;
if( sVoice ) {
if( playing ) {
HRESULT hr = sVoice->Stop( 0 );
assert( hr == S_OK );
}
sVoice->DestroyVoice();
}
if( buffers ) {
free( buffers );
buffers = NULL;
}
if( mVoice ) {
mVoice->DestroyVoice();
}
if( xaud ) {
xaud->Release();
xaud = NULL;
}
}
static int XA2GetDev(IXAudio2 *);
bool XAudio2_Output::init(long sampleRate)
{
if( failed || initialized ) return false;
HRESULT hr;
// Initialize XAudio2
UINT32 flags = 0;
#ifdef _DEBUG
flags = XAUDIO2_DEBUG_ENGINE;
#endif
hr = XAudio2Create( &xaud, flags );
if( hr != S_OK ) {
wxLogError( _("The XAudio2 interface failed to initialize!") );
failed = true;
return false;
}
freq = sampleRate;
// calculate the number of samples per frame first
// then multiply it with the size of a sample frame (16 bit * stereo)
soundBufferLen = ( freq / 60 ) * 4;
// create own buffers to store sound data because it must not be
// manipulated while the voice plays from it
buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen );
// + 1 because we need one temporary buffer when all others are in use
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof( wfx ) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = freq;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 );
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
// create sound receiver
hr = xaud->CreateMasteringVoice(
&mVoice,
XAUDIO2_DEFAULT_CHANNELS,
XAUDIO2_DEFAULT_SAMPLERATE,
0,
XA2GetDev(xaud),
NULL );
if( hr != S_OK ) {
wxLogError( _("XAudio2: Creating mastering voice failed!") );
failed = true;
return false;
}
// create sound emitter
hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, &notify );
if( hr != S_OK ) {
wxLogError( _("XAudio2: Creating source voice failed!") );
failed = true;
return false;
}
if( gopts.upmix ) {
// set up stereo upmixing
XAUDIO2_DEVICE_DETAILS dd;
ZeroMemory( &dd, sizeof( dd ) );
hr = xaud->GetDeviceDetails( 0, &dd );
assert( hr == S_OK );
float *matrix = NULL;
matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels );
if( matrix == NULL ) return false;
bool matrixAvailable = true;
switch( dd.OutputFormat.Format.nChannels ) {
case 4: // 4.0
//Speaker \ Left Source Right Source
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
/*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f;
/*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f;
break;
case 5: // 5.0
//Speaker \ Left Source Right Source
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
/*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f;
/*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f;
break;
case 6: // 5.1
//Speaker \ Left Source Right Source
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
/*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
/*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
break;
case 7: // 6.1
//Speaker \ Left Source Right Source
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
/*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
/*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
/*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f;
break;
case 8: // 7.1
//Speaker \ Left Source Right Source
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
/*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
/*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
/*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f;
/*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f;
break;
default:
matrixAvailable = false;
break;
}
if( matrixAvailable ) {
hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix );
assert( hr == S_OK );
}
free( matrix );
matrix = NULL;
}
hr = sVoice->Start( 0 );
assert( hr == S_OK );
playing = true;
initialized = true;
return true;
}
void XAudio2_Output::write(u16 * finalWave, int length)
{
if( !initialized || failed ) return;
while( true ) {
sVoice->GetState( &vState );
assert( vState.BuffersQueued <= bufferCount );
if( vState.BuffersQueued < bufferCount ) {
if( vState.BuffersQueued == 0 ) {
// buffers ran dry
if( systemVerbose & VERBOSE_SOUNDOUTPUT ) {
static unsigned int i = 0;
log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ );
}
}
// there is at least one free buffer
break;
} else {
// the maximum number of buffers is currently queued
if( synchronize && !speedup && !gopts.throttle ) {
// wait for one buffer to finish playing
WaitForSingleObject( notify.hBufferEndEvent, INFINITE );
} else {
// drop current audio frame
return;
}
}
}
// copy & protect the audio data in own memory area while playing it
CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen );
buf.AudioBytes = soundBufferLen;
buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ];
currentBuffer++;
currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer
HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue
assert( hr == S_OK );
}
void XAudio2_Output::pause()
{
if( !initialized || failed ) return;
if( playing ) {
HRESULT hr = sVoice->Stop( 0 );
assert( hr == S_OK );
playing = false;
}
}
void XAudio2_Output::resume()
{
if( !initialized || failed ) return;
if( !playing ) {
HRESULT hr = sVoice->Start( 0 );
assert( hr == S_OK );
playing = true;
}
}
void XAudio2_Output::reset()
{
if( !initialized || failed ) return;
if( playing ) {
HRESULT hr = sVoice->Stop( 0 );
assert( hr == S_OK );
}
sVoice->FlushSourceBuffers();
sVoice->Start( 0 );
playing = true;
}
void XAudio2_Output::setThrottle( unsigned short throttle )
{
if( !initialized || failed ) return;
if( throttle == 0 ) throttle = 100;
HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f );
assert( hr == S_OK );
}
SoundDriver *newXAudio2_Output()
{
return new XAudio2_Output();
}
int GetXA2Devices(IXAudio2 *xa, wxArrayString *names, wxArrayString *ids,
const wxString *match)
{
HRESULT hr;
UINT32 dev_count = 0;
hr = xa->GetDeviceCount( &dev_count );
if( hr != S_OK ) {
wxLogError(_("XAudio2: Enumerating devices failed!"));
return true;
} else {
XAUDIO2_DEVICE_DETAILS dd;
for( UINT32 i = 0; i < dev_count; i++ ) {
hr = xa->GetDeviceDetails( i, &dd );
if( hr != S_OK ) {
continue;
} else {
if(ids) {
ids->push_back(dd.DeviceID);
names->push_back( dd.DisplayName );
} else if(*match == dd.DeviceID)
return i;
}
}
}
return -1;
}
bool GetXA2Devices(wxArrayString &names, wxArrayString &ids)
{
HRESULT hr;
IXAudio2 *xa = NULL;
UINT32 flags = 0;
#ifdef _DEBUG
flags = XAUDIO2_DEBUG_ENGINE;
#endif
hr = XAudio2Create( &xa, flags );
if( hr != S_OK ) {
wxLogError( _("The XAudio2 interface failed to initialize!") );
return false;
}
GetXA2Devices(xa, &names, &ids, NULL);
xa->Release();
return true;
}
static int XA2GetDev(IXAudio2 *xa)
{
if(gopts.audio_dev.empty())
return 0;
else {
int ret = GetXA2Devices(xa, NULL, NULL, &gopts.audio_dev);
return ret < 0 ? 0 : ret;
}
}
#endif // #ifndef NO_XAUDIO2