Update to v094r22 release.

byuu says:

I fixed the hiro layout enable bug, so when you go to assign joypad
input, the window disables itself so your input doesn't mess with the
controls.

I added "reset" to the hotkeys, in case you feel like clearing all of
them at once.

I added device selection support and the ability to disable audio
synchronization (run > 60fps) to the ruby/OSS driver. This is exposed in
tomoko's configuration file.

I added checks to stringify so that assigning null char* strings to
nall::string won't cause crashes anymore (technically the crash was in
strlen(), which doesn't check for null strings, but whatever ... I'll do
the check myself.)

I hooked up BrowserDialog::folderSelect() to loading slotted media for
now. Tested it by loading a Game Boy game successfully through the Super
Game Boy. Definitely want to write a custom window for this though, that
looks more like the library dialog.

Remaining issues:
- finish slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
- add more configuration panels (video, audio, timing)
This commit is contained in:
Tim Allen 2015-05-25 22:23:49 +10:00
parent 99b2b4b57c
commit 7bf4cff946
21 changed files with 280 additions and 250 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "higan"; static const char Name[] = "higan";
static const char Version[] = "094.21"; static const char Version[] = "094.22";
static const char Author[] = "byuu"; static const char Author[] = "byuu";
static const char License[] = "GPLv3"; static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/"; static const char Website[] = "http://byuu.org/";

View File

@ -31,7 +31,7 @@ auto mFixedLayout::reset() -> type& {
auto mFixedLayout::setEnabled(bool enabled) -> type& { auto mFixedLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled); mLayout::setEnabled(enabled);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setEnabled(sizable(n)->enabled(true)); sizable(n)->setEnabled(sizable(n)->enabled());
} }
return *this; return *this;
} }
@ -39,7 +39,7 @@ auto mFixedLayout::setEnabled(bool enabled) -> type& {
auto mFixedLayout::setFont(const string& font) -> type& { auto mFixedLayout::setFont(const string& font) -> type& {
mLayout::setFont(font); mLayout::setFont(font);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setFont(sizable(n)->font(true)); sizable(n)->setFont(sizable(n)->font());
} }
return *this; return *this;
} }
@ -47,7 +47,7 @@ auto mFixedLayout::setFont(const string& font) -> type& {
auto mFixedLayout::setVisible(bool visible) -> type& { auto mFixedLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible); mLayout::setVisible(visible);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setVisible(sizable(n)->visible(true)); sizable(n)->setVisible(sizable(n)->visible());
} }
return *this; return *this;
} }

View File

@ -51,7 +51,7 @@ auto mHorizontalLayout::setAlignment(double alignment) -> type& {
auto mHorizontalLayout::setEnabled(bool enabled) -> type& { auto mHorizontalLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled); mLayout::setEnabled(enabled);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setEnabled(sizable(n)->enabled(true)); sizable(n)->setEnabled(sizable(n)->enabled());
} }
return *this; return *this;
} }
@ -59,7 +59,7 @@ auto mHorizontalLayout::setEnabled(bool enabled) -> type& {
auto mHorizontalLayout::setFont(const string& font) -> type& { auto mHorizontalLayout::setFont(const string& font) -> type& {
mLayout::setFont(font); mLayout::setFont(font);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setFont(sizable(n)->font(true)); sizable(n)->setFont(sizable(n)->font());
} }
return *this; return *this;
} }
@ -121,7 +121,7 @@ auto mHorizontalLayout::setSpacing(signed spacing) -> type& {
auto mHorizontalLayout::setVisible(bool visible) -> type& { auto mHorizontalLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible); mLayout::setVisible(visible);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setVisible(sizable(n)->visible(true)); sizable(n)->setVisible(sizable(n)->visible());
} }
return *this; return *this;
} }

View File

@ -51,7 +51,7 @@ auto mVerticalLayout::setAlignment(double alignment) -> type& {
auto mVerticalLayout::setEnabled(bool enabled) -> type& { auto mVerticalLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled); mLayout::setEnabled(enabled);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setEnabled(sizable(n)->enabled(true)); sizable(n)->setEnabled(sizable(n)->enabled());
} }
return *this; return *this;
} }
@ -59,7 +59,7 @@ auto mVerticalLayout::setEnabled(bool enabled) -> type& {
auto mVerticalLayout::setFont(const string& font) -> type& { auto mVerticalLayout::setFont(const string& font) -> type& {
mLayout::setFont(font); mLayout::setFont(font);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setFont(sizable(n)->font(true)); sizable(n)->setFont(sizable(n)->font());
} }
return *this; return *this;
} }
@ -123,7 +123,7 @@ auto mVerticalLayout::setSpacing(signed spacing) -> type& {
auto mVerticalLayout::setVisible(bool visible) -> type& { auto mVerticalLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible); mLayout::setVisible(visible);
for(auto n : range(sizables())) { for(auto n : range(sizables())) {
sizable(n)->setVisible(sizable(n)->visible(true)); sizable(n)->setVisible(sizable(n)->visible());
} }
return *this; return *this;
} }

View File

@ -11,21 +11,12 @@ auto pLayout::destruct() -> void {
} }
auto pLayout::setEnabled(bool enabled) -> void { auto pLayout::setEnabled(bool enabled) -> void {
for(auto& sizable : state().sizables) {
if(sizable->self()) sizable->self()->setEnabled(sizable->enabled(true));
}
} }
auto pLayout::setFont(const string& font) -> void { auto pLayout::setFont(const string& font) -> void {
for(auto& sizable : state().sizables) {
if(sizable->self()) sizable->self()->setFont(sizable->font(true));
}
} }
auto pLayout::setVisible(bool visible) -> void { auto pLayout::setVisible(bool visible) -> void {
for(auto& sizable : state().sizables) {
if(sizable->self()) sizable->self()->setVisible(sizable->visible(true));
}
} }
} }

View File

@ -160,14 +160,14 @@ template<> struct stringify<char*> {
const char* _data; const char* _data;
auto data() const -> const char* { return _data; } auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); } auto size() const -> unsigned { return strlen(_data); }
stringify(char* source) : _data(source) {} stringify(char* source) : _data(source ? source : "") {}
}; };
template<> struct stringify<const char*> { template<> struct stringify<const char*> {
const char* _data; const char* _data;
auto data() const -> const char* { return _data; } auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); } auto size() const -> unsigned { return strlen(_data); }
stringify(const char* source) : _data(source) {} stringify(const char* source) : _data(source ? source : "") {}
}; };
//strings //strings

View File

@ -1,18 +1,19 @@
struct Audio { struct Audio {
static const char* Handle; static const nall::string Device;
static const char* Synchronize; static const nall::string Handle;
static const char* Frequency; static const nall::string Synchronize;
static const char* Latency; static const nall::string Frequency;
static const nall::string Latency;
virtual bool cap(const nall::string& name) { return false; } virtual ~Audio() = default;
virtual nall::any get(const nall::string& name) { return false; }
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
virtual void sample(uint16_t left, uint16_t right) {} virtual auto cap(const nall::string& name) -> bool { return false; }
virtual void clear() {} virtual auto get(const nall::string& name) -> nall::any { return false; }
virtual bool init() { return true; } virtual auto set(const nall::string& name, const nall::any& value) -> bool { return false; }
virtual void term() {}
Audio() {} virtual auto sample(uint16_t left, uint16_t right) -> void {}
virtual ~Audio() {} virtual auto clear() -> void {}
virtual auto init() -> bool { return true; }
virtual auto term() -> void {}
}; };

View File

@ -1,9 +1,3 @@
/*
audio.openal (2007-12-26)
author: Nach
contributors: byuu, wertigon, _willow_
*/
#if defined(PLATFORM_MACOSX) #if defined(PLATFORM_MACOSX)
#include <OpenAL/al.h> #include <OpenAL/al.h>
#include <OpenAL/alc.h> #include <OpenAL/alc.h>
@ -14,15 +8,14 @@
namespace ruby { namespace ruby {
class pAudioOpenAL { struct pAudioOpenAL {
public:
struct { struct {
ALCdevice* handle; ALCdevice* handle;
ALCcontext* context; ALCcontext* context;
ALuint source; ALuint source;
ALenum format; ALenum format;
unsigned latency; unsigned latency;
unsigned queue_length; unsigned queueLength;
} device; } device;
struct { struct {
@ -37,21 +30,21 @@ public:
unsigned latency; unsigned latency;
} settings; } settings;
bool cap(const string& name) { auto cap(const string& name) -> bool {
if(name == Audio::Synchronize) return true; if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true; if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true; if(name == Audio::Latency) return true;
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency; if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency; if(name == Audio::Latency) return settings.latency;
return false; return false;
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Audio::Synchronize) { if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value); settings.synchronize = any_cast<bool>(value);
return true; return true;
@ -65,7 +58,7 @@ public:
if(name == Audio::Latency) { if(name == Audio::Latency) {
if(settings.latency != any_cast<unsigned>(value)) { if(settings.latency != any_cast<unsigned>(value)) {
settings.latency = any_cast<unsigned>(value); settings.latency = any_cast<unsigned>(value);
update_latency(); updateLatency();
} }
return true; return true;
} }
@ -73,7 +66,7 @@ public:
return false; return false;
} }
void sample(uint16_t sl, uint16_t sr) { auto sample(uint16_t sl, uint16_t sr) -> void {
buffer.data[buffer.length++] = sl + (sr << 16); buffer.data[buffer.length++] = sl + (sr << 16);
if(buffer.length < buffer.size) return; if(buffer.length < buffer.size) return;
@ -84,17 +77,17 @@ public:
while(processed--) { while(processed--) {
alSourceUnqueueBuffers(device.source, 1, &albuffer); alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer); alDeleteBuffers(1, &albuffer);
device.queue_length--; device.queueLength--;
} }
//wait for buffer playback to catch up to sample generation if not synchronizing //wait for buffer playback to catch up to sample generation if not synchronizing
if(settings.synchronize == false || device.queue_length < 3) break; if(settings.synchronize == false || device.queueLength < 3) break;
} }
if(device.queue_length < 3) { if(device.queueLength < 3) {
alGenBuffers(1, &albuffer); alGenBuffers(1, &albuffer);
alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency); alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency);
alSourceQueueBuffers(device.source, 1, &albuffer); alSourceQueueBuffers(device.source, 1, &albuffer);
device.queue_length++; device.queueLength++;
} }
ALint playing; ALint playing;
@ -103,22 +96,16 @@ public:
buffer.length = 0; buffer.length = 0;
} }
void clear() { auto clear() -> void {
} }
void update_latency() { auto init() -> bool {
if(buffer.data) delete[] buffer.data; updateLatency();
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5; device.queueLength = 0;
buffer.data = new uint32_t[buffer.size];
}
bool init() {
update_latency();
device.queue_length = 0;
bool success = false; bool success = false;
if(device.handle = alcOpenDevice(NULL)) { if(device.handle = alcOpenDevice(nullptr)) {
if(device.context = alcCreateContext(device.handle, NULL)) { if(device.context = alcCreateContext(device.handle, nullptr)) {
alcMakeContextCurrent(device.context); alcMakeContextCurrent(device.context);
alGenSources(1, &device.source); alGenSources(1, &device.source);
@ -147,7 +134,7 @@ public:
return true; return true;
} }
void term() { auto term() -> void {
if(alIsSource(device.source) == AL_TRUE) { if(alIsSource(device.source) == AL_TRUE) {
int playing = 0; int playing = 0;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing); alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
@ -159,7 +146,7 @@ public:
ALuint albuffer = 0; ALuint albuffer = 0;
alSourceUnqueueBuffers(device.source, 1, &albuffer); alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer); alDeleteBuffers(1, &albuffer);
device.queue_length--; device.queueLength--;
} }
} }
@ -168,7 +155,7 @@ public:
} }
if(device.context) { if(device.context) {
alcMakeContextCurrent(NULL); alcMakeContextCurrent(nullptr);
alcDestroyContext(device.context); alcDestroyContext(device.context);
device.context = 0; device.context = 0;
} }
@ -189,7 +176,7 @@ public:
device.handle = 0; device.handle = 0;
device.context = 0; device.context = 0;
device.format = AL_FORMAT_STEREO16; device.format = AL_FORMAT_STEREO16;
device.queue_length = 0; device.queueLength = 0;
buffer.data = 0; buffer.data = 0;
buffer.length = 0; buffer.length = 0;
@ -203,6 +190,27 @@ public:
~pAudioOpenAL() { ~pAudioOpenAL() {
term(); term();
} }
private:
auto queryDevices() -> lstring {
lstring result;
const char* buffer = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
if(!buffer) return result;
while(buffer[0] || buffer[1]) {
result.append(buffer);
while(buffer[0]) buffer++;
}
return result;
}
auto updateLatency() -> void {
if(buffer.data) delete[] buffer.data;
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5;
buffer.data = new uint32_t[buffer.size]();
}
}; };
DeclareAudio(OpenAL) DeclareAudio(OpenAL)

View File

@ -1,8 +1,3 @@
/*
audio.oss (2007-12-26)
author: Nach
*/
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -16,95 +11,112 @@
//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines //Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines
#ifndef SNDCTL_DSP_COOKEDMODE #ifndef SNDCTL_DSP_COOKEDMODE
#define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int) #define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, signed)
#endif #endif
#ifndef SNDCTL_DSP_POLICY #ifndef SNDCTL_DSP_POLICY
#define SNDCTL_DSP_POLICY _IOW('P', 45, int) #define SNDCTL_DSP_POLICY _IOW('P', 45, signed)
#endif #endif
namespace ruby { namespace ruby {
class pAudioOSS { struct pAudioOSS {
public:
struct { struct {
int fd; signed fd = -1;
int format; signed format = AFMT_S16_LE;
int channels; signed channels = 2;
const char* name;
} device; } device;
struct { struct {
unsigned frequency; string device = "/dev/dsp";
bool synchronize = true;
unsigned frequency = 22050;
} settings; } settings;
bool cap(const string& name) { ~pAudioOSS() {
term();
}
auto cap(const string& name) -> bool {
if(name == Audio::Device) return true;
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true; if(name == Audio::Frequency) return true;
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Audio::Device) return settings.device;
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency; if(name == Audio::Frequency) return settings.frequency;
return false; return false;
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Audio::Device) {
settings.device = any_cast<string>(value);
if(!settings.device) settings.device = "/dev/dsp";
return true;
}
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
updateSynchronization();
return true;
}
if(name == Audio::Frequency) { if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value); settings.frequency = any_cast<unsigned>(value);
if(device.fd > 0) init(); if(device.fd >= 0) init();
return true; return true;
} }
return false; return false;
} }
void sample(uint16_t sl, uint16_t sr) { auto sample(uint16_t left, uint16_t right) -> void {
uint32_t sample = sl + (sr << 16); uint32_t sample = left << 0 | right << 16;
unsigned unused = write(device.fd, &sample, 4); auto unused = write(device.fd, &sample, 4);
} }
void clear() { auto clear() -> void {
} }
bool init() { auto init() -> bool {
term(); term();
device.fd = open(device.name, O_WRONLY, O_NONBLOCK); device.fd = open(settings.device, O_WRONLY, O_NONBLOCK);
if(device.fd < 0) return false; if(device.fd < 0) return false;
#if 1 //SOUND_VERSION >= 0x040000 #if 1 //SOUND_VERSION >= 0x040000
//attempt to enable OSS4-specific features regardless of version //attempt to enable OSS4-specific features regardless of version
//OSS3 ioctl calls will silently fail, but sound will still work //OSS3 ioctl calls will silently fail, but sound will still work
int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage signed cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage
ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked); ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked);
ioctl(device.fd, SNDCTL_DSP_POLICY, &policy); ioctl(device.fd, SNDCTL_DSP_POLICY, &policy);
#endif #endif
int freq = settings.frequency; signed freq = settings.frequency;
ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels); ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels);
ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format); ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format);
ioctl(device.fd, SNDCTL_DSP_SPEED, &freq); ioctl(device.fd, SNDCTL_DSP_SPEED, &freq);
updateSynchronization();
return true; return true;
} }
void term() { auto term() -> void {
if(device.fd > 0) { if(device.fd >= 0) {
close(device.fd); close(device.fd);
device.fd = -1; device.fd = -1;
} }
} }
pAudioOSS() { private:
device.fd = -1; auto updateSynchronization() -> void {
device.format = AFMT_S16_LE; if(device.fd < 0) return;
device.channels = 2; auto flags = fcntl(device.fd, F_GETFL);
device.name = "/dev/dsp"; if(flags < 0) return;
settings.synchronize ? flags &=~ O_NONBLOCK : flags |= O_NONBLOCK;
settings.frequency = 22050; fcntl(device.fd, F_SETFL, flags);
}
~pAudioOSS() {
term();
} }
}; };

View File

@ -19,21 +19,21 @@ using namespace nall;
#define DeclareVideo(Name) \ #define DeclareVideo(Name) \
struct Video##Name : Video { \ struct Video##Name : Video { \
bool cap(const string& name) { return p.cap(name); } \
any get(const string& name) { return p.get(name); } \
bool set(const string& name, const any& value) { return p.set(name, value); } \
\
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return p.lock(data, pitch, width, height); } \
void unlock() { p.unlock(); } \
\
void clear() { p.clear(); } \
void refresh() { p.refresh(); } \
bool init() { return p.init(); } \
void term() { p.term(); } \
\
Video##Name() : p(*new pVideo##Name) {} \ Video##Name() : p(*new pVideo##Name) {} \
~Video##Name() { delete &p; } \ ~Video##Name() { delete &p; } \
\ \
auto cap(const string& name) -> bool { return p.cap(name); } \
auto get(const string& name) -> any { return p.get(name); } \
auto set(const string& name, const any& value) -> bool { return p.set(name, value); } \
\
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { return p.lock(data, pitch, width, height); } \
auto unlock() -> void { p.unlock(); } \
auto clear() -> void { p.clear(); } \
auto refresh() -> void { p.refresh(); } \
\
auto init() -> bool { return p.init(); } \
auto term() -> void { p.term(); } \
\
private: \ private: \
pVideo##Name& p; \ pVideo##Name& p; \
}; };
@ -78,18 +78,19 @@ using namespace nall;
#define DeclareAudio(Name) \ #define DeclareAudio(Name) \
struct Audio##Name : Audio { \ struct Audio##Name : Audio { \
bool cap(const string& name) { return p.cap(name); } \
any get(const string& name) { return p.get(name); } \
bool set(const string& name, const any& value) { return p.set(name, value); } \
\
void sample(uint16_t left, uint16_t right) { p.sample(left, right); } \
void clear() { p.clear(); } \
bool init() { return p.init(); } \
void term() { p.term(); } \
\
Audio##Name() : p(*new pAudio##Name) {} \ Audio##Name() : p(*new pAudio##Name) {} \
~Audio##Name() { delete &p; } \ ~Audio##Name() { delete &p; } \
\ \
auto cap(const string& name) -> bool { return p.cap(name); } \
auto get(const string& name) -> any { return p.get(name); } \
auto set(const string& name, const any& value) -> bool { return p.set(name, value); } \
\
auto sample(uint16_t left, uint16_t right) -> void { p.sample(left, right); } \
auto clear() -> void { p.clear(); } \
\
auto init() -> bool { return p.init(); } \
auto term() -> void { p.term(); } \
\
private: \ private: \
pAudio##Name& p; \ pAudio##Name& p; \
}; };
@ -140,9 +141,9 @@ using namespace nall;
auto acquire() -> bool { return p.acquire(); } \ auto acquire() -> bool { return p.acquire(); } \
auto unacquire() -> bool { return p.unacquire(); } \ auto unacquire() -> bool { return p.unacquire(); } \
auto acquired() -> bool { return p.acquired(); } \ auto acquired() -> bool { return p.acquired(); } \
\
auto poll() -> vector<shared_pointer<HID::Device>> { return p.poll(); } \ auto poll() -> vector<shared_pointer<HID::Device>> { return p.poll(); } \
auto rumble(uint64_t id, bool enable) -> bool { return p.rumble(id, enable); } \ auto rumble(uint64_t id, bool enable) -> bool { return p.rumble(id, enable); } \
\
auto init() -> bool { return p.init(); } \ auto init() -> bool { return p.init(); } \
auto term() -> void { p.term(); } \ auto term() -> void { p.term(); } \
\ \

View File

@ -5,7 +5,6 @@ struct Input {
static const nall::string JoypadSupport; static const nall::string JoypadSupport;
static const nall::string JoypadRumbleSupport; static const nall::string JoypadRumbleSupport;
Input() = default;
virtual ~Input() = default; virtual ~Input() = default;
virtual auto cap(const nall::string& name) -> bool { return false; } virtual auto cap(const nall::string& name) -> bool { return false; }
@ -15,9 +14,9 @@ struct Input {
virtual auto acquire() -> bool { return false; } virtual auto acquire() -> bool { return false; }
virtual auto unacquire() -> bool { return false; } virtual auto unacquire() -> bool { return false; }
virtual auto acquired() -> bool { return false; } virtual auto acquired() -> bool { return false; }
virtual auto poll() -> nall::vector<nall::shared_pointer<nall::HID::Device>> { return {}; } virtual auto poll() -> nall::vector<nall::shared_pointer<nall::HID::Device>> { return {}; }
virtual auto rumble(uint64_t id, bool enable) -> bool { return false; } virtual auto rumble(uint64_t id, bool enable) -> bool { return false; }
virtual auto init() -> bool { return true; } virtual auto init() -> bool { return true; }
virtual auto term() -> void {} virtual auto term() -> void {}
}; };

View File

@ -13,70 +13,70 @@ InputInterface input;
/* VideoInterface */ /* VideoInterface */
const char* Video::Handle = "Handle"; const string Video::Handle = "Handle";
const char* Video::Synchronize = "Synchronize"; const string Video::Synchronize = "Synchronize";
const char* Video::Depth = "Depth"; const string Video::Depth = "Depth";
const char* Video::Filter = "Filter"; const string Video::Filter = "Filter";
const char* Video::Shader = "Shader"; const string Video::Shader = "Shader";
const unsigned Video::FilterNearest = 0; const unsigned Video::FilterNearest = 0;
const unsigned Video::FilterLinear = 1; const unsigned Video::FilterLinear = 1;
void VideoInterface::driver(const char* driver) { auto VideoInterface::driver(string driver) -> void {
if(p) term(); if(p) term();
if(!driver || !*driver) driver = optimalDriver(); if(!driver) driver = optimalDriver();
if(0); if(0);
#ifdef VIDEO_CGL #ifdef VIDEO_CGL
else if(!strcmp(driver, "OpenGL")) p = new VideoCGL(); else if(driver == "OpenGL") p = new VideoCGL();
#endif #endif
#ifdef VIDEO_DIRECT3D #ifdef VIDEO_DIRECT3D
else if(!strcmp(driver, "Direct3D")) p = new VideoD3D(); else if(driver == "Direct3D") p = new VideoD3D();
#endif #endif
#ifdef VIDEO_DIRECTDRAW #ifdef VIDEO_DIRECTDRAW
else if(!strcmp(driver, "DirectDraw")) p = new VideoDD(); else if(driver == "DirectDraw") p = new VideoDD();
#endif #endif
#ifdef VIDEO_GDI #ifdef VIDEO_GDI
else if(!strcmp(driver, "GDI")) p = new VideoGDI(); else if(driver == "GDI") p = new VideoGDI();
#endif #endif
#ifdef VIDEO_GLX #ifdef VIDEO_GLX
else if(!strcmp(driver, "OpenGL")) p = new VideoGLX(); else if(driver == "OpenGL") p = new VideoGLX();
#endif #endif
#ifdef VIDEO_QTOPENGL #ifdef VIDEO_QTOPENGL
else if(!strcmp(driver, "Qt-OpenGL")) p = new VideoQtOpenGL(); else if(driver == "Qt-OpenGL") p = new VideoQtOpenGL();
#endif #endif
#ifdef VIDEO_QTRASTER #ifdef VIDEO_QTRASTER
else if(!strcmp(driver, "Qt-Raster")) p = new VideoQtRaster(); else if(driver == "Qt-Raster") p = new VideoQtRaster();
#endif #endif
#ifdef VIDEO_SDL #ifdef VIDEO_SDL
else if(!strcmp(driver, "SDL")) p = new VideoSDL(); else if(driver == "SDL") p = new VideoSDL();
#endif #endif
#ifdef VIDEO_WGL #ifdef VIDEO_WGL
else if(!strcmp(driver, "OpenGL")) p = new VideoWGL(); else if(driver == "OpenGL") p = new VideoWGL();
#endif #endif
#ifdef VIDEO_XSHM #ifdef VIDEO_XSHM
else if(!strcmp(driver, "XShm")) p = new VideoXShm(); else if(driver == "XShm") p = new VideoXShm();
#endif #endif
#ifdef VIDEO_XV #ifdef VIDEO_XV
else if(!strcmp(driver, "X-Video")) p = new VideoXv(); else if(driver == "X-Video") p = new VideoXv();
#endif #endif
else p = new Video(); else p = new Video();
} }
const char* VideoInterface::optimalDriver() { auto VideoInterface::optimalDriver() -> string {
#if defined(VIDEO_WGL) #if defined(VIDEO_WGL)
return "OpenGL"; return "OpenGL";
#elif defined(VIDEO_DIRECT3D) #elif defined(VIDEO_DIRECT3D)
@ -103,7 +103,7 @@ const char* VideoInterface::optimalDriver() {
#endif #endif
} }
const char* VideoInterface::safestDriver() { auto VideoInterface::safestDriver() -> string {
#if defined(VIDEO_DIRECT3D) #if defined(VIDEO_DIRECT3D)
return "Direct3D"; return "Direct3D";
#elif defined(VIDEO_WGL) #elif defined(VIDEO_WGL)
@ -130,7 +130,7 @@ const char* VideoInterface::safestDriver() {
#endif #endif
} }
const char* VideoInterface::availableDrivers() { auto VideoInterface::availableDrivers() -> string {
return return
//Windows //Windows
@ -178,12 +178,12 @@ const char* VideoInterface::availableDrivers() {
"None"; "None";
} }
bool VideoInterface::init() { auto VideoInterface::init() -> bool {
if(!p) driver(); if(!p) driver();
return p->init(); return p->init();
} }
void VideoInterface::term() { auto VideoInterface::term() -> void {
if(p) { if(p) {
p->term(); p->term();
delete p; delete p;
@ -191,66 +191,66 @@ void VideoInterface::term() {
} }
} }
bool VideoInterface::cap(const string& name) { return p ? p->cap(name) : false; }
any VideoInterface::get(const string& name) { return p ? p->get(name) : false; }
bool VideoInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
bool VideoInterface::lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return p ? p->lock(data, pitch, width, height) : false; }
void VideoInterface::unlock() { if(p) p->unlock(); }
void VideoInterface::clear() { if(p) p->clear(); }
void VideoInterface::refresh() { if(p) p->refresh(); }
VideoInterface::VideoInterface() : p(nullptr) {}
VideoInterface::~VideoInterface() { term(); } VideoInterface::~VideoInterface() { term(); }
auto VideoInterface::cap(const string& name) -> bool { return p ? p->cap(name) : false; }
auto VideoInterface::get(const string& name) -> any { return p ? p->get(name) : false; }
auto VideoInterface::set(const string& name, const any& value) -> bool { return p ? p->set(name, value) : false; }
auto VideoInterface::lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { return p ? p->lock(data, pitch, width, height) : false; }
auto VideoInterface::unlock() -> void { if(p) p->unlock(); }
auto VideoInterface::clear() -> void { if(p) p->clear(); }
auto VideoInterface::refresh() -> void { if(p) p->refresh(); }
/* AudioInterface */ /* AudioInterface */
const char* Audio::Handle = "Handle"; const string Audio::Device = "Device";
const char* Audio::Synchronize = "Synchronize"; const string Audio::Handle = "Handle";
const char* Audio::Frequency = "Frequency"; const string Audio::Synchronize = "Synchronize";
const char* Audio::Latency = "Latency"; const string Audio::Frequency = "Frequency";
const string Audio::Latency = "Latency";
void AudioInterface::driver(const char* driver) { auto AudioInterface::driver(string driver) -> void {
if(p) term(); if(p) term();
if(!driver || !*driver) driver = optimalDriver(); if(!driver) driver = optimalDriver();
if(0); if(0);
#ifdef AUDIO_ALSA #ifdef AUDIO_ALSA
else if(!strcmp(driver, "ALSA")) p = new AudioALSA(); else if(driver == "ALSA") p = new AudioALSA();
#endif #endif
#ifdef AUDIO_AO #ifdef AUDIO_AO
else if(!strcmp(driver, "libao")) p = new AudioAO(); else if(driver == "libao") p = new AudioAO();
#endif #endif
#ifdef AUDIO_DIRECTSOUND #ifdef AUDIO_DIRECTSOUND
else if(!strcmp(driver, "DirectSound")) p = new AudioDS(); else if(driver == "DirectSound") p = new AudioDS();
#endif #endif
#ifdef AUDIO_OPENAL #ifdef AUDIO_OPENAL
else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL(); else if(driver == "OpenAL") p = new AudioOpenAL();
#endif #endif
#ifdef AUDIO_OSS #ifdef AUDIO_OSS
else if(!strcmp(driver, "OSS")) p = new AudioOSS(); else if(driver == "OSS") p = new AudioOSS();
#endif #endif
#ifdef AUDIO_PULSEAUDIO #ifdef AUDIO_PULSEAUDIO
else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio(); else if(driver == "PulseAudio") p = new AudioPulseAudio();
#endif #endif
#ifdef AUDIO_PULSEAUDIOSIMPLE #ifdef AUDIO_PULSEAUDIOSIMPLE
else if(!strcmp(driver, "PulseAudioSimple")) p = new AudioPulseAudioSimple(); else if(driver == "PulseAudioSimple") p = new AudioPulseAudioSimple();
#endif #endif
#ifdef AUDIO_XAUDIO2 #ifdef AUDIO_XAUDIO2
else if(!strcmp(driver, "XAudio2")) p = new AudioXAudio2(); else if(driver == "XAudio2") p = new AudioXAudio2();
#endif #endif
else p = new Audio(); else p = new Audio();
} }
const char* AudioInterface::optimalDriver() { auto AudioInterface::optimalDriver() -> string {
#if defined(AUDIO_XAUDIO2) #if defined(AUDIO_XAUDIO2)
return "XAudio2"; return "XAudio2";
#elif defined(AUDIO_DIRECTSOUND) #elif defined(AUDIO_DIRECTSOUND)
@ -274,7 +274,7 @@ const char* AudioInterface::optimalDriver() {
#endif #endif
} }
const char* AudioInterface::safestDriver() { auto AudioInterface::safestDriver() -> string {
#if defined(AUDIO_DIRECTSOUND) #if defined(AUDIO_DIRECTSOUND)
return "DirectSound"; return "DirectSound";
#elif defined(AUDIO_XAUDIO2) #elif defined(AUDIO_XAUDIO2)
@ -298,7 +298,7 @@ const char* AudioInterface::safestDriver() {
#endif #endif
} }
const char* AudioInterface::availableDrivers() { auto AudioInterface::availableDrivers() -> string {
return return
//Windows //Windows
@ -340,12 +340,12 @@ const char* AudioInterface::availableDrivers() {
"None"; "None";
} }
bool AudioInterface::init() { auto AudioInterface::init() -> bool {
if(!p) driver(); if(!p) driver();
return p->init(); return p->init();
} }
void AudioInterface::term() { auto AudioInterface::term() -> void {
if(p) { if(p) {
p->term(); p->term();
delete p; delete p;
@ -353,13 +353,12 @@ void AudioInterface::term() {
} }
} }
bool AudioInterface::cap(const string& name) { return p ? p->cap(name) : false; }
any AudioInterface::get(const string& name) { return p ? p->get(name) : false; }
bool AudioInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
void AudioInterface::sample(uint16_t left, uint16_t right) { if(p) p->sample(left, right); }
void AudioInterface::clear() { if(p) p->clear(); }
AudioInterface::AudioInterface() : p(nullptr) {}
AudioInterface::~AudioInterface() { term(); } AudioInterface::~AudioInterface() { term(); }
auto AudioInterface::cap(const string& name) -> bool { return p ? p->cap(name) : false; }
auto AudioInterface::get(const string& name) -> any { return p ? p->get(name) : false; }
auto AudioInterface::set(const string& name, const any& value) -> bool { return p ? p->set(name, value) : false; }
auto AudioInterface::sample(uint16_t left, uint16_t right) -> void { if(p) p->sample(left, right); }
auto AudioInterface::clear() -> void { if(p) p->clear(); }
/* InputInterface */ /* InputInterface */

View File

@ -19,47 +19,45 @@ namespace ruby {
#include <ruby/input.hpp> #include <ruby/input.hpp>
struct VideoInterface { struct VideoInterface {
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
const char* availableDrivers();
bool init();
void term();
bool cap(const nall::string& name);
nall::any get(const nall::string& name);
bool set(const nall::string& name, const nall::any& value);
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height);
void unlock();
void clear();
void refresh();
VideoInterface();
~VideoInterface(); ~VideoInterface();
auto driver(nall::string driver = "") -> void;
auto optimalDriver() -> nall::string;
auto safestDriver() -> nall::string;
auto availableDrivers() -> nall::string;
auto init() -> bool;
auto term() -> void;
auto cap(const nall::string& name) -> bool;
auto get(const nall::string& name) -> nall::any;
auto set(const nall::string& name, const nall::any& value) -> bool;
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool;
auto unlock() -> void;
auto clear() -> void;
auto refresh() -> void;
private: private:
Video* p = nullptr; Video* p = nullptr;
}; };
struct AudioInterface { struct AudioInterface {
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
const char* availableDrivers();
bool init();
void term();
bool cap(const nall::string& name);
nall::any get(const nall::string& name);
bool set(const nall::string& name, const nall::any& value);
void sample(uint16_t left, uint16_t right);
void clear();
AudioInterface();
~AudioInterface(); ~AudioInterface();
auto driver(nall::string driver = "") -> void;
auto optimalDriver() -> nall::string;
auto safestDriver() -> nall::string;
auto availableDrivers() -> nall::string;
auto init() -> bool;
auto term() -> void;
auto cap(const nall::string& name) -> bool;
auto get(const nall::string& name) -> nall::any;
auto set(const nall::string& name, const nall::any& value) -> bool;
auto sample(uint16_t left, uint16_t right) -> void;
auto clear() -> void;
private: private:
Audio* p = nullptr; Audio* p = nullptr;
}; };
@ -83,7 +81,6 @@ struct InputInterface {
auto acquire() -> bool; auto acquire() -> bool;
auto unacquire() -> bool; auto unacquire() -> bool;
auto acquired() -> bool; auto acquired() -> bool;
auto poll() -> nall::vector<nall::shared_pointer<nall::HID::Device>>; auto poll() -> nall::vector<nall::shared_pointer<nall::HID::Device>>;
auto rumble(uint64_t id, bool enable) -> bool; auto rumble(uint64_t id, bool enable) -> bool;

View File

@ -1,25 +1,24 @@
struct Video { struct Video {
static const char* Handle; static const nall::string Handle;
static const char* Synchronize; static const nall::string Synchronize;
static const char* Depth; static const nall::string Depth;
static const char* Filter; static const nall::string Filter;
static const char* Shader; static const nall::string Shader;
static const unsigned FilterNearest; static const unsigned FilterNearest;
static const unsigned FilterLinear; static const unsigned FilterLinear;
virtual bool cap(const nall::string& name) { return false; } virtual ~Video() = default;
virtual nall::any get(const nall::string& name) { return false; }
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
virtual bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return false; } virtual auto cap(const nall::string& name) -> bool { return false; }
virtual void unlock() {} virtual auto get(const nall::string& name) -> nall::any { return false; }
virtual auto set(const nall::string& name, const nall::any& value) -> bool { return false; }
virtual void clear() {} virtual auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { return false; }
virtual void refresh() {} virtual auto unlock() -> void {}
virtual bool init() { return true; } virtual auto clear() -> void {}
virtual void term() {} virtual auto refresh() -> void {}
Video() {} virtual auto init() -> bool { return true; }
virtual ~Video() {} virtual auto term() -> void {}
}; };

View File

@ -24,6 +24,7 @@ ConfigurationManager::ConfigurationManager() {
append(video, "Video"); append(video, "Video");
audio.append(audio.driver, "Driver"); audio.append(audio.driver, "Driver");
audio.append(audio.device, "Device");
audio.append(audio.synchronize, "Synchronize"); audio.append(audio.synchronize, "Synchronize");
audio.append(audio.mute, "Mute"); audio.append(audio.mute, "Mute");
append(audio, "Audio"); append(audio, "Audio");

View File

@ -27,6 +27,7 @@ struct ConfigurationManager : Configuration::Document {
struct Audio : Configuration::Node { struct Audio : Configuration::Node {
string driver; string driver;
string device;
bool synchronize = true; bool synchronize = true;
bool mute = false; bool mute = false;
} audio; } audio;

View File

@ -1,5 +1,16 @@
//request from emulation core to load non-volatile media folder //request from emulation core to load non-volatile media folder
auto Program::loadRequest(unsigned id, string name, string type) -> void { auto Program::loadRequest(unsigned id, string name, string type) -> void {
string location = BrowserDialog()
.setParent(*presentation)
.setTitle({"Load ", name})
.setPath({config().library.location, name})
.setFilters({string{name, "|*.", type}})
.selectFolder();
if(!directory::exists(location)) return;
mediaPaths(id) = location;
folderPaths.append(location);
emulator->load(id);
} }
//request from emulation core to load non-volatile media file //request from emulation core to load non-volatile media file

View File

@ -36,6 +36,7 @@ Program::Program() {
if(!video.init()) { video.driver("None"); video.init(); } if(!video.init()) { video.driver("None"); video.init(); }
audio.driver(config().audio.driver); audio.driver(config().audio.driver);
audio.set(Audio::Device, config().audio.device);
audio.set(Audio::Handle, presentation->viewport.handle()); audio.set(Audio::Handle, presentation->viewport.handle());
audio.set(Audio::Synchronize, config().audio.synchronize); audio.set(Audio::Synchronize, config().audio.synchronize);
audio.set(Audio::Frequency, 96000u); audio.set(Audio::Frequency, 96000u);

View File

@ -8,6 +8,12 @@ HotkeySettings::HotkeySettings(TabFrame* parent) : TabFrameItem(parent) {
mappingList.onChange([&] { mappingList.onChange([&] {
eraseButton.setEnabled((bool)mappingList.selected()); eraseButton.setEnabled((bool)mappingList.selected());
}); });
resetButton.setText("Reset").onActivate([&] {
if(MessageDialog("Are you sure you want to erase all hotkey mappings?").setParent(*settingsManager).question() == 0) {
for(auto& mapping : inputManager->hotkeys) mapping->unbind();
refreshMappings();
}
});
eraseButton.setText("Erase").onActivate([&] { eraseButton.setText("Erase").onActivate([&] {
if(auto item = mappingList.selected()) { if(auto item = mappingList.selected()) {
inputManager->hotkeys[item->offset()]->unbind(); inputManager->hotkeys[item->offset()]->unbind();
@ -43,6 +49,7 @@ auto HotkeySettings::assignMapping() -> void {
if(auto item = mappingList.selected()) { if(auto item = mappingList.selected()) {
activeMapping = inputManager->hotkeys[item->offset()]; activeMapping = inputManager->hotkeys[item->offset()];
settingsManager->layout.setEnabled(false);
settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."}); settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
} }
} }
@ -54,6 +61,7 @@ auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, unsigned gro
if(activeMapping->bind(device, group, input, oldValue, newValue)) { if(activeMapping->bind(device, group, input, oldValue, newValue)) {
activeMapping = nullptr; activeMapping = nullptr;
settingsManager->statusBar.setText(""); settingsManager->statusBar.setText("");
settingsManager->layout.setEnabled(true);
refreshMappings(); refreshMappings();
} }
} }

View File

@ -102,11 +102,11 @@ auto InputSettings::refreshMappings() -> void {
auto InputSettings::assignMapping() -> void { auto InputSettings::assignMapping() -> void {
inputManager->poll(); //clear any pending events first inputManager->poll(); //clear any pending events first
auto mapping = mappingList.selected(); if(auto mapping = mappingList.selected()) {
activeMapping = activeDevice().mappings[mapping->offset()]; activeMapping = activeDevice().mappings[mapping->offset()];
settingsManager->layout.setEnabled(false);
//settingsManager->layout.setEnabled(false);
settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."}); settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
}
} }
auto InputSettings::assignMouseInput(unsigned id) -> void { auto InputSettings::assignMouseInput(unsigned id) -> void {
@ -130,7 +130,7 @@ auto InputSettings::inputEvent(shared_pointer<HID::Device> device, unsigned grou
if(activeMapping->bind(device, group, input, oldValue, newValue)) { if(activeMapping->bind(device, group, input, oldValue, newValue)) {
activeMapping = nullptr; activeMapping = nullptr;
settingsManager->statusBar.setText(""); settingsManager->statusBar.setText("");
//settingsManager->layout.setEnabled(true); //todo: this isn't enabling child widgets properly (bug in hiro) settingsManager->layout.setEnabled(true);
refreshMappings(); refreshMappings();
} }
} }

View File

@ -42,6 +42,7 @@ struct HotkeySettings : TabFrameItem {
ListView mappingList{&layout, Size{~0, ~0}}; ListView mappingList{&layout, Size{~0, ~0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget spacer{&controlLayout, Size{~0, 0}}; Widget spacer{&controlLayout, Size{~0, 0}};
Button resetButton{&controlLayout, Size{80, 0}};
Button eraseButton{&controlLayout, Size{80, 0}}; Button eraseButton{&controlLayout, Size{80, 0}};
}; };