Message queue for sending

This commit is contained in:
Niellune
2026-03-13 04:05:24 +02:00
parent cef813bcd6
commit efbf3cc892
14 changed files with 326 additions and 268 deletions
+6 -6
View File
@@ -270,7 +270,7 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
int key = processKey(e.key.keysym);
if (key > 0)
{
protocol.sendKey(key);
protocol.send(Command::Control(key));
result = true;
}
break;
@@ -279,7 +279,7 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
{
if (e.key.keysym.sym == Settings::keyEnter)
{
protocol.sendKey(Settings::keyEnterUp.key);
protocol.send(Command::Control(Settings::keyEnterUp.key));
result = true;
}
break;
@@ -290,11 +290,11 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
if (_state.frameRendered && (downX >= 0 || upX >= 0 || motionX >= 0))
{
if (downX >= 0)
protocol.sendClick(renderer.xScale * downX / _width, renderer.yScale * downY / _height, true);
protocol.send(Command::Click(renderer.xScale * downX / _width, renderer.yScale * downY / _height, true));
if (motionX >= 0)
protocol.sendMove(renderer.xScale * motionX / _width, renderer.yScale * motionY / _height);
protocol.send(Command::Move(renderer.xScale * motionX / _width, renderer.yScale * motionY / _height));
if (upX >= 0)
protocol.sendClick(renderer.xScale * upX / _width, renderer.yScale * upY / _height, false);
protocol.send(Command::Click(renderer.xScale * upX / _width, renderer.yScale * upY / _height, false));
}
return result;
@@ -358,7 +358,7 @@ void Application::loop()
{
if (latestFrameid == requestFrameId)
{
protocol.requestKeyframe();
protocol.send(Command::Control(BTN_SCREEN_REFRESH));
_state.requestFrame = 1;
requestFrameId = latestFrameid;
}
+27 -12
View File
@@ -30,6 +30,7 @@ Connector::Connector(uint16_t videoPadding)
Connector::~Connector()
{
_active = false;
_queue.notify();
if (_write_thread.joinable())
_write_thread.join();
@@ -69,6 +70,7 @@ void Connector::stop()
return;
_active = false;
_queue.notify();
state(PROTOCOL_STATUS_INITIALISING);
@@ -181,7 +183,15 @@ bool Connector::linkFail(int status, const char *msg)
return true;
}
int Connector::send(int cmd, bool encrypt, uint8_t *data, uint32_t size)
bool Connector::send(std::unique_ptr<Command> packet)
{
if (!_connected || !packet)
return false;
return _queue.pushDiscard(std::move(packet));
}
int Connector::write(int cmd, bool encrypt, uint8_t *data, uint32_t size)
{
if (!_connected)
return 0;
@@ -380,9 +390,6 @@ void Connector::readLoop()
void Connector::writeLoop()
{
std::mutex mtx;
std::condition_variable cv;
// Set thread name
setThreadName("protocol-writer");
state(PROTOCOL_STATUS_LINKING);
@@ -400,10 +407,18 @@ void Connector::writeLoop()
state(PROTOCOL_STATUS_ONLINE);
while (_connected && _active)
{
send(170);
std::unique_lock<std::mutex> lock(mtx);
cv.wait_for(lock, std::chrono::seconds(2), [&]()
{ return !_active.load(); });
std::unique_ptr<Command> message = _queue.pop();
if (!message)
{
if (!_queue.waitFor(_active, 2000))
break;
message = _queue.pop();
}
if (message)
write(message->command, message->encryption, message->data, message->length);
else
write(CMD_HEARTBEAT, false, nullptr, 0);
}
if (_active)
@@ -412,11 +427,11 @@ void Connector::writeLoop()
onDevice(false);
}
_queue.clear();
if (_read_thread.joinable())
_read_thread.join();
}
std::unique_lock<std::mutex> lock(mtx);
cv.wait_for(lock, std::chrono::seconds(1), [&]()
{ return !_active.load(); });
_queue.waitFor(_active, 1000);
}
}
}
+8 -4
View File
@@ -8,7 +8,10 @@
#include <mutex>
#include <string>
#include "helper/isender.h"
#include "aes_cipher.h"
#include "struct/atomic_queue.h"
#include "struct/command.h"
#define READ_TIMEOUT 3000
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
@@ -29,7 +32,7 @@ struct Header
};
#pragma pack(pop)
class Connector
class Connector : public ISender
{
public:
@@ -38,10 +41,9 @@ public:
void start();
void stop();
bool send(std::unique_ptr<Command> packet) override;
protected:
int send(int cmd, bool encrypt = true, uint8_t *data = nullptr, uint32_t size = 0);
virtual void onData(uint32_t cmd, uint32_t length, uint8_t *data) = 0;
virtual void onStatus(u_int8_t status) = 0;
virtual void onDevice(bool connected) = 0;
@@ -66,12 +68,13 @@ private:
void state(u_int8_t state);
bool nextState(u_int8_t state);
bool linkFail(int status, const char *msg);
int write(int cmd, bool encrypt, uint8_t *data, uint32_t size);
libusb_context *_context = nullptr;
libusb_device_handle *_device = nullptr;
uint8_t _endpoint_in;
uint8_t _endpoint_out;
bool _connected;
std::atomic<bool> _connected = false;
std::atomic<bool> _ecnrypt = false;
uint8_t _state;
@@ -83,6 +86,7 @@ private:
std::thread _write_thread;
std::mutex _write_mutex;
std::atomic<bool> _active = false;
AtomicQueue<Command> _queue{256};
u_int16_t _videoPadding;
};
-12
View File
@@ -1,12 +0,0 @@
#ifndef SRC_HELPER_IAUDIO_SENDER
#define SRC_HELPER_IAUDIO_SENDER
#include <cstdint>
class IAudioSender {
public:
virtual ~IAudioSender() = default;
virtual void sendAudio(uint8_t* data, uint32_t length) = 0;
};
#endif /* SRC_HELPER_IAUDIO_SENDER */
+15
View File
@@ -0,0 +1,15 @@
#ifndef SRC_HELPER_ISENDER
#define SRC_HELPER_ISENDER
#include <memory>
#include "struct/command.h"
class ISender
{
public:
virtual ~ISender() = default;
virtual bool send(std::unique_ptr<Command> packet) = 0;
};
#endif /* SRC_HELPER_ISENDER */
+24 -180
View File
@@ -15,7 +15,6 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi
_width(width),
_height(height),
_fps(fps),
_keySent(false),
_phoneConnected(false)
{
}
@@ -37,20 +36,6 @@ void Protocol::stop()
Connector::stop();
}
void Protocol::sendInit(int width, int height, int fps)
{
uint8_t buf[28];
write_uint32_le(&buf[0], width);
write_uint32_le(&buf[4], height);
write_uint32_le(&buf[8], fps);
write_uint32_le(&buf[12], 5);
write_uint32_le(&buf[16], 49152);
write_uint32_le(&buf[20], 2);
write_uint32_le(&buf[24], 2);
send(CMD_OPEN, true, buf, 28);
}
void Protocol::sendConfig()
{
int syncTime = std::time(nullptr);
@@ -91,171 +76,25 @@ void Protocol::sendConfig()
std::cout << "[Protocol] Request android image " << width << "x" << height << std::endl;
char buffer[512];
snprintf(buffer, sizeof(buffer),
"{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d,"
"\"androidAutoSizeW\":%d,\"androidAutoSizeH\":%d,\"HiCarConnectMode\":0,"
"\"GNSSCapability\":7,\"DashboardInfo\":1,\"UseBTPhone\":0}",
syncTime, Settings::mediaDelay.value, drivePosition, width, height);
send(Command::String(
CMD_JSON_CONTROL,
"{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d,"
"\"androidAutoSizeW\":%d,\"androidAutoSizeH\":%d,\"HiCarConnectMode\":0,"
"\"GNSSCapability\":7,\"DashboardInfo\":1,\"UseBTPhone\":0}",
syncTime, Settings::mediaDelay.value, drivePosition, width, height));
sendString(CMD_JSON_CONTROL, buffer);
send(Command::String(CMD_DAYNIGHT, "{\"DayNightMode\":%d}", nightMode));
snprintf(buffer, sizeof(buffer), "{\"DayNightMode\":%d}", nightMode);
sendString(CMD_DAYNIGHT, buffer);
send(Command::File("/tmp/night_mode", nightMode));
send(Command::File("/tmp/charge_mode", Settings::weakCharge ? 0 : 2)); // Weak charge 0, other 2
send(Command::File("/etc/box_name", "CarPlay"));
send(Command::File("/tmp/hand_drive_mode", drivePosition));
sendFile("/tmp/night_mode", nightMode);
sendFile("/tmp/charge_mode", Settings::weakCharge ? 0 : 2); // Weak charge 0, other 2
sendFile("/etc/box_name", "CarPlay");
sendFile("/tmp/hand_drive_mode", drivePosition);
sendInt(CMD_CONTROL, mic);
sendInt(CMD_CONTROL, Settings::wifi5 ? 25 : 24);
sendInt(CMD_CONTROL, Settings::bluetoothAudio ? 22 : 23);
send(Command::Control(mic));
send(Command::Control(Settings::wifi5 ? 25 : 24));
send(Command::Control(Settings::bluetoothAudio ? 22 : 23));
if (Settings::autoconnect)
sendInt(CMD_CONTROL, 1002);
}
bool Protocol::checkKey()
{
bool result = _keySent;
_keySent = false;
return result;
}
void Protocol::sendKey(int key)
{
sendInt(CMD_CONTROL, key, false);
_keySent = true;
}
void Protocol::requestKeyframe()
{
sendInt(CMD_CONTROL, BTN_SCREEN_REFRESH, false);
}
void Protocol::sendFile(const char *filename, const char *value)
{
uint32_t len = strlen(value);
if (len > 16)
{
throw std::invalid_argument("String too long (max 16 bytes)");
}
// note: we send only the ASCII bytes, no trailing '\0'
sendFile(filename,
reinterpret_cast<const uint8_t *>(value),
static_cast<uint32_t>(len));
}
// overload for a single 32bit integer
void Protocol::sendFile(const char *filename, int value)
{
uint8_t buf[4];
write_uint32_le(buf, value);
sendFile(filename, buf, 4);
}
void Protocol::sendClick(float x, float y, bool down)
{
uint8_t buf[16];
write_uint32_le(buf, down ? 14 : 16);
write_uint32_le(buf + 4, int(10000 * x));
write_uint32_le(buf + 8, int(10000 * y));
write_uint32_le(buf + 12, 0);
send(CMD_TOUCH, false, buf, 16);
}
void Protocol::sendMultiTouch(const Multitouch &touches)
{
int count = touches.size();
if (count == 0)
return;
uint8_t buf[MUTLITOUCH_MAX_TOUCH * sizeof(Multitouch::Touch)];
uint8_t *p = buf;
for (int i = 0; i < count; ++i)
{
const Multitouch::Touch &t = touches[i];
write_float_le(p + 0, t.x);
write_float_le(p + 4, t.y);
write_uint32_le(p + 8, static_cast<uint32_t>(t.state));
write_uint32_le(p + 12, static_cast<uint32_t>(t.id));
p += 16;
}
send(CMD_MULTI_TOUCH, false, buf, 16 * count);
}
void Protocol::sendMove(float dx, float dy)
{
uint8_t buf[16];
write_uint32_le(buf, 15);
write_uint32_le(buf + 4, int(10000 * dx));
write_uint32_le(buf + 8, int(10000 * dy));
write_uint32_le(buf + 12, 0);
send(CMD_TOUCH, false, buf, 16);
}
void Protocol::sendAudio(uint8_t *data, uint32_t length)
{
write_uint32_le(data, 5);
write_uint32_le(data + 4, 0);
write_uint32_le(data + 8, 3);
send(CMD_AUDIO_DATA, false, data, length);
}
void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length)
{
// filename is assumed nullterminated, so strlen + 1 to include the '\0'
uint32_t fn_len = strlen(filename) + 1;
// Total buffer size: 4 (fn_len) + fn_len + 4 (content_len) + content_len
uint32_t total = 4 + fn_len + 4 + length;
std::vector<uint8_t> result(total);
uint8_t *buf = result.data();
// 1) filename length (LE)
write_uint32_le(buf, fn_len);
buf += 4;
// 2) filename bytes (including the '\0')
std::memcpy(buf, filename, fn_len);
buf += fn_len;
// 3) content length (LE)
write_uint32_le(buf, length);
buf += 4;
// 4) content bytes
std::memcpy(buf, data, length);
send(CMD_SEND_FILE, true, result.data(), total);
}
void Protocol::sendInt(uint32_t cmd, uint32_t value, bool encryption)
{
uint8_t buf[4];
write_uint32_le(buf, value);
send(cmd, encryption, buf, 4);
}
void Protocol::sendString(uint32_t cmd, char *str, bool encryption)
{
uint32_t total = strlen(str);
send(cmd, true, (uint8_t *)str, total);
}
void Protocol::sendEncryption()
{
if (!_cipher)
{
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl;
return;
}
uint8_t buf[4];
write_uint32_le(buf, _cipher->Seed());
send(CMD_ENCRYPTION, false, buf, 4);
send(Command::Control(1002));
}
void Protocol::onStatus(uint8_t status)
@@ -269,11 +108,16 @@ void Protocol::onDevice(bool connected)
if (connected)
{
if (Settings::encryption)
sendEncryption();
{
if (_cipher)
send(Command::Encryption(_cipher->Seed()));
else
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl;
}
if (Settings::dpi > 0)
sendFile("/tmp/screen_dpi", Settings::dpi);
sendFile("/etc/android_work_mode", 1);
sendInit(_width, _height, _fps);
send(Command::File("/tmp/screen_dpi", Settings::dpi));
send(Command::File("/etc/android_work_mode", 1));
send(Command::Init(_width, _height, _fps));
sendConfig();
}
else
+1 -19
View File
@@ -4,12 +4,11 @@
#include "struct/atomic_queue.h"
#include "struct/message.h"
#include "struct/multitouch.h"
#include "helper/iaudio_sender.h"
#include "settings.h"
#include "connector.h"
#include "recorder.h"
class Protocol : private Connector, public IAudioSender
class Protocol : public Connector
{
public:
@@ -22,27 +21,11 @@ public:
void start(uint32_t evtStatus, uint32_t evtPhone);
void stop();
bool checkKey();
void sendKey(int key);
void requestKeyframe();
void sendFile(const char *filename, const uint8_t *data, uint32_t length);
void sendFile(const char *filename, const char *value);
void sendFile(const char *filename, int value);
void sendClick(float x, float y, bool down);
void sendMultiTouch(const Multitouch &touches);
void sendMove(float dx, float dy);
void sendAudio(uint8_t *data, uint32_t length) override;
AtomicQueue<Message> videoData;
AtomicQueue<Message> audioStreamMain;
AtomicQueue<Message> audioStreamAux;
private:
void sendInt(uint32_t cmd, uint32_t value, bool encryption = true);
void sendString(uint32_t cmd, char *str, bool encryption = true);
void sendEncryption();
void sendInit(int width, int height, int fps);
void sendConfig();
void onStatus(uint8_t status) override;
@@ -57,7 +40,6 @@ private:
uint16_t _width;
uint16_t _height;
uint16_t _fps;
bool _keySent;
bool _phoneConnected;
uint32_t _evtStatusId = (uint32_t)-1;
+3 -2
View File
@@ -6,6 +6,7 @@
#include "helper/protocol_const.h"
#include "helper/functions.h"
#include "settings.h"
#include "struct/command.h"
Recorder::Recorder(uint16_t buffSize)
@@ -20,7 +21,7 @@ Recorder::~Recorder()
_thread.join();
}
void Recorder::start(IAudioSender *sender)
void Recorder::start(ISender *sender)
{
if (_active)
return;
@@ -78,7 +79,7 @@ void Recorder::runner()
{
std::unique_ptr<AudioChunk> buffer = _data.pop();
if (buffer && _sender)
_sender->sendAudio(buffer.get()->data, buffer.get()->size);
_sender->send(Command::Audio(std::move(buffer)));
else if (_active)
std::this_thread::sleep_for(std::chrono::milliseconds(5));
+4 -24
View File
@@ -6,44 +6,24 @@
#include <SDL2/SDL.h>
#include "helper/iaudio_sender.h"
#include "helper/isender.h"
#include "struct/audio_chunk.h"
#include "struct/atomic_queue.h"
class AudioChunk
{
public:
AudioChunk(uint16_t size)
: data(new uint8_t[size]), size(size)
{
}
~AudioChunk()
{
delete[] data;
}
// Deleted copy constructor/assignment
AudioChunk(const AudioChunk &) = delete;
AudioChunk &operator=(const AudioChunk &) = delete;
uint8_t *data;
size_t size;
};
class Recorder
{
public:
Recorder(uint16_t buffSize);
~Recorder();
void start(IAudioSender *sender);
void start(ISender *sender);
void stop();
private:
static void AudioCallback(void *userdata, Uint8 *stream, int len);
void runner();
IAudioSender *_sender;
ISender *_sender;
std::atomic<bool> _active;
std::thread _thread;
AtomicQueue<AudioChunk> _data;
+3 -2
View File
@@ -41,8 +41,9 @@ public:
static inline Setting<bool> hwDecode{"hw-decode", true};
static inline Setting<int> forceRedraw{"force-redraw", 0};
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
static inline Setting<std::string> renderDriver{"renderer-driver", ""};
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
static inline Setting<bool> fastScale{"fast-render-scale", false};
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
static inline Setting<int> videoQueue{"video-buffer-size", 32};
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
@@ -50,7 +51,7 @@ public:
static inline Setting<float> audioFade{"audio-fade", 0.3};
static inline Setting<int> audioBuffer{"audio-buffer-samples", 2048};
static inline Setting<std::string> audioDriver{"audio-driver", ""};
static inline Setting<std::string> renderDriver{"renderer-driver", "auto"};
static inline Setting<std::string> onConnect{"on-connect-script", ""};
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
+8
View File
@@ -82,6 +82,14 @@ public:
return waitFlag.load();
}
bool waitFor(atomic<bool> &waitFlag, uint32_t timeoutMs, uint8_t count = 0)
{
unique_lock<std::mutex> lock(_mtx);
_lock.wait_for(lock, std::chrono::milliseconds(timeoutMs), [&]
{ return _count > count || !waitFlag.load(); });
return waitFlag.load();
}
void clear()
{
_data = std::make_unique<std::unique_ptr<T>[]>(_size);
+31
View File
@@ -0,0 +1,31 @@
#ifndef SRC_STRUCT_AUDIO_CHUNK
#define SRC_STRUCT_AUDIO_CHUNK
#include <cstdlib>
#include <cstddef>
#include <cstdint>
class AudioChunk
{
public:
AudioChunk(uint16_t size)
: data(nullptr), size(size)
{
if (size > 0)
data = static_cast<uint8_t *>(malloc(size));
}
~AudioChunk()
{
free(data);
}
// Deleted copy constructor/assignment
AudioChunk(const AudioChunk &) = delete;
AudioChunk &operator=(const AudioChunk &) = delete;
uint8_t *data;
uint16_t size;
};
#endif /* SRC_STRUCT_AUDIO_CHUNK */
+189
View File
@@ -0,0 +1,189 @@
#ifndef SRC_STRUCT_COMMAND
#define SRC_STRUCT_COMMAND
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <memory>
#include "helper/functions.h"
#include "helper/protocol_const.h"
#include "struct/audio_chunk.h"
#include "struct/multitouch.h"
class Command
{
public:
Command(const Command &) = delete;
Command &operator=(const Command &) = delete;
Command(int cmd, bool encrypt = true, uint32_t size = 0)
: command(cmd), encryption(encrypt), length(size), data(nullptr)
{
if (size > 0)
data = static_cast<uint8_t *>(malloc(size));
}
Command(int cmd, uint32_t value, bool encrypt = true)
: Command(cmd, encrypt, 4)
{
write_uint32_le(data, value);
}
Command(int cmd, bool encrypt, uint8_t *buffer, uint32_t size)
: command(cmd), encryption(encrypt), length(size), data(buffer)
{
}
~Command()
{
if (data)
{
free(data);
data = nullptr;
}
}
static std::unique_ptr<Command> Init(int width, int height, int fps)
{
std::unique_ptr<Command> result(new Command(CMD_OPEN, true, 28));
write_uint32_le(result->data + 0, width);
write_uint32_le(result->data + 4, height);
write_uint32_le(result->data + 8, fps);
write_uint32_le(result->data + 12, 5);
write_uint32_le(result->data + 16, 49152);
write_uint32_le(result->data + 20, 2);
write_uint32_le(result->data + 24, 2);
return result;
}
static std::unique_ptr<Command> File(const char *filename, const uint8_t *data, uint32_t length)
{
// filename is assumed nullterminated, so strlen + 1 to include the '\0'
uint32_t fn_len = strlen(filename) + 1;
// Total buffer size: 4 (fn_len) + fn_len + 4 (content_len) + content_len
std::unique_ptr<Command> result(new Command(CMD_SEND_FILE, true, 4 + fn_len + 4 + length));
uint8_t *buf = result->data;
// 1) filename length (LE)
write_uint32_le(buf, fn_len);
buf += 4;
// 2) filename bytes (including the '\0')
std::memcpy(buf, filename, fn_len);
buf += fn_len;
// 3) content length (LE)
write_uint32_le(buf, length);
buf += 4;
// 4) content bytes
if (length > 0 && data)
std::memcpy(buf, data, length);
return result;
}
static std::unique_ptr<Command> File(const char *filename, const char *value)
{
uint32_t len = std::strlen(value);
if (len > 16)
throw std::invalid_argument("String too long (max 16 bytes)");
// note: we send only the ASCII bytes, no trailing '\0'
return File(filename, reinterpret_cast<const uint8_t *>(value), len);
}
// overload for a single 32bit integer
static std::unique_ptr<Command> File(const char *filename, int value)
{
uint8_t buffer[4];
write_uint32_le(buffer, value);
return File(filename, buffer, 4);
}
static std::unique_ptr<Command> Control(uint32_t value, bool encrypt = false)
{
return std::unique_ptr<Command>(new Command(CMD_CONTROL, value, encrypt));
}
static std::unique_ptr<Command> Encryption(uint32_t seed)
{
return std::unique_ptr<Command>(new Command(CMD_ENCRYPTION, seed, false));
}
static std::unique_ptr<Command> String(uint32_t cmd, const char *str, bool encrypt = true)
{
uint32_t length = std::strlen(str);
std::unique_ptr<Command> result(new Command(cmd, encrypt, length));
if (length > 0)
std::memcpy(result->data, str, length);
return result;
}
template <typename... Args>
static std::unique_ptr<Command> String(uint32_t cmd, const char *format, Args... args)
{
char buffer[512];
std::snprintf(buffer, sizeof(buffer), format, args...);
return String(cmd, buffer);
}
static std::unique_ptr<Command> Touch(uint32_t action, float x, float y)
{
std::unique_ptr<Command> result(new Command(CMD_TOUCH, false, 16));
write_uint32_le(result->data, action);
write_uint32_le(result->data + 4, int(10000 * x));
write_uint32_le(result->data + 8, int(10000 * y));
write_uint32_le(result->data + 12, 0);
return result;
}
static std::unique_ptr<Command> Click(float x, float y, bool down)
{
return Touch(down ? 14 : 16, x, y);
}
static std::unique_ptr<Command> Move(float x, float y)
{
return Touch(15, x, y);
}
static std::unique_ptr<Command> Audio(std::unique_ptr<AudioChunk> chunk)
{
std::unique_ptr<Command> result(new Command(CMD_AUDIO_DATA, false, chunk->data, chunk->size));
chunk->data = nullptr;
write_uint32_le(result->data, 5);
write_uint32_le(result->data + 4, 0);
write_uint32_le(result->data + 8, 3);
return result;
}
static std::unique_ptr<Command> MultiTouch(const Multitouch &touches)
{
int count = touches.size();
if (count == 0)
return nullptr;
std::unique_ptr<Command> result(new Command(CMD_MULTI_TOUCH, false, sizeof(Multitouch::Touch) * count));
uint8_t *buf = result->data;
for (int i = 0; i < count; ++i)
{
const Multitouch::Touch &t = touches[i];
write_float_le(buf + 0, t.x);
write_float_le(buf + 4, t.y);
write_uint32_le(buf + 8, static_cast<uint32_t>(t.state));
write_uint32_le(buf + 12, static_cast<uint32_t>(t.id));
buf += 16;
}
return result;
}
int command;
bool encryption;
uint32_t length;
uint8_t *data;
};
#endif /* SRC_STRUCT_COMMAND */