Reworked app, #11 key bindings, optimisations

This commit is contained in:
Niellune
2026-03-10 00:39:00 +02:00
parent 484c4ce2d5
commit 570695fbcf
14 changed files with 570 additions and 487 deletions
+389
View File
@@ -0,0 +1,389 @@
#include "application.h"
#include <SDL2/SDL_ttf.h>
#include "struct/video_buffer.h"
#include "settings.h"
#include "interface.h"
#include "decoder.h"
#include "pcm_audio.h"
#include "pipe_listener.h"
#define EVT_STATUS_OFFSET 0
#define EVT_PHONE_OFFSET 1
static KeySetting<int> *keyMap[] = {
&Settings::keySiri,
&Settings::keyNightOn,
&Settings::keyNightOff,
&Settings::keyLeft,
&Settings::keyRight,
&Settings::keyEnter,
&Settings::keyEnterUp,
&Settings::keyBack,
&Settings::keyUp,
&Settings::keyDown,
&Settings::keyHome,
&Settings::keyPlay,
&Settings::keyPause,
&Settings::keyPlayPause,
&Settings::keyNext,
&Settings::keyPrev,
&Settings::keyAccept,
&Settings::keyReject,
&Settings::keyVideoFocus,
&Settings::keyVideoRelease,
&Settings::keyNavFocus};
static constexpr size_t keyMapSize = sizeof(keyMap) / sizeof(keyMap[0]);
Application::Application(/* args */) : _window(nullptr),
_renderer(nullptr),
_active(true)
{
std::cout << "[App] Creating" << std::endl;
if (!setAudioDriver())
throw std::runtime_error("Unsupported audio driver " + std::string(Settings::audioDriver.value));
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) != 0)
throw std::runtime_error(std::string("SDL initialisation failed > ") + SDL_GetError());
if (TTF_Init() != 0)
{
SDL_Quit();
throw std::runtime_error(std::string("TTF initialisation failed > ") + TTF_GetError());
}
if (SDL_GetCurrentDisplayMode(0, &_displayMode) != 0)
{
TTF_Quit();
SDL_Quit();
throw std::runtime_error(std::string("SDL get display mode failed > ") + SDL_GetError());
}
std::cout << "[App] SDL screen: "
<< _displayMode.w << "x" << _displayMode.h << "@" << _displayMode.refresh_rate
<< ", audio: " << SDL_GetCurrentAudioDriver() << std::endl;
}
Application::~Application()
{
std::cout << "[App] Destroying" << std::endl;
if (_renderer != nullptr)
SDL_DestroyRenderer(_renderer);
if (_window != nullptr)
SDL_DestroyWindow(_window);
TTF_Quit();
SDL_Quit();
std::cout << "[App] Finished" << std::endl;
}
void Application::start(const char *title)
{
std::cout << "[App] Initialising" << std::endl;
// Create SDL window centered on screen
if (Settings::fastScale)
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
else
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
// Prepare window, show it in headless to avoid blinking, otherwise hidden untill iniailised
bool fullsize = Settings::isFullscreen() || Settings::isHeadless();
_width = fullsize ? _displayMode.w : Settings::width;
_height = fullsize ? _displayMode.h : Settings::height;
_window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
_width,
_height,
SDL_WINDOW_RESIZABLE | (Settings::isHeadless() ? 0 : SDL_WINDOW_HIDDEN));
if (!_window)
throw std::runtime_error(std::string("SDL can't create window > ") + SDL_GetError());
if (!Settings::cursor)
SDL_ShowCursor(SDL_DISABLE);
// Create accelerated renderer for the window
Uint32 flags = SDL_RENDERER_ACCELERATED;
if (Settings::vsync)
flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, flags);
if (!_renderer)
throw std::runtime_error(std::string("SDL can't create renderer > ") + SDL_GetError());
// Register additional events
_evtBase = SDL_RegisterEvents(2);
if (_evtBase == (Uint32)-1)
throw std::runtime_error(std::string("Can't register custom events > ") + SDL_GetError());
std::cout << "[App] Starting" << std::endl;
loop();
std::cout << "[App] Stopped" << std::endl;
}
bool Application::setAudioDriver()
{
if (Settings::audioDriver.value.length() < 2)
return true;
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
{
if (SDL_GetAudioDriver(i) == Settings::audioDriver.value)
{
SDL_setenv("SDL_AUDIODRIVER", Settings::audioDriver.value.c_str(), 1);
return true;
}
}
return false;
}
int Application::processKey(SDL_Keysym key)
{
for (uint8_t i = 0; i < keyMapSize; i++)
{
if (keyMap[i]->value == key.sym)
{
return keyMap[i]->key;
}
}
std::cout << "[App] Unmapped key " << key.sym << std::endl;
return 0;
}
bool Application::processSystemEvent(const SDL_Event &e)
{
if (e.type == SDL_QUIT)
{
_active = false;
return true;
}
if (e.type == SDL_WINDOWEVENT)
{
if (e.window.event == SDL_WINDOWEVENT_RESIZED)
{
SDL_GetWindowSize(_window, &_width, &_height);
_state.dirty = true;
}
return true;
}
if (e.type == SDL_KEYDOWN)
{
switch (e.key.keysym.sym)
{
case SDLK_f:
{
if (Settings::isHeadless())
return true;
_state.fullscreen = !_state.fullscreen; // Toggle fullscreen mode
SDL_SetWindowFullscreen(_window, _state.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetWindowBordered(_window, _state.fullscreen ? SDL_FALSE : SDL_TRUE);
return true;
}
case SDLK_q:
{
_active = false;
return true;
}
}
}
if (e.type == (_evtBase + EVT_STATUS_OFFSET))
{
_state.deviceStatus = e.user.code;
return true;
}
if (e.type == (_evtBase + EVT_PHONE_OFFSET))
{
_state.connected = e.user.code != 0;
_state.frameRendered = false;
_state.dirty = true;
_state.requestFrame = -1;
_state.flushBuffers = _state.connected;
return true;
}
return false;
}
bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
{
bool result = false;
SDL_Event e;
int motionX = -1;
int motionY = -1;
int downX = -1;
int downY = -1;
int upX = -1;
int upY = -1;
while (SDL_PollEvent(&e))
{
if (processSystemEvent(e))
continue;
switch (e.type)
{
case SDL_MOUSEBUTTONDOWN:
{
_state.mouseDown = true;
downX = e.button.x;
downY = e.button.y;
break;
}
case SDL_MOUSEBUTTONUP:
{
_state.mouseDown = false;
upX = e.button.x;
upY = e.button.y;
result = true;
break;
}
case SDL_MOUSEMOTION:
{
if (!_state.mouseDown)
break;
motionX = e.motion.x;
motionY = e.motion.y;
break;
}
case SDL_KEYDOWN:
{
int key = processKey(e.key.keysym);
if (key > 0)
{
protocol.sendKey(key);
result = true;
}
break;
}
case SDL_KEYUP:
{
if (e.key.keysym.sym == Settings::keyEnter)
{
protocol.sendKey(Settings::keyEnterUp.key);
result = true;
}
break;
}
}
}
if (_state.frameRendered && (downX >= 0 || upX >= 0 || motionX >= 0))
{
if (downX >= 0)
protocol.sendClick(renderer.xScale * downX / _width, renderer.yScale * downY / _height, true);
if (motionX >= 0)
protocol.sendMove(renderer.xScale * motionX / _width, renderer.yScale * motionY / _height);
if (upX >= 0)
protocol.sendClick(renderer.xScale * upX / _width, renderer.yScale * upY / _height, false);
}
return result;
}
void Application::loop()
{
// Prepare home screen
Interface interface(_renderer);
interface.drawHome(true, PROTOCOL_STATUS_UNKNOWN);
// Process full screen, do not do this in headless to avoid blinking
if (Settings::isFullscreen())
{
_state.fullscreen = true;
SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN);
SDL_SetWindowBordered(_window, SDL_FALSE);
}
// Show window, do not do this in headless to avoid blinking
if (!Settings::isHeadless())
SDL_ShowWindow(_window);
interface.drawHome(true, PROTOCOL_STATUS_UNKNOWN);
VideoBuffer videoBuffer;
Protocol protocol(Settings::width, Settings::height, Settings::sourceFps, AV_INPUT_BUFFER_PADDING_SIZE);
Decoder decoder;
PcmAudio audioMain("Main"), audioAux("Aux");
decoder.start(&protocol.videoData, &videoBuffer, AV_CODEC_ID_H264);
audioMain.start(&protocol.audioStreamMain);
audioAux.start(&protocol.audioStreamAux, &audioMain);
protocol.start(_evtBase + EVT_STATUS_OFFSET, _evtBase + EVT_PHONE_OFFSET);
std::cout << "[App] Loop" << std::endl;
Uint32 frameStart = SDL_GetTicks();
AVFrame *frame = nullptr;
uint32_t frameid = 0;
uint32_t latestFrameid = 0;
uint32_t frameTargetTime = 1000 / Settings::fps;
int frameDelay = 0;
while (_active)
{
if (_state.connected && _state.showVideo)
{
if (videoBuffer.latest(&frame, &frameid) && frame && (frameid != latestFrameid || _state.dirty))
{
if (interface.render(frame))
{
_state.frameRendered = true;
if (!_state.dirty && (frameid != latestFrameid + 1))
std::cout << "[App] Frame drop " << frameid - latestFrameid - 1 << " on " << frameid << std::endl;
latestFrameid = frameid;
_state.dirty = false;
if (_state.requestFrame >= 0)
_state.requestFrame++;
}
videoBuffer.consume();
}
if (_state.requestFrame > 0 && Settings::forceRedraw > 0)
{
if (_state.requestFrame <= Settings::forceRedraw)
protocol.requestKeyframe();
else
_state.requestFrame = -1;
}
}
if (!_state.frameRendered || !_state.showVideo)
{
interface.drawHome(_state.dirty, _state.connected ? PROTOCOL_STATUS_CONNECTED : _state.deviceStatus);
_state.dirty = false;
SDL_Event e;
while (SDL_PollEvent(&e))
processSystemEvent(e);
}
else
{
if (processFrameEvents(protocol, interface) && Settings::forceRedraw > 0)
_state.requestFrame = 1;
}
if (_state.flushBuffers)
{
_state.flushBuffers = false;
decoder.flush();
videoBuffer.reset();
}
if (_active)
{
Uint32 frameEnd = SDL_GetTicks();
frameDelay = frameTargetTime - (frameEnd - frameStart);
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
frameStart += frameTargetTime;
}
}
if (!Settings::isHeadless())
SDL_HideWindow(_window);
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef SRC_APPLICATION
#define SRC_APPLICATION
#include <unistd.h>
#include <SDL2/SDL.h>
#include "helper/protocol_const.h"
#include "protocol.h"
#include "renderer.h"
class Application
{
public:
Application(/* args */);
~Application();
void start(const char *title);
private:
struct State
{
bool connected = false;
bool dirty = false;
bool frameRendered = false;
int requestFrame = 0;
bool showVideo = true;
bool fullscreen = false;
bool mouseDown = false;
int8_t deviceStatus = PROTOCOL_STATUS_INITIALISING;
bool flushBuffers = false;
};
bool setAudioDriver();
int processKey(SDL_Keysym key);
bool processSystemEvent(const SDL_Event &e);
bool processFrameEvents(Protocol &protocol, Renderer &renderer);
void loop();
SDL_Window *_window;
SDL_Renderer *_renderer;
bool _active;
Uint32 _evtBase;
SDL_DisplayMode _displayMode;
State _state;
int _width;
int _height;
};
#endif /* SRC_APPLICATION */
-2
View File
@@ -315,8 +315,6 @@ void Connector::printMessage(uint32_t cmd, uint32_t length, uint8_t *data, bool
void Connector::readLoop()
{
std::mutex mtx;
std::condition_variable cv;
Header header;
int transferred = 0;
uint8_t *data = nullptr;
+3 -3
View File
@@ -37,7 +37,7 @@ void Decoder::stop()
void Decoder::flush()
{
if(_context)
if (_context)
avcodec_flush_buffers(_context);
}
@@ -63,9 +63,9 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
break;
}
if(Settings::codecLowDelay)
if (Settings::codecLowDelay)
result->flags |= AV_CODEC_FLAG_LOW_DELAY;
if(Settings::codecFast)
if (Settings::codecFast)
result->flags2 |= AV_CODEC_FLAG2_FAST;
int ret = avcodec_open2(result, codec, nullptr);
+1
View File
@@ -1,6 +1,7 @@
#ifndef SRC_HELPER_PROTOCOL_CONST
#define SRC_HELPER_PROTOCOL_CONST
#define PROTOCOL_STATUS_UNKNOWN -1 // Manual > 0
#define PROTOCOL_STATUS_INITIALISING 0 // Initialised > 1
#define PROTOCOL_STATUS_NO_DEVICE 1 // Start linking > 3
#define PROTOCOL_STATUS_LINKING 2 // Linked > 4, Failed in sequence > 3
+15 -3
View File
@@ -12,6 +12,7 @@ class ISetting
{
public:
std::string name;
ISetting(std::string name_) : name(std::move(name_)) {}
virtual void parse(std::string &str) = 0;
virtual std::string asString() const = 0;
};
@@ -30,9 +31,8 @@ class Setting : public ISetting
public:
T value;
Setting(std::string name_, T default_)
: value(default_)
: ISetting(std::move(name_)), value(default_)
{
name = std::move(name_);
_settings().push_back(this);
}
@@ -57,7 +57,7 @@ public:
else if (str == "0" || str == "false")
value = false;
else
throw new std::runtime_error("Can't convert to boolean.");
throw std::runtime_error("Can't convert to boolean.");
}
else if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
@@ -90,4 +90,16 @@ public:
}
};
template <typename T>
class KeySetting : public Setting<T>
{
public:
int key;
KeySetting(std::string name_, T default_, int key_)
: Setting<T>(std::move(name_), default_), key(key_)
{
}
};
#endif /* SRC_HELPER_SETTINGS_BASE */
-1
View File
@@ -28,7 +28,6 @@ bool Interface::drawHome(bool force, int state)
_state = state;
int width, height;
SDL_GetRendererOutputSize(_renderer, &width, &height);
SDL_RenderClear(_renderer);
_mainImage.draw(_renderer, width, height);
if (state == PROTOCOL_STATUS_ERROR)
+14 -430
View File
@@ -1,444 +1,26 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string>
#include <unistd.h>
#include <iostream>
extern "C"
{
#include <libavformat/avformat.h> // FFmpeg library for multimedia container format handling
#include <libavcodec/avcodec.h> // FFmpeg library for encoding/decoding
#include <libswscale/swscale.h> // FFmpeg library for image scaling and pixel format conversion
#include <libavutil/imgutils.h> // FFmpeg utility functions for image handling
}
#include "helper/functions.h"
#include "helper/protocol_const.h"
#include "struct/video_buffer.h"
#include "protocol.h"
#include "decoder.h"
#include "pcm_audio.h"
#include "interface.h"
#include "application.h"
#include "pipe_listener.h"
#include "settings.h"
#define FRAME_DELAY_INACTIVE 200
#define MAX_EVENTS_PER_FRAME 5
static const char *title = "Fast Car Play v0.7";
static const char *title = "Fast Car Play v0.6";
static SDL_Window *window = nullptr;
static SDL_Renderer *renderer = nullptr;
Uint32 evtStatus = (Uint32)-1;
Uint32 evtConnected = (Uint32)-1;
bool active = true;
struct RunParams
{
bool connected;
bool videoPrepaired;
bool videoRendered;
bool dirty;
bool fullscreen;
bool mouseDown;
uint8_t deviceStatus;
uint32_t frameDelay;
int activeDelay;
};
void processKey(Protocol &protocol, SDL_Keysym key, RunParams &params)
{
switch (key.sym)
{
case SDLK_f:
params.fullscreen = !params.fullscreen; // Toggle fullscreen mode
SDL_SetWindowFullscreen(window, params.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetWindowBordered(window, params.fullscreen ? SDL_FALSE : SDL_TRUE);
return;
case SDLK_q:
active = false;
return;
case SDLK_r:
protocol.sendKey(BTN_SCREEN_REFRESH);
params.dirty = true;
return;
case SDLK_h:
protocol.sendKey(BTN_HOME);
return;
case SDLK_s:
protocol.sendKey(BTN_SIRI);
return;
case SDLK_m:
protocol.sendKey(BTN_MICROPHONE);
return;
case SDLK_LEFT:
protocol.sendKey(BTN_LEFT);
return;
case SDLK_RIGHT:
protocol.sendKey(BTN_RIGHT);
return;
case SDLK_DOWN:
protocol.sendKey(BTN_DOWN);
return;
case SDLK_SPACE:
protocol.sendKey(BTN_PLAY);
return;
case SDLK_p:
protocol.sendKey(BTN_PAUSE);
return;
case SDLK_MINUS:
protocol.sendKey(BTN_PREVIOUS_TRACK);
return;
case SDLK_EQUALS:
protocol.sendKey(BTN_NEXT_TRACK);
return;
case SDLK_RETURN:
protocol.sendKey(BTN_SELECT_DOWN);
protocol.sendKey(BTN_SELECT_UP);
return;
case SDLK_BACKSPACE:
protocol.sendKey(BTN_BACK);
return;
case 0:
return;
}
if (key.sym == Settings::keyLeft)
{
protocol.sendKey(BTN_LEFT);
}
else if (key.sym == Settings::keyRight)
{
protocol.sendKey(BTN_RIGHT);
}
else if (key.sym == Settings::keyEnter)
{
protocol.sendKey(BTN_SELECT_DOWN);
protocol.sendKey(BTN_SELECT_UP);
}
else if (key.sym == Settings::keyBack)
{
protocol.sendKey(BTN_BACK);
}
else if (key.sym == Settings::keyHome)
{
protocol.sendKey(BTN_HOME);
}
else
{
std::cout << "[Key] Unmapped key " << key.sym << std::endl;
}
}
bool processEvents(Protocol &protocol, RunParams &params, Renderer &renderer)
{
bool result = false;
SDL_Event e;
int motionX = -1;
int motionY = -1;
int downX = -1;
int downY = -1;
int upX = -1;
int upY = -1;
int processed = 0;
while (SDL_PollEvent(&e) && processed++ < MAX_EVENTS_PER_FRAME)
{
switch (e.type)
{
case SDL_QUIT:
active = false;
break;
case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_RESIZED)
{
params.dirty = true;
}
break;
case SDL_MOUSEBUTTONDOWN:
{
params.mouseDown = true;
downX = e.button.x;
downY = e.button.y;
break;
}
case SDL_MOUSEBUTTONUP:
{
params.mouseDown = false;
upX = e.button.x;
upY = e.button.y;
break;
}
case SDL_MOUSEMOTION:
{
if (!params.mouseDown)
break;
motionX = e.motion.x;
motionY = e.motion.y;
break;
}
case SDL_KEYDOWN:
{
processKey(protocol, e.key.keysym, params);
break;
}
default:
{
if (e.type == evtConnected)
{
params.connected = e.user.code != 0;
params.dirty = true;
params.videoRendered = false;
params.frameDelay = params.connected ? params.activeDelay : FRAME_DELAY_INACTIVE;
params.videoPrepaired = false;
result = true;
}
else if (e.type == evtStatus)
{
params.deviceStatus = e.user.code;
}
}
}
}
if (params.videoRendered && (downX >= 0 || upX >= 0 || motionX >= 0))
{
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
if (downX >= 0)
protocol.sendClick(renderer.xScale * downX / window_width, renderer.yScale * downY / window_height, true);
if (motionX >= 0)
protocol.sendMove(renderer.xScale * motionX / window_width, renderer.yScale * motionY / window_height);
if (upX >= 0)
protocol.sendClick(renderer.xScale * upX / window_width, renderer.yScale * upY / window_height, false);
}
return result;
}
void application()
{
RunParams p;
p.activeDelay = 1000 / Settings::fps;
p.connected = false;
p.deviceStatus = PROTOCOL_STATUS_INITIALISING;
p.dirty = false;
p.frameDelay = FRAME_DELAY_INACTIVE;
p.videoPrepaired = false;
p.videoRendered = false;
p.fullscreen = Settings::fullscreen;
p.mouseDown = false;
Interface interface(renderer);
if (p.fullscreen)
{
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
SDL_SetWindowBordered(window, SDL_FALSE);
}
SDL_ShowWindow(window);
interface.drawHome(true, 0);
VideoBuffer videoBuffer;
Protocol protocol(Settings::width, Settings::height, Settings::sourceFps, AV_INPUT_BUFFER_PADDING_SIZE);
Decoder decoder;
PcmAudio audioMain("Main"), audioAux("Aux");
decoder.start(&protocol.videoData, &videoBuffer, AV_CODEC_ID_H264);
audioMain.start(&protocol.audioStreamMain);
audioAux.start(&protocol.audioStreamAux, &audioMain);
protocol.start(evtStatus, evtConnected);
PipeListener pipeListener(protocol, (Settings::keyPipe.value.length() > 2) ? Settings::keyPipe.value.c_str() : nullptr);
std::cout << "[Main] Loop" << std::endl;
uint32_t latestid = 0;
Uint32 frameStart = SDL_GetTicks();
uint32_t FrameRequest = 0;
while (active)
{
if (processEvents(protocol, p, interface))
{
if (p.connected)
{
decoder.flush();
videoBuffer.reset();
}
}
if (p.connected)
{
AVFrame *frame = nullptr;
uint32_t frameid = 0;
if (videoBuffer.latest(&frame, &frameid) && (frameid != latestid || p.dirty) && frame)
{
if (interface.render(frame))
{
p.videoRendered = true;
if (!p.dirty && (frameid != latestid + 1))
std::cout << "[Main] Frame drop " << frameid - latestid - 1 << " on " << frameid << std::endl;
latestid = frameid;
p.dirty = false;
}
videoBuffer.consume();
FrameRequest = 0;
}
else
{
if (Settings::forceRedraw)
{
if (protocol.checkKey())
FrameRequest = 3;
else if (FrameRequest-- > 0)
protocol.requestKeyframe();
}
}
}
if (!p.videoRendered)
{
interface.drawHome(p.dirty, p.connected ? PROTOCOL_STATUS_CONNECTED : p.deviceStatus);
p.dirty = false;
}
if (active)
{
Uint32 frameEnd = SDL_GetTicks();
Uint32 frameTime = frameEnd - frameStart;
if (frameTime < p.frameDelay)
{
SDL_Delay(p.frameDelay - frameTime);
frameStart += p.frameDelay;
}
else
{
SDL_Delay(1);
frameStart += 1;
}
}
}
std::cout << "[Main] Stopping" << std::endl;
SDL_HideWindow(window);
}
bool setAudioDriver()
{
if (Settings::audioDriver.value.length() < 2)
return true;
int num = SDL_GetNumAudioDrivers();
for (int i = 0; i < num; ++i)
{
if (SDL_GetAudioDriver(i) == Settings::audioDriver.value)
{
SDL_setenv("SDL_AUDIODRIVER", SDL_GetAudioDriver(i), 1);
return true;
}
}
return false;
}
int start()
void start()
{
if (!Settings::logging)
disable_cout();
else
Settings::print();
if (!setAudioDriver())
std::cerr << "[Main] Not supported audio driver " << Settings::audioDriver.value << std::endl;
if (Settings::keyPipe.value.length() > 2)
PipeListener pipeListener(Settings::keyPipe.value.c_str());
// Initialize SDL video subsystem
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) != 0)
{
std::cerr << "[Main] SDL initialisation failed: " << SDL_GetError() << std::endl;
return 1;
}
std::cout << "[Main] SDL audio driver: " << SDL_GetCurrentAudioDriver() << std::endl;
if (TTF_Init() != 0)
{
std::cerr << "[Main] TTF initialisation failed: " << TTF_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_DisplayMode displayMode;
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0)
{
std::cerr << "[Main] SDL get display mode failed: " << SDL_GetError() << std::endl;
TTF_Quit();
SDL_Quit();
return 1;
}
// Create SDL window centered on screen
if (Settings::fastScale)
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
else
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
Settings::fullscreen ? displayMode.w : Settings::width,
Settings::fullscreen ? displayMode.h : Settings::height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN);
if (!window)
{
std::cerr << "[Main] SDL can't create window: " << SDL_GetError() << std::endl;
TTF_Quit();
SDL_Quit();
return 1;
}
if (!Settings::cursor)
SDL_ShowCursor(SDL_DISABLE);
// Create accelerated renderer for the window
renderer = SDL_CreateRenderer(window, -1, (Settings::vsync ? (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC) : SDL_RENDERER_ACCELERATED));
if (renderer)
{
evtStatus = SDL_RegisterEvents(2);
if (evtStatus != (Uint32)-1)
{
evtConnected = evtStatus + 1;
std::cout << "[Main] Started" << std::endl;
application();
std::cout << "[Main] Finish" << std::endl;
}
else
{
std::cerr << "[Main] Can't register custom events" << std::endl;
}
SDL_DestroyRenderer(renderer);
}
else
{
std::cerr << "[Main] SDL can't create renderer: " << SDL_GetError() << std::endl;
}
SDL_DestroyWindow(window);
TTF_Quit();
SDL_Quit();
return 0;
Application app;
app.start(title);
}
int main(int argc, char **argv)
@@ -451,13 +33,15 @@ int main(int argc, char **argv)
}
try
{
if (argc == 2)
Settings::load(argv[1]);
return start();
if (argc == 2 && !Settings::load(argv[1]))
return 1;
start();
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;
std::cerr << "[Main] Error > " << e.what() << std::endl;
return 1;
}
return 0;
}
+16 -10
View File
@@ -1,23 +1,21 @@
#include "pipe_listener.h"
#include <SDL2/SDL.h>
#include <cerrno>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>
PipeListener::PipeListener(Protocol &protocol, const char *path)
: _protocol(protocol), _path(path), _active(false)
PipeListener::PipeListener(const char *path)
: _path(path), _active(false)
{
if(path == nullptr)
if (path == nullptr)
return;
unlink(_path);
if (mkfifo(_path, 0666) == -1)
if (errno != EEXIST)
{
std::cout << "[Pipe] Failed to create FIFO " << _path << ": " << std::strerror(errno) << std::endl;
return;
}
if (mkfifo(_path, 0666) == -1 && errno != EEXIST)
throw std::runtime_error(std::string("[Pipe] Failed to create FIFO ") + _path + ": " + std::strerror(errno));
_active = true;
_thread = std::thread(&PipeListener::loop, this);
@@ -57,7 +55,15 @@ void PipeListener::loop()
{
std::cout << "[Pipe] Received: " << (int)value << std::endl;
if (value != 0)
_protocol.sendKey(value);
{
SDL_Event e{};
e.type = SDL_KEYDOWN;
e.key.state = SDL_RELEASED;
e.key.repeat = 0;
e.key.keysym.sym = static_cast<SDL_Keycode>(value);
e.key.keysym.scancode = SDL_GetScancodeFromKey(e.key.keysym.sym);
SDL_PushEvent(&e);
}
}
if (fd >= 0)
+4 -5
View File
@@ -1,5 +1,5 @@
#ifndef SRC_NAMED_PIPE_H
#define SRC_NAMED_PIPE_H
#ifndef SRC_PIPE_LISTENER
#define SRC_PIPE_LISTENER
#include <thread>
#include <atomic>
@@ -7,16 +7,15 @@
class PipeListener {
public:
PipeListener(Protocol &protocol, const char *path);
PipeListener(const char *path);
~PipeListener();
private:
void loop();
Protocol &_protocol;
const char *_path;
bool _active;
std::thread _thread;
};
#endif // SRC_NAMED_PIPE_H
#endif /* SRC_PIPE_LISTENER */
+2 -1
View File
@@ -249,7 +249,7 @@ void Protocol::sendEncryption()
{
if (!_cipher)
{
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised";
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl;
return;
}
uint8_t buf[4];
@@ -259,6 +259,7 @@ void Protocol::sendEncryption()
void Protocol::onStatus(uint8_t status)
{
std::cout << "[Protocol] Status " << (int)status << std::endl;
pushEvent(_evtStatusId, status);
}
+3 -2
View File
@@ -3,13 +3,13 @@
#include <iostream>
#include <fstream>
void Settings::load(const std::string &filename)
bool Settings::load(const std::string &filename)
{
std::ifstream file(filename);
if (!file.is_open())
{
std::cerr << "[Settings] Cannot open “" << filename << "" << std::endl;
return;
return false;
}
std::string line;
@@ -48,6 +48,7 @@ void Settings::load(const std::string &filename)
if (!found)
std::cerr << "[Settings] Unknown key “" << key << "" << std::endl;
}
return true;
}
void Settings::print()
+35 -14
View File
@@ -3,6 +3,10 @@
#include "helper/settings_base.h"
#define SCREEN_MODE_WINDOW 0
#define SCREEN_MODE_FULLSCREEN 1
#define SCREEN_MODE_HEADLESS 2
// The singleton “Settings” namespace
class Settings
{
@@ -14,7 +18,7 @@ public:
static inline Setting<int> height{"height", 576};
static inline Setting<int> sourceFps{"source-fps", 50};
static inline Setting<int> fps{"fps", 50};
static inline Setting<bool> fullscreen{"fullscreen", true};
static inline Setting<int> screenMode{"window-mode", 0};
static inline Setting<bool> cursor{"cursor", false};
static inline Setting<bool> logging{"logging", false};
@@ -35,7 +39,7 @@ public:
static inline Setting<int> fontSize{"font-size", 30};
static inline Setting<bool> vsync{"vsync", false};
static inline Setting<bool> hwDecode{"hw-decode", true};
static inline Setting<bool> forceRedraw{"force-redraw", false};
static inline Setting<int> forceRedraw{"force-redraw", 0};
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
static inline Setting<bool> fastScale{"fast-render-scale", false};
static inline Setting<int> videoQueue{"video-buffer-size", 32};
@@ -43,30 +47,47 @@ public:
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
static inline Setting<int> audioDelayCall{"audio-buffer-wait-call", 8};
static inline Setting<float> audioFade{"audio-fade", 0.3};
static inline Setting<int> audioBuffer{"audio-buffer-samples", 2048};
static inline Setting<int> audioBuffer{"audio-buffer-samples", 2048};
static inline Setting<std::string> audioDriver{"audio-driver", ""};
static inline Setting<std::string> onConnect{"on-connect-script", ""};
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
static inline Setting<int> keyLeft{"key-left", 0};
static inline Setting<int> keyRight{"key-right", 0};
static inline Setting<int> keyEnter{"key-enter", 0};
static inline Setting<int> keyBack{"key-back", 0};
static inline Setting<int> keyHome{"key-home", 0};
static inline Setting<std::string> keyPipe{"key-pipe-path", ""};
// Key mapping section
static inline KeySetting<int> keySiri{"key-siri", 115, 5};
static inline KeySetting<int> keyNightOn{"key-nightmode-on", 122, 16};
static inline KeySetting<int> keyNightOff{"key-nightmode-off", 120, 17};
static inline KeySetting<int> keyLeft{"key-left", 1073741903, 100};
static inline KeySetting<int> keyRight{"key-right", 1073741904, 101};
static inline KeySetting<int> keyEnter{"key-enter", 13, 104};
static inline KeySetting<int> keyEnterUp{"key-enterup", 0, 105};
static inline KeySetting<int> keyBack{"key-back", 8, 106};
static inline KeySetting<int> keyUp{"key-up", 1073741906, 113};
static inline KeySetting<int> keyDown{"key-down", 1073741905, 114};
static inline KeySetting<int> keyHome{"key-home", 104, 200};
static inline KeySetting<int> keyPlay{"key-play", 93, 201};
static inline KeySetting<int> keyPause{"key-pause", 91, 202};
static inline KeySetting<int> keyPlayPause{"key-play-toggle", 112, 203};
static inline KeySetting<int> keyNext{"key-next", 46, 204};
static inline KeySetting<int> keyPrev{"key-previous", 44, 205};
static inline KeySetting<int> keyAccept{"key-call-accept", 97, 300};
static inline KeySetting<int> keyReject{"key-call-reject", 115, 301};
static inline KeySetting<int> keyVideoFocus{"key-video-focus", 118, 500};
static inline KeySetting<int> keyVideoRelease{"key-video-release", 98, 501};
static inline KeySetting<int> keyNavFocus{"key-nav-focus", 110, 508};
static inline KeySetting<int> keyNavRelease{"key-nav-release", 109, 509};
static inline Setting<std::string> keyPipe{"key-pipe-path", ""};
// Debug section
static inline Setting<int> protocolDebug{"protocol-debug", 0};
static inline Setting<float> codecLowDelay{"decode-low-delay", true};
static inline Setting<float> codecFast{"decode-fast", false};
static void load(const std::string &filename);
static bool load(const std::string &filename);
static void print();
static inline bool isFullscreen() { return screenMode == SCREEN_MODE_FULLSCREEN; };
static inline bool isHeadless() { return screenMode == SCREEN_MODE_HEADLESS; };
private:
static void trim(std::string &s);
};