mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-29 15:39:59 +02:00
Include all the code from the bsnes v068 tarball.
byuu describes the changes since v067: This release officially introduces the accuracy and performance cores, alongside the previously-existing compatibility core. The accuracy core allows the most accurate SNES emulation ever seen, with every last processor running at the lowest possible clock synchronization level. The performance core allows slower computers the chance to finally use bsnes. It is capable of attaining 60fps in standard games even on an entry-level Intel Atom processor, commonly found in netbooks. The accuracy core is absolutely not meant for casual gaming at all. It is meant solely for getting as close to 100% perfection as possible, no matter the cost to speed. It should only be used for testing, development or debugging. The compatibility core is identical to bsnes v067 and earlier, but is now roughly 10% faster. This is the default and recommended core for casual gaming. The performance core contains an entirely new S-CPU core, with range-tested IRQs; and uses blargg's heavily-optimized S-DSP core directly. Although there are very minor accuracy tradeoffs to increase speed, I am confident that the performance core is still more accurate and compatible than any other SNES emulator. The S-CPU, S-SMP, S-DSP, SuperFX and SA-1 processors are all clock-based, just as in the accuracy and compatibility cores; and as always, there are zero game-specific hacks. Its compatibility is still well above 99%, running even the most challenging games flawlessly. If you have held off from using bsnes in the past due to its system requirements, please give the performance core a try. I think you will be impressed. I'm also not finished: I believe performance can be increased even further. I would also strongly suggest Windows Vista and Windows 7 users to take advantage of the new XAudio2 driver by OV2. Not only does it give you a performance boost, it also lowers latency and provides better sound by way of skipping an API emulation layer. Changelog: - Split core into three profiles: accuracy, compatibility and performance - Accuracy core now takes advantage of variable-bitlength integers (eg uint24_t) - Performance core uses a new S-CPU core, written from scratch for speed - Performance core uses blargg's snes_dsp library for S-DSP emulation - Binaries are now compiled using GCC 4.5 - Added a workaround in the SA-1 core for a bug in GCC 4.5+ - The clock-based S-PPU renderer has greatly improved OAM emulation; fixing Winter Gold and Megalomania rendering issues - Corrected pseudo-hires color math in the clock-based S-PPU renderer; fixing Super Buster Bros backgrounds - Fixed a clamping bug in the Cx4 16-bit triangle operation [Jonas Quinn]; fixing Mega Man X2 "gained weapon" star background effect - Updated video renderer to properly handle mixed-resolution screens with interlace enabled; fixing Air Strike Patrol level briefing screen - Added mightymo's 2010-08-19 cheat code pack - Windows port: added XAudio2 output support [OV2] - Source: major code restructuring; virtual base classes for processor - cores removed, build system heavily modified, etc.
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
class Audio {
|
||||
public:
|
||||
static const char *Volume;
|
||||
static const char *Resample;
|
||||
static const char *ResampleRatio;
|
||||
|
||||
static const char *Handle;
|
||||
static const char *Synchronize;
|
||||
static const char *Frequency;
|
||||
static const char *Latency;
|
||||
|
||||
virtual bool cap(const nall::string& name) { return false; }
|
||||
virtual nall::any get(const nall::string& name) { return false; }
|
||||
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
|
||||
|
||||
virtual void sample(uint16_t left, uint16_t right) {}
|
||||
virtual void clear() {}
|
||||
virtual bool init() { return true; }
|
||||
virtual void term() {}
|
||||
|
||||
Audio() {}
|
||||
virtual ~Audio() {}
|
||||
};
|
@@ -1,240 +0,0 @@
|
||||
//audio.alsa (2009-11-30)
|
||||
//authors: BearOso, byuu, Nach, RedDwarf
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioALSA {
|
||||
public:
|
||||
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(const string& name) {
|
||||
if(name == Audio::Synchronize) return true;
|
||||
if(name == Audio::Frequency) return true;
|
||||
if(name == Audio::Latency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Synchronize) return settings.synchronize;
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
if(name == Audio::Latency) return settings.latency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Synchronize) {
|
||||
if(settings.synchronize != any_cast<bool>(value)) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
if(device.handle) init();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Frequency) {
|
||||
if(settings.frequency != any_cast<unsigned>(value)) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(device.handle) init();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Latency) {
|
||||
if(settings.latency != any_cast<unsigned>(value)) {
|
||||
settings.latency = any_cast<unsigned>(value);
|
||||
if(device.handle) 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;
|
||||
|
||||
snd_pcm_sframes_t avail;
|
||||
do {
|
||||
avail = snd_pcm_avail_update(device.handle);
|
||||
if(avail < 0) snd_pcm_recover(device.handle, avail, 1);
|
||||
if(avail < buffer.length) {
|
||||
if(settings.synchronize == false) {
|
||||
buffer.length = 0;
|
||||
return;
|
||||
}
|
||||
int error = snd_pcm_wait(device.handle, -1);
|
||||
if(error < 0) snd_pcm_recover(device.handle, error, 1);
|
||||
}
|
||||
} while(avail < buffer.length);
|
||||
|
||||
//below code has issues with PulseAudio sound server
|
||||
#if 0
|
||||
if(settings.synchronize == false) {
|
||||
snd_pcm_sframes_t avail = snd_pcm_avail_update(device.handle);
|
||||
if(avail < device.period_size) {
|
||||
buffer.length = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
//below code will not work with 24khz frequency rate (ALSA library bug)
|
||||
#if 0
|
||||
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
device.channels, settings.frequency, 1, settings.latency * 1000) < 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 * 1000 * 1e-6 * settings.frequency / 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
unsigned rate = settings.frequency;
|
||||
unsigned buffer_time = settings.latency * 1000;
|
||||
unsigned period_time = settings.latency * 1000 / 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() {
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(ALSA)
|
||||
|
||||
};
|
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
audio.ao (2008-06-01)
|
||||
authors: Nach, RedDwarf
|
||||
*/
|
||||
|
||||
#include <ao/ao.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioAO {
|
||||
public:
|
||||
int driver_id;
|
||||
ao_sample_format driver_format;
|
||||
ao_device *audio_device;
|
||||
|
||||
struct {
|
||||
unsigned frequency;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Audio::Frequency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(audio_device) 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
|
||||
}
|
||||
|
||||
void clear() {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
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() {
|
||||
audio_device = 0;
|
||||
ao_initialize();
|
||||
|
||||
settings.frequency = 22050;
|
||||
}
|
||||
|
||||
~pAudioAO() {
|
||||
term();
|
||||
//ao_shutdown(); //FIXME: this is causing a segfault for some reason when called ...
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(AO)
|
||||
|
||||
};
|
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
audio.directsound (2007-12-26)
|
||||
author: byuu
|
||||
*/
|
||||
|
||||
#include <dsound.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioDS {
|
||||
public:
|
||||
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(const string& name) {
|
||||
if(name == Audio::Handle) return true;
|
||||
if(name == Audio::Synchronize) return true;
|
||||
if(name == Audio::Frequency) return true;
|
||||
if(name == Audio::Latency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Audio::Synchronize) return settings.synchronize;
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
if(name == Audio::Latency) return settings.latency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
if(ds) clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(ds) init();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Latency) {
|
||||
settings.latency = any_cast<unsigned>(value);
|
||||
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(settings.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() {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(DS)
|
||||
|
||||
};
|
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
audio.openal (2007-12-26)
|
||||
author: Nach
|
||||
contributors: byuu, wertigon, _willow_
|
||||
*/
|
||||
|
||||
#if defined(PLATFORM_OSX)
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioOpenAL {
|
||||
public:
|
||||
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(const string& name) {
|
||||
if(name == Audio::Synchronize) return true;
|
||||
if(name == Audio::Frequency) return true;
|
||||
if(name == Audio::Latency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Synchronize) return settings.synchronize;
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
if(name == Audio::Latency) return settings.latency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Latency) {
|
||||
if(settings.latency != any_cast<unsigned>(value)) {
|
||||
settings.latency = any_cast<unsigned>(value);
|
||||
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 clear() {
|
||||
}
|
||||
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(OpenAL)
|
||||
|
||||
};
|
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
audio.oss (2007-12-26)
|
||||
author: Nach
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
class pAudioOSS {
|
||||
public:
|
||||
struct {
|
||||
int fd;
|
||||
int format;
|
||||
int channels;
|
||||
const char *name;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
unsigned frequency;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Audio::Frequency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(device.fd > 0) 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);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
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() {
|
||||
device.fd = -1;
|
||||
device.format = AFMT_S16_LE;
|
||||
device.channels = 2;
|
||||
device.name = "/dev/dsp";
|
||||
|
||||
settings.frequency = 22050;
|
||||
}
|
||||
|
||||
~pAudioOSS() {
|
||||
term();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(OSS)
|
||||
|
||||
};
|
@@ -1,177 +0,0 @@
|
||||
//audio.pulseaudio (2010-01-05)
|
||||
//author: RedDwarf
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioPulseAudio {
|
||||
public:
|
||||
struct {
|
||||
pa_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
pa_sample_spec spec;
|
||||
pa_buffer_attr buffer_attr;
|
||||
bool first;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
uint32_t *data;
|
||||
size_t size;
|
||||
unsigned offset;
|
||||
} buffer;
|
||||
|
||||
struct {
|
||||
bool synchronize;
|
||||
unsigned frequency;
|
||||
unsigned latency;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Audio::Synchronize) return true;
|
||||
if(name == Audio::Frequency) return true;
|
||||
if(name == Audio::Latency) return true;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Synchronize) return settings.synchronize;
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
if(name == Audio::Latency) return settings.latency;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(device.stream) {
|
||||
pa_operation_unref(pa_stream_update_sample_rate(device.stream, settings.frequency, NULL, NULL));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Latency) {
|
||||
settings.latency = any_cast<unsigned>(value);
|
||||
if(device.stream) {
|
||||
device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec);
|
||||
pa_stream_set_buffer_attr(device.stream, &device.buffer_attr, NULL, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void sample(uint16_t left, uint16_t right) {
|
||||
pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size);
|
||||
buffer.data[buffer.offset++] = left + (right << 16);
|
||||
if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return;
|
||||
|
||||
while(true) {
|
||||
if(device.first) {
|
||||
device.first = false;
|
||||
pa_mainloop_iterate(device.mainloop, 0, NULL);
|
||||
} else {
|
||||
pa_mainloop_iterate(device.mainloop, 1, NULL);
|
||||
}
|
||||
unsigned length = pa_stream_writable_size(device.stream);
|
||||
if(length >= buffer.offset * pa_frame_size(&device.spec)) break;
|
||||
if(settings.synchronize == false) {
|
||||
buffer.offset = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pa_stream_write(device.stream, (const void*)buffer.data, buffer.offset * pa_frame_size(&device.spec), NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
buffer.data = 0;
|
||||
buffer.offset = 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
device.mainloop = pa_mainloop_new();
|
||||
|
||||
device.context = pa_context_new(pa_mainloop_get_api(device.mainloop), "ruby::pulseaudio");
|
||||
pa_context_connect(device.context, NULL, PA_CONTEXT_NOFLAGS, NULL);
|
||||
|
||||
pa_context_state_t cstate;
|
||||
do {
|
||||
pa_mainloop_iterate(device.mainloop, 1, NULL);
|
||||
cstate = pa_context_get_state(device.context);
|
||||
if(!PA_CONTEXT_IS_GOOD(cstate)) return false;
|
||||
} while(cstate != PA_CONTEXT_READY);
|
||||
|
||||
device.spec.format = PA_SAMPLE_S16LE;
|
||||
device.spec.channels = 2;
|
||||
device.spec.rate = settings.frequency;
|
||||
device.stream = pa_stream_new(device.context, "audio", &device.spec, NULL);
|
||||
|
||||
device.buffer_attr.maxlength = -1;
|
||||
device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec);
|
||||
device.buffer_attr.prebuf = -1;
|
||||
device.buffer_attr.minreq = -1;
|
||||
device.buffer_attr.fragsize = -1;
|
||||
|
||||
pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_VARIABLE_RATE);
|
||||
pa_stream_connect_playback(device.stream, NULL, &device.buffer_attr, flags, NULL, NULL);
|
||||
|
||||
pa_stream_state_t sstate;
|
||||
do {
|
||||
pa_mainloop_iterate(device.mainloop, 1, NULL);
|
||||
sstate = pa_stream_get_state(device.stream);
|
||||
if(!PA_STREAM_IS_GOOD(sstate)) return false;
|
||||
} while(sstate != PA_STREAM_READY);
|
||||
|
||||
buffer.size = 960;
|
||||
buffer.offset = 0;
|
||||
device.first = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(buffer.data) {
|
||||
pa_stream_cancel_write(device.stream);
|
||||
buffer.data = 0;
|
||||
}
|
||||
|
||||
if(device.stream) {
|
||||
pa_stream_disconnect(device.stream);
|
||||
pa_stream_unref(device.stream);
|
||||
device.stream = 0;
|
||||
}
|
||||
|
||||
if(device.context) {
|
||||
pa_context_disconnect(device.context);
|
||||
pa_context_unref(device.context);
|
||||
device.context = 0;
|
||||
}
|
||||
|
||||
if(device.mainloop) {
|
||||
pa_mainloop_free(device.mainloop);
|
||||
device.mainloop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pAudioPulseAudio() {
|
||||
device.mainloop = 0;
|
||||
device.context = 0;
|
||||
device.stream = 0;
|
||||
buffer.data = 0;
|
||||
settings.synchronize = false;
|
||||
settings.frequency = 22050;
|
||||
settings.latency = 60;
|
||||
}
|
||||
|
||||
~pAudioPulseAudio() {
|
||||
term();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(PulseAudio)
|
||||
|
||||
}
|
@@ -1,115 +0,0 @@
|
||||
//audio.pulseaudiosimple (2010-01-05)
|
||||
//author: byuu
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioPulseAudioSimple {
|
||||
public:
|
||||
struct {
|
||||
pa_simple *handle;
|
||||
pa_sample_spec spec;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
uint32_t *data;
|
||||
unsigned offset;
|
||||
} buffer;
|
||||
|
||||
struct {
|
||||
unsigned frequency;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Audio::Frequency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(device.handle) 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;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
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::pulseaudiosimple", //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::pulseaudiosimple 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;
|
||||
}
|
||||
}
|
||||
|
||||
pAudioPulseAudioSimple() {
|
||||
device.handle = 0;
|
||||
buffer.data = 0;
|
||||
settings.frequency = 22050;
|
||||
}
|
||||
|
||||
~pAudioPulseAudioSimple() {
|
||||
term();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(PulseAudioSimple)
|
||||
|
||||
};
|
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
audio.xaudio2 (2010-08-14)
|
||||
author: OV2
|
||||
*/
|
||||
|
||||
#include "xaudio2.hpp"
|
||||
#include <Windows.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pAudioXAudio2: public IXAudio2VoiceCallback {
|
||||
public:
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice* pMasterVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
|
||||
// inherited from IXAudio2VoiceCallback
|
||||
STDMETHODIMP_(void) OnBufferStart(void *pBufferContext){}
|
||||
STDMETHODIMP_(void) OnLoopEnd(void *pBufferContext){}
|
||||
STDMETHODIMP_(void) OnStreamEnd() {}
|
||||
STDMETHODIMP_(void) OnVoiceError(void *pBufferContext, HRESULT Error) {}
|
||||
STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {}
|
||||
STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {}
|
||||
|
||||
struct {
|
||||
unsigned buffers;
|
||||
unsigned latency;
|
||||
|
||||
uint32_t *buffer;
|
||||
unsigned bufferoffset;
|
||||
|
||||
volatile long submitbuffers;
|
||||
unsigned writebuffer;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
bool synchronize;
|
||||
unsigned frequency;
|
||||
unsigned latency;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Audio::Synchronize) return true;
|
||||
if(name == Audio::Frequency) return true;
|
||||
if(name == Audio::Latency) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Audio::Synchronize) return settings.synchronize;
|
||||
if(name == Audio::Frequency) return settings.frequency;
|
||||
if(name == Audio::Latency) return settings.latency;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Audio::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
if(pXAudio2) clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Frequency) {
|
||||
settings.frequency = any_cast<unsigned>(value);
|
||||
if(pXAudio2) init();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Latency) {
|
||||
settings.latency = any_cast<unsigned>(value);
|
||||
if(pXAudio2) init();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void pushbuffer(unsigned bytes,uint32_t *pAudioData) {
|
||||
XAUDIO2_BUFFER xa2buffer={0};
|
||||
xa2buffer.AudioBytes=bytes;
|
||||
xa2buffer.pAudioData=reinterpret_cast<BYTE *>(pAudioData);
|
||||
xa2buffer.pContext=0;
|
||||
InterlockedIncrement(&device.submitbuffers);
|
||||
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
|
||||
}
|
||||
|
||||
void sample(uint16_t left, uint16_t right) {
|
||||
device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = left + (right << 16);
|
||||
if(device.bufferoffset < device.latency) return;
|
||||
device.bufferoffset = 0;
|
||||
|
||||
if(device.submitbuffers == device.buffers - 1) {
|
||||
if(settings.synchronize == true) {
|
||||
//wait until there is at least one other free buffer for the next sample
|
||||
while(device.submitbuffers == device.buffers - 1) {
|
||||
//Sleep(0);
|
||||
}
|
||||
} else { //we need one free buffer for the next sample, so ignore the current contents
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pushbuffer(device.latency * 4,device.buffer + device.writebuffer * device.latency);
|
||||
|
||||
device.writebuffer = (device.writebuffer + 1) % device.buffers;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if(!pSourceVoice) return;
|
||||
pSourceVoice->Stop(0);
|
||||
pSourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers
|
||||
|
||||
device.writebuffer = 0;
|
||||
|
||||
device.bufferoffset = 0;
|
||||
if(device.buffer) memset(device.buffer, 0, device.latency * device.buffers * 4);
|
||||
|
||||
pSourceVoice->Start(0);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
device.buffers = 8;
|
||||
device.latency = settings.frequency * settings.latency / device.buffers / 1000.0 + 0.5;
|
||||
device.buffer = new uint32_t[device.latency * device.buffers];
|
||||
device.bufferoffset = 0;
|
||||
device.submitbuffers = 0;
|
||||
|
||||
HRESULT hr;
|
||||
if(FAILED(hr = XAudio2Create(&pXAudio2, 0 , XAUDIO2_DEFAULT_PROCESSOR))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, 2,
|
||||
settings.frequency, 0, 0 , NULL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = settings.frequency;
|
||||
wfx.nBlockAlign = 4;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx,
|
||||
XAUDIO2_VOICE_NOSRC , XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(pSourceVoice) {
|
||||
pSourceVoice->Stop(0);
|
||||
pSourceVoice->DestroyVoice();
|
||||
pSourceVoice = 0;
|
||||
}
|
||||
if(pMasterVoice) {
|
||||
pMasterVoice->DestroyVoice();
|
||||
pMasterVoice = 0;
|
||||
}
|
||||
if(pXAudio2) {
|
||||
pXAudio2->Release();
|
||||
pXAudio2 = NULL;
|
||||
}
|
||||
if(device.buffer) {
|
||||
delete[] device.buffer;
|
||||
device.buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(void) OnBufferEnd(void *pBufferContext) {
|
||||
InterlockedDecrement(&device.submitbuffers);
|
||||
}
|
||||
|
||||
pAudioXAudio2() {
|
||||
pXAudio2 = 0;
|
||||
pMasterVoice = 0;
|
||||
pSourceVoice = 0;
|
||||
|
||||
device.buffer = 0;
|
||||
device.bufferoffset = 0;
|
||||
device.submitbuffers = 0;
|
||||
device.writebuffer = 0;
|
||||
|
||||
settings.synchronize = false;
|
||||
settings.frequency = 22050;
|
||||
settings.latency = 120;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareAudio(XAudio2)
|
||||
|
||||
};
|
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
xaudio2.hpp (2010-08-14)
|
||||
author: OV2
|
||||
|
||||
ruby-specific header to provide mingw-friendly xaudio2 interfaces
|
||||
*/
|
||||
|
||||
#ifndef XAUDIO2_RUBY_H
|
||||
#define XAUDIO2_RUBY_H
|
||||
|
||||
#include <BaseTyps.h>
|
||||
|
||||
#define DEFINE_GUID_X(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID_EXT const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
|
||||
#define DEFINE_CLSID_X(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID_X(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
#define DEFINE_IID_X(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID_X(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
#define X2DEFAULT(x) =x
|
||||
|
||||
DEFINE_CLSID_X(XAudio2, e21a7345, eb21, 468e, be, 50, 80, 4d, b9, 7c, f7, 08);
|
||||
DEFINE_CLSID_X(XAudio2_Debug, f7a76c21, 53d4, 46bb, ac, 53, 8b, 45, 9c, ae, 46, bd);
|
||||
DEFINE_IID_X(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2Voice);
|
||||
|
||||
#define XAUDIO2_COMMIT_NOW 0
|
||||
#define XAUDIO2_DEFAULT_CHANNELS 0
|
||||
#define XAUDIO2_DEFAULT_SAMPLERATE 0
|
||||
#define XAUDIO2_DEFAULT_FREQ_RATIO 4.0f
|
||||
#define XAUDIO2_DEBUG_ENGINE 0x0001
|
||||
#define XAUDIO2_VOICE_NOSRC 0x0004
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WAVEFORMATEX Format;
|
||||
union
|
||||
{
|
||||
WORD wValidBitsPerSample;
|
||||
WORD wSamplesPerBlock;
|
||||
WORD wReserved;
|
||||
} Samples;
|
||||
DWORD dwChannelMask;
|
||||
GUID SubFormat;
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
|
||||
typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
|
||||
|
||||
typedef enum XAUDIO2_DEVICE_ROLE
|
||||
{
|
||||
NotDefaultDevice = 0x0,
|
||||
DefaultConsoleDevice = 0x1,
|
||||
DefaultMultimediaDevice = 0x2,
|
||||
DefaultCommunicationsDevice = 0x4,
|
||||
DefaultGameDevice = 0x8,
|
||||
GlobalDefaultDevice = 0xf,
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
} XAUDIO2_DEVICE_ROLE;
|
||||
|
||||
typedef struct XAUDIO2_DEVICE_DETAILS
|
||||
{
|
||||
WCHAR DeviceID[256];
|
||||
WCHAR DisplayName[256];
|
||||
XAUDIO2_DEVICE_ROLE Role;
|
||||
WAVEFORMATEXTENSIBLE OutputFormat;
|
||||
} XAUDIO2_DEVICE_DETAILS;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_DETAILS
|
||||
{
|
||||
UINT32 CreationFlags;
|
||||
UINT32 InputChannels;
|
||||
UINT32 InputSampleRate;
|
||||
} XAUDIO2_VOICE_DETAILS;
|
||||
|
||||
typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
|
||||
{
|
||||
Processor1 = 0x00000001,
|
||||
Processor2 = 0x00000002,
|
||||
Processor3 = 0x00000004,
|
||||
Processor4 = 0x00000008,
|
||||
Processor5 = 0x00000010,
|
||||
Processor6 = 0x00000020,
|
||||
Processor7 = 0x00000040,
|
||||
Processor8 = 0x00000080,
|
||||
Processor9 = 0x00000100,
|
||||
Processor10 = 0x00000200,
|
||||
Processor11 = 0x00000400,
|
||||
Processor12 = 0x00000800,
|
||||
Processor13 = 0x00001000,
|
||||
Processor14 = 0x00002000,
|
||||
Processor15 = 0x00004000,
|
||||
Processor16 = 0x00008000,
|
||||
Processor17 = 0x00010000,
|
||||
Processor18 = 0x00020000,
|
||||
Processor19 = 0x00040000,
|
||||
Processor20 = 0x00080000,
|
||||
Processor21 = 0x00100000,
|
||||
Processor22 = 0x00200000,
|
||||
Processor23 = 0x00400000,
|
||||
Processor24 = 0x00800000,
|
||||
Processor25 = 0x01000000,
|
||||
Processor26 = 0x02000000,
|
||||
Processor27 = 0x04000000,
|
||||
Processor28 = 0x08000000,
|
||||
Processor29 = 0x10000000,
|
||||
Processor30 = 0x20000000,
|
||||
Processor31 = 0x40000000,
|
||||
Processor32 = 0x80000000,
|
||||
XAUDIO2_ANY_PROCESSOR = 0xffffffff,
|
||||
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
|
||||
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_SENDS
|
||||
{
|
||||
UINT32 OutputCount;
|
||||
IXAudio2Voice** pOutputVoices;
|
||||
} XAUDIO2_VOICE_SENDS;
|
||||
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR
|
||||
{
|
||||
IUnknown* pEffect;
|
||||
BOOL InitialState;
|
||||
UINT32 OutputChannels;
|
||||
} XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN
|
||||
{
|
||||
UINT32 EffectCount;
|
||||
const XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors;
|
||||
} XAUDIO2_EFFECT_CHAIN;
|
||||
|
||||
typedef enum XAUDIO2_FILTER_TYPE
|
||||
{
|
||||
LowPassFilter,
|
||||
BandPassFilter,
|
||||
HighPassFilter
|
||||
} XAUDIO2_FILTER_TYPE;
|
||||
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS
|
||||
{
|
||||
XAUDIO2_FILTER_TYPE Type;
|
||||
float Frequency;
|
||||
float OneOverQ;
|
||||
|
||||
} XAUDIO2_FILTER_PARAMETERS;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER
|
||||
{
|
||||
UINT32 Flags;
|
||||
UINT32 AudioBytes;
|
||||
const BYTE* pAudioData;
|
||||
UINT32 PlayBegin;
|
||||
UINT32 PlayLength;
|
||||
UINT32 LoopBegin;
|
||||
UINT32 LoopLength;
|
||||
UINT32 LoopCount;
|
||||
void* pContext;
|
||||
} XAUDIO2_BUFFER;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER_WMA
|
||||
{
|
||||
const UINT32* pDecodedPacketCumulativeBytes;
|
||||
UINT32 PacketCount;
|
||||
} XAUDIO2_BUFFER_WMA;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_STATE
|
||||
{
|
||||
void* pCurrentBufferContext;
|
||||
UINT32 BuffersQueued;
|
||||
UINT64 SamplesPlayed;
|
||||
} XAUDIO2_VOICE_STATE;
|
||||
|
||||
typedef struct XAUDIO2_PERFORMANCE_DATA
|
||||
{
|
||||
UINT64 AudioCyclesSinceLastQuery;
|
||||
UINT64 TotalCyclesSinceLastQuery;
|
||||
UINT32 MinimumCyclesPerQuantum;
|
||||
UINT32 MaximumCyclesPerQuantum;
|
||||
UINT32 MemoryUsageInBytes;
|
||||
UINT32 CurrentLatencyInSamples;
|
||||
UINT32 GlitchesSinceEngineStarted;
|
||||
UINT32 ActiveSourceVoiceCount;
|
||||
UINT32 TotalSourceVoiceCount;
|
||||
UINT32 ActiveSubmixVoiceCount;
|
||||
UINT32 TotalSubmixVoiceCount;
|
||||
UINT32 ActiveXmaSourceVoices;
|
||||
UINT32 ActiveXmaStreams;
|
||||
} XAUDIO2_PERFORMANCE_DATA;
|
||||
|
||||
typedef struct XAUDIO2_DEBUG_CONFIGURATION
|
||||
{
|
||||
UINT32 TraceMask;
|
||||
UINT32 BreakMask;
|
||||
BOOL LogThreadID;
|
||||
BOOL LogFileline;
|
||||
BOOL LogFunctionName;
|
||||
BOOL LogTiming;
|
||||
} XAUDIO2_DEBUG_CONFIGURATION;
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2EngineCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnProcessingPassStart) (THIS) PURE;
|
||||
STDMETHOD_(void, OnProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2VoiceCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2Voice)
|
||||
{
|
||||
#define Declare_IXAudio2Voice_Methods() \
|
||||
STDMETHOD_(void, GetVoiceDetails) (THIS_ XAUDIO2_VOICE_DETAILS* pVoiceDetails) PURE; \
|
||||
STDMETHOD(SetOutputVoices) (THIS_ const XAUDIO2_VOICE_SENDS* pSendList) PURE; \
|
||||
STDMETHOD(SetEffectChain) (THIS_ const XAUDIO2_EFFECT_CHAIN* pEffectChain) PURE; \
|
||||
STDMETHOD(EnableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(DisableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
|
||||
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
const void* pParameters, \
|
||||
UINT32 ParametersByteSize, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void* pParameters, \
|
||||
UINT32 ParametersByteSize) PURE; \
|
||||
STDMETHOD(SetFilterParameters) (THIS_ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetFilterParameters) (THIS_ XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
|
||||
STDMETHOD(SetVolume) (THIS_ float Volume, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetVolume) (THIS_ float* pVolume) PURE; \
|
||||
STDMETHOD(SetChannelVolumes) (THIS_ UINT32 Channels, const float* pVolumes, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetChannelVolumes) (THIS_ UINT32 Channels, float* pVolumes) PURE; \
|
||||
STDMETHOD(SetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
const float* pLevelMatrix, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
float* pLevelMatrix) PURE; \
|
||||
STDMETHOD_(void, DestroyVoice) (THIS) PURE
|
||||
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
STDMETHOD(Start) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(Stop) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(SubmitSourceBuffer) (THIS_ const XAUDIO2_BUFFER* pBuffer, const XAUDIO2_BUFFER_WMA* pBufferWMA X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(FlushSourceBuffers) (THIS) PURE;
|
||||
STDMETHOD(Discontinuity) (THIS) PURE;
|
||||
STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetState) (THIS_ XAUDIO2_VOICE_STATE* pVoiceState) PURE;
|
||||
STDMETHOD(SetFrequencyRatio) (THIS_ float Ratio,
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetFrequencyRatio) (THIS_ float* pRatio) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
{
|
||||
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvInterface) PURE;
|
||||
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
|
||||
STDMETHOD_(ULONG, Release) (THIS) PURE;
|
||||
STDMETHOD(GetDeviceCount) (THIS_ UINT32* pCount) PURE;
|
||||
STDMETHOD(GetDeviceDetails) (THIS_ UINT32 Index, XAUDIO2_DEVICE_DETAILS* pDeviceDetails) PURE;
|
||||
STDMETHOD(Initialize) (THIS_ UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) PURE;
|
||||
STDMETHOD(RegisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD_(void, UnregisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD(CreateSourceVoice) (THIS_ IXAudio2SourceVoice** ppSourceVoice,
|
||||
const WAVEFORMATEX* pSourceFormat,
|
||||
UINT32 Flags X2DEFAULT(0),
|
||||
float MaxFrequencyRatio X2DEFAULT(XAUDIO2_DEFAULT_FREQ_RATIO),
|
||||
IXAudio2VoiceCallback* pCallback X2DEFAULT(NULL),
|
||||
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateSubmixVoice) (THIS_ IXAudio2SubmixVoice** ppSubmixVoice,
|
||||
UINT32 InputChannels, UINT32 InputSampleRate,
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 ProcessingStage X2DEFAULT(0),
|
||||
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateMasteringVoice) (THIS_ IXAudio2MasteringVoice** ppMasteringVoice,
|
||||
UINT32 InputChannels X2DEFAULT(XAUDIO2_DEFAULT_CHANNELS),
|
||||
UINT32 InputSampleRate X2DEFAULT(XAUDIO2_DEFAULT_SAMPLERATE),
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 DeviceIndex X2DEFAULT(0),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(StartEngine) (THIS) PURE;
|
||||
STDMETHOD_(void, StopEngine) (THIS) PURE;
|
||||
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
|
||||
STDMETHOD_(void, GetPerformanceData) (THIS_ XAUDIO2_PERFORMANCE_DATA* pPerfData) PURE;
|
||||
STDMETHOD_(void, SetDebugConfiguration) (THIS_ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
|
||||
void* pReserved X2DEFAULT(NULL)) PURE;
|
||||
};
|
||||
|
||||
__inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
|
||||
{
|
||||
IXAudio2* pXAudio2;
|
||||
HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2,
|
||||
NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, (void**)&pXAudio2);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pXAudio2->Initialize(Flags, XAudio2Processor);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*ppXAudio2 = pXAudio2;
|
||||
}
|
||||
else
|
||||
{
|
||||
pXAudio2->Release();
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
#endif
|
@@ -1,22 +0,0 @@
|
||||
class Input {
|
||||
public:
|
||||
static const char *Handle;
|
||||
static const char *KeyboardSupport;
|
||||
static const char *MouseSupport;
|
||||
static const char *JoypadSupport;
|
||||
|
||||
virtual bool cap(const nall::string& name) { return false; }
|
||||
virtual nall::any get(const nall::string& name) { return false; }
|
||||
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
|
||||
|
||||
virtual bool 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() {}
|
||||
};
|
@@ -1,157 +0,0 @@
|
||||
namespace ruby {
|
||||
|
||||
class pInputCarbon {
|
||||
public:
|
||||
bool cap(const string& name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acquire() { return false; }
|
||||
bool unacquire() { return false; }
|
||||
bool acquired() { return false; }
|
||||
|
||||
bool poll(int16_t *table) {
|
||||
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
||||
|
||||
KeyMap keys;
|
||||
GetKeys(keys);
|
||||
uint8_t *keymap = (uint8_t*)keys;
|
||||
|
||||
#define map(id, name) table[keyboard(0)[name]] = (bool)(keymap[id >> 3] & (1 << (id & 7)))
|
||||
map(0x35, Keyboard::Escape);
|
||||
|
||||
map(0x7a, Keyboard::F1);
|
||||
map(0x78, Keyboard::F2);
|
||||
map(0x63, Keyboard::F3);
|
||||
map(0x76, Keyboard::F4);
|
||||
map(0x60, Keyboard::F5);
|
||||
map(0x61, Keyboard::F6);
|
||||
map(0x62, Keyboard::F7);
|
||||
map(0x64, Keyboard::F8);
|
||||
map(0x65, Keyboard::F9);
|
||||
map(0x6d, Keyboard::F10);
|
||||
map(0x67, Keyboard::F11);
|
||||
//map(0x??, Keyboard::F12);
|
||||
|
||||
map(0x69, Keyboard::PrintScreen);
|
||||
//map(0x??, Keyboard::ScrollLock);
|
||||
map(0x71, Keyboard::Pause);
|
||||
|
||||
map(0x32, Keyboard::Tilde);
|
||||
map(0x12, Keyboard::Num1);
|
||||
map(0x13, Keyboard::Num2);
|
||||
map(0x14, Keyboard::Num3);
|
||||
map(0x15, Keyboard::Num4);
|
||||
map(0x17, Keyboard::Num5);
|
||||
map(0x16, Keyboard::Num6);
|
||||
map(0x1a, Keyboard::Num7);
|
||||
map(0x1c, Keyboard::Num8);
|
||||
map(0x19, Keyboard::Num9);
|
||||
map(0x1d, Keyboard::Num0);
|
||||
|
||||
map(0x1b, Keyboard::Dash);
|
||||
map(0x18, Keyboard::Equal);
|
||||
map(0x33, Keyboard::Backspace);
|
||||
|
||||
map(0x72, Keyboard::Insert);
|
||||
map(0x75, Keyboard::Delete);
|
||||
map(0x73, Keyboard::Home);
|
||||
map(0x77, Keyboard::End);
|
||||
map(0x74, Keyboard::PageUp);
|
||||
map(0x79, Keyboard::PageDown);
|
||||
|
||||
map(0x00, Keyboard::A);
|
||||
map(0x0b, Keyboard::B);
|
||||
map(0x08, Keyboard::C);
|
||||
map(0x02, Keyboard::D);
|
||||
map(0x0e, Keyboard::E);
|
||||
map(0x03, Keyboard::F);
|
||||
map(0x05, Keyboard::G);
|
||||
map(0x04, Keyboard::H);
|
||||
map(0x22, Keyboard::I);
|
||||
map(0x26, Keyboard::J);
|
||||
map(0x28, Keyboard::K);
|
||||
map(0x25, Keyboard::L);
|
||||
map(0x2e, Keyboard::M);
|
||||
map(0x2d, Keyboard::N);
|
||||
map(0x1f, Keyboard::O);
|
||||
map(0x23, Keyboard::P);
|
||||
map(0x0c, Keyboard::Q);
|
||||
map(0x0f, Keyboard::R);
|
||||
map(0x01, Keyboard::S);
|
||||
map(0x11, Keyboard::T);
|
||||
map(0x20, Keyboard::U);
|
||||
map(0x09, Keyboard::V);
|
||||
map(0x0d, Keyboard::W);
|
||||
map(0x07, Keyboard::X);
|
||||
map(0x10, Keyboard::Y);
|
||||
map(0x06, Keyboard::Z);
|
||||
|
||||
map(0x21, Keyboard::LeftBracket);
|
||||
map(0x1e, Keyboard::RightBracket);
|
||||
map(0x2a, Keyboard::Backslash);
|
||||
map(0x29, Keyboard::Semicolon);
|
||||
map(0x27, Keyboard::Apostrophe);
|
||||
map(0x2b, Keyboard::Comma);
|
||||
map(0x2f, Keyboard::Period);
|
||||
map(0x2c, Keyboard::Slash);
|
||||
|
||||
map(0x53, Keyboard::Keypad1);
|
||||
map(0x54, Keyboard::Keypad2);
|
||||
map(0x55, Keyboard::Keypad3);
|
||||
map(0x56, Keyboard::Keypad4);
|
||||
map(0x57, Keyboard::Keypad5);
|
||||
map(0x58, Keyboard::Keypad6);
|
||||
map(0x59, Keyboard::Keypad7);
|
||||
map(0x5b, Keyboard::Keypad8);
|
||||
map(0x5c, Keyboard::Keypad9);
|
||||
map(0x52, Keyboard::Keypad0);
|
||||
|
||||
//map(0x??, Keyboard::Point);
|
||||
map(0x4c, Keyboard::Enter);
|
||||
map(0x45, Keyboard::Add);
|
||||
map(0x4e, Keyboard::Subtract);
|
||||
map(0x43, Keyboard::Multiply);
|
||||
map(0x4b, Keyboard::Divide);
|
||||
|
||||
map(0x47, Keyboard::NumLock);
|
||||
//map(0x39, Keyboard::CapsLock);
|
||||
|
||||
map(0x7e, Keyboard::Up);
|
||||
map(0x7d, Keyboard::Down);
|
||||
map(0x7b, Keyboard::Left);
|
||||
map(0x7c, Keyboard::Right);
|
||||
|
||||
map(0x30, Keyboard::Tab);
|
||||
map(0x24, Keyboard::Return);
|
||||
map(0x31, Keyboard::Spacebar);
|
||||
//map(0x??, Keyboard::Menu);
|
||||
|
||||
map(0x38, Keyboard::Shift);
|
||||
map(0x3b, Keyboard::Control);
|
||||
map(0x3a, Keyboard::Alt);
|
||||
map(0x37, Keyboard::Super);
|
||||
#undef map
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
}
|
||||
};
|
||||
|
||||
DeclareInput(Carbon)
|
||||
|
||||
};
|
@@ -1,387 +0,0 @@
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
static BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
|
||||
static BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
||||
|
||||
using namespace nall;
|
||||
|
||||
class pInputDI {
|
||||
public:
|
||||
struct {
|
||||
LPDIRECTINPUT8 context;
|
||||
LPDIRECTINPUTDEVICE8 keyboard;
|
||||
LPDIRECTINPUTDEVICE8 mouse;
|
||||
LPDIRECTINPUTDEVICE8 gamepad[Joypad::Count];
|
||||
bool mouseacquired;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Input::Handle) return true;
|
||||
if(name == Input::KeyboardSupport) return true;
|
||||
if(name == Input::MouseSupport) return true;
|
||||
if(name == Input::JoypadSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Input::Handle) return (uintptr_t)settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Input::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool poll(int16_t *table) {
|
||||
memset(table, 0, Scancode::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);
|
||||
}
|
||||
}
|
||||
|
||||
#define key(id) table[keyboard(0)[id]]
|
||||
|
||||
key(Keyboard::Escape) = (bool)(state[0x01] & 0x80);
|
||||
key(Keyboard::F1 ) = (bool)(state[0x3b] & 0x80);
|
||||
key(Keyboard::F2 ) = (bool)(state[0x3c] & 0x80);
|
||||
key(Keyboard::F3 ) = (bool)(state[0x3d] & 0x80);
|
||||
key(Keyboard::F4 ) = (bool)(state[0x3e] & 0x80);
|
||||
key(Keyboard::F5 ) = (bool)(state[0x3f] & 0x80);
|
||||
key(Keyboard::F6 ) = (bool)(state[0x40] & 0x80);
|
||||
key(Keyboard::F7 ) = (bool)(state[0x41] & 0x80);
|
||||
key(Keyboard::F8 ) = (bool)(state[0x42] & 0x80);
|
||||
key(Keyboard::F9 ) = (bool)(state[0x43] & 0x80);
|
||||
key(Keyboard::F10 ) = (bool)(state[0x44] & 0x80);
|
||||
key(Keyboard::F11 ) = (bool)(state[0x57] & 0x80);
|
||||
key(Keyboard::F12 ) = (bool)(state[0x58] & 0x80);
|
||||
|
||||
key(Keyboard::PrintScreen) = (bool)(state[0xb7] & 0x80);
|
||||
key(Keyboard::ScrollLock ) = (bool)(state[0x46] & 0x80);
|
||||
key(Keyboard::Pause ) = (bool)(state[0xc5] & 0x80);
|
||||
key(Keyboard::Tilde ) = (bool)(state[0x29] & 0x80);
|
||||
|
||||
key(Keyboard::Num1) = (bool)(state[0x02] & 0x80);
|
||||
key(Keyboard::Num2) = (bool)(state[0x03] & 0x80);
|
||||
key(Keyboard::Num3) = (bool)(state[0x04] & 0x80);
|
||||
key(Keyboard::Num4) = (bool)(state[0x05] & 0x80);
|
||||
key(Keyboard::Num5) = (bool)(state[0x06] & 0x80);
|
||||
key(Keyboard::Num6) = (bool)(state[0x07] & 0x80);
|
||||
key(Keyboard::Num7) = (bool)(state[0x08] & 0x80);
|
||||
key(Keyboard::Num8) = (bool)(state[0x09] & 0x80);
|
||||
key(Keyboard::Num9) = (bool)(state[0x0a] & 0x80);
|
||||
key(Keyboard::Num0) = (bool)(state[0x0b] & 0x80);
|
||||
|
||||
key(Keyboard::Dash ) = (bool)(state[0x0c] & 0x80);
|
||||
key(Keyboard::Equal ) = (bool)(state[0x0d] & 0x80);
|
||||
key(Keyboard::Backspace) = (bool)(state[0x0e] & 0x80);
|
||||
|
||||
key(Keyboard::Insert ) = (bool)(state[0xd2] & 0x80);
|
||||
key(Keyboard::Delete ) = (bool)(state[0xd3] & 0x80);
|
||||
key(Keyboard::Home ) = (bool)(state[0xc7] & 0x80);
|
||||
key(Keyboard::End ) = (bool)(state[0xcf] & 0x80);
|
||||
key(Keyboard::PageUp ) = (bool)(state[0xc9] & 0x80);
|
||||
key(Keyboard::PageDown) = (bool)(state[0xd1] & 0x80);
|
||||
|
||||
key(Keyboard::A) = (bool)(state[0x1e] & 0x80);
|
||||
key(Keyboard::B) = (bool)(state[0x30] & 0x80);
|
||||
key(Keyboard::C) = (bool)(state[0x2e] & 0x80);
|
||||
key(Keyboard::D) = (bool)(state[0x20] & 0x80);
|
||||
key(Keyboard::E) = (bool)(state[0x12] & 0x80);
|
||||
key(Keyboard::F) = (bool)(state[0x21] & 0x80);
|
||||
key(Keyboard::G) = (bool)(state[0x22] & 0x80);
|
||||
key(Keyboard::H) = (bool)(state[0x23] & 0x80);
|
||||
key(Keyboard::I) = (bool)(state[0x17] & 0x80);
|
||||
key(Keyboard::J) = (bool)(state[0x24] & 0x80);
|
||||
key(Keyboard::K) = (bool)(state[0x25] & 0x80);
|
||||
key(Keyboard::L) = (bool)(state[0x26] & 0x80);
|
||||
key(Keyboard::M) = (bool)(state[0x32] & 0x80);
|
||||
key(Keyboard::N) = (bool)(state[0x31] & 0x80);
|
||||
key(Keyboard::O) = (bool)(state[0x18] & 0x80);
|
||||
key(Keyboard::P) = (bool)(state[0x19] & 0x80);
|
||||
key(Keyboard::Q) = (bool)(state[0x10] & 0x80);
|
||||
key(Keyboard::R) = (bool)(state[0x13] & 0x80);
|
||||
key(Keyboard::S) = (bool)(state[0x1f] & 0x80);
|
||||
key(Keyboard::T) = (bool)(state[0x14] & 0x80);
|
||||
key(Keyboard::U) = (bool)(state[0x16] & 0x80);
|
||||
key(Keyboard::V) = (bool)(state[0x2f] & 0x80);
|
||||
key(Keyboard::W) = (bool)(state[0x11] & 0x80);
|
||||
key(Keyboard::X) = (bool)(state[0x2d] & 0x80);
|
||||
key(Keyboard::Y) = (bool)(state[0x15] & 0x80);
|
||||
key(Keyboard::Z) = (bool)(state[0x2c] & 0x80);
|
||||
|
||||
key(Keyboard::LeftBracket ) = (bool)(state[0x1a] & 0x80);
|
||||
key(Keyboard::RightBracket) = (bool)(state[0x1b] & 0x80);
|
||||
key(Keyboard::Backslash ) = (bool)(state[0x2b] & 0x80);
|
||||
key(Keyboard::Semicolon ) = (bool)(state[0x27] & 0x80);
|
||||
key(Keyboard::Apostrophe ) = (bool)(state[0x28] & 0x80);
|
||||
key(Keyboard::Comma ) = (bool)(state[0x33] & 0x80);
|
||||
key(Keyboard::Period ) = (bool)(state[0x34] & 0x80);
|
||||
key(Keyboard::Slash ) = (bool)(state[0x35] & 0x80);
|
||||
|
||||
key(Keyboard::Keypad1) = (bool)(state[0x4f] & 0x80);
|
||||
key(Keyboard::Keypad2) = (bool)(state[0x50] & 0x80);
|
||||
key(Keyboard::Keypad3) = (bool)(state[0x51] & 0x80);
|
||||
key(Keyboard::Keypad4) = (bool)(state[0x4b] & 0x80);
|
||||
key(Keyboard::Keypad5) = (bool)(state[0x4c] & 0x80);
|
||||
key(Keyboard::Keypad6) = (bool)(state[0x4d] & 0x80);
|
||||
key(Keyboard::Keypad7) = (bool)(state[0x47] & 0x80);
|
||||
key(Keyboard::Keypad8) = (bool)(state[0x48] & 0x80);
|
||||
key(Keyboard::Keypad9) = (bool)(state[0x49] & 0x80);
|
||||
key(Keyboard::Keypad0) = (bool)(state[0x52] & 0x80);
|
||||
key(Keyboard::Point ) = (bool)(state[0x53] & 0x80);
|
||||
|
||||
key(Keyboard::Add ) = (bool)(state[0x4e] & 0x80);
|
||||
key(Keyboard::Subtract) = (bool)(state[0x4a] & 0x80);
|
||||
key(Keyboard::Multiply) = (bool)(state[0x37] & 0x80);
|
||||
key(Keyboard::Divide ) = (bool)(state[0xb5] & 0x80);
|
||||
key(Keyboard::Enter ) = (bool)(state[0x9c] & 0x80);
|
||||
|
||||
key(Keyboard::NumLock ) = (bool)(state[0x45] & 0x80);
|
||||
key(Keyboard::CapsLock) = (bool)(state[0x3a] & 0x80);
|
||||
|
||||
key(Keyboard::Up ) = (bool)(state[0xc8] & 0x80);
|
||||
key(Keyboard::Down ) = (bool)(state[0xd0] & 0x80);
|
||||
key(Keyboard::Left ) = (bool)(state[0xcb] & 0x80);
|
||||
key(Keyboard::Right) = (bool)(state[0xcd] & 0x80);
|
||||
|
||||
key(Keyboard::Tab ) = (bool)(state[0x0f] & 0x80);
|
||||
key(Keyboard::Return ) = (bool)(state[0x1c] & 0x80);
|
||||
key(Keyboard::Spacebar) = (bool)(state[0x39] & 0x80);
|
||||
key(Keyboard::Menu ) = (bool)(state[0xdd] & 0x80);
|
||||
|
||||
key(Keyboard::Shift ) = (bool)(state[0x2a] & 0x80) || (bool)(state[0x36] & 0x80);
|
||||
key(Keyboard::Control) = (bool)(state[0x1d] & 0x80) || (bool)(state[0x9d] & 0x80);
|
||||
key(Keyboard::Alt ) = (bool)(state[0x38] & 0x80) || (bool)(state[0xb8] & 0x80);
|
||||
key(Keyboard::Super ) = (bool)(state[0xdb] & 0x80) || (bool)(state[0xdc] & 0x80);
|
||||
|
||||
#undef key
|
||||
}
|
||||
|
||||
//=====
|
||||
//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).axis(0)] = state.lX;
|
||||
table[mouse(0).axis(1)] = state.lY;
|
||||
table[mouse(0).axis(2)] = 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;
|
||||
|
||||
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[joypad(i).hat(n)] |= Joypad::HatUp;
|
||||
if(pov >= 4500 && pov <= 13500) table[joypad(i).hat(n)] |= Joypad::HatRight;
|
||||
if(pov >= 13500 && pov <= 22500) table[joypad(i).hat(n)] |= Joypad::HatDown;
|
||||
if(pov >= 22500 && pov <= 31500) table[joypad(i).hat(n)] |= Joypad::HatLeft;
|
||||
}
|
||||
}
|
||||
|
||||
//axes
|
||||
table[joypad(i).axis(0)] = state.lX;
|
||||
table[joypad(i).axis(1)] = state.lY;
|
||||
table[joypad(i).axis(2)] = state.lZ;
|
||||
table[joypad(i).axis(3)] = state.lRx;
|
||||
table[joypad(i).axis(4)] = state.lRy;
|
||||
table[joypad(i).axis(5)] = state.lRz;
|
||||
|
||||
//buttons
|
||||
for(unsigned n = 0; n < min((unsigned)Joypad::Buttons, 128); n++) {
|
||||
table[joypad(i).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() {
|
||||
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);
|
||||
}
|
||||
|
||||
DeclareInput(DI)
|
||||
|
||||
};
|
@@ -1,797 +0,0 @@
|
||||
//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 {
|
||||
|
||||
static DWORD WINAPI RawInputThreadProc(void*);
|
||||
static 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[nall::Keyboard::Size];
|
||||
|
||||
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, nall::Keyboard::Escape)
|
||||
map(0x003b, 0, nall::Keyboard::F1)
|
||||
map(0x003c, 0, nall::Keyboard::F2)
|
||||
map(0x003d, 0, nall::Keyboard::F3)
|
||||
map(0x003e, 0, nall::Keyboard::F4)
|
||||
map(0x003f, 0, nall::Keyboard::F5)
|
||||
map(0x0040, 0, nall::Keyboard::F6)
|
||||
map(0x0041, 0, nall::Keyboard::F7)
|
||||
map(0x0042, 0, nall::Keyboard::F8)
|
||||
map(0x0043, 0, nall::Keyboard::F9)
|
||||
map(0x0044, 0, nall::Keyboard::F10)
|
||||
map(0x0057, 0, nall::Keyboard::F11)
|
||||
map(0x0058, 0, nall::Keyboard::F12)
|
||||
|
||||
map(0x0037, 2, nall::Keyboard::PrintScreen)
|
||||
map(0x0046, 0, nall::Keyboard::ScrollLock)
|
||||
map(0x001d, 4, nall::Keyboard::Pause)
|
||||
map(0x0029, 0, nall::Keyboard::Tilde)
|
||||
|
||||
map(0x0002, 0, nall::Keyboard::Num1)
|
||||
map(0x0003, 0, nall::Keyboard::Num2)
|
||||
map(0x0004, 0, nall::Keyboard::Num3)
|
||||
map(0x0005, 0, nall::Keyboard::Num4)
|
||||
map(0x0006, 0, nall::Keyboard::Num5)
|
||||
map(0x0007, 0, nall::Keyboard::Num6)
|
||||
map(0x0008, 0, nall::Keyboard::Num7)
|
||||
map(0x0009, 0, nall::Keyboard::Num8)
|
||||
map(0x000a, 0, nall::Keyboard::Num9)
|
||||
map(0x000b, 0, nall::Keyboard::Num0)
|
||||
|
||||
map(0x000c, 0, nall::Keyboard::Dash)
|
||||
map(0x000d, 0, nall::Keyboard::Equal)
|
||||
map(0x000e, 0, nall::Keyboard::Backspace)
|
||||
|
||||
map(0x0052, 2, nall::Keyboard::Insert)
|
||||
map(0x0053, 2, nall::Keyboard::Delete)
|
||||
map(0x0047, 2, nall::Keyboard::Home)
|
||||
map(0x004f, 2, nall::Keyboard::End)
|
||||
map(0x0049, 2, nall::Keyboard::PageUp)
|
||||
map(0x0051, 2, nall::Keyboard::PageDown)
|
||||
|
||||
map(0x001e, 0, nall::Keyboard::A)
|
||||
map(0x0030, 0, nall::Keyboard::B)
|
||||
map(0x002e, 0, nall::Keyboard::C)
|
||||
map(0x0020, 0, nall::Keyboard::D)
|
||||
map(0x0012, 0, nall::Keyboard::E)
|
||||
map(0x0021, 0, nall::Keyboard::F)
|
||||
map(0x0022, 0, nall::Keyboard::G)
|
||||
map(0x0023, 0, nall::Keyboard::H)
|
||||
map(0x0017, 0, nall::Keyboard::I)
|
||||
map(0x0024, 0, nall::Keyboard::J)
|
||||
map(0x0025, 0, nall::Keyboard::K)
|
||||
map(0x0026, 0, nall::Keyboard::L)
|
||||
map(0x0032, 0, nall::Keyboard::M)
|
||||
map(0x0031, 0, nall::Keyboard::N)
|
||||
map(0x0018, 0, nall::Keyboard::O)
|
||||
map(0x0019, 0, nall::Keyboard::P)
|
||||
map(0x0010, 0, nall::Keyboard::Q)
|
||||
map(0x0013, 0, nall::Keyboard::R)
|
||||
map(0x001f, 0, nall::Keyboard::S)
|
||||
map(0x0014, 0, nall::Keyboard::T)
|
||||
map(0x0016, 0, nall::Keyboard::U)
|
||||
map(0x002f, 0, nall::Keyboard::V)
|
||||
map(0x0011, 0, nall::Keyboard::W)
|
||||
map(0x002d, 0, nall::Keyboard::X)
|
||||
map(0x0015, 0, nall::Keyboard::Y)
|
||||
map(0x002c, 0, nall::Keyboard::Z)
|
||||
|
||||
map(0x001a, 0, nall::Keyboard::LeftBracket)
|
||||
map(0x001b, 0, nall::Keyboard::RightBracket)
|
||||
map(0x002b, 0, nall::Keyboard::Backslash)
|
||||
map(0x0027, 0, nall::Keyboard::Semicolon)
|
||||
map(0x0028, 0, nall::Keyboard::Apostrophe)
|
||||
map(0x0033, 0, nall::Keyboard::Comma)
|
||||
map(0x0034, 0, nall::Keyboard::Period)
|
||||
map(0x0035, 0, nall::Keyboard::Slash)
|
||||
|
||||
map(0x004f, 0, nall::Keyboard::Keypad1)
|
||||
map(0x0050, 0, nall::Keyboard::Keypad2)
|
||||
map(0x0051, 0, nall::Keyboard::Keypad3)
|
||||
map(0x004b, 0, nall::Keyboard::Keypad4)
|
||||
map(0x004c, 0, nall::Keyboard::Keypad5)
|
||||
map(0x004d, 0, nall::Keyboard::Keypad6)
|
||||
map(0x0047, 0, nall::Keyboard::Keypad7)
|
||||
map(0x0048, 0, nall::Keyboard::Keypad8)
|
||||
map(0x0049, 0, nall::Keyboard::Keypad9)
|
||||
map(0x0052, 0, nall::Keyboard::Keypad0)
|
||||
|
||||
map(0x0053, 0, nall::Keyboard::Point)
|
||||
map(0x001c, 2, nall::Keyboard::Enter)
|
||||
map(0x004e, 0, nall::Keyboard::Add)
|
||||
map(0x004a, 0, nall::Keyboard::Subtract)
|
||||
map(0x0037, 0, nall::Keyboard::Multiply)
|
||||
map(0x0035, 2, nall::Keyboard::Divide)
|
||||
|
||||
map(0x0045, 0, nall::Keyboard::NumLock)
|
||||
map(0x003a, 0, nall::Keyboard::CapsLock)
|
||||
|
||||
//Pause signals 0x1d:4 + 0x45:0, whereas NumLock signals only 0x45:0.
|
||||
//this makes it impractical to detect both Pause+NumLock independently.
|
||||
//workaround: always detect Pause; detect NumLock only when Pause is released.
|
||||
if(state[nall::Keyboard::Pause]) state[nall::Keyboard::NumLock] = false;
|
||||
|
||||
map(0x0048, 2, nall::Keyboard::Up)
|
||||
map(0x0050, 2, nall::Keyboard::Down)
|
||||
map(0x004b, 2, nall::Keyboard::Left)
|
||||
map(0x004d, 2, nall::Keyboard::Right)
|
||||
|
||||
map(0x000f, 0, nall::Keyboard::Tab)
|
||||
map(0x001c, 0, nall::Keyboard::Return)
|
||||
map(0x0039, 0, nall::Keyboard::Spacebar)
|
||||
map(0x005d, 2, nall::Keyboard::Menu)
|
||||
|
||||
//merge left and right modifiers to one ID
|
||||
if(code == 0x002a && flags == 0) state[nall::Keyboard::Shift] = 1; //left shift
|
||||
if(code == 0x002a && flags == 1) state[nall::Keyboard::Shift] = 0;
|
||||
if(code == 0x0036 && flags == 0) state[nall::Keyboard::Shift] = 1; //right shift
|
||||
if(code == 0x0036 && flags == 1) state[nall::Keyboard::Shift] = 0;
|
||||
|
||||
if(code == 0x001d && flags == 0) state[nall::Keyboard::Control] = 1; //left control
|
||||
if(code == 0x001d && flags == 1) state[nall::Keyboard::Control] = 0;
|
||||
if(code == 0x001d && flags == 2) state[nall::Keyboard::Control] = 1; //right control
|
||||
if(code == 0x001d && flags == 3) state[nall::Keyboard::Control] = 0;
|
||||
|
||||
if(code == 0x0038 && flags == 0) state[nall::Keyboard::Alt] = 1; //left alt
|
||||
if(code == 0x0038 && flags == 1) state[nall::Keyboard::Alt] = 0;
|
||||
if(code == 0x0038 && flags == 2) state[nall::Keyboard::Alt] = 1; //right alt
|
||||
if(code == 0x0038 && flags == 3) state[nall::Keyboard::Alt] = 0;
|
||||
|
||||
if(code == 0x005b && flags == 2) state[nall::Keyboard::Super] = 1; //left super
|
||||
if(code == 0x005b && flags == 3) state[nall::Keyboard::Super] = 0;
|
||||
if(code == 0x005c && flags == 2) state[nall::Keyboard::Super] = 1; //right super
|
||||
if(code == 0x005c && flags == 3) state[nall::Keyboard::Super] = 0;
|
||||
#undef map
|
||||
}
|
||||
|
||||
Keyboard() {
|
||||
for(unsigned i = 0; i < nall::Keyboard::Size; 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;
|
||||
};
|
||||
|
||||
linear_vector<Keyboard> lkeyboard;
|
||||
linear_vector<Mouse> lmouse;
|
||||
linear_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:
|
||||
HMODULE libxinput;
|
||||
DWORD WINAPI (*pXInputGetState)(DWORD, XINPUT_STATE*);
|
||||
|
||||
struct Gamepad {
|
||||
unsigned id;
|
||||
|
||||
int16_t hat;
|
||||
int16_t axis[6];
|
||||
bool button[10];
|
||||
|
||||
void poll(XINPUT_STATE &state) {
|
||||
hat = Joypad::HatCenter;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
|
||||
|
||||
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::HatCenter;
|
||||
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
||||
for(unsigned n = 0; n < 10; n++) button[n] = false;
|
||||
}
|
||||
};
|
||||
|
||||
linear_vector<Gamepad> lgamepad;
|
||||
|
||||
void poll() {
|
||||
if(!pXInputGetState) return;
|
||||
|
||||
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
||||
XINPUT_STATE state;
|
||||
DWORD result = pXInputGetState(lgamepad[i].id, &state);
|
||||
if(result == ERROR_SUCCESS) lgamepad[i].poll(state);
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
if(!pXInputGetState) return;
|
||||
|
||||
//XInput only supports up to four controllers
|
||||
for(unsigned i = 0; i <= 3; i++) {
|
||||
XINPUT_STATE state;
|
||||
DWORD result = pXInputGetState(i, &state);
|
||||
if(result == ERROR_SUCCESS) {
|
||||
//valid controller detected, add to gamepad list
|
||||
unsigned n = lgamepad.size();
|
||||
lgamepad[n].id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XInput() : pXInputGetState(0) {
|
||||
//bind xinput1 dynamically, as it does not ship with Windows Vista or below
|
||||
libxinput = LoadLibraryA("xinput1_3.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
|
||||
if(!libxinput) return;
|
||||
pXInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
|
||||
}
|
||||
|
||||
~XInput() {
|
||||
if(libxinput) FreeLibrary(libxinput);
|
||||
}
|
||||
};
|
||||
|
||||
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::HatCenter;
|
||||
|
||||
//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::HatUp;
|
||||
if(pov >= 4500 && pov <= 13500) hat[n] |= Joypad::HatRight;
|
||||
if(pov >= 13500 && pov <= 22500) hat[n] |= Joypad::HatDown;
|
||||
if(pov >= 22500 && pov <= 31500) hat[n] |= Joypad::HatLeft;
|
||||
}
|
||||
|
||||
//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::HatCenter;
|
||||
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
||||
for(unsigned n = 0; n < 128; n++) button[n] = false;
|
||||
}
|
||||
};
|
||||
linear_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:
|
||||
XInput xinput;
|
||||
DirectInput dinput;
|
||||
|
||||
bool acquire_mouse;
|
||||
bool cursor_visible;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Input::Handle) return true;
|
||||
if(name == Input::KeyboardSupport) return true;
|
||||
if(name == Input::MouseSupport) return true;
|
||||
if(name == Input::JoypadSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Input::Handle) return (uintptr_t)settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Input::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
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, Scancode::Limit * sizeof(int16_t));
|
||||
|
||||
WaitForSingleObject(rawinput.mutex, INFINITE);
|
||||
|
||||
//=========
|
||||
//Keyboards
|
||||
//=========
|
||||
for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)Keyboard::Count); i++) {
|
||||
for(unsigned n = 0; n < nall::Keyboard::Size; n++) {
|
||||
table[keyboard(i).key(n)] = rawinput.lkeyboard[i].state[n];
|
||||
}
|
||||
}
|
||||
|
||||
//====
|
||||
//Mice
|
||||
//====
|
||||
for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)Mouse::Count); i++) {
|
||||
table[mouse(i).axis(0)] = rawinput.lmouse[i].xDistance;
|
||||
table[mouse(i).axis(1)] = rawinput.lmouse[i].yDistance;
|
||||
table[mouse(i).axis(2)] = rawinput.lmouse[i].zDistance;
|
||||
|
||||
for(unsigned n = 0; n < min(5U, (unsigned)Mouse::Buttons); n++) {
|
||||
table[mouse(i).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;
|
||||
|
||||
table[joypad(i).hat(0)] = xinput.lgamepad[i].hat;
|
||||
|
||||
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
||||
table[joypad(i).axis(axis)] = xinput.lgamepad[i].axis[axis];
|
||||
}
|
||||
|
||||
for(unsigned button = 0; button < min(10U, (unsigned)Joypad::Buttons); button++) {
|
||||
table[joypad(i).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;
|
||||
|
||||
for(unsigned hat = 0; hat < min(4U, (unsigned)Joypad::Hats); hat++) {
|
||||
table[joypad(i).hat(hat)] = dinput.lgamepad[i].hat[hat];
|
||||
}
|
||||
|
||||
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
||||
table[joypad(i).axis(axis)] = dinput.lgamepad[i].axis[axis];
|
||||
}
|
||||
|
||||
for(unsigned button = 0; button < min(128U, (unsigned)Joypad::Buttons); button++) {
|
||||
table[joypad(i).button(button)] = dinput.lgamepad[i].button[button];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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() {
|
||||
}
|
||||
};
|
||||
|
||||
DeclareInput(Raw)
|
||||
|
||||
};
|
@@ -1,230 +0,0 @@
|
||||
//================
|
||||
//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>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
struct pInputSDL {
|
||||
#include "xlibkeys.hpp"
|
||||
|
||||
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(const string& name) {
|
||||
if(name == Input::Handle) return true;
|
||||
if(name == Input::KeyboardSupport) return true;
|
||||
if(name == Input::MouseSupport) return true;
|
||||
if(name == Input::JoypadSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Input::Handle) return (uintptr_t)settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any &value) {
|
||||
if(name == Input::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
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, Scancode::Limit * sizeof(int16_t));
|
||||
|
||||
//========
|
||||
//Keyboard
|
||||
//========
|
||||
|
||||
x_poll(table);
|
||||
|
||||
//=====
|
||||
//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).axis(0)] = (int16_t)(root_x_return - device.screenwidth / 2);
|
||||
table[mouse(0).axis(1)] = (int16_t)(root_y_return - device.screenheight / 2);
|
||||
|
||||
if(table[mouse(0).axis(0)] != 0 || table[mouse(0).axis(1)] != 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).axis(0)] = (int16_t)(root_x_return - device.relativex);
|
||||
table[mouse(0).axis(1)] = (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;
|
||||
|
||||
//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[joypad(i).hat(hat)] |= Joypad::HatUp;
|
||||
if(state & SDL_HAT_RIGHT) table[joypad(i).hat(hat)] |= Joypad::HatRight;
|
||||
if(state & SDL_HAT_DOWN ) table[joypad(i).hat(hat)] |= Joypad::HatDown;
|
||||
if(state & SDL_HAT_LEFT ) table[joypad(i).hat(hat)] |= Joypad::HatLeft;
|
||||
}
|
||||
|
||||
//axes
|
||||
unsigned axes = min((unsigned)Joypad::Axes, SDL_JoystickNumAxes(device.gamepad[i]));
|
||||
for(unsigned axis = 0; axis < axes; axis++) {
|
||||
table[joypad(i).axis(axis)] = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis);
|
||||
}
|
||||
|
||||
//buttons
|
||||
for(unsigned button = 0; button < Joypad::Buttons; button++) {
|
||||
table[joypad(i).button(button)] = (bool)SDL_JoystickGetButton(device.gamepad[i], button);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init() {
|
||||
x_init();
|
||||
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);
|
||||
XCloseDisplay(device.display);
|
||||
}
|
||||
|
||||
pInputSDL() {
|
||||
for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0;
|
||||
settings.handle = 0;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareInput(SDL)
|
||||
|
||||
};
|
@@ -1,50 +0,0 @@
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pInputX {
|
||||
public:
|
||||
Display *display;
|
||||
#include "xlibkeys.hpp"
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Input::KeyboardSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any &value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acquire() { return false; }
|
||||
bool unacquire() { return false; }
|
||||
bool acquired() { return false; }
|
||||
|
||||
bool poll(int16_t *table) {
|
||||
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
||||
x_poll(table);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init() {
|
||||
x_init();
|
||||
display = XOpenDisplay(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
};
|
||||
|
||||
DeclareInput(X)
|
||||
|
||||
};
|
@@ -1,264 +0,0 @@
|
||||
uint8_t scancode[256];
|
||||
|
||||
enum XScancode {
|
||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||
ScrollLock, Pause, Tilde,
|
||||
Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
|
||||
Dash, Equal, Backspace,
|
||||
Insert, Delete, Home, End, PageUp, PageDown,
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
|
||||
Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
|
||||
Point, Enter, Add, Subtract, Multiply, Divide,
|
||||
Up, Down, Left, Right,
|
||||
Tab, Return, Spacebar, Menu,
|
||||
LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper,
|
||||
};
|
||||
|
||||
void x_poll(int16_t *table) {
|
||||
char state[32];
|
||||
Display *display = XOpenDisplay(0);
|
||||
XQueryKeymap(display, state);
|
||||
XCloseDisplay(display);
|
||||
|
||||
#define key(id) table[keyboard(0)[id]]
|
||||
#define pressed(id) (bool)(state[scancode[id] >> 3] & (1 << (scancode[id] & 7)))
|
||||
|
||||
key(Keyboard::Escape) = pressed(Escape);
|
||||
|
||||
key(Keyboard::F1) = pressed(F1);
|
||||
key(Keyboard::F2) = pressed(F2);
|
||||
key(Keyboard::F3) = pressed(F3);
|
||||
key(Keyboard::F4) = pressed(F4);
|
||||
key(Keyboard::F5) = pressed(F5);
|
||||
key(Keyboard::F6) = pressed(F6);
|
||||
key(Keyboard::F7) = pressed(F7);
|
||||
key(Keyboard::F8) = pressed(F8);
|
||||
key(Keyboard::F9) = pressed(F9);
|
||||
key(Keyboard::F10) = pressed(F10);
|
||||
key(Keyboard::F11) = pressed(F11);
|
||||
key(Keyboard::F12) = pressed(F12);
|
||||
|
||||
key(Keyboard::ScrollLock) = pressed(ScrollLock);
|
||||
key(Keyboard::Pause) = pressed(Pause);
|
||||
key(Keyboard::Tilde) = pressed(Tilde);
|
||||
|
||||
key(Keyboard::Num1) = pressed(Num1);
|
||||
key(Keyboard::Num2) = pressed(Num2);
|
||||
key(Keyboard::Num3) = pressed(Num3);
|
||||
key(Keyboard::Num4) = pressed(Num4);
|
||||
key(Keyboard::Num5) = pressed(Num5);
|
||||
key(Keyboard::Num6) = pressed(Num6);
|
||||
key(Keyboard::Num7) = pressed(Num7);
|
||||
key(Keyboard::Num8) = pressed(Num8);
|
||||
key(Keyboard::Num9) = pressed(Num9);
|
||||
key(Keyboard::Num0) = pressed(Num0);
|
||||
|
||||
key(Keyboard::Dash) = pressed(Dash);
|
||||
key(Keyboard::Equal) = pressed(Equal);
|
||||
key(Keyboard::Backspace) = pressed(Backspace);
|
||||
|
||||
key(Keyboard::Insert) = pressed(Insert);
|
||||
key(Keyboard::Delete) = pressed(Delete);
|
||||
key(Keyboard::Home) = pressed(Home);
|
||||
key(Keyboard::End) = pressed(End);
|
||||
key(Keyboard::PageUp) = pressed(PageUp);
|
||||
key(Keyboard::PageDown) = pressed(PageDown);
|
||||
|
||||
key(Keyboard::A) = pressed(A);
|
||||
key(Keyboard::B) = pressed(B);
|
||||
key(Keyboard::C) = pressed(C);
|
||||
key(Keyboard::D) = pressed(D);
|
||||
key(Keyboard::E) = pressed(E);
|
||||
key(Keyboard::F) = pressed(F);
|
||||
key(Keyboard::G) = pressed(G);
|
||||
key(Keyboard::H) = pressed(H);
|
||||
key(Keyboard::I) = pressed(I);
|
||||
key(Keyboard::J) = pressed(J);
|
||||
key(Keyboard::K) = pressed(K);
|
||||
key(Keyboard::L) = pressed(L);
|
||||
key(Keyboard::M) = pressed(M);
|
||||
key(Keyboard::N) = pressed(N);
|
||||
key(Keyboard::O) = pressed(O);
|
||||
key(Keyboard::P) = pressed(P);
|
||||
key(Keyboard::Q) = pressed(Q);
|
||||
key(Keyboard::R) = pressed(R);
|
||||
key(Keyboard::S) = pressed(S);
|
||||
key(Keyboard::T) = pressed(T);
|
||||
key(Keyboard::U) = pressed(U);
|
||||
key(Keyboard::V) = pressed(V);
|
||||
key(Keyboard::W) = pressed(W);
|
||||
key(Keyboard::X) = pressed(X);
|
||||
key(Keyboard::Y) = pressed(Y);
|
||||
key(Keyboard::Z) = pressed(Z);
|
||||
|
||||
key(Keyboard::LeftBracket) = pressed(LeftBracket);
|
||||
key(Keyboard::RightBracket) = pressed(RightBracket);
|
||||
key(Keyboard::Backslash) = pressed(Backslash);
|
||||
key(Keyboard::Semicolon) = pressed(Semicolon);
|
||||
key(Keyboard::Apostrophe) = pressed(Apostrophe);
|
||||
key(Keyboard::Comma) = pressed(Comma);
|
||||
key(Keyboard::Period) = pressed(Period);
|
||||
key(Keyboard::Slash) = pressed(Slash);
|
||||
|
||||
key(Keyboard::Keypad1) = pressed(Keypad1);
|
||||
key(Keyboard::Keypad2) = pressed(Keypad2);
|
||||
key(Keyboard::Keypad3) = pressed(Keypad3);
|
||||
key(Keyboard::Keypad4) = pressed(Keypad4);
|
||||
key(Keyboard::Keypad5) = pressed(Keypad5);
|
||||
key(Keyboard::Keypad6) = pressed(Keypad6);
|
||||
key(Keyboard::Keypad7) = pressed(Keypad7);
|
||||
key(Keyboard::Keypad8) = pressed(Keypad8);
|
||||
key(Keyboard::Keypad9) = pressed(Keypad9);
|
||||
key(Keyboard::Keypad0) = pressed(Keypad0);
|
||||
|
||||
key(Keyboard::Point) = pressed(Point);
|
||||
key(Keyboard::Enter) = pressed(Enter);
|
||||
key(Keyboard::Add) = pressed(Add);
|
||||
key(Keyboard::Subtract) = pressed(Subtract);
|
||||
key(Keyboard::Multiply) = pressed(Multiply);
|
||||
key(Keyboard::Divide) = pressed(Divide);
|
||||
|
||||
key(Keyboard::Up) = pressed(Up);
|
||||
key(Keyboard::Down) = pressed(Down);
|
||||
key(Keyboard::Left) = pressed(Left);
|
||||
key(Keyboard::Right) = pressed(Right);
|
||||
|
||||
key(Keyboard::Tab) = pressed(Tab);
|
||||
key(Keyboard::Return) = pressed(Return);
|
||||
key(Keyboard::Spacebar) = pressed(Spacebar);
|
||||
key(Keyboard::Menu) = pressed(Menu);
|
||||
|
||||
key(Keyboard::Shift) = pressed(LeftShift) || pressed(RightShift);
|
||||
key(Keyboard::Control) = pressed(LeftControl) || pressed(RightControl);
|
||||
key(Keyboard::Alt) = pressed(LeftAlt) || pressed(RightAlt);
|
||||
key(Keyboard::Super) = pressed(LeftSuper) || pressed(RightSuper);
|
||||
|
||||
#undef key
|
||||
#undef pressed
|
||||
}
|
||||
|
||||
void x_init() {
|
||||
Display *display = XOpenDisplay(0);
|
||||
memset(&scancode, 0, sizeof scancode);
|
||||
|
||||
#define assign(x, y) scancode[x] = XKeysymToKeycode(display, y)
|
||||
assign(Escape, XK_Escape);
|
||||
|
||||
assign(F1, XK_F1);
|
||||
assign(F2, XK_F2);
|
||||
assign(F3, XK_F3);
|
||||
assign(F4, XK_F4);
|
||||
assign(F5, XK_F5);
|
||||
assign(F6, XK_F6);
|
||||
assign(F7, XK_F7);
|
||||
assign(F8, XK_F8);
|
||||
assign(F9, XK_F9);
|
||||
assign(F10, XK_F10);
|
||||
assign(F11, XK_F11);
|
||||
assign(F12, XK_F12);
|
||||
|
||||
assign(ScrollLock, XK_Scroll_Lock);
|
||||
assign(Pause, XK_Pause);
|
||||
|
||||
assign(Tilde, XK_asciitilde);
|
||||
|
||||
assign(Num0, XK_0);
|
||||
assign(Num1, XK_1);
|
||||
assign(Num2, XK_2);
|
||||
assign(Num3, XK_3);
|
||||
assign(Num4, XK_4);
|
||||
assign(Num5, XK_5);
|
||||
assign(Num6, XK_6);
|
||||
assign(Num7, XK_7);
|
||||
assign(Num8, XK_8);
|
||||
assign(Num9, XK_9);
|
||||
|
||||
assign(Dash, XK_minus);
|
||||
assign(Equal, XK_equal);
|
||||
assign(Backspace, XK_BackSpace);
|
||||
|
||||
assign(Insert, XK_Insert);
|
||||
assign(Delete, XK_Delete);
|
||||
assign(Home, XK_Home);
|
||||
assign(End, XK_End);
|
||||
assign(PageUp, XK_Prior);
|
||||
assign(PageDown, XK_Next);
|
||||
|
||||
assign(A, XK_A);
|
||||
assign(B, XK_B);
|
||||
assign(C, XK_C);
|
||||
assign(D, XK_D);
|
||||
assign(E, XK_E);
|
||||
assign(F, XK_F);
|
||||
assign(G, XK_G);
|
||||
assign(H, XK_H);
|
||||
assign(I, XK_I);
|
||||
assign(J, XK_J);
|
||||
assign(K, XK_K);
|
||||
assign(L, XK_L);
|
||||
assign(M, XK_M);
|
||||
assign(N, XK_N);
|
||||
assign(O, XK_O);
|
||||
assign(P, XK_P);
|
||||
assign(Q, XK_Q);
|
||||
assign(R, XK_R);
|
||||
assign(S, XK_S);
|
||||
assign(T, XK_T);
|
||||
assign(U, XK_U);
|
||||
assign(V, XK_V);
|
||||
assign(W, XK_W);
|
||||
assign(X, XK_X);
|
||||
assign(Y, XK_Y);
|
||||
assign(Z, XK_Z);
|
||||
|
||||
assign(LeftBracket, XK_bracketleft);
|
||||
assign(RightBracket, XK_bracketright);
|
||||
assign(Backslash, XK_backslash);
|
||||
assign(Semicolon, XK_semicolon);
|
||||
assign(Apostrophe, XK_apostrophe);
|
||||
assign(Comma, XK_comma);
|
||||
assign(Period, XK_period);
|
||||
assign(Slash, XK_slash);
|
||||
|
||||
assign(Keypad0, XK_KP_0);
|
||||
assign(Keypad1, XK_KP_1);
|
||||
assign(Keypad2, XK_KP_2);
|
||||
assign(Keypad3, XK_KP_3);
|
||||
assign(Keypad4, XK_KP_4);
|
||||
assign(Keypad5, XK_KP_5);
|
||||
assign(Keypad6, XK_KP_6);
|
||||
assign(Keypad7, XK_KP_7);
|
||||
assign(Keypad8, XK_KP_8);
|
||||
assign(Keypad9, XK_KP_9);
|
||||
|
||||
assign(Add, XK_KP_Add);
|
||||
assign(Subtract, XK_KP_Subtract);
|
||||
assign(Multiply, XK_KP_Multiply);
|
||||
assign(Divide, XK_KP_Divide);
|
||||
assign(Enter, XK_KP_Enter);
|
||||
|
||||
assign(Up, XK_Up);
|
||||
assign(Down, XK_Down);
|
||||
assign(Left, XK_Left);
|
||||
assign(Right, XK_Right);
|
||||
|
||||
assign(Tab, XK_Tab);
|
||||
assign(Return, XK_Return);
|
||||
assign(Spacebar, XK_space);
|
||||
|
||||
assign(LeftControl, XK_Control_L);
|
||||
assign(RightControl, XK_Control_R);
|
||||
assign(LeftAlt, XK_Alt_L);
|
||||
assign(RightAlt, XK_Alt_R);
|
||||
assign(LeftShift, XK_Shift_L);
|
||||
assign(RightShift, XK_Shift_R);
|
||||
assign(LeftSuper, XK_Super_L);
|
||||
assign(RightSuper, XK_Super_R);
|
||||
assign(Menu, XK_Menu);
|
||||
|
||||
#undef assign
|
||||
|
||||
XCloseDisplay(display);
|
||||
}
|
380
ruby/ruby.cpp
380
ruby/ruby.cpp
@@ -1,380 +0,0 @@
|
||||
#include <ruby/ruby.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <ruby/ruby_impl.cpp>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
VideoInterface video;
|
||||
AudioInterface audio;
|
||||
InputInterface input;
|
||||
|
||||
/* VideoInterface */
|
||||
|
||||
const char *Video::Handle = "Handle";
|
||||
const char *Video::Synchronize = "Synchronize";
|
||||
const char *Video::Filter = "Filter";
|
||||
const char *Video::FragmentShader = "FragmentShader";
|
||||
const char *Video::VertexShader = "VertexShader";
|
||||
|
||||
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_QTOPENGL
|
||||
else if(!strcmp(driver, "Qt-OpenGL")) p = new VideoQtOpenGL();
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_QTRASTER
|
||||
else if(!strcmp(driver, "Qt-Raster")) p = new VideoQtRaster();
|
||||
#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_QTOPENGL)
|
||||
return "Qt-OpenGL";
|
||||
#elif defined(VIDEO_QTRASTER)
|
||||
return "Qt-Raster";
|
||||
#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_QTOPENGL)
|
||||
"Qt-OpenGL;"
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_XV)
|
||||
"X-Video;"
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_QTRASTER)
|
||||
"Qt-Raster;"
|
||||
#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(const string& name) { return p ? p->cap(name) : false; }
|
||||
any VideoInterface::get(const string& name) { return p ? p->get(name) : false; }
|
||||
bool VideoInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
|
||||
bool VideoInterface::lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return p ? p->lock(data, pitch, width, height) : false; }
|
||||
void VideoInterface::unlock() { if(p) p->unlock(); }
|
||||
void VideoInterface::clear() { if(p) p->clear(); }
|
||||
void VideoInterface::refresh() { if(p) p->refresh(); }
|
||||
VideoInterface::VideoInterface() : p(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
|
||||
|
||||
#ifdef AUDIO_PULSEAUDIOSIMPLE
|
||||
else if(!strcmp(driver, "PulseAudioSimple")) p = new AudioPulseAudioSimple();
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO_XAUDIO2
|
||||
else if(!strcmp(driver, "XAudio2")) p = new AudioXAudio2();
|
||||
#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_XAUDIO2)
|
||||
return "XAudio2";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OPENAL)
|
||||
return "OpenAL";
|
||||
#elif defined(AUDIO_PULSEAUDIO)
|
||||
return "PulseAudio";
|
||||
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
return "PulseAudioSimple";
|
||||
#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
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
"XAudio2;"
|
||||
#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_PULSEAUDIOSIMPLE)
|
||||
"PulseAudioSimple;"
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_AO)
|
||||
"libao;"
|
||||
#endif
|
||||
|
||||
"None";
|
||||
}
|
||||
|
||||
#include "ruby_audio.cpp"
|
||||
|
||||
/* InputInterface */
|
||||
|
||||
const char *Input::Handle = "Handle";
|
||||
const char *Input::KeyboardSupport = "KeyboardSupport";
|
||||
const char *Input::MouseSupport = "MouseSupport";
|
||||
const char *Input::JoypadSupport = "JoypadSupport";
|
||||
|
||||
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
|
||||
|
||||
#ifdef INPUT_CARBON
|
||||
else if(!strcmp(driver, "Carbon")) p = new InputCarbon();
|
||||
#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";
|
||||
#elif defined(INPUT_CARBON)
|
||||
return "Carbon";
|
||||
#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
|
||||
|
||||
//OS X
|
||||
|
||||
#if defined(INPUT_CARBON)
|
||||
"Carbon;"
|
||||
#endif
|
||||
|
||||
"None";
|
||||
}
|
||||
|
||||
bool InputInterface::init() {
|
||||
if(!p) driver();
|
||||
return p->init();
|
||||
}
|
||||
|
||||
void InputInterface::term() {
|
||||
if(p) {
|
||||
delete p;
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputInterface::cap(const string& name) { return p ? p->cap(name) : false; }
|
||||
any InputInterface::get(const string& name) { return p ? p->get(name) : false; }
|
||||
bool InputInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : 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(); }
|
||||
|
||||
};
|
109
ruby/ruby.hpp
109
ruby/ruby.hpp
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
ruby
|
||||
version: 0.06 (2009-05-22)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#ifndef RUBY_H
|
||||
#define RUBY_H
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/input.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.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(const nall::string& name);
|
||||
nall::any get(const nall::string& name);
|
||||
bool set(const nall::string& name, const nall::any& value);
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height);
|
||||
void unlock();
|
||||
void clear();
|
||||
void refresh();
|
||||
VideoInterface();
|
||||
~VideoInterface();
|
||||
|
||||
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(const nall::string& name);
|
||||
nall::any get(const nall::string& name);
|
||||
bool set(const nall::string& name, const nall::any& value);
|
||||
|
||||
void sample(uint16_t left, uint16_t right);
|
||||
void clear();
|
||||
AudioInterface();
|
||||
~AudioInterface();
|
||||
|
||||
private:
|
||||
Audio *p;
|
||||
|
||||
unsigned volume;
|
||||
|
||||
//resample unit
|
||||
double hermite(double mu, double a, double b, double c, double d);
|
||||
bool resample_enabled;
|
||||
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(const nall::string& name);
|
||||
nall::any get(const nall::string& name);
|
||||
bool set(const nall::string& name, const nall::any& value);
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,133 +0,0 @@
|
||||
const char *Audio::Volume = "Volume";
|
||||
const char *Audio::Resample = "Resample";
|
||||
const char *Audio::ResampleRatio = "ResampleRatio";
|
||||
|
||||
const char *Audio::Handle = "Handle";
|
||||
const char *Audio::Synchronize = "Synchronize";
|
||||
const char *Audio::Frequency = "Frequency";
|
||||
const char *Audio::Latency = "Latency";
|
||||
|
||||
bool AudioInterface::init() {
|
||||
if(!p) driver();
|
||||
return p->init();
|
||||
}
|
||||
|
||||
void AudioInterface::term() {
|
||||
if(p) {
|
||||
delete p;
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioInterface::cap(const string& name) {
|
||||
if(name == Audio::Volume) return true;
|
||||
if(name == Audio::Resample) return true;
|
||||
if(name == Audio::ResampleRatio) return true;
|
||||
|
||||
return p ? p->cap(name) : false;
|
||||
}
|
||||
|
||||
any AudioInterface::get(const string& name) {
|
||||
if(name == Audio::Volume) return volume;
|
||||
if(name == Audio::Resample) return resample_enabled;
|
||||
if(name == Audio::ResampleRatio);
|
||||
|
||||
return p ? p->get(name) : false;
|
||||
}
|
||||
|
||||
bool AudioInterface::set(const string& name, const any& value) {
|
||||
if(name == Audio::Volume) {
|
||||
volume = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::Resample) {
|
||||
resample_enabled = any_cast<bool>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Audio::ResampleRatio) {
|
||||
r_step = any_cast<double>(value);
|
||||
r_frac = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return p ? p->set(name, value) : 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_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();
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
/* Global Headers */
|
||||
|
||||
#if defined(VIDEO_QTOPENGL) || defined(VIDEO_QTRASTER)
|
||||
#include <QApplication>
|
||||
#include <QtGui>
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_QTOPENGL)
|
||||
#include <QGLWidget>
|
||||
#if defined(PLATFORM_WIN)
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#elif defined(PLATFORM_OSX)
|
||||
#define __INTEL_COMPILER
|
||||
#include <Carbon/Carbon.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/* Video */
|
||||
|
||||
#define DeclareVideo(Name) \
|
||||
class Video##Name : public Video { \
|
||||
public: \
|
||||
bool cap(const string& name) { return p.cap(name); } \
|
||||
any get(const string& name) { return p.get(name); } \
|
||||
bool set(const string& name, const any& value) { return p.set(name, value); } \
|
||||
\
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return p.lock(data, pitch, width, height); } \
|
||||
void unlock() { p.unlock(); } \
|
||||
\
|
||||
void clear() { p.clear(); } \
|
||||
void refresh() { p.refresh(); } \
|
||||
bool init() { return p.init(); } \
|
||||
void term() { p.term(); } \
|
||||
\
|
||||
Video##Name() : p(*new pVideo##Name) {} \
|
||||
~Video##Name() { delete &p; } \
|
||||
\
|
||||
private: \
|
||||
pVideo##Name &p; \
|
||||
};
|
||||
|
||||
#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_QTOPENGL
|
||||
#include <ruby/video/qtopengl.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_QTRASTER
|
||||
#include <ruby/video/qtraster.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 */
|
||||
|
||||
#define DeclareAudio(Name) \
|
||||
class Audio##Name : public Audio { \
|
||||
public: \
|
||||
bool cap(const string& name) { return p.cap(name); } \
|
||||
any get(const string& name) { return p.get(name); } \
|
||||
bool set(const string& name, const any& value) { return p.set(name, value); } \
|
||||
\
|
||||
void sample(uint16_t left, uint16_t right) { p.sample(left, right); } \
|
||||
void clear() { p.clear(); } \
|
||||
bool init() { return p.init(); } \
|
||||
void term() { p.term(); } \
|
||||
\
|
||||
Audio##Name() : p(*new pAudio##Name) {} \
|
||||
~Audio##Name() { delete &p; } \
|
||||
\
|
||||
private: \
|
||||
pAudio##Name &p; \
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
#ifdef AUDIO_PULSEAUDIOSIMPLE
|
||||
#include <ruby/audio/pulseaudiosimple.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO_XAUDIO2
|
||||
#include <ruby/audio/xaudio2.cpp>
|
||||
#endif
|
||||
|
||||
/* Input */
|
||||
|
||||
#define DeclareInput(Name) \
|
||||
class Input##Name : public Input { \
|
||||
public: \
|
||||
bool cap(const string& name) { return p.cap(name); } \
|
||||
any get(const string& name) { return p.get(name); } \
|
||||
bool set(const string& name, const any& value) { return p.set(name, value); } \
|
||||
\
|
||||
bool acquire() { return p.acquire(); } \
|
||||
bool unacquire() { return p.unacquire(); } \
|
||||
bool acquired() { return p.acquired(); } \
|
||||
\
|
||||
bool poll(int16_t *table) { return p.poll(table); } \
|
||||
bool init() { return p.init(); } \
|
||||
void term() { p.term(); } \
|
||||
\
|
||||
Input##Name() : p(*new pInput##Name) {} \
|
||||
~Input##Name() { delete &p; } \
|
||||
\
|
||||
private: \
|
||||
pInput##Name &p; \
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
#ifdef INPUT_CARBON
|
||||
#include <ruby/input/carbon.cpp>
|
||||
#endif
|
@@ -1,28 +0,0 @@
|
||||
class Video {
|
||||
public:
|
||||
static const char *Handle;
|
||||
static const char *Synchronize;
|
||||
static const char *Filter;
|
||||
static const char *FragmentShader;
|
||||
static const char *VertexShader;
|
||||
|
||||
enum Filter {
|
||||
FilterPoint,
|
||||
FilterLinear,
|
||||
};
|
||||
|
||||
virtual bool cap(const nall::string& name) { return false; }
|
||||
virtual nall::any get(const nall::string& name) { return false; }
|
||||
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
|
||||
|
||||
virtual bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return false; }
|
||||
virtual void unlock() {}
|
||||
|
||||
virtual void clear() {}
|
||||
virtual void refresh() {}
|
||||
virtual bool init() { return true; }
|
||||
virtual void term() {}
|
||||
|
||||
Video() {}
|
||||
virtual ~Video() {}
|
||||
};
|
@@ -1,453 +0,0 @@
|
||||
#undef interface
|
||||
#define interface struct
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
#undef interface
|
||||
|
||||
#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1)
|
||||
|
||||
typedef HRESULT (__stdcall *EffectProc)(LPDIRECT3DDEVICE9, LPCVOID, UINT, D3DXMACRO const*, LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, LPD3DXEFFECT*, LPD3DXBUFFER*);
|
||||
typedef HRESULT (__stdcall *TextureProc)(LPDIRECT3DDEVICE9, LPCTSTR, LPDIRECT3DTEXTURE9*);
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoD3D {
|
||||
public:
|
||||
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;
|
||||
LPD3DXEFFECT effect;
|
||||
string shaderSource;
|
||||
|
||||
bool lost;
|
||||
unsigned iwidth, iheight;
|
||||
|
||||
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 shader; //device supports pixel shaders
|
||||
} caps;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
bool synchronize;
|
||||
unsigned filter;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} state;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == Video::FragmentShader) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
if(name == Video::Filter) return settings.filter;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
settings.filter = any_cast<unsigned>(value);
|
||||
if(lpd3d) update_filter();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::FragmentShader) {
|
||||
set_fragment_shader(any_cast<const char*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool recover() {
|
||||
if(!device) return false;
|
||||
|
||||
if(lost) {
|
||||
release_resources();
|
||||
if(device->Reset(&presentation) != D3D_OK) return false;
|
||||
}
|
||||
|
||||
lost = false;
|
||||
|
||||
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);
|
||||
|
||||
device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX, (D3DPOOL)flags.v_pool, &vertex_buffer, NULL);
|
||||
iwidth = 0;
|
||||
iheight = 0;
|
||||
resize(settings.width = 256, settings.height = 256);
|
||||
update_filter();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned rounded_power_of_two(unsigned n) {
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(iwidth >= width && iheight >= height) return;
|
||||
|
||||
iwidth = rounded_power_of_two(max(width, iwidth ));
|
||||
iheight = rounded_power_of_two(max(height, iheight));
|
||||
|
||||
if(d3dcaps.MaxTextureWidth < iwidth || d3dcaps.MaxTextureWidth < iheight) {
|
||||
//TODO: attempt to handle this more gracefully
|
||||
return;
|
||||
}
|
||||
|
||||
if(texture) texture->Release();
|
||||
device->CreateTexture(iwidth, iheight, 1, flags.t_usage, D3DFMT_X8R8G8B8, (D3DPOOL)flags.t_pool, &texture, NULL);
|
||||
}
|
||||
|
||||
void update_filter() {
|
||||
if(!device) return;
|
||||
if(lost && !recover()) 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 - 0.5);
|
||||
vertex[1].x = vertex[3].x = (double)(x + w - 0.5);
|
||||
vertex[0].y = vertex[1].y = (double)(y - 0.5);
|
||||
vertex[2].y = vertex[3].y = (double)(y + h - 0.5);
|
||||
|
||||
//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(lost && !recover()) return;
|
||||
|
||||
texture->GetLevelDesc(0, &d3dsd);
|
||||
texture->GetSurfaceLevel(0, &surface);
|
||||
|
||||
if(surface) {
|
||||
device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00));
|
||||
surface->Release();
|
||||
}
|
||||
|
||||
//clear primary display and all backbuffers
|
||||
for(unsigned 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, unsigned width, unsigned height) {
|
||||
if(lost && !recover()) return false;
|
||||
|
||||
if(width != settings.width || height != settings.height) {
|
||||
resize(settings.width = width, settings.height = height);
|
||||
}
|
||||
|
||||
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();
|
||||
surface->Release();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if(lost && !recover()) return;
|
||||
|
||||
RECT rd, rs; //dest, source rectangles
|
||||
GetClientRect(settings.handle, &rd);
|
||||
SetRect(&rs, 0, 0, settings.width, settings.height);
|
||||
|
||||
//if output size changed, driver must be re-initialized.
|
||||
//failure to do so causes scaling issues on some video drivers.
|
||||
if(state.width != rd.right || state.height != rd.bottom) {
|
||||
init();
|
||||
set_fragment_shader(shaderSource);
|
||||
return;
|
||||
}
|
||||
|
||||
if(caps.shader && effect) {
|
||||
device->BeginScene();
|
||||
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom);
|
||||
|
||||
D3DXVECTOR4 rubyTextureSize;
|
||||
rubyTextureSize.x = iwidth;
|
||||
rubyTextureSize.y = iheight;
|
||||
rubyTextureSize.z = 1.0 / iheight;
|
||||
rubyTextureSize.w = 1.0 / iwidth;
|
||||
effect->SetVector("rubyTextureSize", &rubyTextureSize);
|
||||
|
||||
D3DXVECTOR4 rubyInputSize;
|
||||
rubyInputSize.x = settings.width;
|
||||
rubyInputSize.y = settings.height;
|
||||
rubyInputSize.z = 1.0 / settings.height;
|
||||
rubyInputSize.w = 1.0 / settings.width;
|
||||
effect->SetVector("rubyInputSize", &rubyInputSize);
|
||||
|
||||
D3DXVECTOR4 rubyOutputSize;
|
||||
rubyOutputSize.x = rd.right;
|
||||
rubyOutputSize.y = rd.bottom;
|
||||
rubyOutputSize.z = 1.0 / rd.bottom;
|
||||
rubyOutputSize.w = 1.0 / rd.right;
|
||||
effect->SetVector("rubyOutputSize", &rubyOutputSize);
|
||||
|
||||
UINT passes;
|
||||
effect->Begin(&passes, 0);
|
||||
effect->SetTexture("rubyTexture", texture);
|
||||
device->SetTexture(0, texture);
|
||||
for(unsigned pass = 0; pass < passes; pass++) {
|
||||
effect->BeginPass(pass);
|
||||
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
||||
effect->EndPass();
|
||||
}
|
||||
effect->End();
|
||||
device->EndScene();
|
||||
} else {
|
||||
device->BeginScene();
|
||||
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 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;
|
||||
}
|
||||
}
|
||||
|
||||
if(device->Present(0, 0, 0, 0) == D3DERR_DEVICELOST) lost = true;
|
||||
}
|
||||
|
||||
void set_fragment_shader(const char *source) {
|
||||
if(!caps.shader) return;
|
||||
|
||||
if(effect) {
|
||||
effect->Release();
|
||||
effect = NULL;
|
||||
}
|
||||
|
||||
if(!source || !*source) {
|
||||
shaderSource = "";
|
||||
return;
|
||||
}
|
||||
|
||||
shaderSource = source;
|
||||
|
||||
HMODULE d3dx;
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
char t[256];
|
||||
sprintf(t, "d3dx9_%u.dll", i);
|
||||
d3dx = LoadLibrary(t);
|
||||
if(d3dx) break;
|
||||
}
|
||||
if(!d3dx) d3dx = LoadLibrary("d3dx9.dll");
|
||||
if(!d3dx) return;
|
||||
|
||||
EffectProc effectProc = (EffectProc)GetProcAddress(d3dx, "D3DXCreateEffect");
|
||||
TextureProc textureProc = (TextureProc)GetProcAddress(d3dx, "D3DXCreateTextureFromFileA");
|
||||
|
||||
LPD3DXBUFFER pBufferErrors = NULL;
|
||||
effectProc(device, shaderSource, lstrlen(source), NULL, NULL, 0, NULL, &effect, &pBufferErrors);
|
||||
|
||||
D3DXHANDLE hTech;
|
||||
effect->FindNextValidTechnique(NULL, &hTech);
|
||||
effect->SetTechnique(hTech);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
device->GetDeviceCaps(&d3dcaps);
|
||||
|
||||
caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES);
|
||||
caps.shader = d3dcaps.PixelShaderVersion > D3DPS_VERSION(1, 4);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lost = false;
|
||||
recover();
|
||||
return true;
|
||||
}
|
||||
|
||||
void release_resources() {
|
||||
if(effect) { effect->Release(); effect = 0; }
|
||||
if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; }
|
||||
if(surface) { surface->Release(); surface = 0; }
|
||||
if(texture) { texture->Release(); texture = 0; }
|
||||
}
|
||||
|
||||
void term() {
|
||||
release_resources();
|
||||
if(device) { device->Release(); device = 0; }
|
||||
if(lpd3d) { lpd3d->Release(); lpd3d = 0; }
|
||||
}
|
||||
|
||||
pVideoD3D() {
|
||||
effect = 0;
|
||||
vertex_buffer = 0;
|
||||
surface = 0;
|
||||
texture = 0;
|
||||
device = 0;
|
||||
lpd3d = 0;
|
||||
lost = true;
|
||||
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
settings.filter = Video::FilterLinear;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(D3D)
|
||||
|
||||
};
|
||||
|
||||
#undef D3DVERTEX
|
@@ -1,186 +0,0 @@
|
||||
#include <ddraw.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoDD {
|
||||
public:
|
||||
LPDIRECTDRAW lpdd;
|
||||
LPDIRECTDRAW7 lpdd7;
|
||||
LPDIRECTDRAWSURFACE7 screen, raster;
|
||||
LPDIRECTDRAWCLIPPER clipper;
|
||||
DDSURFACEDESC2 ddsd;
|
||||
DDSCAPS2 ddscaps;
|
||||
unsigned iwidth, iheight;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
bool synchronize;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(iwidth >= width && iheight >= height) return;
|
||||
|
||||
iwidth = max(width, iwidth);
|
||||
iheight = max(height, iheight);
|
||||
|
||||
if(raster) raster->Release();
|
||||
|
||||
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 = iwidth;
|
||||
ddsd.dwHeight = iheight;
|
||||
|
||||
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 clear();
|
||||
|
||||
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 = iwidth;
|
||||
ddsd.dwHeight = iheight;
|
||||
|
||||
if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear();
|
||||
}
|
||||
|
||||
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, unsigned width, unsigned height) {
|
||||
if(width != settings.width || height != settings.height) {
|
||||
resize(settings.width = width, settings.height = height);
|
||||
}
|
||||
|
||||
if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) {
|
||||
raster->Restore();
|
||||
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() {
|
||||
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, settings.width, settings.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);
|
||||
|
||||
raster = 0;
|
||||
iwidth = 0;
|
||||
iheight = 0;
|
||||
resize(settings.width = 256, settings.height = 256);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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() {
|
||||
lpdd = 0;
|
||||
lpdd7 = 0;
|
||||
screen = 0;
|
||||
raster = 0;
|
||||
clipper = 0;
|
||||
|
||||
settings.handle = 0;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(DD)
|
||||
|
||||
};
|
@@ -1,100 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoGDI {
|
||||
public:
|
||||
uint32_t *buffer;
|
||||
HBITMAP bitmap;
|
||||
HDC bitmapdc;
|
||||
BITMAPINFO bmi;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
settings.width = width;
|
||||
settings.height = height;
|
||||
|
||||
pitch = 1024 * 4;
|
||||
return data = buffer;
|
||||
}
|
||||
|
||||
void unlock() {}
|
||||
|
||||
void clear() {}
|
||||
|
||||
void refresh() {
|
||||
RECT rc;
|
||||
GetClientRect(settings.handle, &rc);
|
||||
|
||||
SetDIBits(bitmapdc, bitmap, 0, settings.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 - settings.height, settings.width, settings.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);
|
||||
|
||||
settings.width = 256;
|
||||
settings.height = 256;
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
DeleteObject(bitmap);
|
||||
DeleteDC(bitmapdc);
|
||||
}
|
||||
|
||||
pVideoGDI() {
|
||||
buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t));
|
||||
settings.handle = 0;
|
||||
}
|
||||
|
||||
~pVideoGDI() {
|
||||
if(buffer) free(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(GDI)
|
||||
|
||||
};
|
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
video.glx
|
||||
author: byuu
|
||||
license: public domain
|
||||
last updated: 2010-01-05
|
||||
|
||||
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 "opengl.hpp"
|
||||
|
||||
namespace ruby {
|
||||
|
||||
//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 OpenGL {
|
||||
public:
|
||||
int (*glSwapInterval)(int);
|
||||
|
||||
Display *display;
|
||||
int screen;
|
||||
Window xwindow;
|
||||
Colormap colormap;
|
||||
GLXContext glxcontext;
|
||||
GLXWindow glxwindow;
|
||||
|
||||
struct {
|
||||
int version_major, version_minor;
|
||||
bool double_buffer;
|
||||
bool is_direct;
|
||||
} glx;
|
||||
|
||||
struct {
|
||||
Window handle;
|
||||
bool synchronize;
|
||||
unsigned filter;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == Video::FragmentShader) return true;
|
||||
if(name == Video::VertexShader) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
if(name == Video::Filter) return settings.filter;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize) {
|
||||
if(settings.synchronize != any_cast<bool>(value)) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
if(glSwapInterval) glSwapInterval(settings.synchronize);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
settings.filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::FragmentShader) {
|
||||
OpenGL::set_fragment_shader(any_cast<const char*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::VertexShader) {
|
||||
OpenGL::set_vertex_shader(any_cast<const char*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
resize(width, height);
|
||||
settings.width = width;
|
||||
settings.height = height;
|
||||
return OpenGL::lock(data, pitch);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
OpenGL::clear();
|
||||
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
//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);
|
||||
}
|
||||
|
||||
OpenGL::refresh(settings.filter == Video::FilterLinear,
|
||||
settings.width, settings.height, parent.width, parent.height);
|
||||
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
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;
|
||||
|
||||
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 attributelist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
|
||||
XVisualInfo *vi = glXChooseVisual(display, screen, 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);
|
||||
|
||||
OpenGL::init();
|
||||
settings.width = 256;
|
||||
settings.height = 256;
|
||||
|
||||
//vertical synchronization
|
||||
if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
|
||||
if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
|
||||
if( glSwapInterval) glSwapInterval(settings.synchronize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
OpenGL::term();
|
||||
|
||||
if(glxcontext) {
|
||||
glXDestroyContext(display, glxcontext);
|
||||
glxcontext = 0;
|
||||
}
|
||||
|
||||
if(xwindow) {
|
||||
XUnmapWindow(display, xwindow);
|
||||
xwindow = 0;
|
||||
}
|
||||
|
||||
if(colormap) {
|
||||
XFreeColormap(display, colormap);
|
||||
colormap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pVideoGLX() : glSwapInterval(0) {
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
xwindow = 0;
|
||||
colormap = 0;
|
||||
glxcontext = 0;
|
||||
glxwindow = 0;
|
||||
}
|
||||
|
||||
~pVideoGLX() { term(); }
|
||||
};
|
||||
|
||||
DeclareVideo(GLX)
|
||||
|
||||
};
|
@@ -1,225 +0,0 @@
|
||||
#include <GL/gl.h>
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
#include <GL/glx.h>
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#include <GL/glext.h>
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#else
|
||||
#error "ruby::OpenGL: unsupported platform"
|
||||
#endif
|
||||
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram = 0;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram = 0;
|
||||
PFNGLCREATESHADERPROC glCreateShader = 0;
|
||||
PFNGLDELETESHADERPROC glDeleteShader = 0;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource = 0;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader = 0;
|
||||
PFNGLATTACHSHADERPROC glAttachShader = 0;
|
||||
PFNGLDETACHSHADERPROC glDetachShader = 0;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram = 0;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = 0;
|
||||
PFNGLUNIFORM1IPROC glUniform1i = 0;
|
||||
PFNGLUNIFORM2FVPROC glUniform2fv = 0;
|
||||
PFNGLUNIFORM4FVPROC glUniform4fv = 0;
|
||||
|
||||
class OpenGL {
|
||||
public:
|
||||
GLuint gltexture;
|
||||
GLuint glprogram;
|
||||
GLuint fragmentshader;
|
||||
GLuint vertexshader;
|
||||
bool shader_support;
|
||||
|
||||
uint32_t *buffer;
|
||||
unsigned iwidth, iheight;
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(iwidth >= width && iheight >= height) return;
|
||||
|
||||
if(gltexture) glDeleteTextures(1, &gltexture);
|
||||
iwidth = max(width, iwidth );
|
||||
iheight = max(height, iheight);
|
||||
if(buffer) delete[] buffer;
|
||||
buffer = new uint32_t[iwidth * iheight];
|
||||
|
||||
glGenTextures(1, &gltexture);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
/* mip-map level = */ 0, /* internal format = */ GL_RGB,
|
||||
iwidth, iheight, /* border = */ 0, /* format = */ GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch) {
|
||||
pitch = iwidth * sizeof(uint32_t);
|
||||
return data = buffer;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(buffer, 0, iwidth * iheight * sizeof(uint32_t));
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFlush();
|
||||
}
|
||||
|
||||
void refresh(bool smooth, unsigned inwidth, unsigned inheight, unsigned outwidth, unsigned outheight) {
|
||||
if(shader_support) {
|
||||
glUseProgram(glprogram);
|
||||
GLint location;
|
||||
|
||||
float inputSize[2] = { (float)inwidth, (float)inheight };
|
||||
location = glGetUniformLocation(glprogram, "rubyInputSize");
|
||||
glUniform2fv(location, 1, inputSize);
|
||||
|
||||
float outputSize[2] = { (float)outwidth, (float)outheight };
|
||||
location = glGetUniformLocation(glprogram, "rubyOutputSize");
|
||||
glUniform2fv(location, 1, outputSize);
|
||||
|
||||
float textureSize[2] = { (float)iwidth, (float)iheight };
|
||||
location = glGetUniformLocation(glprogram, "rubyTextureSize");
|
||||
glUniform2fv(location, 1, textureSize);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, outwidth, 0, outheight, -1.0, 1.0);
|
||||
glViewport(0, 0, outwidth, outheight);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
/* mip-map level = */ 0, /* x = */ 0, /* y = */ 0,
|
||||
inwidth, inheight, 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(inwidth) / double(iwidth);
|
||||
double h = double(inheight) / double(iheight);
|
||||
int u = outwidth;
|
||||
int v = outheight;
|
||||
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(shader_support) {
|
||||
glUseProgram(0);
|
||||
}
|
||||
}
|
||||
|
||||
void set_fragment_shader(const char *source) {
|
||||
if(!shader_support) return;
|
||||
|
||||
if(fragmentshader) {
|
||||
glDetachShader(glprogram, fragmentshader);
|
||||
glDeleteShader(fragmentshader);
|
||||
fragmentshader = 0;
|
||||
}
|
||||
|
||||
if(source) {
|
||||
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragmentshader, 1, &source, 0);
|
||||
glCompileShader(fragmentshader);
|
||||
glAttachShader(glprogram, fragmentshader);
|
||||
}
|
||||
|
||||
glLinkProgram(glprogram);
|
||||
}
|
||||
|
||||
void set_vertex_shader(const char *source) {
|
||||
if(!shader_support) return;
|
||||
|
||||
if(vertexshader) {
|
||||
glDetachShader(glprogram, vertexshader);
|
||||
glDeleteShader(vertexshader);
|
||||
vertexshader = 0;
|
||||
}
|
||||
|
||||
if(source) {
|
||||
vertexshader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertexshader, 1, &source, 0);
|
||||
glCompileShader(vertexshader);
|
||||
glAttachShader(glprogram, vertexshader);
|
||||
}
|
||||
|
||||
glLinkProgram(glprogram);
|
||||
}
|
||||
|
||||
void init() {
|
||||
//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);
|
||||
|
||||
//bind shader functions
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)glGetProcAddress("glCreateProgram");
|
||||
glUseProgram = (PFNGLUSEPROGRAMPROC)glGetProcAddress("glUseProgram");
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)glGetProcAddress("glCreateShader");
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)glGetProcAddress("glDeleteShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)glGetProcAddress("glShaderSource");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)glGetProcAddress("glCompileShader");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)glGetProcAddress("glAttachShader");
|
||||
glDetachShader = (PFNGLDETACHSHADERPROC)glGetProcAddress("glDetachShader");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)glGetProcAddress("glLinkProgram");
|
||||
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)glGetProcAddress("glGetUniformLocation");
|
||||
glUniform1i = (PFNGLUNIFORM1IPROC)glGetProcAddress("glUniform1i");
|
||||
glUniform2fv = (PFNGLUNIFORM2FVPROC)glGetProcAddress("glUniform2fv");
|
||||
glUniform4fv = (PFNGLUNIFORM4FVPROC)glGetProcAddress("glUniform4fv");
|
||||
|
||||
shader_support = glCreateProgram && glUseProgram && glCreateShader
|
||||
&& glDeleteShader && glShaderSource && glCompileShader && glAttachShader
|
||||
&& glDetachShader && glLinkProgram && glGetUniformLocation
|
||||
&& glUniform1i && glUniform2fv && glUniform4fv;
|
||||
|
||||
if(shader_support) glprogram = glCreateProgram();
|
||||
|
||||
//create surface texture
|
||||
resize(256, 256);
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(gltexture) {
|
||||
glDeleteTextures(1, &gltexture);
|
||||
gltexture = 0;
|
||||
}
|
||||
|
||||
if(buffer) {
|
||||
delete[] buffer;
|
||||
buffer = 0;
|
||||
iwidth = 0;
|
||||
iheight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGL() {
|
||||
gltexture = 0;
|
||||
glprogram = 0;
|
||||
fragmentshader = 0;
|
||||
vertexshader = 0;
|
||||
|
||||
buffer = 0;
|
||||
iwidth = 0;
|
||||
iheight = 0;
|
||||
}
|
||||
};
|
@@ -1,174 +0,0 @@
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#endif
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoQtOpenGL {
|
||||
public:
|
||||
QWidget *parent;
|
||||
QVBoxLayout *layout;
|
||||
|
||||
class RubyGLWidget : public QGLWidget {
|
||||
public:
|
||||
GLuint texture;
|
||||
unsigned textureWidth, textureHeight;
|
||||
|
||||
uint32_t *buffer;
|
||||
unsigned rasterWidth, rasterHeight;
|
||||
|
||||
bool synchronize;
|
||||
unsigned filter;
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(width > textureWidth || height > textureHeight) {
|
||||
textureWidth = max(width, textureWidth);
|
||||
textureHeight = max(height, textureHeight);
|
||||
|
||||
if(buffer) {
|
||||
delete[] buffer;
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
buffer = new uint32_t[textureWidth * textureHeight];
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, textureWidth);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void updateSynchronization() {
|
||||
#ifdef __APPLE__
|
||||
makeCurrent();
|
||||
CGLContextObj context = CGLGetCurrentContext();
|
||||
GLint value = synchronize; //0 = draw immediately (no vsync), 1 = draw once per frame (vsync)
|
||||
CGLSetParameter(context, kCGLCPSwapInterval, &value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void paintGL() {
|
||||
unsigned outputWidth = width();
|
||||
unsigned outputHeight = height();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, outputWidth, 0, outputHeight, -1.0, 1.0);
|
||||
glViewport(0, 0, outputWidth, outputHeight);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter == Video::FilterPoint) ? GL_NEAREST : GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter == Video::FilterPoint) ? GL_NEAREST : GL_LINEAR);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rasterWidth, rasterHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
|
||||
double w = (double)rasterWidth / (double)textureWidth;
|
||||
double h = (double)rasterHeight / (double)textureHeight;
|
||||
unsigned u = outputWidth;
|
||||
unsigned v = outputHeight;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void initializeGL() {
|
||||
format().setDoubleBuffer(true);
|
||||
|
||||
texture = 0;
|
||||
textureWidth = 0;
|
||||
textureHeight = 0;
|
||||
buffer = 0;
|
||||
resize(rasterWidth = 256, rasterHeight = 256);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glEnable(GL_DITHER);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} *widget;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == "QWidget") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Synchronize) return widget->synchronize;
|
||||
if(name == Video::Filter) return widget->filter;
|
||||
if(name == "QWidget") return parent;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Synchronize) {
|
||||
widget->synchronize = any_cast<bool>(value);
|
||||
widget->updateSynchronization();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
widget->filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == "QWidget") {
|
||||
parent = any_cast<QWidget*>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
widget->resize(width, height);
|
||||
widget->rasterWidth = width;
|
||||
widget->rasterHeight = height;
|
||||
|
||||
pitch = widget->textureWidth * sizeof(uint32_t);
|
||||
return data = widget->buffer;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(widget->buffer, 0, widget->textureWidth * widget->textureHeight * sizeof(uint32_t));
|
||||
widget->updateGL();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
widget->updateGL();
|
||||
}
|
||||
|
||||
bool init() {
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
|
||||
widget = new RubyGLWidget;
|
||||
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(widget);
|
||||
parent->setLayout(layout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(QtOpenGL)
|
||||
|
||||
};
|
@@ -1,126 +0,0 @@
|
||||
namespace ruby {
|
||||
|
||||
struct VideoQtRasterContext {
|
||||
QImage *image;
|
||||
unsigned width, height;
|
||||
unsigned filter;
|
||||
} context;
|
||||
|
||||
class pVideoQtRaster {
|
||||
public:
|
||||
QWidget *parent;
|
||||
QVBoxLayout *layout;
|
||||
|
||||
struct QtImage : public QWidget {
|
||||
VideoQtRasterContext &context;
|
||||
|
||||
void paintEvent(QPaintEvent*) {
|
||||
if(context.image == 0) return;
|
||||
QPainter painter(this);
|
||||
|
||||
if(size().width() == context.width && size().height() == context.height) {
|
||||
painter.drawImage(0, 0, *context.image);
|
||||
} else {
|
||||
Qt::TransformationMode mode = Qt::FastTransformation;
|
||||
if(context.filter == Video::FilterLinear) mode = Qt::SmoothTransformation;
|
||||
painter.drawImage(0, 0, context.image->scaled(size(), Qt::IgnoreAspectRatio, mode));
|
||||
}
|
||||
}
|
||||
|
||||
QtImage(QWidget *parent, VideoQtRasterContext &context_) : QWidget(parent), context(context_) {}
|
||||
} *widget;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == "QWidget") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Filter) return context.filter;
|
||||
if(name == "QWidget") return parent;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Filter) {
|
||||
context.filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == "QWidget") {
|
||||
parent = any_cast<QWidget*>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(context.width != width || context.height != height) {
|
||||
if(context.image) delete context.image;
|
||||
context.image = new QImage(context.width = width, context.height = height, QImage::Format_RGB32);
|
||||
}
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
//if image size has changed since last lock(), re-allocate buffer to match new size
|
||||
if(width != context.width || height != context.height) resize(width, height);
|
||||
|
||||
pitch = width * sizeof(uint32_t);
|
||||
return data = (uint32_t*)context.image->bits();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
context.image->fill(0);
|
||||
widget->update();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
widget->update();
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
|
||||
context.image = 0;
|
||||
context.width = 0;
|
||||
context.height = 0;
|
||||
context.filter = Video::FilterPoint;
|
||||
resize(256, 256);
|
||||
|
||||
widget = new QtImage(parent, context);
|
||||
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(widget);
|
||||
parent->setLayout(layout);
|
||||
clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(context.image) delete context.image;
|
||||
if(widget) delete widget;
|
||||
if(layout) delete layout;
|
||||
|
||||
context.image = 0;
|
||||
widget = 0;
|
||||
layout = 0;
|
||||
}
|
||||
|
||||
pVideoQtRaster() {
|
||||
context.image = 0;
|
||||
widget = 0;
|
||||
layout = 0;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(QtRaster)
|
||||
|
||||
};
|
@@ -1,140 +0,0 @@
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoSDL {
|
||||
public:
|
||||
Display *display;
|
||||
SDL_Surface *screen, *buffer;
|
||||
unsigned iwidth, iheight;
|
||||
|
||||
struct {
|
||||
uintptr_t handle;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(iwidth >= width && iheight >= height) return;
|
||||
|
||||
iwidth = max(width, iwidth);
|
||||
iheight = max(height, iheight);
|
||||
|
||||
if(buffer) SDL_FreeSurface(buffer);
|
||||
buffer = SDL_CreateRGBSurface(
|
||||
SDL_SWSURFACE, iwidth, iheight, 32,
|
||||
0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
|
||||
);
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
if(width != settings.width || height != settings.height) {
|
||||
resize(settings.width = width, settings.height = height);
|
||||
}
|
||||
|
||||
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);
|
||||
for(unsigned y = 0; y < iheight; y++) {
|
||||
uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
|
||||
for(unsigned x = 0; x < iwidth; x++) *data++ = 0xff000000;
|
||||
}
|
||||
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
//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);
|
||||
for(unsigned y = 0; y < settings.height; y++) {
|
||||
uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
|
||||
for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000;
|
||||
}
|
||||
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 = settings.width;
|
||||
src.h = settings.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", (long int)settings.handle);
|
||||
putenv(env);
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
|
||||
|
||||
buffer = 0;
|
||||
iwidth = 0;
|
||||
iheight = 0;
|
||||
resize(settings.width = 256, settings.height = 256);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
XCloseDisplay(display);
|
||||
SDL_FreeSurface(buffer);
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
}
|
||||
|
||||
pVideoSDL() {
|
||||
settings.handle = 0;
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(SDL)
|
||||
|
||||
};
|
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
video.wgl
|
||||
authors: byuu, krom
|
||||
*/
|
||||
|
||||
#include "opengl.hpp"
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoWGL : public OpenGL {
|
||||
public:
|
||||
BOOL (APIENTRY *glSwapInterval)(int);
|
||||
|
||||
HDC display;
|
||||
HGLRC wglcontext;
|
||||
HWND window;
|
||||
HINSTANCE glwindow;
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
bool synchronize;
|
||||
unsigned filter;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == Video::FragmentShader) return true;
|
||||
if(name == Video::VertexShader) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
if(name == Video::Filter) return settings.filter;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize) {
|
||||
if(settings.synchronize != any_cast<bool>(value)) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
if(wglcontext) init();
|
||||
}
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
settings.filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::FragmentShader) {
|
||||
OpenGL::set_fragment_shader(any_cast<const char*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::VertexShader) {
|
||||
OpenGL::set_vertex_shader(any_cast<const char*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
resize(width, height);
|
||||
settings.width = width;
|
||||
settings.height = height;
|
||||
return OpenGL::lock(data, pitch);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
OpenGL::clear();
|
||||
SwapBuffers(display);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
RECT rc;
|
||||
GetClientRect(settings.handle, &rc);
|
||||
|
||||
OpenGL::refresh(settings.filter == Video::FilterLinear,
|
||||
settings.width, settings.height,
|
||||
rc.right - rc.left, rc.bottom - rc.top);
|
||||
|
||||
SwapBuffers(display);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
term();
|
||||
|
||||
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 | PFD_DOUBLEBUFFER;
|
||||
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);
|
||||
|
||||
OpenGL::init();
|
||||
settings.width = 256;
|
||||
settings.height = 256;
|
||||
|
||||
//vertical synchronization
|
||||
if(!glSwapInterval) glSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT");
|
||||
if( glSwapInterval) glSwapInterval(settings.synchronize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
OpenGL::term();
|
||||
|
||||
if(wglcontext) {
|
||||
wglDeleteContext(wglcontext);
|
||||
wglcontext = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pVideoWGL() : glSwapInterval(0) {
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
settings.filter = 0;
|
||||
|
||||
window = 0;
|
||||
wglcontext = 0;
|
||||
glwindow = 0;
|
||||
}
|
||||
|
||||
~pVideoWGL() { term(); }
|
||||
};
|
||||
|
||||
DeclareVideo(WGL)
|
||||
|
||||
};
|
@@ -1,498 +0,0 @@
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.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 {
|
||||
|
||||
class pVideoXv {
|
||||
public:
|
||||
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;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
Window handle;
|
||||
bool synchronize;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) {
|
||||
return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize) {
|
||||
Display *display = XOpenDisplay(0);
|
||||
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
|
||||
if(atom != None && device.port >= 0) {
|
||||
settings.synchronize = any_cast<bool>(value);
|
||||
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(device.width >= width && device.height >= height) return;
|
||||
device.width = max(width, device.width);
|
||||
device.height = max(height, device.height);
|
||||
|
||||
XShmDetach(device.display, &device.shminfo);
|
||||
shmdt(device.shminfo.shmaddr);
|
||||
shmctl(device.shminfo.shmid, IPC_RMID, NULL);
|
||||
XFree(device.image);
|
||||
delete[] buffer;
|
||||
|
||||
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo);
|
||||
|
||||
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;
|
||||
XShmAttach(device.display, &device.shminfo);
|
||||
|
||||
buffer = new uint32_t[device.width * device.height];
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
if(width != settings.width || height != settings.height) {
|
||||
resize(settings.width = width, settings.height = height);
|
||||
}
|
||||
|
||||
pitch = device.width * 4;
|
||||
return data = buffer;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(buffer, 0, device.width * device.height * sizeof(uint32_t));
|
||||
//clear twice in case video is double buffered ...
|
||||
refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
unsigned width = settings.width;
|
||||
unsigned height = settings.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 && format[i].red_mask == 0xf800) {
|
||||
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 <= 16 && format[i].red_mask == 0x7c00) {
|
||||
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.width = 256;
|
||||
device.height = 256;
|
||||
|
||||
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &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[device.width * device.height];
|
||||
settings.width = 256;
|
||||
settings.height = 256;
|
||||
init_yuv_tables();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
XShmDetach(device.display, &device.shminfo);
|
||||
shmdt(device.shminfo.shmaddr);
|
||||
shmctl(device.shminfo.shmid, IPC_RMID, NULL);
|
||||
XFree(device.image);
|
||||
|
||||
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 += device.width;
|
||||
output += device.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 += (device.width - width);
|
||||
output += (device.width - 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 += device.width - width;
|
||||
output += device.width - 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 += device.width - width;
|
||||
output += device.width - 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 += device.width - width;
|
||||
output += device.width - 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 += device.width - width;
|
||||
output += device.width - 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() {
|
||||
device.window = 0;
|
||||
device.colormap = 0;
|
||||
device.port = -1;
|
||||
|
||||
ytable = 0;
|
||||
utable = 0;
|
||||
vtable = 0;
|
||||
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
}
|
||||
|
||||
~pVideoXv() {
|
||||
term();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(Xv)
|
||||
|
||||
};
|
Reference in New Issue
Block a user