diff --git a/docs/index.html b/docs/index.html
index daab10049..df6d923cd 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -4068,10 +4068,10 @@
Stella supports viewing images and ROM properties of the currently
selected ROM in the ROM launcher. Image support is automatic, as long as your
- image directory contains snapshots in the appropriate format. The label
- (if existing) and the number of matching snapshots are displayed under the
- current one. The mouse or the ROM Launcher hotkeys can be used to browse
- multiple images or a ROM.
+ image directory contains any images in the appropriate format. The label (if
+ existing) and the number of matching images are displayed under the
+ current image. The mouse, the ROM Launcher hotkeys or the controller can be
+ used to browse multiple images of a ROM.
Notes:
The images can have PNG or JPG format.
diff --git a/docs/index_r77.html b/docs/index_r77.html
index bf3a3a8a2..982c281c8 100644
--- a/docs/index_r77.html
+++ b/docs/index_r77.html
@@ -160,6 +160,16 @@
Button 2 or 6 close the menu without any action.
+
+ Button 1 + Left |
+ - |
+ Display previous image |
+
+
+ Button 1 + Right |
+ - |
+ Display next image |
+
Button 2 |
SKILL P2 |
diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx
index e66799cc4..9e07564e3 100644
--- a/src/common/SoundSDL2.cxx
+++ b/src/common/SoundSDL2.cxx
@@ -74,11 +74,6 @@ SoundSDL2::~SoundSDL2()
if(!myIsInitializedFlag)
return;
- if(myWavDevice)
- {
- SDL_CloseAudioDevice(myWavDevice);
- SDL_FreeWAV(myWavBuffer);
- }
SDL_CloseAudioDevice(myDevice);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
@@ -165,7 +160,7 @@ void SoundSDL2::open(shared_ptr audioQueue,
openDevice();
myEmulationTiming = emulationTiming;
- myWavSpeed = 262 * 60 * 2. / myEmulationTiming->audioSampleRate();
+ myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
Logger::debug("SoundSDL2::open started ...");
@@ -214,11 +209,7 @@ void SoundSDL2::mute(bool state)
{
myAudioSettings.setEnabled(!state);
if(state)
- {
- SDL_LockAudioDevice(myDevice);
myVolumeFactor = 0;
- SDL_UnlockAudioDevice(myDevice);
- }
else
setVolume(myAudioSettings.volume());
}
@@ -242,10 +233,10 @@ bool SoundSDL2::pause(bool state)
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
if(myIsInitializedFlag)
+ {
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
- if(myWavDevice)
- SDL_PauseAudioDevice(myWavDevice, state ? 1 : 0);
-
+ myWavHandler.pause(state);
+ }
return oldstate;
}
@@ -255,10 +246,7 @@ void SoundSDL2::setVolume(uInt32 volume)
if(myIsInitializedFlag && (volume <= 100))
{
myAudioSettings.setVolume(volume);
-
- SDL_LockAudioDevice(myDevice);
myVolumeFactor = myAudioSettings.enabled() ? static_cast(volume) / 100.F : 0;
- SDL_UnlockAudioDevice(myDevice);
}
}
@@ -336,15 +324,6 @@ string SoundSDL2::about() const
return buf.str();
}
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void SoundSDL2::processFragment(float* stream, uInt32 length)
-{
- myResampler->fillFragment(stream, length);
-
- for(uInt32 i = 0; i < length; ++i)
- stream[i] *= myVolumeFactor;
-}
-
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::initResampler()
{
@@ -395,12 +374,21 @@ void SoundSDL2::initResampler()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void SoundSDL2::callback(void* udata, uInt8* stream, int len)
+void SoundSDL2::callback(void* object, uInt8* stream, int len)
{
- auto* self = static_cast(udata);
+ auto* self = static_cast(object);
if(self->myAudioQueue)
- self->processFragment(reinterpret_cast(stream), len >> 2);
+ {
+ // The stream is 32-bit float (even though this callback is 8-bits), since
+ // the resampler and TIA audio subsystem always generate float samples
+ auto* s = reinterpret_cast(stream);
+ const uInt32 length = len >> 2;
+ self->myResampler->fillFragment(s, length);
+
+ for(uInt32 i = 0; i < length; ++i) // TODO - perhaps move into Resampler
+ s[i] *= SoundSDL2::myVolumeFactor;
+ }
else
SDL_memset(stream, 0, len);
}
@@ -409,128 +397,160 @@ void SoundSDL2::callback(void* udata, uInt8* stream, int len)
bool SoundSDL2::playWav(const string& fileName, const uInt32 position,
const uInt32 length)
{
- // Load WAV file
- if(fileName != myWavFilename || myWavBuffer == nullptr)
- {
- if(myWavBuffer)
- {
- SDL_FreeWAV(myWavBuffer);
- myWavBuffer = nullptr;
- }
- if(SDL_LoadWAV(fileName.c_str(), &myWavSpec, &myWavBuffer, &myWavLength) == nullptr)
- return false;
- // Set the callback function
- myWavSpec.callback = wavCallback;
- myWavSpec.userdata = nullptr;
- //myWavSpec.samples = 4096; // decrease for smaller samples;
- }
- if(position > myWavLength)
- return false;
+ const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
- myWavFilename = fileName;
-
- myWavLen = length
- ? std::min(length, myWavLength - position)
- : myWavLength;
- myWavPos = myWavBuffer + position;
-
- // Open audio device
- if(!myWavDevice)
- {
- const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
-
- myWavDevice = SDL_OpenAudioDevice(device, 0, &myWavSpec, nullptr, 0);
- if(!myWavDevice)
- return false;
- // Play audio
- SDL_PauseAudioDevice(myWavDevice, 0);
- }
- return true;
+ return myWavHandler.play(fileName, device, position, length);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::stopWav()
{
- if(myWavBuffer)
- {
- // Clean up
- myWavLen = 0;
- SDL_CloseAudioDevice(myWavDevice);
- myWavDevice = 0;
- SDL_FreeWAV(myWavBuffer);
- myWavBuffer = nullptr;
- }
- if(myWavCvtBuffer)
- {
- myWavCvtBuffer.reset();
- myWavCvtBufferSize = 0;
- }
+ myWavHandler.stop();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL2::wavSize() const
{
- return myWavBuffer ? myWavLen : 0;
+ return myWavHandler.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void SoundSDL2::wavCallback(void* udata, uInt8* stream, int len)
+bool SoundSDL2::WavHandlerSDL2::play(
+ const string& fileName, const char* device,
+ const uInt32 position, const uInt32 length
+)
{
- SDL_memset(stream, myWavSpec.silence, len);
- if(myWavLen)
+ // Load WAV file
+ if(fileName != myFilename || myBuffer == nullptr)
{
- if(myWavSpeed != 1.0)
+ if(myBuffer)
{
- const int origLen = len;
- len = std::round(len / myWavSpeed);
- const int newFreq =
- std::round(static_cast(myWavSpec.freq) * origLen / len);
-
- if(static_cast(len) > myWavLen)
- len = myWavLen;
-
- SDL_AudioCVT cvt;
- SDL_BuildAudioCVT(&cvt, myWavSpec.format, myWavSpec.channels, myWavSpec.freq,
- myWavSpec.format, myWavSpec.channels, newFreq);
- SDL_assert(cvt.needed); // Obviously, this one is always needed.
- cvt.len = len * myWavSpec.channels; // Mono 8 bit sample frames
-
- if(!myWavCvtBuffer ||
- myWavCvtBufferSize < static_cast(cvt.len * cvt.len_mult))
- {
- myWavCvtBufferSize = cvt.len * cvt.len_mult;
- myWavCvtBuffer = make_unique(myWavCvtBufferSize);
- }
- cvt.buf = myWavCvtBuffer.get();
-
- // Read original data into conversion buffer
- SDL_memcpy(cvt.buf, myWavPos, cvt.len);
- SDL_ConvertAudio(&cvt);
- // Mix volume adjusted WAV data into silent buffer
- SDL_MixAudioFormat(stream, cvt.buf, myWavSpec.format, cvt.len_cvt,
- SDL_MIX_MAXVOLUME * myVolumeFactor);
+ SDL_FreeWAV(myBuffer);
+ myBuffer = nullptr;
}
- else
- {
- if(static_cast(len) > myWavLen)
- len = myWavLen;
+ SDL_zero(mySpec);
+ if(SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength) == nullptr)
+ return false;
- // Mix volume adjusted WAV data into silent buffer
- SDL_MixAudioFormat(stream, myWavPos, myWavSpec.format, len,
- SDL_MIX_MAXVOLUME * myVolumeFactor);
- }
- myWavPos += len;
- myWavLen -= len;
+ // Set the callback function
+ mySpec.callback = callback;
+ mySpec.userdata = this;
+ }
+ if(position > myLength)
+ return false;
+
+ myFilename = fileName;
+
+ myRemaining = length
+ ? std::min(length, myLength - position)
+ : myLength;
+ myPos = myBuffer + position;
+
+ // Open audio device
+ if(!myDevice)
+ {
+ myDevice = SDL_OpenAudioDevice(device, 0, &mySpec, nullptr, 0);
+ if(!myDevice)
+ return false;
+ // Play audio
+ SDL_PauseAudioDevice(myDevice, 0);
+ }
+ return true;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void SoundSDL2::WavHandlerSDL2::stop()
+{
+ if(myBuffer)
+ {
+ // Clean up
+ myRemaining = 0;
+ SDL_CloseAudioDevice(myDevice); myDevice = 0;
+ SDL_FreeWAV(myBuffer); myBuffer = nullptr;
+ }
+ if(myCvtBuffer)
+ {
+ myCvtBuffer.reset();
+ myCvtBufferSize = 0;
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void SoundSDL2::WavHandlerSDL2::processWav(uInt8* stream, uInt32 len)
+{
+ SDL_memset(stream, mySpec.silence, len);
+ if(myRemaining)
+ {
+ if(mySpeed != 1.0)
+ {
+ const int origLen = len;
+ len = std::round(len / mySpeed);
+ const int newFreq =
+ std::round(static_cast(mySpec.freq) * origLen / len);
+
+ if(static_cast(len) > myRemaining)
+ len = myRemaining;
+
+ SDL_AudioCVT cvt;
+ SDL_BuildAudioCVT(&cvt, mySpec.format, mySpec.channels, mySpec.freq,
+ mySpec.format, mySpec.channels, newFreq);
+ SDL_assert(cvt.needed); // Obviously, this one is always needed.
+ cvt.len = len * mySpec.channels; // Mono 8 bit sample frames
+
+ if(!myCvtBuffer ||
+ myCvtBufferSize < static_cast(cvt.len * cvt.len_mult))
+ {
+ myCvtBufferSize = cvt.len * cvt.len_mult;
+ myCvtBuffer = make_unique(myCvtBufferSize);
+ }
+ cvt.buf = myCvtBuffer.get();
+
+ // Read original data into conversion buffer
+ SDL_memcpy(cvt.buf, myPos, cvt.len);
+ SDL_ConvertAudio(&cvt);
+ // Mix volume adjusted WAV data into silent buffer
+ SDL_MixAudioFormat(stream, cvt.buf, mySpec.format, cvt.len_cvt,
+ SDL_MIX_MAXVOLUME * SoundSDL2::myVolumeFactor);
+ }
+ else
+ {
+ if(static_cast(len) > myRemaining)
+ len = myRemaining;
+
+ // Mix volume adjusted WAV data into silent buffer
+ SDL_MixAudioFormat(stream, myPos, mySpec.format, len,
+ SDL_MIX_MAXVOLUME * myVolumeFactor);
+ }
+ myPos += len;
+ myRemaining -= len;
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void SoundSDL2::WavHandlerSDL2::callback(void* object, uInt8* stream, int len)
+{
+ static_cast(object)->processWav(
+ stream, static_cast(len));
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+SoundSDL2::WavHandlerSDL2::~WavHandlerSDL2()
+{
+ if(myDevice)
+ {
+ SDL_CloseAudioDevice(myDevice);
+ SDL_FreeWAV(myBuffer);
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void SoundSDL2::WavHandlerSDL2::pause(bool state) const
+{
+ if(myDevice)
+ SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float SoundSDL2::myVolumeFactor = 0.F;
-SDL_AudioSpec SoundSDL2::myWavSpec; // audio output format
-uInt8* SoundSDL2::myWavPos = nullptr; // pointer to the audio buffer to be played
-uInt32 SoundSDL2::myWavLen = 0; // remaining length of the sample we have to play
-double SoundSDL2::myWavSpeed = 1.0;
-unique_ptr SoundSDL2::myWavCvtBuffer;
-uInt32 SoundSDL2::myWavCvtBufferSize = 0;
#endif // SOUND_SUPPORT
diff --git a/src/common/SoundSDL2.hxx b/src/common/SoundSDL2.hxx
index 2753afb78..016d1a612 100644
--- a/src/common/SoundSDL2.hxx
+++ b/src/common/SoundSDL2.hxx
@@ -125,7 +125,7 @@ class SoundSDL2 : public Sound
@param position The position to start playing
@param length The played length
- @return True, if the WAV file can be played
+ @return True if the WAV file can be played, else false
*/
bool playWav(const string& fileName, const uInt32 position = 0,
const uInt32 length = 0) override;
@@ -142,7 +142,7 @@ class SoundSDL2 : public Sound
*/
uInt32 wavSize() const override;
- protected:
+ private:
/**
This method is called to query the audio devices.
@@ -150,17 +150,6 @@ class SoundSDL2 : public Sound
*/
void queryHardware(VariantList& devices) override;
- /**
- Invoked by the sound callback to process the next sound fragment.
- The stream is 16-bits (even though the callback is 8-bits), since
- the TIASnd class always generates signed 16-bit stereo samples.
-
- @param stream Pointer to the start of the fragment
- @param length Length of the fragment
- */
- void processFragment(float* stream, uInt32 length);
-
- private:
/**
The actual sound device is opened only when absolutely necessary.
Typically this will only happen once per program run, but it can also
@@ -171,47 +160,75 @@ class SoundSDL2 : public Sound
void initResampler();
private:
+ AudioSettings& myAudioSettings;
+
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag{false};
// Audio specification structure
SDL_AudioSpec myHardwareSpec;
+ SDL_AudioDeviceID myDevice{0};
uInt32 myDeviceId{0};
- SDL_AudioDeviceID myDevice{0};
-
shared_ptr myAudioQueue;
+ unique_ptr myResampler;
EmulationTiming* myEmulationTiming{nullptr};
Int16* myCurrentFragment{nullptr};
bool myUnderrun{false};
- unique_ptr myResampler;
+ string myAboutString;
- AudioSettings& myAudioSettings;
+ /**
+ This class implements WAV file playback using the SDL2 sound API.
+ */
+ class WavHandlerSDL2
+ {
+ public:
+ explicit WavHandlerSDL2() = default;
+ ~WavHandlerSDL2();
- // WAV file sound variables
- string myWavFilename;
- uInt32 myWavLength{0};
- SDL_AudioDeviceID myWavDevice{0};
- uInt8* myWavBuffer{nullptr};
+ bool play(const string& fileName, const char* device,
+ const uInt32 position, const uInt32 length);
+ void stop();
+ uInt32 size() const { return myBuffer ? myRemaining : 0; }
+
+ void setSpeed(const double speed) { mySpeed = speed; }
+ void pause(bool state) const;
+
+ private:
+ string myFilename;
+ uInt32 myLength{0};
+ SDL_AudioDeviceID myDevice{0};
+ uInt8* myBuffer{nullptr};
+ double mySpeed{1.0};
+ unique_ptr myCvtBuffer;
+ uInt32 myCvtBufferSize{0};
+ SDL_AudioSpec mySpec; // audio output format
+ uInt8* myPos{nullptr}; // pointer to the audio buffer to be played
+ uInt32 myRemaining{0}; // remaining length of the sample we have to play
+
+ private:
+ // Callback function invoked by the SDL Audio library when it needs data
+ void processWav(uInt8* stream, uInt32 len);
+ static void callback(void* object, uInt8* stream, int len);
+
+ // Following constructors and assignment operators not supported
+ WavHandlerSDL2(const WavHandlerSDL2&) = delete;
+ WavHandlerSDL2(WavHandlerSDL2&&) = delete;
+ WavHandlerSDL2& operator=(const WavHandlerSDL2&) = delete;
+ WavHandlerSDL2& operator=(WavHandlerSDL2&&) = delete;
+ };
+
+ WavHandlerSDL2 myWavHandler;
static float myVolumeFactor; // Current volume level (0 - 100)
- static double myWavSpeed;
- static unique_ptr myWavCvtBuffer;
- static uInt32 myWavCvtBufferSize;
- static SDL_AudioSpec myWavSpec; // audio output format
- static uInt8* myWavPos; // pointer to the audio buffer to be played
- static uInt32 myWavLen; // remaining length of the sample we have to play
-
- string myAboutString;
private:
// Callback functions invoked by the SDL Audio library when it needs data
- static void callback(void* udata, uInt8* stream, int len);
- static void wavCallback(void* udata, uInt8* stream, int len);
+ static void callback(void* object, uInt8* stream, int len);
// Following constructors and assignment operators not supported
SoundSDL2() = delete;
diff --git a/src/gui/DialogContainer.cxx b/src/gui/DialogContainer.cxx
index 35e5cf601..cdca03d44 100644
--- a/src/gui/DialogContainer.cxx
+++ b/src/gui/DialogContainer.cxx
@@ -67,7 +67,7 @@ void DialogContainer::updateTime(uInt64 time)
// Joystick has been pressed long
if(myCurrentButtonDown.stick != -1 && myButtonLongPressTime < myTime)
{
- myButtonLongPress = true;
+ myIgnoreButtonUp = true;
activeDialog->handleJoyDown(myCurrentButtonDown.stick, myCurrentButtonDown.button, true);
myButtonLongPressTime = myButtonRepeatTime = myTime + _REPEAT_NONE;
}
@@ -75,8 +75,12 @@ void DialogContainer::updateTime(uInt64 time)
// Joystick axis still pressed
if(myCurrentAxisDown.stick != -1 && myAxisRepeatTime < myTime)
{
- activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
- myCurrentAxisDown.adir);
+ if(myCurrentButtonDown.stick == myCurrentAxisDown.stick)
+ activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
+ myCurrentAxisDown.adir, myCurrentButtonDown.button);
+ else
+ activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
+ myCurrentAxisDown.adir);
myAxisRepeatTime = myTime + _REPEAT_SUSTAIN_DELAY;
}
@@ -323,14 +327,18 @@ void DialogContainer::handleJoyBtnEvent(int stick, int button, bool pressed)
// Send the event to the dialog box on the top of the stack
Dialog* activeDialog = myDialogStack.top();
- if(pressed && myButtonRepeatTime < myTime) // prevent pending repeats after enabling repeat again
+ if(pressed)
{
- myCurrentButtonDown.stick = stick;
- myCurrentButtonDown.button = button;
- myButtonRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
- myButtonLongPressTime = myTime + _LONG_PRESS_DELAY;
+ if(myButtonRepeatTime < myTime || // prevent pending repeats after enabling repeat again
+ myButtonRepeatTime + _REPEAT_INITIAL_DELAY > myTime) // ignore blocking delays
+ {
+ myCurrentButtonDown.stick = stick;
+ myCurrentButtonDown.button = button;
+ myButtonRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
+ myButtonLongPressTime = myTime + _LONG_PRESS_DELAY;
- activeDialog->handleJoyDown(stick, button);
+ activeDialog->handleJoyDown(stick, button);
+ }
}
else
{
@@ -340,8 +348,8 @@ void DialogContainer::handleJoyBtnEvent(int stick, int button, bool pressed)
myCurrentButtonDown.stick = myCurrentButtonDown.button = -1;
myButtonRepeatTime = myButtonLongPressTime = 0;
}
- if (myButtonLongPress)
- myButtonLongPress = false;
+ if(myIgnoreButtonUp)
+ myIgnoreButtonUp = false;
else
activeDialog->handleJoyUp(stick, button);
}
@@ -356,7 +364,8 @@ void DialogContainer::handleJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, i
// Send the event to the dialog box on the top of the stack
Dialog* activeDialog = myDialogStack.top();
- // Prevent long button press in button/axis combinations
+ // Prevent button repeats and long button press in button/axis combinations
+ myButtonRepeatTime = myTime + _REPEAT_NONE;
myButtonLongPressTime = myTime + _REPEAT_NONE;
// Only stop firing events if it's the current stick
@@ -374,6 +383,8 @@ void DialogContainer::handleJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, i
myCurrentAxisDown.adir = adir;
myAxisRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
}
+ if(adir != JoyDir::NONE)
+ myIgnoreButtonUp = true; // prevent button released events
activeDialog->handleJoyAxis(stick, axis, adir, button);
}
@@ -386,7 +397,8 @@ void DialogContainer::handleJoyHatEvent(int stick, int hat, JoyHatDir hdir, int
// Send the event to the dialog box on the top of the stack
Dialog* activeDialog = myDialogStack.top();
- // Prevent long button press in button/hat combinations
+ // Prevent button repeats and long button press in button/hat combinations
+ myButtonRepeatTime = myTime + _REPEAT_NONE;
myButtonLongPressTime = myTime + _REPEAT_NONE;
// Only stop firing events if it's the current stick
diff --git a/src/gui/DialogContainer.hxx b/src/gui/DialogContainer.hxx
index 69c6f5d66..44ef0cf2d 100644
--- a/src/gui/DialogContainer.hxx
+++ b/src/gui/DialogContainer.hxx
@@ -214,7 +214,7 @@ class DialogContainer
} myCurrentButtonDown;
uInt64 myButtonRepeatTime{0};
uInt64 myButtonLongPressTime{0};
- bool myButtonLongPress{false};
+ bool myIgnoreButtonUp{false};
// For continuous 'joy axis down' events
struct {
diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx
index 37f8c6b53..5ddcc8bd9 100644
--- a/src/gui/LauncherDialog.cxx
+++ b/src/gui/LauncherDialog.cxx
@@ -952,12 +952,13 @@ Event::Type LauncherDialog::getJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir
break;
case Event::UITabPrev:
- // TODO: check with controller, then update doc (R77 too)
myRomImageWidget->changeImage(-1);
+ myEventHandled = true;
break;
case Event::UITabNext:
myRomImageWidget->changeImage(1);
+ myEventHandled = true;
break;
default:
@@ -1136,11 +1137,13 @@ void LauncherDialog::openContextMenu(int x, int y)
{
// Dynamically create context menu for ROM list options
+ bool addCancel = false;
if(x < 0 || y < 0)
{
// Long pressed button, determine position from currently selected list item
x = myList->getLeft() + myList->getWidth() / 2;
y = myList->getTop() + (myList->getSelected() - myList->currentPos() + 1) * _font.getLineHeight();
+ addCancel = true;
}
struct ContextItem {
@@ -1219,6 +1222,8 @@ void LauncherDialog::openContextMenu(int x, int y)
// items.push_back(ContextItem("Options" + ELLIPSIS, "Ctrl+O", "options"));
//}
}
+ if(addCancel)
+ items.push_back(ContextItem("Cancel", "")); // closes the context menu and does nothing
// Format items for menu
VariantList varItems;