all repos — mgba @ ddab7a7e44b16f75855f071fffacb73b701c80ff

mGBA Game Boy Advance Emulator

Merge branch 'master' into port/wii
Jeffrey Pfau jeffrey@endrift.com
Sat, 15 Aug 2015 20:33:05 -0700
commit

ddab7a7e44b16f75855f071fffacb73b701c80ff

parent

74c4cbe33e5bc67ef2b0eb1cb6bd518270735e63

64 files changed, 1389 insertions(+), 142 deletions(-)

jump to
M CHANGESCHANGES

@@ -30,6 +30,8 @@ - Default controller profiles for several common controllers

- Libretro now supports BIOS, rumble and solar sensor - Implement BIOS call Stop, for sleep mode - Automatically load patches, if found + - Improved video synchronization + - Configurable audio output sample rate Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit

@@ -67,6 +69,10 @@ - GBA Cheats: Fix Pro Action Replay and GameShark issues when used together

- Qt: Fix analog buttons not getting unmapped - GBA Video: Prevent tiles < 512 from being used in modes 3 - 5 - Qt: Fix passing command line options + - Qt: Fix crashes on Windows by using using QMetaObject to do cross-thread calls + - GBA Video: Fix timing on first scanline + - GBA: Ensure cycles never go negative + - Util: Fix formatting of floats Misc: - Qt: Handle saving input settings better - Debugger: Free watchpoints in addition to breakpoints

@@ -115,6 +121,9 @@ - Qt: Gamepads can now have both buttons and analog axes mapped to the same key

- Qt: Increase usability of key mapper - Qt: Show checkmark for window sizes - Qt: Set window path to loaded ROM + - GBA Memory: Run multiple DMAs in a tight loop if they all occur before present + - GBA Audio: Process multiple audio events at once, if necessary + - GBA: Process multiple timer events at once, if necessary 0.2.1: (2015-05-13) Bugfixes:
M CMakeLists.txtCMakeLists.txt

@@ -45,13 +45,17 @@ if(NOT CMAKE_BUILD_TYPE)

set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE) endif() +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") + +include(GNUInstallDirs) + if (NOT DEFINED LIBDIR) set(LIBDIR "lib") endif() -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") - -include(GNUInstallDirs) +if (NOT DEFINED MANDIR) + set(MANDIR ${CMAKE_INSTALL_MANDIR}) +endif() # Function definitions include(FindPkgConfig)
M PORTING.mdPORTING.md

@@ -30,13 +30,12 @@ * Hardware acceleration

### PS Vita (port/psp2) * Add menu -* Add audio +* Fix audio * Make it faster * Threaded renderer shim * Hardware acceleration ### Wii (port/wii) * Add menu -* Add audio * Thread support * Clean up video detection
A doc/mgba-qt.6

@@ -0,0 +1,118 @@

+.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name> +.\" +.\" This Source Code Form is subject to the terms of the Mozilla Public +.\" License, v. 2.0. If a copy of the MPL was not distributed with this +.\" file, you can obtain one at https://mozilla.org/MPL/2.0/. +.Dd July 29, 2015 +.Dt MGBA-QT 6 +.Os +.Sh NAME +.Nm mgba-qt +.Nd Game Boy Advance emulator +.Sh SYNOPSIS +.Nm mgba-qt +.Op Fl b Ar biosfile +.Op Fl l Ar loglevel +.Op Fl p Ar patchfile +.Op Fl s Ar n +.Ar file +.Sh DESCRIPTION +.Nm +is a Game Boy Advance emulator. +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar biosfile , Fl -bios Ar biosfile +Specify a BIOS file to use during boot. +If this flag is omitted, +.Nm +will use the BIOS specified in the configuration file, +or a high\(hylevel emulated BIOS if none is specified. +.It Fl l Ar loglevel +Log messages during emulation. +.Ar loglevel +is a bitmask defining which types of messages to log: +.Bl -bullet -compact +.It +1 \(en fatal errors +.It +2 \(en errors +.It +4 \(en warnings +.It +8 \(en informative messages +.It +16 \(en debugging messages +.It +32 \(en stub messages for unimplemented features +.It +256 \(en in\(hygame errors +.It +512 \(en software interrupts +.It +1024 \(en emulator status messages +.It +2048 \(en serial I/O messages +.El +The default is to log warnings, errors, fatal errors, and status messages. +.It Fl p Ar patchfile , Fl -patch Ar patchfile +Specify a patch file in BPS, IPS, or UPS format. +.It Fl s Ar n , Fl -frameskip Ar n +Skip every +.Ar n +frames. +.El +.Sh CONTROLS +The default controls are as follows: +.Bl -hang -width "Frame advance" -compact +.It A +.Cm x +.It B +.Cm z +.It L +.Cm a +.It R +.Cm s +.It Start +.Aq Cm Enter +.It Select +.Aq Cm Backspace +.It Load state +.Cm F1 Ns \(en Ns Cm F9 +.It Save state +.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9 +.It Frame advance +.Ao Cm Ctrl Ac Ns \(hy Ns Cm n +.El +.Sh FILES +.Bl -tag -width ~/.config/mgba/config.ini -compact +.It Pa ~/.config/mgba/config.ini +Default +.Xr mgba 6 +configuration file. +.It Pa ~/.config/mgba/qt.ini +Default +.Nm mgba-qt +configuration file. +.It Pa portable.ini +If this file exists in the current directory, +.Nm +will read +.Pa config.ini +and +.Pa qt.ini +from the current directory instead of +.Pa ~/.config/mgba . +.El +.Sh AUTHORS +.An Jeffrey Pfau Aq Mt jeffrey@endrift.com +.Sh HOMEPAGE +.Bl -bullet +.It +.Lk https://mgba.io/ "mGBA homepage" +.It +.Lk https://github.com/mgba-emu/mgba "Development repository" +.It +.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker" +.It +.Lk https://forums.mgba.io/ "Message board" +.El
A doc/mgba.6

@@ -0,0 +1,259 @@

+.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name> +.\" +.\" This Source Code Form is subject to the terms of the Mozilla Public +.\" License, v. 2.0. If a copy of the MPL was not distributed with this +.\" file, you can obtain one at https://mozilla.org/MPL/2.0/. +.Dd July 29, 2015 +.Dt MGBA 6 +.Os +.Sh NAME +.Nm mgba +.Nd Game Boy Advance emulator +.Sh SYNOPSIS +.Nm mgba +.Op Fl 123456dfg +.Op Fl b Ar biosfile +.Op Fl c Ar cheatfile +.Op Fl l Ar loglevel +.Op Fl p Ar patchfile +.Op Fl s Ar n +.Op Fl v Ar moviefile +.Ar file +.Sh DESCRIPTION +.Nm +is a Game Boy Advance emulator. +The options are as follows: +.Bl -tag -width Ds +.It Fl 1 +Scale the window 1\(mu. +.It Fl 2 +Scale the window 2\(mu. +.It Fl 3 +Scale the window 3\(mu. +.It Fl 4 +Scale the window 4\(mu. +.It Fl 5 +Scale the window 5\(mu. +.It Fl 6 +Scale the window 6\(mu. +.It Fl b Ar biosfile , Fl -bios Ar biosfile +Specify a BIOS file to use during boot. +If this flag is omitted, +.Nm +will use the BIOS specified in the configuration file, +or a high\(hylevel emulated BIOS if none is specified. +.It Fl c Ar cheatfile , Fl -cheats Ar cheatfile +Apply cheat codes from +.Ar cheatfile . +.It Fl d +Start emulating via the command\(hyline debugger. +.It Fl f +Start the emulator full\(hyscreen. +.It Fl g +Start a +.Xr gdb 1 +session. +By default the session starts on port 2345. +.It Fl l Ar loglevel +Log messages during emulation to +.Dv stdout . +.Ar loglevel +is a bitmask defining which types of messages to log: +.Bl -bullet -compact +.It +1 \(en fatal errors +.It +2 \(en errors +.It +4 \(en warnings +.It +8 \(en informative messages +.It +16 \(en debugging messages +.It +32 \(en stub messages for unimplemented features +.It +256 \(en in\(hygame errors +.It +512 \(en software interrupts +.It +1024 \(en emulator status messages +.It +2048 \(en serial I/O messages +.El +The default is to log warnings, errors, fatal errors, and status messages. +.It Fl p Ar patchfile , Fl -patch Ar patchfile +Specify a patch file in BPS, IPS, or UPS format. +.It Fl s Ar n , Fl -frameskip Ar n +Skip every +.Ar n +frames. +.It Fl v Ar moviefile , Fl -movie Ar moviefile +Play back a movie of recording input from +.Ar moviefile . +.El +.Sh CONTROLS +The default controls are as follows: +.Bl -hang -width "Frame advance" -compact +.It A +.Cm x +.It B +.Cm z +.It L +.Cm a +.It R +.Cm s +.It Start +.Aq Cm Enter +.It Select +.Aq Cm Backspace +.It Load state +.Cm F1 Ns \(en Ns Cm F9 +.It Save state +.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9 +.It Frame advance +.Ao Cm Ctrl Ac Ns \(hy Ns Cm n +.El +.Sh DEBUGGER +When +.Nm +is run with the +.Fl d +option, the command\(hyline debugger is enabled. +It supports the following commands: +.Pp +.Bl -tag -compact -width 1 +.It Cm b Ns Oo Cm reak Oc Ar address +.It Cm b Ns Oo Cm reak Oc Ns Cm /a Ar address +.It Cm b Ns Oo Cm reak Oc Ns Cm /t Ar address +Set a breakpoint \(en ARM +.Pq Ql /a , +Thumb +.Pq Ql /t , +or the current CPU mode \(en at +.Ar address . +.It Cm c Ns Op Cm ontinue +Continue execution. +.It Cm d Ns Oo Cm elete Oc Ar address +Delete a breakpoint at +.Ar address . +.It Cm dis Ns Oo Cm asm Oc Op Ar address Op Ar count +.It Cm dis Ns Oo Cm asm Oc Ns Cm /a Op Ar address Op Ar count +.It Cm dis Ns Oo Cm asm Oc Ns Cm /t Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Ns Cm /a Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Ns Cm /t Op Ar address Op Ar count +Disassemble +.Ar count +instructions starting at +.Ar address , +as ARM +.Pq Ql /a , +Thumb +.Pq Ql /t , +or the current CPU mode. +If +.Ar count +is not specified, only disassemble the instruction at +.Ar address . +If +.Ar address +is not specified, only disassemble the current address. +.It Cm h Ns Op Cm elp +Print help. +.It Cm i Ns Op Cm nfo +.It Cm status +Print the current contents of general\(hypurpose registers and the current +program state register, and disassemble the current instruction. +.It Cm n Ns Op Cm ext +Execute the next instruction. +.It Cm p Ns Oo Cm rint Oc Ar value ... +.It Cm p Ns Oo Cm rint Oc Ns Cm /t Ar value ... +.It Cm p Ns Oo Cm rint Oc Ns Cm /x Ar value ... +Print one or more +.Ar value Ns s +as binary +.Pq Ql /t , +hexadecimal +.Pq Ql /x , +or decimal. +.It Cm q Ns Op Cm uit +Quit the emulator. +.It Cm reset +Reset the emulation. +.It Cm r/1 Ar address +.It Cm r/2 Ar address +.It Cm r/4 Ar address +Read a byte +.Pq Ql /1 , +halfword +.Pq Ql /2 , +or word +.Pq Ql /4 +from +.Ar address . +.It Cm w Ns Oo Cm atch Oc Ar address +Set a watchpoint at +.Ar address . +.It Cm w/1 Ar address data +.It Cm w/2 Ar address data +.It Cm w/4 Ar address data +Write +.Ar data +as a byte +.Pq Ql /1 , +halfword +.Pq Ql /2 , +or word +.Pq Ql /4 +to +.Ar address . +.It Cm w/r Ar register data +Write +.Ar data +as a word to +.Ar register . +.It Cm x/1 Ar address Op Ar count +.It Cm x/2 Ar address Op Ar count +.It Cm x/4 Ar address Op Ar count +Examine +.Ar count +bytes +.Pq Ql /1 , +halfwords +.Pq Ql /2 , +or words +.Pq Ql /4 +from +.Ar address . +If +.Ar count +is not specified, examine 16 bytes, 8 halfwords, or 4 words. +.El +.Sh FILES +.Bl -tag -width ~/.config/mgba/config.ini -compact +.It Pa ~/.config/mgba/config.ini +Default +.Nm +configuration file. +.It Pa portable.ini +If this file exists in the current directory, +.Nm +will read +.Pa config.ini +from the current directory instead of +.Pa ~/.config/mgba . +.El +.Sh AUTHORS +.An Jeffrey Pfau Aq Mt jeffrey@endrift.com +.Sh HOMEPAGE +.Bl -bullet +.It +.Lk https://mgba.io/ "mGBA homepage" +.It +.Lk https://github.com/mgba-emu/mgba "Development repository" +.It +.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker" +.It +.Lk https://forums.mgba.io/ "Message board" +.El
M src/arm/decoder-thumb.csrc/arm/decoder-thumb.c

@@ -31,7 +31,7 @@ #define DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, MNEMONIC, CYCLES, WIDTH) \

DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ info->op1.reg = opcode & 0x0007; \ info->memory.baseReg = (opcode >> 3) & 0x0007; \ - info->memory.offset.immediate = ((opcode >> 6) & 0x0007) * WIDTH; \ + info->memory.offset.immediate = ((opcode >> 6) & 0x001F) * WIDTH; \ info->memory.width = (enum ARMMemoryAccessType) WIDTH; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ ARM_OPERAND_AFFECTED_1 | \
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -18,7 +18,9 @@ static const char* ERROR_OVERFLOW = "Arguments overflow";

static struct CLIDebugger* _activeDebugger; +#ifndef NDEBUG static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*); +#endif static void _continue(struct CLIDebugger*, struct CLIDebugVector*); static void _disassemble(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*);

@@ -99,7 +101,9 @@ { "w/r", _writeRegister, CLIDVParse, "Write a register" },

{ "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" }, { "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" }, { "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" }, +#ifndef NDEBUG { "!", _breakInto, 0, "Break into attached debugger (for developers)" }, +#endif { 0, 0, 0, 0 } };

@@ -114,6 +118,7 @@ psr.f ? 'F' : '-',

psr.t ? 'T' : '-'); } +#ifndef NDEBUG static void _handleDeath(int sig) { UNUSED(sig); printf("No debugger attached!\n");

@@ -135,6 +140,7 @@ kill(getpid(), SIGTRAP);

#endif sigaction(SIGTRAP, &osa, 0); } +#endif static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(dv);
M src/gba/audio.csrc/gba/audio.c

@@ -167,7 +167,7 @@

int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) { audio->nextEvent -= cycles; audio->eventDiff += cycles; - if (audio->nextEvent <= 0) { + while (audio->nextEvent <= 0) { audio->nextEvent = INT_MAX; if (audio->enable) { if (audio->playingCh1 && !audio->ch1.envelope.dead) {
M src/gba/gba.csrc/gba/gba.c

@@ -184,6 +184,11 @@ struct GBA* gba = (struct GBA*) cpu->master;

int32_t cycles = cpu->nextEvent; int32_t nextEvent = INT_MAX; int32_t testEvent; +#ifndef NDEBUG + if (cycles < 0) { + GBALog(gba, GBA_LOG_FATAL, "Negative cycles passed: %i", cycles); + } +#endif gba->bus = cpu->prefetch[1]; if (cpu->executionMode == MODE_THUMB) {

@@ -239,7 +244,7 @@ timer = &gba->timers[0];

if (timer->enable) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; - if (timer->nextEvent <= 0) { + while (timer->nextEvent <= 0) { timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
M src/gba/hardware.csrc/gba/hardware.c

@@ -612,6 +612,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) {

hw->readWrite = state->hw.readWrite; hw->pinState = state->hw.pinState; hw->direction = state->hw.pinDirection; + hw->devices = state->hw.devices; hw->rtc = state->hw.rtc; hw->gyroSample = state->hw.gyroSample; hw->gyroEdge = state->hw.gyroEdge;

@@ -624,4 +625,7 @@ hw->lightEdge = state->hw.lightEdge;

hw->gbpInputsPosted = state->hw.gbpInputsPosted; hw->gbpTxPosition = state->hw.gbpTxPosition; hw->gbpNextEvent = state->hw.gbpNextEvent; + if (hw->devices & HW_GB_PLAYER) { + GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32); + } }
M src/gba/memory.csrc/gba/memory.c

@@ -446,8 +446,7 @@ }

} else { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address); LOAD_BAD; - uint32_t v2 = value; - LOAD_16(value, address & 2, &v2); + value = (value >> ((address & 2) * 8)) & 0xFFFF; } break; case REGION_WORKING_RAM:

@@ -506,8 +505,7 @@ break;

default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address); LOAD_BAD; - uint32_t v2 = value; - LOAD_16(value, address & 2, &v2); + value = (value >> ((address & 2) * 8)) & 0xFFFF; break; }

@@ -1405,7 +1403,7 @@ return INT_MAX;

} memory->nextDMA -= cycles; memory->eventDiff += cycles; - if (memory->nextDMA <= 0) { + while (memory->nextDMA <= 0) { struct GBADMA* dma = &memory->dma[memory->activeDMA]; GBAMemoryServiceDMA(gba, memory->activeDMA, dma); GBAMemoryUpdateDMAs(gba, memory->eventDiff);
M src/gba/serialize.csrc/gba/serialize.c

@@ -87,6 +87,10 @@ if (state->cpu.cycles < 0) {

GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are negative"); error = true; } + if (state->cpu.nextEvent < 0) { + GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: Next event is negative"); + error = true; + } if (state->video.eventDiff < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: video eventDiff is negative"); error = true;

@@ -97,6 +101,10 @@ error = true;

} if (state->timers[0].overflowInterval < 0 || state->timers[1].overflowInterval < 0 || state->timers[2].overflowInterval < 0 || state->timers[3].overflowInterval < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: overflowInterval is negative"); + error = true; + } + if (state->timers[0].nextEvent < 0 || state->timers[1].nextEvent < 0 || state->timers[2].nextEvent < 0 || state->timers[3].nextEvent < 0) { + GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: timer nextEvent is negative"); error = true; } if (state->audio.eventDiff < 0) {
M src/gba/supervisor/config.csrc/gba/supervisor/config.c

@@ -247,6 +247,7 @@ unsigned audioBuffers;

if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) { opts->audioBuffers = audioBuffers; } + _lookupUIntValue(config, "sampleRate", &opts->sampleRate); int fakeBool; if (_lookupIntValue(config, "useBios", &fakeBool)) {

@@ -305,6 +306,7 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);

ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); + ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync); ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync); ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
M src/gba/supervisor/config.hsrc/gba/supervisor/config.h

@@ -29,6 +29,7 @@ int rewindBufferCapacity;

int rewindBufferInterval; float fpsTarget; size_t audioBuffers; + unsigned sampleRate; int fullscreen; int width;
M src/gba/video.csrc/gba/video.c

@@ -67,7 +67,7 @@

video->lastHblank = 0; video->nextHblank = VIDEO_HDRAW_LENGTH; video->nextEvent = video->nextHblank; - video->eventDiff = video->nextEvent; + video->eventDiff = 0; video->nextHblankIRQ = 0; video->nextVblankIRQ = 0;
M src/platform/qt/AboutScreen.uisrc/platform/qt/AboutScreen.ui

@@ -145,7 +145,7 @@ </item>

<item row="4" column="1"> <widget class="QLabel" name="extraLinks"> <property name="text"> - <string>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forumsmgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE&quot;&gt;License&lt;/a&gt;</string> + <string>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE&quot;&gt;License&lt;/a&gt;</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set>
M src/platform/qt/AudioProcessor.hsrc/platform/qt/AudioProcessor.h

@@ -31,6 +31,7 @@ AudioProcessor(QObject* parent = nullptr);

virtual void setInput(GBAThread* input); int getBufferSamples() const { return m_samples; } + virtual unsigned sampleRate() const = 0; public slots: virtual void start() = 0;

@@ -39,7 +40,7 @@

virtual void setBufferSamples(int samples) = 0; virtual void inputParametersChanged() = 0; - virtual unsigned sampleRate() const = 0; + virtual void requestSampleRate(unsigned) = 0; protected: GBAThread* input() { return m_context; }
M src/platform/qt/AudioProcessorQt.cppsrc/platform/qt/AudioProcessorQt.cpp

@@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)

: AudioProcessor(parent) , m_audioOutput(nullptr) , m_device(nullptr) + , m_sampleRate(44100) { }

@@ -45,7 +46,7 @@ }

if (!m_audioOutput) { QAudioFormat format; - format.setSampleRate(44100); + format.setSampleRate(m_sampleRate); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm");

@@ -81,6 +82,15 @@

void AudioProcessorQt::inputParametersChanged() { if (m_device) { m_device->setFormat(m_audioOutput->format()); + } +} + +void AudioProcessorQt::requestSampleRate(unsigned rate) { + m_sampleRate = rate; + if (m_device) { + QAudioFormat format(m_audioOutput->format()); + format.setSampleRate(rate); + m_device->setFormat(format); } }
M src/platform/qt/AudioProcessorQt.hsrc/platform/qt/AudioProcessorQt.h

@@ -20,6 +20,7 @@ public:

AudioProcessorQt(QObject* parent = nullptr); virtual void setInput(GBAThread* input); + virtual unsigned sampleRate() const override; public slots: virtual void start();

@@ -28,11 +29,12 @@

virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); - virtual unsigned sampleRate() const override; + virtual void requestSampleRate(unsigned) override; private: QAudioOutput* m_audioOutput; AudioDevice* m_device; + unsigned m_sampleRate; }; }
M src/platform/qt/AudioProcessorSDL.cppsrc/platform/qt/AudioProcessorSDL.cpp

@@ -15,7 +15,7 @@ using namespace QGBA;

AudioProcessorSDL::AudioProcessorSDL(QObject* parent) : AudioProcessor(parent) - , m_audio() + , m_audio{ 2048, 44100 } { }

@@ -55,6 +55,18 @@

void AudioProcessorSDL::inputParametersChanged() { } +void AudioProcessorSDL::requestSampleRate(unsigned rate) { + m_audio.sampleRate = rate; + if (m_audio.thread) { + GBASDLDeinitAudio(&m_audio); + GBASDLInitAudio(&m_audio, input()); + } +} + unsigned AudioProcessorSDL::sampleRate() const { - return m_audio.obtainedSpec.freq; + if (m_audio.thread) { + return m_audio.obtainedSpec.freq; + } else { + return 0; + } }
M src/platform/qt/AudioProcessorSDL.hsrc/platform/qt/AudioProcessorSDL.h

@@ -22,6 +22,8 @@ public:

AudioProcessorSDL(QObject* parent = nullptr); ~AudioProcessorSDL(); + virtual unsigned sampleRate() const override; + public slots: virtual void start(); virtual void pause();

@@ -29,7 +31,7 @@

virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); - virtual unsigned sampleRate() const override; + virtual void requestSampleRate(unsigned) override; private: GBASDLAudio m_audio;
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -170,6 +170,9 @@ if(DESKTOP_FILE_INSTALL)

install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/applications/\")") endif() endif() +if(UNIX) + install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt) +endif() if(APPLE OR WIN32) set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) endif()
M src/platform/qt/CheatsView.cppsrc/platform/qt/CheatsView.cpp

@@ -99,19 +99,29 @@ m_controller->threadContinue();

} void CheatsView::enterCheat(std::function<bool(GBACheatSet*, const char*)> callback) { - GBACheatSet* set; + GBACheatSet* set = nullptr; QModelIndexList selection = m_ui.cheatList->selectionModel()->selectedIndexes(); - if (selection.count() != 1) { - return; + QModelIndex index; + if (selection.count() == 0) { + set = new GBACheatSet; + GBACheatSetInit(set, nullptr); + } else if (selection.count() == 1) { + index = selection[0]; + set = m_model.itemAt(index); } - set = m_model.itemAt(selection[0]); + if (!set) { return; } m_controller->threadInterrupt(); + if (selection.count() == 0) { + m_model.addSet(set); + index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex()); + m_ui.cheatList->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts); for (const QString& string : cheats) { - m_model.beginAppendRow(selection[0]); + m_model.beginAppendRow(index); callback(set, string.toUtf8().constData()); m_model.endAppendRow(); }
M src/platform/qt/ConfigController.cppsrc/platform/qt/ConfigController.cpp

@@ -107,7 +107,8 @@

m_opts.audioSync = GameController::AUDIO_SYNC; m_opts.videoSync = GameController::VIDEO_SYNC; m_opts.fpsTarget = 60; - m_opts.audioBuffers = 2048; + m_opts.audioBuffers = 1536; + m_opts.sampleRate = 44100; m_opts.volume = GBA_AUDIO_VOLUME_MAX; m_opts.logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS; m_opts.rewindEnable = false;
M src/platform/qt/Display.cppsrc/platform/qt/Display.cpp

@@ -22,7 +22,7 @@ #endif

Display* Display::create(QWidget* parent) { #ifdef BUILD_GL - QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer)); + QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer)); format.setSwapInterval(1); #endif
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -118,7 +118,8 @@ }

void DisplayGL::framePosted(const uint32_t* buffer) { if (m_drawThread && buffer) { - QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer)); + m_painter->enqueue(buffer); + QMetaObject::invokeMethod(m_painter, "draw"); } }

@@ -152,6 +153,19 @@ };

m_backend.d.user = this; m_backend.d.filter = false; m_backend.d.lockAspectRatio = false; + + for (int i = 0; i < 2; ++i) { + m_free.append(new uint32_t[256 * 256]); + } +} + +PainterGL::~PainterGL() { + while (!m_queue.isEmpty()) { + delete[] m_queue.dequeue(); + } + for (auto item : m_free) { + delete[] item; + } } void PainterGL::setContext(GBAThread* context) {

@@ -162,15 +176,6 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {

m_messagePainter = messagePainter; } -void PainterGL::setBacking(const uint32_t* backing) { - m_gl->makeCurrent(); - m_backend.d.postFrame(&m_backend.d, backing); - if (m_active) { - draw(); - } - m_gl->doneCurrent(); -} - void PainterGL::resize(const QSize& size) { m_size = size; if (m_active) {

@@ -200,7 +205,11 @@ m_active = true;

} void PainterGL::draw() { - if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) { + if (m_queue.isEmpty()) { + return; + } + if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip) || !m_queue.isEmpty()) { + dequeue(); m_painter.begin(m_gl->context()->device()); performDraw(); m_painter.end();

@@ -208,6 +217,9 @@ GBASyncWaitFrameEnd(&m_context->sync);

m_backend.d.swap(&m_backend.d); } else { GBASyncWaitFrameEnd(&m_context->sync); + } + if (!m_queue.isEmpty()) { + QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); } }

@@ -221,6 +233,7 @@

void PainterGL::stop() { m_active = false; m_gl->makeCurrent(); + dequeueAll(); m_backend.d.clear(&m_backend.d); m_backend.d.swap(&m_backend.d); m_backend.d.deinit(&m_backend.d);

@@ -231,9 +244,6 @@ }

void PainterGL::pause() { m_active = false; - // Make sure both buffers are filled - forceDraw(); - forceDraw(); } void PainterGL::unpause() {

@@ -250,3 +260,41 @@ if (m_messagePainter) {

m_messagePainter->paint(&m_painter); } } + +void PainterGL::enqueue(const uint32_t* backing) { + m_mutex.lock(); + uint32_t* buffer; + if (m_free.isEmpty()) { + buffer = m_queue.dequeue(); + } else { + buffer = m_free.takeLast(); + } + memcpy(buffer, backing, 256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); + m_queue.enqueue(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeue() { + m_mutex.lock(); + if (m_queue.isEmpty()) { + m_mutex.unlock(); + return; + } + uint32_t* buffer = m_queue.dequeue(); + m_backend.d.postFrame(&m_backend.d, buffer); + m_free.append(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeueAll() { + uint32_t* buffer = 0; + m_mutex.lock(); + while (!m_queue.isEmpty()) { + buffer = m_queue.dequeue(); + m_free.append(buffer); + } + if (buffer) { + m_backend.d.postFrame(&m_backend.d, buffer); + } + m_mutex.unlock(); +}
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -9,7 +9,9 @@

#include "Display.h" #include <QGLWidget> +#include <QList> #include <QMouseEvent> +#include <QQueue> #include <QThread> #include <QTimer>

@@ -74,12 +76,13 @@ Q_OBJECT

public: PainterGL(QGLWidget* parent); + ~PainterGL(); void setContext(GBAThread*); void setMessagePainter(MessagePainter*); + void enqueue(const uint32_t* backing); public slots: - void setBacking(const uint32_t*); void forceDraw(); void draw(); void start();

@@ -92,8 +95,13 @@ void filter(bool filter);

private: void performDraw(); + void dequeue(); + void dequeueAll(); + QList<uint32_t*> m_free; + QQueue<uint32_t*> m_queue; QPainter m_painter; + QMutex m_mutex; QGLWidget* m_gl; bool m_active; GBAThread* m_context;
M src/platform/qt/GBAApp.cppsrc/platform/qt/GBAApp.cpp

@@ -15,6 +15,7 @@ #include <QFileOpenEvent>

#include <QIcon> extern "C" { +#include "gba/supervisor/thread.h" #include "platform/commandline.h" #include "util/socket.h" }

@@ -33,10 +34,13 @@ #ifdef BUILD_SDL

SDL_Init(SDL_INIT_NOPARACHUTE); #endif +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/res/mgba-1024.png")); +#endif SocketSubsystemInit(); qRegisterMetaType<const uint32_t*>("const uint32_t*"); + qRegisterMetaType<GBAThread*>("GBAThread*"); QApplication::setApplicationName(projectName); QApplication::setApplicationVersion(projectVersion);

@@ -53,6 +57,7 @@ ::exit(0);

return; } + AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); Window* w = new Window(&m_configController); connect(w, &Window::destroyed, [this]() { m_windows[0] = nullptr;

@@ -66,9 +71,6 @@ w->loadConfig();

} freeArguments(&args); w->show(); - - AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); - w->controller()->reloadAudioDriver(); w->controller()->setMultiplayerController(&m_multiplayer); }
M src/platform/qt/GDBWindow.cppsrc/platform/qt/GDBWindow.cpp

@@ -18,7 +18,7 @@

using namespace QGBA; GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_gdbController(controller) { setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
M src/platform/qt/GDBWindow.hsrc/platform/qt/GDBWindow.h

@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef QGBA_GDB_WINDOW #define QGBA_GDB_WINDOW -#include <QWidget> +#include <QDialog> class QLineEdit; class QPushButton;

@@ -15,7 +15,7 @@ namespace QGBA {

class GDBController; -class GDBWindow : public QWidget { +class GDBWindow : public QDialog { Q_OBJECT public:
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -31,7 +31,8 @@ using namespace std;

GameController::GameController(QObject* parent) : QObject(parent) - , m_drawContext(new uint32_t[256 * 256]) + , m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS]) + , m_frontBuffer(new uint32_t[256 * 256]) , m_threadContext() , m_activeKeys(0) , m_inactiveKeys(0)

@@ -86,7 +87,9 @@ setLuminanceLevel(0);

m_threadContext.startCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - controller->m_audioProcessor->setInput(context); + if (controller->m_audioProcessor) { + controller->m_audioProcessor->setInput(context); + } context->gba->luminanceSource = &controller->m_lux; GBARTCGenericSourceInit(&controller->m_rtc, context->gba); context->gba->rtcSource = &controller->m_rtc.d;

@@ -111,24 +114,25 @@ if (vf) {

vf->truncate(vf, 0); } } - controller->gameStarted(context); + QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context)); }; m_threadContext.cleanCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - controller->gameStopped(context); + QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(GBAThread*, context)); }; m_threadContext.frameCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); if (GBASyncDrawingFrame(&controller->m_threadContext.sync)) { - controller->frameAvailable(controller->m_drawContext); + memcpy(controller->m_frontBuffer, controller->m_drawContext, 256 * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL); + QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer)); } else { - controller->frameAvailable(nullptr); + QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, nullptr)); } if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) { GBAThreadPauseFromThread(context); - controller->gamePaused(&controller->m_threadContext); + QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context)); } };

@@ -157,7 +161,7 @@ va_list argc;

va_copy(argc, args); int immediate = va_arg(argc, int); va_end(argc); - controller->unimplementedBiosCall(immediate); + QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate)); } else if (level == GBA_LOG_STATUS) { // Slot 0 is reserved for suspend points if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {

@@ -185,9 +189,9 @@ return;

} QString message(QString().vsprintf(format, args)); if (level == GBA_LOG_STATUS) { - controller->statusPosted(message); + QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message)); } - controller->postLog(level, message); + QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(const QString&, message)); }; connect(&m_rewindTimer, &QTimer::timeout, [this]() {

@@ -216,6 +220,7 @@ closeGame();

GBACheatDeviceDestroy(&m_cheatDevice); delete m_renderer; delete[] m_drawContext; + delete[] m_frontBuffer; delete m_backupLoadState; }

@@ -339,7 +344,7 @@ m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);

} m_inputController->recalibrateAxes(); - memset(m_drawContext, 0xF8, 1024 * 256); + memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS); if (!GBAThreadStart(&m_threadContext)) { m_gameOpen = false;

@@ -530,9 +535,9 @@ if (!m_gameOpen || m_rewindTimer.isActive()) {

return; } m_wasPaused = isPaused(); - bool signalsBlocked = blockSignals(true); - setPaused(true); - blockSignals(signalsBlocked); + if (!GBAThreadIsPaused(&m_threadContext)) { + GBAThreadPause(&m_threadContext); + } m_rewindTimer.start(); }

@@ -585,10 +590,24 @@ updateKeys();

} void GameController::setAudioBufferSamples(int samples) { - threadInterrupt(); - redoSamples(samples); - threadContinue(); - QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples)); + if (m_audioProcessor) { + threadInterrupt(); + redoSamples(samples); + threadContinue(); + QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples)); + } +} + +void GameController::setAudioSampleRate(unsigned rate) { + if (!rate) { + return; + } + if (m_audioProcessor) { + threadInterrupt(); + redoSamples(m_audioProcessor->getBufferSamples()); + threadContinue(); + QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate)); + } } void GameController::setAudioChannelEnabled(int channel, bool enable) {

@@ -641,7 +660,9 @@ m_threadContext.fpsTarget = fps;

if (m_turbo && m_turboSpeed > 0) { m_threadContext.fpsTarget *= m_turboSpeed; } - redoSamples(m_audioProcessor->getBufferSamples()); + if (m_audioProcessor) { + redoSamples(m_audioProcessor->getBufferSamples()); + } threadContinue(); }

@@ -652,9 +673,14 @@ threadContinue();

} void GameController::setUseBIOS(bool use) { - threadInterrupt(); + if (use == m_useBios) { + return; + } m_useBios = use; - threadContinue(); + if (m_gameOpen) { + closeGame(); + openGame(); + } } void GameController::loadState(int slot) {

@@ -798,7 +824,9 @@ m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;

m_threadContext.sync.audioWait = true; m_threadContext.sync.videoFrameWait = false; } - redoSamples(m_audioProcessor->getBufferSamples()); + if (m_audioProcessor) { + redoSamples(m_audioProcessor->getBufferSamples()); + } threadContinue(); }

@@ -827,11 +855,21 @@ }

#endif void GameController::reloadAudioDriver() { - QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection); - int samples = m_audioProcessor->getBufferSamples(); - delete m_audioProcessor; + int samples = 0; + unsigned sampleRate = 0; + if (m_audioProcessor) { + QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection); + samples = m_audioProcessor->getBufferSamples(); + sampleRate = m_audioProcessor->sampleRate(); + delete m_audioProcessor; + } m_audioProcessor = AudioProcessor::create(); - m_audioProcessor->setBufferSamples(samples); + if (samples) { + m_audioProcessor->setBufferSamples(samples); + } + if (sampleRate) { + m_audioProcessor->requestSampleRate(sampleRate); + } m_audioProcessor->moveToThread(m_audioThread); connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start())); connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -119,6 +119,7 @@ void keyPressed(int key);

void keyReleased(int key); void clearKeys(); void setAudioBufferSamples(int samples); + void setAudioSampleRate(unsigned rate); void setAudioChannelEnabled(int channel, bool enable = true); void setVideoLayerEnabled(int layer, bool enable = true); void setFPSTarget(float fps);

@@ -167,6 +168,7 @@ void redoSamples(int samples);

void enableTurbo(); uint32_t* m_drawContext; + uint32_t* m_frontBuffer; GBAThread m_threadContext; GBAVideoSoftwareRenderer* m_renderer; GBACheatDevice m_cheatDevice;
M src/platform/qt/LoadSaveState.cppsrc/platform/qt/LoadSaveState.cpp

@@ -26,6 +26,7 @@ , m_controller(controller)

, m_currentFocus(controller->stateSlot() - 1) , m_mode(LoadSave::LOAD) { + setAttribute(Qt::WA_TranslucentBackground); m_ui.setupUi(this); m_slots[0] = m_ui.state1;

@@ -208,6 +209,5 @@

void LoadSaveState::paintEvent(QPaintEvent*) { QPainter painter(this); QRect full(QPoint(), size()); - painter.drawPixmap(full, m_currentImage); painter.fillRect(full, QColor(0, 0, 0, 128)); }
M src/platform/qt/OverrideView.cppsrc/platform/qt/OverrideView.cpp

@@ -15,7 +15,7 @@

using namespace QGBA; OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_controller(controller) , m_config(config) {
M src/platform/qt/OverrideView.hsrc/platform/qt/OverrideView.h

@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef QGBA_OVERRIDE_VIEW #define QGBA_OVERRIDE_VIEW -#include <QWidget> +#include <QDialog> #include "ui_OverrideView.h"

@@ -21,7 +21,7 @@

class ConfigController; class GameController; -class OverrideView : public QWidget { +class OverrideView : public QDialog { Q_OBJECT public:
M src/platform/qt/SavestateButton.cppsrc/platform/qt/SavestateButton.cpp

@@ -13,7 +13,7 @@

SavestateButton::SavestateButton(QWidget* parent) : QAbstractButton(parent) { - // Nothing to do + setAttribute(Qt::WA_TranslucentBackground); } void SavestateButton::paintEvent(QPaintEvent*) {
M src/platform/qt/SensorView.cppsrc/platform/qt/SensorView.cpp

@@ -12,7 +12,7 @@

using namespace QGBA; SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_controller(controller) , m_input(input) , m_rotation(input->rotationSource())
M src/platform/qt/SensorView.hsrc/platform/qt/SensorView.h

@@ -7,7 +7,7 @@ #ifndef QGBA_SENSOR_VIEW

#define QGBA_SENSOR_VIEW #include <QTimer> -#include <QWidget> +#include <QDialog> #include <functional>

@@ -22,7 +22,7 @@ class GameController;

class GamepadAxisEvent; class InputController; -class SensorView : public QWidget { +class SensorView : public QDialog { Q_OBJECT public:
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -13,8 +13,8 @@

using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, QWidget* parent) - : QWidget(parent) - , m_controller(controller) + : QDialog(parent) + , m_controller(controller) { m_ui.setupUi(this);

@@ -22,6 +22,7 @@ loadSetting("bios", m_ui.bios);

loadSetting("useBios", m_ui.useBios); loadSetting("skipBios", m_ui.skipBios); loadSetting("audioBuffers", m_ui.audioBufferSize); + loadSetting("sampleRate", m_ui.sampleRate); loadSetting("videoSync", m_ui.videoSync); loadSetting("audioSync", m_ui.audioSync); loadSetting("frameskip", m_ui.frameskip);

@@ -102,6 +103,7 @@ saveSetting("bios", m_ui.bios);

saveSetting("useBios", m_ui.useBios); saveSetting("skipBios", m_ui.skipBios); saveSetting("audioBuffers", m_ui.audioBufferSize); + saveSetting("sampleRate", m_ui.sampleRate); saveSetting("videoSync", m_ui.videoSync); saveSetting("audioSync", m_ui.audioSync); saveSetting("frameskip", m_ui.frameskip);
M src/platform/qt/SettingsView.hsrc/platform/qt/SettingsView.h

@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef QGBA_SETTINGS_VIEW #define QGBA_SETTINGS_VIEW -#include <QWidget> +#include <QDialog> #include "ui_SettingsView.h"

@@ -14,7 +14,7 @@ namespace QGBA {

class ConfigController; -class SettingsView : public QWidget { +class SettingsView : public QDialog { Q_OBJECT public:
M src/platform/qt/SettingsView.uisrc/platform/qt/SettingsView.ui

@@ -62,10 +62,10 @@ <property name="editable">

<bool>true</bool> </property> <property name="currentText"> - <string>2048</string> + <string>1536</string> </property> <property name="currentIndex"> - <number>2</number> + <number>3</number> </property> <item> <property name="text">

@@ -74,7 +74,17 @@ </property>

</item> <item> <property name="text"> + <string>768</string> + </property> + </item> + <item> + <property name="text"> <string>1024</string> + </property> + </item> + <item> + <property name="text"> + <string>1536</string> </property> </item> <item>

@@ -84,6 +94,11 @@ </property>

</item> <item> <property name="text"> + <string>3072</string> + </property> + </item> + <item> + <property name="text"> <string>4096</string> </property> </item>

@@ -99,13 +114,20 @@ </item>

</layout> </item> <item row="2" column="0"> + <widget class="QLabel" name="label_19"> + <property name="text"> + <string>Sample rate:</string> + </property> + </widget> + </item> + <item row="3" column="0"> <widget class="QLabel" name="label_17"> <property name="text"> <string>Volume:</string> </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QSlider" name="volume">

@@ -132,28 +154,38 @@ </widget>

</item> </layout> </item> - <item row="3" column="0" colspan="2"> + <item row="4" column="0" colspan="2"> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <widget class="QLabel" name="label_10"> <property name="text"> <string>Display driver:</string> </property> </widget> </item> - <item row="5" column="0"> + <item row="5" column="1"> + <widget class="QComboBox" name="displayDriver"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="6" column="0"> <widget class="QLabel" name="label_9"> <property name="text"> <string>Frameskip:</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_16"> <item> <widget class="QLabel" name="label_12">

@@ -174,14 +206,14 @@ </widget>

</item> </layout> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>FPS target:</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="7" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QDoubleSpinBox" name="fpsTarget">

@@ -205,21 +237,21 @@ </widget>

</item> </layout> </item> - <item row="7" column="0" colspan="2"> + <item row="8" column="0" colspan="2"> <widget class="Line" name="line_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Sync:</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QCheckBox" name="videoSync">

@@ -237,29 +269,63 @@ </widget>

</item> </layout> </item> - <item row="9" column="1"> + <item row="10" column="1"> <widget class="QCheckBox" name="lockAspectRatio"> <property name="text"> <string>Lock aspect ratio</string> </property> </widget> </item> - <item row="10" column="1"> + <item row="11" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Resample video</string> </property> </widget> </item> - <item row="4" column="1"> - <widget class="QComboBox" name="displayDriver"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <item> + <widget class="QComboBox" name="sampleRate"> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string>44100</string> + </property> + <property name="currentIndex"> + <number>2</number> + </property> + <item> + <property name="text"> + <string>22050</string> + </property> + </item> + <item> + <property name="text"> + <string>32000</string> + </property> + </item> + <item> + <property name="text"> + <string>44100</string> + </property> + </item> + <item> + <property name="text"> + <string>48000</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string>Hz</string> + </property> + </widget> + </item> + </layout> </item> </layout> </item>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -100,6 +100,14 @@ connect(m_controller, SIGNAL(gameStopped(GBAThread*)), this, SLOT(gameStopped()));

connect(m_controller, SIGNAL(gameStopped(GBAThread*)), &m_inputController, SLOT(resumeScreensaver())); connect(m_controller, SIGNAL(stateLoaded(GBAThread*)), m_display, SLOT(forceDraw())); connect(m_controller, SIGNAL(rewound(GBAThread*)), m_display, SLOT(forceDraw())); + connect(m_controller, &GameController::gamePaused, [this]() { + QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, + VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGBX8888); + QPixmap pixmap; + pixmap.convertFromImage(currentImage); + m_screenWidget->setPixmap(pixmap); + m_screenWidget->setLockAspectRatio(3, 2); + }); connect(m_controller, SIGNAL(gamePaused(GBAThread*)), m_display, SLOT(pauseDrawing())); #ifndef Q_OS_MAC connect(m_controller, SIGNAL(gamePaused(GBAThread*)), menuBar(), SLOT(show()));

@@ -127,6 +135,7 @@ connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing()));

connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame())); connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide())); connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int))); + connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned))); connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); connect(m_display, &Display::hideCursor, [this]() {

@@ -195,12 +204,17 @@ if (opts->bios) {

m_controller->loadBIOS(opts->bios); } + // TODO: Move these to ConfigController if (opts->fpsTarget) { emit fpsTargetChanged(opts->fpsTarget); } if (opts->audioBuffers) { emit audioBufferSamplesChanged(opts->audioBuffers); + } + + if (opts->sampleRate) { + emit sampleRateChanged(opts->sampleRate); } if (opts->width && opts->height) {

@@ -393,9 +407,7 @@ if (!m_gdbController) {

m_gdbController = new GDBController(m_controller, this); } GDBWindow* window = new GDBWindow(m_gdbController); - connect(this, SIGNAL(shutdown()), window, SLOT(close())); - window->setAttribute(Qt::WA_DeleteOnClose); - window->show(); + openView(window); } #endif

@@ -673,7 +685,7 @@ m_stateWindow = new LoadSaveState(m_controller);

connect(this, SIGNAL(shutdown()), m_stateWindow, SLOT(close())); connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_stateWindow, SLOT(close())); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - m_screenWidget->layout()->removeWidget(m_stateWindow); + detachWidget(m_stateWindow); m_stateWindow = nullptr; QMetaObject::invokeMethod(this, "setFocus", Qt::QueuedConnection); });

@@ -823,13 +835,6 @@ pause->setShortcut(tr("Ctrl+P"));

connect(pause, SIGNAL(triggered(bool)), m_controller, SLOT(setPaused(bool))); connect(m_controller, &GameController::gamePaused, [this, pause]() { pause->setChecked(true); - - QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, - VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32); - QPixmap pixmap; - pixmap.convertFromImage(currentImage.rgbSwapped()); - m_screenWidget->setPixmap(pixmap); - m_screenWidget->setLockAspectRatio(3, 2); }); connect(m_controller, &GameController::gameUnpaused, [pause]() { pause->setChecked(false); }); m_gameActions.append(pause);

@@ -939,9 +944,12 @@ m_shortcutController->addMenu(frameMenu, avMenu);

for (int i = 1; i <= 6; ++i) { QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu); setSize->setCheckable(true); - connect(setSize, &QAction::triggered, [this, i]() { + connect(setSize, &QAction::triggered, [this, i, setSize]() { showNormal(); resizeFrame(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i); + bool enableSignals = setSize->blockSignals(true); + setSize->setChecked(true); + setSize->blockSignals(enableSignals); }); m_frameSizes[i] = setSize; addControlledAction(frameMenu, setSize, QString("frame%1x").arg(QString::number(i)));

@@ -980,20 +988,6 @@ m_config->updateOption("frameskip");

avMenu->addSeparator(); - QMenu* buffersMenu = avMenu->addMenu(tr("Audio buffer &size")); - ConfigOption* buffers = m_config->addOption("audioBuffers"); - buffers->connect([this](const QVariant& value) { - emit audioBufferSamplesChanged(value.toInt()); - }, this); - buffers->addValue(tr("512"), 512, buffersMenu); - buffers->addValue(tr("768"), 768, buffersMenu); - buffers->addValue(tr("1024"), 1024, buffersMenu); - buffers->addValue(tr("2048"), 2048, buffersMenu); - buffers->addValue(tr("4096"), 4096, buffersMenu); - m_config->updateOption("audioBuffers"); - - avMenu->addSeparator(); - QMenu* target = avMenu->addMenu(tr("FPS target")); ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget"); fpsTargetOption->connect([this](const QVariant& value) {

@@ -1129,6 +1123,21 @@ skipBios->connect([this](const QVariant& value) {

m_controller->setSkipBIOS(value.toBool()); }, this); + ConfigOption* useBios = m_config->addOption("useBios"); + useBios->connect([this](const QVariant& value) { + m_controller->setUseBIOS(value.toBool()); + }, this); + + ConfigOption* buffers = m_config->addOption("audioBuffers"); + buffers->connect([this](const QVariant& value) { + emit audioBufferSamplesChanged(value.toInt()); + }, this); + + ConfigOption* sampleRate = m_config->addOption("sampleRate"); + sampleRate->connect([this](const QVariant& value) { + emit sampleRateChanged(value.toUInt()); + }, this); + ConfigOption* volume = m_config->addOption("volume"); volume->connect([this](const QVariant& value) { m_controller->setVolume(value.toInt());

@@ -1171,7 +1180,7 @@ }

void Window::attachWidget(QWidget* widget) { m_screenWidget->layout()->addWidget(widget); - unsetCursor(); + m_screenWidget->unsetCursor(); static_cast<QStackedLayout*>(m_screenWidget->layout())->setCurrentWidget(widget); }
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -54,6 +54,7 @@ signals:

void startDrawing(GBAThread*); void shutdown(); void audioBufferSamplesChanged(int samples); + void sampleRateChanged(unsigned samples); void fpsTargetChanged(float target); public slots:
M src/platform/sdl/CMakeLists.txtsrc/platform/sdl/CMakeLists.txt

@@ -84,3 +84,6 @@ set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}")

target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin COMPONENT ${BINARY_NAME}-sdl) +if(UNIX) + install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) +endif()
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -112,6 +112,10 @@

bool didFail = false; renderer.audio.samples = context.audioBuffers; + renderer.audio.sampleRate = 44100; + if (opts.sampleRate) { + renderer.audio.sampleRate = opts.sampleRate; + } if (!GBASDLInitAudio(&renderer.audio, &context)) { didFail = true; }
M src/platform/sdl/sdl-audio.csrc/platform/sdl/sdl-audio.c

@@ -22,7 +22,7 @@ GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());

return false; } - context->desiredSpec.freq = 44100; + context->desiredSpec.freq = context->sampleRate; context->desiredSpec.format = AUDIO_S16SYS; context->desiredSpec.channels = 2; context->desiredSpec.samples = context->samples;
M src/platform/sdl/sdl-audio.hsrc/platform/sdl/sdl-audio.h

@@ -15,6 +15,7 @@

struct GBASDLAudio { // Input size_t samples; + unsigned sampleRate; // State SDL_AudioSpec desiredSpec;
M src/util/formatting.csrc/util/formatting.c

@@ -9,20 +9,20 @@ #include <float.h>

int ftostr_l(char* restrict str, size_t size, float f, locale_t locale) { #ifdef HAVE_SNPRINTF_L - return snprintf_l(str, size, locale, "%*.g", FLT_DIG, f); + return snprintf_l(str, size, locale, "%.*g", FLT_DIG, f); #elif defined(HAVE_LOCALE) locale_t old = uselocale(locale); - int res = snprintf(str, size, "%*.g", FLT_DIG, f); + int res = snprintf(str, size, "%.*g", FLT_DIG, f); uselocale(old); return res; #elif defined(HAVE_SETLOCALE) char* old = setlocale(LC_NUMERIC, locale); - int res = snprintf(str, size, "%*.g", FLT_DIG, f); + int res = snprintf(str, size, "%.*g", FLT_DIG, f); setlocale(LC_NUMERIC, old); return res; #else UNUSED(locale); - return snprintf(str, size, "%*.g", FLT_DIG, f); + return snprintf(str, size, "%.*g", FLT_DIG, f); #endif }
A tools/debian/changelog

@@ -0,0 +1,5 @@

+mgba (0.3.0-1) UNRELEASED; urgency=low + + * Initial release (closes: Bug#787470). + + -- Sérgio Benjamim <sergio_br2@yahoo.com.br> Mon, 17 Aug 2015 18:40:00 -0300
A tools/debian/clean

@@ -0,0 +1,2 @@

+debian/libmgba.install +debian/libretro-mgba.install
A tools/debian/compat

@@ -0,0 +1,1 @@

+9
A tools/debian/control

@@ -0,0 +1,78 @@

+Source: mgba +Section: otherosfs +Priority: extra +Maintainer: Sérgio Benjamim <sergio_br2@yahoo.com.br> +Build-Depends: cmake (>= 2.8.11), + debhelper (>= 9), + libavcodec-dev, + libavformat-dev, + libavresample-dev, + libavutil-dev, + libedit-dev, + libmagickwand-dev, + libpng-dev, + libqt5opengl5-dev, + libsdl2-dev, + libswscale-dev, + libzip-dev, + pkg-config, + qtbase5-dev, + qtmultimedia5-dev, + zlib1g-dev +Standards-Version: 3.9.6 +Homepage: http://mgba.io/ + +Package: libmgba +Architecture: any +Multi-Arch: same +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (common library for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the common library for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: mgba-qt +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (Qt frontend for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the Qt GUI frontend for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: mgba-sdl +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (SDL frontend for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the SDL UI console for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: libretro-mgba +Architecture: any +Multi-Arch: same +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Libretro wrapper for mGBA + This wrapper makes mGBA API compatible with libretro, thus allowing its use + with libretro frontends, such as RetroArch. + . + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned.
A tools/debian/copyright

@@ -0,0 +1,472 @@

+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: mGBA +Upstream-Contact: Jeffrey Pfau (aka endrift) <jeffrey@endrift.com> +Source: https://github.com/mgba-emu/mgba +Comment: This package was debianized by + Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br> on Mon, 01 Jun 2015 18:40:00 -0300 + + +Files: * +Copyright: 2013-2015 Jeffrey Pfau +License: MPL-2.0 + +Files: src/third-party/blip_buf/* +Copyright: 2003-2009 Shay Green +License: LGPL-2.1+ + +Files: src/third-party/inih/* +Copyright: 2009 Brush Technology. All rights reserved. +License: BSD-3-clause-Brush-Technology + +Files: src/third-party/lzma/* +Copyright: 2008-2015 Igor Pavlov +License: public-domain + These files have been put in the public domain by their author + +Files: src/platform/libretro/libretro.h +Copyright: 2010-2015 The RetroArch Team +License: Expat + +Files: debian/* +Copyright: 2015 Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br> +License: MPL-2.0 + + +License: MPL-2.0 + Mozilla Public License Version 2.0 + ================================== + . + 1. Definitions + -------------- + . + 1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + . + 1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + . + 1.3. "Contribution" + means Covered Software of a particular Contributor. + . + 1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + . + 1.5. "Incompatible With Secondary Licenses" + means + . + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + . + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + . + 1.6. "Executable Form" + means any form of the work other than Source Code Form. + . + 1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + . + 1.8. "License" + means this document. + . + 1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + . + 1.10. "Modifications" + means any of the following: + . + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + . + (b) any new file in Source Code Form that contains any Covered + Software. + . + 1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + . + 1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + . + 1.13. "Source Code Form" + means the form of the work preferred for making modifications. + . + 1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + . + 2. License Grants and Conditions + -------------------------------- + . + 2.1. Grants + . + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + . + (a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + . + (b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + . + 2.2. Effective Date + . + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + . + 2.3. Limitations on Grant Scope + . + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + . + (a) for any code that a Contributor has removed from Covered Software; + or + . + (b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + . + (c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + . + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + . + 2.4. Subsequent Licenses + . + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + . + 2.5. Representation + . + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights + to grant the rights to its Contributions conveyed by this License. + . + 2.6. Fair Use + . + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + . + 2.7. Conditions + . + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted + in Section 2.1. + . + 3. Responsibilities + ------------------- + . + 3.1. Distribution of Source Form + . + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + . + 3.2. Distribution of Executable Form + . + If You distribute Covered Software in Executable Form then: + . + (a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + . + (b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + . + 3.3. Distribution of a Larger Work + . + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + . + 3.4. Notices + . + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, + or limitations of liability) contained within the Source Code Form of + the Covered Software, except that You may alter any license notices to + the extent required to remedy known factual inaccuracies. + . + 3.5. Application of Additional Terms + . + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + . + 4. Inability to Comply Due to Statute or Regulation + --------------------------------------------------- + . + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Software due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description must + be placed in a text file included with all distributions of the Covered + Software under this License. Except to the extent prohibited by statute + or regulation, such description must be sufficiently detailed for a + recipient of ordinary skill to be able to understand it. + . + 5. Termination + -------------- + . + 5.1. The rights granted under this License will terminate automatically + if You fail to comply with any of its terms. However, if You become + compliant, then the rights granted under this License from a particular + Contributor are reinstated (a) provisionally, unless and until such + Contributor explicitly and finally terminates Your grants, and (b) on an + ongoing basis, if such Contributor fails to notify You of the + non-compliance by some reasonable means prior to 60 days after You have + come back into compliance. Moreover, Your grants from a particular + Contributor are reinstated on an ongoing basis if such Contributor + notifies You of the non-compliance by some reasonable means, this is the + first time You have received notice of non-compliance with this License + from such Contributor, and You become compliant prior to 30 days after + Your receipt of the notice. + . + 5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + . + 5.3. In the event of termination under Sections 5.1 or 5.2 above, all + end user license agreements (excluding distributors and resellers) which + have been validly granted by You or Your distributors under this License + prior to termination shall survive termination. + . + ************************************************************************ + * * + * 6. Disclaimer of Warranty * + * ------------------------- * + * * + * Covered Software is provided under this License on an "as is" * + * basis, without warranty of any kind, either expressed, implied, or * + * statutory, including, without limitation, warranties that the * + * Covered Software is free of defects, merchantable, fit for a * + * particular purpose or non-infringing. The entire risk as to the * + * quality and performance of the Covered Software is with You. * + * Should any Covered Software prove defective in any respect, You * + * (not any Contributor) assume the cost of any necessary servicing, * + * repair, or correction. This disclaimer of warranty constitutes an * + * essential part of this License. No use of any Covered Software is * + * authorized under this License except under this disclaimer. * + * * + ************************************************************************ + . + ************************************************************************ + * * + * 7. Limitation of Liability * + * -------------------------- * + * * + * Under no circumstances and under no legal theory, whether tort * + * (including negligence), contract, or otherwise, shall any * + * Contributor, or anyone who distributes Covered Software as * + * permitted above, be liable to You for any direct, indirect, * + * special, incidental, or consequential damages of any character * + * including, without limitation, damages for lost profits, loss of * + * goodwill, work stoppage, computer failure or malfunction, or any * + * and all other commercial damages or losses, even if such party * + * shall have been informed of the possibility of such damages. This * + * limitation of liability shall not apply to liability for death or * + * personal injury resulting from such party's negligence to the * + * extent applicable law prohibits such limitation. Some * + * jurisdictions do not allow the exclusion or limitation of * + * incidental or consequential damages, so this exclusion and * + * limitation may not apply to You. * + * * + ************************************************************************ + . + 8. Litigation + ------------- + . + Any litigation relating to this License may be brought only in the + courts of a jurisdiction where the defendant maintains its principal + place of business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. + Nothing in this Section shall prevent a party's ability to bring + cross-claims or counter-claims. + . + 9. Miscellaneous + ---------------- + . + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides + that the language of a contract shall be construed against the drafter + shall not be used to construe this License against a Contributor. + . + 10. Versions of the License + --------------------------- + . + 10.1. New Versions + . + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + . + 10.2. Effect of New Versions + . + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + . + 10.3. Modified Versions + . + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + . + 10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses + . + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + . + Exhibit A - Source Code Form License Notice + ------------------------------------------- + . + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + . + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to look + for such a notice. + . + You may add additional accurate notices of copyright ownership. + . + Exhibit B - "Incompatible With Secondary Licenses" Notice + --------------------------------------------------------- + . + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + + +License: BSD-3-clause-Brush-Technology + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Brush Technology nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + +License: LGPL-2.1+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + . + On Debian systems, the full text of the GNU Lesser General Public + License version 2.1 can be found in the file + `/usr/share/common-licenses/LGPL-2.1'.
A tools/debian/docs

@@ -0,0 +1,2 @@

+README.md +CHANGES
A tools/debian/libmgba.install.in

@@ -0,0 +1,1 @@

+obj/libmgba.so* usr/lib/@DEB_HOST_MULTIARCH@/
A tools/debian/libretro-mgba.install.in

@@ -0,0 +1,1 @@

+obj/mgba_libretro.so usr/lib/@DEB_HOST_MULTIARCH@/libretro
A tools/debian/mgba-qt.install

@@ -0,0 +1,3 @@

+usr/bin/mgba-qt +res/mgba-qt.desktop usr/share/applications +usr/share/icons
A tools/debian/mgba-qt.manpages

@@ -0,0 +1,1 @@

+doc/mgba-qt.6
A tools/debian/mgba-sdl.install

@@ -0,0 +1,1 @@

+usr/bin/mgba
A tools/debian/mgba-sdl.manpages

@@ -0,0 +1,1 @@

+doc/mgba.6
A tools/debian/rules

@@ -0,0 +1,27 @@

+#!/usr/bin/make -f + +# Copyright (C) 2015 Sergio Benjamim + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +ARCH=$(shell dpkg-architecture -qDEB_HOST_ARCH) + +ifeq ($(ARCH),armhf) + ARM=-DBUILD_GL=OFF -DBUILD_GLES2=ON +endif + +%: + dh $@ --buildsystem=cmake --builddirectory=obj --parallel + +override_dh_auto_configure: + dh_auto_configure -- -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_SKIP_RPATH=ON -DBUILD_LIBRETRO=ON $(ARM) + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \ + debian/libretro-mgba.install.in > debian/libretro-mgba.install + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \ + debian/libmgba.install.in > debian/libmgba.install + +override_dh_installchangelogs: + dh_installchangelogs -k CHANGES
A tools/debian/source/format

@@ -0,0 +1,1 @@

+3.0 (quilt)
A tools/debian/watch

@@ -0,0 +1,3 @@

+version=3 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/mgba-$1\.tar\.gz/ \ + https://github.com/mgba-emu/mgba/tags .*/v?(\d\S*)\.tar\.gz
M version.cmakeversion.cmake

@@ -32,6 +32,16 @@ set(VERSION_STRING ${LIB_VERSION_ABI}-${VERSION_STRING})

endif() endif() +if(NOT GIT_COMMIT) + set(GIT_COMMIT "(unknown)") +endif() +if(NOT GIT_COMMIT_SHORT) + set(GIT_COMMIT_SHORT "(unknown)") +endif() +if(NOT GIT_BRANCH) + set(GIT_BRANCH "(unknown)") +endif() + if(CONFIG_FILE AND OUT_FILE) configure_file("${CONFIG_FILE}" "${OUT_FILE}") endif()