From 03e14584f6490d72246550df3da3c793028fff87 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 29 Oct 2021 17:14:40 +0000 Subject: [PATCH] re-add gtk+2 frontend in gtk2 directory --- desmume/src/frontend/posix/gtk2/DeSmuME.xpm | 50 + desmume/src/frontend/posix/gtk2/Info.plist | 20 + desmume/src/frontend/posix/gtk2/Makefile.am | 46 + desmume/src/frontend/posix/gtk2/avout.h | 32 + .../src/frontend/posix/gtk2/avout_flac.cpp | 61 + desmume/src/frontend/posix/gtk2/avout_flac.h | 34 + .../frontend/posix/gtk2/avout_pipe_base.cpp | 102 + .../src/frontend/posix/gtk2/avout_pipe_base.h | 39 + .../src/frontend/posix/gtk2/avout_x264.cpp | 47 + desmume/src/frontend/posix/gtk2/avout_x264.h | 34 + desmume/src/frontend/posix/gtk2/cheatsGTK.cpp | 440 ++ desmume/src/frontend/posix/gtk2/cheatsGTK.h | 29 + desmume/src/frontend/posix/gtk2/check | 9 + desmume/src/frontend/posix/gtk2/config.cpp | 166 + desmume/src/frontend/posix/gtk2/config.h | 113 + desmume/src/frontend/posix/gtk2/config_opts.h | 74 + desmume/src/frontend/posix/gtk2/dTool.h | 20 + .../src/frontend/posix/gtk2/dToolsList.cpp | 33 + desmume/src/frontend/posix/gtk2/desmume.cpp | 74 + .../src/frontend/posix/gtk2/desmume.desktop | 10 + desmume/src/frontend/posix/gtk2/desmume.h | 36 + .../src/frontend/posix/gtk2/doc/Makefile.am | 1 + desmume/src/frontend/posix/gtk2/doc/desmume.1 | 169 + desmume/src/frontend/posix/gtk2/dylibbundler | Bin 0 -> 72920 bytes .../gtk2/dylibbundler.modified.Settings.cpp | 94 + desmume/src/frontend/posix/gtk2/fixups | 9 + desmume/src/frontend/posix/gtk2/main.cpp | 3668 +++++++++++++++++ desmume/src/frontend/posix/gtk2/main.h | 7 + desmume/src/frontend/posix/gtk2/meson.build | 40 + .../gtk2/org.desmume.DeSmuME.metainfo.xml | 37 + .../src/frontend/posix/gtk2/osmesa_3Demu.cpp | 112 + .../src/frontend/posix/gtk2/osmesa_3Demu.h | 27 + desmume/src/frontend/posix/gtk2/osxbuild | 11 + desmume/src/frontend/posix/gtk2/sdl_3Demu.cpp | 87 + desmume/src/frontend/posix/gtk2/sdl_3Demu.h | 26 + .../frontend/posix/gtk2/tools/ioregsView.cpp | 492 +++ .../frontend/posix/gtk2/tools/ioregsView.h | 29 + 37 files changed, 6278 insertions(+) create mode 100644 desmume/src/frontend/posix/gtk2/DeSmuME.xpm create mode 100644 desmume/src/frontend/posix/gtk2/Info.plist create mode 100644 desmume/src/frontend/posix/gtk2/Makefile.am create mode 100644 desmume/src/frontend/posix/gtk2/avout.h create mode 100644 desmume/src/frontend/posix/gtk2/avout_flac.cpp create mode 100644 desmume/src/frontend/posix/gtk2/avout_flac.h create mode 100644 desmume/src/frontend/posix/gtk2/avout_pipe_base.cpp create mode 100644 desmume/src/frontend/posix/gtk2/avout_pipe_base.h create mode 100644 desmume/src/frontend/posix/gtk2/avout_x264.cpp create mode 100644 desmume/src/frontend/posix/gtk2/avout_x264.h create mode 100644 desmume/src/frontend/posix/gtk2/cheatsGTK.cpp create mode 100644 desmume/src/frontend/posix/gtk2/cheatsGTK.h create mode 100755 desmume/src/frontend/posix/gtk2/check create mode 100644 desmume/src/frontend/posix/gtk2/config.cpp create mode 100644 desmume/src/frontend/posix/gtk2/config.h create mode 100644 desmume/src/frontend/posix/gtk2/config_opts.h create mode 100644 desmume/src/frontend/posix/gtk2/dTool.h create mode 100644 desmume/src/frontend/posix/gtk2/dToolsList.cpp create mode 100644 desmume/src/frontend/posix/gtk2/desmume.cpp create mode 100644 desmume/src/frontend/posix/gtk2/desmume.desktop create mode 100644 desmume/src/frontend/posix/gtk2/desmume.h create mode 100644 desmume/src/frontend/posix/gtk2/doc/Makefile.am create mode 100644 desmume/src/frontend/posix/gtk2/doc/desmume.1 create mode 100755 desmume/src/frontend/posix/gtk2/dylibbundler create mode 100644 desmume/src/frontend/posix/gtk2/dylibbundler.modified.Settings.cpp create mode 100755 desmume/src/frontend/posix/gtk2/fixups create mode 100644 desmume/src/frontend/posix/gtk2/main.cpp create mode 100644 desmume/src/frontend/posix/gtk2/main.h create mode 100644 desmume/src/frontend/posix/gtk2/meson.build create mode 100644 desmume/src/frontend/posix/gtk2/org.desmume.DeSmuME.metainfo.xml create mode 100644 desmume/src/frontend/posix/gtk2/osmesa_3Demu.cpp create mode 100644 desmume/src/frontend/posix/gtk2/osmesa_3Demu.h create mode 100755 desmume/src/frontend/posix/gtk2/osxbuild create mode 100644 desmume/src/frontend/posix/gtk2/sdl_3Demu.cpp create mode 100644 desmume/src/frontend/posix/gtk2/sdl_3Demu.h create mode 100644 desmume/src/frontend/posix/gtk2/tools/ioregsView.cpp create mode 100644 desmume/src/frontend/posix/gtk2/tools/ioregsView.h diff --git a/desmume/src/frontend/posix/gtk2/DeSmuME.xpm b/desmume/src/frontend/posix/gtk2/DeSmuME.xpm new file mode 100644 index 000000000..d1ce27621 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/DeSmuME.xpm @@ -0,0 +1,50 @@ +/* XPM */ +static const char * DeSmuME_xpm[] = { +"32 32 15 1", +" c None", +". c #000000", +"+ c #F6F6FB", +"@ c #ECECF6", +"# c #E2E2F1", +"$ c #D8D8EC", +"% c #CFCFE7", +"& c #C5C5E2", +"* c #BBBBDE", +"= c #B1B1D9", +"- c #A8A8D4", +"; c #9E9ECF", +"> c #9494CA", +", c #8A8AC5", +"' c #8080C0", +" .................... ", +" .................... ", +".....++..+++++..+++++.. ", +".....++..++.....++..... ", +"..@@@@@..@@@@@..@@@@@.. ", +"..@@.@@..@@........@@.. ", +"..##.##..##.##..##.##.. ", +"..##.##..##.##..##.##.. ", +"..$$.$$..$$.$$..$$.$$.. ", +"..$$.$$..$$.$$..$$.$$.. ", +"..%%.%%..%%.%%..%%.%%.. ", +"..%%.%%..%%.%%..%%.%%.. ", +"..&&&&&..&&&&&..&&&&&.. ", +"....................... ", +".......********.**.**.. ", +" ..==.==.==.==.==.. ", +" ..==.==.==.==.==.. ", +" ..--.--.--.--.--.. ", +" ..--.--.--.--.--.. ", +" ..;;.;;.;;.;;;;;.. ", +" ..................... ", +" ..................... ", +" ..>>>>>>>>.>>>>>.. ", +" ..>>.>>.>>.>>..... ", +" ..,,.,,.,,.,,,,,.. ", +" ..,,.,,.,,.,,..... ", +" ..''.''.''.''.''.. ", +" ..''.''.''.''.''.. ", +" ..''.''.''.''.''.. ", +" ..''.''.''.'''''.. ", +" .................. ", +" .................. "}; diff --git a/desmume/src/frontend/posix/gtk2/Info.plist b/desmume/src/frontend/posix/gtk2/Info.plist new file mode 100644 index 000000000..b5313a446 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + desmume + CFBundleGetInfoString + desmume + CFBundleIdentifier + org.desmume.desmume.gtk + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Desmume + CFBundlePackageType + APPL + + diff --git a/desmume/src/frontend/posix/gtk2/Makefile.am b/desmume/src/frontend/posix/gtk2/Makefile.am new file mode 100644 index 000000000..9ba2a51b4 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/Makefile.am @@ -0,0 +1,46 @@ +SUBDIRS = doc +include ../desmume.mk + +AM_CPPFLAGS += $(SDL_CFLAGS) $(GTK_CFLAGS) $(GTHREAD_CFLAGS) $(ALSA_CFLAGS) $(LIBAGG_CFLAGS) $(LIBSOUNDTOUCH_CFLAGS) + +Applicationsdir = $(datadir)/applications +Applications_DATA = desmume.desktop +pixmapdir = $(datadir)/pixmaps +pixmap_DATA = DeSmuME.xpm +EXTRA_DIST = DeSmuME.xpm desmume.desktop +bin_PROGRAMS = desmume +desmume_SOURCES = \ + avout.h \ + avout_pipe_base.cpp avout_pipe_base.h \ + avout_x264.cpp avout_x264.h \ + avout_flac.cpp avout_flac.h \ + config.cpp config.h config_opts.h \ + desmume.h desmume.cpp \ + dTool.h dToolsList.cpp \ + tools/ioregsView.cpp tools/ioregsView.h \ + ../shared/sndsdl.cpp \ + ../shared/ctrlssdl.h ../shared/ctrlssdl.cpp \ + osmesa_3Demu.cpp osmesa_3Demu.h \ + sdl_3Demu.cpp sdl_3Demu.h \ + cheatsGTK.h cheatsGTK.cpp \ + main.cpp main.h +desmume_LDADD = ../libdesmume.a \ + $(X_LIBS) -lX11 $(SDL_LIBS) $(GTK_LIBS) $(GTHREAD_LIBS) $(ALSA_LIBS) $(LIBAGG_LIBS) $(LIBSOUNDTOUCH_LIBS) +if HAVE_LIBOSMESA +desmume_LDADD += $(OSMESA_LIBS) +else +endif + +UPDATE_DESKTOP = \ + appsdir=$(DESTDIR)$(datadir)/applications ; \ + if [ -f $$appsdir/mimeinfo.cache ] ; then \ + if test ! "x$(UPDATEDESKTOP)" = "x" ; then \ + $(UPDATEDESKTOP) $$appsdir ; \ + fi \ + fi + +install-data-hook: + $(UPDATE_DESKTOP) + +uninstall-hook: + $(UPDATE_DESKTOP) diff --git a/desmume/src/frontend/posix/gtk2/avout.h b/desmume/src/frontend/posix/gtk2/avout.h new file mode 100644 index 000000000..8966856ea --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#ifndef _AVOUT_H_ +#define _AVOUT_H_ + +#include "types.h" + +class AVOut { +public: + virtual bool begin(const char* fname) { return false; } + virtual void end() {} + virtual bool isRecording() { return false; } + virtual void updateAudio(void* soundData, int soundLen) {} + virtual void updateVideo(const u16* buffer) {} +}; + +#endif diff --git a/desmume/src/frontend/posix/gtk2/avout_flac.cpp b/desmume/src/frontend/posix/gtk2/avout_flac.cpp new file mode 100644 index 000000000..a45e77bc0 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_flac.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#include +#include + +#include "avout_flac.h" +#include "SPU.h" + +// Helper macros to convert numerics to strings +#if defined(_MSC_VER) + //re: http://72.14.203.104/search?q=cache:HG-okth5NGkJ:mail.python.org/pipermail/python-checkins/2002-November/030704.html+_msc_ver+compiler+version+string&hl=en&gl=us&ct=clnk&cd=5 + #define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) + #define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X + #define _Py_STRINGIZE2(X) #X + + #define TOSTRING(X) _Py_STRINGIZE(X) // Alias _Py_STRINGIZE so that we have a common macro name +#else + #define STRINGIFY(x) #x + #define TOSTRING(x) STRINGIFY(x) +#endif + +AVOutFlac::AVOutFlac() { + const char* const args[] = { + "flac", + "-f", + "-o", this->filename, + "--endian=little", + "--channels=2", + "--bps=16", + "--sample-rate=" TOSTRING(DESMUME_SAMPLE_RATE), + "--sign=signed", + "--force-raw-format", + "-", + NULL + }; + memcpy(this->args, args, sizeof(args)); +} + +const char* const* AVOutFlac::getArgv(const char* fname) { + if (strlen(fname) >= sizeof(this->filename)) { + return NULL; + } + strncpy(this->filename, fname, sizeof(this->filename)); + return this->args; +} + diff --git a/desmume/src/frontend/posix/gtk2/avout_flac.h b/desmume/src/frontend/posix/gtk2/avout_flac.h new file mode 100644 index 000000000..818d81665 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_flac.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#ifndef _AVOUT_FLAC_H_ +#define _AVOUT_FLAC_H_ + +#include "avout_pipe_base.h" + +class AVOutFlac : public AVOutPipeBase { +public: + AVOutFlac(); +protected: + Type type() { return TYPE_AUDIO; } + const char* const* getArgv(const char* fname); +private: + char filename[1024]; + const char* args[12]; +}; + +#endif diff --git a/desmume/src/frontend/posix/gtk2/avout_pipe_base.cpp b/desmume/src/frontend/posix/gtk2/avout_pipe_base.cpp new file mode 100644 index 000000000..fa4f5d431 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_pipe_base.cpp @@ -0,0 +1,102 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#include +#include + +#include "types.h" +#include "SPU.h" + +#include "avout_pipe_base.h" + +static inline int writeAll(int fd, const void* buf, size_t count) { + ssize_t written = 0, writtenTotal = 0; + do { + written = write(fd, ((u8*)buf) + writtenTotal, count - writtenTotal); + } while (written >= 0 && (writtenTotal += written) < count); + return written; +} + +bool AVOutPipeBase::begin(const char* fname) { + if (this->recording) { + return false; + } + const char* const* args = this->getArgv(fname); + if (args == NULL) { + return false; + } + int pipefd[2]; + if (pipe(pipefd) < 0) { + fprintf(stderr, "Fail to open pipe\n"); + return false; + } + pid_t pid = fork(); + if (pid == 0) { + close(pipefd[1]); + dup2(pipefd[0], STDIN_FILENO); + execvp(args[0], (char* const*)args); + fprintf(stderr, "Fail to start %s: %d %s\n", args[0], errno, strerror(errno)); + _exit(1); + } + close(pipefd[0]); + this->pipe_fd = pipefd[1]; + this->recording = true; + return true; +} + +void AVOutPipeBase::end() { + if (this->recording) { + close(this->pipe_fd); + this->recording = false; + } +} + +bool AVOutPipeBase::isRecording() { + return this->recording; +} + +void AVOutPipeBase::updateAudio(void* soundData, int soundLen) { + if(!this->recording || this->type() != TYPE_AUDIO) { + return; + } + if (writeAll(this->pipe_fd, soundData, soundLen * 2 * 2) == -1) { + fprintf(stderr, "Error on writing audio: %d %s\n", errno, strerror(errno)); + this->end(); + } +} + +void AVOutPipeBase::updateVideo(const u16* buffer) { + if(!this->recording || this->type() != TYPE_VIDEO) { + return; + } + u8 rgb[256 * 384 * 3]; + u8* cur = rgb; + for (int i = 0; i < 256 * 384; i++) { + u16 gpu_pixel = buffer[i]; + *cur = ((gpu_pixel >> 0) & 0x1f) << 3; + cur++; + *cur = ((gpu_pixel >> 5) & 0x1f) << 3; + cur++; + *cur = ((gpu_pixel >> 10) & 0x1f) << 3; + cur++; + } + if (write(this->pipe_fd, rgb, 256 * 384 * 3) == -1) { + fprintf(stderr, "Error on writing video: %d %s\n", errno, strerror(errno)); + this->end(); + } +} + diff --git a/desmume/src/frontend/posix/gtk2/avout_pipe_base.h b/desmume/src/frontend/posix/gtk2/avout_pipe_base.h new file mode 100644 index 000000000..3f1c0edd8 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_pipe_base.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#ifndef _AVOUT_PIPE_BASE_H_ +#define _AVOUT_PIPE_BASE_H_ + +#include "avout.h" + +class AVOutPipeBase : public AVOut { +public: + bool begin(const char* fname); + void end(); + bool isRecording(); + void updateAudio(void* soundData, int soundLen); + void updateVideo(const u16* buffer); +protected: + enum Type { TYPE_AUDIO, TYPE_VIDEO }; + virtual Type type() = 0; + virtual const char* const* getArgv(const char* fname) = 0; +private: + bool recording; + int pipe_fd; +}; + +#endif diff --git a/desmume/src/frontend/posix/gtk2/avout_x264.cpp b/desmume/src/frontend/posix/gtk2/avout_x264.cpp new file mode 100644 index 000000000..1996c0f8a --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_x264.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#include +#include + +#include "avout_x264.h" + +AVOutX264::AVOutX264() { + const char* const args[] = { + "x264", + "--qp", "0", + "--demuxer", "raw", + "--input-csp", "rgb", + "--input-depth", "8", + "--input-res", "256x384", + "--fps", "60", + "--output-csp", "i444", + "-o", this->filename, + "-", + NULL + }; + memcpy(this->args, args, sizeof(args)); +} + +const char* const* AVOutX264::getArgv(const char* fname) { + if (strlen(fname) >= sizeof(this->filename)) { + return NULL; + } + strncpy(this->filename, fname, sizeof(this->filename)); + return this->args; +} + diff --git a/desmume/src/frontend/posix/gtk2/avout_x264.h b/desmume/src/frontend/posix/gtk2/avout_x264.h new file mode 100644 index 000000000..24125a0f9 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/avout_x264.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2014 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#ifndef _AVOUT_X264_H_ +#define _AVOUT_X264_H_ + +#include "avout_pipe_base.h" + +class AVOutX264 : public AVOutPipeBase { +public: + AVOutX264(); +protected: + Type type() { return TYPE_VIDEO; } + const char* const* getArgv(const char* fname); +private: + char filename[1024]; + const char* args[19]; +}; + +#endif diff --git a/desmume/src/frontend/posix/gtk2/cheatsGTK.cpp b/desmume/src/frontend/posix/gtk2/cheatsGTK.cpp new file mode 100644 index 000000000..6045bdfdb --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/cheatsGTK.cpp @@ -0,0 +1,440 @@ +/* cheats.cpp - this file is part of DeSmuME + * + * Copyright (C) 2006-2009 DeSmuME Team + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include +#include +#include +#include "cheatsGTK.h" +#include "cheatSystem.h" +#include "main.h" +#include "desmume.h" + +#undef GPOINTER_TO_INT +#define GPOINTER_TO_INT(p) ((gint) (glong) (p)) + +enum { + COLUMN_ENABLED, + COLUMN_SIZE, + COLUMN_HI, + COLUMN_LO, + COLUMN_DESC, + NUM_COL +}; + +enum +{ + COLUMN_SIZE_TEXT, + NUM_SIZE_COLUMNS +}; + +enum { + TYPE_TOGGLE, + TYPE_COMBO, + TYPE_STRING +}; + +static struct { + const gchar *caption; + gint type; + gint column; +} columnTable[]={ + { "Enabled", TYPE_TOGGLE, COLUMN_ENABLED}, + { "Size", TYPE_COMBO, COLUMN_SIZE}, + { "Offset", TYPE_STRING, COLUMN_HI}, + { "Value", TYPE_STRING, COLUMN_LO}, + { "Description", TYPE_STRING, COLUMN_DESC} +}; + +static GtkWidget *win = NULL; +static BOOL shouldBeRunning = FALSE; + +// --------------------------------------------------------------------------------- +// SEARCH +// --------------------------------------------------------------------------------- + +static void +enabled_toggled(GtkCellRendererToggle * cell, + gchar * path_str, gpointer data) +{ + GtkTreeModel *model = (GtkTreeModel *) data; + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_string(path_str); + gboolean enabled; + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COLUMN_ENABLED, &enabled, -1); + + enabled ^= 1; + CHEATS_LIST cheat; + u32 ii; + GtkTreePath *path1; + + path1 = gtk_tree_model_get_path (model, &iter); + ii = gtk_tree_path_get_indices (path)[0]; + + cheats->get(&cheat, ii); + + cheats->update(cheat.size, cheat.code[0][0], cheat.code[0][1], cheat.description, + enabled, ii); + + gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_ENABLED, enabled, -1); + + gtk_tree_path_free(path); +} + +static void cheat_list_modify_cheat(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer data) +{ + GtkTreeModel *model = (GtkTreeModel *) data; + GtkTreePath *path = gtk_tree_path_new_from_string(path_string); + GtkTreeIter iter; + + gint column = + GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column")); + + gtk_tree_model_get_iter(model, &iter, path); + + { + u32 ii; + GtkTreePath *path1; + CHEATS_LIST cheat; + + path1 = gtk_tree_model_get_path (model, &iter); + ii = gtk_tree_path_get_indices (path)[0]; + + cheats->get(&cheat, ii); + + gtk_tree_path_free (path1); + + if (column == COLUMN_LO || column == COLUMN_HI + || column == COLUMN_SIZE) { + u32 v = atoi(new_text); + switch (column) { + case COLUMN_SIZE: + cheats->update(v-1, cheat.code[0][0], cheat.code[0][1], + cheat.description, cheat.enabled, ii); + break; + case COLUMN_HI: + cheats->update(cheat.size, v, cheat.code[0][1], cheat.description, + cheat.enabled, ii); + break; + case COLUMN_LO: + cheats->update(cheat.size, cheat.code[0][0], v, cheat.description, + cheat.enabled, ii); + break; + } + gtk_list_store_set(GTK_LIST_STORE(model), &iter, column, + atoi(new_text), -1); + } else if (column == COLUMN_DESC){ + cheats->update(cheat.size, cheat.code[0][0], cheat.code[0][1], + g_strdup(new_text), cheat.enabled, ii); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, column, + g_strdup(new_text), -1); + } + + } +} + +static void cheat_list_remove_cheat(GtkWidget * widget, gpointer data) +{ + GtkTreeView *tree = (GtkTreeView *) data; + GtkTreeSelection *selection = gtk_tree_view_get_selection (tree); + GtkTreeModel *model = gtk_tree_view_get_model (tree); + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)){ + u32 ii; + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, &iter); + ii = gtk_tree_path_get_indices (path)[0]; + + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + cheats->remove(ii); + + gtk_tree_path_free (path); + } +} + +static void cheat_list_add_cheat(GtkWidget * widget, gpointer data) +{ +#define NEW_DESC "New cheat" + GtkListStore *store = (GtkListStore *) data; + GtkTreeIter iter; + cheats->add(1, 0, 0, g_strdup(NEW_DESC), FALSE); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COLUMN_ENABLED, FALSE, + COLUMN_SIZE, 1, + COLUMN_HI, 0, + COLUMN_LO, 0, COLUMN_DESC, NEW_DESC, -1); + +#undef NEW_DESC +} + +static GtkTreeModel * create_numbers_model (void) +{ +#define N_NUMBERS 4 + gint i = 0; + GtkListStore *model; + GtkTreeIter iter; + + /* create list store */ + model = gtk_list_store_new (NUM_SIZE_COLUMNS, G_TYPE_STRING, G_TYPE_INT); + + /* add numbers */ + for (i = 1; i < N_NUMBERS+1; i++) + { + char str[2]; + + str[0] = '0' + i; + str[1] = '\0'; + + gtk_list_store_append (model, &iter); + + gtk_list_store_set (model, &iter, + COLUMN_SIZE_TEXT, str, + -1); + } + + return GTK_TREE_MODEL (model); + +#undef N_NUMBERS +} + +static void cheat_list_add_columns(GtkTreeView * tree, GtkListStore * store) +{ + + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree)); + static GtkTreeModel * size_model; + + for (u32 ii = 0; ii < sizeof(columnTable) / sizeof(columnTable[0]); ii++) { + GtkCellRenderer *renderer = NULL; + GtkTreeViewColumn *column; + const gchar *attrib = NULL; + switch (columnTable[ii].type) { + case TYPE_TOGGLE: + renderer = gtk_cell_renderer_toggle_new(); + g_signal_connect(renderer, "toggled", + G_CALLBACK(enabled_toggled), model); + attrib = "active"; + break; + case TYPE_STRING: + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "editable", TRUE, NULL); + g_signal_connect(renderer, "edited", + G_CALLBACK(cheat_list_modify_cheat), store); + attrib = "text"; + break; + case TYPE_COMBO: + renderer = gtk_cell_renderer_combo_new(); + size_model = create_numbers_model(); + g_object_set(renderer, + "model", size_model, + "text-column", COLUMN_SIZE_TEXT, + "editable", TRUE, + "has-entry", FALSE, + NULL); + g_object_unref(size_model); + g_signal_connect(renderer, "edited", + G_CALLBACK(cheat_list_modify_cheat), store); + attrib = "text"; + break; + } + column = + gtk_tree_view_column_new_with_attributes(columnTable[ii]. + caption, renderer, + attrib, columnTable[ii].column, + NULL); + g_object_set_data(G_OBJECT(renderer), "column", + GINT_TO_POINTER(columnTable[ii].column)); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + } + +} + +static void cheatListEnd() +{ + cheats->save(); + if(shouldBeRunning) + Launch(); +} + +static GtkListStore *cheat_list_populate() +{ + GtkListStore *store = gtk_list_store_new (5, G_TYPE_BOOLEAN, + G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING); + + CHEATS_LIST cheat; + u32 chsize = cheats->getSize(); + for(u32 ii = 0; ii < chsize; ii++){ + GtkTreeIter iter; + cheats->get(&cheat, ii); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COLUMN_ENABLED, cheat.enabled, + COLUMN_SIZE, cheat.size+1, + COLUMN_HI, cheat.code[0][0], + COLUMN_LO, cheat.code[0][1], + COLUMN_DESC, cheat.description, + -1); + } + return store; +} + +static GtkWidget *cheat_list_create_ui() +{ + GtkListStore *store = cheat_list_populate(); + GtkWidget *tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + GtkWidget *vbox = gtk_vbox_new(FALSE, 1); + GtkWidget *hbbox = gtk_hbutton_box_new(); + GtkWidget *button; + + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(tree)); + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(hbbox)); + gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vbox)); + + button = gtk_button_new_with_label("add cheat"); + g_signal_connect (button, "clicked", G_CALLBACK (cheat_list_add_cheat), store); + gtk_container_add(GTK_CONTAINER(hbbox),button); + + button = gtk_button_new_with_label("Remove cheat"); + g_signal_connect (button, "clicked", G_CALLBACK (cheat_list_remove_cheat), tree); + gtk_container_add(GTK_CONTAINER(hbbox),button); + + cheat_list_add_columns(GTK_TREE_VIEW(tree), store); + + /* Setup the selection handler */ + GtkTreeSelection *select; + select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); + gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); + + return tree; +} + +void CheatList () +{ + shouldBeRunning = desmume_running(); + Pause(); + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win),"Cheat List"); + gtk_window_set_modal(GTK_WINDOW(win), TRUE); + g_signal_connect(G_OBJECT(win), "destroy", cheatListEnd, NULL); + + cheat_list_create_ui(); + + gtk_widget_show_all(win); +} + +// --------------------------------------------------------------------------------- +// SEARCH +// --------------------------------------------------------------------------------- + +static void cheat_search_create_ui() +{ + GtkWidget *button; + GtkWidget *vbox = gtk_vbox_new(FALSE, 1); + GtkWidget *hbbox = gtk_hbutton_box_new(); + GtkWidget *b; + + gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vbox)); + + { + b = gtk_hbox_new(FALSE, 1); + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(b)); + + { + GtkTreeModel * size_model; + GtkWidget *w; + w = gtk_label_new("size"); + gtk_container_add(GTK_CONTAINER(b), w); + + size_model = create_numbers_model(); + + w = gtk_combo_box_new_with_model(size_model); + g_object_unref(size_model); + GtkCellRenderer * renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, + "text", COLUMN_SIZE_TEXT, + NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0); + gtk_container_add(GTK_CONTAINER(b), w); + } + + b = gtk_hbox_new(FALSE, 1); + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(b)); + + { + GtkWidget *w; + w = gtk_label_new("signedness"); + gtk_container_add(GTK_CONTAINER(b), w); + +// m = create_sign_model(); + + w = gtk_combo_box_new(); +// w = gtk_combo_box_new_with_model(size_model); +// g_object_unref(size_model); +// size_model = NULL; + GtkCellRenderer * renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, TRUE); +// gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, +// "text", COLUMN_SIZE_TEXT, +// NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0); + gtk_container_add(GTK_CONTAINER(b), w); + } + } + + // BUTTONS: + + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(hbbox)); + + button = gtk_button_new_with_label("add cheats"); +// g_signal_connect (button, "clicked", g_callback (cheat_list_add_cheat), store); + gtk_container_add(GTK_CONTAINER(hbbox),button); + + button = gtk_button_new_with_label("search"); +// g_signal_connect (button, "clicked", g_callback (cheat_list_add_cheat), store); + gtk_container_add(GTK_CONTAINER(hbbox),button); + +// GtkWidget *vbox = gtk_vbox_new(FALSE, 1); +// gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vbox)); +} + +static void cheatSearchEnd() +{ +} + +void CheatSearch () +{ + shouldBeRunning = desmume_running(); + Pause(); + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win),"Cheat Search"); + gtk_window_set_modal(GTK_WINDOW(win), TRUE); + g_signal_connect(G_OBJECT(win), "destroy", cheatSearchEnd, NULL); + + cheat_search_create_ui(); + + gtk_widget_show_all(win); +} diff --git a/desmume/src/frontend/posix/gtk2/cheatsGTK.h b/desmume/src/frontend/posix/gtk2/cheatsGTK.h new file mode 100644 index 000000000..1d8345459 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/cheatsGTK.h @@ -0,0 +1,29 @@ +/* cheatsGtk.h - this file is part of DeSmuME + * + * Copyright (C) 2006,2007 DeSmuME Team + * Copyright (C) 2007 Pascal Giard (evilynux) + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CHEATS_H__ +#define __CHEATS_H__ + +void CheatSearch (); +void CheatList (); + +#endif /*__CHEATS_H__*/ + diff --git a/desmume/src/frontend/posix/gtk2/check b/desmume/src/frontend/posix/gtk2/check new file mode 100755 index 000000000..91b3f01a6 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/check @@ -0,0 +1,9 @@ +#!/bin/sh + +for filename in desmume.app/libs/* +do + echo $filename + otool -L $filename | grep "Cairo.framework" + otool -L $filename | grep "Gtk.framework" + otool -L $filename | grep "GLib.framework" +done; diff --git a/desmume/src/frontend/posix/gtk2/config.cpp b/desmume/src/frontend/posix/gtk2/config.cpp new file mode 100644 index 000000000..eceb2f06b --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/config.cpp @@ -0,0 +1,166 @@ +/* + Copyright (C) 2014 DeSmuME team + Copyright (C) 2014 Alvin Wong + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#include +#include + +#include "config.h" + +using std::string; +using std::vector; + +namespace desmume { +namespace config { + +/* class value */ + +template<> +void value::load() { + GError* err = NULL; + bool val = g_key_file_get_boolean(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), &err); + if (err != NULL) { + g_error_free(err); + } else { + this->mData = val; + } +} + +template<> +void value::save() { + g_key_file_set_boolean(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), this->mData ? TRUE : FALSE); +} + +/* class value */ + +template<> +void value::load() { + GError* err = NULL; + int val = g_key_file_get_integer(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), &err); + if (err != NULL) { + g_error_free(err); + } else { + this->mData = val; + } +} + +template<> +void value::save() { + g_key_file_set_integer(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), this->mData); +} + +/* class value */ + +template<> +void value::load() { + char* val = g_key_file_get_string(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), NULL); + if (val != NULL) { + this->mData = val; + g_free(val); + } +} + +template<> +void value::save() { + g_key_file_set_string(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), this->mData.c_str()); +} + +/* class Config */ + +Config::Config() + : mKeyFile(g_key_file_new()) +# define OPT(name, type, default, section, key) \ + , name(default, #section, #key) +# include "config_opts.h" +# undef OPT +{ +# define OPT(name, type, default, section, key) \ + this->name.mKeyFile = this->mKeyFile; +# include "config_opts.h" +# undef OPT +} + +Config::~Config() { + g_key_file_free(this->mKeyFile); +} + +void Config::load() { + char* config_dir = g_build_filename(g_get_user_config_dir(), "desmume", NULL); + g_mkdir_with_parents(config_dir, 0755); + char* config_file = g_build_filename(config_dir, "config.cfg", NULL); + g_key_file_load_from_file(this->mKeyFile, config_file, G_KEY_FILE_KEEP_COMMENTS, NULL); + g_free(config_file); + g_free(config_dir); +# define OPT(name, type, default, section, key) \ + this->name.load(); +# include "config_opts.h" +# undef OPT +} + +void Config::save() { +# define OPT(name, type, default, section, key) \ + this->name.save(); +# include "config_opts.h" +# undef OPT + gsize length; + char* data = g_key_file_to_data(this->mKeyFile, &length, NULL); + char* config_dir = g_build_filename(g_get_user_config_dir(), "desmume", NULL); + g_mkdir_with_parents(config_dir, 0755); + char* config_file = g_build_filename(config_dir, "config.cfg", NULL); + g_file_set_contents(config_file, data, length, NULL); + g_free(config_file); + g_free(config_dir); + g_free(data); +} + +#if 0 +/* class Config::Input */ + +Config::Input::Input() + : keys(NB_KEYS) + , joykeys(NB_KEYS) +{ +static const u16 gtk_kb_cfg[NB_KEYS] = { + GDK_x, // A + GDK_z, // B + GDK_Shift_R, // select + GDK_Return, // start + GDK_Right, // Right + GDK_Left, // Left + GDK_Up, // Up + GDK_Down, // Down + GDK_w, // R + GDK_q, // L + GDK_s, // X + GDK_a, // Y + GDK_p, // DEBUG + GDK_o, // BOOST + GDK_BackSpace, // Lid +}; + for (size_t i = 0; i < NB_KEYS; i++) { + this->keys[i] = new value(gtk_kb_cfg[i], "Keys", ...); + } +} + +void Config::Input::load(); + +void Config::Input::save(); +#endif + +} /* namespace config */ +} /* namespace desmume */ + diff --git a/desmume/src/frontend/posix/gtk2/config.h b/desmume/src/frontend/posix/gtk2/config.h new file mode 100644 index 000000000..9ebd99b9b --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/config.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2014 DeSmuME team + Copyright (C) 2014 Alvin Wong + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +#ifndef DESMUME_CONFIG_CLASS_H_ +#define DESMUME_CONFIG_CLASS_H_ + +#include +#include + +#include + +namespace desmume { +namespace config { + +template +class value { + friend class Config; + + std::string mSection; + std::string mKey; + GKeyFile* mKeyFile; + + T mDefaultVal; + T mData; + + value(const value&); // DO NOT IMPLEMENT + value operator=(const value&); // DO NOT IMPLEMENT + void load(); + void save(); +public: + value(const T& defaultVal, const std::string& section, const std::string& key) + : mSection(section) + , mKey(key) + , mDefaultVal(defaultVal) + , mData(defaultVal) {} + inline T defaultVal() { + return this->mDefaultVal; + } + inline T get() { + return this->mData; + } + inline operator T() { + return this->mData; + } + inline void set(const T& value) { + this->mData = value; + //this->save(); + } + inline T operator=(const T& value) { + this->mData = value; + return this->mData; + } +}; /* class value */ + +#ifdef OPT +# undef OPT +#endif + +class Config { +protected: + GKeyFile* mKeyFile; + + Config(const Config&); // DO NOT IMPLEMENT + Config operator=(const Config&); // DO NOT IMPLEMENT +public: + // member fields +# define OPT(name, type, default, section, key) \ + value name; +# include "config_opts.h" +# undef OPT +#if 0 + // Special case: input config + class Input { + friend class Config; + std::vector*> keys; + std::vector*> joykeys; + Input(); + void load(); + void save(); + } inputs; +#endif + + // constructor + Config(); + + // methods + virtual ~Config(); + void load(); + void save(); +}; /* class Config */ + +#undef DESMUME_CONFIG_CONFIG + +} /* namespace config */ +} /* namespace desmume */ + +#endif /* DESMUME_CONFIG_CLASS_H_ */ + diff --git a/desmume/src/frontend/posix/gtk2/config_opts.h b/desmume/src/frontend/posix/gtk2/config_opts.h new file mode 100644 index 000000000..2d1b121fc --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/config_opts.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2014 DeSmuME team + Copyright (C) 2014 Alvin Wong + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ +/* + This file contains a list of config options. + + #define OPT(name, type, default, section, key) +*/ + +/* View */ +OPT(view_orient, int, 0, View, ScreenLayout) +OPT(view_swap, bool, false, View, SwapScreens) +OPT(view_rot, int, 0, View, Rotation) +OPT(view_gap, bool, false, View, ScreenGap) +OPT(view_filter, int, 0, View, Filter) +OPT(view_cairoFilter, int, 3, View, SecondaryFilter) /* default: nearest-neighbour */ +OPT(view_menu, bool, true, View, ShowMenu) +OPT(view_toolbar, bool, true, View, ShowToolbar) +OPT(view_statusbar, bool, true, View, ShowStatusbar) + +/* Window */ +OPT(window_scale, int, 0, Window, Scale2x) +/*OPT(window_x, int, -1, Window, X)*/ +/*OPT(window_y, int, -1, Window, Y)*/ +/*OPT(window_w, int, 0, Window, Width)*/ +/*OPT(window_h, int, 0, Window, Height)*/ +/*OPT(window_maximized, bool, false, Window, Maximized)*/ +OPT(window_fullscreen, bool, false, Window, Fullscreen) + +/* HUD display */ +OPT(hud_fps, bool, false, HudDisplay, Fps) +OPT(hud_frameCounter, bool, false, HudDisplay, FrameCounter) +OPT(hud_lagCounter, bool, false, HudDisplay, LagCounter) +OPT(hud_input, bool, false, HudDisplay, Input) +OPT(hud_graphicalInput, bool, false, HudDisplay, GraphicalInput) +OPT(hud_rtc, bool, false, HudDisplay, RTC) +OPT(hud_mic, bool, false, HudDisplay, Mic) + +/* Config */ +OPT(fpslimiter, bool, true, Config, FpsLimiter) +OPT(autoframeskip, bool, true, Config, AudoFrameskip) +OPT(frameskip, int, 0, Config, Frameskip) +OPT(use_jit,bool,false,Config,JIT_Enabled) +OPT(jit_max_block_size,int,100,Config,JITBlockSize) +OPT(core3D, int, 1, Config, Core3D) +OPT(textureDeposterize, bool, false, Config, 3DTextureDeposterization) +OPT(textureSmoothing, bool, false, Config, 3DTextureSmoothing) +OPT(textureUpscale, int, 1, Config, 3DTextureUpscaling) +OPT(highColorInterpolation, bool, true, Config, HighResolutionColorInterpolation) +OPT(multisampling, bool, false, Config, OpenGLMultisampling) +OPT(multisamplingSize, int, 0, Config, OpenGLMultisamplingSize) +OPT(command_line_overriding_firmware_language, bool, false, Config, CommandLineOverridingFirmwareLanguage) +OPT(firmware_language, int, 1, Config, FirmwareLanguage) + +/* Audio */ +OPT(audio_enabled, bool, true, Audio, Enabled) +OPT(audio_sync, int, 0, Audio, Synchronization) +OPT(audio_interpolation, int, 1, Audio, Interpolation) +OPT(audio_volume, int, 128, Audio, Volume) + diff --git a/desmume/src/frontend/posix/gtk2/dTool.h b/desmume/src/frontend/posix/gtk2/dTool.h new file mode 100644 index 000000000..cd71e0c64 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/dTool.h @@ -0,0 +1,20 @@ +#ifndef __DTOOL_H__ +#define __DTOOL_H__ + +typedef void (*dTool_openFn)(int id); +typedef void (*dTool_updateFn)(); +typedef void (*dTool_closeFn)(); + +typedef struct +{ + /* this should be the same name of the action in the gui xml */ + const char shortname[16]; + const char name[32]; + dTool_openFn open; + dTool_updateFn update; + dTool_closeFn close; +} dTool_t; + +extern void dTool_CloseCallback(int id); + +#endif /*__DTOOL_H__*/ diff --git a/desmume/src/frontend/posix/gtk2/dToolsList.cpp b/desmume/src/frontend/posix/gtk2/dToolsList.cpp new file mode 100644 index 000000000..503da5b43 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/dToolsList.cpp @@ -0,0 +1,33 @@ +/* dToolsList.cpp + * + * Copyright (C) 2006 Thoduv + * Copyright (C) 2006-2015 DeSmuME Team + * + * This file is part of DeSmuME + * + * DeSmuME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * DeSmuME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DeSmuME; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "dTool.h" +#include "../types.h" +#include "tools/ioregsView.h" + +dTool_t *dTools_list[] = +{ + &dTool_ioregsView +}; + +int dTools_list_size = ARRAY_SIZE(dTools_list); + diff --git a/desmume/src/frontend/posix/gtk2/desmume.cpp b/desmume/src/frontend/posix/gtk2/desmume.cpp new file mode 100644 index 000000000..b11d029a0 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/desmume.cpp @@ -0,0 +1,74 @@ +/* desmume.c - this file is part of DeSmuME + * + * Copyright (C) 2006-2015 DeSmuME Team + * Copyright (C) 2007 Pascal Giard (evilynux) + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "desmume.h" + +#include "../NDSSystem.h" +#include "../SPU.h" +#include "../shared/sndsdl.h" +#include "../movie.h" + +volatile bool execute = false; +BOOL click = FALSE; + +void desmume_init( int disable_sound) +{ + NDS_Init(); + + if ( !disable_sound) { + SPU_ChangeSoundCore(SNDCORE_SDL, 735 * 4); + } + execute = false; +} + +void desmume_free( void) +{ + execute = false; + NDS_DeInit(); +} + +void desmume_pause( void) +{ + execute = false; + SPU_Pause(1); +} +void desmume_resume( void) +{ + execute = true; + SPU_Pause(0); +} +void desmume_toggle( void) +{ + execute ^= true; +} +bool desmume_running( void) +{ + return execute; +} + +void desmume_cycle( void) +{ + FCEUMOV_AddInputState(); + NDS_exec(); + SPU_Emulate_user(); +} + diff --git a/desmume/src/frontend/posix/gtk2/desmume.desktop b/desmume/src/frontend/posix/gtk2/desmume.desktop new file mode 100644 index 000000000..cae4c8eba --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/desmume.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=DeSmuME (Gtk) +Comment=Nintento DS emulator +TryExec=desmume +Exec=desmume +Icon=DeSmuME +Categories=GNOME;GTK;Game;Emulator; +MimeType=application/x-nintendo-ds-rom; diff --git a/desmume/src/frontend/posix/gtk2/desmume.h b/desmume/src/frontend/posix/gtk2/desmume.h new file mode 100644 index 000000000..3b778df1c --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/desmume.h @@ -0,0 +1,36 @@ +/* desmume.h - this file is part of DeSmuME + * + * Copyright (C) 2006,2007 DeSmuME Team + * Copyright (C) 2007 Pascal Giard (evilynux) + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __DESMUME_H__ +#define __DESMUME_H__ + +extern void desmume_init( int disable_sound); +extern void desmume_free( void); + +extern void desmume_pause( void); +extern void desmume_resume( void); +extern void desmume_toggle( void); +extern bool desmume_running( void); + +extern void desmume_cycle( void); + +#endif /*__DESMUME_H__*/ + diff --git a/desmume/src/frontend/posix/gtk2/doc/Makefile.am b/desmume/src/frontend/posix/gtk2/doc/Makefile.am new file mode 100644 index 000000000..41aee3291 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/doc/Makefile.am @@ -0,0 +1 @@ +dist_man_MANS = desmume.1 diff --git a/desmume/src/frontend/posix/gtk2/doc/desmume.1 b/desmume/src/frontend/posix/gtk2/doc/desmume.1 new file mode 100644 index 000000000..728bd50dd --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/doc/desmume.1 @@ -0,0 +1,169 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH DESMUME 1 "June 26, 2007" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +desmume \- Nintendo DS emulator +.SH SYNOPSIS +.B desmume +.RI [ options ] " files" ... +.SH DESCRIPTION +This manual page documents briefly the +.B desmume +program +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBdesmume\fP is a Nintendo DS emulator running homebrew demos and commercial games. +.SH OPTIONS +These programs follow the usual GNU command line syntax, with long +options starting with two dashes (`-'). +A summary of options is included below. +.TP +.B \-\-load-slot=NUM +Loads savegame from slot NUM +.TP +.B \-\-opengl-2d +Enables using OpenGL for screen rendering +.TP +.B \-\-soft-convert +Use software colour conversion during OpenGL screen rendering. May produce better or worse frame rates depending on hardware. +.TP +.B \-\-3d-engine=ENGINE +Select available 3d emulation: +.RS +0 = 3d disabled +.RE +.RS +1 = internal desmume software rasterizer (default) +.RE +.RS +2 = osmesa or gtkglext opengl (if available, depending on compilation options, see desmume \-\-help) +.RE +.TP +.B \-\-disable-sound +Disables the sound emulation +.TP +.B \-\-disable-limiter +Disables the 60 fps limiter +.TP +.B \-\-arm9gdb=PORT_NUM +Enable the ARM9 GDB stub on the given port +.TP +.B \-\-arm7gdb=PORT_NUM +Enable the ARM7 GDB stub on the given port +.TP +.B \-\-save-type=TYPE +Select savetype from the following: +.RS +0 = Autodetect +.RE +.RS +1 = EEPROM 4kbit +.RE +.RS +2 = EEPROM 64kbit +.RE +.RS +3 = EEPROM 512kbit +.RE +.RS +4 = FRAM 256kbit +.RE +.RS +5 = FLASH 2mbit +.RE +.RS +6 = FLASH 4mbit +.RE +.TP +.B \-\-fwlang=LANG +Set the language in the firmware, LANG as follows: +.RS +0 = Japanese +.RE +.RS +1 = English +.RE +.RS +2 = French +.RE +.RS +3 = German +.RE +.RS +4 = Italian +.RE +.RS +5 = Spanish +.RE +.TP +.B \-\-cflash=PATH_TO_DISK_IMAGE +Enable disk image GBAMP compact flash emulation +.TP +.B \-h, \-\-help +Show summary of options. +.TP +.B \-v, \-\-version +Show version of program. +.SH INPUT +Mouse cursor acts as stylus, mouse click replaces stylus touch. +.PP +Keyboard can be configured and by default works as follows: +.RS +F1-F10 load savegame from slot 1-10 +.RE +.RS +Shift+F1-F10 save game to slot 1-10 +.RE +.RS +Non-keypad arrows act as d-pad +.RE +.RS +z = A +.RE +.RS +x = B +.RE +.RS +a = Y +.RE +.RS +s = X +.RE +.RS +q = L +.RE +.RS +w = R +.RE +.RS +Enter = START +.RE +.RS +Left Shift = SELECT +.RE +.RS +Backspace = LID +.RE +.PP +Desmume accepts joystick events, which can be configured by user. +.SH AUTHOR +desmume was written by DeSmuME team +(http://sourceforge.net/projects/desmume). +.PP +This manual page was written by Reinhard Tartler and modified by Pascal Giard , for the Debian project (but may be used by others). diff --git a/desmume/src/frontend/posix/gtk2/dylibbundler b/desmume/src/frontend/posix/gtk2/dylibbundler new file mode 100755 index 0000000000000000000000000000000000000000..0aa0b622ca215d0ce6837a0f07a3ce4e3994039e GIT binary patch literal 72920 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
    ;b4>1Cx}Mg{@h5pC>i-xR86RwrcPo)SOO=(#`i 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 literal 0 HcmV?d00001 diff --git a/desmume/src/frontend/posix/gtk2/dylibbundler.modified.Settings.cpp b/desmume/src/frontend/posix/gtk2/dylibbundler.modified.Settings.cpp new file mode 100644 index 000000000..15b5672e9 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/dylibbundler.modified.Settings.cpp @@ -0,0 +1,94 @@ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Settings.h" +#include +#include + +namespace Settings +{ + +bool overwrite_files = false; +bool overwrite_dir = false; +bool create_dir = false; + +bool canOverwriteFiles(){ return overwrite_files; } +bool canOverwriteDir(){ return overwrite_dir; } +bool canCreateDir(){ return create_dir; } + +void canOverwriteFiles(bool permission){ overwrite_files = permission; } +void canOverwriteDir(bool permission){ overwrite_dir = permission; } +void canCreateDir(bool permission){ create_dir = permission; } + + +bool bundleLibs_bool = false; +bool bundleLibs(){ return bundleLibs_bool; } +void bundleLibs(bool on){ bundleLibs_bool = on; } + + +std::string dest_folder_str = "./libs/"; +std::string destFolder(){ return dest_folder_str; } +void destFolder(std::string path) +{ + dest_folder_str = path; + // fix path if needed so it ends with '/' + if( dest_folder_str[ dest_folder_str.size()-1 ] != '/' ) dest_folder_str += "/"; +} + +std::vector files; +void addFileToFix(std::string path){ files.push_back(path); } +int fileToFixAmount(){ return files.size(); } +std::string fileToFix(const int n){ return files[n]; } + +std::string inside_path_str = "@executable_path/../libs/"; +std::string inside_lib_path(){ return inside_path_str; } +void inside_lib_path(std::string p) +{ + inside_path_str = p; + // fix path if needed so it ends with '/' + if( inside_path_str[ inside_path_str.size()-1 ] != '/' ) inside_path_str += "/"; +} + +std::vector prefixes_to_ignore; +void ignore_prefix(std::string prefix) +{ + if( prefix[ prefix.size()-1 ] != '/' ) prefix += "/"; + prefixes_to_ignore.push_back(prefix); +} + +bool isPrefixBundled(std::string prefix) +{ + //std::cout << "Testing " << prefix << " : " << std::endl; + + if(prefix.find("Gtk.framework") == std::string::npos + && prefix.find("GLib.framework") == std::string::npos + && prefix.find("Cairo.framework") == std::string::npos) + if(prefix.find(".framework") != std::string::npos) return false; + if(prefix.find("@executable_path") != std::string::npos) return false; + if(prefix.compare("/usr/lib/") == 0) return false; + + const int prefix_amount = prefixes_to_ignore.size(); + for(int n=0; n. + */ + +#ifndef GTK_UI +#define GTK_UI +#endif + +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "firmware.h" +#include "NDSSystem.h" +#include "driver.h" +#include "GPU.h" +#include "SPU.h" +#include "../shared/sndsdl.h" +#include "../shared/ctrlssdl.h" +#include "MMU.h" +#include "render3D.h" +#include "desmume.h" +#include "debug.h" +#include "rasterize.h" +#include "saves.h" +#include "mic.h" +#include "movie.h" +#include "dTool.h" +#include "../shared/desmume_config.h" +#include "cheatsGTK.h" +#include "frontend/modules/osd/agg/agg_osd.h" + +#include "avout_x264.h" +#include "avout_flac.h" + +#include "commandline.h" + +#include "slot2.h" + +#include "utils/xstring.h" + +#include "filter/videofilter.h" + +#ifdef GDB_STUB + #include "armcpu.h" + #include "gdbstub.h" +#endif + +#define HAVE_OPENGL + +#ifdef HAVE_OPENGL + #include + #include "OGLRender.h" + #include "OGLRender_3_2.h" +#endif + +#if defined(HAVE_LIBOSMESA) + #include "osmesa_3Demu.h" +#else + #include "sdl_3Demu.h" +#endif + +#include "config.h" + +#include "DeSmuME.xpm" + +#undef GPOINTER_TO_INT +#define GPOINTER_TO_INT(p) ((gint) (glong) (p)) + +#undef GPOINTER_TO_UINT +#define GPOINTER_TO_UINT(p) ((guint) (glong) (p)) + +#define EMULOOP_PRIO (G_PRIORITY_HIGH_IDLE + 20 + 1) + +#define GAP_SIZE 64 + +static int draw_count; +extern int _scanline_filter_a, _scanline_filter_b, _scanline_filter_c, _scanline_filter_d; +VideoFilter* video; + +desmume::config::Config config; + +enum { + MAIN_BG_0 = 0, + MAIN_BG_1, + MAIN_BG_2, + MAIN_BG_3, + MAIN_OBJ, + SUB_BG_0, + SUB_BG_1, + SUB_BG_2, + SUB_BG_3, + SUB_OBJ +}; + +gboolean EmuLoop(gpointer data); + +static AVOutX264 avout_x264; +static AVOutFlac avout_flac; +static void RecordAV_x264(); +static void RecordAV_flac(); +static void RecordAV_stop(); +static void RedrawScreen(); + +static void DoQuit(); +static void RecordMovieDialog(); +static void PlayMovieDialog(); +static void StopMovie(); +static void OpenNdsDialog(); +static void SaveStateDialog(); +static void LoadStateDialog(); +void Launch(); +void Pause(); +static void ResetSaveStateTimes(); +static void LoadSaveStateInfo(); +static void Printscreen(); +static void Reset(); +static void SetAudioVolume(); +static void SetFirmwareLanguage(); +static void Edit_Controls(); +static void Edit_Joystick_Controls(); +static void MenuSave(GtkMenuItem *item, gpointer slot); +static void MenuLoad(GtkMenuItem *item, gpointer slot); +static void About(); +static void ToggleMenuVisible(GtkToggleAction *action); +static void ToggleStatusbarVisible(GtkToggleAction *action); +static void ToggleToolbarVisible(GtkToggleAction *action); +static void ToggleFullscreen (GtkToggleAction *action); +static void ToggleAudio (GtkToggleAction *action); +#ifdef FAKE_MIC +static void ToggleMicNoise (GtkToggleAction *action); +#endif +static void ToggleFpsLimiter (GtkToggleAction *action); +static void ToggleAutoFrameskip (GtkToggleAction *action); +static void ToggleSwapScreens(GtkToggleAction *action); +static void ToggleGap (GtkToggleAction *action); +static void SetRotation(GtkAction *action, GtkRadioAction *current); +static void SetWinSize(GtkAction *action, GtkRadioAction *current); +static void SetOrientation(GtkAction *action, GtkRadioAction *current); +static void ToggleLayerVisibility(GtkToggleAction* action, gpointer data); +static void ToggleHudDisplay(GtkToggleAction* action, gpointer data); +#ifdef DESMUME_GTK_FIRMWARE_BROKEN +static void SelectFirmwareFile(); +#endif +#ifdef HAVE_JIT +static void EmulationSettingsDialog(); +static void ToggleJIT(); +static void JITMaxBlockSizeChanged(GtkAdjustment* adj,void *); +#endif +static void GraphicsSettingsDialog(); + + +static const char *ui_description = +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +#ifdef GTK_DESMUME_FIRMWARE_BROKEN +" " +#endif +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +#ifdef HAVE_LIBAGG +" " +" " +" " +" " +" " +" " +" " +" " +" " +#else +" " +#endif +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +#ifdef HAVE_JIT +" " +#endif +" " +" " +#ifdef FAKE_MIC +" " +#endif +" " +" " +" " +" " +#ifdef HAVE_LIBSOUNDTOUCH +" " +#endif +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +""; + +static const GtkActionEntry action_entries[] = { + { "FileMenu", NULL, "_File" }, + { "open", "gtk-open", "_Open", "o", NULL, OpenNdsDialog }, + { "RecentMenu", NULL, "Open _recent" }, + { "run", "gtk-media-play", "_Run", "Pause", NULL, Launch }, + { "pause", "gtk-media-pause", "_Pause", "Pause", NULL, Pause }, + { "reset", "gtk-refresh", "Re_set", NULL, NULL, Reset }, + { "savestateto", NULL, "Save state _to ...", NULL, NULL, SaveStateDialog }, + { "loadstatefrom", NULL, "Load state _from ...", NULL, NULL, LoadStateDialog }, + { "recordmovie", NULL, "Record movie _to ...", NULL, NULL, RecordMovieDialog }, + { "playmovie", NULL, "Play movie _from ...", NULL, NULL, PlayMovieDialog }, + { "stopmovie", NULL, "Stop movie", NULL, NULL, StopMovie }, + { "RecordAVMenu", NULL, "Record _video/audio" }, + { "record_x264", "gtk-media-record", "Record lossless H._264 (video only)...", NULL, NULL, RecordAV_x264 }, + { "record_flac", "gtk-media-record", "Record _flac (audio only)...", NULL, NULL, RecordAV_flac }, + { "record_stop", "gtk-media-stop", "_Stop recording", NULL, NULL, RecordAV_stop }, + { "SavestateMenu", NULL, "_Save state" }, + { "LoadstateMenu", NULL, "_Load state" }, +#ifdef DESMUME_GTK_FIRMWARE_BROKEN + { "loadfirmware","gtk-open", "Load Firm_ware file", "l", NULL, SelectFirmwareFile }, +#endif + { "printscreen","gtk-media-record", "Take a _screenshot", "s", NULL, Printscreen }, + { "quit", "gtk-quit", "_Quit", "q", NULL, DoQuit }, + + { "ViewMenu", NULL, "_View" }, + { "RotationMenu", NULL, "_Rotation" }, + { "OrientationMenu", NULL, "LCDs _Layout" }, + { "WinsizeMenu", NULL, "_Window Size" }, + { "PriInterpolationMenu", NULL, "Video _Filter" }, + { "InterpolationMenu", NULL, "S_econdary Video Filter" }, + { "HudMenu", NULL, "_HUD" }, +#ifndef HAVE_LIBAGG + { "hud_notsupported", NULL, "HUD support not compiled" }, +#endif + + { "ConfigMenu", NULL, "_Config" }, + { "FrameskipMenu", NULL, "_Frameskip" }, +#ifdef HAVE_JIT + { "emulationsettings",NULL,"Em_ulation Settings",NULL,NULL,EmulationSettingsDialog}, +#endif + { "graphicssettings",NULL,"_Graphics Settings",NULL,NULL, GraphicsSettingsDialog}, + { "SPUModeMenu", NULL, "Audio _Synchronization" }, + { "SPUInterpolationMenu", NULL, "Audio _Interpolation" }, + { "CheatMenu", NULL, "_Cheat" }, + { "cheatsearch", NULL, "_Search", NULL, NULL, CheatSearch }, + { "cheatlist", NULL, "_List", NULL, NULL, CheatList }, + { "ConfigSaveMenu", NULL, "_Saves" }, + { "setaudiovolume", NULL, "Set audio _volume", NULL, NULL, SetAudioVolume }, + { "setfirmwarelanguage", NULL, "Set firmware _language", NULL, NULL, SetFirmwareLanguage }, + { "editctrls", NULL, "_Edit controls",NULL, NULL, Edit_Controls }, + { "editjoyctrls", NULL, "Edit _Joystick controls",NULL, NULL, Edit_Joystick_Controls }, + + { "ToolsMenu", NULL, "_Tools" }, + { "LayersMenu", NULL, "View _Layers" }, + + { "HelpMenu", NULL, "_Help" }, + { "about", "gtk-about", "_About", NULL, NULL, About } +}; + +static const GtkToggleActionEntry toggle_entries[] = { + { "enableaudio", NULL, "_Enable audio", NULL, NULL, G_CALLBACK(ToggleAudio), TRUE}, +#ifdef FAKE_MIC + { "micnoise", NULL, "Fake mic _noise", NULL, NULL, G_CALLBACK(ToggleMicNoise), FALSE}, +#endif + { "enablefpslimiter", NULL, "_Limit framerate", NULL, NULL, G_CALLBACK(ToggleFpsLimiter), TRUE}, + { "frameskipA", NULL, "_Auto-minimize skipping", NULL, NULL, G_CALLBACK(ToggleAutoFrameskip), TRUE}, + { "gap", NULL, "Screen _Gap", NULL, NULL, G_CALLBACK(ToggleGap), FALSE}, + { "view_menu", NULL, "Show _menu", NULL, NULL, G_CALLBACK(ToggleMenuVisible), TRUE}, + { "view_toolbar", NULL, "Show _toolbar", NULL, NULL, G_CALLBACK(ToggleToolbarVisible), TRUE}, + { "view_statusbar", NULL, "Show _statusbar", NULL, NULL, G_CALLBACK(ToggleStatusbarVisible), TRUE}, + { "orient_swapscreens", NULL, "S_wap screens", "space", NULL, G_CALLBACK(ToggleSwapScreens), FALSE}, + { "fullscreen", NULL, "_Fullscreen", "F11", NULL, G_CALLBACK(ToggleFullscreen), FALSE}, +}; + +static const GtkRadioActionEntry pri_interpolation_entries[] = { + { "pri_interp_none", NULL, VideoFilterAttributesList[VideoFilterTypeID_None].typeString, NULL, NULL, VideoFilterTypeID_None}, + { "pri_interp_lq2x", NULL, VideoFilterAttributesList[VideoFilterTypeID_LQ2X].typeString, NULL, NULL, VideoFilterTypeID_LQ2X}, + { "pri_interp_lq2xs", NULL, VideoFilterAttributesList[VideoFilterTypeID_LQ2XS].typeString, NULL, NULL, VideoFilterTypeID_LQ2XS}, + { "pri_interp_hq2x", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ2X].typeString, NULL, NULL, VideoFilterTypeID_HQ2X}, + { "pri_interp_hq2xs", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ2XS].typeString, NULL, NULL, VideoFilterTypeID_HQ2XS}, + { "pri_interp_hq3x", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ3X].typeString, NULL, NULL, VideoFilterTypeID_HQ3X}, + { "pri_interp_hq3xs", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ3XS].typeString, NULL, NULL, VideoFilterTypeID_HQ3XS}, + { "pri_interp_hq4x", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ4X].typeString, NULL, NULL, VideoFilterTypeID_HQ4X}, + { "pri_interp_hq4xs", NULL, VideoFilterAttributesList[VideoFilterTypeID_HQ4XS].typeString, NULL, NULL, VideoFilterTypeID_HQ4XS}, + { "pri_interp_2xsai", NULL, VideoFilterAttributesList[VideoFilterTypeID_2xSaI].typeString, NULL, NULL, VideoFilterTypeID_2xSaI}, + { "pri_interp_super2xsai", NULL, VideoFilterAttributesList[VideoFilterTypeID_Super2xSaI].typeString, NULL, NULL, VideoFilterTypeID_Super2xSaI}, + { "pri_interp_supereagle", NULL, VideoFilterAttributesList[VideoFilterTypeID_SuperEagle].typeString, NULL, NULL, VideoFilterTypeID_SuperEagle}, + { "pri_interp_scanline", NULL, VideoFilterAttributesList[VideoFilterTypeID_Scanline].typeString, NULL, NULL, VideoFilterTypeID_Scanline}, + { "pri_interp_bilinear", NULL, VideoFilterAttributesList[VideoFilterTypeID_Bilinear].typeString, NULL, NULL, VideoFilterTypeID_Bilinear}, + { "pri_interp_nearest2x", NULL, VideoFilterAttributesList[VideoFilterTypeID_Nearest2X].typeString, NULL, NULL, VideoFilterTypeID_Nearest2X}, + { "pri_interp_nearest_1point5x", NULL, VideoFilterAttributesList[VideoFilterTypeID_Nearest1_5X].typeString, NULL, NULL, VideoFilterTypeID_Nearest1_5X}, + { "pri_interp_nearestplus_1point5x", NULL, VideoFilterAttributesList[VideoFilterTypeID_NearestPlus1_5X].typeString, NULL, NULL, VideoFilterTypeID_NearestPlus1_5X}, + { "pri_interp_epx", NULL, VideoFilterAttributesList[VideoFilterTypeID_EPX].typeString, NULL, NULL, VideoFilterTypeID_EPX}, + { "pri_interp_epxplus", NULL, VideoFilterAttributesList[VideoFilterTypeID_EPXPlus].typeString, NULL, NULL, VideoFilterTypeID_EPXPlus}, + { "pri_interp_epx_1point5x", NULL, VideoFilterAttributesList[VideoFilterTypeID_EPX1_5X].typeString, NULL, NULL, VideoFilterTypeID_EPX1_5X}, + { "pri_interp_epxplus_1point5x", NULL, VideoFilterAttributesList[VideoFilterTypeID_EPXPlus1_5X].typeString, NULL, NULL, VideoFilterTypeID_EPXPlus1_5X}, + { "pri_interp_2xbrz", NULL, VideoFilterAttributesList[VideoFilterTypeID_2xBRZ].typeString, NULL, NULL, VideoFilterTypeID_2xBRZ}, + { "pri_interp_3xbrz", NULL, VideoFilterAttributesList[VideoFilterTypeID_3xBRZ].typeString, NULL, NULL, VideoFilterTypeID_3xBRZ}, + { "pri_interp_4xbrz", NULL, VideoFilterAttributesList[VideoFilterTypeID_4xBRZ].typeString, NULL, NULL, VideoFilterTypeID_4xBRZ}, + { "pri_interp_5xbrz", NULL, VideoFilterAttributesList[VideoFilterTypeID_5xBRZ].typeString, NULL, NULL, VideoFilterTypeID_5xBRZ}, +}; + +static const GtkRadioActionEntry interpolation_entries[] = { + { "interp_fast", NULL, "_Fast", NULL, NULL, CAIRO_FILTER_FAST }, + { "interp_nearest", NULL, "_Nearest-neighbor", NULL, NULL, CAIRO_FILTER_NEAREST }, + { "interp_good", NULL, "_Good", NULL, NULL, CAIRO_FILTER_GOOD }, + { "interp_bilinear", NULL, "_Bilinear", NULL, NULL, CAIRO_FILTER_BILINEAR }, + { "interp_best", NULL, "B_est", NULL, NULL, CAIRO_FILTER_BEST }, +}; + +static const GtkRadioActionEntry rotation_entries[] = { + { "rotate_0", "gtk-orientation-portrait", "_0", NULL, NULL, 0 }, + { "rotate_90", "gtk-orientation-landscape", "_90", NULL, NULL, 90 }, + { "rotate_180", "gtk-orientation-reverse-portrait", "_180",NULL, NULL, 180 }, + { "rotate_270", "gtk-orientation-reverse-landscape", "_270",NULL, NULL, 270 }, +}; + +enum winsize_enum { + WINSIZE_SCALE = 0, + WINSIZE_HALF = 1, + WINSIZE_1 = 2, + WINSIZE_1HALF = 3, + WINSIZE_2 = 4, + WINSIZE_2HALF = 5, + WINSIZE_3 = 6, + WINSIZE_4 = 8, + WINSIZE_5 = 10, +}; + +static winsize_enum winsize_current; + +static const GtkRadioActionEntry winsize_entries[] = { + { "winsize_half", NULL, "0_.5x", NULL, NULL, WINSIZE_HALF }, + { "winsize_1", NULL, "_1x", NULL, NULL, WINSIZE_1 }, + { "winsize_1half", NULL, "1.5x", NULL, NULL, WINSIZE_1HALF }, + { "winsize_2", NULL, "_2x", NULL, NULL, WINSIZE_2 }, + { "winsize_2half", NULL, "2.5x", NULL, NULL, WINSIZE_2HALF }, + { "winsize_3", NULL, "_3x", NULL, NULL, WINSIZE_3 }, + { "winsize_4", NULL, "_4x", NULL, NULL, WINSIZE_4 }, + { "winsize_5", NULL, "_5x", NULL, NULL, WINSIZE_5 }, + { "winsize_scale", NULL, "_Scale to window", NULL, NULL, WINSIZE_SCALE }, +}; + +/* When adding modes here remember to add the relevent entry to screen_size */ +enum orientation_enum { + ORIENT_VERTICAL = 0, + ORIENT_HORIZONTAL = 1, + ORIENT_SINGLE = 2, + ORIENT_N +}; + +static const GtkRadioActionEntry orientation_entries[] = { + { "orient_vertical", NULL, "_Vertical", "1", NULL, ORIENT_VERTICAL }, + { "orient_horizontal", NULL, "_Horizontal", "2", NULL, ORIENT_HORIZONTAL }, + { "orient_single", NULL, "_Single screen", "0", NULL, ORIENT_SINGLE }, +}; + +struct screen_size_t { + gint width; + gint height; +}; + +const struct screen_size_t screen_size[ORIENT_N] = { + {256, 384}, + {512, 192}, + {256, 192} +}; + +enum spumode_enum { + SPUMODE_DUALASYNC = 0, + SPUMODE_SYNCN = 1, + SPUMODE_SYNCZ = 2, + SPUMODE_SYNCP = 3 +}; + +static const GtkRadioActionEntry spumode_entries[] = { + { "SPUModeSyncN", NULL, "Synchronous (N)", NULL, NULL, SPUMODE_SYNCN}, + { "SPUModeSyncZ", NULL, "Synchronous (Z)", NULL, NULL, SPUMODE_SYNCZ}, +#ifdef HAVE_LIBSOUNDTOUCH + { "SPUModeSyncP", NULL, "Synchronous (P)", NULL, NULL, SPUMODE_SYNCP}, +#endif + { "SPUModeDualASync", NULL, "Dual Asynchronous", NULL, NULL, SPUMODE_DUALASYNC} +}; + +static const GtkRadioActionEntry spuinterpolation_entries[] = { + { "SPUInterpolationNone", NULL, "_None", NULL, NULL, SPUInterpolation_None }, + { "SPUInterpolationLinear", NULL, "_Linear", NULL, NULL, SPUInterpolation_Linear }, + { "SPUInterpolationCosine", NULL, "_Cosine", NULL, NULL, SPUInterpolation_Cosine } +}; + +enum frameskip_enum { + FRAMESKIP_0 = 0, + FRAMESKIP_1 = 1, + FRAMESKIP_2 = 2, + FRAMESKIP_3 = 3, + FRAMESKIP_4 = 4, + FRAMESKIP_5 = 5, + FRAMESKIP_6 = 6, + FRAMESKIP_7 = 7, + FRAMESKIP_8 = 8, + FRAMESKIP_9 = 9, +}; + +static const GtkRadioActionEntry frameskip_entries[] = { + { "frameskip0", NULL, "_0 (never skip)", NULL, NULL, FRAMESKIP_0}, + { "frameskip1", NULL, "_1", NULL, NULL, FRAMESKIP_1}, + { "frameskip2", NULL, "_2", NULL, NULL, FRAMESKIP_2}, + { "frameskip3", NULL, "_3", NULL, NULL, FRAMESKIP_3}, + { "frameskip4", NULL, "_4", NULL, NULL, FRAMESKIP_4}, + { "frameskip5", NULL, "_5", NULL, NULL, FRAMESKIP_5}, + { "frameskip6", NULL, "_6", NULL, NULL, FRAMESKIP_6}, + { "frameskip7", NULL, "_7", NULL, NULL, FRAMESKIP_7}, + { "frameskip8", NULL, "_8", NULL, NULL, FRAMESKIP_8}, + { "frameskip9", NULL, "_9", NULL, NULL, FRAMESKIP_9}, +}; + +static const GtkRadioActionEntry savet_entries[] = { + { "save_t0", NULL, "_0 Autodetect", NULL, NULL, 0}, + { "save_t1", NULL, "_1 EEPROM 4kbit", NULL, NULL, 1}, + { "save_t2", NULL, "_2 EEPROM 64kbit", NULL, NULL, 2}, + { "save_t3", NULL, "_3 EEPROM 512kbit", NULL, NULL, 3}, + { "save_t4", NULL, "_4 FRAM 256kbit", NULL, NULL, 4}, + { "save_t5", NULL, "_5 FLASH 2mbit", NULL, NULL, 5}, + { "save_t6", NULL, "_6 FLASH 4mbit", NULL, NULL, 6} +}; + +SoundInterface_struct *SNDCoreList[] = { +&SNDDummy, +&SNDSDL, +NULL +}; + +GPU3DInterface *core3DList[] = { + &gpu3DNull, + &gpu3DRasterize, +#ifdef HAVE_OPENGL + &gpu3Dgl, +#endif +}; + +int multisampleSizes[] = {0, 2, 4, 8, 16, 32}; + +static const u32 gtk_kb_cfg[NB_KEYS] = { + GDK_KEY_x, // A + GDK_KEY_z, // B + GDK_KEY_Shift_R, // select + GDK_KEY_Return, // start + GDK_KEY_Right, // Right + GDK_KEY_Left, // Left + GDK_KEY_Up, // Up + GDK_KEY_Down, // Down + GDK_KEY_w, // R + GDK_KEY_q, // L + GDK_KEY_s, // X + GDK_KEY_a, // Y + GDK_KEY_p, // DEBUG + GDK_KEY_o, // BOOST + GDK_KEY_BackSpace, // Lid +}; + +GKeyFile *keyfile; + +struct modify_key_ctx { + gint mk_key_chosen; + GtkWidget *label; + u8 key_id; +}; + +static u16 keys_latch = 0; +static u16 gdk_shift_pressed = 0; +u16 Keypad_Temp[NB_KEYS]; + +class configured_features : public CommandLine +{ +public: + int engine_3d; + int savetype; + + int firmware_language; + + int timeout; +}; + +static void +init_configured_features( class configured_features *config ) +{ + if(config->render3d == COMMANDLINE_RENDER3D_GL || config->render3d == COMMANDLINE_RENDER3D_OLDGL || config->render3d == COMMANDLINE_RENDER3D_AUTOGL) + config->engine_3d = 2; + else if (config->render3d == COMMANDLINE_RENDER3D_DEFAULT) + // Setting it to -1 so that common_gtk_main() will ignore it and load it from file or reconfigure it. + config->engine_3d = -1; + else + config->engine_3d = 1; + + config->savetype = 0; + + config->timeout = 0; + + /* use the default language */ + config->firmware_language = -1; + + /* If specified by --lang option the lang will change to choosed one */ + config->firmware_language = config->language; +} + +static int +fill_configured_features( class configured_features *config, + char ** argv) +{ + GOptionEntry options[] = { + { "3d-render", 0, 0, G_OPTION_ARG_INT, &config->engine_3d, "Select 3D rendering engine. Available engines:\n" + "\t\t\t\t 0 = 3d disabled\n" + "\t\t\t\t 1 = internal rasterizer (default)\n" +#ifdef HAVE_OPENGL + "\t\t\t\t 2 = opengl\n" +#endif + ,"ENGINE"}, + { "save-type", 0, 0, G_OPTION_ARG_INT, &config->savetype, "Select savetype from the following:\n" + "\t\t\t\t 0 = Autodetect (default)\n" + "\t\t\t\t 1 = EEPROM 4kbit\n" + "\t\t\t\t 2 = EEPROM 64kbit\n" + "\t\t\t\t 3 = EEPROM 512kbit\n" + "\t\t\t\t 4 = FRAM 256kbit\n" + "\t\t\t\t 5 = FLASH 2mbit\n" + "\t\t\t\t 6 = FLASH 4mbit\n", + "SAVETYPE"}, + { "fwlang", 0, 0, G_OPTION_ARG_INT, &config->firmware_language, "Set the language in the firmware, LANG as follows:\n" + "\t\t\t\t 0 = Japanese\n" + "\t\t\t\t 1 = English\n" + "\t\t\t\t 2 = French\n" + "\t\t\t\t 3 = German\n" + "\t\t\t\t 4 = Italian\n" + "\t\t\t\t 5 = Spanish\n", + "LANG"}, + { "timeout", 0, 0, G_OPTION_ARG_INT, &config->timeout, "Quit DeSmuME after the specified seconds for testing purpose.", "SECONDS"}, + { NULL } + }; + + //g_option_context_add_main_entries (config->ctx, options, "options"); + //g_option_context_add_group (config->ctx, gtk_get_option_group (TRUE)); + + if(!config->validate()) + goto error; + + if (config->savetype < 0 || config->savetype > 6) { + g_printerr("Accepted savetypes are from 0 to 6.\n"); + return false; + } + + if (config->firmware_language < -1 || config->firmware_language > 5) { + g_printerr("Firmware language must be set to a value from 0 to 5.\n"); + goto error; + } + + // Check if the commandLine argument was actually passed + if (config->engine_3d != -1) { + if (config->engine_3d != 0 && config->engine_3d != 1 +#ifdef HAVE_OPENGL + && config->engine_3d != 2 +#endif + ) { + g_printerr("Currently available ENGINES: 0, 1" +#ifdef HAVE_OPENGL + ", 2" +#endif + "\n"); + goto error; + } + } +#ifdef GDB_STUB + if (config->arm9_gdb_port != 0 && (config->arm9_gdb_port < 1 || config->arm9_gdb_port > 65535)) { + g_printerr("ARM9 GDB stub port must be in the range 1 to 65535\n"); + goto error; + } + + if (config->arm7_gdb_port != 0 && (config->arm7_gdb_port < 1 || config->arm7_gdb_port > 65535)) { + g_printerr("ARM7 GDB stub port must be in the range 1 to 65535\n"); + goto error; + } +#endif + + return 1; + +error: + config->errorHelp(argv[0]); + + return 0; +} + + +/* + * The thread handling functions needed by the GDB stub code. + */ +#ifdef GDB_STUB +void * +createThread_gdb( void (*thread_function)( void *data), + void *thread_data) +{ + GThread *new_thread = g_thread_new(NULL, + (GThreadFunc)thread_function, + thread_data); + + return new_thread; +} + +void +joinThread_gdb( void *thread_handle) { + g_thread_join( (GThread *)thread_handle); +} +#endif + +/************************ GTK *******************************/ + +uint SPUMode = SPUMODE_DUALASYNC; +uint Frameskip = 0; +uint autoFrameskipMax = 0; +bool autoframeskip = true; +cairo_filter_t Interpolation = CAIRO_FILTER_NEAREST; + +static GtkWidget *pWindow; +static GtkWidget *pStatusBar; +static GtkWidget *pDrawingArea; +static GtkActionGroup * action_group; +static GtkUIManager *ui_manager; + +struct nds_screen_t { + guint gap_size; + gint rotation_angle; + orientation_enum orientation; + cairo_matrix_t touch_matrix; + cairo_matrix_t topscreen_matrix; + gboolean swap; +}; + +struct nds_screen_t nds_screen; + +static BOOL regMainLoop = FALSE; + +static inline void UpdateStatusBar (const char *message) +{ + gint pStatusBar_Ctx; + + pStatusBar_Ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(pStatusBar), "Global"); + gtk_statusbar_pop(GTK_STATUSBAR(pStatusBar), pStatusBar_Ctx); + gtk_statusbar_push(GTK_STATUSBAR(pStatusBar), pStatusBar_Ctx, message); +} + +static void About() +{ + GdkPixbuf * pixbuf = gdk_pixbuf_new_from_xpm_data(DeSmuME_xpm); + + static const gchar *authors[] = { + "yopyop (original author)", + "DeSmuME team", + NULL + }; + + gtk_show_about_dialog(GTK_WINDOW(pWindow), + "program-name", "DeSmuME", + "version", EMU_DESMUME_VERSION_STRING() + 1, // skip space + "website", "http://desmume.org", + "logo", pixbuf, + "comments", "Nintendo DS emulator based on work by Yopyop", + "authors", authors, + NULL); + + g_object_unref(pixbuf); +} + +static void ToggleMenuVisible(GtkToggleAction *action) +{ + GtkWidget *pMenuBar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); + + config.view_menu = gtk_toggle_action_get_active(action); + if (config.view_menu) + gtk_widget_show(pMenuBar); + else + gtk_widget_hide(pMenuBar); +} + +static void ToggleToolbarVisible(GtkToggleAction *action) +{ + GtkWidget *pToolBar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); + + config.view_toolbar = gtk_toggle_action_get_active(action); + if (config.view_toolbar) + gtk_widget_show(pToolBar); + else + gtk_widget_hide(pToolBar); +} + +static void ToggleStatusbarVisible(GtkToggleAction *action) +{ + config.view_statusbar = gtk_toggle_action_get_active(action); + if (config.view_statusbar) + gtk_widget_show(pStatusBar); + else + gtk_widget_hide(pStatusBar); +} + +static void ToggleFullscreen(GtkToggleAction *action) +{ + GtkWidget *pMenuBar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); + GtkWidget *pToolBar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); + config.window_fullscreen = gtk_toggle_action_get_active(action); + if (config.window_fullscreen) + { + GdkColor black = {0, 0, 0, 0}; + gtk_widget_modify_bg(pDrawingArea, GTK_STATE_NORMAL, &black); + + gtk_widget_hide(pMenuBar); + gtk_widget_hide(pToolBar); + gtk_widget_hide(pStatusBar); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_menu"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_toolbar"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_statusbar"), FALSE); + gtk_window_fullscreen(GTK_WINDOW(pWindow)); + } + else + { + gtk_widget_modify_bg(pDrawingArea, GTK_STATE_NORMAL, NULL); + + if (config.view_menu) { + gtk_widget_show(pMenuBar); + } + if (config.view_toolbar) { + gtk_widget_show(pToolBar); + } + if (config.view_statusbar) { + gtk_widget_show(pStatusBar); + } + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_menu"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_toolbar"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_statusbar"), TRUE); + gtk_window_unfullscreen(GTK_WINDOW(pWindow)); + } +} + + +static int Open(const char *filename) +{ + int res; + ResetSaveStateTimes(); + res = NDS_LoadROM( filename ); + if(res > 0) { + LoadSaveStateInfo(); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "cheatlist"), TRUE); + } + return res; +} + +void Launch() +{ + GtkWidget *pause; + desmume_resume(); + + if(!regMainLoop) { + g_idle_add_full(EMULOOP_PRIO, &EmuLoop, pWindow, NULL); + regMainLoop = TRUE; + } + + UpdateStatusBar("Running ..."); + + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "pause"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "reset"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "printscreen"), TRUE); + + pause = gtk_bin_get_child(GTK_BIN(gtk_ui_manager_get_widget(ui_manager, "/ToolBar/pause"))); + gtk_widget_grab_focus(pause); +} + +void Pause() +{ + GtkWidget *run; + desmume_pause(); + UpdateStatusBar("Paused"); + + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "pause"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), TRUE); + + run = gtk_bin_get_child(GTK_BIN(gtk_ui_manager_get_widget(ui_manager, "/ToolBar/run"))); + gtk_widget_grab_focus(run); +} + +static void LoadStateDialog() +{ + GtkFileFilter *pFilter_ds, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_ds = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_ds, "*.ds*"); + gtk_file_filter_set_name(pFilter_ds, "DeSmuME binary (.ds*)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Load State From ...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_ds); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + + if(savestate_load(sPath) == false ) { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to load :\n%s", sPath); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } else { + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), TRUE); + } + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void RecordMovieDialog() +{ + GtkFileFilter *pFilter_dsm, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_dsm = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_dsm, "*.dsm*"); + gtk_file_filter_set_name(pFilter_dsm, "DeSmuME movie file (.dsm*)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Save Movie To ...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (pFileSelection), TRUE); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_dsm); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + FCEUI_SaveMovie(sPath,L"",START_BLANK,"", FCEUI_MovieGetRTCDefault()); + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void StopMovie() +{ + FCEUI_StopMovie(); +} + +static void PlayMovieDialog() +{ + GtkFileFilter *pFilter_dsm, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_dsm = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_dsm, "*.dsm*"); + gtk_file_filter_set_name(pFilter_dsm, "DeSmuME movie file (.dsm*)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Play movie from...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (pFileSelection), TRUE); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_dsm); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + + FCEUI_LoadMovie(sPath,true,false,-1); + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void SaveStateDialog() +{ + GtkFileFilter *pFilter_ds, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_ds = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_ds, "*.ds*"); + gtk_file_filter_set_name(pFilter_ds, "DeSmuME binary (.ds*)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Save State To ...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (pFileSelection), TRUE); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_ds); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + + if(savestate_save(sPath) == false ) { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to save :\n%s", sPath); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } else { + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), TRUE); + } + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void RecordAV_x264() +{ + GtkFileFilter *pFilter_mkv, *pFilter_mp4, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_mkv = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_mkv, "*.mkv"); + gtk_file_filter_set_name(pFilter_mkv, "Matroska (.mkv)"); + + pFilter_mp4 = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_mp4, "*.mp4"); + gtk_file_filter_set_name(pFilter_mp4, "MP4 (.mp4)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Save video...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (pFileSelection), TRUE); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_mkv); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_mp4); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + + if(avout_x264.begin(sPath)) { + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "record_x264"), FALSE); + } else { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to record video to:\n%s", sPath); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void RecordAV_flac() +{ + GtkFileFilter *pFilter_flac, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_flac = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_flac, "*.flac"); + gtk_file_filter_set_name(pFilter_flac, "FLAC file (.flac)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Save audio...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (pFileSelection), TRUE); + + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_flac); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + + if(avout_flac.begin(sPath)) { + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "record_flac"), FALSE); + } else { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to record audio to:\n%s", sPath); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void RecordAV_stop() { + avout_x264.end(); + avout_flac.end(); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "record_x264"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "record_flac"), TRUE); +} + +static void OpenNdsDialog() +{ + GtkFileFilter *pFilter_nds, *pFilter_dsgba, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + if (desmume_running()) + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_nds = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_nds, "*.[nN][dD][sS]"); +#ifdef HAVE_LIBZ + gtk_file_filter_add_pattern(pFilter_nds, "*.[nN][dD][sS].gz"); +#endif +#ifdef HAVE_LIBZZIP + gtk_file_filter_add_pattern(pFilter_nds, "*.[nN][dD][sS].zip"); +#endif + gtk_file_filter_set_name(pFilter_nds, "Nds binary (.nds)"); + + pFilter_dsgba = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_dsgba, "*.ds.gba"); + gtk_file_filter_set_name(pFilter_dsgba, "Nds binary with loader (.ds.gba)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + /* Creating the selection window */ + pFileSelection = gtk_file_chooser_dialog_new("Open...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + /* Only the dialog window is accepting events: */ + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_nds); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_dsgba); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(pFileSelection), g_get_home_dir()); + + /* Showing the window */ + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + if(Open((const char*)sPath) < 0) { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to load :\n%s", sPath); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } else { + GtkRecentData recentData; + gchar *uri; + memset(&recentData, 0, sizeof(GtkRecentData)); + recentData.mime_type = g_strdup("application/x-nintendo-ds-rom"); + recentData.app_name = (gchar *) g_get_application_name (); + recentData.app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL); + + GtkRecentManager *manager; + manager = gtk_recent_manager_get_default (); + uri = g_filename_to_uri (sPath, NULL, NULL); + gtk_recent_manager_add_full (manager, uri, &recentData); + + g_free(uri); + g_free(recentData.app_exec); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), TRUE); + Launch(); + } + + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); +} + +static void OpenRecent(GtkRecentChooser *chooser, gpointer user_data) +{ + GtkRecentManager *recent_manager = gtk_recent_manager_get_default(); + gchar *uri, *romname; + int ret; + + if (desmume_running()) + Pause(); + + uri = gtk_recent_chooser_get_current_uri(chooser); + romname = g_filename_from_uri(uri, NULL, NULL); + ret = Open(romname); + if (ret > 0) { + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), TRUE); + Launch(); + } else { + gtk_recent_manager_remove_item(recent_manager, uri, NULL); + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to load :\n%s", uri); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } + + g_free(uri); + g_free(romname); +} + +static void Reset() +{ + bool shouldBeRunning = desmume_running(); + Pause(); + NDS_Reset(); + RedrawScreen(); + if (shouldBeRunning) { + Launch(); + } +} + + +/////////////////////////////// DRAWING SCREEN ////////////////////////////////// +static void UpdateDrawingAreaAspect() +{ + gint H, W; + + if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) { + W = screen_size[nds_screen.orientation].width; + H = screen_size[nds_screen.orientation].height; + } else { + W = screen_size[nds_screen.orientation].height; + H = screen_size[nds_screen.orientation].width; + } + + if (nds_screen.orientation != ORIENT_SINGLE) { + if (nds_screen.orientation == ORIENT_VERTICAL) { + if ((nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180)) { + H += nds_screen.gap_size; + } else { + W += nds_screen.gap_size; + } + } + } + + if (winsize_current == WINSIZE_SCALE) { + gtk_window_set_resizable(GTK_WINDOW(pWindow), TRUE); + gtk_widget_set_size_request(GTK_WIDGET(pDrawingArea), W, H); + } else { + gtk_window_unmaximize(GTK_WINDOW(pWindow)); + gtk_window_set_resizable(GTK_WINDOW(pWindow), FALSE); + gtk_widget_set_size_request(GTK_WIDGET(pDrawingArea), W * winsize_current / 2, H * winsize_current / 2); + } +} + +static void ToggleGap(GtkToggleAction* action) +{ + config.view_gap = gtk_toggle_action_get_active(action); + nds_screen.gap_size = config.view_gap ? GAP_SIZE : 0; + UpdateDrawingAreaAspect(); +} + +static void SetRotation(GtkAction *action, GtkRadioAction *current) +{ + nds_screen.rotation_angle = gtk_radio_action_get_current_value(current); + config.view_rot = nds_screen.rotation_angle; + UpdateDrawingAreaAspect(); +} + +static void SetWinsize(GtkAction *action, GtkRadioAction *current) +{ + winsize_current = (winsize_enum) gtk_radio_action_get_current_value(current); + config.window_scale = winsize_current; + if (config.window_fullscreen) { + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "fullscreen"), FALSE); + } + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "fullscreen"), winsize_current == WINSIZE_SCALE); + UpdateDrawingAreaAspect(); +} + +static void SetOrientation(GtkAction *action, GtkRadioAction *current) +{ + nds_screen.orientation = (orientation_enum)gtk_radio_action_get_current_value(current); +#ifdef HAVE_LIBAGG + osd->singleScreen = nds_screen.orientation == ORIENT_SINGLE; +#endif + config.view_orient = nds_screen.orientation; + UpdateDrawingAreaAspect(); +} + +static void ToggleSwapScreens(GtkToggleAction *action) { + nds_screen.swap = gtk_toggle_action_get_active(action); +#ifdef HAVE_LIBAGG + osd->swapScreens = nds_screen.swap; +#endif + config.view_swap = nds_screen.swap; + RedrawScreen(); +} + +static int ConfigureDrawingArea(GtkWidget *widget, GdkEventConfigure *event, gpointer data) +{ + return TRUE; +} + +static inline void gpu_screen_to_rgb(u32* dst) +{ + ColorspaceConvertBuffer555To8888Opaque(GPU->GetDisplayInfo().masterNativeBuffer16, dst, GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2); +} + +static inline void drawScreen(cairo_t* cr, u32* buf, gint w, gint h) { + cairo_surface_t* surf = cairo_image_surface_create_for_data((u8*)buf, CAIRO_FORMAT_RGB24, w, h, w * 4); + cairo_set_source_surface(cr, surf, 0, 0); + cairo_pattern_set_filter(cairo_get_source(cr), Interpolation); + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + cairo_surface_destroy(surf); +} + +static inline void drawTopScreen(cairo_t* cr, u32* buf, gint w, gint h, gint gap, gint rotation_angle, bool swap, orientation_enum orientation) { + if (orientation == ORIENT_SINGLE && swap) { + return; + } + cairo_save(cr); + switch (orientation) { + case ORIENT_VERTICAL: + if (swap) { + cairo_translate(cr, 0, h + gap); + } + break; + case ORIENT_HORIZONTAL: + if (swap) { + cairo_translate(cr, w, 0); + } + break; + } + // Used for HUD editor mode + cairo_get_matrix(cr, &nds_screen.topscreen_matrix); + drawScreen(cr, buf, w, h); + cairo_restore(cr); +} + +static inline void drawBottomScreen(cairo_t* cr, u32* buf, gint w, gint h, gint gap, gint rotation_angle, bool swap, orientation_enum orientation) { + if (orientation == ORIENT_SINGLE && !swap) { + return; + } + cairo_save(cr); + switch (orientation) { + case ORIENT_VERTICAL: + if (!swap) { + cairo_translate(cr, 0, h + gap); + } + break; + case ORIENT_HORIZONTAL: + if (!swap) { + cairo_translate(cr, w, 0); + } + break; + } + // Store the matrix for converting touchscreen coordinates + cairo_get_matrix(cr, &nds_screen.touch_matrix); + drawScreen(cr, buf, w, h); + cairo_restore(cr); +} + +/* Drawing callback */ +static gboolean ExposeDrawingArea (GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + GdkWindow* window = gtk_widget_get_window(widget); + gint daW, daH; +#if GTK_CHECK_VERSION(2,24,0) + daW = gdk_window_get_width(window); + daH = gdk_window_get_height(window); +#else + gdk_drawable_get_size(window, &daW, &daH); +#endif + u32* fbuf = video->GetDstBufferPtr(); + gint dstW = video->GetDstWidth(); + gint dstH = video->GetDstHeight(); + + gint dstScale = dstW * 2 / 256; // Actual scale * 2 to handle 1.5x filters + + gint gap = nds_screen.orientation == ORIENT_VERTICAL ? nds_screen.gap_size * dstScale / 2 : 0; + gint imgW, imgH; + if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) { + imgW = screen_size[nds_screen.orientation].width * dstScale / 2; + imgH = screen_size[nds_screen.orientation].height * dstScale / 2 + gap; + } else { + imgH = screen_size[nds_screen.orientation].width * dstScale / 2; + imgW = screen_size[nds_screen.orientation].height * dstScale / 2 + gap; + } + + // Calculate scale to fit display area to window + gfloat hratio = (gfloat)daW / (gfloat)imgW; + gfloat vratio = (gfloat)daH / (gfloat)imgH; + hratio = MIN(hratio, vratio); + vratio = hratio; + + cairo_t* cr = gdk_cairo_create(window); + + // Scale to window size at center of area + cairo_translate(cr, daW / 2, daH / 2); + cairo_scale(cr, hratio, vratio); + // Rotate area + cairo_rotate(cr, M_PI / 180 * nds_screen.rotation_angle); + // Translate area to top-left corner + if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) { + cairo_translate(cr, -imgW / 2, -imgH / 2); + } else { + cairo_translate(cr, -imgH / 2, -imgW / 2); + } + // Draw both screens + drawTopScreen(cr, fbuf, dstW, dstH / 2, gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation); + drawBottomScreen(cr, fbuf + dstW * dstH / 2, dstW, dstH / 2, gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation); + // Draw gap + cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); + cairo_rectangle(cr, 0, dstH / 2, dstW, gap); + cairo_fill(cr); + // Complete the touch transformation matrix + cairo_matrix_scale(&nds_screen.topscreen_matrix, (double)dstScale / 2, (double)dstScale / 2); + cairo_matrix_invert(&nds_screen.topscreen_matrix); + cairo_matrix_scale(&nds_screen.touch_matrix, (double)dstScale / 2, (double)dstScale / 2); + cairo_matrix_invert(&nds_screen.touch_matrix); + + cairo_destroy(cr); + draw_count++; + + return TRUE; +} + +static void RedrawScreen() { + ColorspaceConvertBuffer555To8888Opaque(GPU->GetDisplayInfo().masterNativeBuffer16, (uint32_t *)video->GetSrcBufferPtr(), GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2); +#ifdef HAVE_LIBAGG + aggDraw.hud->attach((u8*)video->GetSrcBufferPtr(), 256, 384, 1024); + osd->update(); + DrawHUD(); + osd->clear(); +#endif + video->RunFilter(); + gtk_widget_queue_draw(pDrawingArea); +} + +/////////////////////////////// KEYS AND STYLUS UPDATE /////////////////////////////////////// + +#ifdef HAVE_LIBAGG +static gboolean rotoscaled_hudedit(gint x, gint y, gboolean start) +{ + double devX, devY; + gint X, Y, topX = -1, topY = -1, botX = -1, botY = -1; + static gint startScreen = 0; + + if (nds_screen.orientation != ORIENT_SINGLE || !nds_screen.swap) { + devX = x; + devY = y; + cairo_matrix_transform_point(&nds_screen.topscreen_matrix, &devX, &devY); + topX = devX; + topY = devY; + } + + if (nds_screen.orientation != ORIENT_SINGLE || nds_screen.swap) { + devX = x; + devY = y; + cairo_matrix_transform_point(&nds_screen.touch_matrix, &devX, &devY); + botX = devX; + botY = devY; + } + + if (topX >= 0 && topY >= 0 && topX < 256 && topY < 192) { + X = topX; + Y = topY + (nds_screen.swap ? 192 : 0); + startScreen = 0; + } else if (botX >= 0 && botY >= 0 && botX < 256 && botY < 192) { + X = botX; + Y = botY + (nds_screen.swap ? 0 : 192); + startScreen = 1; + } else if (!start) { + if (startScreen == 0) { + X = CLAMP(topX, 0, 255); + Y = CLAMP(topY, 0, 191) + (nds_screen.swap ? 192 : 0); + } else { + X = CLAMP(botX, 0, 255); + Y = CLAMP(botY, 0, 191) + (nds_screen.swap ? 0 : 192); + } + } else { + LOG("TopX=%d, TopY=%d, BotX=%d, BotY=%d\n", topX, topY, botX, botY); + return FALSE; + } + + LOG("TopX=%d, TopY=%d, BotX=%d, BotY=%d, X=%d, Y=%d\n", topX, topY, botX, botY, X, Y); + EditHud(X, Y, &Hud); + RedrawScreen(); + return TRUE; +} +#endif + +static gboolean rotoscaled_touchpos(gint x, gint y, gboolean start) +{ + double devX, devY; + u16 EmuX, EmuY; + gint X, Y; + + if (nds_screen.orientation == ORIENT_SINGLE && !nds_screen.swap) { + return FALSE; + } + + devX = x; + devY = y; + cairo_matrix_transform_point(&nds_screen.touch_matrix, &devX, &devY); + X = devX; + Y = devY; + + LOG("X=%d, Y=%d\n", X, Y); + + if (!start || (X >= 0 && Y >= 0 && X < 256 && Y < 192)) { + EmuX = CLAMP(X, 0, 255); + EmuY = CLAMP(Y, 0, 191); + NDS_setTouchPos(EmuX, EmuY); + return TRUE; + } + + return FALSE; +} + +static gboolean Stylus_Move(GtkWidget *w, GdkEventMotion *e, gpointer data) +{ + GdkModifierType state; + gint x,y; + + if(click) { + if(e->is_hint) + gdk_window_get_pointer(gtk_widget_get_window(w), &x, &y, &state); + else { + x= (gint)e->x; + y= (gint)e->y; + state=(GdkModifierType)e->state; + } + + if(state & GDK_BUTTON1_MASK) { +#ifdef HAVE_LIBAGG + if (HudEditorMode) { + rotoscaled_hudedit(x, y, FALSE); + } else { +#else + { +#endif + rotoscaled_touchpos(x, y, FALSE); + } + } + } + + return TRUE; +} + +static gboolean Stylus_Press(GtkWidget * w, GdkEventButton * e, + gpointer data) +{ + GdkModifierType state; + gint x, y; + + if (e->button == 3) { + GtkWidget * pMenu = gtk_menu_item_get_submenu ( GTK_MENU_ITEM( + gtk_ui_manager_get_widget (ui_manager, "/MainMenu/ViewMenu"))); + gtk_menu_popup(GTK_MENU(pMenu), NULL, NULL, NULL, NULL, 3, e->time); + } + + if (e->button == 1) { + gdk_window_get_pointer(gtk_widget_get_window(w), &x, &y, &state); + + if(state & GDK_BUTTON1_MASK) { +#ifdef HAVE_LIBAGG + if (HudEditorMode) { + click = rotoscaled_hudedit(x, y, TRUE); + } else +#endif + if (desmume_running()) { + click = rotoscaled_touchpos(x, y, TRUE); + } + } + } + + return TRUE; +} +static gboolean Stylus_Release(GtkWidget *w, GdkEventButton *e, gpointer data) +{ +#ifdef HAVE_LIBAGG + HudClickRelease(&Hud); +#endif + if(click) NDS_releaseTouch(); + click = FALSE; + return TRUE; +} + +static void loadgame(int num){ + if (desmume_running()) + { + Pause(); + loadstate_slot(num); + Launch(); + } + else + loadstate_slot(num); + RedrawScreen(); +} + +static void savegame(int num){ + if (desmume_running()) + { + Pause(); + savestate_slot(num); + Launch(); + } + else + savestate_slot(num); + LoadSaveStateInfo(); + RedrawScreen(); +} + +static void MenuLoad(GtkMenuItem *item, gpointer slot) +{ + loadgame(GPOINTER_TO_INT(slot)); +} + +static void MenuSave(GtkMenuItem *item, gpointer slot) +{ + savegame(GPOINTER_TO_INT(slot)); +} + +static gint Key_Press(GtkWidget *w, GdkEventKey *e, gpointer data) +{ + if (e->keyval == GDK_KEY_Shift_L){ + gdk_shift_pressed |= 1; + return 1; + } + if (e->keyval == GDK_KEY_Shift_R){ + gdk_shift_pressed |= 2; + return 1; + } + if( e->keyval >= GDK_KEY_F1 && e->keyval <= GDK_KEY_F10 ){ + if(!gdk_shift_pressed) + loadgame((e->keyval - GDK_KEY_F1 + 1) % 10); + else + savegame((e->keyval - GDK_KEY_F1 + 1) % 10); + return 1; + } + guint mask; + mask = gtk_accelerator_get_default_mod_mask (); + if( (e->state & mask) == 0){ + u16 Key = lookup_key(e->keyval); + if(Key){ + ADD_KEY( keys_latch, Key ); + return 1; + } + } + +#ifdef PROFILE_MEMORY_ACCESS + if ( e->keyval == GDK_Tab) { + print_memory_profiling(); + return 1; + } +#endif + return 0; +} + +static gint Key_Release(GtkWidget *w, GdkEventKey *e, gpointer data) +{ + if (e->keyval == GDK_KEY_Shift_L){ + gdk_shift_pressed &= ~1; + return 1; + } + if (e->keyval == GDK_KEY_Shift_R){ + gdk_shift_pressed &= ~2; + return 1; + } + u16 Key = lookup_key(e->keyval); + RM_KEY( keys_latch, Key ); + return 1; + +} + +/////////////////////////////// SET AUDIO VOLUME ////////////////////////////////////// + +static void CallbackSetAudioVolume(GtkWidget *hscale, gpointer data) +{ + SNDSDLSetAudioVolume(gtk_range_get_value(GTK_RANGE(hscale))); + config.audio_volume = SNDSDLGetAudioVolume(); +} + +static void SetAudioVolume() +{ + GtkWidget *dialog = NULL; + GtkWidget *hscale = NULL; + int audio_volume = SNDSDLGetAudioVolume(); + dialog = gtk_dialog_new_with_buttons("Set audio volume", GTK_WINDOW(pWindow), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + hscale = gtk_hscale_new_with_range(0, SDL_MIX_MAXVOLUME, 1); + gtk_range_set_value(GTK_RANGE(hscale), SNDSDLGetAudioVolume()); + g_signal_connect(G_OBJECT(hscale), "value-changed", G_CALLBACK(CallbackSetAudioVolume), NULL); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), hscale, TRUE, FALSE, 0); + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog))); + switch(gtk_dialog_run(GTK_DIALOG(dialog))) + { + case GTK_RESPONSE_OK: + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + SNDSDLSetAudioVolume(audio_volume); + config.audio_volume = SNDSDLGetAudioVolume(); + break; + } + gtk_widget_destroy(dialog); +} + +/////////////////////////////// SET FIRMWARE LANGUAGE ////////////////////////////////////// + +static void CallbackSetFirmwareLanguage(GtkWidget *check_button, gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_button))); +} + +static void SetFirmwareLanguage() +{ + GtkWidget *dialog = NULL; + GtkWidget *combo_box_text = NULL; + GtkWidget *check_button = NULL; + const char *languages[6] = {"Japanese", "English", "French", "German", "Italian", "Spanish"}; + gchar *text = NULL; + dialog = gtk_dialog_new_with_buttons("Set firmware language", GTK_WINDOW(pWindow), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + combo_box_text = gtk_combo_box_text_new(); + for(int index = 0; index < 6; index++) + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box_text), languages[index]); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box_text), config.firmware_language); + gtk_widget_set_sensitive(combo_box_text, config.command_line_overriding_firmware_language); + check_button = gtk_check_button_new_with_mnemonic("_Enable command line overriding"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), config.command_line_overriding_firmware_language); + g_signal_connect(G_OBJECT(check_button), "toggled", G_CALLBACK(CallbackSetFirmwareLanguage), combo_box_text); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), check_button, TRUE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo_box_text, TRUE, FALSE, 0); + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog))); + switch(gtk_dialog_run(GTK_DIALOG(dialog))) + { + case GTK_RESPONSE_OK: + text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_box_text)); + for(int index = 0; index < 6; index++) + if(strcmp(text, languages[index]) == 0) + { + CommonSettings.fwConfig.language = index; + config.firmware_language = index; + } + config.command_line_overriding_firmware_language = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_button)); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + break; + } + gtk_widget_destroy(dialog); +} + +/////////////////////////////// CONTROLS EDIT ////////////////////////////////////// + +static void AcceptNewInputKey(GtkWidget *w, GdkEventKey *e, struct modify_key_ctx *ctx) +{ + gchar *YouPressed; + + ctx->mk_key_chosen = e->keyval; + YouPressed = g_strdup_printf("You pressed : %s\nClick OK to keep this key.", gdk_keyval_name(e->keyval)); + gtk_label_set_text(GTK_LABEL(ctx->label), YouPressed); + g_free(YouPressed); +} + +static void Modify_Key(GtkWidget* widget, gpointer data) +{ + struct modify_key_ctx ctx; + GtkWidget *mkDialog; + gchar *Key_Label; + gchar *Title; + gint Key; + + Key = GPOINTER_TO_INT(data); + ctx.mk_key_chosen = 0; + Title = g_strdup_printf("Press \"%s\" key ...\n", key_names[Key]); + mkDialog = gtk_dialog_new_with_buttons(Title, + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_OK, + GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL, + NULL); + + ctx.label = gtk_label_new(Title); + g_free(Title); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(mkDialog))), ctx.label, TRUE, FALSE, 0); + + g_signal_connect(G_OBJECT(mkDialog), "key_press_event", G_CALLBACK(AcceptNewInputKey), &ctx); + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(mkDialog))); + + switch(gtk_dialog_run(GTK_DIALOG(mkDialog))) { + case GTK_RESPONSE_OK: + Keypad_Temp[Key] = ctx.mk_key_chosen; + Key_Label = g_strdup_printf("%s (%s)", key_names[Key], gdk_keyval_name(Keypad_Temp[Key])); + gtk_button_set_label(GTK_BUTTON(widget), Key_Label); + g_free(Key_Label); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + ctx.mk_key_chosen = 0; + break; + } + + gtk_widget_destroy(mkDialog); + +} + +static void Edit_Controls() +{ + GtkWidget *ecDialog; + GtkWidget *ecKey; + gchar *Key_Label; + int i; + + memcpy(&Keypad_Temp, &keyboard_cfg, sizeof(keyboard_cfg)); + + ecDialog = gtk_dialog_new_with_buttons("Edit controls", + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_OK, + GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL, + NULL); + + for(i = 0; i < NB_KEYS; i++) { + Key_Label = g_strdup_printf("%s (%s)", key_names[i], gdk_keyval_name(Keypad_Temp[i])); + ecKey = gtk_button_new_with_label(Key_Label); + g_free(Key_Label); + g_signal_connect(G_OBJECT(ecKey), "clicked", G_CALLBACK(Modify_Key), GINT_TO_POINTER(i)); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(ecDialog))), ecKey,TRUE, FALSE, 0); + } + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(ecDialog))); + + switch (gtk_dialog_run(GTK_DIALOG(ecDialog))) { + case GTK_RESPONSE_OK: + memcpy(&keyboard_cfg, &Keypad_Temp, sizeof(keyboard_cfg)); + desmume_config_update_keys(keyfile); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + break; + } + gtk_widget_destroy(ecDialog); + +} + +static void AcceptNewJoyKey(GtkWidget *w, GdkEventFocus *e, struct modify_key_ctx *ctx) +{ + gchar *YouPressed; + + ctx->mk_key_chosen = get_joy_key(ctx->key_id); + + YouPressed = g_strdup_printf("You pressed : %d\nClick OK to keep this key.", ctx->mk_key_chosen); + gtk_label_set_text(GTK_LABEL(ctx->label), YouPressed); + g_free(YouPressed); +} + +static void Modify_JoyKey(GtkWidget* widget, gpointer data) +{ + struct modify_key_ctx ctx; + GtkWidget *mkDialog; + gchar *Key_Label; + gchar *Title; + gint Key; + + Key = GPOINTER_TO_INT(data); + /* Joypad keys start at 1 */ + ctx.key_id = Key+1; + ctx.mk_key_chosen = 0; + Title = g_strdup_printf("Press \"%s\" key ...\n", key_names[Key]); + mkDialog = gtk_dialog_new_with_buttons(Title, + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_OK, + GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL, + NULL); + + ctx.label = gtk_label_new(Title); + g_free(Title); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(mkDialog))), ctx.label, TRUE, FALSE, 0); + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(mkDialog))); + + g_signal_connect(G_OBJECT(mkDialog), "focus_in_event", G_CALLBACK(AcceptNewJoyKey), &ctx); + + switch(gtk_dialog_run(GTK_DIALOG(mkDialog))) { + case GTK_RESPONSE_OK: + Keypad_Temp[Key] = ctx.mk_key_chosen; + Key_Label = g_strdup_printf("%s (%d)", key_names[Key], Keypad_Temp[Key]); + gtk_button_set_label(GTK_BUTTON(widget), Key_Label); + g_free(Key_Label); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + ctx.mk_key_chosen = 0; + break; + } + + gtk_widget_destroy(mkDialog); + +} + +#ifdef HAVE_JIT + +static void EmulationSettingsDialog(){ + GtkWidget *esDialog; + GtkWidget *esKey; + + esDialog=gtk_dialog_new_with_buttons("Emulation Settings", + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_OK, + GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL, + NULL); + + esKey=gtk_label_new("CPU Mode:\n"); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))), esKey,TRUE, FALSE, 0); + + esKey=gtk_check_button_new_with_label("Use dynamic recompiler"); + gtk_toggle_button_set_active((GtkToggleButton*)esKey,config.use_jit); + g_signal_connect(G_OBJECT(esKey),"clicked",G_CALLBACK(ToggleJIT),GINT_TO_POINTER(0)); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))), esKey,TRUE, FALSE, 0); + + esKey=gtk_label_new("Block Size (1 - accuracy, 100 - fastest):"); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))), esKey,TRUE, FALSE, 0); + + GtkAdjustment* JITBlockSizeAdjustment=(GtkAdjustment*)gtk_adjustment_new(config.jit_max_block_size,1,100,1,5,0); + esKey=gtk_hscale_new(JITBlockSizeAdjustment); + gtk_scale_set_digits((GtkScale*)esKey,0); + g_signal_connect(G_OBJECT(JITBlockSizeAdjustment),"value_changed",G_CALLBACK(JITMaxBlockSizeChanged),NULL); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))), esKey,TRUE, FALSE, 0); + + esKey=gtk_label_new( + "Enabling this will get you 0-50% speedups. It is optional because it\n" + "may still contain small small bugs, due mostly merely to newness, \n" + "which can safely be fixed in time. Furthermore, you may have to \n" + "tune the block size to prevent some games from breaking.\n" + "\n" + "This should not be assumed to be deterministic." + ); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))), esKey,TRUE, FALSE, 0); + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(esDialog))); + + bool prev_use_jit=config.use_jit; + int prev_jit_max_block_size=config.jit_max_block_size; + + switch (gtk_dialog_run(GTK_DIALOG(esDialog))) { + case GTK_RESPONSE_OK: + CommonSettings.jit_max_block_size=config.jit_max_block_size; + arm_jit_sync(); + arm_jit_reset(CommonSettings.use_jit=config.use_jit); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + config.use_jit=prev_use_jit; + config.jit_max_block_size=prev_jit_max_block_size; + break; + } + gtk_widget_destroy(esDialog); + +} + +static void JITMaxBlockSizeChanged(GtkAdjustment* adj,void * nullPtr){ + config.jit_max_block_size=(int)gtk_adjustment_get_value(adj); +} + +static void ToggleJIT(){ + config.use_jit=!config.use_jit; +} + +#endif + +static void Edit_Joystick_Controls() +{ + GtkWidget *ecDialog; + GtkWidget *ecKey; + gchar *Key_Label; + int i; + + memcpy(&Keypad_Temp, &joypad_cfg, sizeof(joypad_cfg)); + + ecDialog = gtk_dialog_new_with_buttons("Edit controls", + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_OK, + GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL, + NULL); + + for(i = 0; i < NB_KEYS; i++) { + Key_Label = g_strdup_printf("%s (%d)", key_names[i], Keypad_Temp[i]); + ecKey = gtk_button_new_with_label(Key_Label); + g_free(Key_Label); + g_signal_connect(G_OBJECT(ecKey), "clicked", G_CALLBACK(Modify_JoyKey), GINT_TO_POINTER(i)); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(ecDialog))), ecKey,TRUE, FALSE, 0); + } + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(ecDialog))); + + switch (gtk_dialog_run(GTK_DIALOG(ecDialog))) { + case GTK_RESPONSE_OK: + memcpy(&joypad_cfg, &Keypad_Temp, sizeof(keyboard_cfg)); + desmume_config_update_joykeys(keyfile); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + break; + } + gtk_widget_destroy(ecDialog); + +} + + +static void GraphicsSettingsDialog() { + GtkWidget *gsDialog; + GtkWidget *gsKey, *coreCombo, *wTable, *wPosterize, *wScale, *wSmoothing, *wMultisample, *wHCInterpolate; + + gsDialog = gtk_dialog_new_with_buttons("Graphics Settings", + GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_STOCK_OK, + GTK_RESPONSE_OK, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NULL); + + + wTable = gtk_table_new(2 ,2, TRUE); + gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gsDialog))), wTable, TRUE, FALSE, 0); + + // 3D Core + gsKey = gtk_label_new("3D Core:"); + gtk_misc_set_alignment(GTK_MISC(gsKey), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(wTable), gsKey, 0, 1, 0, 1, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); + + coreCombo = gtk_combo_box_text_new(); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(coreCombo), 0, "Null"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(coreCombo), 1, "SoftRasterizer"); +#ifdef HAVE_OPENGL + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(coreCombo), 2, "OpenGL"); +#endif + gtk_combo_box_set_active(GTK_COMBO_BOX(coreCombo), cur3DCore); + gtk_table_attach(GTK_TABLE(wTable), coreCombo, 1, 2, 0, 1, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); + + + // 3D Texture Upscaling + gsKey = gtk_label_new("3D Texture Upscaling:"); + gtk_misc_set_alignment(GTK_MISC(gsKey), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(wTable), gsKey, 0, 1, 1, 2, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); + + wScale = gtk_combo_box_text_new(); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wScale), 0, "x1"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wScale), 1, "x2"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wScale), 2, "x4"); + + // The shift it work for scale up to 4. For scaling more than 4, a mapping function is required + gtk_combo_box_set_active(GTK_COMBO_BOX(wScale), CommonSettings.GFX3D_Renderer_TextureScalingFactor >> 1); + gtk_table_attach(GTK_TABLE(wTable), wScale, 1, 2, 1, 2, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); + + + // 3D Texture Deposterization + wPosterize = gtk_check_button_new_with_label("3D Texture Deposterization"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wPosterize), CommonSettings.GFX3D_Renderer_TextureDeposterize); + gtk_table_attach(GTK_TABLE(wTable), wPosterize, 0, 1, 2, 3, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 0, 0); + + + // 3D Texture Smoothing + wSmoothing = gtk_check_button_new_with_label("3D Texture Smoothing"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wSmoothing), CommonSettings.GFX3D_Renderer_TextureSmoothing); + gtk_table_attach(GTK_TABLE(wTable), wSmoothing, 0, 1, 3, 4, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 0, 0); + + +#ifdef HAVE_OPENGL + // OpenGL Multisample + gsKey = gtk_label_new("Multisample Antialiasing (OpenGL):"); + gtk_misc_set_alignment(GTK_MISC(gsKey), 0.0, 0.5); + gtk_table_attach(GTK_TABLE(wTable), gsKey, 0, 1, 4, 5, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); + + wMultisample = gtk_combo_box_text_new(); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 0, "None"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 1, "2"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 2, "4"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 3, "8"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 4, "16"); + gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(wMultisample), 5, "32"); + + int currentMultisample = CommonSettings.GFX3D_Renderer_MultisampleSize; + int currentActive = 0; + // find smallest option that is larger than current value, i.e. round up to power of 2 + while (multisampleSizes[currentActive] < currentMultisample && currentActive < 5) { currentActive++; } + gtk_combo_box_set_active(GTK_COMBO_BOX(wMultisample), currentActive); + gtk_table_attach(GTK_TABLE(wTable), wMultisample, 1, 2, 4, 5, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 5, 0); +#endif + + // SoftRasterizer High Color Interpolation + wHCInterpolate = gtk_check_button_new_with_label("High Resolution Color Interpolation (SoftRasterizer)"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wHCInterpolate), CommonSettings.GFX3D_HighResolutionInterpolateColor); + gtk_table_attach(GTK_TABLE(wTable), wHCInterpolate, 1, 2, 3, 4, + static_cast(GTK_EXPAND | GTK_FILL), + static_cast(GTK_EXPAND | GTK_FILL), 10, 0); + + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(gsDialog))); + + switch (gtk_dialog_run(GTK_DIALOG(gsDialog))) { + case GTK_RESPONSE_OK: + // Start: OK Response block + { + int sel3DCore = gtk_combo_box_get_active(GTK_COMBO_BOX(coreCombo)); + + // Change only if needed + if (sel3DCore != cur3DCore) + { + if (sel3DCore == 2) + { +#if !defined(HAVE_OPENGL) + sel3DCore = RENDERID_SOFTRASTERIZER; +#elif defined(HAVE_LIBOSMESA) + if (!is_osmesa_initialized()) + { + init_osmesa_3Demu(); + } +#else + if (!is_sdl_initialized()) + { + init_sdl_3Demu(); + } +#endif + } + + if (GPU->Change3DRendererByID(sel3DCore)) + { + config.core3D = sel3DCore; + } + else + { + GPU->Change3DRendererByID(RENDERID_SOFTRASTERIZER); + g_printerr("3D renderer initialization failed!\nFalling back to 3D core: %s\n", core3DList[RENDERID_SOFTRASTERIZER]->name); + config.core3D = RENDERID_SOFTRASTERIZER; + } + } + + size_t scale = 1; + + switch (gtk_combo_box_get_active(GTK_COMBO_BOX(wScale))){ + case 1: + scale = 2; + break; + case 2: + scale = 4; + break; + default: + break; + } + CommonSettings.GFX3D_Renderer_TextureDeposterize = config.textureDeposterize = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wPosterize)); + CommonSettings.GFX3D_Renderer_TextureSmoothing = config.textureSmoothing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wSmoothing)); + CommonSettings.GFX3D_Renderer_TextureScalingFactor = config.textureUpscale = scale; + CommonSettings.GFX3D_HighResolutionInterpolateColor = config.highColorInterpolation = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wHCInterpolate)); +#ifdef HAVE_OPENGL + int selectedMultisample = gtk_combo_box_get_active(GTK_COMBO_BOX(wMultisample)); + config.multisamplingSize = multisampleSizes[selectedMultisample]; + config.multisampling = selectedMultisample != 0; + CommonSettings.GFX3D_Renderer_MultisampleSize = multisampleSizes[selectedMultisample]; +#endif + } + // End: OK Response Block + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + break; + } + + gtk_widget_destroy(gsDialog); + +} + +static void ToggleLayerVisibility(GtkToggleAction* action, gpointer data) +{ + guint Layer = GPOINTER_TO_UINT(data); + gboolean active; + + // FIXME: make it work after resume + if (!desmume_running()) + return; + + active = gtk_toggle_action_get_active(action); + + switch (Layer) { + case MAIN_BG_0: + case MAIN_BG_1: + case MAIN_BG_2: + case MAIN_BG_3: + case MAIN_OBJ: + GPU->GetEngineMain()->SetLayerEnableState(Layer, (active == TRUE) ? true : false); + break; + case SUB_BG_0: + case SUB_BG_1: + case SUB_BG_2: + case SUB_BG_3: + case SUB_OBJ: + GPU->GetEngineSub()->SetLayerEnableState(Layer-SUB_BG_0, (active == TRUE) ? true : false); + break; + default: + break; + } +} + +static void Printscreen() +{ + GdkPixbuf *screenshot; + const gchar *dir; + gchar *filename = NULL, *filen = NULL; + GError *error = NULL; + u8 rgb[256 * 384 * 4]; + static int seq = 0; + gint H, W; + + //rgb = (u8 *) malloc(SCREENS_PIXEL_SIZE*SCREEN_BYTES_PER_PIXEL); + //if (!rgb) + // return; + + if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) { + W = screen_size[nds_screen.orientation].width; + H = screen_size[nds_screen.orientation].height; + } else { + W = screen_size[nds_screen.orientation].height; + H = screen_size[nds_screen.orientation].width; + } + + gpu_screen_to_rgb((u32*)rgb); + screenshot = gdk_pixbuf_new_from_data(rgb, + GDK_COLORSPACE_RGB, + TRUE, + 8, + W, + H, + W * 4, + NULL, + NULL); + + dir = g_get_user_special_dir(G_USER_DIRECTORY_PICTURES); + if (dir == NULL) { + dir = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP); + } + if (dir == NULL) { + dir = g_get_home_dir(); + } + + do { + g_free(filen); + g_free(filename); + filen = g_strdup_printf("desmume-screenshot-%d.png", seq++); + filename = g_build_filename(dir, filen, NULL); + } + while (g_file_test(filename, G_FILE_TEST_EXISTS)); + + gdk_pixbuf_save(screenshot, filename, "png", &error, NULL); + if (error) { + g_error_free (error); + g_printerr("Failed to save %s", filename); + seq--; + } + + //free(rgb); + g_object_unref(screenshot); + g_free(filename); + g_free(filen); +} + +#ifdef DESMUME_GTK_FIRMWARE_BROKEN +static void SelectFirmwareFile() +{ + GtkFileFilter *pFilter_nds, *pFilter_bin, *pFilter_any; + GtkWidget *pFileSelection; + GtkWidget *pParent; + gchar *sPath; + + BOOL oldState = desmume_running(); + Pause(); + + pParent = GTK_WIDGET(pWindow); + + pFilter_nds = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_nds, "*.nds"); + gtk_file_filter_set_name(pFilter_nds, "Nds binary (.nds)"); + + pFilter_bin = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_bin, "*.bin"); + gtk_file_filter_set_name(pFilter_bin, "Binary file (.bin)"); + + pFilter_any = gtk_file_filter_new(); + gtk_file_filter_add_pattern(pFilter_any, "*"); + gtk_file_filter_set_name(pFilter_any, "All files"); + + pFileSelection = gtk_file_chooser_dialog_new("Load firmware...", + GTK_WINDOW(pParent), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_nds); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_bin); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pFileSelection), pFilter_any); + + switch(gtk_dialog_run(GTK_DIALOG(pFileSelection))) { + case GTK_RESPONSE_OK: + sPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSelection)); + CommonSettings.UseExtFirmware = true; + strncpy(CommonSettings.ExtFirmwarePath, (const char*)sPath, g_utf8_strlen(sPath, -1)); + g_free(sPath); + break; + default: + break; + } + gtk_widget_destroy(pFileSelection); + + if(oldState) Launch(); +} +#endif + +static void Modify_PriInterpolation(GtkAction *action, GtkRadioAction *current) +{ + uint filter = gtk_radio_action_get_current_value(current) ; + video->ChangeFilterByID((VideoFilterTypeID)filter); + config.view_filter = filter; + RedrawScreen(); +} + +static void Modify_Interpolation(GtkAction *action, GtkRadioAction *current) +{ + Interpolation = (cairo_filter_t)gtk_radio_action_get_current_value(current); + config.view_cairoFilter = Interpolation; + RedrawScreen(); +} + +static void Modify_SPUMode(GtkAction *action, GtkRadioAction *current) +{ + const uint mode = gtk_radio_action_get_current_value(current); + + switch (mode) { + case SPUMODE_SYNCN: + case SPUMODE_SYNCZ: +#ifdef HAVE_LIBSOUNDTOUCH + case SPUMODE_SYNCP: +#endif + SPUMode = mode; + SPU_SetSynchMode(1, mode-1); + break; + + case SPUMODE_DUALASYNC: + default: + SPUMode = SPUMODE_DUALASYNC; + SPU_SetSynchMode(0, 0); + break; + } + config.audio_sync = SPUMode; +} + +static void Modify_SPUInterpolation(GtkAction *action, GtkRadioAction *current) +{ + CommonSettings.spuInterpolationMode = (SPUInterpolationMode)gtk_radio_action_get_current_value(current); + config.audio_interpolation = CommonSettings.spuInterpolationMode; +} + +static void Modify_Frameskip(GtkAction *action, GtkRadioAction *current) +{ + autoFrameskipMax = gtk_radio_action_get_current_value(current) ; + config.frameskip = autoFrameskipMax; + if (!autoframeskip) { + Frameskip = autoFrameskipMax; + } +} + +/////////////////////////////// TOOLS MANAGEMENT /////////////////////////////// + +extern const dTool_t *dTools_list[]; +extern const int dTools_list_size; + +BOOL *dTools_running; + +static void Start_dTool(GtkWidget *widget, gpointer data) +{ + int tool = GPOINTER_TO_INT(data); + + if(dTools_running == NULL || dTools_running[tool]) + return; + + dTools_list[tool]->open(tool); + dTools_running[tool] = TRUE; +} + +void dTool_CloseCallback(int tool) +{ + if (dTools_running == NULL) + return; + + dTools_running[tool] = FALSE; +} + + +static inline void _updateDTools() +{ + if (dTools_running == NULL) + return; + + for(int i = 0; i < dTools_list_size; i++) { + if(dTools_running[i]) { dTools_list[i]->update(); } + } +} + +/////////////////////////////// MAIN EMULATOR LOOP /////////////////////////////// + +class GtkDriver : public BaseDriver +{ +public: + virtual void EMU_DebugIdleUpdate() + { + usleep(1000); + _updateDTools(); + while (gtk_events_pending()) + gtk_main_iteration(); + } + + // HUD uses this to show pause state + virtual bool EMU_IsEmulationPaused() { return !desmume_running(); } + + virtual bool AVI_IsRecording() + { + return avout_x264.isRecording() || avout_flac.isRecording(); + } + + virtual void AVI_SoundUpdate(void* soundData, int soundLen) { + avout_flac.updateAudio(soundData, soundLen); + } +}; + +static void DoQuit() +{ + emu_halt(EMUHALT_REASON_USER_REQUESTED_HALT, NDSErrorTag_None); + gtk_main_quit(); +} + + +gboolean EmuLoop(gpointer data) +{ + static Uint32 fps_SecStart, next_fps_SecStart, fps_FrameCount, skipped_frames; + static unsigned next_frame_time; + static int frame_mod3; + unsigned int i; + gchar Title[50]; + + if (!desmume_running()) { + // Set the next frame time to 0 so that it will recount + next_frame_time = 0; + frame_mod3 = 0; + gtk_window_set_title(GTK_WINDOW(pWindow), "DeSmuME - Paused"); + fps_SecStart = 0; + regMainLoop = FALSE; + RedrawScreen(); + return FALSE; + } + + fps_FrameCount += Frameskip + 1; + next_fps_SecStart = SDL_GetTicks(); + + if (fps_SecStart == 0) { + fps_SecStart = next_fps_SecStart; + fps_FrameCount = 0; + gtk_window_set_title(GTK_WINDOW(pWindow), "DeSmuME - Running"); + } + + bool oneSecond = false; + if ((next_fps_SecStart - fps_SecStart) >= 1000) { + oneSecond = true; + fps_SecStart = next_fps_SecStart; + + float emu_ratio = fps_FrameCount / 60.0; + LOG("auto: %d fps: %u skipped: %u emu_ratio: %f Frameskip: %u\n", autoframeskip, fps_FrameCount, skipped_frames, emu_ratio, Frameskip); + + snprintf(Title, sizeof(Title), "DeSmuME - %dfps, %d skipped, draw: %dfps", fps_FrameCount, skipped_frames, draw_count); + gtk_window_set_title(GTK_WINDOW(pWindow), Title); + +#ifdef HAVE_LIBAGG + Hud.fps = fps_FrameCount; +#endif + + fps_FrameCount = 0; + skipped_frames = 0; + draw_count = 0; + } + + // HUD display things (copied from Windows main.cpp) +#ifdef HAVE_LIBAGG + Hud.fps3d = GPU->GetFPSRender3D(); + + if(nds.idleFrameCounter==0 || oneSecond) + { + u32 loadAvgARM9; + u32 loadAvgARM7; + NDS_GetCPULoadAverage(loadAvgARM9, loadAvgARM7); + + Hud.cpuload[ARMCPU_ARM9] = (int)loadAvgARM9; + Hud.cpuload[ARMCPU_ARM7] = (int)loadAvgARM7; + + } + Hud.cpuloopIterationCount = nds.cpuloopIterationCount; +#endif + + /* Merge the joystick keys with the keyboard ones */ + process_joystick_events(&keys_latch); + /* Update! */ + update_keypad(keys_latch); + + desmume_cycle(); /* Emule ! */ + + _updateDTools(); + avout_x264.updateVideo(GPU->GetDisplayInfo().masterNativeBuffer16); + RedrawScreen(); + + if (!config.fpslimiter || keys_latch & KEYMASK_(KEY_BOOST - 1)) { + if (autoframeskip) { + Frameskip = 0; + } else { + for (i = 0; i < Frameskip; i++) { + NDS_SkipNextFrame(); +#ifdef HAVE_LIBAGG + Hud.fps3d = GPU->GetFPSRender3D(); +#endif + desmume_cycle(); + skipped_frames++; + } + } + next_frame_time = SDL_GetTicks() + 16; + frame_mod3 = 0; + } else { + if (!autoframeskip) { + for (i = 0; i < Frameskip; i++) { + NDS_SkipNextFrame(); +#ifdef HAVE_LIBAGG + Hud.fps3d = GPU->GetFPSRender3D(); +#endif + desmume_cycle(); + skipped_frames++; + // Update next frame time + frame_mod3 = (frame_mod3 + 1) % 3; + next_frame_time += (frame_mod3 == 0) ? 16 : 17; + } + } + unsigned this_tick = SDL_GetTicks(); + if (this_tick < next_frame_time) { + unsigned timeleft = next_frame_time - this_tick; + usleep((timeleft - 1) * 1000); + while (SDL_GetTicks() < next_frame_time); + this_tick = SDL_GetTicks(); + } + if (autoframeskip) { + // Determine the auto frameskip value, maximum 4 + for (Frameskip = 0; this_tick > next_frame_time && Frameskip < autoFrameskipMax; Frameskip++, this_tick = SDL_GetTicks()) { + // Aggressively skip frames to avoid delay + NDS_SkipNextFrame(); +#ifdef HAVE_LIBAGG + Hud.fps3d = GPU->GetFPSRender3D(); +#endif + desmume_cycle(); + skipped_frames++; + // Update next frame time + frame_mod3 = (frame_mod3 + 1) % 3; + next_frame_time += (frame_mod3 == 0) ? 16 : 17; + } + if (Frameskip > autoFrameskipMax) { + Frameskip = autoFrameskipMax; + } + //while (SDL_GetTicks() < next_frame_time); + //this_tick = SDL_GetTicks(); + } + if (this_tick > next_frame_time && this_tick - next_frame_time >= 17) { + // If we fall too much behind, don't try to catch up. + next_frame_time = this_tick; + } + // We want to achieve a total 17 + 17 + 16 = 50 ms for every 3 frames. + frame_mod3 = (frame_mod3 + 1) % 3; + next_frame_time += (frame_mod3 == 0) ? 16 : 17; + } + + return TRUE; +} + +static void desmume_try_adding_ui(GtkUIManager *self, const char *ui_descr){ + GError *error; + error = NULL; + if (!gtk_ui_manager_add_ui_from_string (self, ui_descr, -1, &error)) + { + g_message ("building menus failed: %s", error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } +} + +static void dui_set_accel_group(gpointer action, gpointer group) { + gtk_action_set_accel_group((GtkAction *)action, (GtkAccelGroup *)group); +} + +// The following functions are adapted from the Windows port: +// UpdateSaveStateMenu, ResetSaveStateTimes, LoadSaveStateInfo +static void UpdateSaveStateMenu(int pos, char* txt) +{ + char name[64]; + snprintf(name, sizeof(name), "savestate%d", (pos == 0) ? 10 : pos); + gtk_action_set_label(gtk_action_group_get_action(action_group, name), txt); + snprintf(name, sizeof(name), "loadstate%d", (pos == 0) ? 10 : pos); + gtk_action_set_label(gtk_action_group_get_action(action_group, name), txt); +} + +static void ResetSaveStateTimes() +{ + char ntxt[64]; + for(int i = 0; i < NB_STATES;i++) + { + snprintf(ntxt, sizeof(ntxt), "_%d", i); + UpdateSaveStateMenu(i, ntxt); + } +} + +static void LoadSaveStateInfo() +{ + scan_savestates(); + char ntxt[128]; + for(int i = 0; i < NB_STATES; i++) + { + if(savestates[i].exists) + { + snprintf(ntxt, sizeof(ntxt), "_%d %s", i, savestates[i].date); + UpdateSaveStateMenu(i, ntxt); + } + } +} + +static void desmume_gtk_menu_file_saveload_slot (GtkActionGroup *ag) +{ + for(guint i = 1; i <= 10; i++){ + GtkAction *act; + char label[64], name[64], accel[64]; + + snprintf(label, sizeof(label), "_%d", i % 10); + + // Note: GTK+ doesn't handle Shift correctly, so the actual action is + // done in Key_Press. The accelerators here are simply visual cues. + + snprintf(name, sizeof(name), "savestate%d", i); + snprintf(accel, sizeof(accel), "F%d", i); + act = gtk_action_new(name, label, NULL, NULL); + g_signal_connect(G_OBJECT(act), "activate", G_CALLBACK(MenuSave), GUINT_TO_POINTER(i % 10)); + gtk_action_group_add_action_with_accel(ag, GTK_ACTION(act), accel); + + snprintf(name, sizeof(name), "loadstate%d", i); + snprintf(accel, sizeof(accel), "F%d", i); + act = gtk_action_new(name, label, NULL, NULL); + g_signal_connect(G_OBJECT(act), "activate", G_CALLBACK(MenuLoad), GUINT_TO_POINTER(i % 10)); + gtk_action_group_add_action_with_accel(ag, GTK_ACTION(act), accel); + } +} + +static void changesavetype(GtkAction *action, GtkRadioAction *current) +{ + backup_setManualBackupType( gtk_radio_action_get_current_value(current)); +} + +static void desmume_gtk_menu_tool_layers (GtkActionGroup *ag) +{ + const char *Layers_Menu[10][2] = { + {"layermainbg0","_0 Main BG 0"}, + {"layermainbg1","_1 Main BG 1"}, + {"layermainbg2","_2 Main BG 2"}, + {"layermainbg3","_3 Main BG 3"}, + {"layermainobj","_4 Main OBJ"}, + {"layersubbg0", "_5 SUB BG 0"}, + {"layersubbg1", "_6 SUB BG 1"}, + {"layersubbg2", "_7 SUB BG 2"}, + {"layersubbg3", "_8 SUB BG 3"}, + {"layersubobj", "_9 SUB OBJ"} + }; + guint i; + + GtkToggleAction *act; + for(i = 0; i< 10; i++){ + act = gtk_toggle_action_new(Layers_Menu[i][0],Layers_Menu[i][1],NULL,NULL); + gtk_toggle_action_set_active(act, TRUE); + g_signal_connect(G_OBJECT(act), "activate", G_CALLBACK(ToggleLayerVisibility), GUINT_TO_POINTER(i)); + gtk_action_group_add_action_with_accel(ag, GTK_ACTION(act), NULL); + } +} + +#ifdef HAVE_LIBAGG +enum hud_display_enum { + HUD_DISPLAY_FPS, + HUD_DISPLAY_INPUT, + HUD_DISPLAY_GINPUT, + HUD_DISPLAY_FCOUNTER, + HUD_DISPLAY_LCOUNTER, + HUD_DISPLAY_RTC, + HUD_DISPLAY_MIC, + HUD_DISPLAY_EDITOR, +}; + +static void ToggleHudDisplay(GtkToggleAction* action, gpointer data) +{ + guint hudId = GPOINTER_TO_UINT(data); + gboolean active; + + active = gtk_toggle_action_get_active(action); + + switch (hudId) { + case HUD_DISPLAY_FPS: + CommonSettings.hud.FpsDisplay = active; + config.hud_fps = active; + break; + case HUD_DISPLAY_INPUT: + CommonSettings.hud.ShowInputDisplay = active; + config.hud_input = active; + break; + case HUD_DISPLAY_GINPUT: + CommonSettings.hud.ShowGraphicalInputDisplay = active; + config.hud_graphicalInput = active; + break; + case HUD_DISPLAY_FCOUNTER: + CommonSettings.hud.FrameCounterDisplay = active; + config.hud_frameCounter = active; + break; + case HUD_DISPLAY_LCOUNTER: + CommonSettings.hud.ShowLagFrameCounter = active; + config.hud_lagCounter = active; + break; + case HUD_DISPLAY_RTC: + CommonSettings.hud.ShowRTC = active; + config.hud_rtc = active; + break; + case HUD_DISPLAY_MIC: + CommonSettings.hud.ShowMicrophone = active; + config.hud_mic = active; + break; + case HUD_DISPLAY_EDITOR: + HudEditorMode = active; + break; + default: + g_printerr("Unknown HUD toggle %u!", hudId); + break; + } + RedrawScreen(); +} + +static void desmume_gtk_menu_view_hud (GtkActionGroup *ag) +{ + const struct { + const gchar* name; + const gchar* label; + guint id; + bool active; + bool& setting; + } hud_menu[] = { + { "hud_fps","Display _fps", HUD_DISPLAY_FPS, config.hud_fps, CommonSettings.hud.FpsDisplay }, + { "hud_input","Display _Input", HUD_DISPLAY_INPUT, config.hud_input, CommonSettings.hud.ShowInputDisplay }, + { "hud_graphicalinput","Display _Graphical Input", HUD_DISPLAY_GINPUT, config.hud_graphicalInput, CommonSettings.hud.ShowGraphicalInputDisplay }, + { "hud_framecounter","Display Frame _Counter", HUD_DISPLAY_FCOUNTER, config.hud_frameCounter, CommonSettings.hud.FrameCounterDisplay }, + { "hud_lagcounter","Display _Lag Counter", HUD_DISPLAY_LCOUNTER, config.hud_lagCounter, CommonSettings.hud.ShowLagFrameCounter }, + { "hud_rtc","Display _RTC", HUD_DISPLAY_RTC, config.hud_rtc, CommonSettings.hud.ShowRTC }, + { "hud_mic","Display _Mic", HUD_DISPLAY_MIC, config.hud_mic, CommonSettings.hud.ShowMicrophone }, + { "hud_editor","_Editor Mode", HUD_DISPLAY_EDITOR, false, HudEditorMode }, + }; + guint i; + + GtkToggleAction *act; + for(i = 0; i < sizeof(hud_menu) / sizeof(hud_menu[0]); i++){ + act = gtk_toggle_action_new(hud_menu[i].name, hud_menu[i].label, NULL, NULL); + gtk_toggle_action_set_active(act, hud_menu[i].active ? TRUE : FALSE); + hud_menu[i].setting = hud_menu[i].active; + g_signal_connect(G_OBJECT(act), "activate", G_CALLBACK(ToggleHudDisplay), GUINT_TO_POINTER(hud_menu[i].id)); + gtk_action_group_add_action_with_accel(ag, GTK_ACTION(act), NULL); + } +} +#endif + +static void ToggleAudio (GtkToggleAction *action) +{ + config.audio_enabled = gtk_toggle_action_get_active(action); + if (config.audio_enabled) { + SPU_ChangeSoundCore(SNDCORE_SDL, 735 * 4); + driver->AddLine("Audio enabled"); + } else { + SPU_ChangeSoundCore(0, 0); + driver->AddLine("Audio disabled"); + } + RedrawScreen(); +} + +#ifdef FAKE_MIC +static void ToggleMicNoise (GtkToggleAction *action) +{ + BOOL doNoise = (BOOL)gtk_toggle_action_get_active(action); + + Mic_DoNoise(doNoise); + if (doNoise) + driver->AddLine("Fake mic enabled"); + else + driver->AddLine("Fake mic disabled"); + RedrawScreen(); +} +#endif + +static void ToggleFpsLimiter (GtkToggleAction *action) +{ + config.fpslimiter = (BOOL)gtk_toggle_action_get_active(action); + + if (config.fpslimiter) + driver->AddLine("Fps limiter enabled"); + else + driver->AddLine("Fps limiter disabled"); + RedrawScreen(); +} + +static void ToggleAutoFrameskip (GtkToggleAction *action) +{ + config.autoframeskip = (BOOL)gtk_toggle_action_get_active(action); + + if (config.autoframeskip) { + autoframeskip = true; + Frameskip = 0; + driver->AddLine("Auto frameskip enabled"); + } else { + autoframeskip = false; + Frameskip = autoFrameskipMax; + driver->AddLine("Auto frameskip disabled"); + } + RedrawScreen(); +} + +static void desmume_gtk_menu_tools (GtkActionGroup *ag) +{ + gint i; + for(i = 0; i < dTools_list_size; i++) { + GtkAction *act; + act = gtk_action_new(dTools_list[i]->shortname, dTools_list[i]->name, NULL, NULL); + g_signal_connect(G_OBJECT(act), "activate", G_CALLBACK(Start_dTool), GINT_TO_POINTER(i)); + gtk_action_group_add_action(ag, GTK_ACTION(act)); + } +} + +static gboolean timeout_exit_cb(gpointer data) +{ + DoQuit(); + INFO("Quit after %d seconds timeout\n", GPOINTER_TO_INT(data)); + + return FALSE; +} + +static int +common_gtk_main( class configured_features *my_config) +{ + config.load(); + + driver = new GtkDriver(); + + SDL_TimerID limiter_timer = 0; + + GtkAccelGroup * accel_group; + GtkWidget *pVBox; + GtkWidget *pMenuBar; + GtkWidget *pToolBar; + + /* use any language set on the command line */ + if ( my_config->firmware_language != -1) { + CommonSettings.fwConfig.language = my_config->firmware_language; + } + + /* if the command line overriding is enabled then use the language set on the GUI */ + if(config.command_line_overriding_firmware_language) + CommonSettings.fwConfig.language = config.firmware_language; + + //------------------addons---------- + my_config->process_addonCommands(); + + int slot2_device_type = NDS_SLOT2_AUTO; + + if (my_config->is_cflash_configured) + slot2_device_type = NDS_SLOT2_CFLASH; + + if(my_config->gbaslot_rom != "") { + + //set the GBA rom and sav paths + GBACartridge_RomPath = my_config->gbaslot_rom.c_str(); + if(toupper(strright(GBACartridge_RomPath,4)) == ".GBA") + GBACartridge_SRAMPath = strright(GBACartridge_RomPath,4) + ".sav"; + else + //what to do? lets just do the same thing for now + GBACartridge_SRAMPath = strright(GBACartridge_RomPath,4) + ".sav"; + + // Check if the file exists and can be opened + FILE * test = fopen(GBACartridge_RomPath.c_str(), "rb"); + if (test) { + slot2_device_type = NDS_SLOT2_GBACART; + fclose(test); + } + } + + switch (slot2_device_type) + { + case NDS_SLOT2_NONE: + break; + case NDS_SLOT2_AUTO: + break; + case NDS_SLOT2_CFLASH: + break; + case NDS_SLOT2_RUMBLEPAK: + break; + case NDS_SLOT2_GBACART: + break; + case NDS_SLOT2_GUITARGRIP: + break; + case NDS_SLOT2_EXPMEMORY: + break; + case NDS_SLOT2_EASYPIANO: + break; + case NDS_SLOT2_PADDLE: + break; + case NDS_SLOT2_PASSME: + break; + default: + slot2_device_type = NDS_SLOT2_NONE; + break; + } + + slot2_Init(); + slot2_Change((NDS_SLOT2_TYPE)slot2_device_type); + + /* FIXME: SDL_INIT_VIDEO is needed for joystick support to work!? + * Perhaps it needs a "window" to catch events...? */ + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,"1"); + if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO) == -1) { + g_printerr("Error trying to initialize SDL: %s\n", + SDL_GetError()); + return 1; + } + desmume_init( my_config->disable_sound || !config.audio_enabled); + + /* Init the hud / osd stuff */ +#ifdef HAVE_LIBAGG + Desmume_InitOnce(); + Hud.reset(); + osd = new OSDCLASS(-1); +#endif + + /* + * Activate the GDB stubs + * This has to come after the NDS_Init (called in desmume_init) + * where the cpus are set up. + */ +#ifdef GDB_STUB + gdbstub_mutex_init(); + + gdbstub_handle_t arm9_gdb_stub = NULL; + gdbstub_handle_t arm7_gdb_stub = NULL; + + if ( my_config->arm9_gdb_port > 0) { + arm9_gdb_stub = createStub_gdb( my_config->arm9_gdb_port, + &NDS_ARM9, + &arm9_direct_memory_iface); + + if ( arm9_gdb_stub == NULL) { + g_printerr("Failed to create ARM9 gdbstub on port %d\n", + my_config->arm9_gdb_port); + exit( -1); + } + else { + activateStub_gdb( arm9_gdb_stub); + } + } + if ( my_config->arm7_gdb_port > 0) { + arm7_gdb_stub = createStub_gdb( my_config->arm7_gdb_port, + &NDS_ARM7, + &arm7_base_memory_iface); + + if ( arm7_gdb_stub == NULL) { + g_printerr("Failed to create ARM7 gdbstub on port %d\n", + my_config->arm7_gdb_port); + exit( -1); + } + else { + activateStub_gdb( arm7_gdb_stub); + } + } +#endif + + /* Initialize joysticks */ + if(!init_joy()) return 1; + + dTools_running = (BOOL*)malloc(sizeof(BOOL) * dTools_list_size); + if (dTools_running != NULL) + memset(dTools_running, FALSE, sizeof(BOOL) * dTools_list_size); + + keyfile = desmume_config_read_file(gtk_kb_cfg); + + memset(&nds_screen, 0, sizeof(nds_screen)); + nds_screen.orientation = ORIENT_VERTICAL; + + g_printerr("Using %d threads for video filter.\n", CommonSettings.num_cores); + video = new VideoFilter(256, 384, VideoFilterTypeID_None, CommonSettings.num_cores); + + /* Create the window */ + pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(pWindow), "DeSmuME"); + gtk_window_set_resizable(GTK_WINDOW (pWindow), TRUE); + gtk_window_set_icon(GTK_WINDOW (pWindow), gdk_pixbuf_new_from_xpm_data(DeSmuME_xpm)); + + g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(DoQuit), NULL); + g_signal_connect(G_OBJECT(pWindow), "key_press_event", G_CALLBACK(Key_Press), NULL); + g_signal_connect(G_OBJECT(pWindow), "key_release_event", G_CALLBACK(Key_Release), NULL); + + /* Create the GtkVBox */ + pVBox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(pWindow), pVBox); + + ui_manager = gtk_ui_manager_new (); + accel_group = gtk_accel_group_new(); + action_group = gtk_action_group_new("dui"); + + gtk_action_group_add_actions(action_group, action_entries, G_N_ELEMENTS(action_entries), NULL); + gtk_action_group_add_toggle_actions(action_group, toggle_entries, G_N_ELEMENTS(toggle_entries), NULL); + /* Update audio toggle status */ + if (my_config->disable_sound || !config.audio_enabled) { + GtkAction *action = gtk_action_group_get_action(action_group, "enableaudio"); + if (action) + gtk_toggle_action_set_active((GtkToggleAction *)action, FALSE); + } + desmume_gtk_menu_tool_layers(action_group); +#ifdef HAVE_LIBAGG + desmume_gtk_menu_view_hud(action_group); +#else + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "hud_notsupported"), FALSE); +#endif + desmume_gtk_menu_file_saveload_slot(action_group); + desmume_gtk_menu_tools(action_group); + gtk_action_group_add_radio_actions(action_group, savet_entries, G_N_ELEMENTS(savet_entries), + my_config->savetype, G_CALLBACK(changesavetype), NULL); + + if (config.view_cairoFilter < CAIRO_FILTER_FAST || config.view_cairoFilter > CAIRO_FILTER_BILINEAR) { + config.view_cairoFilter = CAIRO_FILTER_NEAREST; + } + Interpolation = (cairo_filter_t)config.view_cairoFilter.get(); + gtk_action_group_add_radio_actions(action_group, interpolation_entries, G_N_ELEMENTS(interpolation_entries), + Interpolation, G_CALLBACK(Modify_Interpolation), NULL); + + if (config.view_filter < VideoFilterTypeID_None || config.view_filter >= VideoFilterTypeIDCount) { + config.view_filter = VideoFilterTypeID_None; + } + video->ChangeFilterByID((VideoFilterTypeID)config.view_filter.get()); + gtk_action_group_add_radio_actions(action_group, pri_interpolation_entries, G_N_ELEMENTS(pri_interpolation_entries), + config.view_filter, G_CALLBACK(Modify_PriInterpolation), NULL); + + switch (config.audio_sync) { + case SPUMODE_SYNCN: + case SPUMODE_SYNCZ: +#ifdef HAVE_LIBSOUNDTOUCH + case SPUMODE_SYNCP: +#endif + SPUMode = config.audio_sync; + SPU_SetSynchMode(1, config.audio_sync-1); + break; + + case SPUMODE_DUALASYNC: + default: + config.audio_sync = SPUMODE_DUALASYNC; + SPUMode = SPUMODE_DUALASYNC; + SPU_SetSynchMode(0, 0); + break; + } + gtk_action_group_add_radio_actions(action_group, spumode_entries, G_N_ELEMENTS(spumode_entries), + config.audio_sync, G_CALLBACK(Modify_SPUMode), NULL); + + CommonSettings.spuInterpolationMode = (SPUInterpolationMode)(config.audio_interpolation.get()); + gtk_action_group_add_radio_actions(action_group, spuinterpolation_entries, G_N_ELEMENTS(spuinterpolation_entries), + CommonSettings.spuInterpolationMode, G_CALLBACK(Modify_SPUInterpolation), NULL); + + gtk_action_group_add_radio_actions(action_group, frameskip_entries, G_N_ELEMENTS(frameskip_entries), + config.frameskip, G_CALLBACK(Modify_Frameskip), NULL); + autoFrameskipMax = config.frameskip; + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "frameskipA"), config.autoframeskip); + if (config.autoframeskip) { + autoframeskip = true; + Frameskip = 0; + } else { + autoframeskip = false; + Frameskip = autoFrameskipMax; + } + + switch (config.view_rot) { + case 0: + case 90: + case 180: + case 270: + break; + default: + config.view_rot = 0; + break; + } + nds_screen.rotation_angle = config.view_rot; + gtk_action_group_add_radio_actions(action_group, rotation_entries, G_N_ELEMENTS(rotation_entries), + nds_screen.rotation_angle, G_CALLBACK(SetRotation), NULL); + + + if (config.window_scale < WINSIZE_SCALE || config.window_scale > WINSIZE_5) { + config.window_scale = WINSIZE_SCALE; + } + winsize_current = (winsize_enum)config.window_scale.get(); + gtk_action_group_add_radio_actions(action_group, winsize_entries, G_N_ELEMENTS(winsize_entries), + winsize_current, G_CALLBACK(SetWinsize), NULL); + + if (config.view_orient < ORIENT_VERTICAL || config.view_orient > ORIENT_SINGLE) { + config.view_orient = ORIENT_VERTICAL; + } + nds_screen.orientation = (orientation_enum)config.view_orient.get(); + gtk_action_group_add_radio_actions(action_group, orientation_entries, G_N_ELEMENTS(orientation_entries), + nds_screen.orientation, G_CALLBACK(SetOrientation), NULL); + + { + GList * list = gtk_action_group_list_actions(action_group); + g_list_foreach(list, dui_set_accel_group, accel_group); + } + gtk_window_add_accel_group(GTK_WINDOW(pWindow), accel_group); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "pause"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "run"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "reset"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "printscreen"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "cheatlist"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "cheatsearch"), FALSE); + + nds_screen.gap_size = config.view_gap ? GAP_SIZE : 0; + + nds_screen.swap = config.view_swap; + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "orient_swapscreens"), config.view_swap); + + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + + accel_group = gtk_ui_manager_get_accel_group (ui_manager); + gtk_window_add_accel_group (GTK_WINDOW (pWindow), accel_group); + + desmume_try_adding_ui(ui_manager, ui_description); + + pMenuBar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); + gtk_box_pack_start (GTK_BOX (pVBox), pMenuBar, FALSE, FALSE, 0); + pToolBar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); + gtk_box_pack_start (GTK_BOX(pVBox), pToolBar, FALSE, FALSE, 0); + + { + GtkWidget * recentMenu = gtk_ui_manager_get_widget (ui_manager, "/MainMenu/FileMenu/RecentMenu"); + GtkWidget * recentFiles = gtk_recent_chooser_menu_new(); + GtkRecentFilter * recentFilter = gtk_recent_filter_new(); + gtk_recent_filter_add_mime_type(recentFilter, "application/x-nintendo-ds-rom"); + gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(recentFiles), recentFilter); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recentMenu), recentFiles); + g_signal_connect(G_OBJECT(recentFiles), "item-activated", G_CALLBACK(OpenRecent), NULL); + } + + /* Creating the place for showing DS screens */ + pDrawingArea = gtk_drawing_area_new(); + + /* This toggle action must not be set active before the pDrawingArea initialization to avoid a GTK warning */ + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "gap"), config.view_gap); + + gtk_container_add (GTK_CONTAINER (pVBox), pDrawingArea); + + gtk_widget_set_events(pDrawingArea, + GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK ); + + g_signal_connect(G_OBJECT(pDrawingArea), "button_press_event", + G_CALLBACK(Stylus_Press), NULL); + g_signal_connect(G_OBJECT(pDrawingArea), "button_release_event", + G_CALLBACK(Stylus_Release), NULL); + g_signal_connect(G_OBJECT(pDrawingArea), "motion_notify_event", + G_CALLBACK(Stylus_Move), NULL); + g_signal_connect(G_OBJECT(pDrawingArea), "expose_event", + G_CALLBACK(ExposeDrawingArea), NULL ) ; + g_signal_connect(G_OBJECT(pDrawingArea), "configure_event", + G_CALLBACK(ConfigureDrawingArea), NULL ) ; + + /* Status bar */ + pStatusBar = gtk_statusbar_new(); + UpdateStatusBar(EMU_DESMUME_NAME_AND_VERSION()); + gtk_box_pack_end(GTK_BOX(pVBox), pStatusBar, FALSE, FALSE, 0); + + gtk_widget_show_all(pWindow); + + if (winsize_current == WINSIZE_SCALE) { + if (config.window_fullscreen) { + gtk_widget_hide(pMenuBar); + gtk_widget_hide(pToolBar); + gtk_widget_hide(pStatusBar); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_menu"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_toolbar"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "view_statusbar"), FALSE); + gtk_window_fullscreen(GTK_WINDOW(pWindow)); + } + } else { + config.window_fullscreen = false; + gtk_action_set_sensitive(gtk_action_group_get_action(action_group, "fullscreen"), FALSE); + } + if (!config.view_menu) { + gtk_widget_hide(pMenuBar); + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "view_menu"), FALSE); + } + if (!config.view_toolbar) { + gtk_widget_hide(pToolBar); + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "view_toolbar"), FALSE); + } + if (!config.view_statusbar) { + gtk_widget_hide(pStatusBar); + gtk_toggle_action_set_active((GtkToggleAction*)gtk_action_group_get_action(action_group, "view_statusbar"), FALSE); + } + UpdateDrawingAreaAspect(); + + if (my_config->disable_limiter || !config.fpslimiter) { + config.fpslimiter = false; + GtkAction *action = gtk_action_group_get_action(action_group, "enablefpslimiter"); + if (action) + gtk_toggle_action_set_active((GtkToggleAction *)action, FALSE); + } + +#if defined(HAVE_OPENGL) && defined(OGLRENDER_3_2_H) + OGLLoadEntryPoints_3_2_Func = OGLLoadEntryPoints_3_2; + OGLCreateRenderer_3_2_Func = OGLCreateRenderer_3_2; +#endif + + //Set the 3D emulation to use + int core = my_config->engine_3d; + // setup the gdk 3D emulation; + + // Check if commandLine argument was passed or not + if (core == -1) { + // If it was not passed, then get the Renderer config from the file + core = config.core3D; + + // Check if it is valid + if (!(core >= 0 && core <= 2)) { + // If it is invalid, reset it to SoftRasterizer + core = 1; + } + //Set this too for clarity + my_config->engine_3d = core; + } + + if (core == 2) + { +#if !defined(HAVE_OPENGL) + core = RENDERID_SOFTRASTERIZER; +#elif defined(HAVE_LIBOSMESA) + if (!is_osmesa_initialized()) + { + init_osmesa_3Demu(); + } +#else + if (!is_sdl_initialized()) + { + init_sdl_3Demu(); + } +#endif + } + + if (!GPU->Change3DRendererByID(core)) { + GPU->Change3DRendererByID(RENDERID_SOFTRASTERIZER); + g_printerr("3D renderer initialization failed!\nFalling back to 3D core: %s\n", core3DList[RENDERID_SOFTRASTERIZER]->name); + my_config->engine_3d = RENDERID_SOFTRASTERIZER; + } + + CommonSettings.GFX3D_HighResolutionInterpolateColor = config.highColorInterpolation; + CommonSettings.GFX3D_Renderer_MultisampleSize = (config.multisamplingSize > 0) + ? config.multisamplingSize + : config.multisampling ? 4 : 0; + CommonSettings.GFX3D_Renderer_TextureDeposterize = config.textureDeposterize; + CommonSettings.GFX3D_Renderer_TextureScalingFactor = (config.textureUpscale == 1 || + config.textureUpscale == 2 || + config.textureUpscale == 4) + ? config.textureUpscale : 1 ; + CommonSettings.GFX3D_Renderer_TextureSmoothing = config.textureSmoothing; + + backup_setManualBackupType(my_config->savetype); + + // Command line arg + if( my_config->nds_file != "") { + if(Open( my_config->nds_file.c_str()) >= 0) { + my_config->process_movieCommands(); + + if(my_config->load_slot != -1){ + int todo = my_config->load_slot; + if(todo == 10) + todo = 0; + loadstate_slot(my_config->load_slot); + } + + Launch(); + } else { + GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pWindow), + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "Unable to load :\n%s", my_config->nds_file.c_str()); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + } + } + + if (my_config->timeout > 0) { + g_timeout_add_seconds(my_config->timeout, timeout_exit_cb, GINT_TO_POINTER(my_config->timeout)); + } + + SNDSDLSetAudioVolume(config.audio_volume); + + /* Video filter parameters */ + video->SetFilterParameteri(VF_PARAM_SCANLINE_A, _scanline_filter_a); + video->SetFilterParameteri(VF_PARAM_SCANLINE_B, _scanline_filter_b); + video->SetFilterParameteri(VF_PARAM_SCANLINE_C, _scanline_filter_c); + video->SetFilterParameteri(VF_PARAM_SCANLINE_D, _scanline_filter_d); + + RedrawScreen(); + /* Main loop */ + gtk_main(); + + delete video; + + config.save(); + avout_x264.end(); + avout_flac.end(); + + desmume_free(); + +#if defined(HAVE_LIBOSMESA) + deinit_osmesa_3Demu(); +#else + deinit_sdl_3Demu(); +#endif + + /* Unload joystick */ + uninit_joy(); + + SDL_Quit(); + + desmume_config_dispose(keyfile); + +#ifdef GDB_STUB + destroyStub_gdb( arm9_gdb_stub); + arm9_gdb_stub = NULL; + + destroyStub_gdb( arm7_gdb_stub); + arm7_gdb_stub = NULL; + + gdbstub_mutex_destroy(); +#endif + + return EXIT_SUCCESS; +} + + +int main (int argc, char *argv[]) +{ + configured_features my_config; + + // The global menu screws up the window size... + unsetenv("UBUNTU_MENUPROXY"); + + + my_config.parse(argc, argv); + init_configured_features( &my_config); + + /* X11 multi-threading support */ + if(!XInitThreads()) + { + fprintf(stderr, "Warning: X11 not thread-safe\n"); + } + + gtk_init(&argc, &argv); + + if ( !fill_configured_features( &my_config, argv)) { + exit(0); + } +#ifdef HAVE_JIT + config.load(); + CommonSettings.jit_max_block_size=config.jit_max_block_size; + CommonSettings.use_jit=config.use_jit; + arm_jit_sync(); + arm_jit_reset(CommonSettings.use_jit); +#endif + return common_gtk_main( &my_config); +} + +#ifdef WIN32 +int WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) +{ + int argc = 0; + char *argv[] = NULL; + + /* + * FIXME: + * Emulate the argc and argv main parameters. Could do this using + * CommandLineToArgvW and then convert the wide chars to thin chars. + * Or parse the wide chars directly and call common_gtk_main with a + * filled configuration structure. + */ + main( argc, argv); +} +#endif + diff --git a/desmume/src/frontend/posix/gtk2/main.h b/desmume/src/frontend/posix/gtk2/main.h new file mode 100644 index 000000000..b64706d67 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/main.h @@ -0,0 +1,7 @@ +#ifndef __DESMUME_GTK_MAIN_H__ +#define __DESMUME_GTK_MAIN_H__ + +void Pause(); +void Launch(); + +#endif diff --git a/desmume/src/frontend/posix/gtk2/meson.build b/desmume/src/frontend/posix/gtk2/meson.build new file mode 100644 index 000000000..25aa124b8 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/meson.build @@ -0,0 +1,40 @@ +dep_gtk3 = dependency('gtk+-3.0') +dep_x11 = dependency('x11') + +gtk_dependencies = dependencies + [dep_gtk3, dep_x11] + +desmume_src = [ + 'avout_pipe_base.cpp', + 'avout_x264.cpp', + 'avout_flac.cpp', + 'config.cpp', + 'desmume.cpp', + 'dToolsList.cpp', + 'tools/ioregsView.cpp', + '../shared/sndsdl.cpp', + '../shared/ctrlssdl.cpp', + 'osmesa_3Demu.cpp', + 'sdl_3Demu.cpp', + 'cheatsGTK.cpp', + 'main.cpp', +] + +# TODO: why do we have to redeclare it here with one more fs level? +includes = include_directories( + '../../../../src', + '../../../../src/libretro-common/include', + '../../../../src/frontend', +) + +executable('desmume', + desmume_src, + dependencies: gtk_dependencies, + include_directories: includes, + link_with: libdesmume, + install: true, +) + +install_data('desmume.desktop', install_dir: join_paths(get_option('datadir'), 'applications')) +install_data('org.desmume.DeSmuME.metainfo.xml', install_dir: join_paths(get_option('datadir'), 'metainfo')) +install_data('DeSmuME.xpm', install_dir: join_paths(get_option('datadir'), 'pixmaps')) +install_man('doc/desmume.1') diff --git a/desmume/src/frontend/posix/gtk2/org.desmume.DeSmuME.metainfo.xml b/desmume/src/frontend/posix/gtk2/org.desmume.DeSmuME.metainfo.xml new file mode 100644 index 000000000..7efac5557 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/org.desmume.DeSmuME.metainfo.xml @@ -0,0 +1,37 @@ + + + org.desmume.DeSmuME + DeSmuME + Nintendo DS emulator + GPL-2.0-or-later + CC0-1.0 + DeSmuME Team + linkmauve_AT_linkmauve.fr + desmume.desktop + + http://desmume.org + https://github.com/TASVideos/desmume/issues + + +

    + DeSmuME is a Nintendo DS emulator. +

    +
    + + + desmume + + + + + https://linkmauve.fr/files/desmume-pokémon.png + DeSmuME running Pokémon Heart Gold + + + + + + + + +
    diff --git a/desmume/src/frontend/posix/gtk2/osmesa_3Demu.cpp b/desmume/src/frontend/posix/gtk2/osmesa_3Demu.cpp new file mode 100644 index 000000000..9366a20cc --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/osmesa_3Demu.cpp @@ -0,0 +1,112 @@ +/* + Copyright (C) 2009 Guillaume Duhamel + Copyright (C) 2009-2017 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#ifdef HAVE_LIBOSMESA + +#include +#include +#include "../OGLRender.h" +#include "../OGLRender_3_2.h" + +#include "osmesa_3Demu.h" + +static void *buffer = NULL; +static OSMesaContext ctx = NULL; + +static bool osmesa_beginOpenGL(void) { return 1; } +static void osmesa_endOpenGL(void) { } +static bool osmesa_init(void) { return is_osmesa_initialized(); } + +void deinit_osmesa_3Demu (void) +{ + free(buffer); + buffer = NULL; + + OSMesaDestroyContext(ctx); + ctx = NULL; +} + +bool init_osmesa_3Demu(void) +{ +#if (((OSMESA_MAJOR_VERSION * 100) + OSMESA_MINOR_VERSION) >= 1102) && defined(OSMESA_CONTEXT_MAJOR_VERSION) + static const int attributes_3_2_core_profile[] = { + OSMESA_FORMAT, OSMESA_RGBA, + OSMESA_DEPTH_BITS, 24, + OSMESA_STENCIL_BITS, 8, + OSMESA_ACCUM_BITS, 0, + OSMESA_PROFILE, OSMESA_CORE_PROFILE, + OSMESA_CONTEXT_MAJOR_VERSION, 3, + OSMESA_CONTEXT_MINOR_VERSION, 2, + 0 }; + + ctx = OSMesaCreateContextAttribs(attributes_3_2_core_profile, NULL); + if (ctx == NULL) + { + printf("OSMesa: Could not create a 3.2 Core Profile context. Will attempt to create a 2.1 compatibility context...\n"); + + static const int attributes_2_1[] = { + OSMESA_FORMAT, OSMESA_RGBA, + OSMESA_DEPTH_BITS, 24, + OSMESA_STENCIL_BITS, 8, + OSMESA_ACCUM_BITS, 0, + OSMESA_PROFILE, OSMESA_COMPAT_PROFILE, + OSMESA_CONTEXT_MAJOR_VERSION, 2, + OSMESA_CONTEXT_MINOR_VERSION, 1, + 0 }; + + ctx = OSMesaCreateContextAttribs(attributes_2_1, NULL); + } +#endif + + if (ctx == NULL) + { + ctx = OSMesaCreateContextExt(OSMESA_RGBA, 24, 8, 0, NULL); + if (ctx == NULL) + { + printf("OSMesa: OSMesaCreateContextExt() failed!\n"); + return false; + } + } + + buffer = malloc(GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(uint32_t)); + if (!buffer) + { + printf("OSMesa: Could not allocate enough memory for the context!\n"); + return false; + } + + if (!OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT)) + { + printf("OSMesa: OSMesaMakeCurrent() failed!\n"); + free(buffer); + return false; + } + + oglrender_init = osmesa_init; + oglrender_beginOpenGL = osmesa_beginOpenGL; + oglrender_endOpenGL = osmesa_endOpenGL; + + return true; +} + +bool is_osmesa_initialized(void) +{ + return (ctx != NULL); +} + +#endif diff --git a/desmume/src/frontend/posix/gtk2/osmesa_3Demu.h b/desmume/src/frontend/posix/gtk2/osmesa_3Demu.h new file mode 100644 index 000000000..af6a9f4db --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/osmesa_3Demu.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2009 Guillaume Duhamel + Copyright (C) 2009-2017 DeSmuME team + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#ifndef OSMESA_3DEMU_H +#define OSMESA_3DEMU_H + +bool init_osmesa_3Demu(void); +void deinit_osmesa_3Demu(void); +bool is_osmesa_initialized(void); + +#endif // OSMESA_3DEMU_H + diff --git a/desmume/src/frontend/posix/gtk2/osxbuild b/desmume/src/frontend/posix/gtk2/osxbuild new file mode 100755 index 000000000..4e69c5502 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/osxbuild @@ -0,0 +1,11 @@ +#!/bin/sh + +rm desmume.app.zip +rm -rf desmume.app +mkdir desmume.app +mkdir desmume.app/MacOS +cp desmume desmume.app/MacOS +cp Info.plist desmume.app +./dylibbundler -cd -d desmume.app/libs -b -x desmume.app/MacOS/desmume +./fixups +zip -r desmume.app.zip desmume.app diff --git a/desmume/src/frontend/posix/gtk2/sdl_3Demu.cpp b/desmume/src/frontend/posix/gtk2/sdl_3Demu.cpp new file mode 100644 index 000000000..fb05b157c --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/sdl_3Demu.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2020 Emmanuel Gil Peyrot + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#include +#include +#include "../OGLRender.h" + +#include "sdl_3Demu.h" + +static bool sdl_beginOpenGL(void) { return 1; } +static void sdl_endOpenGL(void) { } +static bool sdl_init(void) { return is_sdl_initialized(); } + +static SDL_Window *win = NULL; +static SDL_GLContext ctx = NULL; + +bool deinit_sdl_3Demu(void) +{ + bool ret = false; + + if (ctx) { + SDL_GL_DeleteContext(ctx); + ctx = NULL; + ret = true; + } + + if (win) { + SDL_DestroyWindow(win); + win = NULL; + ret = true; + } + + return ret; +} + +bool init_sdl_3Demu(void) +{ + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + + win = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 256, 192, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (!win) { + fprintf(stderr, "SDL: Failed to create a window: %s\n", SDL_GetError()); + return false; + } + + ctx = SDL_GL_CreateContext(win); + if (!ctx) { + fprintf(stderr, "SDL: Failed to create an OpenGL context: %s\n", SDL_GetError()); + return false; + } + + printf("OGL/SDL Renderer has finished the initialization.\n"); + + oglrender_init = sdl_init; + oglrender_beginOpenGL = sdl_beginOpenGL; + oglrender_endOpenGL = sdl_endOpenGL; + + return true; +} + +bool is_sdl_initialized(void) +{ + return (ctx != NULL); +} diff --git a/desmume/src/frontend/posix/gtk2/sdl_3Demu.h b/desmume/src/frontend/posix/gtk2/sdl_3Demu.h new file mode 100644 index 000000000..2088263da --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/sdl_3Demu.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2020 Emmanuel Gil Peyrot + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . + */ + +#ifndef SDL_3DEMU_H +#define SDL_3DEMU_H + +bool init_sdl_3Demu(void); +bool deinit_sdl_3Demu(void); +bool is_sdl_initialized(void); + +#endif // SDL_3DEMU_H + diff --git a/desmume/src/frontend/posix/gtk2/tools/ioregsView.cpp b/desmume/src/frontend/posix/gtk2/tools/ioregsView.cpp new file mode 100644 index 000000000..c338470c4 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/tools/ioregsView.cpp @@ -0,0 +1,492 @@ +/* ioregsView.cpp - this file is part of DeSmuME + * + * Copyright (C) 2006 Thoduv + * Copyright (C) 2006-2015 DeSmuME Team + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "../dTool.h" + +#include "../../MMU.h" +#include "../../registers.h" + +#undef GPOINTER_TO_INT +#define GPOINTER_TO_INT(p) ((gint) (glong) (p)) + +#define SHORTNAME "ioregs" +#define TOOL_NAME "IO regs view" + +#if !GTK_CHECK_VERSION(2,24,0) +#define gtk_combo_box_text_new gtk_combo_box_new_text +#define gtk_combo_box_text_append_text gtk_combo_box_append_text +#define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX +#endif + +BOOL CPUS [2] = {TRUE, TRUE}; + +static GtkWidget *mWin[2]; +static GtkWidget *mVbox0[2]; +static GtkWidget *mIoRegCombo[2]; +static GtkWidget *mRegInfos[2]; + +typedef void (*reg_dispFn)(int c); +typedef u32 (*reg_valFn)(int c); + +typedef struct +{ + char name[64]; + u32 adress; + int size; + reg_dispFn create; + reg_dispFn update; + reg_dispFn destroy; + reg_valFn value; +} reg_t; + +static reg_t *current_reg[2] = {NULL, NULL}; + +#define REGFN_BEGIN(reg) \ + GtkWidget **_wl_ = Widgets_##reg [c]; + +#define BIT_CHECK(w, n, s) { \ + char _bit_check_buf[64]; \ + snprintf(_bit_check_buf, ARRAY_SIZE(_bit_check_buf), "Bit %d: %s", n,s); \ + _wl_[w] = gtk_check_button_new_with_label(_bit_check_buf ); \ + gtk_box_pack_start(GTK_BOX(mVbox0[c]), _wl_[w], FALSE, FALSE, 0); } + +#define BIT_COMBO(w,n,s) { \ + _wl_[w] = gtk_hbox_new(FALSE, 0); \ + gtk_box_pack_start(GTK_BOX(mVbox0[c]), _wl_[w], FALSE, FALSE, 0); } \ + char _bit_combo_buf[64]; \ + snprintf(_bit_combo_buf, ARRAY_SIZE(_bit_combo_buf), "Bits %s: %s", n,s); \ + GtkWidget *__combo_lbl_tmp = gtk_label_new(_bit_combo_buf); \ + GtkWidget *__combo_tmp = gtk_combo_box_text_new(); \ + +#define BIT_COMBO_ADD(w, s) { \ + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(__combo_tmp), s); } + +#define BIT_COMBO_GET(w) (GTK_WIDGET(g_list_first(gtk_container_get_children(GTK_CONTAINER(_wl_[w])))->data)) + +#define BIT_COMBO_END(w) \ + gtk_box_pack_start(GTK_BOX(_wl_[w]), __combo_tmp, FALSE, FALSE, 0); \ + gtk_box_pack_start(GTK_BOX(_wl_[w]), __combo_lbl_tmp, FALSE, FALSE, 0); + +#define CREA_END() \ + gtk_widget_show_all(mWin[c]); + +/////////////////////////////// REG_IME /////////////////////////////// +static GtkWidget *Widgets_REG_IME[2][1]; +static void crea_REG_IME(int c) +{ + REGFN_BEGIN(REG_IME); + BIT_CHECK(0, 0, "Master interrupt enable"); + CREA_END(); +} +static void updt_REG_IME(int c) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Widgets_REG_IME[c][0]), MMU.reg_IME[c] ? 1 : 0); } +static void dest_REG_IME(int c) { gtk_widget_destroy(Widgets_REG_IME[c][0]); } +static u32 val_REG_IME(int c) { return MMU.reg_IME[c]; } + +/////////////////////////////// REG_IE /////////////////////////////// + +static const char *interrupt_strings[25] = +{ + "LCD VBlank", // 0 + "LCD HBlank", // 1 + "LCD VCount", // 2 + "Timer0 overflow", // 3 + "Timer1 overflow", + "Timer2 overflow", + "Timer3 overflow", + "Serial communication (RTC)", // 7 + "DMA0", // 8 + "DMA1", + "DMA2", + "DMA3", + "Keypad", // 12 + "Game Pak (GBA slot)", // 13 + "", // 14 + "", // 15 + "IPC Sync", // 16 + "IPC Send FIFO empty", // 17 + "IPC Recv FIFO not empty", // 18 + "Card Data Transfer Completion (DS-card slot)", // 19 + "Card IREQ_MC (DS-card slot)", // 20 + "Geometry (3D) command FIFO", // 21 + "Screens unfolding", // 22 + "SPI bus", // 23 + "Wifi" // 24 +}; +#define INTERRUPT_SKIP(c) if(i == 14 || i == 15 || (c == 0 && (i == 7 || i == 22 || i == 23 || i == 24)) || (c == 1 && i == 21))continue; + +static GtkWidget *Widgets_REG_IE[2][32]; +static void crea_REG_IE(int c) +{ + REGFN_BEGIN(REG_IE); + int i; + for(i = 0; i < 24; i++) { INTERRUPT_SKIP(c); BIT_CHECK(i, i, interrupt_strings[i]); } + CREA_END(); +} +static void updt_REG_IE(int c) +{ + int i; + for(i = 0; i < 24; i++) { INTERRUPT_SKIP(c); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Widgets_REG_IE[c][i]), (MMU.reg_IE[c] & (1<():MMU.gen_IF<1>()) & (1<():MMU.gen_IF<1>()); } + +/////////////////////////////// REG_IPCFIFOCNT /////////////////////////////// +static const char *fifocnt_strings[] = +{ + "Send FIFO empty", + "Send FIFO full", + "Send FIFO empty IRQ", + "Send FIFO clear (flush)", + "","","","", + "Receive FIFO empty", + "Receive FIFO full", + "Receive FIFO not empty IRQ", + "","","", + "Error (read when empty/write when full)", + "Enable FIFOs" +}; +#define FIFOCNT_SKIP(c) if(i==4||i==5||i==6||i==11||i==12||i==13)continue; + +static GtkWidget *Widgets_REG_IPCFIFOCNT[2][16]; +static void crea_REG_IPCFIFOCNT(int c) +{ + REGFN_BEGIN(REG_IPCFIFOCNT); + int i; + for(i = 0; i < 16; i++) { FIFOCNT_SKIP(c); BIT_CHECK(i, i, fifocnt_strings[i]); } + CREA_END(); +} +static void updt_REG_IPCFIFOCNT(int c) +{ + int i; + for(i = 0; i < 16; i++) { FIFOCNT_SKIP(c); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Widgets_REG_IPCFIFOCNT[c][i]), (((u16 *)(MMU.MMU_MEM[c][0x40]))[0x184>>1]&(1<>1]; } + +/////////////////////////////// POWER_CR /////////////////////////////// +static const char *powercr9_strings[] = +{ + "Enable LCD", + "2D A engine", + "3D render engine", + "3D geometry engine (matrix)", + "", "", "", "", "", + "2D B engine", + "", "", "", "", "", + "Swap LCD" +}; +static const char *powercr7_strings[] = +{ + "Sound speakers", + "Wifi system" +}; +#define POWER_CR_SKIP(c) if(i==4||i==5||i==6||i==7||i==8||i==10||i==11||i==12||i==13||i==14)continue; +#define POWER_CR_SIZE(c) ((c==0)?16:2) + +static GtkWidget *Widgets_POWER_CR[2][16]; +static void crea_POWER_CR(int c) +{ + REGFN_BEGIN(POWER_CR); + int i; + for(i = 0; i < POWER_CR_SIZE(c); i++) { POWER_CR_SKIP(c); BIT_CHECK(i, i, (c==0?powercr9_strings:powercr7_strings)[i]); } + CREA_END(); +} +static void updt_POWER_CR(int c) +{ + int i; + for(i = 0; i < POWER_CR_SIZE(c); i++) { POWER_CR_SKIP(c); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Widgets_POWER_CR[c][i]), (((u16 *)(MMU.MMU_MEM[c][0x40]))[0x304>>1]&(1<>1]; } + +/////////////////////////////// REG_SPICNT /////////////////////////////// +static const char *spicnt_strings[16] = +{ + "Baudrate", + "","","","","","", + "Busy flag", + "Device", + "", + "Transfer size", + "Chipselect Hold", + "Unknown", + "Unknown", + "Interrupt request", + "Enable SPI bus", +}; + +// 0-1 Baudrate (0=4MHz/Firmware, 1=2MHz/Touchscr, 2=1MHz/Powerman., 3=512KHz) +// 2-6 Not used (Zero) +// 7 Busy Flag (0=Ready, 1=Busy) (presumably Read-only) +// 8-9 Device Select (0=Powerman., 1=Firmware, 2=Touchscr, 3=Reserved) +// 10 Transfer Size (0=8bit, 1=16bit) +// 11 Chipselect Hold (0=Deselect after transfer, 1=Keep selected) +// 12 Unknown (usually 0) (set equal to Bit11 when BIOS accesses firmware) +// 13 Unknown (usually 0) (set to 1 when BIOS accesses firmware) +// 14 Interrupt Request (0=Disable, 1=Enable) +// 15 SPI Bus Enable (0=Disable, 1=Enable) + + +#define REG_SPICNT_SKIP(c) if(i==1||i==2||i==3||i==4||i==5||i==6||i==9)continue; +#define REG_SPICNT_ISCHECK(c) (i==7||i==11||i==14||i==15||i==12||i==13) + +static GtkWidget *Widgets_REG_SPICNT[2][16]; +static void crea_REG_SPICNT(int c) +{ + REGFN_BEGIN(REG_SPICNT); + int i; + for(i = 0; i < 16; i++) { REG_SPICNT_SKIP(c); + if(REG_SPICNT_ISCHECK(c)) { BIT_CHECK(i, i, spicnt_strings[i]); } + else if(i == 0) { BIT_COMBO(i, "0-1", spicnt_strings[0]); + BIT_COMBO_ADD(i, "0= 4Mhz"); + BIT_COMBO_ADD(i, "1= 2Mhz"); + BIT_COMBO_ADD(i, "2= 1Mhz"); + BIT_COMBO_ADD(i, "3= 512Khz"); + BIT_COMBO_END(i); } + else if(i == 8) { BIT_COMBO(i, "8-9", spicnt_strings[8]); + BIT_COMBO_ADD(i, "0= Power management device"); + BIT_COMBO_ADD(i, "1= Firmware"); + BIT_COMBO_ADD(i, "2= Touchscreen/Microphone"); + BIT_COMBO_ADD(i, "3= Reserved/Prohibited"); + BIT_COMBO_END(i); } + else if(i == 10) { BIT_COMBO(i, "10", spicnt_strings[10]); + BIT_COMBO_ADD(i, "0= 8 bits"); + BIT_COMBO_ADD(i, "1= 16 bits"); + BIT_COMBO_END(i); } + } + CREA_END(); +} +static u32 val_REG_SPICNT(int c) { return ((u16 *)(MMU.MMU_MEM[c][0x40]))[0x1C0>>1]; } +static void updt_REG_SPICNT(int c) +{ + REGFN_BEGIN(REG_SPICNT); + u16 val = val_REG_SPICNT(c); + int i; + for(i = 0; i < 16; i++) { REG_SPICNT_SKIP(c); + if(REG_SPICNT_ISCHECK(c)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Widgets_REG_SPICNT[c][i]), (((u16 *)(MMU.MMU_MEM[c][0x40]))[0x1C0>>1]&(1<>8)&3); } + else if(i == 10) { gtk_combo_box_set_active(GTK_COMBO_BOX(BIT_COMBO_GET(i)) , (val>>10)&1); } + } +} +static void dest_REG_SPICNT(int c) +{ + int i; + for(i = 0; i < 16; i++) { REG_SPICNT_SKIP(c); gtk_widget_destroy(Widgets_REG_SPICNT[c][i]); } +} + +/////////////////////////////// LIST /////////////////////////////// +#define BITS_8 1 +#define BITS_16 2 +#define BITS_32 4 +static const char *bits_strings[5] = {"0", "8", "16", "24", "32"}; + +#define REG_STR(r, s) {#r, r, s, &crea_##r, &updt_##r, &dest_##r, &val_##r} +#define REG_FNS(r) &crea_##r, &updt_##r, &dest_##r, &val_##r +//////// ARM9 //////// +#define REG_LIST_SIZE_ARM9 5 +static const reg_t regs_list_9[] = +{ + {"REG_IME", REG_IME, BITS_16, REG_FNS(REG_IME)}, + {"REG_IE", REG_IE, BITS_32, REG_FNS(REG_IE)}, + {"REG_IF", REG_IF, BITS_32, REG_FNS(REG_IF)}, + {"REG_IPCFIFOCNT", 0x04000184, BITS_16, REG_FNS(REG_IPCFIFOCNT)}, + {"POWER_CR", REG_POWCNT1, BITS_16, REG_FNS(POWER_CR)} +}; + +//////// ARM7 //////// +#define REG_LIST_SIZE_ARM7 6 +static const reg_t regs_list_7[] = +{ + {"REG_IME", REG_IME, BITS_16, REG_FNS(REG_IME)}, + {"REG_IE", REG_IE, BITS_32, REG_FNS(REG_IE)}, + {"REG_IF", REG_IF, BITS_32, REG_FNS(REG_IF)}, + {"REG_IPCFIFOCNT", 0x04000184, BITS_16, REG_FNS(REG_IPCFIFOCNT)}, + {"POWER_CR", REG_POWCNT1, BITS_16, REG_FNS(POWER_CR)}, + {"REG_SPICNT", REG_SPICNT, BITS_16, REG_FNS(REG_SPICNT)} +}; + +#define GET_REG_LIST_SIZE(i) ((i==0)?REG_LIST_SIZE_ARM9:REG_LIST_SIZE_ARM7) +#define GET_REG_LIST(i) ((i==0)?regs_list_9:regs_list_7) + +static void _clearContainer(GtkWidget *widget, gpointer data) +{ + if(widget == mRegInfos[0] || widget == mRegInfos[1]) return; + if(widget == mIoRegCombo[0] || widget == mIoRegCombo[1]) return; + + gtk_container_remove(GTK_CONTAINER((GtkWidget*)data), widget); +// gtk_widget_destroy(widget); +// gtk_object_destroy(GTK_OBJECT(widget)); +} + +static void selected_reg(GtkWidget* widget, gpointer data) +{ + int c = GPOINTER_TO_INT(data); + gchar *regInfosBuffer; + + guint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); + + if(current_reg[c]) current_reg[c]->destroy(c); + gtk_container_foreach(GTK_CONTAINER(mVbox0[c]), _clearContainer, (gpointer)mVbox0[c]); + + current_reg[c] = (reg_t*)&(GET_REG_LIST(c)[active]); + +// gtk_box_pack_start(GTK_BOX(mVbox0[c]), mIoRegCombo[c], FALSE, FALSE, 0); + + switch (current_reg[c]->size) { + case BITS_8: + regInfosBuffer = g_strdup_printf("0x%02X", current_reg[c]->value(c)); + break; + case BITS_16: + regInfosBuffer = g_strdup_printf("0x%04X", current_reg[c]->value(c)); + break; + default: + regInfosBuffer = g_strdup_printf("0x%08X", current_reg[c]->value(c)); + } +// gtk_box_pack_start(GTK_BOX(mVbox0[c]), mRegInfos[c], FALSE, FALSE, 0); + gtk_label_set_label(GTK_LABEL(mRegInfos[c]), regInfosBuffer); + g_free(regInfosBuffer); + + current_reg[c]->create(c); + current_reg[c]->update(c); +} + +/////////////////////////////// TOOL /////////////////////////////// + +static int DTOOL_ID; + +static void close() +{ + memset(current_reg, 0, sizeof(current_reg)); + dTool_CloseCallback(DTOOL_ID); +} + +static void _closeOne(GtkWidget *widget, gpointer data) +{ + int c = GPOINTER_TO_INT(data); + + CPUS[c] = FALSE; + if(c == 0 && !CPUS[1]) close(); + if(c == 1 && !CPUS[0]) close(); + + gtk_widget_destroy(mRegInfos[c]); + gtk_widget_destroy(mIoRegCombo[c]); + gtk_widget_destroy(mVbox0[c]); +// gtk_widget_destroy(mWin[c]); +} + +static void update() +{ + int c; + + for(c = 0; c < 2; c++) + { + if(!CPUS[c]) continue; + current_reg[c]->update(c); + } +} + +static void open(int ID) +{ + int c, i; + + DTOOL_ID = ID; + + for(c = 0; c < 2; c++) + { + CPUS[c] = TRUE; + + mWin[c]= gtk_window_new(GTK_WINDOW_TOPLEVEL); + if(c == 0) gtk_window_set_title(GTK_WINDOW(mWin[c]), TOOL_NAME " : ARM9"); + else gtk_window_set_title(GTK_WINDOW(mWin[c]), TOOL_NAME " : ARM7"); + g_signal_connect(G_OBJECT(mWin[c]), "destroy", G_CALLBACK(&_closeOne), GINT_TO_POINTER(c)); + + mVbox0[c] = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(mWin[c]), mVbox0[c]); + + mIoRegCombo[c] = gtk_combo_box_text_new(); + mRegInfos[c] = gtk_label_new(""); + + for(i = 0; i < GET_REG_LIST_SIZE(c); i++) + { + gchar *reg_name_buffer; + reg_name_buffer = g_strdup_printf("0x%08X : %s (%s)", GET_REG_LIST(c)[i].adress, GET_REG_LIST(c)[i].name, bits_strings[GET_REG_LIST(c)[i].size]); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mIoRegCombo[c]), reg_name_buffer); + g_free(reg_name_buffer); + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(mIoRegCombo[c]), 0); + g_signal_connect(G_OBJECT(mIoRegCombo[c]), "changed", G_CALLBACK(selected_reg), GINT_TO_POINTER(c)); + + gtk_box_pack_start(GTK_BOX(mVbox0[c]), mIoRegCombo[c], FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(mVbox0[c]), mRegInfos[c], FALSE, FALSE, 0); + selected_reg(mIoRegCombo[c], GINT_TO_POINTER(c)); + + gtk_widget_show_all(mWin[c]); + } +} + +/////////////////////////////// TOOL DEFINITION /////////////////////////////// + +dTool_t dTool_ioregsView = +{ + SHORTNAME, + TOOL_NAME, + &open, + &update, + &close +}; + diff --git a/desmume/src/frontend/posix/gtk2/tools/ioregsView.h b/desmume/src/frontend/posix/gtk2/tools/ioregsView.h new file mode 100644 index 000000000..4ee8949f8 --- /dev/null +++ b/desmume/src/frontend/posix/gtk2/tools/ioregsView.h @@ -0,0 +1,29 @@ +/* ioregsview.h - this file is part of DeSmuME + * + * Copyright (C) 2006,2007 DeSmuME Team + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __IOREGSVIEW_H__ +#define __IOREGSVIEW_H__ + +#include "../dTool.h" + +extern dTool_t dTool_ioregsView; + +#endif /*__IOREGSVIEW_H__*/ +