1
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
# Tom Game
|
||||
|
||||
`src/Apps/Game` 是 Tom 游戏应用目录。仓库根目录下的 `src/Core` 继续作为渲染、数学、光栅化和平台显示层;Game 目录只放游戏入口、游戏逻辑、素材和素材转换工具。
|
||||
|
||||
## 当前入口
|
||||
|
||||
主构建目标 `IMX6U-Game` 现在从下面的文件启动:
|
||||
|
||||
```text
|
||||
src/Apps/Game/Main.cpp
|
||||
```
|
||||
|
||||
运行后默认启动 Tom,不再需要 `--tom` 参数。可选参数只保留帧率:
|
||||
|
||||
```bash
|
||||
IMX6U-Game.exe --fps 30
|
||||
IMX6U-Game.exe --fps=60
|
||||
```
|
||||
|
||||
## 素材目录
|
||||
|
||||
```text
|
||||
src/Apps/Game/assets/raw/ PNG 源素材
|
||||
src/Apps/Game/assets/sprites/ 转换后的 .sprite 运行时素材
|
||||
src/Apps/Game/tools/ 素材转换等开发工具
|
||||
src/Apps/Game/src/ 旧版 TomGameApp、音频、动画等游戏模块
|
||||
src/Apps/Game/tests/ 手工测试或后续测试代码
|
||||
```
|
||||
|
||||
板端运行时不直接解码 PNG。开发时把 PNG 放在 `src/Apps/Game/assets/raw/`,再转换为 `src/Apps/Game/assets/sprites/*.sprite`。
|
||||
|
||||
## 素材转换
|
||||
|
||||
`.sprite` 是项目自定义的 RGBA8888 二进制格式:
|
||||
|
||||
```text
|
||||
offset size content
|
||||
0 4 magic: "SPRT"
|
||||
4 4 version: 1, little-endian uint32
|
||||
8 4 width, little-endian uint32
|
||||
12 4 height, little-endian uint32
|
||||
16 4 format: 1 means RGBA8888
|
||||
20 * width * height pixels, little-endian uint32, 0xRRGGBBAA
|
||||
```
|
||||
|
||||
常用命令:
|
||||
|
||||
```bash
|
||||
cmake --build build-win --config Release --target SpriteAssetTool
|
||||
cmake --build build-win --config Release --target ConvertTomSprites
|
||||
```
|
||||
|
||||
单张转换示例:
|
||||
|
||||
```bash
|
||||
./build-win/Release/SpriteAssetTool.exe src/Apps/Game/assets/raw/ui-record.png src/Apps/Game/assets/sprites/ui-record.sprite
|
||||
./build-win/Release/SpriteAssetTool.exe src/Apps/Game/assets/raw/Tom-stand.png src/Apps/Game/assets/sprites/Tom-stand.sprite --fit 560 360
|
||||
```
|
||||
|
||||
## 运行
|
||||
|
||||
Windows PC:
|
||||
|
||||
```bash
|
||||
./build-win/Release/IMX6U-Game.exe
|
||||
```
|
||||
|
||||
Linux framebuffer / IMX6U:
|
||||
|
||||
```bash
|
||||
./IMX6U-Game --fps 30
|
||||
```
|
||||
|
||||
部署到板端时,把可执行文件和 `src/Apps/Game/assets/sprites/` 一起拷贝,并保持相对路径;也可以把 `.sprite` 放到 `/usr/local/share/imx6u-game/sprites/`。
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "TomGameApp.h"
|
||||
#include "../audio/VoiceEffect.h"
|
||||
#include "../hardware/AudioInput.h"
|
||||
#include "../hardware/AudioOutput.h"
|
||||
#include "../hardware/ButtonInput.h"
|
||||
#include "AudioInput.h"
|
||||
#include "AudioOutput.h"
|
||||
#include "ButtonInput.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "SpriteRasterizer.h"
|
||||
#include "Image.h"
|
||||
@@ -12,9 +12,9 @@ namespace Game
|
||||
TomGameApp::TomGameApp(
|
||||
Core::FrameBuffer* frameBuffer,
|
||||
Rasterizer::SpriteRasterizer* spriteRasterizer,
|
||||
AudioInput* audioInput,
|
||||
AudioOutput* audioOutput,
|
||||
ButtonInput* buttonInput) :
|
||||
Platform::IAudioInput* audioInput,
|
||||
Platform::IAudioOutput* audioOutput,
|
||||
Platform::IButtonInput* buttonInput) :
|
||||
frameBuffer(frameBuffer),
|
||||
spriteRasterizer(spriteRasterizer),
|
||||
audioInput(audioInput),
|
||||
|
||||
@@ -21,12 +21,15 @@ namespace RenderData
|
||||
class Image;
|
||||
}
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
class IAudioInput;
|
||||
class IAudioOutput;
|
||||
class IButtonInput;
|
||||
}
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class AudioInput;
|
||||
class AudioOutput;
|
||||
class ButtonInput;
|
||||
|
||||
enum class TomGameState
|
||||
{
|
||||
Idle,
|
||||
@@ -63,9 +66,9 @@ namespace Game
|
||||
private:
|
||||
Core::FrameBuffer* frameBuffer;
|
||||
Rasterizer::SpriteRasterizer* spriteRasterizer;
|
||||
AudioInput* audioInput;
|
||||
AudioOutput* audioOutput;
|
||||
ButtonInput* buttonInput;
|
||||
Platform::IAudioInput* audioInput;
|
||||
Platform::IAudioOutput* audioOutput;
|
||||
Platform::IButtonInput* buttonInput;
|
||||
|
||||
TomGameAssets assets;
|
||||
TomGameState state;
|
||||
@@ -99,9 +102,9 @@ namespace Game
|
||||
TomGameApp(
|
||||
Core::FrameBuffer* frameBuffer,
|
||||
Rasterizer::SpriteRasterizer* spriteRasterizer,
|
||||
AudioInput* audioInput,
|
||||
AudioOutput* audioOutput,
|
||||
ButtonInput* buttonInput = nullptr);
|
||||
Platform::IAudioInput* audioInput,
|
||||
Platform::IAudioOutput* audioOutput,
|
||||
Platform::IButtonInput* buttonInput = nullptr);
|
||||
|
||||
void set_assets(const TomGameAssets& assets);
|
||||
void configure_audio(uint32_t sampleRate = 16000, uint32_t channels = 1);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "VoicePlayer.h"
|
||||
#include "../hardware/AudioOutput.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Game
|
||||
@@ -59,7 +58,7 @@ namespace Game
|
||||
finished = false;
|
||||
}
|
||||
|
||||
int VoicePlayer::update(AudioOutput& audioOutput, int maxSamplesPerUpdate)
|
||||
int VoicePlayer::update(Platform::IAudioOutput& audioOutput, int maxSamplesPerUpdate)
|
||||
{
|
||||
if (!playing || samples.empty())
|
||||
{
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "AudioOutput.h"
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class AudioOutput;
|
||||
|
||||
class VoicePlayer
|
||||
{
|
||||
private:
|
||||
@@ -27,7 +26,7 @@ namespace Game
|
||||
void stop();
|
||||
void reset();
|
||||
void clear();
|
||||
int update(AudioOutput& audioOutput, int maxSamplesPerUpdate = 1024);
|
||||
int update(Platform::IAudioOutput& audioOutput, int maxSamplesPerUpdate = 1024);
|
||||
|
||||
const std::vector<int16_t>& get_samples() const { return samples; }
|
||||
size_t get_sample_count() const { return samples.size(); }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "VoiceRecorder.h"
|
||||
#include "../hardware/AudioInput.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
@@ -78,7 +77,7 @@ namespace Game
|
||||
finishedBySilence = false;
|
||||
}
|
||||
|
||||
int VoiceRecorder::update(AudioInput& audioInput, int sampleCount)
|
||||
int VoiceRecorder::update(Platform::IAudioInput& audioInput, int sampleCount)
|
||||
{
|
||||
if (!recording || sampleCount <= 0)
|
||||
{
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "AudioInput.h"
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class AudioInput;
|
||||
|
||||
class VoiceRecorder
|
||||
{
|
||||
private:
|
||||
@@ -42,7 +41,7 @@ namespace Game
|
||||
void start();
|
||||
void stop();
|
||||
void clear();
|
||||
int update(AudioInput& audioInput, int sampleCount = 512);
|
||||
int update(Platform::IAudioInput& audioInput, int sampleCount = 512);
|
||||
|
||||
const std::vector<int16_t>& get_samples() const { return samples; }
|
||||
size_t get_sample_count() const { return samples.size(); }
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
#include "AudioInput.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
namespace Game
|
||||
{
|
||||
AudioInput::AudioInput() :
|
||||
deviceName("default"),
|
||||
sampleRate(16000),
|
||||
channels(1),
|
||||
opened(false)
|
||||
#if defined(__linux__)
|
||||
,
|
||||
handle(nullptr)
|
||||
#endif
|
||||
{}
|
||||
|
||||
AudioInput::~AudioInput()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool AudioInput::init(const std::string& deviceName, uint32_t sampleRate, uint32_t channels)
|
||||
{
|
||||
shutdown();
|
||||
|
||||
if (sampleRate == 0 || channels == 0)
|
||||
{
|
||||
std::cerr << "AudioInput invalid params." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->deviceName = deviceName;
|
||||
this->sampleRate = sampleRate;
|
||||
this->channels = channels;
|
||||
|
||||
#if defined(__linux__)
|
||||
int result = snd_pcm_open(&handle, deviceName.c_str(), SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioInput open failed: " << snd_strerror(result) << std::endl;
|
||||
handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = snd_pcm_set_params(
|
||||
handle,
|
||||
SND_PCM_FORMAT_S16_LE,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
channels,
|
||||
sampleRate,
|
||||
1,
|
||||
50000);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioInput set params failed: " << snd_strerror(result) << std::endl;
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
return true;
|
||||
#else
|
||||
std::cerr << "AudioInput is only implemented on Linux ALSA." << std::endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int AudioInput::read_samples(int16_t* buffer, int sampleCount)
|
||||
{
|
||||
if (buffer == nullptr || sampleCount <= 0 || !opened)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
const snd_pcm_uframes_t frames = static_cast<snd_pcm_uframes_t>(sampleCount / channels);
|
||||
int result = snd_pcm_readi(handle, buffer, frames);
|
||||
if (result == -EPIPE)
|
||||
{
|
||||
snd_pcm_prepare(handle);
|
||||
return 0;
|
||||
}
|
||||
if (result < 0)
|
||||
{
|
||||
result = snd_pcm_recover(handle, result, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioInput read failed: " << snd_strerror(result) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result * static_cast<int>(channels);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float AudioInput::read_volume(int sampleCount)
|
||||
{
|
||||
if (sampleCount <= 0)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::vector<int16_t> samples(static_cast<size_t>(sampleCount), 0);
|
||||
const int count = read_samples(samples.data(), sampleCount);
|
||||
if (count <= 0)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const double value = static_cast<double>(samples[i]) / 32768.0;
|
||||
sum += value * value;
|
||||
}
|
||||
|
||||
return static_cast<float>(std::sqrt(sum / count));
|
||||
}
|
||||
|
||||
void AudioInput::shutdown()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_pcm_close(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__)
|
||||
typedef struct _snd_pcm snd_pcm_t;
|
||||
#endif
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class AudioInput
|
||||
{
|
||||
private:
|
||||
std::string deviceName;
|
||||
uint32_t sampleRate;
|
||||
uint32_t channels;
|
||||
bool opened;
|
||||
#if defined(__linux__)
|
||||
snd_pcm_t* handle;
|
||||
#endif
|
||||
|
||||
public:
|
||||
AudioInput();
|
||||
~AudioInput();
|
||||
|
||||
bool init(
|
||||
const std::string& deviceName = "default",
|
||||
uint32_t sampleRate = 16000,
|
||||
uint32_t channels = 1);
|
||||
int read_samples(int16_t* buffer, int sampleCount);
|
||||
float read_volume(int sampleCount = 512);
|
||||
void shutdown();
|
||||
|
||||
bool is_open() const { return opened; }
|
||||
uint32_t get_sample_rate() const { return sampleRate; }
|
||||
uint32_t get_channels() const { return channels; }
|
||||
const std::string& get_device_name() const { return deviceName; }
|
||||
};
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "AudioOutput.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
struct WavHeader
|
||||
{
|
||||
char riff[4];
|
||||
uint32_t fileSize;
|
||||
char wave[4];
|
||||
char fmt[4];
|
||||
uint32_t fmtSize;
|
||||
uint16_t audioFormat;
|
||||
uint16_t channels;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t blockAlign;
|
||||
uint16_t bitsPerSample;
|
||||
char data[4];
|
||||
uint32_t dataSize;
|
||||
};
|
||||
|
||||
static bool IsSupportedWav(const WavHeader& header)
|
||||
{
|
||||
return std::memcmp(header.riff, "RIFF", 4) == 0 &&
|
||||
std::memcmp(header.wave, "WAVE", 4) == 0 &&
|
||||
std::memcmp(header.fmt, "fmt ", 4) == 0 &&
|
||||
std::memcmp(header.data, "data", 4) == 0 &&
|
||||
header.audioFormat == 1 &&
|
||||
header.bitsPerSample == 16 &&
|
||||
header.fmtSize == 16;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Game
|
||||
{
|
||||
AudioOutput::AudioOutput() :
|
||||
deviceName("default"),
|
||||
sampleRate(16000),
|
||||
channels(1),
|
||||
opened(false)
|
||||
#if defined(__linux__)
|
||||
,
|
||||
handle(nullptr)
|
||||
#endif
|
||||
{}
|
||||
|
||||
AudioOutput::~AudioOutput()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool AudioOutput::init(const std::string& deviceName, uint32_t sampleRate, uint32_t channels)
|
||||
{
|
||||
shutdown();
|
||||
|
||||
if (sampleRate == 0 || channels == 0)
|
||||
{
|
||||
std::cerr << "AudioOutput invalid params." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->deviceName = deviceName;
|
||||
this->sampleRate = sampleRate;
|
||||
this->channels = channels;
|
||||
|
||||
#if defined(__linux__)
|
||||
int result = snd_pcm_open(&handle, deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioOutput open failed: " << snd_strerror(result) << std::endl;
|
||||
handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = snd_pcm_set_params(
|
||||
handle,
|
||||
SND_PCM_FORMAT_S16_LE,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
channels,
|
||||
sampleRate,
|
||||
1,
|
||||
50000);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioOutput set params failed: " << snd_strerror(result) << std::endl;
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
return true;
|
||||
#else
|
||||
std::cerr << "AudioOutput is only implemented on Linux ALSA." << std::endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int AudioOutput::write_samples(const int16_t* samples, int sampleCount)
|
||||
{
|
||||
if (samples == nullptr || sampleCount <= 0 || !opened)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
const snd_pcm_uframes_t frames = static_cast<snd_pcm_uframes_t>(sampleCount / channels);
|
||||
int result = snd_pcm_writei(handle, samples, frames);
|
||||
if (result == -EPIPE)
|
||||
{
|
||||
snd_pcm_prepare(handle);
|
||||
return 0;
|
||||
}
|
||||
if (result < 0)
|
||||
{
|
||||
result = snd_pcm_recover(handle, result, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
std::cerr << "AudioOutput write failed: " << snd_strerror(result) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result * static_cast<int>(channels);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AudioOutput::play_wav(const std::string& path)
|
||||
{
|
||||
std::ifstream file(path.c_str(), std::ios::binary);
|
||||
if (!file.good())
|
||||
{
|
||||
std::cerr << "Open wav failed: " << path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
WavHeader header;
|
||||
file.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (!file.good() || !IsSupportedWav(header))
|
||||
{
|
||||
std::cerr << "Unsupported wav: " << path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opened || sampleRate != header.sampleRate || channels != header.channels)
|
||||
{
|
||||
if (!init(deviceName, header.sampleRate, header.channels))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int16_t> samples(header.dataSize / sizeof(int16_t), 0);
|
||||
file.read(reinterpret_cast<char*>(samples.data()), header.dataSize);
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
while (offset < samples.size())
|
||||
{
|
||||
const int count = static_cast<int>(std::min<size_t>(4096, samples.size() - offset));
|
||||
const int written = write_samples(samples.data() + offset, count);
|
||||
if (written <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += static_cast<size_t>(written);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_pcm_drain(handle);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioOutput::shutdown()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_pcm_close(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#if defined(__linux__)
|
||||
typedef struct _snd_pcm snd_pcm_t;
|
||||
#endif
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class AudioOutput
|
||||
{
|
||||
private:
|
||||
std::string deviceName;
|
||||
uint32_t sampleRate;
|
||||
uint32_t channels;
|
||||
bool opened;
|
||||
#if defined(__linux__)
|
||||
snd_pcm_t* handle;
|
||||
#endif
|
||||
|
||||
public:
|
||||
AudioOutput();
|
||||
~AudioOutput();
|
||||
|
||||
bool init(
|
||||
const std::string& deviceName = "default",
|
||||
uint32_t sampleRate = 16000,
|
||||
uint32_t channels = 1);
|
||||
int write_samples(const int16_t* samples, int sampleCount);
|
||||
bool play_wav(const std::string& path);
|
||||
void shutdown();
|
||||
|
||||
bool is_open() const { return opened; }
|
||||
uint32_t get_sample_rate() const { return sampleRate; }
|
||||
uint32_t get_channels() const { return channels; }
|
||||
const std::string& get_device_name() const { return deviceName; }
|
||||
};
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "ButtonInput.h"
|
||||
#include <iostream>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Game
|
||||
{
|
||||
ButtonInput::ButtonInput() :
|
||||
devicePath("/dev/input/event0"),
|
||||
keyCode(28),
|
||||
opened(false),
|
||||
down(false),
|
||||
pressed(false),
|
||||
released(false)
|
||||
#if defined(__linux__)
|
||||
,
|
||||
fd(-1)
|
||||
#endif
|
||||
{}
|
||||
|
||||
ButtonInput::~ButtonInput()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool ButtonInput::init(const std::string& devicePath, int keyCode)
|
||||
{
|
||||
shutdown();
|
||||
|
||||
this->devicePath = devicePath;
|
||||
this->keyCode = keyCode;
|
||||
down = false;
|
||||
pressed = false;
|
||||
released = false;
|
||||
|
||||
#if defined(__linux__)
|
||||
fd = open(devicePath.c_str(), O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
{
|
||||
std::cerr << "ButtonInput open failed: " << devicePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
return true;
|
||||
#else
|
||||
std::cerr << "ButtonInput is only implemented on Linux evdev." << std::endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ButtonInput::update()
|
||||
{
|
||||
pressed = false;
|
||||
released = false;
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
input_event event;
|
||||
while (true)
|
||||
{
|
||||
const ssize_t bytes = read(fd, &event, sizeof(event));
|
||||
if (bytes == static_cast<ssize_t>(sizeof(event)))
|
||||
{
|
||||
if (event.type != EV_KEY || event.code != keyCode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.value == 1)
|
||||
{
|
||||
if (!down)
|
||||
{
|
||||
pressed = true;
|
||||
}
|
||||
down = true;
|
||||
}
|
||||
else if (event.value == 0)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
released = true;
|
||||
}
|
||||
down = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
std::cerr << "ButtonInput read failed." << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ButtonInput::shutdown()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
#endif
|
||||
opened = false;
|
||||
down = false;
|
||||
pressed = false;
|
||||
released = false;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Game
|
||||
{
|
||||
class ButtonInput
|
||||
{
|
||||
private:
|
||||
std::string devicePath;
|
||||
int keyCode;
|
||||
bool opened;
|
||||
bool down;
|
||||
bool pressed;
|
||||
bool released;
|
||||
#if defined(__linux__)
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
public:
|
||||
ButtonInput();
|
||||
~ButtonInput();
|
||||
|
||||
bool init(const std::string& devicePath = "/dev/input/event0", int keyCode = 28);
|
||||
void update();
|
||||
void shutdown();
|
||||
|
||||
bool is_open() const { return opened; }
|
||||
bool is_down() const { return down; }
|
||||
bool was_pressed() const { return pressed; }
|
||||
bool was_released() const { return released; }
|
||||
const std::string& get_device_path() const { return devicePath; }
|
||||
int get_key_code() const { return keyCode; }
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user