o add my bsnes branch

This commit is contained in:
David Voswinkel
2009-05-12 20:05:00 +02:00
parent 1c30d3db56
commit 091b82a7ef
402 changed files with 68660 additions and 0 deletions

28
tools/bsnes/lib/ruby/audio.hpp Executable file
View File

@@ -0,0 +1,28 @@
class Audio {
public:
enum Setting {
//AudioInterface settings
Volume,
Resample,
ResampleOutputFrequency,
ResampleInputFrequency,
//Audio settings
Handle,
Synchronize,
Frequency,
Latency,
};
virtual bool cap(Setting) { return false; }
virtual uintptr_t get(Setting) { return false; }
virtual bool set(Setting, uintptr_t) { return false; }
virtual void sample(uint16_t left, uint16_t right) {}
virtual void clear() {}
virtual bool init() { return true; }
virtual void term() {}
Audio() {}
virtual ~Audio() {}
};

View File

@@ -0,0 +1,237 @@
#include <alsa/asoundlib.h>
namespace ruby {
#include "alsa.hpp"
class pAudioALSA {
public:
AudioALSA &self;
struct {
snd_pcm_t *handle;
snd_pcm_format_t format;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
int channels;
const char *name;
} device;
struct {
uint32_t *data;
unsigned length;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Synchronize) {
if(settings.synchronize != param) {
settings.synchronize = param;
if(device.handle) {
term();
init();
}
}
return true;
}
if(setting == Audio::Frequency) {
if(settings.frequency != param) {
settings.frequency = param;
if(device.handle) {
term();
init();
}
}
return true;
}
if(setting == Audio::Latency) {
if(settings.latency != param) {
settings.latency = param;
if(device.handle) {
term();
init();
}
}
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
if(!device.handle) return;
buffer.data[buffer.length++] = left + (right << 16);
if(buffer.length < device.period_size) return;
if(settings.synchronize == false) {
snd_pcm_avail_update(device.handle);
snd_pcm_sframes_t delay;
snd_pcm_delay(device.handle, &delay);
if(delay < 0) {
snd_pcm_prepare(device.handle);
} else if(delay > device.buffer_size - device.period_size) {
buffer.length = 0;
return;
}
}
uint32_t *buffer_ptr = buffer.data;
int i = 4;
while((buffer.length > 0) && i--) {
snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length);
if(written < 0) {
//no samples written
snd_pcm_recover(device.handle, written, 1);
} else if(written <= buffer.length) {
buffer.length -= written;
buffer_ptr += written;
}
}
if(i < 0) {
if(buffer.data == buffer_ptr) {
buffer.length--;
buffer_ptr++;
}
memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t));
}
}
bool init() {
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
term();
return false;
}
/* //below code will not work with 24khz frequency rate (ALSA library bug)
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED,
device.channels, settings.frequency, 1, settings.latency * 100) < 0) {
//failed to set device parameters
term();
return false;
}
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
device.period_size = settings.latency * 100 * 1e-6 * settings.frequency / 4;
}*/
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
unsigned rate = settings.frequency;
unsigned buffer_time = settings.latency * 100;
unsigned period_time = settings.latency * 100 / 4;
snd_pcm_hw_params_alloca(&hwparams);
if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) {
term();
return false;
}
if(snd_pcm_hw_params_set_access(device.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0
|| snd_pcm_hw_params_set_format(device.handle, hwparams, device.format) < 0
|| snd_pcm_hw_params_set_channels(device.handle, hwparams, device.channels) < 0
|| snd_pcm_hw_params_set_rate_near(device.handle, hwparams, &rate, 0) < 0
|| snd_pcm_hw_params_set_period_time_near(device.handle, hwparams, &period_time, 0) < 0
|| snd_pcm_hw_params_set_buffer_time_near(device.handle, hwparams, &buffer_time, 0) < 0
) {
term();
return false;
}
if(snd_pcm_hw_params(device.handle, hwparams) < 0) {
term();
return false;
}
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
term();
return false;
}
snd_pcm_sw_params_alloca(&swparams);
if(snd_pcm_sw_params_current(device.handle, swparams) < 0) {
term();
return false;
}
if(snd_pcm_sw_params_set_start_threshold(device.handle, swparams,
(device.buffer_size / device.period_size) * device.period_size) < 0
) {
term();
return false;
}
if(snd_pcm_sw_params(device.handle, swparams) < 0) {
term();
return false;
}
buffer.data = new uint32_t[device.period_size];
return true;
}
void term() {
if(device.handle) {
snd_pcm_drain(device.handle);
snd_pcm_close(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioALSA(AudioALSA &self_) : self(self_) {
device.handle = 0;
device.format = SND_PCM_FORMAT_S16_LE;
device.channels = 2;
device.name = "default";
buffer.data = 0;
buffer.length = 0;
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 60;
}
~pAudioALSA() {
term();
}
};
bool AudioALSA::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioALSA::get(Setting setting) { return p.get(setting); }
bool AudioALSA::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioALSA::sample(uint16_t left, uint16_t right) { p.sample(left, right); }
bool AudioALSA::init() { return p.init(); }
void AudioALSA::term() { p.term(); }
AudioALSA::AudioALSA() : p(*new pAudioALSA(*this)) {}
AudioALSA::~AudioALSA() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,23 @@
/*
audio.alsa (2008-08-12)
authors: Nach, RedDwarf
*/
class pAudioALSA;
class AudioALSA : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t left, uint16_t right);
bool init();
void term();
AudioALSA();
~AudioALSA();
private:
pAudioALSA &p;
};

View File

@@ -0,0 +1,97 @@
#include <ao/ao.h>
namespace ruby {
#include "ao.hpp"
class pAudioAO {
public:
AudioAO &self;
int driver_id;
ao_sample_format driver_format;
ao_device *audio_device;
struct {
unsigned frequency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Frequency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Frequency) return settings.frequency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Frequency) {
settings.frequency = param;
if(audio_device) {
term();
init();
}
return true;
}
return false;
}
void sample(uint16_t l_sample, uint16_t r_sample) {
uint32_t samp = (l_sample << 0) + (r_sample << 16);
ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian
}
bool init() {
driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver)
if(driver_id < 0) return false;
driver_format.bits = 16;
driver_format.channels = 2;
driver_format.rate = settings.frequency;
driver_format.byte_format = AO_FMT_LITTLE;
ao_option *options = 0;
ao_info *di = ao_driver_info(driver_id);
if(!di) return false;
if(!strcmp(di->short_name, "alsa")) {
ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms)
}
audio_device = ao_open_live(driver_id, &driver_format, options);
if(!audio_device) return false;
return true;
}
void term() {
if(audio_device) {
ao_close(audio_device);
audio_device = 0;
}
}
pAudioAO(AudioAO &self_) : self(self_) {
audio_device = 0;
ao_initialize();
settings.frequency = 22050;
}
~pAudioAO() {
term();
//ao_shutdown(); //FIXME: this is causing a segfault for some reason when called ...
}
};
bool AudioAO::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioAO::get(Setting setting) { return p.get(setting); }
bool AudioAO::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioAO::sample(uint16_t l_sample, uint16_t r_sample) { p.sample(l_sample, r_sample); }
bool AudioAO::init() { return p.init(); }
void AudioAO::term() { p.term(); }
AudioAO::AudioAO() : p(*new pAudioAO(*this)) {}
AudioAO::~AudioAO() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,23 @@
/*
audio.ao (2008-06-01)
authors: Nach, RedDwarf
*/
class pAudioAO;
class AudioAO : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t left, uint16_t right);
bool init();
void term();
AudioAO();
~AudioAO();
private:
pAudioAO &p;
};

View File

@@ -0,0 +1,220 @@
#include <windows.h>
#include <dsound.h>
namespace ruby {
#include "directsound.hpp"
class pAudioDS {
public:
AudioDS &self;
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb_p, dsb_b;
DSBUFFERDESC dsbd;
WAVEFORMATEX wfx;
struct {
unsigned rings;
unsigned latency;
uint32_t *buffer;
unsigned bufferoffset;
unsigned readring;
unsigned writering;
int distance;
} device;
struct {
HWND handle;
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Handle) return true;
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Handle) return (uintptr_t)settings.handle;
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Handle) {
settings.handle = (HWND)param;
return true;
}
if(setting == Audio::Synchronize) {
settings.synchronize = param;
if(ds) clear();
return true;
}
if(setting == Audio::Frequency) {
settings.frequency = param;
if(ds) init();
return true;
}
if(setting == Audio::Latency) {
settings.latency = param;
if(ds) init();
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
device.buffer[device.bufferoffset++] = left + (right << 16);
if(device.bufferoffset < device.latency) return;
device.bufferoffset = 0;
DWORD pos, size;
void *output;
if(settings.synchronize == true) {
//wait until playback buffer has an empty ring to write new audio data to
while(device.distance >= device.rings - 1) {
dsb_b->GetCurrentPosition(&pos, 0);
unsigned activering = pos / (device.latency * 4);
if(activering == device.readring) {
if(video.get(Video::Synchronize) == false) Sleep(1);
continue;
}
//subtract number of played rings from ring distance counter
device.distance -= (device.rings + activering - device.readring) % device.rings;
device.readring = activering;
if(device.distance < 2) {
//buffer underflow; set max distance to recover quickly
device.distance = device.rings - 1;
device.writering = (device.rings + device.readring - 1) % device.rings;
break;
}
}
}
device.writering = (device.writering + 1) % device.rings;
device.distance = (device.distance + 1) % device.rings;
if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) {
memcpy(output, device.buffer, device.latency * 4);
dsb_b->Unlock(output, size, 0, 0);
}
}
void clear() {
device.readring = 0;
device.writering = device.rings - 1;
device.distance = device.rings - 1;
device.bufferoffset = 0;
if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4);
if(!dsb_b) return;
dsb_b->Stop();
dsb_b->SetCurrentPosition(0);
DWORD size;
void *output;
dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0);
memset(output, 0, size);
dsb_b->Unlock(output, size, 0, 0);
dsb_b->Play(0, 0, DSBPLAY_LOOPING);
}
bool init() {
term();
device.rings = 8;
device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5;
device.buffer = new uint32_t[device.latency * device.rings];
device.bufferoffset = 0;
DirectSoundCreate(0, &ds, 0);
ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = 0;
ds->CreateSoundBuffer(&dsbd, &dsb_p, 0);
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = settings.frequency;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
dsb_p->SetFormat(&wfx);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t);
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b, 0);
dsb_b->SetFrequency(settings.frequency);
dsb_b->SetCurrentPosition(0);
clear();
return true;
}
void term() {
if(device.buffer) {
delete[] device.buffer;
device.buffer = 0;
}
if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; }
if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; }
if(ds) { ds->Release(); ds = 0; }
}
pAudioDS(AudioDS &self_) : self(self_) {
ds = 0;
dsb_p = 0;
dsb_b = 0;
device.buffer = 0;
device.bufferoffset = 0;
device.readring = 0;
device.writering = 0;
device.distance = 0;
settings.handle = GetDesktopWindow();
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 120;
}
};
bool AudioDS::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioDS::get(Setting setting) { return p.get(setting); }
bool AudioDS::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioDS::sample(uint16_t left, uint16_t right) { p.sample(left, right); }
void AudioDS::clear() { p.clear(); }
bool AudioDS::init() { return p.init(); }
void AudioDS::term() { p.term(); }
AudioDS::AudioDS() : p(*new pAudioDS(*this)) {}
AudioDS::~AudioDS() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,24 @@
/*
audio.directsound (2007-12-26)
author: byuu
*/
class pAudioDS;
class AudioDS : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t left, uint16_t right);
void clear();
bool init();
void term();
AudioDS();
~AudioDS();
private:
pAudioDS &p;
};

View File

@@ -0,0 +1,207 @@
#include <AL/al.h>
#include <AL/alc.h>
namespace ruby {
#include "openal.hpp"
class pAudioOpenAL {
public:
AudioOpenAL &self;
struct {
ALCdevice *handle;
ALCcontext *context;
ALuint source;
ALenum format;
unsigned latency;
unsigned queue_length;
} device;
struct {
uint32_t *data;
unsigned length;
unsigned size;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Synchronize) {
settings.synchronize = param;
return true;
}
if(setting == Audio::Frequency) {
settings.frequency = param;
return true;
}
if(setting == Audio::Latency) {
if(settings.latency != param) {
settings.latency = param;
update_latency();
}
return true;
}
return false;
}
void sample(uint16_t sl, uint16_t sr) {
buffer.data[buffer.length++] = sl + (sr << 16);
if(buffer.length < buffer.size) return;
ALuint albuffer = 0;
int processed = 0;
while(true) {
alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
//wait for buffer playback to catch up to sample generation if not synchronizing
if(settings.synchronize == false || device.queue_length < 3) break;
}
if(device.queue_length < 3) {
alGenBuffers(1, &albuffer);
alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency);
alSourceQueueBuffers(device.source, 1, &albuffer);
device.queue_length++;
}
ALint playing;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing != AL_PLAYING) alSourcePlay(device.source);
buffer.length = 0;
}
void update_latency() {
if(buffer.data) delete[] buffer.data;
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5;
buffer.data = new uint32_t[buffer.size];
}
bool init() {
update_latency();
device.queue_length = 0;
bool success = false;
if(device.handle = alcOpenDevice(NULL)) {
if(device.context = alcCreateContext(device.handle, NULL)) {
alcMakeContextCurrent(device.context);
alGenSources(1, &device.source);
//alSourcef (device.source, AL_PITCH, 1.0);
//alSourcef (device.source, AL_GAIN, 1.0);
//alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0);
//alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0);
//alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE);
alListener3f(AL_POSITION, 0.0, 0.0, 0.0);
alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0);
ALfloat listener_orientation[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
alListenerfv(AL_ORIENTATION, listener_orientation);
success = true;
}
}
if(success == false) {
term();
return false;
}
return true;
}
void term() {
if(alIsSource(device.source) == AL_TRUE) {
int playing = 0;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing == AL_PLAYING) {
alSourceStop(device.source);
int queued = 0;
alGetSourcei(device.source, AL_BUFFERS_QUEUED, &queued);
while(queued--) {
ALuint albuffer = 0;
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
}
alDeleteSources(1, &device.source);
device.source = 0;
}
if(device.context) {
alcMakeContextCurrent(NULL);
alcDestroyContext(device.context);
device.context = 0;
}
if(device.handle) {
alcCloseDevice(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioOpenAL(AudioOpenAL &self_) : self(self_) {
device.source = 0;
device.handle = 0;
device.context = 0;
device.format = AL_FORMAT_STEREO16;
device.queue_length = 0;
buffer.data = 0;
buffer.length = 0;
buffer.size = 0;
settings.synchronize = true;
settings.frequency = 22050;
settings.latency = 40;
}
~pAudioOpenAL() {
term();
}
};
bool AudioOpenAL::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioOpenAL::get(Setting setting) { return p.get(setting); }
bool AudioOpenAL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioOpenAL::sample(uint16_t sl, uint16_t sr) { p.sample(sl, sr); }
bool AudioOpenAL::init() { return p.init(); }
void AudioOpenAL::term() { p.term(); }
AudioOpenAL::AudioOpenAL() : p(*new pAudioOpenAL(*this)) {}
AudioOpenAL::~AudioOpenAL() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,24 @@
/*
audio.openal (2007-12-26)
author: Nach
contributors: byuu, wertigon, _willow_
*/
class pAudioOpenAL;
class AudioOpenAL : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t sl, uint16_t sr);
bool init();
void term();
AudioOpenAL();
~AudioOpenAL();
private:
pAudioOpenAL &p;
};

View File

@@ -0,0 +1,116 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not
//However, OSS4 soundcard.h does not reside in <sys/>
//Therefore, attempt to manually define SNDCTL values if using OSS3 header
//Note that if the defines below fail to work on any specific platform, one can point soundcard.h
//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h)
//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines
#ifndef SNDCTL_DSP_COOKEDMODE
#define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int)
#endif
#ifndef SNDCTL_DSP_POLICY
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
#endif
namespace ruby {
#include "oss.hpp"
class pAudioOSS {
public:
AudioOSS &self;
struct {
int fd;
int format;
int channels;
const char *name;
} device;
struct {
unsigned frequency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Frequency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Frequency) return settings.frequency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Frequency) {
settings.frequency = param;
if(device.fd > 0) {
term();
init();
}
return true;
}
return false;
}
void sample(uint16_t sl, uint16_t sr) {
uint32_t sample = sl + (sr << 16);
unsigned unused = write(device.fd, &sample, 4);
}
bool init() {
device.fd = open(device.name, O_WRONLY, O_NONBLOCK);
if(device.fd < 0) return false;
#if 1 //SOUND_VERSION >= 0x040000
//attempt to enable OSS4-specific features regardless of version
//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
ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked);
ioctl(device.fd, SNDCTL_DSP_POLICY, &policy);
#endif
int freq = settings.frequency;
ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels);
ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format);
ioctl(device.fd, SNDCTL_DSP_SPEED, &freq);
return true;
}
void term() {
if(device.fd > 0) {
close(device.fd);
device.fd = -1;
}
}
pAudioOSS(AudioOSS &self_) : self(self_) {
device.fd = -1;
device.format = AFMT_S16_LE;
device.channels = 2;
device.name = "/dev/dsp";
settings.frequency = 22050;
}
~pAudioOSS() {
term();
}
};
bool AudioOSS::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioOSS::get(Setting setting) { return p.get(setting); }
bool AudioOSS::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioOSS::sample(uint16_t sl, uint16_t sr) { p.sample(sl, sr); }
bool AudioOSS::init() { return p.init(); }
void AudioOSS::term() { p.term(); }
AudioOSS::AudioOSS() : p(*new pAudioOSS(*this)) {}
AudioOSS::~AudioOSS() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,23 @@
/*
audio.oss (2007-12-26)
author: Nach
*/
class pAudioOSS;
class AudioOSS : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t sl, uint16_t sr);
bool init();
void term();
AudioOSS();
~AudioOSS();
private:
pAudioOSS &p;
};

View File

@@ -0,0 +1,121 @@
#include <pulse/simple.h>
#include <pulse/error.h>
namespace ruby {
#include "pulseaudio.hpp"
class pAudioPulseAudio {
public:
struct {
pa_simple *handle;
pa_sample_spec spec;
} device;
struct {
uint32_t *data;
unsigned offset;
} buffer;
struct {
unsigned frequency;
} settings;
AudioPulseAudio &self;
bool cap(Audio::Setting setting) {
if(setting == Audio::Frequency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Frequency) return settings.frequency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Frequency) {
settings.frequency = param;
if(device.handle) {
term();
init();
}
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
if(!device.handle) return;
buffer.data[buffer.offset++] = left + (right << 16);
if(buffer.offset >= 64) {
int error;
pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error);
buffer.offset = 0;
}
}
bool init() {
device.spec.format = PA_SAMPLE_S16LE;
device.spec.channels = 2;
device.spec.rate = settings.frequency;
int error = 0;
device.handle = pa_simple_new(
0, //default server
"ruby::pulseaudio", //application name
PA_STREAM_PLAYBACK, //direction
0, //default device
"audio", //stream description
&device.spec, //sample format
0, //default channel map
0, //default buffering attributes
&error //error code
);
if(!device.handle) {
fprintf(stderr, "ruby::pulseaudio failed to initialize - %s\n", pa_strerror(error));
return false;
}
buffer.data = new uint32_t[64];
buffer.offset = 0;
return true;
}
void term() {
if(device.handle) {
int error;
pa_simple_flush(device.handle, &error);
pa_simple_free(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioPulseAudio(AudioPulseAudio &self_) : self(self_) {
device.handle = 0;
buffer.data = 0;
settings.frequency = 22050;
}
~pAudioPulseAudio() {
term();
}
};
bool AudioPulseAudio::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioPulseAudio::get(Setting setting) { return p.get(setting); }
bool AudioPulseAudio::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioPulseAudio::sample(uint16_t left, uint16_t right) { return p.sample(left, right); }
bool AudioPulseAudio::init() { return p.init(); }
void AudioPulseAudio::term() { p.term(); }
AudioPulseAudio::AudioPulseAudio() : p(*new pAudioPulseAudio(*this)) {}
AudioPulseAudio::~AudioPulseAudio() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,23 @@
/*
audio.pulseaudio (2008-10-31)
author: byuu
*/
class pAudioPulseAudio;
class AudioPulseAudio : public Audio {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
void sample(uint16_t left, uint16_t right);
bool init();
void term();
AudioPulseAudio();
~AudioPulseAudio();
private:
pAudioPulseAudio &p;
};

24
tools/bsnes/lib/ruby/input.hpp Executable file
View File

@@ -0,0 +1,24 @@
class Input {
public:
enum Setting {
Handle,
KeyboardSupport,
MouseSupport,
JoypadSupport,
};
virtual bool cap(Setting) { return false; }
virtual uintptr_t get(Setting) { return false; }
virtual bool set(Setting, uintptr_t) { return false; }
virtual bool acquire() { return false; }
virtual bool unacquire() { return false; }
virtual bool acquired() { return false; }
virtual bool poll(int16_t *table) { return false; }
virtual bool init() { return true; }
virtual void term() {}
Input() {}
virtual ~Input() {}
};

View File

@@ -0,0 +1,403 @@
#define DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <dinput.h>
namespace ruby {
#include "directinput.hpp"
static BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
static BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
using namespace nall;
class pInputDI {
public:
InputDI &self;
struct {
LPDIRECTINPUT8 context;
LPDIRECTINPUTDEVICE8 keyboard;
LPDIRECTINPUTDEVICE8 mouse;
LPDIRECTINPUTDEVICE8 gamepad[joypad<>::count];
bool mouseacquired;
} device;
struct {
HWND handle;
} settings;
bool cap(Input::Setting setting) {
if(setting == Input::Handle) return true;
if(setting == Input::KeyboardSupport) return true;
if(setting == Input::MouseSupport) return true;
if(setting == Input::JoypadSupport) return true;
return false;
}
uintptr_t get(Input::Setting setting) {
if(setting == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(Input::Setting setting, uintptr_t param) {
if(setting == Input::Handle) {
settings.handle = (HWND)param;
return true;
}
return false;
}
bool poll(int16_t *table) {
memset(table, 0, nall::input_limit * sizeof(int16_t));
//========
//Keyboard
//========
if(device.keyboard) {
uint8_t state[256];
if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) {
device.keyboard->Acquire();
if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) {
memset(state, 0, sizeof state);
}
}
table[keyboard<0>::escape] = (bool)(state[0x01] & 0x80);
table[keyboard<0>::f1 ] = (bool)(state[0x3b] & 0x80);
table[keyboard<0>::f2 ] = (bool)(state[0x3c] & 0x80);
table[keyboard<0>::f3 ] = (bool)(state[0x3d] & 0x80);
table[keyboard<0>::f4 ] = (bool)(state[0x3e] & 0x80);
table[keyboard<0>::f5 ] = (bool)(state[0x3f] & 0x80);
table[keyboard<0>::f6 ] = (bool)(state[0x40] & 0x80);
table[keyboard<0>::f7 ] = (bool)(state[0x41] & 0x80);
table[keyboard<0>::f8 ] = (bool)(state[0x42] & 0x80);
table[keyboard<0>::f9 ] = (bool)(state[0x43] & 0x80);
table[keyboard<0>::f10 ] = (bool)(state[0x44] & 0x80);
table[keyboard<0>::f11 ] = (bool)(state[0x57] & 0x80);
table[keyboard<0>::f12 ] = (bool)(state[0x58] & 0x80);
table[keyboard<0>::print_screen] = (bool)(state[0xb7] & 0x80);
table[keyboard<0>::scroll_lock ] = (bool)(state[0x46] & 0x80);
table[keyboard<0>::pause ] = (bool)(state[0xc5] & 0x80);
table[keyboard<0>::tilde ] = (bool)(state[0x29] & 0x80);
table[keyboard<0>::num_1] = (bool)(state[0x02] & 0x80);
table[keyboard<0>::num_2] = (bool)(state[0x03] & 0x80);
table[keyboard<0>::num_3] = (bool)(state[0x04] & 0x80);
table[keyboard<0>::num_4] = (bool)(state[0x05] & 0x80);
table[keyboard<0>::num_5] = (bool)(state[0x06] & 0x80);
table[keyboard<0>::num_6] = (bool)(state[0x07] & 0x80);
table[keyboard<0>::num_7] = (bool)(state[0x08] & 0x80);
table[keyboard<0>::num_8] = (bool)(state[0x09] & 0x80);
table[keyboard<0>::num_9] = (bool)(state[0x0a] & 0x80);
table[keyboard<0>::num_0] = (bool)(state[0x0b] & 0x80);
table[keyboard<0>::dash ] = (bool)(state[0x0c] & 0x80);
table[keyboard<0>::equal ] = (bool)(state[0x0d] & 0x80);
table[keyboard<0>::backspace] = (bool)(state[0x0e] & 0x80);
table[keyboard<0>::insert ] = (bool)(state[0xd2] & 0x80);
table[keyboard<0>::delete_ ] = (bool)(state[0xd3] & 0x80);
table[keyboard<0>::home ] = (bool)(state[0xc7] & 0x80);
table[keyboard<0>::end ] = (bool)(state[0xcf] & 0x80);
table[keyboard<0>::page_up ] = (bool)(state[0xc9] & 0x80);
table[keyboard<0>::page_down] = (bool)(state[0xd1] & 0x80);
table[keyboard<0>::a] = (bool)(state[0x1e] & 0x80);
table[keyboard<0>::b] = (bool)(state[0x30] & 0x80);
table[keyboard<0>::c] = (bool)(state[0x2e] & 0x80);
table[keyboard<0>::d] = (bool)(state[0x20] & 0x80);
table[keyboard<0>::e] = (bool)(state[0x12] & 0x80);
table[keyboard<0>::f] = (bool)(state[0x21] & 0x80);
table[keyboard<0>::g] = (bool)(state[0x22] & 0x80);
table[keyboard<0>::h] = (bool)(state[0x23] & 0x80);
table[keyboard<0>::i] = (bool)(state[0x17] & 0x80);
table[keyboard<0>::j] = (bool)(state[0x24] & 0x80);
table[keyboard<0>::k] = (bool)(state[0x25] & 0x80);
table[keyboard<0>::l] = (bool)(state[0x26] & 0x80);
table[keyboard<0>::m] = (bool)(state[0x32] & 0x80);
table[keyboard<0>::n] = (bool)(state[0x31] & 0x80);
table[keyboard<0>::o] = (bool)(state[0x18] & 0x80);
table[keyboard<0>::p] = (bool)(state[0x19] & 0x80);
table[keyboard<0>::q] = (bool)(state[0x10] & 0x80);
table[keyboard<0>::r] = (bool)(state[0x13] & 0x80);
table[keyboard<0>::s] = (bool)(state[0x1f] & 0x80);
table[keyboard<0>::t] = (bool)(state[0x14] & 0x80);
table[keyboard<0>::u] = (bool)(state[0x16] & 0x80);
table[keyboard<0>::v] = (bool)(state[0x2f] & 0x80);
table[keyboard<0>::w] = (bool)(state[0x11] & 0x80);
table[keyboard<0>::x] = (bool)(state[0x2d] & 0x80);
table[keyboard<0>::y] = (bool)(state[0x15] & 0x80);
table[keyboard<0>::z] = (bool)(state[0x2c] & 0x80);
table[keyboard<0>::lbracket ] = (bool)(state[0x1a] & 0x80);
table[keyboard<0>::rbracket ] = (bool)(state[0x1b] & 0x80);
table[keyboard<0>::backslash ] = (bool)(state[0x2b] & 0x80);
table[keyboard<0>::semicolon ] = (bool)(state[0x27] & 0x80);
table[keyboard<0>::apostrophe] = (bool)(state[0x28] & 0x80);
table[keyboard<0>::comma ] = (bool)(state[0x33] & 0x80);
table[keyboard<0>::period ] = (bool)(state[0x34] & 0x80);
table[keyboard<0>::slash ] = (bool)(state[0x35] & 0x80);
table[keyboard<0>::pad_0] = (bool)(state[0x4f] & 0x80);
table[keyboard<0>::pad_1] = (bool)(state[0x50] & 0x80);
table[keyboard<0>::pad_2] = (bool)(state[0x51] & 0x80);
table[keyboard<0>::pad_3] = (bool)(state[0x4b] & 0x80);
table[keyboard<0>::pad_4] = (bool)(state[0x4c] & 0x80);
table[keyboard<0>::pad_5] = (bool)(state[0x4d] & 0x80);
table[keyboard<0>::pad_6] = (bool)(state[0x47] & 0x80);
table[keyboard<0>::pad_7] = (bool)(state[0x48] & 0x80);
table[keyboard<0>::pad_8] = (bool)(state[0x49] & 0x80);
table[keyboard<0>::pad_9] = (bool)(state[0x52] & 0x80);
table[keyboard<0>::point] = (bool)(state[0x53] & 0x80);
table[keyboard<0>::add] = (bool)(state[0x4e] & 0x80);
table[keyboard<0>::subtract] = (bool)(state[0x4a] & 0x80);
table[keyboard<0>::multiply] = (bool)(state[0x37] & 0x80);
table[keyboard<0>::divide] = (bool)(state[0xb5] & 0x80);
table[keyboard<0>::enter] = (bool)(state[0x9c] & 0x80);
table[keyboard<0>::num_lock ] = (bool)(state[0x45] & 0x80);
table[keyboard<0>::caps_lock] = (bool)(state[0x3a] & 0x80);
table[keyboard<0>::up ] = (bool)(state[0xc8] & 0x80);
table[keyboard<0>::down ] = (bool)(state[0xd0] & 0x80);
table[keyboard<0>::left ] = (bool)(state[0xcb] & 0x80);
table[keyboard<0>::right] = (bool)(state[0xcd] & 0x80);
table[keyboard<0>::tab ] = (bool)(state[0x0f] & 0x80);
table[keyboard<0>::return_ ] = (bool)(state[0x1c] & 0x80);
table[keyboard<0>::spacebar] = (bool)(state[0x39] & 0x80);
table[keyboard<0>::lctrl ] = (bool)(state[0x1d] & 0x80);
table[keyboard<0>::rctrl ] = (bool)(state[0x9d] & 0x80);
table[keyboard<0>::lalt ] = (bool)(state[0x38] & 0x80);
table[keyboard<0>::ralt ] = (bool)(state[0xb8] & 0x80);
table[keyboard<0>::lshift] = (bool)(state[0x2a] & 0x80);
table[keyboard<0>::rshift] = (bool)(state[0x36] & 0x80);
table[keyboard<0>::lsuper] = (bool)(state[0xdb] & 0x80);
table[keyboard<0>::rsuper] = (bool)(state[0xdc] & 0x80);
table[keyboard<0>::menu ] = (bool)(state[0xdd] & 0x80);
}
//=====
//Mouse
//=====
if(device.mouse) {
DIMOUSESTATE2 state;
if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) {
device.mouse->Acquire();
if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) {
memset(&state, 0, sizeof(DIMOUSESTATE2));
}
}
table[mouse<0>::x] = state.lX;
table[mouse<0>::y] = state.lY;
table[mouse<0>::z] = state.lZ / WHEEL_DELTA;
for(unsigned n = 0; n < mouse<>::buttons; n++) {
table[mouse<0>::button + n] = (bool)state.rgbButtons[n];
}
//on Windows, 0 = left, 1 = right, 2 = middle
//swap middle and right buttons for consistency with Linux
int16_t temp = table[mouse<0>::button + 1];
table[mouse<0>::button + 1] = table[mouse<0>::button + 2];
table[mouse<0>::button + 2] = temp;
}
//=========
//Joypad(s)
//=========
for(unsigned i = 0; i < joypad<>::count; i++) {
if(!device.gamepad[i]) continue;
unsigned index = joypad<>::index(i, joypad<>::none);
if(FAILED(device.gamepad[i]->Poll())) {
device.gamepad[i]->Acquire();
continue;
}
DIJOYSTATE2 state;
device.gamepad[i]->GetDeviceState(sizeof(DIJOYSTATE2), &state);
//POV hats
for(unsigned n = 0; n < min((unsigned)joypad<>::hats, 4); n++) {
//POV value is in clockwise-hundredth degree units.
unsigned pov = state.rgdwPOV[n];
//some drivers report a centered POV hat as -1U, others as 65535U.
//>= 36000 will match both, as well as invalid ranges.
if(pov < 36000) {
if(pov >= 31500 || pov <= 4500) table[index + joypad<>::hat + n] |= joypad<>::hat_up;
if(pov >= 4500 && pov <= 13500) table[index + joypad<>::hat + n] |= joypad<>::hat_right;
if(pov >= 13500 && pov <= 22500) table[index + joypad<>::hat + n] |= joypad<>::hat_down;
if(pov >= 22500 && pov <= 31500) table[index + joypad<>::hat + n] |= joypad<>::hat_left;
}
}
//axes
table[index + joypad<>::axis + 0] = state.lX;
table[index + joypad<>::axis + 1] = state.lY;
table[index + joypad<>::axis + 2] = state.lZ;
table[index + joypad<>::axis + 3] = state.lRx;
table[index + joypad<>::axis + 4] = state.lRy;
table[index + joypad<>::axis + 5] = state.lRz;
//buttons
for(unsigned n = 0; n < min((unsigned)joypad<>::buttons, 128); n++) {
table[index + joypad<>::button + n] = (bool)state.rgbButtons[n];
}
}
return true;
}
bool init_joypad(const DIDEVICEINSTANCE *instance) {
unsigned n;
for(n = 0; n < joypad<>::count; n++) { if(!device.gamepad[n]) break; }
if(n >= joypad<>::count) return DIENUM_STOP;
if(FAILED(device.context->CreateDevice(instance->guidInstance, &device.gamepad[n], 0))) {
return DIENUM_CONTINUE; //continue and try next gamepad
}
device.gamepad[n]->SetDataFormat(&c_dfDIJoystick2);
device.gamepad[n]->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device.gamepad[n]->EnumObjects(DI_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
return DIENUM_CONTINUE;
}
bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) {
signed n;
for(n = joypad<>::count - 1; n >= 0; n--) { if(device.gamepad[n]) break; }
if(n < 0) return DIENUM_STOP;
DIPROPRANGE range;
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYID;
range.diph.dwObj = instance->dwType;
range.lMin = -32768;
range.lMax = +32767;
device.gamepad[n]->SetProperty(DIPROP_RANGE, &range.diph);
return DIENUM_CONTINUE;
}
bool init() {
device.context = 0;
device.keyboard = 0;
device.mouse = 0;
for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0;
device.mouseacquired = false;
DirectInput8Create(GetModuleHandle(0), 0x0800, IID_IDirectInput8, (void**)&device.context, 0);
device.context->CreateDevice(GUID_SysKeyboard, &device.keyboard, 0);
device.keyboard->SetDataFormat(&c_dfDIKeyboard);
device.keyboard->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device.keyboard->Acquire();
device.context->CreateDevice(GUID_SysMouse, &device.mouse, 0);
device.mouse->SetDataFormat(&c_dfDIMouse2);
HRESULT hr = device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device.mouse->Acquire();
device.context->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
return true;
}
void term() {
if(device.keyboard) {
device.keyboard->Unacquire();
device.keyboard->Release();
device.keyboard = 0;
}
if(device.mouse) {
device.mouse->Unacquire();
device.mouse->Release();
device.mouse = 0;
}
for(unsigned i = 0; i < joypad<>::count; i++) {
if(device.gamepad[i]) {
device.gamepad[i]->Unacquire();
device.gamepad[i]->Release();
device.gamepad[i] = 0;
}
}
if(device.context) {
device.context->Release();
device.context = 0;
}
}
bool acquire() {
if(!device.mouse) return false;
if(acquired() == false) {
device.mouse->Unacquire();
device.mouse->SetCooperativeLevel(settings.handle, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
device.mouse->Acquire();
device.mouseacquired = true;
}
return true;
}
bool unacquire() {
if(!device.mouse) return false;
if(acquired() == true) {
device.mouse->Unacquire();
device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device.mouse->Acquire();
device.mouseacquired = false;
}
return true;
}
bool acquired() {
return device.mouseacquired;
}
pInputDI(InputDI &self_) : self(self_) {
device.context = 0;
device.keyboard = 0;
device.mouse = 0;
for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0;
device.mouseacquired = false;
settings.handle = 0;
}
~pInputDI() { term(); }
};
BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) {
return ((pInputDI*)p)->init_joypad(instance);
}
BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) {
return ((pInputDI*)p)->init_axis(instance);
}
bool InputDI::cap(Setting setting) { return p.cap(setting); }
uintptr_t InputDI::get(Setting setting) { return p.get(setting); }
bool InputDI::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool InputDI::acquire() { return p.acquire(); }
bool InputDI::unacquire() { return p.unacquire(); }
bool InputDI::acquired() { return p.acquired(); }
bool InputDI::poll(int16_t *table) { return p.poll(table); }
bool InputDI::init() { return p.init(); }
void InputDI::term() { p.term(); }
InputDI::InputDI() : p(*new pInputDI(*this)) {}
InputDI::~InputDI() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,22 @@
class pInputDI;
class InputDI : public Input {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool acquire();
bool unacquire();
bool acquired();
bool poll(int16_t *table);
bool init();
void term();
InputDI();
~InputDI();
private:
pInputDI &p;
};

View File

@@ -0,0 +1,781 @@
//RawInput driver
//author: byuu
//this driver utilizes RawInput (WM_INPUT) to capture keyboard and mouse input.
//although this requires WinXP or newer, it is the only way to uniquely identify
//and independently map multiple keyboards and mice. DirectInput merges all
//keyboards and mice into one device per.
//
//as WM_INPUT lacks specific RAWINPUT structures for gamepads, giving only raw
//data, and because DirectInput supports up to 16 joypads, DirectInput is used
//for joypad mapping.
//
//further, Xbox 360 controllers are explicitly detected and supported through
//XInput. this is because under DirectInput, the LT / RT (trigger) buttons are
//merged into a single Z-axis -- making it impossible to detect both buttons
//being pressed at the same time. with XInput, the state of both trigger
//buttons can be read independently.
//
//so in essence, this is actually more of a hybrid driver.
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <xinput.h>
namespace ruby {
#include "rawinput.hpp"
DWORD WINAPI RawInputThreadProc(void*);
LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
class RawInput {
public:
HANDLE mutex;
HWND hwnd;
bool initialized;
bool ready;
struct Device {
HANDLE handle;
};
struct Keyboard : Device {
bool state[keyboard<>::length];
void update(RAWINPUT *input) {
unsigned code = input->data.keyboard.MakeCode;
unsigned flags = input->data.keyboard.Flags;
#define map(id, flag, name) if(code == id) state[name] = (bool)(flags == flag);
map(0x0001, 0, keyboard<>::escape)
map(0x003b, 0, keyboard<>::f1)
map(0x003c, 0, keyboard<>::f2)
map(0x003d, 0, keyboard<>::f3)
map(0x003e, 0, keyboard<>::f4)
map(0x003f, 0, keyboard<>::f5)
map(0x0040, 0, keyboard<>::f6)
map(0x0041, 0, keyboard<>::f7)
map(0x0042, 0, keyboard<>::f8)
map(0x0043, 0, keyboard<>::f9)
map(0x0044, 0, keyboard<>::f10)
map(0x0057, 0, keyboard<>::f11)
map(0x0058, 0, keyboard<>::f12)
map(0x0037, 2, keyboard<>::print_screen)
map(0x0046, 0, keyboard<>::scroll_lock)
map(0x001d, 4, keyboard<>::pause)
map(0x0029, 0, keyboard<>::tilde)
map(0x0002, 0, keyboard<>::num_1)
map(0x0003, 0, keyboard<>::num_2)
map(0x0004, 0, keyboard<>::num_3)
map(0x0005, 0, keyboard<>::num_4)
map(0x0006, 0, keyboard<>::num_5)
map(0x0007, 0, keyboard<>::num_6)
map(0x0008, 0, keyboard<>::num_7)
map(0x0009, 0, keyboard<>::num_8)
map(0x000a, 0, keyboard<>::num_9)
map(0x000b, 0, keyboard<>::num_0)
map(0x000c, 0, keyboard<>::dash)
map(0x000d, 0, keyboard<>::equal)
map(0x000e, 0, keyboard<>::backspace)
map(0x0052, 2, keyboard<>::insert)
map(0x0053, 2, keyboard<>::delete_)
map(0x0047, 2, keyboard<>::home)
map(0x004f, 2, keyboard<>::end)
map(0x0049, 2, keyboard<>::page_up)
map(0x0051, 2, keyboard<>::page_down)
map(0x001e, 0, keyboard<>::a)
map(0x0030, 0, keyboard<>::b)
map(0x002e, 0, keyboard<>::c)
map(0x0020, 0, keyboard<>::d)
map(0x0012, 0, keyboard<>::e)
map(0x0021, 0, keyboard<>::f)
map(0x0022, 0, keyboard<>::g)
map(0x0023, 0, keyboard<>::h)
map(0x0017, 0, keyboard<>::i)
map(0x0024, 0, keyboard<>::j)
map(0x0025, 0, keyboard<>::k)
map(0x0026, 0, keyboard<>::l)
map(0x0032, 0, keyboard<>::m)
map(0x0031, 0, keyboard<>::n)
map(0x0018, 0, keyboard<>::o)
map(0x0019, 0, keyboard<>::p)
map(0x0010, 0, keyboard<>::q)
map(0x0013, 0, keyboard<>::r)
map(0x001f, 0, keyboard<>::s)
map(0x0014, 0, keyboard<>::t)
map(0x0016, 0, keyboard<>::u)
map(0x002f, 0, keyboard<>::v)
map(0x0011, 0, keyboard<>::w)
map(0x002d, 0, keyboard<>::x)
map(0x0015, 0, keyboard<>::y)
map(0x002c, 0, keyboard<>::z)
map(0x001a, 0, keyboard<>::lbracket)
map(0x001b, 0, keyboard<>::rbracket)
map(0x002b, 0, keyboard<>::backslash)
map(0x0027, 0, keyboard<>::semicolon)
map(0x0028, 0, keyboard<>::apostrophe)
map(0x0033, 0, keyboard<>::comma)
map(0x0034, 0, keyboard<>::period)
map(0x0035, 0, keyboard<>::slash)
map(0x004f, 0, keyboard<>::pad_1)
map(0x0050, 0, keyboard<>::pad_2)
map(0x0051, 0, keyboard<>::pad_3)
map(0x004b, 0, keyboard<>::pad_4)
map(0x004c, 0, keyboard<>::pad_5)
map(0x004d, 0, keyboard<>::pad_6)
map(0x0047, 0, keyboard<>::pad_7)
map(0x0048, 0, keyboard<>::pad_8)
map(0x0049, 0, keyboard<>::pad_9)
map(0x0052, 0, keyboard<>::pad_0)
map(0x0053, 0, keyboard<>::point)
map(0x001c, 2, keyboard<>::enter)
map(0x004e, 0, keyboard<>::add)
map(0x004a, 0, keyboard<>::subtract)
map(0x0037, 0, keyboard<>::multiply)
map(0x0035, 2, keyboard<>::divide)
map(0x0045, 0, keyboard<>::num_lock)
map(0x003a, 0, keyboard<>::caps_lock)
//pause signals 0x1d:4 + 0x45:0, whereas num_lock signals only 0x45:0.
//this makes it impractical to detect both pause+num_lock independently.
//workaround: always detect pause; detect num_lock only when pause is released.
if(state[keyboard<>::pause]) state[keyboard<>::num_lock] = false;
map(0x0048, 2, keyboard<>::up)
map(0x0050, 2, keyboard<>::down)
map(0x004b, 2, keyboard<>::left)
map(0x004d, 2, keyboard<>::right)
map(0x000f, 0, keyboard<>::tab)
map(0x001c, 0, keyboard<>::return_)
map(0x0039, 0, keyboard<>::spacebar)
map(0x001d, 0, keyboard<>::lctrl)
map(0x001d, 2, keyboard<>::rctrl)
map(0x0038, 0, keyboard<>::lalt)
map(0x0038, 2, keyboard<>::ralt)
map(0x002a, 0, keyboard<>::lshift)
map(0x0036, 0, keyboard<>::rshift)
map(0x005b, 2, keyboard<>::lsuper)
map(0x005c, 2, keyboard<>::rsuper)
map(0x005d, 2, keyboard<>::menu)
#undef map
}
Keyboard() {
for(unsigned i = 0; i < keyboard<>::length; i++) state[i] = false;
}
};
struct Mouse : Device {
signed xDistance;
signed yDistance;
signed zDistance;
unsigned buttonState;
void sync() {
xDistance = 0;
yDistance = 0;
zDistance = 0;
}
void update(RAWINPUT *input) {
if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) {
xDistance += input->data.mouse.lLastX;
yDistance += input->data.mouse.lLastY;
}
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) buttonState |= 1 << 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) buttonState &=~ 1 << 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) buttonState |= 1 << 2; //swap middle and right buttons,
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) buttonState &=~ 1 << 2; //for consistency with Linux:
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) buttonState |= 1 << 1; //left = 0, middle = 1, right = 2
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) buttonState &=~ 1 << 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) buttonState |= 1 << 3;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) buttonState &=~ 1 << 3;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) buttonState |= 1 << 4;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) buttonState &=~ 1 << 4;
if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
zDistance += (int16_t)input->data.mouse.usButtonData;
}
}
Mouse() {
xDistance = yDistance = zDistance = 0;
buttonState = 0;
}
};
//keep track of gamepads for the sole purpose of distinguishing XInput devices
//from all other devices. this is necessary, as DirectInput does not provide
//a way to retrieve the necessary RIDI_DEVICENAME string.
struct Gamepad : Device {
bool isXInputDevice;
uint16_t vendorId;
uint16_t productId;
};
vector<Keyboard> lkeyboard;
vector<Mouse> lmouse;
vector<Gamepad> lgamepad;
LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg == WM_INPUT) {
unsigned size = 0;
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
RAWINPUT *input = new RAWINPUT[size];
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
WaitForSingleObject(mutex, INFINITE);
if(input->header.dwType == RIM_TYPEKEYBOARD) {
for(unsigned i = 0; i < lkeyboard.size(); i++) {
if(input->header.hDevice == lkeyboard[i].handle) {
lkeyboard[i].update(input);
break;
}
}
} else if(input->header.dwType == RIM_TYPEMOUSE) {
for(unsigned i = 0; i < lmouse.size(); i++) {
if(input->header.hDevice == lmouse[i].handle) {
lmouse[i].update(input);
break;
}
}
}
ReleaseMutex(mutex);
//allow propogation of WM_INPUT message
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
delete[] input;
return result;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
//this is used to sort device IDs
struct DevicePool {
HANDLE handle;
char name[4096];
bool operator<(const DevicePool &pool) const { return strcmp(name, pool.name) < 0; }
};
int main() {
//create an invisible window to act as a sink, capturing all WM_INPUT messages
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = RawInputWindowProc;
wc.lpszClassName = "RawInputClass";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
RegisterClass(&wc);
hwnd = CreateWindow("RawInputClass", "RawInputClass", WS_POPUP,
0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
//enumerate all HID devices
unsigned devices = 0;
GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST));
RAWINPUTDEVICELIST *list = new RAWINPUTDEVICELIST[devices];
GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST));
//sort all devices by name. this has two important properties:
//1) it consistently orders peripherals, so mapped IDs remain constant
//2) it sorts the virtual keyboard and mouse to the bottom of the list
// (real devices start with \\?\HID#, virtual with \\?\Root#)
DevicePool pool[devices];
for(unsigned i = 0; i < devices; i++) {
pool[i].handle = list[i].hDevice;
unsigned size = sizeof(pool[i].name) - 1;
GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, &pool[i].name, &size);
}
nall::sort(pool, devices);
delete[] list;
for(unsigned i = 0; i < devices; i++) {
RID_DEVICE_INFO info;
info.cbSize = sizeof(RID_DEVICE_INFO);
unsigned size = info.cbSize;
GetRawInputDeviceInfo(pool[i].handle, RIDI_DEVICEINFO, &info, &size);
if(info.dwType == RIM_TYPEKEYBOARD) {
unsigned n = lkeyboard.size();
lkeyboard[n].handle = pool[i].handle;
} else if(info.dwType == RIM_TYPEMOUSE) {
unsigned n = lmouse.size();
lmouse[n].handle = pool[i].handle;
} else if(info.dwType == RIM_TYPEHID) {
//if this is a gamepad or joystick device ...
if(info.hid.usUsagePage == 1 && (info.hid.usUsage == 4 || info.hid.usUsage == 5)) {
//... then cache device information for later use
unsigned n = lgamepad.size();
lgamepad[n].handle = pool[i].handle;
lgamepad[n].vendorId = (uint16_t)info.hid.dwVendorId;
lgamepad[n].productId = (uint16_t)info.hid.dwProductId;
//per MSDN: XInput devices have "IG_" in their device strings,
//which is how they should be identified.
const char *p = strstr(pool[i].name, "IG_");
lgamepad[n].isXInputDevice = (bool)p;
}
}
}
RAWINPUTDEVICE device[2];
//capture all keyboard input
device[0].usUsagePage = 1;
device[0].usUsage = 6;
device[0].dwFlags = RIDEV_INPUTSINK;
device[0].hwndTarget = hwnd;
//capture all mouse input
device[1].usUsagePage = 1;
device[1].usUsage = 2;
device[1].dwFlags = RIDEV_INPUTSINK;
device[1].hwndTarget = hwnd;
RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE));
WaitForSingleObject(mutex, INFINITE);
ready = true;
ReleaseMutex(mutex);
while(true) {
MSG msg;
GetMessage(&msg, hwnd, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
RawInput() : initialized(false), ready(false) {
}
};
static RawInput rawinput;
DWORD WINAPI RawInputThreadProc(void*) {
return rawinput.main();
}
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return rawinput.window_proc(hwnd, msg, wparam, lparam);
}
class XInput {
public:
struct Gamepad {
unsigned id;
int16_t hat;
int16_t axis[6];
bool button[10];
void poll(XINPUT_STATE &state) {
hat = joypad<>::hat_center;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= joypad<>::hat_up;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= joypad<>::hat_right;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= joypad<>::hat_down;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= joypad<>::hat_left;
axis[0] = (int16_t)state.Gamepad.sThumbLX;
axis[1] = (int16_t)state.Gamepad.sThumbLY;
axis[2] = (int16_t)state.Gamepad.sThumbRX;
axis[3] = (int16_t)state.Gamepad.sThumbRY;
//transform left and right trigger ranges:
//from: 0 (low, eg released) to 255 (high, eg pressed all the way down)
//to: +32767 (low) to -32768 (high)
uint16_t triggerX = state.Gamepad.bLeftTrigger;
uint16_t triggerY = state.Gamepad.bRightTrigger;
triggerX = (triggerX << 8) | triggerX;
triggerY = (triggerY << 8) | triggerY;
axis[4] = (~triggerX) - 32768;
axis[5] = (~triggerY) - 32768;
button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
}
Gamepad() {
hat = joypad<>::hat_center;
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
for(unsigned n = 0; n < 10; n++) button[n] = false;
}
};
vector<Gamepad> lgamepad;
void poll() {
for(unsigned i = 0; i < lgamepad.size(); i++) {
XINPUT_STATE state;
DWORD result = XInputGetState(lgamepad[i].id, &state);
if(result == ERROR_SUCCESS) lgamepad[i].poll(state);
}
}
void init() {
//XInput only supports up to four controllers
for(unsigned i = 0; i <= 3; i++) {
XINPUT_STATE state;
DWORD result = XInputGetState(i, &state);
if(result == ERROR_SUCCESS) {
//valid controller detected, add to gamepad list
unsigned n = lgamepad.size();
lgamepad[n].id = i;
}
}
}
};
static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
class DirectInput {
public:
HWND handle;
LPDIRECTINPUT8 context;
struct Gamepad {
LPDIRECTINPUTDEVICE8 handle;
int16_t hat[4];
int16_t axis[6];
bool button[128];
void poll(DIJOYSTATE2 &state) {
//POV hats
for(unsigned n = 0; n < 4; n++) {
hat[n] = joypad<>::hat_center;
//POV value is in clockwise-hundredth degree units
unsigned pov = state.rgdwPOV[n];
//some drivers report a centered POV hat as -1U, others as 65535U.
//>= 36000 will match both, as well as invalid ranges.
if(pov >= 36000) continue;
if(pov >= 31500 || pov <= 4500) hat[n] |= joypad<>::hat_up;
if(pov >= 4500 && pov <= 13500) hat[n] |= joypad<>::hat_right;
if(pov >= 13500 && pov <= 22500) hat[n] |= joypad<>::hat_down;
if(pov >= 22500 && pov <= 31500) hat[n] |= joypad<>::hat_left;
}
//axes
axis[0] = state.lX;
axis[1] = state.lY;
axis[2] = state.lZ;
axis[3] = state.lRx;
axis[4] = state.lRy;
axis[5] = state.lRz;
//buttons
for(unsigned n = 0; n < 128; n++) {
button[n] = (bool)state.rgbButtons[n];
}
}
Gamepad() {
handle = 0;
for(unsigned n = 0; n < 4; n++) hat[n] = joypad<>::hat_center;
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
for(unsigned n = 0; n < 128; n++) button[n] = false;
}
};
vector<Gamepad> lgamepad;
void poll() {
for(unsigned i = 0; i < lgamepad.size(); i++) {
if(FAILED(lgamepad[i].handle->Poll())) {
lgamepad[i].handle->Acquire();
continue;
}
DIJOYSTATE2 state;
lgamepad[i].handle->GetDeviceState(sizeof(DIJOYSTATE2), &state);
lgamepad[i].poll(state);
}
}
bool init_joypad(const DIDEVICEINSTANCE *instance) {
//if this is an XInput device, do not acquire it via DirectInput ...
//the XInput driver above will handle said device.
for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) {
uint32_t guid = MAKELONG(rawinput.lgamepad[i].vendorId, rawinput.lgamepad[i].productId);
if(guid == instance->guidProduct.Data1) {
if(rawinput.lgamepad[i].isXInputDevice == true) {
return DIENUM_CONTINUE;
}
}
}
if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) {
return DIENUM_CONTINUE;
}
device->SetDataFormat(&c_dfDIJoystick2);
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
unsigned n = lgamepad.size();
lgamepad[n].handle = device;
return DIENUM_CONTINUE;
}
bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) {
DIPROPRANGE range;
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYID;
range.diph.dwObj = instance->dwType;
range.lMin = -32768;
range.lMax = +32767;
device->SetProperty(DIPROP_RANGE, &range.diph);
return DIENUM_CONTINUE;
}
void init(HWND handle_) {
handle = handle_;
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0);
context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
}
void term() {
for(unsigned i = 0; i < lgamepad.size(); i++) {
lgamepad[i].handle->Unacquire();
lgamepad[i].handle->Release();
}
lgamepad.reset();
if(context) {
context->Release();
context = 0;
}
}
private:
LPDIRECTINPUTDEVICE8 device;
};
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) {
return ((DirectInput*)p)->init_joypad(instance);
}
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) {
return ((DirectInput*)p)->init_axis(instance);
}
class pInputRaw {
public:
InputRaw &self;
XInput xinput;
DirectInput dinput;
bool acquire_mouse;
bool cursor_visible;
struct {
HWND handle;
} settings;
bool cap(Input::Setting setting) {
if(setting == Input::Handle) return true;
if(setting == Input::KeyboardSupport) return true;
if(setting == Input::MouseSupport) return true;
if(setting == Input::JoypadSupport) return true;
return false;
}
uintptr_t get(Input::Setting setting) {
if(setting == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(Input::Setting setting, uintptr_t param) {
if(setting == Input::Handle) {
settings.handle = (HWND)param;
return true;
}
return false;
}
bool acquire() {
acquire_mouse = true;
if(cursor_visible == true) {
ShowCursor(cursor_visible = false);
}
return acquired();
}
bool unacquire() {
acquire_mouse = false;
ReleaseCapture();
ClipCursor(NULL);
if(cursor_visible == false) {
ShowCursor(cursor_visible = true);
}
return true;
}
bool acquired() {
if(acquire_mouse == true) {
SetFocus(settings.handle);
SetCapture(settings.handle);
RECT rc;
GetWindowRect(settings.handle, &rc);
ClipCursor(&rc);
}
return GetCapture() == settings.handle;
}
bool poll(int16_t *table) {
memset(table, 0, nall::input_limit * sizeof(int16_t));
WaitForSingleObject(rawinput.mutex, INFINITE);
//=========
//Keyboards
//=========
for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)keyboard<>::count); i++) {
unsigned index = keyboard<>::index(i, keyboard<>::none);
for(unsigned n = 0; n < keyboard<>::length; n++) {
table[index + n] = rawinput.lkeyboard[i].state[n];
}
}
//====
//Mice
//====
for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)mouse<>::count); i++) {
unsigned index = mouse<>::index(i, mouse<>::none);
table[index + mouse<>::x] = rawinput.lmouse[i].xDistance;
table[index + mouse<>::y] = rawinput.lmouse[i].yDistance;
table[index + mouse<>::z] = rawinput.lmouse[i].zDistance;
for(unsigned n = 0; n < min(5U, (unsigned)mouse<>::buttons); n++) {
table[index + mouse<>::button + n] = (bool)(rawinput.lmouse[i].buttonState & (1 << n));
}
rawinput.lmouse[i].sync();
}
ReleaseMutex(rawinput.mutex);
unsigned joy = 0;
//==================
//XInput controllers
//==================
xinput.poll();
for(unsigned i = 0; i < xinput.lgamepad.size(); i++) {
if(joy >= joypad<>::count) break;
unsigned index = joypad<>::index(joy++, joypad<>::none);
table[index + joypad<>::hat + 0] = xinput.lgamepad[i].hat;
for(unsigned axis = 0; axis < min(6U, (unsigned)joypad<>::axes); axis++) {
table[index + joypad<>::axis + axis] = xinput.lgamepad[i].axis[axis];
}
for(unsigned button = 0; button < min(10U, (unsigned)joypad<>::buttons); button++) {
table[index + joypad<>::button + button] = xinput.lgamepad[i].button[button];
}
}
//=======================
//DirectInput controllers
//=======================
dinput.poll();
for(unsigned i = 0; i < dinput.lgamepad.size(); i++) {
if(joy >= joypad<>::count) break;
unsigned index = joypad<>::index(joy++, joypad<>::none);
for(unsigned hat = 0; hat < min(4U, (unsigned)joypad<>::hats); hat++) {
table[index + joypad<>::hat + hat] = dinput.lgamepad[i].hat[hat];
}
for(unsigned axis = 0; axis < min(6U, (unsigned)joypad<>::axes); axis++) {
table[index + joypad<>::axis + axis] = dinput.lgamepad[i].axis[axis];
}
for(unsigned button = 0; button < min(128U, (unsigned)joypad<>::buttons); button++) {
table[index + joypad<>::button + button] = dinput.lgamepad[i].button[button];
}
}
}
bool init() {
//only spawn RawInput processing thread one time
if(rawinput.initialized == false) {
rawinput.initialized = true;
rawinput.mutex = CreateMutex(NULL, FALSE, NULL);
CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);
//RawInput device calibration needs to finish before initializing DirectInput;
//as it needs device GUIDs to distinguish XInput devices from ordinary joypads.
bool ready = false;
do {
Sleep(10);
WaitForSingleObject(rawinput.mutex, INFINITE);
ready = rawinput.ready;
ReleaseMutex(rawinput.mutex);
} while(ready == false);
}
xinput.init();
dinput.init(settings.handle);
acquire_mouse = false;
cursor_visible = true;
return true;
}
void term() {
unacquire();
dinput.term();
}
pInputRaw(InputRaw &self_) : self(self_) {
}
};
bool InputRaw::cap(Setting setting) { return p.cap(setting); }
uintptr_t InputRaw::get(Setting setting) { return p.get(setting); }
bool InputRaw::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool InputRaw::acquire() { return p.acquire(); }
bool InputRaw::unacquire() { return p.unacquire(); }
bool InputRaw::acquired() { return p.acquired(); }
bool InputRaw::poll(int16_t *table) { return p.poll(table); }
bool InputRaw::init() { return p.init(); }
void InputRaw::term() { p.term(); }
InputRaw::InputRaw() : p(*new pInputRaw(*this)) {}
InputRaw::~InputRaw() { delete &p; }
}

View File

@@ -0,0 +1,22 @@
class pInputRaw;
class InputRaw : public Input {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool acquire();
bool unacquire();
bool acquired();
bool poll(int16_t *table);
bool init();
void term();
InputRaw();
~InputRaw();
private:
pInputRaw &p;
};

View File

@@ -0,0 +1,254 @@
//================
//SDL input driver
//================
//Keyboard and mouse are controlled directly via Xlib,
//as SDL cannot capture input from windows it does not create itself.
//SDL is used only to handle joysticks / gamepads.
#include <SDL/SDL.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
namespace ruby {
#include "sdl.hpp"
struct pInputSDL {
#include "xlibkeys.hpp"
InputSDL &self;
struct {
Display *display;
Window rootwindow;
Cursor InvisibleCursor;
SDL_Joystick *gamepad[joypad<>::count];
unsigned screenwidth, screenheight;
unsigned relativex, relativey;
bool mouseacquired;
//mouse device settings
int accel_numerator;
int accel_denominator;
int threshold;
} device;
struct {
uintptr_t handle;
} settings;
bool cap(Input::Setting setting) {
if(setting == Input::Handle) return true;
if(setting == Input::KeyboardSupport) return true;
if(setting == Input::MouseSupport) return true;
if(setting == Input::JoypadSupport) return true;
return false;
}
uintptr_t get(Input::Setting setting) {
if(setting == Input::Handle) return settings.handle;
return false;
}
bool set(Input::Setting setting, uintptr_t param) {
if(setting == Input::Handle) {
settings.handle = param;
return true;
}
return false;
}
bool acquire() {
if(acquired()) return true;
if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync,
device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) {
//backup existing cursor acceleration settings
XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold);
//disable cursor acceleration
XChangePointerControl(device.display, True, False, 1, 1, 0);
//center cursor (so that first relative poll returns 0, 0 if mouse has not moved)
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
return device.mouseacquired = true;
} else {
return device.mouseacquired = false;
}
}
bool unacquire() {
if(acquired()) {
//restore cursor acceleration and release cursor
XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold);
XUngrabPointer(device.display, CurrentTime);
device.mouseacquired = false;
}
return true;
}
bool acquired() {
return device.mouseacquired;
}
bool poll(int16_t *table) {
memset(table, 0, nall::input_limit * sizeof(int16_t));
//========
//Keyboard
//========
char state[32];
XQueryKeymap(device.display, state);
for(unsigned i = 0; i < keyboard<>::length; i++) {
uint8_t code = keycode[i];
if(code == 0) continue; //unmapped
table[i] = (bool)(state[code >> 3] & (1 << (code & 7)));
}
//=====
//Mouse
//=====
Window root_return, child_return;
int root_x_return = 0, root_y_return = 0;
int win_x_return = 0, win_y_return = 0;
unsigned int mask_return = 0;
XQueryPointer(device.display, settings.handle,
&root_return, &child_return, &root_x_return, &root_y_return,
&win_x_return, &win_y_return, &mask_return);
if(acquired()) {
XWindowAttributes attributes;
XGetWindowAttributes(device.display, settings.handle, &attributes);
//absolute -> relative conversion
table[mouse<0>::x] = (int16_t)(root_x_return - device.screenwidth / 2);
table[mouse<0>::y] = (int16_t)(root_y_return - device.screenheight / 2);
if(table[mouse<0>::x] != 0 || table[mouse<0>::y] != 0) {
//if mouse movement occurred, re-center mouse for next poll
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
}
} else {
table[mouse<0>::x] = (int16_t)(root_x_return - device.relativex);
table[mouse<0>::y] = (int16_t)(root_y_return - device.relativey);
device.relativex = root_x_return;
device.relativey = root_y_return;
}
//manual device polling is limited to only five buttons ...
table[mouse<0>::button + 0] = (bool)(mask_return & Button1Mask);
table[mouse<0>::button + 1] = (bool)(mask_return & Button2Mask);
table[mouse<0>::button + 2] = (bool)(mask_return & Button3Mask);
table[mouse<0>::button + 3] = (bool)(mask_return & Button4Mask);
table[mouse<0>::button + 4] = (bool)(mask_return & Button5Mask);
//=========
//Joypad(s)
//=========
SDL_JoystickUpdate();
for(unsigned i = 0; i < joypad<>::count; i++) {
if(!device.gamepad[i]) continue;
unsigned index = joypad<>::index(i, joypad<>::none);
//POV hats
unsigned hats = min((unsigned)joypad<>::hats, SDL_JoystickNumHats(device.gamepad[i]));
for(unsigned hat = 0; hat < hats; hat++) {
uint8_t state = SDL_JoystickGetHat(device.gamepad[i], hat);
if(state & SDL_HAT_UP ) table[index + joypad<>::hat + hat] |= joypad<>::hat_up;
if(state & SDL_HAT_RIGHT) table[index + joypad<>::hat + hat] |= joypad<>::hat_right;
if(state & SDL_HAT_DOWN ) table[index + joypad<>::hat + hat] |= joypad<>::hat_down;
if(state & SDL_HAT_LEFT ) table[index + joypad<>::hat + hat] |= joypad<>::hat_left;
}
//axes
unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(device.gamepad[i]));
for(unsigned axis = 0; axis < axes; axis++) {
table[index + joypad<>::axis + axis] = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis);
}
//buttons
for(unsigned button = 0; button < joypad<>::buttons; button++) {
table[index + joypad<>::button + button] = (bool)SDL_JoystickGetButton(device.gamepad[i], button);
}
}
return true;
}
bool init() {
init_keycodes();
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
SDL_JoystickEventState(SDL_IGNORE);
device.display = XOpenDisplay(0);
device.rootwindow = DefaultRootWindow(device.display);
XWindowAttributes attributes;
XGetWindowAttributes(device.display, device.rootwindow, &attributes);
device.screenwidth = attributes.width;
device.screenheight = attributes.height;
//Xlib: "because XShowCursor(false) would be too easy."
//create a fully transparent cursor named InvisibleCursor,
//for use while acquire() / XGrabPointer() is active.
Pixmap pixmap;
XColor black, unused;
static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display));
XAllocNamedColor(device.display, colormap, "black", &black, &unused);
pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8);
device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0);
XFreePixmap(device.display, pixmap);
XFreeColors(device.display, colormap, &black.pixel, 1, 0);
device.mouseacquired = false;
device.relativex = 0;
device.relativey = 0;
unsigned joypads = min((unsigned)joypad<>::count, SDL_NumJoysticks());
for(unsigned i = 0; i < joypads; i++) device.gamepad[i] = SDL_JoystickOpen(i);
return true;
}
void term() {
unacquire();
XFreeCursor(device.display, device.InvisibleCursor);
for(unsigned i = 0; i < joypad<>::count; i++) {
if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]);
device.gamepad[i] = 0;
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
pInputSDL(InputSDL &self_) : self(self_) {
for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0;
settings.handle = 0;
}
};
bool InputSDL::cap(Setting setting) { return p.cap(setting); }
uintptr_t InputSDL::get(Setting setting) { return p.get(setting); }
bool InputSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool InputSDL::acquire() { return p.acquire(); }
bool InputSDL::unacquire() { return p.unacquire(); }
bool InputSDL::acquired() { return p.acquired(); }
bool InputSDL::poll(int16_t *table) { return p.poll(table); }
bool InputSDL::init() { return p.init(); }
void InputSDL::term() { p.term(); }
InputSDL::InputSDL() : p(*new pInputSDL(*this)) {}
InputSDL::~InputSDL() { delete &p; }
}

View File

@@ -0,0 +1,22 @@
class pInputSDL;
class InputSDL : public Input {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool acquire();
bool unacquire();
bool acquired();
bool poll(int16_t *table);
bool init();
void term();
InputSDL();
~InputSDL();
private:
pInputSDL &p;
};

View File

@@ -0,0 +1,66 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
namespace ruby {
#include "x.hpp"
class pInputX {
public:
InputX &self;
Display *display;
#include "xlibkeys.hpp"
bool cap(Input::Setting setting) {
if(setting == Input::KeyboardSupport) return true;
return false;
}
uintptr_t get(Input::Setting setting) {
return false;
}
bool set(Input::Setting setting, uintptr_t param) {
return false;
}
bool poll(int16_t *table) {
memset(table, 0, input_limit * sizeof(int16_t));
char state[32];
XQueryKeymap(display, state);
for(unsigned i = 0; i < keyboard<>::length; i++) {
uint8_t code = keycode[i];
if(code == 0) continue; //unmapped
table[i] = (bool)(state[code >> 3] & (1 << (code & 7)));
}
return true;
}
bool init() {
init_keycodes();
display = XOpenDisplay(0);
return true;
}
void term() {
}
pInputX(InputX &self_) : self(self_) {}
};
bool InputX::cap(Setting setting) { return p.cap(setting); }
uintptr_t InputX::get(Setting setting) { return p.get(setting); }
bool InputX::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool InputX::poll(int16_t *table) { return p.poll(table); }
bool InputX::init() { return p.init(); }
void InputX::term() { p.term(); }
InputX::InputX() : p(*new pInputX(*this)) {}
InputX::~InputX() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,18 @@
class pInputX;
class InputX : public Input {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool poll(int16_t *table);
bool init();
void term();
InputX();
~InputX();
private:
pInputX &p;
};

View File

@@ -0,0 +1,138 @@
//shared keycode lookup table + initialization routine:
//#include inside a class interface to use
//Xlib keycodes for each key can vary between platforms, so this header file
//will lookup keycodes from static keysyms, and map them to nall/input.hpp's
//keyboard identifiers.
//
//this allows input capture routine to iterate quickly over all keycodes and
//map their states to ruby's input state table.
uint8_t keycode[256];
bool init_keycodes() {
Display *display = XOpenDisplay(0);
memset(&keycode, 0, sizeof keycode);
#define assign(x, y) keycode[x] = XKeysymToKeycode(display, y)
assign(keyboard<0>::escape, XK_Escape);
assign(keyboard<0>::f1, XK_F1);
assign(keyboard<0>::f2, XK_F2);
assign(keyboard<0>::f3, XK_F3);
assign(keyboard<0>::f4, XK_F4);
assign(keyboard<0>::f5, XK_F5);
assign(keyboard<0>::f6, XK_F6);
assign(keyboard<0>::f7, XK_F7);
assign(keyboard<0>::f8, XK_F8);
assign(keyboard<0>::f9, XK_F9);
assign(keyboard<0>::f10, XK_F10);
assign(keyboard<0>::f11, XK_F11);
assign(keyboard<0>::f12, XK_F12);
//assign(keyboard<0>::print_screen, XK_???);
assign(keyboard<0>::scroll_lock, XK_Scroll_Lock);
assign(keyboard<0>::pause, XK_Pause);
assign(keyboard<0>::tilde, XK_asciitilde);
assign(keyboard<0>::num_0, XK_0);
assign(keyboard<0>::num_1, XK_1);
assign(keyboard<0>::num_2, XK_2);
assign(keyboard<0>::num_3, XK_3);
assign(keyboard<0>::num_4, XK_4);
assign(keyboard<0>::num_5, XK_5);
assign(keyboard<0>::num_6, XK_6);
assign(keyboard<0>::num_7, XK_7);
assign(keyboard<0>::num_8, XK_8);
assign(keyboard<0>::num_9, XK_9);
assign(keyboard<0>::dash, XK_minus);
assign(keyboard<0>::equal, XK_equal);
assign(keyboard<0>::backspace, XK_BackSpace);
assign(keyboard<0>::insert, XK_Insert);
assign(keyboard<0>::delete_, XK_Delete);
assign(keyboard<0>::home, XK_Home);
assign(keyboard<0>::end, XK_End);
assign(keyboard<0>::page_up, XK_Prior);
assign(keyboard<0>::page_down, XK_Next);
assign(keyboard<0>::a, XK_A);
assign(keyboard<0>::b, XK_B);
assign(keyboard<0>::c, XK_C);
assign(keyboard<0>::d, XK_D);
assign(keyboard<0>::e, XK_E);
assign(keyboard<0>::f, XK_F);
assign(keyboard<0>::g, XK_G);
assign(keyboard<0>::h, XK_H);
assign(keyboard<0>::i, XK_I);
assign(keyboard<0>::j, XK_J);
assign(keyboard<0>::k, XK_K);
assign(keyboard<0>::l, XK_L);
assign(keyboard<0>::m, XK_M);
assign(keyboard<0>::n, XK_N);
assign(keyboard<0>::o, XK_O);
assign(keyboard<0>::p, XK_P);
assign(keyboard<0>::q, XK_Q);
assign(keyboard<0>::r, XK_R);
assign(keyboard<0>::s, XK_S);
assign(keyboard<0>::t, XK_T);
assign(keyboard<0>::u, XK_U);
assign(keyboard<0>::v, XK_V);
assign(keyboard<0>::w, XK_W);
assign(keyboard<0>::x, XK_X);
assign(keyboard<0>::y, XK_Y);
assign(keyboard<0>::z, XK_Z);
assign(keyboard<0>::lbracket, XK_bracketleft);
assign(keyboard<0>::rbracket, XK_bracketright);
assign(keyboard<0>::backslash, XK_backslash);
assign(keyboard<0>::semicolon, XK_semicolon);
assign(keyboard<0>::apostrophe, XK_apostrophe);
assign(keyboard<0>::comma, XK_comma);
assign(keyboard<0>::period, XK_period);
assign(keyboard<0>::slash, XK_slash);
assign(keyboard<0>::pad_0, XK_KP_0);
assign(keyboard<0>::pad_1, XK_KP_1);
assign(keyboard<0>::pad_2, XK_KP_2);
assign(keyboard<0>::pad_3, XK_KP_3);
assign(keyboard<0>::pad_4, XK_KP_4);
assign(keyboard<0>::pad_5, XK_KP_5);
assign(keyboard<0>::pad_6, XK_KP_6);
assign(keyboard<0>::pad_7, XK_KP_7);
assign(keyboard<0>::pad_8, XK_KP_8);
assign(keyboard<0>::pad_9, XK_KP_9);
assign(keyboard<0>::add, XK_KP_Add);
assign(keyboard<0>::subtract, XK_KP_Subtract);
assign(keyboard<0>::multiply, XK_KP_Multiply);
assign(keyboard<0>::divide, XK_KP_Divide);
assign(keyboard<0>::enter, XK_KP_Enter);
//assign(keyboard<0>::num_lock, XK_???);
//assign(keyboard<0>::caps_lock, XK_???);
assign(keyboard<0>::up, XK_Up);
assign(keyboard<0>::down, XK_Down);
assign(keyboard<0>::left, XK_Left);
assign(keyboard<0>::right, XK_Right);
assign(keyboard<0>::tab, XK_Tab);
assign(keyboard<0>::return_, XK_Return);
assign(keyboard<0>::spacebar, XK_space);
assign(keyboard<0>::lctrl, XK_Control_L);
assign(keyboard<0>::rctrl, XK_Control_R);
assign(keyboard<0>::lalt, XK_Alt_L);
assign(keyboard<0>::ralt, XK_Alt_R);
assign(keyboard<0>::lshift, XK_Shift_L);
assign(keyboard<0>::rshift, XK_Shift_R);
assign(keyboard<0>::lsuper, XK_Super_L);
assign(keyboard<0>::rsuper, XK_Super_R);
assign(keyboard<0>::menu, XK_Menu);
#undef assign
XCloseDisplay(display);
}

317
tools/bsnes/lib/ruby/ruby.cpp Executable file
View File

@@ -0,0 +1,317 @@
#include <ruby/ruby.hpp>
using namespace nall;
#include <ruby/ruby_impl.cpp>
namespace ruby {
VideoInterface video;
AudioInterface audio;
InputInterface input;
/* VideoInterface */
void VideoInterface::driver(const char *driver) {
if(p) term();
if(!driver || !*driver) driver = default_driver();
if(0);
#ifdef VIDEO_DIRECT3D
else if(!strcmp(driver, "Direct3D")) p = new VideoD3D();
#endif
#ifdef VIDEO_DIRECTDRAW
else if(!strcmp(driver, "DirectDraw")) p = new VideoDD();
#endif
#ifdef VIDEO_GDI
else if(!strcmp(driver, "GDI")) p = new VideoGDI();
#endif
#ifdef VIDEO_GLX
else if(!strcmp(driver, "OpenGL")) p = new VideoGLX();
#endif
#ifdef VIDEO_SDL
else if(!strcmp(driver, "SDL")) p = new VideoSDL();
#endif
#ifdef VIDEO_WGL
else if(!strcmp(driver, "OpenGL")) p = new VideoWGL();
#endif
#ifdef VIDEO_XV
else if(!strcmp(driver, "X-Video")) p = new VideoXv();
#endif
else p = new Video();
}
//select the *safest* available driver, not the fastest
const char* VideoInterface::default_driver() {
#if defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_SDL)
return "SDL";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_GLX)
return "OpenGL";
#else
return "None";
#endif
}
//returns list of available drivers, sorted from most to least optimal
const char* VideoInterface::driver_list() {
return
//Windows
#if defined(VIDEO_DIRECT3D)
"Direct3D;"
#endif
#if defined(VIDEO_WGL)
"OpenGL;"
#endif
#if defined(VIDEO_DIRECTDRAW)
"DirectDraw;"
#endif
#if defined(VIDEO_GDI)
"GDI;"
#endif
//Linux
#if defined(VIDEO_GLX)
"OpenGL;"
#endif
#if defined(VIDEO_XV)
"X-Video;"
#endif
#if defined(VIDEO_SDL)
"SDL;"
#endif
"None";
}
bool VideoInterface::init() {
if(!p) driver();
return p->init();
}
void VideoInterface::term() {
if(p) {
delete p;
p = 0;
}
}
bool VideoInterface::cap(Video::Setting setting) { return p ? p->cap(setting) : false; }
uintptr_t VideoInterface::get(Video::Setting setting) { return p ? p->get(setting) : false; }
bool VideoInterface::set(Video::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; }
bool VideoInterface::lock(uint32_t *&data, unsigned &pitch) { return p ? p->lock(data, pitch) : false; }
void VideoInterface::unlock() { if(p) p->unlock(); }
void VideoInterface::clear() { if(p) p->clear(); }
void VideoInterface::refresh(unsigned width, unsigned height) { if(p) p->refresh(width, height); }
VideoInterface::VideoInterface() : p(0) {}
VideoInterface::~VideoInterface() { term(); }
/* AudioInterface */
void AudioInterface::driver(const char *driver) {
if(p) term();
if(!driver || !*driver) driver = default_driver();
if(0);
#ifdef AUDIO_ALSA
else if(!strcmp(driver, "ALSA")) p = new AudioALSA();
#endif
#ifdef AUDIO_AO
else if(!strcmp(driver, "libao")) p = new AudioAO();
#endif
#ifdef AUDIO_DIRECTSOUND
else if(!strcmp(driver, "DirectSound")) p = new AudioDS();
#endif
#ifdef AUDIO_OPENAL
else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL();
#endif
#ifdef AUDIO_OSS
else if(!strcmp(driver, "OSS")) p = new AudioOSS();
#endif
#ifdef AUDIO_PULSEAUDIO
else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio();
#endif
else p = new Audio();
}
//select the *safest* available driver, not the fastest
const char* AudioInterface::default_driver() {
#if defined(AUDIO_DIRECTSOUND)
return "DirectSound";
#elif defined(AUDIO_ALSA)
return "ALSA";
#elif defined(AUDIO_OPENAL)
return "OpenAL";
#elif defined(AUDIO_PULSEAUDIO)
return "PulseAudio";
#elif defined(AUDIO_AO)
return "libao";
#elif defined(AUDIO_OSS)
return "OSS";
#else
return "None";
#endif
}
//returns list of available drivers, sorted from most to least optimal
const char* AudioInterface::driver_list() {
return
//Windows
#if defined(AUDIO_DIRECTSOUND)
"DirectSound;"
#endif
//Linux
#if defined(AUDIO_ALSA)
"ALSA;"
#endif
#if defined(AUDIO_OPENAL)
"OpenAL;"
#endif
#if defined(AUDIO_OSS)
"OSS;"
#endif
#if defined(AUDIO_PULSEAUDIO)
"PulseAudio;"
#endif
#if defined(AUDIO_AO)
"libao;"
#endif
"None";
}
#include "ruby_audio.cpp"
/* InputInterface */
void InputInterface::driver(const char *driver) {
if(p) term();
if(!driver || !*driver) driver = default_driver();
if(0);
#ifdef INPUT_DIRECTINPUT
else if(!strcmp(driver, "DirectInput")) p = new InputDI();
#endif
#ifdef INPUT_RAWINPUT
else if(!strcmp(driver, "RawInput")) p = new InputRaw();
#endif
#ifdef INPUT_SDL
else if(!strcmp(driver, "SDL")) p = new InputSDL();
#endif
#ifdef INPUT_X
else if(!strcmp(driver, "X-Windows")) p = new InputX();
#endif
else p = new Input();
}
//select the *safest* available driver, not the fastest
const char* InputInterface::default_driver() {
#if defined(INPUT_RAWINPUT)
return "RawInput";
#elif defined(INPUT_DIRECTINPUT)
return "DirectInput";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_X)
return "X-Windows";
#else
return "none";
#endif
}
const char* InputInterface::driver_list() {
return
//Windows
#if defined(INPUT_RAWINPUT)
"RawInput;"
#endif
#if defined(INPUT_DIRECTINPUT)
"DirectInput;"
#endif
//Linux
#if defined(INPUT_SDL)
"SDL;"
#endif
#if defined(INPUT_X)
"X-Windows;"
#endif
"None";
}
bool InputInterface::init() {
if(!p) driver();
return p->init();
}
void InputInterface::term() {
if(p) {
delete p;
p = 0;
}
}
bool InputInterface::cap(Input::Setting setting) { return p ? p->cap(setting) : false; }
uintptr_t InputInterface::get(Input::Setting setting) { return p ? p->get(setting) : false; }
bool InputInterface::set(Input::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; }
bool InputInterface::acquire() { return p ? p->acquire() : false; }
bool InputInterface::unacquire() { return p ? p->unacquire() : false; }
bool InputInterface::acquired() { return p ? p->acquired() : false; }
bool InputInterface::poll(int16_t *table) { return p ? p->poll(table) : false; }
InputInterface::InputInterface() : p(0) {}
InputInterface::~InputInterface() { term(); }
} //namespace ruby

106
tools/bsnes/lib/ruby/ruby.hpp Executable file
View File

@@ -0,0 +1,106 @@
/*
ruby
version: 0.05 (2009-03-21)
license: public domain
*/
#ifndef RUBY_H
#define RUBY_H
#include <nall/algorithm.hpp>
#include <nall/array.hpp>
#include <nall/bit.hpp>
#include <nall/input.hpp>
#include <nall/new.hpp>
#include <nall/sort.hpp>
#include <nall/stdint.hpp>
#include <nall/vector.hpp>
namespace ruby {
#include <ruby/video.hpp>
#include <ruby/audio.hpp>
#include <ruby/input.hpp>
class VideoInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();
bool cap(Video::Setting setting);
uintptr_t get(Video::Setting setting);
bool set(Video::Setting setting, uintptr_t param);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
VideoInterface();
~VideoInterface();
private:
Video *p;
};
class AudioInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();
bool cap(Audio::Setting setting);
uintptr_t get(Audio::Setting setting);
bool set(Audio::Setting setting, uintptr_t param);
void sample(uint16_t left, uint16_t right);
void clear();
AudioInterface();
~AudioInterface();
private:
Audio *p;
unsigned volume;
//resample unit
double hermite(double mu, double a, double b, double c, double d);
bool resample_enabled;
double r_outfreq, r_infreq;
double r_step, r_frac;
int r_left[4], r_right[4];
};
class InputInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();
bool cap(Input::Setting setting);
uintptr_t get(Input::Setting setting);
bool set(Input::Setting setting, uintptr_t param);
bool acquire();
bool unacquire();
bool acquired();
bool poll(int16_t *table);
InputInterface();
~InputInterface();
private:
Input *p;
};
extern VideoInterface video;
extern AudioInterface audio;
extern InputInterface input;
} //namespace ruby
#endif //ifndef RUBY_H

View File

@@ -0,0 +1,135 @@
bool AudioInterface::init() {
if(!p) driver();
return p->init();
}
void AudioInterface::term() {
if(p) {
delete p;
p = 0;
}
}
bool AudioInterface::cap(Audio::Setting setting) {
if(setting == Audio::Volume) return true;
if(setting == Audio::Resample) return true;
if(setting == Audio::ResampleOutputFrequency) return true;
if(setting == Audio::ResampleInputFrequency) return true;
return p ? p->cap(setting) : false;
}
uintptr_t AudioInterface::get(Audio::Setting setting) {
if(setting == Audio::Volume) return volume;
if(setting == Audio::Resample) return resample_enabled;
if(setting == Audio::ResampleOutputFrequency) return r_outfreq;
if(setting == Audio::ResampleInputFrequency) return r_infreq;
return p ? p->get(setting) : false;
}
bool AudioInterface::set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Volume) {
volume = param;
return true;
}
if(setting == Audio::Resample) {
resample_enabled = param;
return true;
}
if(setting == Audio::ResampleOutputFrequency) {
r_outfreq = max(1, param);
r_step = (double)r_infreq / (double)r_outfreq;
r_frac = 0;
return true;
}
if(setting == Audio::ResampleInputFrequency) {
r_infreq = max(1, param);
r_step = (double)r_infreq / (double)r_outfreq;
r_frac = 0;
return true;
}
return p ? p->set(setting, param) : false;
}
//4-tap hermite interpolation
double AudioInterface::hermite(double mu1, double a, double b, double c, double d) {
const double tension = 0.0; //-1 = low, 0 = normal, 1 = high
const double bias = 0.0; //-1 = left, 0 = even, 1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1 + bias) * (1 - tension) / 2;
m0 += (c - b) * (1 - bias) * (1 - tension) / 2;
m1 = (c - b) * (1 + bias) * (1 - tension) / 2;
m1 += (d - c) * (1 - bias) * (1 - tension) / 2;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
void AudioInterface::sample(uint16_t left, uint16_t right) {
int s_left = (int16_t)left;
int s_right = (int16_t)right;
if(volume != 100) {
s_left = sclamp<16>((double)s_left * (double)volume / 100.0);
s_right = sclamp<16>((double)s_right * (double)volume / 100.0);
}
r_left [0] = r_left [1];
r_left [1] = r_left [2];
r_left [2] = r_left [3];
r_left [3] = s_left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = s_right;
if(resample_enabled == false) {
if(p) p->sample(left, right);
return;
}
while(r_frac <= 1.0) {
int output_left = sclamp<16>(hermite(r_frac, r_left [0], r_left [1], r_left [2], r_left [3]));
int output_right = sclamp<16>(hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3]));
r_frac += r_step;
if(p) p->sample(output_left, output_right);
}
r_frac -= 1.0;
}
void AudioInterface::clear() {
r_frac = 0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
if(p) p->clear();
}
AudioInterface::AudioInterface() {
p = 0;
volume = 100;
resample_enabled = false;
r_outfreq = r_infreq = 1;
r_step = r_frac = 0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
AudioInterface::~AudioInterface() {
term();
}

View File

@@ -0,0 +1,78 @@
/* Video */
#ifdef _WIN32
#define _WIN32_WINNT 0x0501
#include <windows.h>
#endif
#ifdef VIDEO_DIRECT3D
#include <ruby/video/direct3d.cpp>
#endif
#ifdef VIDEO_DIRECTDRAW
#include <ruby/video/directdraw.cpp>
#endif
#ifdef VIDEO_GDI
#include <ruby/video/gdi.cpp>
#endif
#ifdef VIDEO_GLX
#include <ruby/video/glx.cpp>
#endif
#ifdef VIDEO_SDL
#include <ruby/video/sdl.cpp>
#endif
#ifdef VIDEO_WGL
#include <ruby/video/wgl.cpp>
#endif
#ifdef VIDEO_XV
#include <ruby/video/xv.cpp>
#endif
/* Audio */
#ifdef AUDIO_ALSA
#include <ruby/audio/alsa.cpp>
#endif
#ifdef AUDIO_AO
#include <ruby/audio/ao.cpp>
#endif
#ifdef AUDIO_DIRECTSOUND
#include <ruby/audio/directsound.cpp>
#endif
#ifdef AUDIO_OPENAL
#include <ruby/audio/openal.cpp>
#endif
#ifdef AUDIO_OSS
#include <ruby/audio/oss.cpp>
#endif
#ifdef AUDIO_PULSEAUDIO
#include <ruby/audio/pulseaudio.cpp>
#endif
/* Input */
#ifdef INPUT_DIRECTINPUT
#include <ruby/input/directinput.cpp>
#endif
#ifdef INPUT_RAWINPUT
#include <ruby/input/rawinput.cpp>
#endif
#ifdef INPUT_SDL
#include <ruby/input/sdl.cpp>
#endif
#ifdef INPUT_X
#include <ruby/input/x.cpp>
#endif

28
tools/bsnes/lib/ruby/video.hpp Executable file
View File

@@ -0,0 +1,28 @@
class Video {
public:
enum Setting {
Handle,
Synchronize,
Filter,
};
enum Filter {
FilterPoint,
FilterLinear,
};
virtual bool cap(Setting) { return false; }
virtual uintptr_t get(Setting) { return false; }
virtual bool set(Setting, uintptr_t) { return false; }
virtual bool lock(uint32_t *&data, unsigned &pitch) { return false; }
virtual void unlock() {}
virtual void clear() {}
virtual void refresh(unsigned width, unsigned height) {}
virtual bool init() { return true; }
virtual void term() {}
Video() {}
virtual ~Video() {}
};

View File

@@ -0,0 +1,346 @@
#include <windows.h>
#include <d3d9.h>
#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1)
namespace ruby {
#include "direct3d.hpp"
class pVideoD3D {
public:
VideoD3D &self;
LPDIRECT3D9 lpd3d;
LPDIRECT3DDEVICE9 device;
LPDIRECT3DVERTEXBUFFER9 vertex_buffer, *vertex_ptr;
D3DPRESENT_PARAMETERS presentation;
D3DSURFACE_DESC d3dsd;
D3DLOCKED_RECT d3dlr;
D3DRASTER_STATUS d3drs;
D3DCAPS9 d3dcaps;
LPDIRECT3DTEXTURE9 texture;
LPDIRECT3DSURFACE9 surface;
struct d3dvertex {
float x, y, z, rhw; //screen coords
float u, v; //texture coords
};
struct {
uint32_t t_usage, v_usage;
uint32_t t_pool, v_pool;
uint32_t lock;
uint32_t filter;
} flags;
struct {
bool dynamic; //device supports dynamic textures
bool stretchrect; //device supports StretchRect
} caps;
struct {
HWND handle;
bool synchronize;
unsigned filter;
} settings;
struct {
unsigned width;
unsigned height;
} state;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
if(setting == Video::Filter) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return (uintptr_t)settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
if(setting == Video::Filter) return settings.filter;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = (HWND)param;
return true;
}
if(setting == Video::Synchronize) {
settings.synchronize = param;
return true;
}
if(setting == Video::Filter) {
settings.filter = param;
if(lpd3d) update_filter();
return true;
}
return false;
}
void update_filter() {
if(!device) return;
switch(settings.filter) { default:
case Video::FilterPoint: flags.filter = D3DTEXF_POINT; break;
case Video::FilterLinear: flags.filter = D3DTEXF_LINEAR; break;
}
device->SetSamplerState(0, D3DSAMP_MINFILTER, flags.filter);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, flags.filter);
}
/* Vertex format:
0----------1
| /|
| / |
| / |
| / |
| / |
2----------3
(x,y) screen coords, in pixels
(u,v) texture coords, betweeen 0.0 (top, left) to 1.0 (bottom, right)
*/
void set_vertex(
uint32_t px, uint32_t py, uint32_t pw, uint32_t ph,
uint32_t tw, uint32_t th,
uint32_t x, uint32_t y, uint32_t w, uint32_t h
) {
d3dvertex vertex[4];
vertex[0].x = vertex[2].x = (double)(x );
vertex[1].x = vertex[3].x = (double)(x + w);
vertex[0].y = vertex[1].y = (double)(y );
vertex[2].y = vertex[3].y = (double)(y + h);
//Z-buffer and RHW are unused for 2D blit, set to normal values
vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0;
vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0;
double rw = (double)w / (double)pw * (double)tw;
double rh = (double)h / (double)ph * (double)th;
vertex[0].u = vertex[2].u = (double)(px ) / rw;
vertex[1].u = vertex[3].u = (double)(px + w) / rw;
vertex[0].v = vertex[1].v = (double)(py ) / rh;
vertex[2].v = vertex[3].v = (double)(py + h) / rh;
vertex_buffer->Lock(0, sizeof(d3dvertex) * 4, (void**)&vertex_ptr, 0);
memcpy(vertex_ptr, vertex, sizeof(d3dvertex) * 4);
vertex_buffer->Unlock();
device->SetStreamSource(0, vertex_buffer, 0, sizeof(d3dvertex));
}
void clear() {
if(!device) return;
if(caps.stretchrect == false && !texture) return;
if(caps.stretchrect == false) {
texture->GetLevelDesc(0, &d3dsd);
texture->GetSurfaceLevel(0, &surface);
}
if(surface) {
device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00));
if(caps.stretchrect == false) {
surface->Release();
}
}
//clear primary display and all backbuffers
for(int i = 0; i < 3; i++) {
device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0);
device->Present(0, 0, 0, 0);
}
}
bool lock(uint32_t *&data, unsigned &pitch) {
if(caps.stretchrect == false) {
texture->GetLevelDesc(0, &d3dsd);
texture->GetSurfaceLevel(0, &surface);
}
surface->LockRect(&d3dlr, 0, flags.lock);
pitch = d3dlr.Pitch;
return data = (uint32_t*)d3dlr.pBits;
}
void unlock() {
surface->UnlockRect();
if(caps.stretchrect == false) surface->Release();
}
void refresh(unsigned width, unsigned height) {
if(!device) return;
RECT rd, rs; //dest, source rectangles
GetClientRect(settings.handle, &rd);
SetRect(&rs, 0, 0, width, height);
if(state.width != rd.right || state.height != rd.bottom) {
//if window size changed, D3DPRESENT_PARAMETERS must be updated
//failure to do so causes scaling issues on some ATI drivers
init();
}
device->BeginScene();
if(caps.stretchrect == true) {
LPDIRECT3DSURFACE9 temp;
device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &temp);
device->StretchRect(surface, &rs, temp, 0, static_cast<D3DTEXTUREFILTERTYPE>(flags.filter));
temp->Release();
} else {
set_vertex(0, 0, width, height, 1024, 1024, 0, 0, rd.right, rd.bottom);
device->SetTexture(0, texture);
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
}
device->EndScene();
if(settings.synchronize) {
while(true) {
D3DRASTER_STATUS status;
device->GetRasterStatus(0, &status);
if(status.InVBlank == true) break;
}
}
device->Present(0, 0, 0, 0);
}
bool init() {
term();
RECT rd;
GetClientRect(settings.handle, &rd);
state.width = rd.right;
state.height = rd.bottom;
lpd3d = Direct3DCreate9(D3D_SDK_VERSION);
if(!lpd3d) return false;
memset(&presentation, 0, sizeof(presentation));
presentation.Flags = D3DPRESENTFLAG_VIDEO;
presentation.SwapEffect = D3DSWAPEFFECT_FLIP;
presentation.hDeviceWindow = settings.handle;
presentation.BackBufferCount = 1;
presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
presentation.MultiSampleQuality = 0;
presentation.EnableAutoDepthStencil = false;
presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
presentation.Windowed = true;
presentation.BackBufferFormat = D3DFMT_UNKNOWN;
presentation.BackBufferWidth = 0;
presentation.BackBufferHeight = 0;
if(lpd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, settings.handle,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentation, &device) != D3D_OK) {
return false;
}
//detect device capabilities
device->GetDeviceCaps(&d3dcaps);
if(d3dcaps.MaxTextureWidth < 1024 || d3dcaps.MaxTextureWidth < 1024) return false;
caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES);
caps.stretchrect = (d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) &&
(d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) &&
(d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) &&
(d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) &&
(d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR);
if(caps.dynamic == true) {
flags.t_usage = D3DUSAGE_DYNAMIC;
flags.v_usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
flags.t_pool = D3DPOOL_DEFAULT;
flags.v_pool = D3DPOOL_DEFAULT;
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
} else {
flags.t_usage = 0;
flags.v_usage = D3DUSAGE_WRITEONLY;
flags.t_pool = D3DPOOL_MANAGED;
flags.v_pool = D3DPOOL_MANAGED;
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
}
device->SetDialogBoxMode(false);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
device->SetRenderState(D3DRS_LIGHTING, false);
device->SetRenderState(D3DRS_ZENABLE, false);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
device->SetVertexShader(NULL);
device->SetFVF(D3DVERTEX);
if(caps.stretchrect == true) {
device->CreateOffscreenPlainSurface(1024, 1024, D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT, &surface, NULL);
} else {
device->CreateTexture(1024, 1024, 1, flags.t_usage, D3DFMT_X8R8G8B8,
static_cast<D3DPOOL>(flags.t_pool), &texture, NULL);
}
device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX,
static_cast<D3DPOOL>(flags.v_pool), &vertex_buffer, NULL);
update_filter();
clear();
return true;
}
void term() {
if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; }
if(surface) { surface->Release(); surface = 0; }
if(texture) { texture->Release(); texture = 0; }
if(device) { device->Release(); device = 0; }
if(lpd3d) { lpd3d->Release(); lpd3d = 0; }
}
pVideoD3D(VideoD3D &self_) : self(self_) {
vertex_buffer = 0;
surface = 0;
texture = 0;
device = 0;
lpd3d = 0;
settings.handle = 0;
settings.synchronize = false;
settings.filter = Video::FilterLinear;
}
};
bool VideoD3D::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoD3D::get(Setting setting) { return p.get(setting); }
bool VideoD3D::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoD3D::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoD3D::unlock() { p.unlock(); }
void VideoD3D::clear() { p.clear(); }
void VideoD3D::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoD3D::init() { return p.init(); }
void VideoD3D::term() { p.term(); }
VideoD3D::VideoD3D() : p(*new pVideoD3D(*this)) {}
VideoD3D::~VideoD3D() { delete &p; }
} //namespace ruby
#undef D3DVERTEX

View File

@@ -0,0 +1,22 @@
class pVideoD3D;
class VideoD3D : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoD3D();
~VideoD3D();
private:
pVideoD3D &p;
};

View File

@@ -0,0 +1,178 @@
#include <windows.h>
#include <ddraw.h>
namespace ruby {
#include "directdraw.hpp"
class pVideoDD {
public:
VideoDD &self;
LPDIRECTDRAW lpdd;
LPDIRECTDRAW7 lpdd7;
LPDIRECTDRAWSURFACE7 screen, raster;
LPDIRECTDRAWCLIPPER clipper;
DDSURFACEDESC2 ddsd;
DDSCAPS2 ddscaps;
struct {
HWND handle;
bool synchronize;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return (uintptr_t)settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = (HWND)param;
return true;
}
if(setting == Video::Synchronize) {
settings.synchronize = param;
return true;
}
return false;
}
void clear() {
DDBLTFX fx;
fx.dwSize = sizeof(DDBLTFX);
fx.dwFillColor = 0x00000000;
screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
}
bool lock(uint32_t *&data, unsigned &pitch) {
if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false;
pitch = ddsd.lPitch;
return data = (uint32_t*)ddsd.lpSurface;
}
void unlock() {
raster->Unlock(0);
}
void refresh(unsigned r_width, unsigned r_height) {
if(settings.synchronize) {
while(true) {
BOOL in_vblank;
lpdd7->GetVerticalBlankStatus(&in_vblank);
if(in_vblank == true) break;
}
}
HRESULT hr;
RECT rd, rs;
SetRect(&rs, 0, 0, r_width, r_height);
POINT p = { 0, 0 };
ClientToScreen(settings.handle, &p);
GetClientRect(settings.handle, &rd);
OffsetRect(&rd, p.x, p.y);
if(screen->Blt(&rd, raster, &rs, DDBLT_WAIT, 0) == DDERR_SURFACELOST) {
screen->Restore();
raster->Restore();
}
}
bool init() {
term();
DirectDrawCreate(0, &lpdd, 0);
lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7);
if(lpdd) { lpdd->Release(); lpdd = 0; }
lpdd7->SetCooperativeLevel(settings.handle, DDSCL_NORMAL);
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
lpdd7->CreateSurface(&ddsd, &screen, 0);
lpdd7->CreateClipper(0, &clipper, 0);
clipper->SetHWnd(0, settings.handle);
screen->SetClipper(clipper);
create_raster();
clear();
return true;
}
void create_raster() {
screen->GetSurfaceDesc(&ddsd);
int depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
if(depth == 32) goto try_native_surface;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
ddsd.dwWidth = 1024;
ddsd.dwHeight = 1024;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000;
ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00;
ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff;
if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return;
try_native_surface:
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
ddsd.dwWidth = 1024;
ddsd.dwHeight = 1024;
if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return;
}
void term() {
if(clipper) { clipper->Release(); clipper = 0; }
if(raster) { raster->Release(); raster = 0; }
if(screen) { screen->Release(); screen = 0; }
if(lpdd7) { lpdd7->Release(); lpdd7 = 0; }
if(lpdd) { lpdd->Release(); lpdd = 0; }
}
pVideoDD(VideoDD &self_) : self(self_) {
lpdd = 0;
lpdd7 = 0;
screen = 0;
raster = 0;
clipper = 0;
settings.handle = 0;
}
};
bool VideoDD::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoDD::get(Setting setting) { return p.get(setting); }
bool VideoDD::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoDD::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoDD::unlock() { p.unlock(); }
void VideoDD::clear() { p.clear(); }
void VideoDD::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoDD::init() { return p.init(); }
void VideoDD::term() { p.term(); }
VideoDD::VideoDD() : p(*new pVideoDD(*this)) {}
VideoDD::~VideoDD() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,22 @@
class pVideoDD;
class VideoDD : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoDD();
~VideoDD();
private:
pVideoDD &p;
};

View File

@@ -0,0 +1,103 @@
#include <assert.h>
#include <windows.h>
namespace ruby {
#include "gdi.hpp"
class pVideoGDI {
public:
VideoGDI &self;
uint32_t *buffer;
HBITMAP bitmap;
HDC bitmapdc;
BITMAPINFO bmi;
struct {
HWND handle;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = (HWND)param;
return true;
}
return false;
}
bool lock(uint32_t *&data, unsigned &pitch) {
pitch = 1024 * 4;
return data = buffer;
}
void unlock() {}
void refresh(unsigned r_width, unsigned r_height) {
RECT rc;
GetClientRect(settings.handle, &rc);
SetDIBits(bitmapdc, bitmap, 0, r_height, (void*)buffer, &bmi, DIB_RGB_COLORS);
HDC hdc = GetDC(settings.handle);
StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, bitmapdc, 0, 1024 - r_height, r_width, r_height, SRCCOPY);
ReleaseDC(settings.handle, hdc);
}
bool init() {
HDC hdc = GetDC(settings.handle);
bitmapdc = CreateCompatibleDC(hdc);
assert(bitmapdc);
bitmap = CreateCompatibleBitmap(hdc, 1024, 1024);
assert(bitmap);
SelectObject(bitmapdc, bitmap);
ReleaseDC(settings.handle, hdc);
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 1024;
bmi.bmiHeader.biHeight = -1024;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint32_t);
return true;
}
void term() {
DeleteObject(bitmap);
DeleteDC(bitmapdc);
}
pVideoGDI(VideoGDI &self_) : self(self_) {
buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t));
settings.handle = 0;
}
~pVideoGDI() {
if(buffer) free(buffer);
}
};
bool VideoGDI::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoGDI::get(Setting setting) { return p.get(setting); }
bool VideoGDI::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoGDI::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoGDI::unlock() { p.unlock(); }
void VideoGDI::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoGDI::init() { return p.init(); }
void VideoGDI::term() { p.term(); }
VideoGDI::VideoGDI() : p(*new pVideoGDI(*this)) {}
VideoGDI::~VideoGDI() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,21 @@
class pVideoGDI;
class VideoGDI : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoGDI();
~VideoGDI();
private:
pVideoGDI &p;
};

View File

@@ -0,0 +1,292 @@
/*
video.glx
author: byuu
license: public domain
last updated: 2008-08-20
Design notes:
SGI's GLX is the X11/Xlib interface to OpenGL.
At the time of this writing, there are three relevant versions of the API: versions 1.2, 1.3 and 1.4.
Version 1.2 was released on March 4th, 1997.
Version 1.3 was released on October 19th, 1998.
Version 1.4 was released on December 16th, 2005.
Despite version 1.3 being roughly ten years old at this time, there are still many modern X11 GLX drivers
that lack full support for the specification. Most notable would be the official video drivers from ATI.
Given this, 1.4 support is pretty much hopeless to target.
Luckily, each version has been designed to be backwards compatible with the previous version. As well,
version 1.2 is wholly sufficient, albeit less convenient, to implement this video module.
Therefore, for the purpose of compatibility, this driver only uses GLX 1.2 or earlier API commands.
As well, it only uses raw Xlib API commands, so that it is compatible with any toolkit.
*/
#include <GL/gl.h>
#include <GL/glx.h>
namespace ruby {
#include "glx.hpp"
//returns true once window is mapped (created and displayed onscreen)
static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
class pVideoGLX {
public:
VideoGLX &self;
uint32_t *buffer;
Display *display;
int screen;
Window xwindow;
Colormap colormap;
GLXContext glxcontext;
GLXWindow glxwindow;
GLuint gltexture;
struct {
int version_major, version_minor;
bool double_buffer;
bool is_direct;
} glx;
struct {
Window handle;
bool synchronize;
unsigned filter;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
if(setting == Video::Filter) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
if(setting == Video::Filter) return settings.filter;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = param;
return true;
}
if(setting == Video::Synchronize) {
if(settings.synchronize != param) {
settings.synchronize = param;
if(glxcontext) {
term();
init();
}
return true;
}
}
if(setting == Video::Filter) {
settings.filter = param;
return true;
}
return false;
}
bool lock(uint32_t *&data, unsigned &pitch) {
pitch = 1024 * 4;
return data = buffer;
}
void unlock() {
}
void clear() {
memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
}
void refresh(unsigned width, unsigned height) {
//we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used.
//therefore, inelegant as it may be, we query each window size and resize as needed.
XWindowAttributes parent, child;
XGetWindowAttributes(display, settings.handle, &parent);
XGetWindowAttributes(display, xwindow, &child);
if(child.width != parent.width || child.height != parent.height) {
XResizeWindow(display, xwindow, parent.width, parent.height);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, parent.width, 0, parent.height, -1.0, 1.0);
glViewport(0, 0, parent.width, parent.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); //length of buffer in pixels
glTexSubImage2D(GL_TEXTURE_2D,
/* mip-map level = */ 0, /* x = */ 0, /* y = */ 0,
width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
//OpenGL projection sets 0,0 as *bottom-left* of screen.
//therefore, below vertices flip image to support top-left source.
//texture range = x1:0.0, y1:0.0, x2:1.0, y2:1.0
//vertex range = x1:0, y1:0, x2:width, y2:height
double w = double(width) / 1024.0;
double h = double(height) / 1024.0;
int u = parent.width;
int v = parent.height;
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex3i(0, v, 0);
glTexCoord2f(w, 0); glVertex3i(u, v, 0);
glTexCoord2f(0, h); glVertex3i(0, 0, 0);
glTexCoord2f(w, h); glVertex3i(u, 0, 0);
glEnd();
glFlush();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
}
bool init() {
display = XOpenDisplay(0);
screen = DefaultScreen(display);
glXQueryVersion(display, &glx.version_major, &glx.version_minor);
//require GLX 1.2+ API
if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false;
buffer = new(zeromemory) uint32_t[1024 * 1024];
XWindowAttributes window_attributes;
XGetWindowAttributes(display, settings.handle, &window_attributes);
//let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute
int elements = 0;
int attributelist[] = { GLX_RGBA, None };
int attributelist_sync[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi = glXChooseVisual(display, screen,
settings.synchronize ? attributelist_sync : attributelist);
//Window settings.handle has already been realized, most likely with DefaultVisual.
//GLX requires that the GL output window has the same Visual as the GLX context.
//it is not possible to change the Visual of an already realized (created) window.
//therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle.
colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XSetWindowBackground(display, xwindow, /* color = */ 0);
XMapWindow(display, xwindow);
XEvent event;
//window must be realized (appear onscreen) before we make the context current
XIfEvent(display, &event, glx_wait_for_map_notify, (char*)xwindow);
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
//read attributes of frame buffer for later use, as requested attributes from above are not always granted
int value = 0;
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value);
glx.double_buffer = value;
glx.is_direct = glXIsDirect(display, glxcontext);
//disable unused features
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_SMOOTH);
glDisable(GL_STENCIL_TEST);
//enable useful and required features
glEnable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
//create GL texture to copy buffer to
gltexture = 0;
glGenTextures(1, &gltexture);
glBindTexture(GL_TEXTURE_2D, gltexture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024);
glTexImage2D(GL_TEXTURE_2D,
/* mip-map level = */ 0, /* internal format = */ GL_RGB,
/* width = */ 1024, /* height = */ 1024, /* border = */ 0,
/* format = */ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
return true;
}
void term() {
if(gltexture) {
glDeleteTextures(1, &gltexture);
gltexture = 0;
}
if(glxcontext) {
glXDestroyContext(display, glxcontext);
glxcontext = 0;
}
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
if(buffer) {
delete[] buffer;
buffer = 0;
}
}
pVideoGLX(VideoGLX &self_) : self(self_) {
settings.handle = 0;
settings.synchronize = false;
xwindow = 0;
colormap = 0;
glxcontext = 0;
glxwindow = 0;
gltexture = 0;
}
~pVideoGLX() { term(); }
};
bool VideoGLX::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoGLX::get(Setting setting) { return p.get(setting); }
bool VideoGLX::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoGLX::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoGLX::unlock() { p.unlock(); }
void VideoGLX::clear() { p.clear(); }
void VideoGLX::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoGLX::init() { return p.init(); }
void VideoGLX::term() { p.term(); }
VideoGLX::VideoGLX() : p(*new pVideoGLX(*this)) {}
VideoGLX::~VideoGLX() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,22 @@
class pVideoGLX;
class VideoGLX : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoGLX();
~VideoGLX();
private:
pVideoGLX &p;
};

View File

@@ -0,0 +1,137 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#include <X11/extensions/XShm.h>
#include <SDL/SDL.h>
namespace ruby {
#include "sdl.hpp"
class pVideoSDL {
public:
VideoSDL &self;
Display *display;
SDL_Surface *screen, *buffer;
struct {
uintptr_t handle;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return settings.handle;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = param;
return true;
}
return false;
}
bool lock(uint32_t *&data, unsigned &pitch) {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
pitch = buffer->pitch;
return data = (uint32_t*)buffer->pixels;
}
void unlock() {
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
}
void clear() {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
uint32_t *data = (uint32_t*)buffer->pixels;
for(unsigned y = 0; y < 1024; y++) {
for(unsigned x = 0; x < 1024; x++) {
*data++ |= 0xff000000;
}
data += (buffer->pitch >> 2) - 1024;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
refresh(1024, 1024);
}
void refresh(unsigned width, unsigned height) {
//ruby input is X8R8G8B8, top 8-bits are ignored.
//as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity)
//to prevent blending against the window beneath when X window visual is 32-bits.
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
uint32_t *data = (uint32_t*)buffer->pixels;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
*data++ |= 0xff000000;
}
data += (buffer->pitch >> 2) - width;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
SDL_Rect src, dest;
src.x = 0;
src.y = 0;
src.w = width;
src.h = height;
dest.x = 0;
dest.y = 0;
dest.w = attributes.width;
dest.h = attributes.height;
SDL_SoftStretch(buffer, &src, screen, &dest);
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
}
bool init() {
display = XOpenDisplay(0);
char env[512];
sprintf(env, "SDL_WINDOWID=%ld", settings.handle);
putenv(env);
SDL_InitSubSystem(SDL_INIT_VIDEO);
//screen depth must be 32, as 24bpp with a 32-bit X window visual produces no output.
screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
//buffer depth must be 32, as this is the input format used by all ruby drivers.
buffer = SDL_CreateRGBSurface(SDL_HWSURFACE,
1024, 1024, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
);
return true;
}
void term() {
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
pVideoSDL(VideoSDL &self_) : self(self_) {
settings.handle = 0;
}
};
bool VideoSDL::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoSDL::get(Setting setting) { return p.get(setting); }
bool VideoSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoSDL::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoSDL::unlock() { p.unlock(); }
void VideoSDL::clear() { p.clear(); }
void VideoSDL::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoSDL::init() { return p.init(); }
void VideoSDL::term() { p.term(); }
VideoSDL::VideoSDL() : p(*new pVideoSDL(*this)) {}
VideoSDL::~VideoSDL() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,22 @@
class pVideoSDL;
class VideoSDL : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoSDL();
~VideoSDL();
private:
pVideoSDL &p;
};

View File

@@ -0,0 +1,214 @@
/*
video.wgl
authors: byuu, krom
license: public domain
last updated: 2008-08-20
*/
#include <windows.h>
#include <GL/gl.h>
#include <GL/glext.h>
namespace ruby {
#include "wgl.hpp"
class pVideoWGL {
public:
VideoWGL &self;
uint32_t *buffer;
HDC display;
HGLRC wglcontext;
HWND window;
HINSTANCE glwindow;
GLuint gltexture;
struct {
HWND handle;
bool synchronize;
unsigned filter;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
if(setting == Video::Filter) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return (uintptr_t)settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
if(setting == Video::Filter) return settings.filter;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = (HWND)param;
return true;
}
if(setting == Video::Synchronize) {
if(settings.synchronize != param) {
settings.synchronize = param;
if(wglcontext) {
term();
init();
}
}
}
if(setting == Video::Filter) {
settings.filter = param;
return true;
}
return false;
}
bool lock(uint32_t *&data, unsigned &pitch) {
pitch = 1024 * 4;
return data = buffer;
}
void unlock() {
}
void clear() {
memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
SwapBuffers(display);
}
void refresh(unsigned width, unsigned height) {
RECT rc;
GetClientRect(settings.handle, &rc);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1.0, 1.0);
glViewport(0, 0, rc.right, rc.bottom);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); //length of buffer in pixels
glTexSubImage2D(GL_TEXTURE_2D,
/* mip-map level = */ 0, /* x = */ 0, /* y = */ 0,
width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
//OpenGL projection sets 0,0 as *bottom-left* of screen.
//therefore, below vertices flip image to support top-left source.
//texture range = x1:0.0, y1:0.0, x2:1.0, y2:1.0
//vertex range = x1:0, y1:0, x2:width, y2:height
double w = double(width) / 1024.0;
double h = double(height) / 1024.0;
int u = width;
int v = height;
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex3i(0, v, 0);
glTexCoord2f(w, 0); glVertex3i(u, v, 0);
glTexCoord2f(0, h); glVertex3i(0, 0, 0);
glTexCoord2f(w, h); glVertex3i(u, 0, 0);
glEnd();
glFlush();
SwapBuffers(display);
}
bool init() {
buffer = new(zeromemory) uint32_t[1024 * 1024];
GLuint pixel_format;
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | (settings.synchronize ? PFD_DOUBLEBUFFER : 0);
pfd.iPixelType = PFD_TYPE_RGBA;
display = GetDC(settings.handle);
pixel_format = ChoosePixelFormat(display, &pfd);
SetPixelFormat(display, pixel_format, &pfd);
wglcontext = wglCreateContext(display);
wglMakeCurrent(display, wglcontext);
//disable unused features
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_SMOOTH);
glDisable(GL_STENCIL_TEST);
//enable useful and required features
glEnable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
//create GL texture to copy buffer to
gltexture = 0;
glGenTextures(1, &gltexture);
glBindTexture(GL_TEXTURE_2D, gltexture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024);
glTexImage2D(GL_TEXTURE_2D,
/* mip-map level = */ 0, /* internal format = */ GL_RGB,
/* width = */ 1024, /* height = */ 1024, /* border = */ 0,
/* format = */ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
return true;
}
void term() {
if(gltexture) {
glDeleteTextures(1, &gltexture);
gltexture = 0;
}
if(wglcontext) {
wglDeleteContext(wglcontext);
wglcontext = 0;
}
if(buffer) {
delete[] buffer;
buffer = 0;
}
}
pVideoWGL(VideoWGL &self_) : self(self_) {
settings.handle = 0;
settings.synchronize = false;
settings.filter = 0;
window = 0;
wglcontext = 0;
glwindow = 0;
gltexture = 0;
}
~pVideoWGL() { term(); }
};
bool VideoWGL::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoWGL::get(Setting setting) { return p.get(setting); }
bool VideoWGL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoWGL::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoWGL::unlock() { p.unlock(); }
void VideoWGL::clear() { p.clear(); }
void VideoWGL::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoWGL::init() { return p.init(); }
void VideoWGL::term() { p.term(); }
VideoWGL::VideoWGL() : p(*new pVideoWGL(*this)) {}
VideoWGL::~VideoWGL() { delete &p; }
} //namespace ruby

View File

@@ -0,0 +1,22 @@
class pVideoWGL;
class VideoWGL : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoWGL();
~VideoWGL();
private:
pVideoWGL &p;
};

469
tools/bsnes/lib/ruby/video/xv.cpp Executable file
View File

@@ -0,0 +1,469 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*);
namespace ruby {
#include "xv.hpp"
class pVideoXv {
public:
VideoXv &self;
uint32_t *buffer;
uint8_t *ytable, *utable, *vtable;
enum XvFormat {
XvFormatRGB32,
XvFormatRGB24,
XvFormatRGB16,
XvFormatRGB15,
XvFormatYUY2,
XvFormatUYVY,
XvFormatUnknown
};
struct {
Display *display;
GC gc;
Window window;
Colormap colormap;
XShmSegmentInfo shminfo;
int port;
int depth;
int visualid;
XvImage *image;
XvFormat format;
uint32_t fourcc;
} device;
struct {
Window handle;
bool synchronize;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) {
return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None;
}
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
return false;
}
bool set(Video::Setting setting, uintptr_t param) {
if(setting == Video::Handle) {
settings.handle = param;
return true;
}
if(setting == Video::Synchronize) {
Display *display = XOpenDisplay(0);
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
if(atom != None && device.port >= 0) {
settings.synchronize = param;
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
return true;
}
return false;
}
return false;
}
bool lock(uint32_t *&data, unsigned &pitch) {
pitch = 1024 * 4;
return data = buffer;
}
void unlock() {
}
void clear() {
memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t));
//clear twice in case video is double buffered ...
refresh(1024, 1024);
refresh(1024, 1024);
}
void refresh(unsigned width, unsigned height) {
XWindowAttributes target;
XGetWindowAttributes(device.display, device.window, &target);
//we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used.
//therefore, query each window size and resize as needed.
XWindowAttributes parent;
XGetWindowAttributes(device.display, settings.handle, &parent);
if(target.width != parent.width || target.height != parent.height) {
XResizeWindow(device.display, device.window, parent.width, parent.height);
}
//update target width and height attributes
XGetWindowAttributes(device.display, device.window, &target);
switch(device.format) {
case XvFormatRGB32: render_rgb32(width, height); break;
case XvFormatRGB24: render_rgb24(width, height); break;
case XvFormatRGB16: render_rgb16(width, height); break;
case XvFormatRGB15: render_rgb15(width, height); break;
case XvFormatYUY2: render_yuy2 (width, height); break;
case XvFormatUYVY: render_uyvy (width, height); break;
}
XvShmPutImage(device.display, device.port, device.window, device.gc, device.image,
0, 0, width, height,
0, 0, target.width, target.height,
true);
}
bool init() {
device.display = XOpenDisplay(0);
if(!XShmQueryExtension(device.display)) {
fprintf(stderr, "VideoXv: XShm extension not found.\n");
return false;
}
//find an appropriate Xv port
device.port = -1;
XvAdaptorInfo *adaptor_info;
unsigned adaptor_count;
XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info);
for(unsigned i = 0; i < adaptor_count; i++) {
//find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks
if(adaptor_info[i].num_formats < 1) continue;
if(!(adaptor_info[i].type & XvInputMask)) continue;
if(!(adaptor_info[i].type & XvImageMask)) continue;
device.port = adaptor_info[i].base_id;
device.depth = adaptor_info[i].formats->depth;
device.visualid = adaptor_info[i].formats->visual_id;
break;
}
XvFreeAdaptorInfo(adaptor_info);
if(device.port < 0) {
fprintf(stderr, "VideoXv: failed to find valid XvPort.\n");
return false;
}
//create child window to attach to parent window.
//this is so that even if parent window visual depth doesn't match Xv visual
//(common with composited windows), Xv can still render to child window.
XWindowAttributes window_attributes;
XGetWindowAttributes(device.display, settings.handle, &window_attributes);
XVisualInfo visualtemplate;
visualtemplate.visualid = device.visualid;
visualtemplate.screen = DefaultScreen(device.display);
visualtemplate.depth = device.depth;
visualtemplate.visual = 0;
int visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if(visualmatches < 1 || !visualinfo->visual) {
if(visualinfo) XFree(visualinfo);
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
return false;
}
device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = device.colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
device.window = XCreateWindow(device.display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, device.depth, InputOutput, visualinfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XFree(visualinfo);
XSetWindowBackground(device.display, device.window, /* color = */ 0);
XMapWindow(device.display, device.window);
device.gc = XCreateGC(device.display, device.window, 0, 0);
//set colorkey to auto paint, so that Xv video output is always visible
Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1);
//find optimal rendering format
device.format = XvFormatUnknown;
signed format_count;
XvImageFormatValues *format = XvListImageFormats(device.display, device.port, &format_count);
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) {
device.format = XvFormatRGB32;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) {
device.format = XvFormatRGB24;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 16) {
device.format = XvFormatRGB16;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 15) {
device.format = XvFormatRGB15;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V'
) {
device.format = XvFormatYUY2;
device.fourcc = format[i].id;
break;
}
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y'
) {
device.format = XvFormatUYVY;
device.fourcc = format[i].id;
break;
}
}
}
free(format);
if(device.format == XvFormatUnknown) {
fprintf(stderr, "VideoXv: unable to find a supported image format.\n");
return false;
}
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, 1024, 1024, &device.shminfo);
if(!device.image) {
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
return false;
}
device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777);
device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0);
device.shminfo.readOnly = false;
if(!XShmAttach(device.display, &device.shminfo)) {
fprintf(stderr, "VideoXv: XShmAttach failed.\n");
return false;
}
buffer = new uint32_t[1024 * 1024];
init_yuv_tables();
clear();
return true;
}
void term() {
XShmDetach(device.display, &device.shminfo);
if(device.window) {
XUnmapWindow(device.display, device.window);
device.window = 0;
}
if(device.colormap) {
XFreeColormap(device.display, device.colormap);
device.colormap = 0;
}
if(buffer) { delete[] buffer; buffer = 0; }
if(ytable) { delete[] ytable; ytable = 0; }
if(utable) { delete[] utable; utable = 0; }
if(vtable) { delete[] vtable; vtable = 0; }
}
void render_rgb32(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint32_t *output = (uint32_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
memcpy(output, input, width * 4);
input += 1024 - width;
output += 1024 - width;
}
}
void render_rgb24(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint8_t *output = (uint8_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = p;
*output++ = p >> 8;
*output++ = p >> 16;
}
input += (1024 - width);
output += (1024 - width) * 3;
}
}
void render_rgb16(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_rgb15(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_yuy2(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (u << 8) | ytable[p0];
*output++ = (v << 8) | ytable[p1];
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_uyvy(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (ytable[p0] << 8) | u;
*output++ = (ytable[p1] << 8) | v;
}
input += 1024 - width;
output += 1024 - width;
}
}
void init_yuv_tables() {
ytable = new uint8_t[65536];
utable = new uint8_t[65536];
vtable = new uint8_t[65536];
for(unsigned i = 0; i < 65536; i++) {
//extract RGB565 color data from i
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
r = (r << 3) | (r >> 2); //R5->R8
g = (g << 2) | (g >> 4); //G6->G8
b = (b << 3) | (b >> 2); //B5->B8
//ITU-R Recommendation BT.601
//double lr = 0.299, lg = 0.587, lb = 0.114;
int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 );
int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 );
int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 );
//ITU-R Recommendation BT.709
//double lr = 0.2126, lg = 0.7152, lb = 0.0722;
//int y = int( double(r) * lr + double(g) * lg + double(b) * lb );
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
}
}
pVideoXv(VideoXv &self_) : self(self_) {
device.window = 0;
device.colormap = 0;
device.port = -1;
ytable = 0;
utable = 0;
vtable = 0;
settings.handle = 0;
settings.synchronize = false;
}
};
bool VideoXv::cap(Setting setting) { return p.cap(setting); }
uintptr_t VideoXv::get(Setting setting) { return p.get(setting); }
bool VideoXv::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool VideoXv::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); }
void VideoXv::unlock() { p.unlock(); }
void VideoXv::clear() { p.clear(); }
void VideoXv::refresh(unsigned width, unsigned height) { p.refresh(width, height); }
bool VideoXv::init() { return p.init(); }
void VideoXv::term() { p.term(); }
VideoXv::VideoXv() : p(*new pVideoXv(*this)) {}
VideoXv::~VideoXv() { delete &p; }
}

View File

@@ -0,0 +1,22 @@
class pVideoXv;
class VideoXv : public Video {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool lock(uint32_t *&data, unsigned &pitch);
void unlock();
void clear();
void refresh(unsigned width, unsigned height);
bool init();
void term();
VideoXv();
~VideoXv();
private:
pVideoXv &p;
};