Add DDP (Distributed Display Protocol) controller support
This commit is contained in:
parent
570cc16c98
commit
a31c4f5254
8 changed files with 930 additions and 0 deletions
309
Controllers/DDPController/DDPController.cpp
Normal file
309
Controllers/DDPController/DDPController.cpp
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDPController.cpp |
|
||||||
|
| |
|
||||||
|
| Driver for DDP protocol devices |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDPController.h"
|
||||||
|
#include "LogManager.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
DDPController::DDPController(const std::vector<DDPDevice>& device_list)
|
||||||
|
{
|
||||||
|
devices = device_list;
|
||||||
|
unique_endpoints = NULL;
|
||||||
|
num_endpoints = 0;
|
||||||
|
sequence_number = 0;
|
||||||
|
keepalive_time_ms = 1000;
|
||||||
|
keepalive_thread_run = false;
|
||||||
|
|
||||||
|
InitializeNetPorts();
|
||||||
|
|
||||||
|
if(!devices.empty())
|
||||||
|
{
|
||||||
|
keepalive_thread_run = true;
|
||||||
|
keepalive_thread = std::thread(&DDPController::KeepaliveThreadFunction, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DDPController::~DDPController()
|
||||||
|
{
|
||||||
|
keepalive_thread_run = false;
|
||||||
|
if(keepalive_thread.joinable())
|
||||||
|
{
|
||||||
|
keepalive_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseNetPorts();
|
||||||
|
if(unique_endpoints != NULL)
|
||||||
|
{
|
||||||
|
delete[] unique_endpoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DDPController::InitializeNetPorts()
|
||||||
|
{
|
||||||
|
if(devices.empty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_endpoints = 0;
|
||||||
|
|
||||||
|
for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++)
|
||||||
|
{
|
||||||
|
if(strcmp(unique_endpoints[ep_idx].ip, devices[dev_idx].ip.c_str()) == 0 &&
|
||||||
|
unique_endpoints[ep_idx].port == devices[dev_idx].port)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
{
|
||||||
|
num_endpoints++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_endpoints = new DDPEndpoint[num_endpoints];
|
||||||
|
unsigned int endpoint_count = 0;
|
||||||
|
|
||||||
|
for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for(unsigned int ep_idx = 0; ep_idx < endpoint_count; ep_idx++)
|
||||||
|
{
|
||||||
|
if(strcmp(unique_endpoints[ep_idx].ip, devices[dev_idx].ip.c_str()) == 0 &&
|
||||||
|
unique_endpoints[ep_idx].port == devices[dev_idx].port)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
{
|
||||||
|
strncpy(unique_endpoints[endpoint_count].ip, devices[dev_idx].ip.c_str(), 15);
|
||||||
|
unique_endpoints[endpoint_count].ip[15] = '\0';
|
||||||
|
unique_endpoints[endpoint_count].port = devices[dev_idx].port;
|
||||||
|
endpoint_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++)
|
||||||
|
{
|
||||||
|
net_port* port = new net_port();
|
||||||
|
char port_str[16];
|
||||||
|
snprintf(port_str, 16, "%d", unique_endpoints[ep_idx].port);
|
||||||
|
|
||||||
|
if(port->udp_client(unique_endpoints[ep_idx].ip, port_str))
|
||||||
|
{
|
||||||
|
udp_ports.push_back(port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
udp_ports.push_back(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPController::CloseNetPorts()
|
||||||
|
{
|
||||||
|
for(unsigned int port_idx = 0; port_idx < udp_ports.size(); port_idx++)
|
||||||
|
{
|
||||||
|
if(udp_ports[port_idx] != NULL)
|
||||||
|
{
|
||||||
|
delete udp_ports[port_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
udp_ports.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DDPController::GetPortIndex(const DDPDevice& device)
|
||||||
|
{
|
||||||
|
for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++)
|
||||||
|
{
|
||||||
|
if(strcmp(unique_endpoints[ep_idx].ip, device.ip.c_str()) == 0 &&
|
||||||
|
unique_endpoints[ep_idx].port == device.port)
|
||||||
|
{
|
||||||
|
return (int)ep_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPController::UpdateLEDs(const std::vector<unsigned int>& colors)
|
||||||
|
{
|
||||||
|
if(udp_ports.empty()) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(last_update_mutex);
|
||||||
|
last_colors = colors;
|
||||||
|
last_update_time = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int color_index = 0;
|
||||||
|
|
||||||
|
for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++)
|
||||||
|
{
|
||||||
|
if(color_index >= colors.size()) break;
|
||||||
|
|
||||||
|
unsigned int bytes_per_pixel = 3;
|
||||||
|
unsigned int total_bytes = devices[dev_idx].num_leds * bytes_per_pixel;
|
||||||
|
std::vector<unsigned char> device_data(total_bytes);
|
||||||
|
|
||||||
|
for(unsigned int led_idx = 0; led_idx < devices[dev_idx].num_leds && (color_index + led_idx) < colors.size(); led_idx++)
|
||||||
|
{
|
||||||
|
unsigned int color = colors[color_index + led_idx];
|
||||||
|
unsigned char r = color & 0xFF;
|
||||||
|
unsigned char g = (color >> 8) & 0xFF;
|
||||||
|
unsigned char b = (color >> 16) & 0xFF;
|
||||||
|
unsigned int pixel_offset = led_idx * bytes_per_pixel;
|
||||||
|
|
||||||
|
device_data[pixel_offset + 0] = r;
|
||||||
|
device_data[pixel_offset + 1] = g;
|
||||||
|
device_data[pixel_offset + 2] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int max_data_per_packet = DDP_MAX_DATA_SIZE;
|
||||||
|
unsigned int bytes_sent = 0;
|
||||||
|
|
||||||
|
while(bytes_sent < total_bytes)
|
||||||
|
{
|
||||||
|
unsigned int chunk_size = (max_data_per_packet < (total_bytes - bytes_sent)) ? max_data_per_packet : (total_bytes - bytes_sent);
|
||||||
|
|
||||||
|
if(!SendDDPPacket(devices[dev_idx], device_data.data() + bytes_sent, (unsigned short)chunk_size, bytes_sent))
|
||||||
|
break;
|
||||||
|
|
||||||
|
bytes_sent += chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
color_index += devices[dev_idx].num_leds;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence_number++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DDPController::SendDDPPacket(const DDPDevice& device, const unsigned char* data, unsigned short length, unsigned int offset)
|
||||||
|
{
|
||||||
|
int port_index = GetPortIndex(device);
|
||||||
|
if(port_index < 0 || port_index >= (int)udp_ports.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(udp_ports[port_index] == NULL)
|
||||||
|
{
|
||||||
|
net_port* port = new net_port();
|
||||||
|
char port_str[16];
|
||||||
|
snprintf(port_str, 16, "%d", unique_endpoints[port_index].port);
|
||||||
|
|
||||||
|
if(port->udp_client(unique_endpoints[port_index].ip, port_str))
|
||||||
|
{
|
||||||
|
udp_ports[port_index] = port;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete port;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> packet(DDP_HEADER_SIZE + length);
|
||||||
|
ddp_header* header = (ddp_header*)packet.data();
|
||||||
|
|
||||||
|
header->flags = DDP_FLAG_VER_1 | DDP_FLAG_PUSH;
|
||||||
|
header->sequence = sequence_number & 0x0F;
|
||||||
|
header->data_type = 1;
|
||||||
|
header->dest_id = 1;
|
||||||
|
header->data_offset = htonl(offset);
|
||||||
|
header->data_length = htons(length);
|
||||||
|
|
||||||
|
memcpy(packet.data() + DDP_HEADER_SIZE, data, length);
|
||||||
|
|
||||||
|
int bytes_sent = udp_ports[port_index]->udp_write((char*)packet.data(), (int)packet.size());
|
||||||
|
|
||||||
|
return bytes_sent == (int)packet.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPController::SetKeepaliveTime(unsigned int time_ms)
|
||||||
|
{
|
||||||
|
keepalive_time_ms = time_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPController::KeepaliveThreadFunction()
|
||||||
|
{
|
||||||
|
while(keepalive_thread_run)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
if(keepalive_time_ms == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<unsigned int> colors_to_send;
|
||||||
|
bool should_send = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(last_update_mutex);
|
||||||
|
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||||
|
long long time_since_update = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update_time).count();
|
||||||
|
|
||||||
|
if(time_since_update >= keepalive_time_ms && !last_colors.empty())
|
||||||
|
{
|
||||||
|
colors_to_send = last_colors;
|
||||||
|
should_send = true;
|
||||||
|
last_update_time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(should_send)
|
||||||
|
{
|
||||||
|
unsigned int color_index = 0;
|
||||||
|
|
||||||
|
for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++)
|
||||||
|
{
|
||||||
|
if(color_index >= colors_to_send.size()) break;
|
||||||
|
|
||||||
|
unsigned int bytes_per_pixel = 3;
|
||||||
|
unsigned int total_bytes = devices[dev_idx].num_leds * bytes_per_pixel;
|
||||||
|
std::vector<unsigned char> device_data(total_bytes);
|
||||||
|
|
||||||
|
for(unsigned int led_idx = 0; led_idx < devices[dev_idx].num_leds && (color_index + led_idx) < colors_to_send.size(); led_idx++)
|
||||||
|
{
|
||||||
|
unsigned int color = colors_to_send[color_index + led_idx];
|
||||||
|
unsigned char r = color & 0xFF;
|
||||||
|
unsigned char g = (color >> 8) & 0xFF;
|
||||||
|
unsigned char b = (color >> 16) & 0xFF;
|
||||||
|
unsigned int pixel_offset = led_idx * bytes_per_pixel;
|
||||||
|
|
||||||
|
device_data[pixel_offset + 0] = r;
|
||||||
|
device_data[pixel_offset + 1] = g;
|
||||||
|
device_data[pixel_offset + 2] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int max_data_per_packet = DDP_MAX_DATA_SIZE;
|
||||||
|
unsigned int bytes_sent = 0;
|
||||||
|
|
||||||
|
while(bytes_sent < total_bytes)
|
||||||
|
{
|
||||||
|
unsigned int chunk_size = (max_data_per_packet < (total_bytes - bytes_sent)) ? max_data_per_packet : (total_bytes - bytes_sent);
|
||||||
|
|
||||||
|
if(!SendDDPPacket(devices[dev_idx], device_data.data() + bytes_sent, (unsigned short)chunk_size, bytes_sent))
|
||||||
|
break;
|
||||||
|
|
||||||
|
bytes_sent += chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
color_index += devices[dev_idx].num_leds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
Controllers/DDPController/DDPController.h
Normal file
97
Controllers/DDPController/DDPController.h
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDPController.h |
|
||||||
|
| |
|
||||||
|
| Driver for DDP protocol devices |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include "net_port.h"
|
||||||
|
|
||||||
|
#define DDP_DEFAULT_PORT 4048
|
||||||
|
#define DDP_HEADER_SIZE 10
|
||||||
|
#define DDP_HEADER_SIZE_TC 14
|
||||||
|
#define DDP_VERSION 1
|
||||||
|
#define DDP_MAX_PACKET_SIZE 1450
|
||||||
|
#define DDP_MAX_DATA_SIZE 1440
|
||||||
|
|
||||||
|
#define DDP_FLAG_VER_MASK 0xC0
|
||||||
|
#define DDP_FLAG_VER_1 0x40
|
||||||
|
#define DDP_FLAG_TIMECODE 0x10
|
||||||
|
#define DDP_FLAG_STORAGE 0x08
|
||||||
|
#define DDP_FLAG_REPLY 0x04
|
||||||
|
#define DDP_FLAG_QUERY 0x02
|
||||||
|
#define DDP_FLAG_PUSH 0x01
|
||||||
|
|
||||||
|
#define DDP_TYPE_RGB8 0x0B
|
||||||
|
#define DDP_TYPE_RGB_SIMPLE 1
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct ddp_header
|
||||||
|
{
|
||||||
|
unsigned char flags;
|
||||||
|
unsigned char sequence;
|
||||||
|
unsigned char data_type;
|
||||||
|
unsigned char dest_id;
|
||||||
|
unsigned int data_offset;
|
||||||
|
unsigned short data_length;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct DDPDevice
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string ip;
|
||||||
|
unsigned short port;
|
||||||
|
unsigned int num_leds;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDPEndpoint
|
||||||
|
{
|
||||||
|
char ip[16];
|
||||||
|
unsigned short port;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DDPController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DDPController(const std::vector<DDPDevice>& devices);
|
||||||
|
~DDPController();
|
||||||
|
|
||||||
|
void UpdateLEDs(const std::vector<unsigned int>& colors);
|
||||||
|
void SetKeepaliveTime(unsigned int time_ms);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<DDPDevice> devices;
|
||||||
|
std::vector<net_port*> udp_ports;
|
||||||
|
DDPEndpoint* unique_endpoints;
|
||||||
|
unsigned int num_endpoints;
|
||||||
|
unsigned char sequence_number;
|
||||||
|
|
||||||
|
|
||||||
|
std::atomic<bool> keepalive_thread_run;
|
||||||
|
std::thread keepalive_thread;
|
||||||
|
std::mutex last_update_mutex;
|
||||||
|
std::chrono::steady_clock::time_point last_update_time;
|
||||||
|
std::vector<unsigned int> last_colors;
|
||||||
|
unsigned int keepalive_time_ms;
|
||||||
|
|
||||||
|
bool InitializeNetPorts();
|
||||||
|
void CloseNetPorts();
|
||||||
|
int GetPortIndex(const DDPDevice& device);
|
||||||
|
bool SendDDPPacket(const DDPDevice& device,
|
||||||
|
const unsigned char* data,
|
||||||
|
unsigned short length,
|
||||||
|
unsigned int offset = 0);
|
||||||
|
void KeepaliveThreadFunction();
|
||||||
|
};
|
||||||
92
Controllers/DDPController/DDPControllerDetect.cpp
Normal file
92
Controllers/DDPController/DDPControllerDetect.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDPControllerDetect.cpp |
|
||||||
|
| |
|
||||||
|
| Detector for DDP devices |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "Detector.h"
|
||||||
|
#include "RGBController.h"
|
||||||
|
#include "RGBController_DDP.h"
|
||||||
|
#include "SettingsManager.h"
|
||||||
|
#include "LogManager.h"
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
void DetectDDPControllers()
|
||||||
|
{
|
||||||
|
json ddp_settings;
|
||||||
|
std::vector<std::vector<DDPDevice>> device_lists;
|
||||||
|
DDPDevice dev;
|
||||||
|
|
||||||
|
ddp_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DDPDevices");
|
||||||
|
|
||||||
|
if(ddp_settings.contains("devices"))
|
||||||
|
{
|
||||||
|
for(unsigned int device_idx = 0; device_idx < ddp_settings["devices"].size(); device_idx++)
|
||||||
|
{
|
||||||
|
dev.name = "";
|
||||||
|
dev.ip = "";
|
||||||
|
dev.port = DDP_DEFAULT_PORT;
|
||||||
|
dev.num_leds = 0;
|
||||||
|
|
||||||
|
if(ddp_settings["devices"][device_idx].contains("name"))
|
||||||
|
dev.name = ddp_settings["devices"][device_idx]["name"];
|
||||||
|
if(ddp_settings["devices"][device_idx].contains("ip"))
|
||||||
|
dev.ip = ddp_settings["devices"][device_idx]["ip"];
|
||||||
|
if(ddp_settings["devices"][device_idx].contains("port"))
|
||||||
|
dev.port = ddp_settings["devices"][device_idx]["port"];
|
||||||
|
if(ddp_settings["devices"][device_idx].contains("num_leds"))
|
||||||
|
dev.num_leds = ddp_settings["devices"][device_idx]["num_leds"];
|
||||||
|
|
||||||
|
if(dev.name.empty())
|
||||||
|
dev.name = "DDP Device " + std::to_string(device_idx + 1);
|
||||||
|
if(dev.ip.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(dev.num_leds == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool device_added_to_existing_list = false;
|
||||||
|
|
||||||
|
for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++)
|
||||||
|
{
|
||||||
|
for(unsigned int existing_device_idx = 0; existing_device_idx < device_lists[list_idx].size(); existing_device_idx++)
|
||||||
|
{
|
||||||
|
if(dev.ip == device_lists[list_idx][existing_device_idx].ip &&
|
||||||
|
dev.port == device_lists[list_idx][existing_device_idx].port)
|
||||||
|
{
|
||||||
|
device_lists[list_idx].push_back(dev);
|
||||||
|
device_added_to_existing_list = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(device_added_to_existing_list)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!device_added_to_existing_list)
|
||||||
|
{
|
||||||
|
std::vector<DDPDevice> new_list;
|
||||||
|
new_list.push_back(dev);
|
||||||
|
device_lists.push_back(new_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++)
|
||||||
|
{
|
||||||
|
RGBController_DDP* rgb_controller = new RGBController_DDP(device_lists[list_idx]);
|
||||||
|
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_DETECTOR("DDP", DetectDDPControllers);
|
||||||
136
Controllers/DDPController/RGBController_DDP.cpp
Normal file
136
Controllers/DDPController/RGBController_DDP.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| RGBController_DDP.cpp |
|
||||||
|
| |
|
||||||
|
| RGBController for DDP devices |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "RGBController_DDP.h"
|
||||||
|
|
||||||
|
/**------------------------------------------------------------------*\
|
||||||
|
@name DDP Devices
|
||||||
|
@category LEDStrip
|
||||||
|
@type Network
|
||||||
|
@save :x:
|
||||||
|
@direct :white_check_mark:
|
||||||
|
@effects :x:
|
||||||
|
@detectors DetectDDPControllers
|
||||||
|
@comment
|
||||||
|
\*-------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
RGBController_DDP::RGBController_DDP(std::vector<DDPDevice> device_list)
|
||||||
|
{
|
||||||
|
devices = device_list;
|
||||||
|
name = "DDP Device Group";
|
||||||
|
type = DEVICE_TYPE_LEDSTRIP;
|
||||||
|
description = "Distributed Display Protocol Device";
|
||||||
|
location = "DDP: ";
|
||||||
|
|
||||||
|
if(devices.size() == 1)
|
||||||
|
name = devices[0].name;
|
||||||
|
else if(!devices[0].ip.empty())
|
||||||
|
name += " (" + devices[0].ip + ")";
|
||||||
|
|
||||||
|
if(!devices[0].ip.empty())
|
||||||
|
location += devices[0].ip + ":" + std::to_string(devices[0].port);
|
||||||
|
|
||||||
|
mode Direct;
|
||||||
|
Direct.name = "Direct";
|
||||||
|
Direct.value = 0;
|
||||||
|
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||||
|
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||||
|
Direct.brightness_min = 0;
|
||||||
|
Direct.brightness_max = 100;
|
||||||
|
Direct.brightness = 100;
|
||||||
|
modes.push_back(Direct);
|
||||||
|
|
||||||
|
controller = new DDPController(devices);
|
||||||
|
SetupZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
RGBController_DDP::~RGBController_DDP()
|
||||||
|
{
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::SetupZones()
|
||||||
|
{
|
||||||
|
for(unsigned int zone_idx = 0; zone_idx < devices.size(); zone_idx++)
|
||||||
|
{
|
||||||
|
zone led_zone;
|
||||||
|
led_zone.name = devices[zone_idx].name;
|
||||||
|
led_zone.type = ZONE_TYPE_LINEAR;
|
||||||
|
led_zone.leds_min = devices[zone_idx].num_leds;
|
||||||
|
led_zone.leds_max = devices[zone_idx].num_leds;
|
||||||
|
led_zone.leds_count = devices[zone_idx].num_leds;
|
||||||
|
led_zone.matrix_map = NULL;
|
||||||
|
zones.push_back(led_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
|
||||||
|
{
|
||||||
|
for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++)
|
||||||
|
{
|
||||||
|
led new_led;
|
||||||
|
new_led.name = zones[zone_idx].name + " LED " + std::to_string(led_idx + 1);
|
||||||
|
new_led.value = 0;
|
||||||
|
leds.push_back(new_led);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetupColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::DeviceUpdateLEDs()
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> brightness_adjusted_colors;
|
||||||
|
brightness_adjusted_colors.reserve(colors.size());
|
||||||
|
float brightness_scale = (float)modes[active_mode].brightness / 100.0f;
|
||||||
|
|
||||||
|
for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++)
|
||||||
|
{
|
||||||
|
unsigned int color = colors[color_idx];
|
||||||
|
unsigned char r = color & 0xFF;
|
||||||
|
unsigned char g = (color >> 8) & 0xFF;
|
||||||
|
unsigned char b = (color >> 16) & 0xFF;
|
||||||
|
r = (unsigned char)(r * brightness_scale);
|
||||||
|
g = (unsigned char)(g * brightness_scale);
|
||||||
|
b = (unsigned char)(b * brightness_scale);
|
||||||
|
unsigned int adjusted_color = r | (g << 8) | (b << 16);
|
||||||
|
brightness_adjusted_colors.push_back(adjusted_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller->UpdateLEDs(brightness_adjusted_colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::UpdateZoneLEDs(int /*zone*/)
|
||||||
|
{
|
||||||
|
DeviceUpdateLEDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::UpdateSingleLED(int /*led*/)
|
||||||
|
{
|
||||||
|
DeviceUpdateLEDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::DeviceUpdateMode()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::SetCustomMode()
|
||||||
|
{
|
||||||
|
active_mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGBController_DDP::SetKeepaliveTime(unsigned int time_ms)
|
||||||
|
{
|
||||||
|
if(controller != nullptr)
|
||||||
|
{
|
||||||
|
controller->SetKeepaliveTime(time_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
Controllers/DDPController/RGBController_DDP.h
Normal file
34
Controllers/DDPController/RGBController_DDP.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| RGBController_DDP.h |
|
||||||
|
| |
|
||||||
|
| RGBController for DDP devices |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "RGBController.h"
|
||||||
|
#include "DDPController.h"
|
||||||
|
|
||||||
|
class RGBController_DDP : public RGBController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RGBController_DDP(std::vector<DDPDevice> device_list);
|
||||||
|
~RGBController_DDP();
|
||||||
|
|
||||||
|
void SetupZones();
|
||||||
|
void ResizeZone(int zone, int new_size);
|
||||||
|
void DeviceUpdateLEDs();
|
||||||
|
void UpdateZoneLEDs(int zone);
|
||||||
|
void UpdateSingleLED(int led);
|
||||||
|
void DeviceUpdateMode();
|
||||||
|
void SetCustomMode();
|
||||||
|
void SetKeepaliveTime(unsigned int time_ms);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<DDPDevice> devices;
|
||||||
|
DDPController* controller;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDPSettingsEntry.cpp |
|
||||||
|
| |
|
||||||
|
| User interface for OpenRGB DDP settings entry |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDPSettingsEntry.h"
|
||||||
|
#include "ui_DDPSettingsEntry.h"
|
||||||
|
#include "ManualDevicesTypeManager.h"
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
DDPSettingsEntry::DDPSettingsEntry(QWidget *parent) :
|
||||||
|
BaseManualDeviceEntry(parent),
|
||||||
|
ui(new Ui::DDPSettingsEntry)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
DDPSettingsEntry::~DDPSettingsEntry()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPSettingsEntry::changeEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
if(event->type() == QEvent::LanguageChange)
|
||||||
|
{
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDPSettingsEntry::loadFromSettings(const json& data)
|
||||||
|
{
|
||||||
|
if(data.contains("name"))
|
||||||
|
{
|
||||||
|
ui->NameEdit->setText(QString::fromStdString(data["name"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.contains("ip"))
|
||||||
|
{
|
||||||
|
ui->IPEdit->setText(QString::fromStdString(data["ip"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.contains("port"))
|
||||||
|
{
|
||||||
|
ui->PortSpinBox->setValue(data["port"]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->PortSpinBox->setValue(4048);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.contains("num_leds"))
|
||||||
|
{
|
||||||
|
ui->NumLedsSpinBox->setValue(data["num_leds"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.contains("keepalive_time"))
|
||||||
|
{
|
||||||
|
ui->KeepaliveTimeSpinBox->setValue(data["keepalive_time"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json DDPSettingsEntry::saveSettings()
|
||||||
|
{
|
||||||
|
json result;
|
||||||
|
|
||||||
|
result["name"] = ui->NameEdit->text().toStdString();
|
||||||
|
result["ip"] = ui->IPEdit->text().toStdString();
|
||||||
|
result["port"] = ui->PortSpinBox->value();
|
||||||
|
result["num_leds"] = ui->NumLedsSpinBox->value();
|
||||||
|
|
||||||
|
if(ui->KeepaliveTimeSpinBox->value() > 0)
|
||||||
|
{
|
||||||
|
result["keepalive_time"] = ui->KeepaliveTimeSpinBox->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DDPSettingsEntry::isDataValid()
|
||||||
|
{
|
||||||
|
return !ui->IPEdit->text().isEmpty() && ui->NumLedsSpinBox->value() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BaseManualDeviceEntry* SpawnDDPEntry(const json& data)
|
||||||
|
{
|
||||||
|
DDPSettingsEntry* entry = new DDPSettingsEntry;
|
||||||
|
entry->loadFromSettings(data);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* DDPDeviceName = QT_TRANSLATE_NOOP("ManualDevice", "DDP (Distributed Display Protocol)");
|
||||||
|
|
||||||
|
REGISTER_MANUAL_DEVICE_TYPE(DDPDeviceName, "DDPDevices", SpawnDDPEntry);
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDPSettingsEntry.h |
|
||||||
|
| |
|
||||||
|
| User interface for OpenRGB DDP settings entry |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BaseManualDeviceEntry.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class DDPSettingsEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DDPSettingsEntry : public BaseManualDeviceEntry
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DDPSettingsEntry(QWidget *parent = nullptr);
|
||||||
|
~DDPSettingsEntry();
|
||||||
|
void loadFromSettings(const json& data);
|
||||||
|
json saveSettings() override;
|
||||||
|
bool isDataValid() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::DDPSettingsEntry *ui;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void changeEvent(QEvent *event) override;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DDPSettingsEntry</class>
|
||||||
|
<widget class="QWidget" name="DDPSettingsEntry">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>200</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string notr="true">DDP Settings Entry</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>DDP Device</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="IPLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>IP Address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="IPEdit">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>192.168.1.100</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="NameLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="NameEdit">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Device Name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="PortLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Port:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="PortSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>4048</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="NumLedsLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number of LEDs:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="NumLedsSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="KeepaliveTimeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Keepalive Time (ms):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QSpinBox" name="KeepaliveTimeSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="specialValueText">
|
||||||
|
<string>Disabled</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue