visualboyadvance-m/src/gtk/window.cpp

1483 lines
39 KiB
C++

// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// 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, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "window.h"
#include <gtkmm/stock.h>
#include <gtkmm/alignment.h>
#include <gtkmm/messagedialog.h>
#include <sys/stat.h>
#include <SDL.h>
#include "../gba/GBA.h"
#include "../gba/RTC.h"
#include "../gba/Sound.h"
#include "../gb/gb.h"
#include "../gb/gbGlobals.h"
#include "../gb/gbSound.h"
#include "../gb/gbPrinter.h"
#include "../Util.h"
#include "tools.h"
#include "intl.h"
#include "screenarea-cairo.h"
#ifdef USE_OPENGL
#include "screenarea-opengl.h"
#endif // USE_OPENGL
extern int RGB_LOW_BITS_MASK;
namespace VBA
{
using Gnome::Glade::Xml;
Window * Window::m_poInstance = NULL;
const Window::SJoypadKey Window::m_astJoypad[] =
{
{ "left", KEY_LEFT },
{ "right", KEY_RIGHT },
{ "up", KEY_UP },
{ "down", KEY_DOWN },
{ "A", KEY_BUTTON_A },
{ "B", KEY_BUTTON_B },
{ "select", KEY_BUTTON_SELECT },
{ "start", KEY_BUTTON_START },
{ "L", KEY_BUTTON_L },
{ "R", KEY_BUTTON_R },
{ "speed", KEY_BUTTON_SPEED },
{ "capture", KEY_BUTTON_CAPTURE },
{ "speed", KEY_BUTTON_SPEED },
{ "capture", KEY_BUTTON_CAPTURE },
{ "autoA", KEY_BUTTON_AUTO_A },
{ "autoB", KEY_BUTTON_AUTO_B }
};
Window::Window(GtkWindow * _pstWindow, const Glib::RefPtr<Xml> & _poXml) :
Gtk::Window (_pstWindow),
m_iGBScreenWidth (160),
m_iGBScreenHeight (144),
m_iSGBScreenWidth (256),
m_iSGBScreenHeight(224),
m_iGBAScreenWidth (240),
m_iGBAScreenHeight(160),
m_iFrameskipMin (0),
m_iFrameskipMax (9),
m_iScaleMin (1),
m_iScaleMax (6),
m_iShowSpeedMin (ShowNone),
m_iShowSpeedMax (ShowDetailed),
m_iSaveTypeMin (SaveAuto),
m_iSaveTypeMax (SaveNone),
m_iSoundSampleRateMin(11025),
m_iSoundSampleRateMax(48000),
m_fSoundVolumeMin (0.50f),
m_fSoundVolumeMax (2.00f),
m_iEmulatorTypeMin(EmulatorAuto),
m_iEmulatorTypeMax(EmulatorSGB2),
m_iFilter2xMin (FirstFilter),
m_iFilter2xMax (LastFilter),
m_iFilterIBMin (FirstFilterIB),
m_iFilterIBMax (LastFilterIB),
m_iJoypadMin (PAD_1),
m_iJoypadMax (PAD_4),
m_iVideoOutputMin (OutputCairo),
m_iVideoOutputMax (OutputOpenGL),
m_bFullscreen (false)
{
m_poXml = _poXml;
m_poFileOpenDialog = NULL;
m_iScreenWidth = m_iGBAScreenWidth;
m_iScreenHeight = m_iGBAScreenHeight;
m_eCartridge = CartridgeNone;
vInitSDL();
vInitSystem();
vSetDefaultTitle();
// Get config
//
m_sUserDataDir = Glib::get_user_config_dir() + "/gvbam";
m_sConfigFile = m_sUserDataDir + "/config";
vInitConfig();
if (! Glib::file_test(m_sUserDataDir, Glib::FILE_TEST_EXISTS))
{
mkdir(m_sUserDataDir.c_str(), 0777);
}
if (Glib::file_test(m_sConfigFile, Glib::FILE_TEST_EXISTS))
{
vLoadConfig(m_sConfigFile);
vCheckConfig();
}
else
{
vSaveConfig(m_sConfigFile);
}
vCreateFileOpenDialog();
vApplyConfigJoypads();
vApplyConfigScreenArea();
vApplyConfigFilter();
vApplyConfigFilterIB();
vApplyConfigMute();
vApplyConfigVolume();
vApplyConfigGBSystem();
vApplyConfigGBBorder();
vApplyConfigGBPrinter();
vApplyConfigGBASaveType();
vApplyConfigGBAFlashSize();
vApplyConfigGBARTC();
Gtk::MenuItem * poMI;
Gtk::CheckMenuItem * poCMI;
// Menu bar
m_poMenuBar = dynamic_cast<Gtk::MenuBar *>(_poXml->get_widget("MenuBar"));
m_poMenuBar->signal_deactivate().connect(sigc::mem_fun(*this, &Window::vOnMenuExit));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileMenu"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("EmulationMenu"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("OptionsMenu"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("HelpMenu"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter));
// File menu
//
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileOpen"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileOpen));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileLoad"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileLoad));
m_listSensitiveWhenPlaying.push_back(poMI);
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileSave"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileSave));
m_listSensitiveWhenPlaying.push_back(poMI);
for (int i = 0; i < 10; i++)
{
char csName[20];
snprintf(csName, 20, "LoadGameSlot%d", i + 1);
m_apoLoadGameItem[i] = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget(csName));
snprintf(csName, 20, "SaveGameSlot%d", i + 1);
m_apoSaveGameItem[i] = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget(csName));
m_apoLoadGameItem[i]->signal_activate().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnLoadGame),
i + 1));
m_apoSaveGameItem[i]->signal_activate().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnSaveGame),
i + 1));
}
vUpdateGameSlots();
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("LoadGameMostRecent"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnLoadGameMostRecent));
m_listSensitiveWhenPlaying.push_back(poMI);
poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("LoadGameAuto"));
poCMI->set_active(m_poCoreConfig->oGetKey<bool>("load_game_auto"));
vOnLoadGameAutoToggled(poCMI);
poCMI->signal_toggled().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnLoadGameAutoToggled),
poCMI));
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("SaveGameOldest"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSaveGameOldest));
m_listSensitiveWhenPlaying.push_back(poMI);
m_poFilePauseItem = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("FilePause"));
m_poFilePauseItem->set_active(false);
vOnFilePauseToggled(m_poFilePauseItem);
m_poFilePauseItem->signal_toggled().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnFilePauseToggled),
m_poFilePauseItem));
m_listSensitiveWhenPlaying.push_back(m_poFilePauseItem);
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileReset"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileReset));
m_listSensitiveWhenPlaying.push_back(poMI);
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileScreenCapture"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileScreenCapture));
m_listSensitiveWhenPlaying.push_back(poMI);
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileClose"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileClose));
m_listSensitiveWhenPlaying.push_back(poMI);
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileExit"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileExit));
// Recent menu
//
m_poRecentManager = Gtk::RecentManager::get_default();
Gtk::RecentFilter oRecentFilter;
oRecentFilter.add_application( Glib::get_application_name() );
m_poRecentChooserMenu = Gtk::manage( new Gtk::RecentChooserMenu(m_poRecentManager) );
m_poRecentChooserMenu->set_show_numbers();
m_poRecentChooserMenu->set_show_tips();
m_poRecentChooserMenu->set_local_only();
m_poRecentChooserMenu->add_filter(oRecentFilter);
m_poRecentChooserMenu->signal_item_activated().connect(
sigc::mem_fun(*this, &Window::vOnRecentFile));
m_poRecentMenu = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("RecentMenu"));
m_poRecentMenu->set_submenu(static_cast<Gtk::Menu &>(*m_poRecentChooserMenu));
// Frameskip menu
//
struct
{
const char * m_csName;
const int m_iFrameskip;
}
astFrameskip[] =
{
{ "FrameskipAutomatic", -1 },
{ "Frameskip0", 0 },
{ "Frameskip1", 1 },
{ "Frameskip2", 2 },
{ "Frameskip3", 3 },
{ "Frameskip4", 4 },
{ "Frameskip5", 5 },
{ "Frameskip6", 6 },
{ "Frameskip7", 7 },
{ "Frameskip8", 8 },
{ "Frameskip9", 9 }
};
int iDefaultFrameskip;
if (m_poCoreConfig->sGetKey("frameskip") == "auto")
{
iDefaultFrameskip = -1;
}
else
{
iDefaultFrameskip = m_poCoreConfig->oGetKey<int>("frameskip");
}
for (guint i = 0; i < G_N_ELEMENTS(astFrameskip); i++)
{
poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astFrameskip[i].m_csName));
if (astFrameskip[i].m_iFrameskip == iDefaultFrameskip)
{
poCMI->set_active();
vOnFrameskipToggled(poCMI, iDefaultFrameskip);
}
poCMI->signal_toggled().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnFrameskipToggled),
poCMI, astFrameskip[i].m_iFrameskip));
}
// Emulator menu
//
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("DirectoriesConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDirectories));
poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("EmulatorPauseWhenInactive"));
poCMI->set_active(m_poDisplayConfig->oGetKey<bool>("pause_when_inactive"));
vOnPauseWhenInactiveToggled(poCMI);
poCMI->signal_toggled().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnPauseWhenInactiveToggled),
poCMI));
// Show speed menu
//
struct
{
const char * m_csName;
const EShowSpeed m_eShowSpeed;
}
astShowSpeed[] =
{
{ "ShowSpeedNone", ShowNone },
{ "ShowSpeedPercentage", ShowPercentage },
{ "ShowSpeedDetailed", ShowDetailed }
};
EShowSpeed eDefaultShowSpeed = (EShowSpeed)m_poDisplayConfig->oGetKey<int>("show_speed");
for (guint i = 0; i < G_N_ELEMENTS(astShowSpeed); i++)
{
poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astShowSpeed[i].m_csName));
if (astShowSpeed[i].m_eShowSpeed == eDefaultShowSpeed)
{
poCMI->set_active();
vOnShowSpeedToggled(poCMI, eDefaultShowSpeed);
}
poCMI->signal_toggled().connect(sigc::bind(
sigc::mem_fun(*this, &Window::vOnShowSpeedToggled),
poCMI, astShowSpeed[i].m_eShowSpeed));
}
// Game Boy menu
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GameBoyConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyConfigure));
// Game Boy Advance menu
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GameBoyAdvanceConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyAdvanceConfigure));
// Display menu
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("DisplayConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDisplayConfigure));
// Sound menu
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("SoundConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSoundConfigure));
// Joypad menu
//
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("JoypadConfigure"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnJoypadConfigure));
EPad eDefaultJoypad = (EPad)m_poInputConfig->oGetKey<int>("active_joypad");
inputSetDefaultJoypad(eDefaultJoypad);
// Fullscreen menu
//
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("VideoFullscreen"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnVideoFullscreen));
// Help menu
//
poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("HelpAbout"));
poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnHelpAbout));
// Init widgets sensitivity
for (std::list<Gtk::Widget *>::iterator it = m_listSensitiveWhenPlaying.begin();
it != m_listSensitiveWhenPlaying.end();
it++)
{
(*it)->set_sensitive(false);
}
if (m_poInstance == NULL)
{
m_poInstance = this;
}
else
{
abort();
}
}
Window::~Window()
{
vOnFileClose();
vUnInitSystem();
vSaveJoypadsToConfig();
vSaveConfig(m_sConfigFile);
if (m_poFileOpenDialog != NULL)
{
delete m_poFileOpenDialog;
}
m_poInstance = NULL;
}
void Window::vInitColors(EColorFormat _eColorFormat)
{
switch (_eColorFormat)
{
case ColorFormatBGR:
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
systemRedShift = 3;
systemGreenShift = 11;
systemBlueShift = 19;
RGB_LOW_BITS_MASK = 0x00010101;
#else
systemRedShift = 27;
systemGreenShift = 19;
systemBlueShift = 11;
RGB_LOW_BITS_MASK = 0x01010100;
#endif
break;
default:
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
systemRedShift = 19;
systemGreenShift = 11;
systemBlueShift = 3;
RGB_LOW_BITS_MASK = 0x00010101;
#else
systemRedShift = 11;
systemGreenShift = 19;
systemBlueShift = 27;
RGB_LOW_BITS_MASK = 0x01010100;
#endif
break;
}
for (int i = 0; i < 0x10000; i++)
{
systemColorMap32[i] = (((i & 0x1f) << systemRedShift)
| (((i & 0x3e0) >> 5) << systemGreenShift)
| (((i & 0x7c00) >> 10) << systemBlueShift));
}
for (int i = 0; i < 24; )
{
systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10);
systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10);
systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10);
systemGbPalette[i++] = 0;
}
Init_2xSaI(32);
}
void Window::vApplyConfigScreenArea()
{
EVideoOutput eVideoOutput = (EVideoOutput)m_poDisplayConfig->oGetKey<int>("output");
Gtk::Alignment * poC;
poC = dynamic_cast<Gtk::Alignment *>(m_poXml->get_widget("ScreenContainer"));
poC->remove();
poC->set(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER, 1.0, 1.0);
try
{
switch (eVideoOutput)
{
#ifdef USE_OPENGL
case OutputOpenGL:
vInitColors(ColorFormatBGR);
m_poScreenArea = Gtk::manage(new ScreenAreaGl(m_iScreenWidth, m_iScreenHeight));
break;
#endif // USE_OPENGL
case OutputCairo:
default:
vInitColors(ColorFormatRGB);
m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight));
break;
}
}
catch (std::exception e)
{
fprintf(stderr, "Unable to initialize output, falling back to Cairo\n");
m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight));
}
poC->add(*m_poScreenArea);
vDrawDefaultScreen();
m_poScreenArea->show();
}
void Window::vInitSystem()
{
systemColorDepth = 32;
systemVerbose = 0;
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
systemFrameSkip = 2;
emulating = 0;
gbFrameSkip = 0;
m_iFrameCount = 0;
soundInit();
}
void Window::vUnInitSystem()
{
soundShutdown();
}
void Window::vInitSDL()
{
static bool bDone = false;
if (bDone)
return;
int iFlags = (SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE);
if (SDL_Init(iFlags) < 0)
{
fprintf(stderr, "Failed to init SDL: %s", SDL_GetError());
abort();
}
inputSetKeymap(PAD_DEFAULT, KEY_LEFT, GDK_Left);
inputSetKeymap(PAD_DEFAULT, KEY_RIGHT, GDK_Right);
inputSetKeymap(PAD_DEFAULT, KEY_UP, GDK_Up);
inputSetKeymap(PAD_DEFAULT, KEY_DOWN, GDK_Down);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_A, GDK_z);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_B, GDK_x);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_START, GDK_Return);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SELECT, GDK_BackSpace);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_L, GDK_a);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_R, GDK_s);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SPEED, GDK_space);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_CAPTURE, GDK_F12);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_A, GDK_q);
inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_B, GDK_w);
// TODO : remove
int sdlNumDevices = SDL_NumJoysticks();
for (int i = 0; i < sdlNumDevices; i++)
SDL_JoystickOpen(i);
inputInitJoysticks();
bDone = true;
}
void Window::vInitConfig()
{
m_oConfig.vClear();
// Directories section
//
m_poDirConfig = m_oConfig.poAddSection("Directories");
m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir());
m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir());
m_poDirConfig->vSetKey("batteries", m_sUserDataDir);
m_poDirConfig->vSetKey("saves", m_sUserDataDir);
m_poDirConfig->vSetKey("captures", m_sUserDataDir);
// Core section
//
m_poCoreConfig = m_oConfig.poAddSection("Core");
m_poCoreConfig->vSetKey("load_game_auto", false );
m_poCoreConfig->vSetKey("frameskip", "auto" );
m_poCoreConfig->vSetKey("use_bios_file", false );
m_poCoreConfig->vSetKey("bios_file", "" );
m_poCoreConfig->vSetKey("enable_rtc", false );
m_poCoreConfig->vSetKey("save_type", SaveAuto );
m_poCoreConfig->vSetKey("flash_size", 64 );
m_poCoreConfig->vSetKey("gb_border", false );
m_poCoreConfig->vSetKey("gb_printer", false );
m_poCoreConfig->vSetKey("gb_use_bios_file", false );
m_poCoreConfig->vSetKey("gb_bios_file", "" );
m_poCoreConfig->vSetKey("emulator_type", EmulatorAuto );
// Display section
//
m_poDisplayConfig = m_oConfig.poAddSection("Display");
m_poDisplayConfig->vSetKey("scale", 1 );
m_poDisplayConfig->vSetKey("show_speed", ShowPercentage );
m_poDisplayConfig->vSetKey("pause_when_inactive", true );
m_poDisplayConfig->vSetKey("filter2x", FilterNone );
m_poDisplayConfig->vSetKey("filterIB", FilterIBNone );
#ifdef USE_OPENGL
m_poDisplayConfig->vSetKey("output", OutputOpenGL );
#else
m_poDisplayConfig->vSetKey("output", OutputCairo );
#endif // USE_OPENGL
// Sound section
//
m_poSoundConfig = m_oConfig.poAddSection("Sound");
m_poSoundConfig->vSetKey("mute", false );
m_poSoundConfig->vSetKey("sample_rate", 44100 );
m_poSoundConfig->vSetKey("volume", 1.00f );
// Input section
//
m_poInputConfig = m_oConfig.poAddSection("Input");
m_poInputConfig->vSetKey("active_joypad", m_iJoypadMin );
for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
{
char csPrefix[20];
snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i);
std::string sPrefix(csPrefix);
for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++)
{
m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey,
inputGetKeymap(PAD_DEFAULT, m_astJoypad[j].m_eKeyFlag));
}
}
}
void Window::vCheckConfig()
{
int iValue;
int iAdjusted;
float fValue;
float fAdjusted;
std::string sValue;
// Directories section
//
sValue = m_poDirConfig->sGetKey("gb_roms");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
{
m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir());
}
sValue = m_poDirConfig->sGetKey("gba_roms");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
{
m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir());
}
sValue = m_poDirConfig->sGetKey("batteries");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
{
m_poDirConfig->vSetKey("batteries", m_sUserDataDir);
}
sValue = m_poDirConfig->sGetKey("saves");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
{
m_poDirConfig->vSetKey("saves", m_sUserDataDir);
}
sValue = m_poDirConfig->sGetKey("captures");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
{
m_poDirConfig->vSetKey("captures", m_sUserDataDir);
}
// Core section
//
if (m_poCoreConfig->sGetKey("frameskip") != "auto")
{
iValue = m_poCoreConfig->oGetKey<int>("frameskip");
iAdjusted = CLAMP(iValue, m_iFrameskipMin, m_iFrameskipMax);
if (iValue != iAdjusted)
{
m_poCoreConfig->vSetKey("frameskip", iAdjusted);
}
}
sValue = m_poCoreConfig->sGetKey("bios_file");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR))
{
m_poCoreConfig->vSetKey("bios_file", "");
}
if (m_poCoreConfig->sGetKey("bios_file") == "")
{
m_poCoreConfig->vSetKey("use_bios_file", false);
}
if (m_poCoreConfig->sGetKey("enable_rtc") == "")
{
m_poCoreConfig->vSetKey("enable_rtc", false);
}
sValue = m_poCoreConfig->sGetKey("gb_bios_file");
if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR))
{
m_poCoreConfig->vSetKey("gb_bios_file", "");
}
if (m_poCoreConfig->sGetKey("gb_bios_file") == "")
{
m_poCoreConfig->vSetKey("gb_use_bios_file", false);
}
iValue = m_poCoreConfig->oGetKey<int>("save_type");
if (iValue != 0)
{
iAdjusted = CLAMP(iValue, m_iSaveTypeMin, m_iSaveTypeMax);
if (iValue != iAdjusted)
{
m_poCoreConfig->vSetKey("save_type", iAdjusted);
}
}
iValue = m_poCoreConfig->oGetKey<int>("flash_size");
if (iValue != 64 && iValue != 128)
{
m_poCoreConfig->vSetKey("flash_size", 64);
}
iValue = m_poCoreConfig->oGetKey<int>("emulator_type");
iAdjusted = CLAMP(iValue, m_iEmulatorTypeMin, m_iEmulatorTypeMax);
if (iValue != iAdjusted)
{
m_poCoreConfig->vSetKey("emulator_type", iAdjusted);
}
// Display section
//
iValue = m_poDisplayConfig->oGetKey<int>("scale");
iAdjusted = CLAMP(iValue, m_iScaleMin, m_iScaleMax);
if (iValue != iAdjusted)
{
m_poDisplayConfig->vSetKey("scale", iAdjusted);
}
iValue = m_poDisplayConfig->oGetKey<int>("show_speed");
iAdjusted = CLAMP(iValue, m_iShowSpeedMin, m_iShowSpeedMax);
if (iValue != iAdjusted)
{
m_poDisplayConfig->vSetKey("show_speed", iAdjusted);
}
iValue = m_poDisplayConfig->oGetKey<int>("filter2x");
iAdjusted = CLAMP(iValue, m_iFilter2xMin, m_iFilter2xMax);
if (iValue != iAdjusted)
{
m_poDisplayConfig->vSetKey("filter2x", iAdjusted);
}
iValue = m_poDisplayConfig->oGetKey<int>("filterIB");
iAdjusted = CLAMP(iValue, m_iFilterIBMin, m_iFilterIBMax);
if (iValue != iAdjusted)
{
m_poDisplayConfig->vSetKey("filterIB", iAdjusted);
}
iValue = m_poDisplayConfig->oGetKey<int>("output");
iAdjusted = CLAMP(iValue, m_iVideoOutputMin, m_iVideoOutputMax);
if (iValue != iAdjusted)
{
m_poDisplayConfig->vSetKey("output", iAdjusted);
}
// Sound section
//
iValue = m_poSoundConfig->oGetKey<int>("sample_rate");
iAdjusted = CLAMP(iValue, m_iSoundSampleRateMin, m_iSoundSampleRateMax);
if (iValue != iAdjusted)
{
m_poSoundConfig->vSetKey("sample_rate", iAdjusted);
}
fValue = m_poSoundConfig->oGetKey<float>("volume");
fAdjusted = CLAMP(fValue, m_fSoundVolumeMin, m_fSoundVolumeMax);
if (fValue != fAdjusted)
{
m_poSoundConfig->vSetKey("volume", fAdjusted);
}
// Input section
//
iValue = m_poInputConfig->oGetKey<int>("active_joypad");
iAdjusted = CLAMP(iValue, m_iJoypadMin, m_iJoypadMax);
if (iValue != iAdjusted)
{
m_poInputConfig->vSetKey("active_joypad", iAdjusted);
}
}
void Window::vLoadConfig(const std::string & _rsFile)
{
try
{
m_oConfig.vLoad(_rsFile, false, false);
}
catch (const Glib::Error & e)
{
vPopupError(e.what().c_str());
}
}
void Window::vSaveConfig(const std::string & _rsFile)
{
try
{
m_oConfig.vSave(_rsFile);
}
catch (const Glib::Error & e)
{
vPopupError(e.what().c_str());
}
}
void Window::vApplyConfigFilter()
{
int iFilter = m_poDisplayConfig->oGetKey<int>("filter2x");
m_poScreenArea->vSetFilter((EFilter)iFilter);
if (emulating)
{
vDrawScreen();
}
}
void Window::vApplyConfigFilterIB()
{
int iFilter = m_poDisplayConfig->oGetKey<int>("filterIB");
m_poScreenArea->vSetFilterIB((EFilterIB)iFilter);
if (emulating)
{
vDrawScreen();
}
}
void Window::vApplyConfigMute()
{
bool bMute = m_poSoundConfig->oGetKey<bool>("mute");
if (bMute)
{
soundSetEnable(0x000);
}
else
{
soundSetEnable(0x30f);
}
}
void Window::vApplyConfigVolume()
{
float fSoundVolume = m_poSoundConfig->oGetKey<float>("volume");
soundSetVolume(fSoundVolume);
}
void Window::vApplyConfigSoundSampleRate()
{
long iSoundSampleRate = m_poSoundConfig->oGetKey<int>("sample_rate");
if (m_eCartridge == CartridgeGBA)
{
soundSetSampleRate(iSoundSampleRate);
}
else if (m_eCartridge == CartridgeGB)
{
gbSoundSetSampleRate(iSoundSampleRate);
}
}
void Window::vApplyConfigGBSystem()
{
gbEmulatorType = m_poCoreConfig->oGetKey<int>("emulator_type");
}
void Window::vApplyConfigGBBorder()
{
gbBorderOn = m_poCoreConfig->oGetKey<bool>("gb_border");
if (emulating && m_eCartridge == CartridgeGB && gbBorderOn)
{
gbSgbRenderBorder();
}
vUpdateScreen();
}
void Window::vApplyConfigGBPrinter()
{
bool bPrinter = m_poCoreConfig->oGetKey<bool>("gb_printer");
if (bPrinter)
{
gbSerialFunction = gbPrinterSend;
}
else
{
gbSerialFunction = NULL;
}
}
void Window::vApplyConfigGBASaveType()
{
int iSaveType = m_poCoreConfig->oGetKey<int>("save_type");
cpuSaveType = iSaveType;
}
void Window::vApplyConfigGBAFlashSize()
{
int iFlashSize = m_poCoreConfig->oGetKey<int>("flash_size");
if (iFlashSize == 64)
{
flashSetSize(0x10000);
}
else
{
flashSetSize(0x20000);
}
}
void Window::vApplyConfigGBARTC()
{
bool iRTC = m_poCoreConfig->oGetKey<bool>("enable_rtc");
rtcEnable(iRTC);
}
void Window::vHistoryAdd(const std::string & _rsFile)
{
std::string sURL = "file://" + _rsFile;
m_poRecentManager->add_item(sURL);
}
void Window::vApplyConfigJoypads()
{
for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
{
char csPrefix[20];
snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i);
std::string sPrefix(csPrefix);
for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++)
{
inputSetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag,
m_poInputConfig->oGetKey<guint>(sPrefix + m_astJoypad[j].m_csKey));
}
}
}
void Window::vSaveJoypadsToConfig()
{
for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
{
char csPrefix[20];
snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i);
std::string sPrefix(csPrefix);
for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++)
{
m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey,
inputGetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag));
}
}
}
void Window::vUpdateScreen()
{
if (m_eCartridge == CartridgeGB)
{
if (gbBorderOn)
{
m_iScreenWidth = m_iSGBScreenWidth;
m_iScreenHeight = m_iSGBScreenHeight;
gbBorderLineSkip = m_iSGBScreenWidth;
gbBorderColumnSkip = (m_iSGBScreenWidth - m_iGBScreenWidth) / 2;
gbBorderRowSkip = (m_iSGBScreenHeight - m_iGBScreenHeight) / 2;
}
else
{
m_iScreenWidth = m_iGBScreenWidth;
m_iScreenHeight = m_iGBScreenHeight;
gbBorderLineSkip = m_iGBScreenWidth;
gbBorderColumnSkip = 0;
gbBorderRowSkip = 0;
}
}
else if (m_eCartridge == CartridgeGBA)
{
m_iScreenWidth = m_iGBAScreenWidth;
m_iScreenHeight = m_iGBAScreenHeight;
}
g_return_if_fail(m_iScreenWidth >= 1 && m_iScreenHeight >= 1);
m_poScreenArea->vSetSize(m_iScreenWidth, m_iScreenHeight);
m_poScreenArea->vSetScale(m_poDisplayConfig->oGetKey<int>("scale"));
resize(1, 1);
if (emulating)
{
vDrawScreen();
}
else
{
vDrawDefaultScreen();
}
}
bool Window::bLoadROM(const std::string & _rsFile)
{
vOnFileClose();
m_sRomFile = _rsFile;
const char * csFile = _rsFile.c_str();
IMAGE_TYPE eType = utilFindType(csFile);
if (eType == IMAGE_UNKNOWN)
{
vPopupError(_("Unknown file type %s"), csFile);
return false;
}
bool bLoaded = false;
if (eType == IMAGE_GB)
{
bLoaded = gbLoadRom(csFile);
if (bLoaded)
{
m_eCartridge = CartridgeGB;
m_stEmulator = GBSystem;
useBios = m_poCoreConfig->oGetKey<bool>("gb_use_bios_file");
gbGetHardwareType();
if (gbHardware & 5)
{
gbCPUInit(m_poCoreConfig->sGetKey("gb_bios_file").c_str(), useBios);
}
// If the bios file was rejected by gbCPUInit
if (m_poCoreConfig->oGetKey<bool>("gb_use_bios_file") && ! useBios)
{
m_poCoreConfig->vSetKey("gb_bios_file", "");
}
gbReset();
}
}
else if (eType == IMAGE_GBA)
{
int iSize = CPULoadRom(csFile);
bLoaded = (iSize > 0);
if (bLoaded)
{
m_eCartridge = CartridgeGBA;
m_stEmulator = GBASystem;
useBios = m_poCoreConfig->oGetKey<bool>("use_bios_file");
CPUInit(m_poCoreConfig->sGetKey("bios_file").c_str(), useBios);
CPUReset();
// If the bios file was rejected by CPUInit
if (m_poCoreConfig->oGetKey<bool>("use_bios_file") && ! useBios)
{
m_poUseBiosItem->set_active(false);
m_poUseBiosItem->set_sensitive(false);
m_poCoreConfig->vSetKey("bios_file", "");
}
rtcEnable(m_poCoreConfig->oGetKey<bool>("enable_rtc"));
}
}
if (! bLoaded)
{
return false;
}
vLoadBattery();
vUpdateScreen();
emulating = 1;
m_bWasEmulating = false;
vApplyConfigSoundSampleRate();
vUpdateGameSlots();
vHistoryAdd(_rsFile);
for (std::list<Gtk::Widget *>::iterator it = m_listSensitiveWhenPlaying.begin();
it != m_listSensitiveWhenPlaying.end();
it++)
{
(*it)->set_sensitive();
}
if (m_poCoreConfig->oGetKey<bool>("load_game_auto"))
{
vOnLoadGameMostRecent();
}
vStartEmu();
return true;
}
void Window::vPopupError(const char * _csFormat, ...)
{
va_list args;
va_start(args, _csFormat);
char * csMsg = g_strdup_vprintf(_csFormat, args);
va_end(args);
Gtk::MessageDialog oDialog(*this,
csMsg,
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK);
oDialog.run();
g_free(csMsg);
}
void Window::vPopupErrorV(const char * _csFormat, va_list _args)
{
char * csMsg = g_strdup_vprintf(_csFormat, _args);
Gtk::MessageDialog oDialog(*this,
csMsg,
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK);
oDialog.run();
g_free(csMsg);
}
void Window::vDrawScreen()
{
m_poScreenArea->vDrawPixels(pix);
m_iFrameCount++;
}
void Window::vDrawDefaultScreen()
{
m_poScreenArea->vDrawBlackScreen();
}
void Window::vSetDefaultTitle()
{
set_title("VBA-M");
}
void Window::vShowSpeed(int _iSpeed)
{
char csTitle[50];
if (m_eShowSpeed == ShowPercentage)
{
snprintf(csTitle, 50, "VBA-M - %d%%", _iSpeed);
set_title(csTitle);
}
else if (m_eShowSpeed == ShowDetailed)
{
snprintf(csTitle, 50, "VBA-M - %d%% (%d, %d fps)",
_iSpeed, systemFrameSkip, m_iFrameCount);
set_title(csTitle);
}
m_iFrameCount = 0;
}
void Window::vComputeFrameskip(int _iRate)
{
static Glib::TimeVal uiLastTime;
static int iFrameskipAdjust = 0;
Glib::TimeVal uiTime;
uiTime.assign_current_time();
if (m_bWasEmulating)
{
if (m_bAutoFrameskip)
{
Glib::TimeVal uiDiff = uiTime - uiLastTime;
const int iWantedSpeed = 100;
int iSpeed = iWantedSpeed;
if (uiDiff != Glib::TimeVal(0, 0))
{
iSpeed = (1000000 / _iRate) / (uiDiff.as_double() * 1000);
}
if (iSpeed >= iWantedSpeed - 2)
{
iFrameskipAdjust++;
if (iFrameskipAdjust >= 3)
{
iFrameskipAdjust = 0;
if (systemFrameSkip > 0)
{
systemFrameSkip--;
}
}
}
else
{
if (iSpeed < iWantedSpeed - 20)
{
iFrameskipAdjust -= ((iWantedSpeed - 10) - iSpeed) / 5;
}
else if (systemFrameSkip < 9)
{
iFrameskipAdjust--;
}
if (iFrameskipAdjust <= -4)
{
iFrameskipAdjust = 0;
if (systemFrameSkip < 9)
{
systemFrameSkip++;
}
}
}
}
}
else
{
m_bWasEmulating = true;
}
uiLastTime = uiTime;
}
void Window::vCaptureScreen(int _iNum)
{
std::string sBaseName;
std::string sDir = m_poDirConfig->sGetKey("captures");
if (sDir == "")
{
sDir = m_sUserDataDir;
}
sBaseName = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile));
char * csFile = g_strdup_printf("%s_%02d.png",
sBaseName.c_str(),
_iNum);
m_stEmulator.emuWritePNG(csFile);
g_free(csFile);
}
void Window::vCreateFileOpenDialog()
{
if (m_poFileOpenDialog != NULL)
{
return;
}
std::string sGBDir = m_poDirConfig->sGetKey("gb_roms");
std::string sGBADir = m_poDirConfig->sGetKey("gba_roms");
Gtk::FileChooserDialog * poDialog = new Gtk::FileChooserDialog(*this, _("Open"));
poDialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
poDialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
try
{
if (sGBDir != "")
{
poDialog->add_shortcut_folder(sGBDir);
poDialog->set_current_folder(sGBDir);
}
if (sGBADir != "" && sGBADir != sGBDir)
{
poDialog->add_shortcut_folder(sGBADir);
poDialog->set_current_folder(sGBADir);
}
}
catch (const Gtk::FileChooserError& e)
{
// Most likely the shortcut already exists, so do nothing
}
const char * acsPattern[] =
{
// GBA
"*.[bB][iI][nN]", "*.[aA][gG][bB]", "*.[gG][bB][aA]",
// GB
"*.[gG][bB]", "*.[sS][gG][bB]", "*.[cC][gG][bB]", "*.[gG][bB][cC]",
// Both
"*.[mM][bB]", "*.[eE][lL][fF]", "*.[zZ][iI][pP]", "*.[zZ]", "*.[gG][zZ]"
};
Gtk::FileFilter oAllGBAFilter;
oAllGBAFilter.set_name(_("All Gameboy Advance files"));
for (guint i = 0; i < G_N_ELEMENTS(acsPattern); i++)
{
oAllGBAFilter.add_pattern(acsPattern[i]);
}
Gtk::FileFilter oGBAFilter;
oGBAFilter.set_name(_("Gameboy Advance files"));
for (int i = 0; i < 3; i++)
{
oGBAFilter.add_pattern(acsPattern[i]);
}
Gtk::FileFilter oGBFilter;
oGBFilter.set_name(_("Gameboy files"));
for (int i = 3; i < 7; i++)
{
oGBFilter.add_pattern(acsPattern[i]);
}
poDialog->add_filter(oAllGBAFilter);
poDialog->add_filter(oGBAFilter);
poDialog->add_filter(oGBFilter);
m_poFileOpenDialog = poDialog;
}
void Window::vLoadBattery()
{
std::string sBattery;
std::string sDir = m_poDirConfig->sGetKey("batteries");
if (sDir == "")
{
sDir = m_sUserDataDir;
}
sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav";
if (m_stEmulator.emuReadBattery(sBattery.c_str()))
{
systemScreenMessage(_("Loaded battery"));
}
}
void Window::vSaveBattery()
{
std::string sBattery;
std::string sDir = m_poDirConfig->sGetKey("batteries");
if (sDir == "")
{
sDir = m_sUserDataDir;
}
sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav";
if (m_stEmulator.emuWriteBattery(sBattery.c_str()))
{
systemScreenMessage(_("Saved battery"));
}
}
void Window::vStartEmu()
{
if (m_oEmuSig.connected())
{
return;
}
m_oEmuSig = Glib::signal_idle().connect(sigc::mem_fun(*this, &Window::bOnEmuIdle),
Glib::PRIORITY_HIGH_IDLE + 30);
}
void Window::vStopEmu()
{
m_oEmuSig.disconnect();
m_bWasEmulating = false;
}
void Window::vUpdateGameSlots()
{
if (m_eCartridge == CartridgeNone)
{
std::string sDateTime = _("----/--/-- --:--:--");
for (int i = 0; i < 10; i++)
{
char csPrefix[10];
snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1);
Gtk::Label * poLabel;
poLabel = dynamic_cast<Gtk::Label *>(m_apoLoadGameItem[i]->get_child());
poLabel->set_text(csPrefix + sDateTime);
m_apoLoadGameItem[i]->set_sensitive(false);
poLabel = dynamic_cast<Gtk::Label *>(m_apoSaveGameItem[i]->get_child());
poLabel->set_text(csPrefix + sDateTime);
m_apoSaveGameItem[i]->set_sensitive(false);
m_astGameSlot[i].m_bEmpty = true;
}
}
else
{
std::string sFileBase;
std::string sDir = m_poDirConfig->sGetKey("saves");
if (sDir == "")
{
sDir = m_sUserDataDir;
}
sFileBase = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile));
const char * csDateFormat = _("%Y/%m/%d %H:%M:%S");
for (int i = 0; i < 10; i++)
{
char csPrefix[10];
snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1);
char csSlot[10];
snprintf(csSlot, sizeof(csSlot), "%d", i + 1);
m_astGameSlot[i].m_sFile = sFileBase + csSlot + ".sgm";
std::string sDateTime;
struct stat stStat;
if (stat(m_astGameSlot[i].m_sFile.c_str(), &stStat) == -1)
{
sDateTime = _("----/--/-- --:--:--");
m_astGameSlot[i].m_bEmpty = true;
}
else
{
char csDateTime[30];
strftime(csDateTime, sizeof(csDateTime), csDateFormat,
localtime(&stStat.st_mtime));
sDateTime = csDateTime;
m_astGameSlot[i].m_bEmpty = false;
m_astGameSlot[i].m_uiTime = stStat.st_mtime;
}
Gtk::Label * poLabel;
poLabel = dynamic_cast<Gtk::Label *>(m_apoLoadGameItem[i]->get_child());
poLabel->set_text(csPrefix + sDateTime);
m_apoLoadGameItem[i]->set_sensitive(! m_astGameSlot[i].m_bEmpty);
poLabel = dynamic_cast<Gtk::Label *>(m_apoSaveGameItem[i]->get_child());
poLabel->set_text(csPrefix + sDateTime);
m_apoSaveGameItem[i]->set_sensitive();
}
}
}
void Window::vToggleFullscreen()
{
if(!m_bFullscreen)
{
fullscreen();
m_poMenuBar->hide();
}
else
{
unfullscreen();
m_poMenuBar->show();
}
}
void Window::vSDLPollEvents()
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_JOYHATMOTION:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
case SDL_JOYAXISMOTION:
case SDL_KEYDOWN:
case SDL_KEYUP:
inputProcessSDLEvent(event);
break;
}
}
}
std::string Window::sGetUiFilePath(const std::string &_sFileName)
{
// Use the ui file from the source folder if it exists
// to make gvbam runnable without installation
std::string sUiFile = "src/gtk/ui/" + _sFileName;
if (!Glib::file_test(sUiFile, Glib::FILE_TEST_EXISTS))
{
sUiFile = PKGDATADIR "/ui/" + _sFileName;
}
return sUiFile;
}
} // VBA namespace