mirror of https://github.com/AxioDL/boo.git
Add PulseAudio backend. Various Xlib fixes
This commit is contained in:
parent
b5f1657dff
commit
edf2c1e34d
|
@ -150,7 +150,7 @@ else(NOT GEKKO)
|
|||
lib/graphicsdev/GL.cpp
|
||||
lib/graphicsdev/GLX.cpp
|
||||
lib/graphicsdev/glew.c
|
||||
lib/audiodev/ALSA.cpp)
|
||||
lib/audiodev/LinuxMidi.hpp)
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
|
@ -190,6 +190,18 @@ else(NOT GEKKO)
|
|||
lib32
|
||||
lib64)
|
||||
|
||||
find_path(PULSEAUDIO_INCLUDE_DIR
|
||||
NAMES pulse/pulseaudio.h)
|
||||
|
||||
if (PULSEAUDIO_INCLUDE_DIR)
|
||||
list(APPEND PLAT_SRCS lib/audiodev/PulseAudio.cpp)
|
||||
list(APPEND _BOO_SYS_LIBS pulse)
|
||||
message(STATUS "Using PulseAudio backend")
|
||||
else()
|
||||
list(APPEND PLAT_SRCS lib/audiodev/ALSA.cpp)
|
||||
message(STATUS "Using ALSA backend")
|
||||
endif()
|
||||
|
||||
if(DBUS_INCLUDE_DIR-NOTFOUND)
|
||||
message(FATAL_ERROR "Unix build of boo requires dbus")
|
||||
endif()
|
||||
|
|
|
@ -299,7 +299,7 @@ public:
|
|||
virtual void waitForRetrace(IAudioVoiceEngine* voxEngine=nullptr)=0;
|
||||
|
||||
virtual uintptr_t getPlatformHandle() const=0;
|
||||
virtual void _incomingEvent(void* event) {(void)event;}
|
||||
virtual bool _incomingEvent(void* event) {(void)event; return false;}
|
||||
|
||||
virtual ETouchType getTouchType() const=0;
|
||||
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
#include <memory>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include "AudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
static inline double TimespecToDouble(struct timespec& ts)
|
||||
{
|
||||
return ts.tv_sec + ts.tv_nsec / 1.0e9;
|
||||
}
|
||||
#include "LinuxMidi.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
static logvisor::Module Log("boo::ALSA");
|
||||
logvisor::Module ALSALog("boo::ALSA");
|
||||
|
||||
static const uint64_t StereoChans = (1 << SND_CHMAP_FL) |
|
||||
(1 << SND_CHMAP_FR);
|
||||
|
@ -40,7 +33,7 @@ static const uint64_t S71Chans = (1 << SND_CHMAP_FL) |
|
|||
(1 << SND_CHMAP_SL) |
|
||||
(1 << SND_CHMAP_SR);
|
||||
|
||||
struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||
struct ALSAAudioVoiceEngine : LinuxMidi
|
||||
{
|
||||
snd_pcm_t* m_pcm;
|
||||
snd_pcm_uframes_t m_bufSize;
|
||||
|
@ -129,7 +122,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
/* Open device */
|
||||
if (snd_pcm_open(&m_pcm, "default", SND_PCM_STREAM_PLAYBACK, 0))
|
||||
{
|
||||
Log.report(logvisor::Error, "unable to allocate ALSA voice");
|
||||
ALSALog.report(logvisor::Error, "unable to allocate ALSA voice");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -144,7 +137,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set interleaved mode. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set interleaved mode. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -172,7 +165,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "unsupported audio formats on default ALSA device");
|
||||
ALSALog.report(logvisor::Error, "unsupported audio formats on default ALSA device");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,7 +174,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set format. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set format. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -193,7 +186,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set channels number. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set channels number. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -211,7 +204,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "unsupported audio sample rates on default ALSA device");
|
||||
ALSALog.report(logvisor::Error, "unsupported audio sample rates on default ALSA device");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -220,7 +213,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set rate. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set rate. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
m_mixInfo.m_sampleRate = bestRate;
|
||||
|
@ -232,7 +225,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set period size. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set period size. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -242,7 +235,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set buffer size. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set buffer size. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -252,7 +245,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_hw_params_free(hwParams);
|
||||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
Log.report(logvisor::Error, "Can't set harware parameters. %s\n", snd_strerror(errr));
|
||||
ALSALog.report(logvisor::Error, "Can't set harware parameters. %s\n", snd_strerror(errr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -308,7 +301,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
snd_pcm_close(m_pcm);
|
||||
m_pcm = nullptr;
|
||||
snd_pcm_free_chmaps(chmaps);
|
||||
Log.report(logvisor::Error, "unable to find matching ALSA voice chmap");
|
||||
ALSALog.report(logvisor::Error, "unable to find matching ALSA voice chmap");
|
||||
return;
|
||||
}
|
||||
chmapOut.m_channelCount = chCount;
|
||||
|
@ -373,7 +366,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
{
|
||||
snd_pcm_prepare(m_pcm);
|
||||
frames = snd_pcm_avail_update(m_pcm);
|
||||
Log.report(logvisor::Warning, "ALSA underrun %ld frames", frames);
|
||||
ALSALog.report(logvisor::Warning, "ALSA underrun %ld frames", frames);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
@ -403,234 +396,6 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> ret;
|
||||
int status;
|
||||
int card = -1; /* use -1 to prime the pump of iterating through card list */
|
||||
|
||||
if ((status = snd_card_next(&card)) < 0)
|
||||
return {};
|
||||
if (card < 0)
|
||||
return {};
|
||||
|
||||
while (card >= 0)
|
||||
{
|
||||
snd_ctl_t *ctl;
|
||||
char name[32];
|
||||
int device = -1;
|
||||
int status;
|
||||
sprintf(name, "hw:%d", card);
|
||||
if ((status = snd_ctl_open(&ctl, name, 0)) < 0)
|
||||
continue;
|
||||
|
||||
do {
|
||||
status = snd_ctl_rawmidi_next_device(ctl, &device);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (device >= 0)
|
||||
{
|
||||
snd_rawmidi_info_t *info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info_set_device(info, device);
|
||||
sprintf(name + strlen(name), ",%d", device);
|
||||
ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info)));
|
||||
}
|
||||
} while (device >= 0);
|
||||
|
||||
snd_ctl_close(ctl);
|
||||
|
||||
if ((status = snd_card_next(&card)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Empty handler for SIGQUIT */
|
||||
static void _sigquit(int) {}
|
||||
|
||||
static void MIDIReceiveProc(snd_rawmidi_t* midi, const ReceiveFunctor& receiver, bool& running)
|
||||
{
|
||||
struct sigaction s;
|
||||
s.sa_handler = _sigquit;
|
||||
sigemptyset(&s.sa_mask);
|
||||
s.sa_flags = 0;
|
||||
sigaction(SIGQUIT, &s, nullptr);
|
||||
|
||||
snd_rawmidi_status_t* midiStatus;
|
||||
snd_rawmidi_status_malloc(&midiStatus);
|
||||
|
||||
uint8_t buf[512];
|
||||
while (running)
|
||||
{
|
||||
snd_htimestamp_t ts;
|
||||
snd_rawmidi_status(midi, midiStatus);
|
||||
snd_rawmidi_status_get_tstamp(midiStatus, &ts);
|
||||
int rdBytes = snd_rawmidi_read(midi, buf, 512);
|
||||
if (rdBytes < 0)
|
||||
{
|
||||
if (rdBytes != -EINTR)
|
||||
Log.report(logvisor::Error, "MIDI connection lost");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
receiver(std::vector<uint8_t>(std::cbegin(buf), std::cbegin(buf) + rdBytes), TimespecToDouble(ts));
|
||||
}
|
||||
|
||||
snd_rawmidi_status_free(midiStatus);
|
||||
}
|
||||
|
||||
struct MIDIIn : public IMIDIIn
|
||||
{
|
||||
bool m_midiRunning = true;
|
||||
snd_rawmidi_t* m_midi;
|
||||
std::thread m_midiThread;
|
||||
|
||||
MIDIIn(snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver)
|
||||
: IMIDIIn(virt, std::move(receiver)), m_midi(midi),
|
||||
m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver, m_midiRunning)) {}
|
||||
|
||||
~MIDIIn()
|
||||
{
|
||||
m_midiRunning = false;
|
||||
pthread_kill(m_midiThread.native_handle(), SIGQUIT);
|
||||
if (m_midiThread.joinable())
|
||||
m_midiThread.join();
|
||||
snd_rawmidi_close(m_midi);
|
||||
}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midi, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
struct MIDIOut : public IMIDIOut
|
||||
{
|
||||
snd_rawmidi_t* m_midi;
|
||||
MIDIOut(snd_rawmidi_t* midi, bool virt)
|
||||
: IMIDIOut(virt), m_midi(midi) {}
|
||||
|
||||
~MIDIOut() {snd_rawmidi_close(m_midi);}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midi, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t send(const void* buf, size_t len) const
|
||||
{
|
||||
return size_t(std::max(0l, snd_rawmidi_write(m_midi, buf, len)));
|
||||
}
|
||||
};
|
||||
|
||||
struct MIDIInOut : public IMIDIInOut
|
||||
{
|
||||
bool m_midiRunning = true;
|
||||
snd_rawmidi_t* m_midiIn;
|
||||
snd_rawmidi_t* m_midiOut;
|
||||
std::thread m_midiThread;
|
||||
|
||||
MIDIInOut(snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver)
|
||||
: IMIDIInOut(virt, std::move(receiver)), m_midiIn(midiIn), m_midiOut(midiOut),
|
||||
m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver, m_midiRunning)) {}
|
||||
|
||||
~MIDIInOut()
|
||||
{
|
||||
m_midiRunning = false;
|
||||
pthread_kill(m_midiThread.native_handle(), SIGQUIT);
|
||||
if (m_midiThread.joinable())
|
||||
m_midiThread.join();
|
||||
snd_rawmidi_close(m_midiIn);
|
||||
snd_rawmidi_close(m_midiOut);
|
||||
}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midiIn, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t send(const void* buf, size_t len) const
|
||||
{
|
||||
return size_t(std::max(0l, snd_rawmidi_write(m_midiOut, buf, len)));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midi;
|
||||
status = snd_rawmidi_open(&midi, nullptr, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midi;
|
||||
status = snd_rawmidi_open(nullptr, &midi, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIOut>(midi, true);
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midiIn;
|
||||
snd_rawmidi_t* midiOut;
|
||||
status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)
|
||||
{
|
||||
snd_rawmidi_t* midi;
|
||||
int status = snd_rawmidi_open(&midi, nullptr, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)
|
||||
{
|
||||
snd_rawmidi_t* midi;
|
||||
int status = snd_rawmidi_open(nullptr, &midi, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIOut>(midi, true);
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)
|
||||
{
|
||||
snd_rawmidi_t* midiIn;
|
||||
snd_rawmidi_t* midiOut;
|
||||
int status = snd_rawmidi_open(&midiIn, &midiOut, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||
}
|
||||
|
||||
bool useMIDILock() const {return true;}
|
||||
};
|
||||
|
||||
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine()
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
#ifndef BOO_LINUXMIDI_HPP
|
||||
#define BOO_LINUXMIDI_HPP
|
||||
|
||||
#include "AudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include <thread>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
extern logvisor::Module ALSALog;
|
||||
|
||||
static inline double TimespecToDouble(struct timespec& ts)
|
||||
{
|
||||
return ts.tv_sec + ts.tv_nsec / 1.0e9;
|
||||
}
|
||||
|
||||
struct LinuxMidi : BaseAudioVoiceEngine
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> ret;
|
||||
int status;
|
||||
int card = -1; /* use -1 to prime the pump of iterating through card list */
|
||||
|
||||
if ((status = snd_card_next(&card)) < 0)
|
||||
return {};
|
||||
if (card < 0)
|
||||
return {};
|
||||
|
||||
while (card >= 0)
|
||||
{
|
||||
snd_ctl_t *ctl;
|
||||
char name[32];
|
||||
int device = -1;
|
||||
int status;
|
||||
sprintf(name, "hw:%d", card);
|
||||
if ((status = snd_ctl_open(&ctl, name, 0)) < 0)
|
||||
continue;
|
||||
|
||||
do {
|
||||
status = snd_ctl_rawmidi_next_device(ctl, &device);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (device >= 0)
|
||||
{
|
||||
snd_rawmidi_info_t *info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info_set_device(info, device);
|
||||
sprintf(name + strlen(name), ",%d", device);
|
||||
ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info)));
|
||||
}
|
||||
} while (device >= 0);
|
||||
|
||||
snd_ctl_close(ctl);
|
||||
|
||||
if ((status = snd_card_next(&card)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void MIDIFreeProc(void* midiStatus)
|
||||
{
|
||||
snd_rawmidi_status_free((snd_rawmidi_status_t*)midiStatus);
|
||||
}
|
||||
|
||||
static void MIDIReceiveProc(snd_rawmidi_t* midi, const ReceiveFunctor& receiver)
|
||||
{
|
||||
snd_rawmidi_status_t* midiStatus;
|
||||
snd_rawmidi_status_malloc(&midiStatus);
|
||||
pthread_cleanup_push(MIDIFreeProc, midiStatus);
|
||||
|
||||
uint8_t buf[512];
|
||||
while (true)
|
||||
{
|
||||
snd_htimestamp_t ts;
|
||||
snd_rawmidi_status(midi, midiStatus);
|
||||
snd_rawmidi_status_get_tstamp(midiStatus, &ts);
|
||||
int rdBytes = snd_rawmidi_read(midi, buf, 512);
|
||||
if (rdBytes < 0)
|
||||
{
|
||||
if (rdBytes != -EINTR)
|
||||
{
|
||||
ALSALog.report(logvisor::Error, "MIDI connection lost");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int oldtype;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
|
||||
receiver(std::vector<uint8_t>(std::cbegin(buf), std::cbegin(buf) + rdBytes), TimespecToDouble(ts));
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(1);
|
||||
}
|
||||
|
||||
struct MIDIIn : public IMIDIIn
|
||||
{
|
||||
snd_rawmidi_t* m_midi;
|
||||
std::thread m_midiThread;
|
||||
|
||||
MIDIIn(snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver)
|
||||
: IMIDIIn(virt, std::move(receiver)), m_midi(midi),
|
||||
m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver)) {}
|
||||
|
||||
~MIDIIn()
|
||||
{
|
||||
pthread_cancel(m_midiThread.native_handle());
|
||||
if (m_midiThread.joinable())
|
||||
m_midiThread.join();
|
||||
snd_rawmidi_close(m_midi);
|
||||
}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midi, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
struct MIDIOut : public IMIDIOut
|
||||
{
|
||||
snd_rawmidi_t* m_midi;
|
||||
MIDIOut(snd_rawmidi_t* midi, bool virt)
|
||||
: IMIDIOut(virt), m_midi(midi) {}
|
||||
|
||||
~MIDIOut() {snd_rawmidi_close(m_midi);}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midi, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t send(const void* buf, size_t len) const
|
||||
{
|
||||
return size_t(std::max(0l, snd_rawmidi_write(m_midi, buf, len)));
|
||||
}
|
||||
};
|
||||
|
||||
struct MIDIInOut : public IMIDIInOut
|
||||
{
|
||||
snd_rawmidi_t* m_midiIn;
|
||||
snd_rawmidi_t* m_midiOut;
|
||||
std::thread m_midiThread;
|
||||
|
||||
MIDIInOut(snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver)
|
||||
: IMIDIInOut(virt, std::move(receiver)), m_midiIn(midiIn), m_midiOut(midiOut),
|
||||
m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver)) {}
|
||||
|
||||
~MIDIInOut()
|
||||
{
|
||||
pthread_cancel(m_midiThread.native_handle());
|
||||
if (m_midiThread.joinable())
|
||||
m_midiThread.join();
|
||||
snd_rawmidi_close(m_midiIn);
|
||||
snd_rawmidi_close(m_midiOut);
|
||||
}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca(&info);
|
||||
snd_rawmidi_info(m_midiIn, info);
|
||||
std::string ret = snd_rawmidi_info_get_name(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t send(const void* buf, size_t len) const
|
||||
{
|
||||
return size_t(std::max(0l, snd_rawmidi_write(m_midiOut, buf, len)));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midi;
|
||||
status = snd_rawmidi_open(&midi, nullptr, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midi;
|
||||
status = snd_rawmidi_open(nullptr, &midi, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIOut>(midi, true);
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)
|
||||
{
|
||||
int status;
|
||||
snd_rawmidi_t* midiIn;
|
||||
snd_rawmidi_t* midiOut;
|
||||
status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)
|
||||
{
|
||||
snd_rawmidi_t* midi;
|
||||
int status = snd_rawmidi_open(&midi, nullptr, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)
|
||||
{
|
||||
snd_rawmidi_t* midi;
|
||||
int status = snd_rawmidi_open(nullptr, &midi, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIOut>(midi, true);
|
||||
}
|
||||
|
||||
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)
|
||||
{
|
||||
snd_rawmidi_t* midiIn;
|
||||
snd_rawmidi_t* midiOut;
|
||||
int status = snd_rawmidi_open(&midiIn, &midiOut, name, 0);
|
||||
if (status)
|
||||
return {};
|
||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||
}
|
||||
|
||||
bool useMIDILock() const {return true;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BOO_LINUXMIDI_HPP
|
|
@ -0,0 +1,352 @@
|
|||
#include "AudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include "boo/boo.hpp"
|
||||
#include "LinuxMidi.hpp"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
static logvisor::Module Log("boo::PulseAudio");
|
||||
logvisor::Module ALSALog("boo::ALSA");
|
||||
|
||||
static const uint64_t StereoChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT);
|
||||
|
||||
static const uint64_t QuadChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_RIGHT);
|
||||
|
||||
static const uint64_t S51Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_RIGHT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_CENTER) |
|
||||
(1 << PA_CHANNEL_POSITION_LFE);
|
||||
|
||||
static const uint64_t S71Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_REAR_RIGHT) |
|
||||
(1 << PA_CHANNEL_POSITION_FRONT_CENTER) |
|
||||
(1 << PA_CHANNEL_POSITION_LFE) |
|
||||
(1 << PA_CHANNEL_POSITION_SIDE_LEFT) |
|
||||
(1 << PA_CHANNEL_POSITION_SIDE_RIGHT);
|
||||
|
||||
struct PulseAudioVoiceEngine : LinuxMidi
|
||||
{
|
||||
pa_mainloop* m_mainloop = nullptr;
|
||||
pa_context* m_ctx = nullptr;
|
||||
pa_stream* m_stream = nullptr;
|
||||
std::string m_sinkName;
|
||||
pa_sample_spec m_sampleSpec = {};
|
||||
pa_channel_map m_chanMap = {};
|
||||
|
||||
int _paWaitReady()
|
||||
{
|
||||
int retval = 0;
|
||||
while (pa_context_get_state(m_ctx) < PA_CONTEXT_READY)
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int _paStreamWaitReady()
|
||||
{
|
||||
int retval = 0;
|
||||
while (pa_stream_get_state(m_stream) < PA_STREAM_READY)
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int _paIterate(pa_operation* op)
|
||||
{
|
||||
int retval = 0;
|
||||
while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
PulseAudioVoiceEngine()
|
||||
{
|
||||
if (!(m_mainloop = pa_mainloop_new()))
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to pa_mainloop_new()");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_mainloop_api* mlApi = pa_mainloop_get_api(m_mainloop);
|
||||
pa_proplist* propList = pa_proplist_new();
|
||||
pa_proplist_sets(propList, PA_PROP_APPLICATION_ICON_NAME, APP->getUniqueName().data());
|
||||
char pidStr[16];
|
||||
snprintf(pidStr, 16, "%d", int(getpid()));
|
||||
pa_proplist_sets(propList, PA_PROP_APPLICATION_PROCESS_ID, pidStr);
|
||||
if (!(m_ctx = pa_context_new_with_proplist(mlApi, APP->getFriendlyName().data(), propList)))
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to pa_context_new_with_proplist()");
|
||||
pa_mainloop_free(m_mainloop);
|
||||
m_mainloop = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
pa_operation* op;
|
||||
size_t periodSz;
|
||||
|
||||
if (pa_context_connect(m_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr))
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to pa_context_connect()");
|
||||
goto err;
|
||||
}
|
||||
|
||||
_paWaitReady();
|
||||
|
||||
op = pa_context_get_server_info(m_ctx, pa_server_info_cb_t(_getServerInfoReply), this);
|
||||
_paIterate(op);
|
||||
pa_operation_unref(op);
|
||||
|
||||
m_sampleSpec.format = PA_SAMPLE_INVALID;
|
||||
op = pa_context_get_sink_info_by_name(m_ctx, m_sinkName.c_str(), pa_sink_info_cb_t(_getSinkInfoReply), this);
|
||||
_paIterate(op);
|
||||
pa_operation_unref(op);
|
||||
|
||||
if (m_sampleSpec.format == PA_SAMPLE_INVALID)
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to setup audio stream");
|
||||
goto err;
|
||||
}
|
||||
|
||||
m_5msFrames = m_sampleSpec.rate * 5 / 1000;
|
||||
periodSz = m_5msFrames * 4;
|
||||
|
||||
m_mixInfo.m_sampleRate = m_sampleSpec.rate;
|
||||
m_mixInfo.m_sampleFormat = SOXR_FLOAT32;
|
||||
m_mixInfo.m_bitsPerSample = 32;
|
||||
m_mixInfo.m_periodFrames = periodSz;
|
||||
if (!(m_stream = pa_stream_new(m_ctx, "master", &m_sampleSpec, &m_chanMap)))
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to pa_stream_new(): %s", pa_strerror(pa_context_errno(m_ctx)));
|
||||
goto err;
|
||||
}
|
||||
|
||||
pa_buffer_attr bufAttr;
|
||||
bufAttr.minreq = uint32_t(periodSz * m_sampleSpec.channels * sizeof(float));
|
||||
bufAttr.maxlength = bufAttr.minreq * 6;
|
||||
bufAttr.tlength = bufAttr.maxlength;
|
||||
bufAttr.prebuf = UINT32_MAX;
|
||||
bufAttr.fragsize = UINT32_MAX;
|
||||
|
||||
if (pa_stream_connect_playback(m_stream, m_sinkName.c_str(), &bufAttr,
|
||||
pa_stream_flags_t(PA_STREAM_START_UNMUTED | PA_STREAM_EARLY_REQUESTS),
|
||||
nullptr, nullptr))
|
||||
{
|
||||
Log.report(logvisor::Error, "Unable to pa_stream_connect_playback()");
|
||||
goto err;
|
||||
}
|
||||
|
||||
_paStreamWaitReady();
|
||||
|
||||
return;
|
||||
err:
|
||||
if (m_stream)
|
||||
{
|
||||
pa_stream_disconnect(m_stream);
|
||||
pa_stream_unref(m_stream);
|
||||
m_stream = nullptr;
|
||||
}
|
||||
pa_context_disconnect(m_ctx);
|
||||
pa_context_unref(m_ctx);
|
||||
m_ctx = nullptr;
|
||||
pa_mainloop_free(m_mainloop);
|
||||
m_mainloop = nullptr;
|
||||
}
|
||||
|
||||
~PulseAudioVoiceEngine()
|
||||
{
|
||||
if (m_stream)
|
||||
{
|
||||
pa_stream_disconnect(m_stream);
|
||||
pa_stream_unref(m_stream);
|
||||
}
|
||||
if (m_ctx)
|
||||
{
|
||||
pa_context_disconnect(m_ctx);
|
||||
pa_context_unref(m_ctx);
|
||||
}
|
||||
if (m_mainloop)
|
||||
{
|
||||
pa_mainloop_free(m_mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
static void _getServerInfoReply(pa_context* c, const pa_server_info* i, PulseAudioVoiceEngine* userdata)
|
||||
{
|
||||
userdata->m_sinkName = i->default_sink_name;
|
||||
}
|
||||
|
||||
void _parseAudioChannelSet(const pa_channel_map* chm)
|
||||
{
|
||||
m_chanMap = *chm;
|
||||
|
||||
ChannelMap& chmapOut = m_mixInfo.m_channelMap;
|
||||
m_mixInfo.m_channels = AudioChannelSet::Unknown;
|
||||
|
||||
static const std::array<AudioChannelSet, 4> testSets =
|
||||
{{AudioChannelSet::Surround71, AudioChannelSet::Surround51,
|
||||
AudioChannelSet::Quad, AudioChannelSet::Stereo}};
|
||||
for (AudioChannelSet set : testSets)
|
||||
{
|
||||
uint64_t chBits = 0;
|
||||
chmapOut.m_channelCount = chm->channels;
|
||||
for (unsigned c=0 ; c<chm->channels ; ++c)
|
||||
{
|
||||
chBits |= 1 << chm->map[c];
|
||||
switch (chm->map[c])
|
||||
{
|
||||
case PA_CHANNEL_POSITION_FRONT_LEFT:
|
||||
chmapOut.m_channels[c] = AudioChannel::FrontLeft;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_FRONT_RIGHT:
|
||||
chmapOut.m_channels[c] = AudioChannel::FrontRight;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_REAR_LEFT:
|
||||
chmapOut.m_channels[c] = AudioChannel::RearLeft;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_REAR_RIGHT:
|
||||
chmapOut.m_channels[c] = AudioChannel::RearRight;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_FRONT_CENTER:
|
||||
chmapOut.m_channels[c] = AudioChannel::FrontCenter;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_LFE:
|
||||
chmapOut.m_channels[c] = AudioChannel::LFE;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_SIDE_LEFT:
|
||||
chmapOut.m_channels[c] = AudioChannel::SideLeft;
|
||||
break;
|
||||
case PA_CHANNEL_POSITION_SIDE_RIGHT:
|
||||
chmapOut.m_channels[c] = AudioChannel::SideRight;
|
||||
break;
|
||||
default:
|
||||
chmapOut.m_channels[c] = AudioChannel::Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (set)
|
||||
{
|
||||
case AudioChannelSet::Stereo:
|
||||
{
|
||||
if ((chBits & StereoChans) == StereoChans)
|
||||
{
|
||||
m_mixInfo.m_channels = AudioChannelSet::Stereo;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AudioChannelSet::Quad:
|
||||
{
|
||||
if ((chBits & QuadChans) == QuadChans)
|
||||
{
|
||||
m_mixInfo.m_channels = AudioChannelSet::Quad;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AudioChannelSet::Surround51:
|
||||
{
|
||||
if ((chBits & S51Chans) == S51Chans)
|
||||
{
|
||||
m_mixInfo.m_channels = AudioChannelSet::Surround51;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AudioChannelSet::Surround71:
|
||||
{
|
||||
if ((chBits & S71Chans) == S71Chans)
|
||||
{
|
||||
m_mixInfo.m_channels = AudioChannelSet::Surround71;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _getSinkInfoReply(pa_context *c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata)
|
||||
{
|
||||
if (!i)
|
||||
return;
|
||||
userdata->m_sampleSpec.format = PA_SAMPLE_FLOAT32;
|
||||
userdata->m_sampleSpec.rate = i->sample_spec.rate;
|
||||
userdata->m_sampleSpec.channels = i->sample_spec.channels;
|
||||
userdata->_parseAudioChannelSet(&i->channel_map);
|
||||
}
|
||||
|
||||
std::vector<float> m_finalFlt;
|
||||
|
||||
void pumpAndMixVoices()
|
||||
{
|
||||
if (!m_stream)
|
||||
{
|
||||
/* Dummy pump mode - use failsafe defaults for 1/60sec of samples */
|
||||
m_mixInfo.m_sampleRate = 32000.0;
|
||||
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
||||
m_mixInfo.m_bitsPerSample = 32;
|
||||
m_5msFrames = 32000 / 60;
|
||||
m_mixInfo.m_periodFrames = m_5msFrames;
|
||||
m_mixInfo.m_channels = AudioChannelSet::Stereo;
|
||||
m_mixInfo.m_channelMap.m_channelCount = 2;
|
||||
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
|
||||
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
|
||||
if (m_finalFlt.size() < m_5msFrames * 2)
|
||||
m_finalFlt.resize(m_5msFrames * 2);
|
||||
_pumpAndMixVoices(m_5msFrames, m_finalFlt.data());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t writableSz = pa_stream_writable_size(m_stream);
|
||||
size_t frameSz = m_mixInfo.m_channelMap.m_channelCount * sizeof(float);
|
||||
size_t writableFrames = writableSz / frameSz;
|
||||
size_t writablePeriods = writableFrames / m_mixInfo.m_periodFrames;
|
||||
|
||||
int retval;
|
||||
if (!writablePeriods)
|
||||
{
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
return;
|
||||
}
|
||||
|
||||
void* data;
|
||||
size_t periodSz = m_mixInfo.m_periodFrames * frameSz;
|
||||
size_t nbytes = writablePeriods * periodSz;
|
||||
if (pa_stream_begin_write(m_stream, &data, &nbytes))
|
||||
{
|
||||
pa_stream_state_t st = pa_stream_get_state(m_stream);
|
||||
Log.report(logvisor::Error, "Unable to pa_stream_begin_write(): %s %d",
|
||||
pa_strerror(pa_context_errno(m_ctx)), st);
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
return;
|
||||
}
|
||||
|
||||
writablePeriods = nbytes / periodSz;
|
||||
size_t periodSamples = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||
for (int p=0 ; p<writablePeriods ; ++p)
|
||||
_pumpAndMixVoices(m_mixInfo.m_periodFrames, reinterpret_cast<float*>(data) + p * periodSamples);
|
||||
|
||||
if (pa_stream_write(m_stream, data, nbytes, nullptr, 0, PA_SEEK_RELATIVE))
|
||||
Log.report(logvisor::Error, "Unable to pa_stream_write()");
|
||||
|
||||
pa_mainloop_iterate(m_mainloop, 1, &retval);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine()
|
||||
{
|
||||
return std::make_unique<PulseAudioVoiceEngine>();
|
||||
}
|
||||
|
||||
}
|
|
@ -28,7 +28,6 @@ class HIDListenerUdev final : public IHIDListener
|
|||
|
||||
udev_monitor* m_udevMon;
|
||||
std::thread m_udevThread;
|
||||
bool m_udevRunning;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
static void deviceConnected(HIDListenerUdev* listener,
|
||||
|
@ -152,36 +151,23 @@ class HIDListenerUdev final : public IHIDListener
|
|||
listener->m_finder._removeToken(devPath);
|
||||
}
|
||||
|
||||
/* Empty handler for SIGTERM */
|
||||
static void _sigterm(int) {}
|
||||
|
||||
static void _udevProc(HIDListenerUdev* listener)
|
||||
{
|
||||
/* SIGTERM will be used to terminate thread */
|
||||
struct sigaction s;
|
||||
s.sa_handler = _sigterm;
|
||||
sigemptyset(&s.sa_mask);
|
||||
s.sa_flags = 0;
|
||||
sigaction(SIGTERM, &s, nullptr);
|
||||
|
||||
sigset_t waitmask, origmask;
|
||||
sigemptyset(&waitmask);
|
||||
sigaddset(&waitmask, SIGTERM);
|
||||
pthread_sigmask(SIG_BLOCK, &waitmask, &origmask);
|
||||
|
||||
udev_monitor_enable_receiving(listener->m_udevMon);
|
||||
int fd = udev_monitor_get_fd(listener->m_udevMon);
|
||||
while (listener->m_udevRunning)
|
||||
while (true)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
if (pselect(fd+1, &fds, nullptr, nullptr, nullptr, &origmask) < 0)
|
||||
if (pselect(fd+1, &fds, nullptr, nullptr, nullptr, nullptr) < 0)
|
||||
{
|
||||
/* SIGTERM handled here */
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
}
|
||||
int oldtype;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
|
||||
udev_device* dev = udev_monitor_receive_device(listener->m_udevMon);
|
||||
if (dev)
|
||||
{
|
||||
|
@ -192,6 +178,8 @@ class HIDListenerUdev final : public IHIDListener
|
|||
deviceDisconnected(listener, dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
|
||||
pthread_testcancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,14 +205,13 @@ public:
|
|||
m_scanningEnabled = false;
|
||||
|
||||
/* Start hotplug thread */
|
||||
m_udevRunning = true;
|
||||
m_udevThread = std::thread(_udevProc, this);
|
||||
}
|
||||
|
||||
~HIDListenerUdev()
|
||||
{
|
||||
m_udevRunning = false;
|
||||
pthread_kill(m_udevThread.native_handle(), SIGTERM);
|
||||
pthread_cancel(m_udevThread.native_handle());
|
||||
if (m_udevThread.joinable())
|
||||
m_udevThread.join();
|
||||
udev_monitor_unref(m_udevMon);
|
||||
}
|
||||
|
|
|
@ -481,8 +481,9 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void _incomingEvent(void* ev)
|
||||
bool _incomingEvent(void* ev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnKeyDown(CoreWindow^ window, KeyEventArgs^ keyEventArgs)
|
||||
|
|
|
@ -1358,7 +1358,7 @@ public:
|
|||
}
|
||||
|
||||
bool mouseTracking = false;
|
||||
void _incomingEvent(void* ev)
|
||||
bool _incomingEvent(void* ev)
|
||||
{
|
||||
HWNDEvent& e = *static_cast<HWNDEvent*>(ev);
|
||||
switch (e.uMsg)
|
||||
|
@ -1366,28 +1366,28 @@ public:
|
|||
case WM_CLOSE:
|
||||
if (m_callback)
|
||||
m_callback->destroyed();
|
||||
return;
|
||||
return true;
|
||||
case WM_SIZE:
|
||||
{
|
||||
SWindowRect rect;
|
||||
getWindowFrame(rect.location[0], rect.location[1], rect.size[0], rect.size[1]);
|
||||
if (!rect.size[0] || !rect.size[1])
|
||||
return;
|
||||
return false;
|
||||
m_gfxCtx->resized(rect);
|
||||
if (m_callback)
|
||||
m_callback->resized(rect, m_openGL);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MOVING:
|
||||
{
|
||||
SWindowRect rect;
|
||||
getWindowFrame(rect.location[0], rect.location[1], rect.size[0], rect.size[1]);
|
||||
if (!rect.size[0] || !rect.size[1])
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (m_callback)
|
||||
m_callback->windowMoved(rect);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
|
@ -1405,7 +1405,7 @@ public:
|
|||
else if (modifierKey != EModifierKey::None)
|
||||
m_callback->modKeyDown(modifierKey, (HIWORD(e.lParam) & KF_REPEAT) != 0);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
|
@ -1423,37 +1423,37 @@ public:
|
|||
else if (modifierKey != EModifierKey::None)
|
||||
m_callback->modKeyUp(modifierKey);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_LBUTTONDOWN:
|
||||
{
|
||||
buttonDown(e, EMouseButton::Primary);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_LBUTTONUP:
|
||||
{
|
||||
buttonUp(e, EMouseButton::Primary);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_RBUTTONDOWN:
|
||||
{
|
||||
buttonDown(e, EMouseButton::Secondary);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
buttonUp(e, EMouseButton::Secondary);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MBUTTONDOWN:
|
||||
{
|
||||
buttonDown(e, EMouseButton::Middle);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MBUTTONUP:
|
||||
{
|
||||
buttonUp(e, EMouseButton::Middle);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_XBUTTONDOWN:
|
||||
{
|
||||
|
@ -1461,7 +1461,7 @@ public:
|
|||
buttonDown(e, EMouseButton::Aux1);
|
||||
else if (HIWORD(e.wParam) == XBUTTON2)
|
||||
buttonDown(e, EMouseButton::Aux2);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_XBUTTONUP:
|
||||
{
|
||||
|
@ -1469,7 +1469,7 @@ public:
|
|||
buttonUp(e, EMouseButton::Aux1);
|
||||
else if (HIWORD(e.wParam) == XBUTTON2)
|
||||
buttonUp(e, EMouseButton::Aux2);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
|
@ -1493,7 +1493,7 @@ public:
|
|||
m_callback->mouseMove(coord);
|
||||
}
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MOUSELEAVE:
|
||||
case WM_NCMOUSELEAVE:
|
||||
|
@ -1511,7 +1511,7 @@ public:
|
|||
m_callback->mouseLeave(coord);
|
||||
mouseTracking = false;
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_NCMOUSEHOVER:
|
||||
case WM_MOUSEHOVER:
|
||||
|
@ -1528,7 +1528,7 @@ public:
|
|||
};
|
||||
m_callback->mouseEnter(coord);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
|
@ -1546,7 +1546,7 @@ public:
|
|||
scroll.delta[1] = GET_WHEEL_DELTA_WPARAM(e.wParam) / double(WHEEL_DELTA);
|
||||
m_callback->scroll(coord, scroll);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_MOUSEHWHEEL:
|
||||
{
|
||||
|
@ -1564,7 +1564,7 @@ public:
|
|||
scroll.delta[0] = GET_WHEEL_DELTA_WPARAM(e.wParam) / double(-WHEEL_DELTA);
|
||||
m_callback->scroll(coord, scroll);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case WM_CHAR:
|
||||
case WM_UNICHAR:
|
||||
|
@ -1577,10 +1577,12 @@ public:
|
|||
if (inputCb && len)
|
||||
inputCb->insertText(std::string((char*)utf8ch, len));
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ETouchType getTouchType() const
|
||||
|
|
|
@ -473,6 +473,8 @@ public:
|
|||
|
||||
if (FD_ISSET(m_x11Fd, &fds))
|
||||
{
|
||||
bool needsQuit = false;
|
||||
|
||||
XLockDisplay(m_xDisp);
|
||||
while (XPending(m_xDisp))
|
||||
{
|
||||
|
@ -486,10 +488,17 @@ public:
|
|||
auto window = m_windows.find(evWindow);
|
||||
if (window != m_windows.end())
|
||||
if (std::shared_ptr<IWindow> w = window->second.lock())
|
||||
w->_incomingEvent(&event);
|
||||
if (w->_incomingEvent(&event) && m_windows.size() == 1)
|
||||
{
|
||||
needsQuit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
XUnlockDisplay(m_xDisp);
|
||||
|
||||
if (needsQuit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(m_dbusFd, &fds))
|
||||
|
|
|
@ -360,7 +360,7 @@ public:
|
|||
glXGetFBConfigAttrib(display, config, GLX_DOUBLEBUFFER, &doubleBuffer);
|
||||
|
||||
/* Double-buffer only */
|
||||
if (!doubleBuffer)
|
||||
if (!doubleBuffer || !visualId)
|
||||
continue;
|
||||
|
||||
if (m_pf == EPixelFormat::RGBA8 && colorSize >= 32)
|
||||
|
@ -1641,7 +1641,7 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
void _incomingEvent(void* e)
|
||||
bool _incomingEvent(void* e)
|
||||
{
|
||||
XEvent* event = (XEvent*)e;
|
||||
switch (event->type)
|
||||
|
@ -1649,7 +1649,7 @@ public:
|
|||
case SelectionRequest:
|
||||
{
|
||||
handleSelectionRequest(&event->xselectionrequest);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case ClientMessage:
|
||||
{
|
||||
|
@ -1658,7 +1658,7 @@ public:
|
|||
m_callback->destroyed();
|
||||
m_callback = nullptr;
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
case Expose:
|
||||
{
|
||||
|
@ -1684,7 +1684,7 @@ public:
|
|||
m_callback->resized(m_wrect, m_openGL);
|
||||
XLockDisplay(m_xDisp);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case ConfigureNotify:
|
||||
{
|
||||
|
@ -1700,7 +1700,7 @@ public:
|
|||
|
||||
if (m_callback)
|
||||
m_callback->windowMoved(m_wrect);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case KeyPress:
|
||||
{
|
||||
|
@ -1718,7 +1718,7 @@ public:
|
|||
{
|
||||
if (inputCb)
|
||||
inputCb->insertText(utf8Frag);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
char charCode = translateKeysym(&event->xkey, specialKey, modifierKey);
|
||||
|
@ -1749,7 +1749,7 @@ public:
|
|||
m_modKeys.insert((unsigned long)modifierKey);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case KeyRelease:
|
||||
{
|
||||
|
@ -1777,7 +1777,7 @@ public:
|
|||
m_callback->modKeyUp(modifierKey);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case ButtonPress:
|
||||
{
|
||||
|
@ -1812,7 +1812,7 @@ public:
|
|||
m_callback->scroll(MakeButtonEventCoord(event), scrollDelta);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case ButtonRelease:
|
||||
{
|
||||
|
@ -1827,19 +1827,19 @@ public:
|
|||
(EModifierKey)modifierMask);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case FocusIn:
|
||||
{
|
||||
if (m_callback)
|
||||
m_callback->focusGained();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case FocusOut:
|
||||
{
|
||||
if (m_callback)
|
||||
m_callback->focusLost();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case MotionNotify:
|
||||
{
|
||||
|
@ -1848,7 +1848,7 @@ public:
|
|||
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
||||
m_callback->mouseMove(MakeMotionEventCoord(event));
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case EnterNotify:
|
||||
{
|
||||
|
@ -1857,7 +1857,7 @@ public:
|
|||
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
||||
m_callback->mouseEnter(MakeCrossingEventCoord(event));
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case LeaveNotify:
|
||||
{
|
||||
|
@ -1866,7 +1866,7 @@ public:
|
|||
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
||||
m_callback->mouseLeave(MakeCrossingEventCoord(event));
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case GenericEvent:
|
||||
{
|
||||
|
@ -1925,7 +1925,7 @@ public:
|
|||
};
|
||||
m_callback->scroll(coord, scrollDelta);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case XI_TouchBegin:
|
||||
{
|
||||
|
@ -1951,7 +1951,7 @@ public:
|
|||
|
||||
if (m_callback)
|
||||
m_callback->touchDown(coord, ev->detail);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case XI_TouchUpdate:
|
||||
{
|
||||
|
@ -1977,7 +1977,7 @@ public:
|
|||
|
||||
if (m_callback)
|
||||
m_callback->touchMove(coord, ev->detail);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
case XI_TouchEnd:
|
||||
{
|
||||
|
@ -2003,12 +2003,14 @@ public:
|
|||
|
||||
if (m_callback)
|
||||
m_callback->touchUp(coord, ev->detail);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ETouchType getTouchType() const
|
||||
|
|
Loading…
Reference in New Issue