mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Reworked app, #11 key bindings, optimisations
This commit is contained in:
+37
-16
@@ -20,8 +20,11 @@
|
||||
# source images and drawing times
|
||||
#fps = 50
|
||||
|
||||
# Application starts in full screen
|
||||
#fullscreen = true
|
||||
# Application window mode
|
||||
# 0 - windowed
|
||||
# 1 - fullscreen
|
||||
# 2 - headless linux (in case of direct output to screen without window manager)
|
||||
#window-mode = 0
|
||||
|
||||
# Show mouse pointer
|
||||
#cursor = false
|
||||
@@ -100,7 +103,8 @@
|
||||
|
||||
# Request extra frames onevery key press. Usefull if you do not see last updates after key press
|
||||
# Enable for RPI hardware decoding and other systems where hardware decoder tends to buffer frames
|
||||
#force-redraw = false
|
||||
# 0 - disabled, enter number of frames to request (find minimal value that get all screen updates)
|
||||
#force-redraw = 0
|
||||
|
||||
# Corrects aspect of UI
|
||||
#aspect-correction = 1
|
||||
@@ -152,24 +156,41 @@
|
||||
#on-connect-script =
|
||||
#on-disconnect-script =
|
||||
|
||||
# Map extra keys for control with there codes
|
||||
##############################################################################
|
||||
# 4. Key mapping
|
||||
##############################################################################
|
||||
|
||||
# Map keys to control
|
||||
# If you set logging true you and press keys you will see there codes in logs
|
||||
#key-left = 0
|
||||
#key-right = 0
|
||||
#key-enter = 0
|
||||
#key-back = 0
|
||||
#key-home = 0
|
||||
#key-siri = 115 # S
|
||||
#key-nightmode-on = 122 # Z
|
||||
#key-nightmode-off = 120 # X
|
||||
#key-left = 1073741903 # Left arrow
|
||||
#key-right = 1073741904 # Right arrow
|
||||
#key-enter = 13 # Enter
|
||||
# For pipe to simulate key up after key-enter press
|
||||
#key-enterup = 0 # unmapped by default
|
||||
#key-back = 8 # Backspace
|
||||
#key-up = 1073741906 # Up arrow
|
||||
#key-down = 1073741905 # Down arrow
|
||||
#key-home = 104 # H
|
||||
#key-play = 93 # P
|
||||
#key-pause = 91 # [
|
||||
#key-play-toggle = 112 # ]
|
||||
#key-next = 46 # >
|
||||
#key-previous = 44 # <
|
||||
#key-call-accept = 97 # A
|
||||
#key-call-reject = 115 # S
|
||||
#key-video-focus = 118 # V
|
||||
#key-video-release = 98 # B
|
||||
#key-nav-focus = 110 # N
|
||||
#key-nav-release = 109 # M
|
||||
|
||||
# Path for named pipe that keys are going to be listen on to.
|
||||
# If empty pipe listening is disables.
|
||||
# Example /tmp/fastcarplay_pipe
|
||||
# Keys are bytes and defined directly as in protocol, examples:
|
||||
# BTN_LEFT 100
|
||||
# BTN_RIGHT 101
|
||||
# BTN_SELECT_DOWN 104
|
||||
# BTN_SELECT_UP 105
|
||||
# BTN_BACK 106
|
||||
# BTN_HOME 200
|
||||
# Keys are byte value [1..255]
|
||||
# They are mapped to keys defined above
|
||||
#key-pipe-path =
|
||||
|
||||
##############################################################################
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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 ¶ms)
|
||||
{
|
||||
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 ¶ms, 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user