+++ /dev/null
-#include "acmp_client.h"
-
-#include <functional>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-
-using namespace std;
-
-ACMPClient::ACMPClient(const string &host, int port)
- : host(host), port(port) {}
-
-void ACMPClient::add_init_command(const string &cmd)
-{
- init_commands.push_back(cmd + "\r\n");
-}
-
-void ACMPClient::start()
-{
- t = thread(&ACMPClient::thread_func, this);
-}
-
-void ACMPClient::end()
-{
- t.join();
-}
-
-void ACMPClient::send_command(const string &cmd)
-{
- lock_guard<mutex> lock(mu);
- queued_commands.push_back(cmd + "\r\n");
-}
-
-void ACMPClient::change_server(const string &host, int port)
-{
- lock_guard<mutex> lock(mu);
- queued_commands.push_back(""); // Marker for disconnect.
- this->host = host;
- this->port = port;
-}
-
-void ACMPClient::set_connection_callback(const std::function<void(bool)> &callback)
-{
- connection_callback = callback;
-}
-
-namespace {
-
-int lookup_and_connect(const char *host, int port)
-{
- addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- addrinfo *res;
- char portstr[16];
- snprintf(portstr, sizeof(portstr), "%d", port);
- int err = getaddrinfo(host, portstr, &hints, &res);
- if (err != 0) {
- fprintf(stderr, "Lookup of %s:%d failed: %s\n", host, port, strerror(errno));
- return -1;
- }
-
- for (addrinfo *p = res; p != NULL; p = p->ai_next) {
- int sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
- if (sock == -1) {
- perror("socket");
- continue;
- }
-
- if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
- perror("connect");
- close(sock);
- continue;
- }
-
- // Success!
- freeaddrinfo(res);
- return sock;
- }
-
- freeaddrinfo(res);
- return -1;
-}
-
-} // namespace
-
-void ACMPClient::thread_func()
-{
- if (connection_callback) {
- connection_callback(false);
- }
- for ( ;; ) {
- int sock, port_copy;
- string host_copy;
- {
- lock_guard<mutex> lock(mu);
- host_copy = host;
- port_copy = port;
- }
- sock = lookup_and_connect(host_copy.c_str(), port_copy);
- if (sock == -1) {
- sleep(1);
- continue;
- }
-
- int one = 1;
- if (ioctl(sock, FIONBIO, &one) == 1) {
- perror("ioctl(FIONBIO)");
- close(sock);
- sleep(1);
- continue;
- }
-
- printf("Connected to CasparCG.\n");
- if (connection_callback) {
- connection_callback(true);
- }
-
- bool first = true;
-
- for ( ;; ) {
- vector<string> commands;
- if (first) {
- commands = init_commands;
- first = false;
- } else {
- lock_guard<mutex> lock(mu);
- swap(commands, queued_commands);
- }
-
- bool broken = false;
- string buf;
- for (const string &cmd : commands) {
- buf += cmd;
- if (cmd.empty()) {
- printf("Closing CasparCG socket for reconnection.\n");
- broken = true;
- break;
- }
- }
-
- if (broken) {
- break;
- }
- if (!buf.empty()) {
- printf("Writing: '%s'\n", buf.c_str());
- }
-
- size_t pos = 0;
- do {
- // Consume until there is no more.
- char junk[1024];
- int err = read(sock, junk, sizeof(junk));
- if (err == -1) {
- if (err == EAGAIN) {
- perror("read");
- broken = true;
- break;
- }
- }
- if (err == 0) {
- // Closed.
- printf("Server closed connection.\n");
- broken = true;
- break;
- }
- if (err > 0) {
- // Try again.
- junk[err] = 0;
- printf("From server: '%s'\n", junk);
- continue;
- }
-
- if (pos < buf.size()) {
- // Now write as much as we can.
- err = write(sock, buf.data() + pos, buf.size() - pos);
- if (err == -1) {
- perror("write");
- broken = true;
- break;
- }
- if (err == 0) {
- // Uh-oh. Buffer full for some reason?
- usleep(10000);
- }
- pos += err;
- }
- } while (pos < buf.size());
-
- if (broken) {
- break;
- }
- if (buf.empty()) {
- usleep(100000);
- continue;
- }
- }
-
- close(sock);
- if (connection_callback) {
- connection_callback(false);
- }
- sleep(1);
- }
-}
{
string s = "";
for (char ch : str) {
- if (ch == '"' || ch == '\\') {
+ if (ch == '"') {
s += '\\';
}
s += ch;
ui(new Ui::MainWindow)
{
ui->setupUi(this);
- acmp = new ACMPClient("127.0.0.1", 5250);
- acmp->add_init_command("MIXER 1 STRAIGHT_ALPHA_OUTPUT 1");
- acmp->add_init_command("CG 1 ADD 0 \"score\" 1 \"<templateData></templateData>\"");
- acmp->set_connection_callback([this](bool connected) {
+ ws = new WSServer("127.0.0.1", 5250);
+ ws->set_connection_callback([this](bool connected) {
string msg = connected ? "Connected" : "Not connected";
post_to_main_thread([this, msg]() {
- ui->casparcg_connected_label->setText(QString::fromStdString(msg));
+ ui->ws_connected_label->setText(QString::fromStdString(msg));
});
});
- acmp->start();
-
- connect(ui->casparcg_reconnect_btn, &QPushButton::clicked, this, &MainWindow::casparcg_reconnect_clicked);
+ connect(ui->ws_disconnect_btn, &QPushButton::clicked, this, &MainWindow::ws_disconnect_clicked);
connect(ui->set_initials_btn, &QPushButton::clicked, this, &MainWindow::set_initials_clicked);
connect(ui->set_color_btn, &QPushButton::clicked, this, &MainWindow::set_color_clicked);
connect(ui->set_score_btn, &QPushButton::clicked, this, &MainWindow::set_score_clicked);
MainWindow::~MainWindow()
{
- acmp->end();
delete ui;
}
-void MainWindow::casparcg_reconnect_clicked()
+void MainWindow::ws_disconnect_clicked()
{
- acmp->change_server(ui->casparcg_host_box->text().toStdString(),
- stoi(ui->casparcg_port_box->text().toStdString()));
+ ws->change_port(stoi(ui->ws_port_box->text().toStdString()));
}
void MainWindow::set_initials_clicked()
map<string, string> param;
param["team1"] = escape_html(ui->initials_1_edit->text().toStdString());
param["team2"] = escape_html(ui->initials_2_edit->text().toStdString());
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setteams");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setteams()");
}
void MainWindow::set_color_clicked()
map<string, string> param;
param["team1color"] = ui->color_1_edit->text().toStdString(); // Should maybe be escaped, but meh.
param["team2color"] = ui->color_2_edit->text().toStdString();
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setcolors");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setcolors()");
}
void MainWindow::set_score_clicked()
map<string, string> param;
param["score1"] = to_string(ui->score_1_box->value());
param["score2"] = to_string(ui->score_2_box->value());
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setscore");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setscore()");
autocomment_update();
}
map<string, string> param;
param["clock_min"] = to_string(ui->clock_min_box->value());
param["clock_sec"] = to_string(ui->clock_sec_box->value());
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setclockfromstate");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setclockfromstate()");
}
void MainWindow::set_clock_limit_clicked()
map<string, string> param;
param["clock_limit_min"] = to_string(ui->clock_limit_min_box->value());
param["clock_limit_sec"] = to_string(ui->clock_limit_sec_box->value());
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setclocklimitfromstate");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setclocklimitfromstate()");
}
void MainWindow::start_and_show_clock_clicked()
{
- acmp->send_command("cg 1 invoke 1 startclock"); // Also shows.
+ ws->send_command("eval startclock()"); // Also shows.
}
void MainWindow::stop_clock_clicked()
{
- acmp->send_command("cg 1 invoke 1 stopclock");
+ ws->send_command("eval stopclock()");
}
void MainWindow::show_clock_clicked()
{
- acmp->send_command("cg 1 invoke 1 showclock");
+ ws->send_command("eval showclock()");
}
void MainWindow::hide_clock_clicked()
{
- acmp->send_command("cg 1 invoke 1 hideclock");
+ ws->send_command("eval hideclock()");
}
void MainWindow::set_comment_clicked()
{
map<string, string> param;
param["comment"] = ui->comment_edit->text().toStdString();
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setcomment");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setcomment()");
}
void MainWindow::set_and_show_comment_clicked()
{
set_comment_clicked();
- acmp->send_command("cg 1 invoke 1 showcomment");
+ ws->send_command("eval showcomment()");
}
void MainWindow::set_and_show_autocomment_clicked()
void MainWindow::hide_comment_clicked()
{
- acmp->send_command("cg 1 invoke 1 hidecomment");
+ ws->send_command("eval hidecomment()");
}
void MainWindow::show_lower_third_clicked()
map<string, string> param;
param["text1"] = ui->lowerthird_heading_edit->text().toStdString();
param["text2"] = ui->lowerthird_subheading_edit->text().toStdString();
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 setandshowlowerthird");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval setandshowlowerthird()");
}
void MainWindow::hide_lower_third_clicked()
{
- acmp->send_command("cg 1 invoke 1 hidelowerthird");
+ ws->send_command("eval hidelowerthird()");
}
void MainWindow::quick_lower_third_activate()
} else if (code == "B") {
add_goal(ui->score_2_box, 1);
} else if (code == "C") {
- acmp->send_command("cg 1 invoke 1 hidelowerthird");
+ ws->send_command("eval hidelowerthird()");
} else {
map<string, string> param;
param["code"] = code;
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 quicklowerthird");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval quicklowerthird()");
}
ui->quick_lower_third_edit->clear();
}
void MainWindow::show_scorebug_clicked()
{
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 invoke 1 hidetable");
- acmp->send_command("cg 1 invoke 1 showscorebug");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("eval hidetable()");
+ ws->send_command("eval showscorebug()");
}
void MainWindow::show_group_clicked(const std::string &group_name)
{
map<string, string> param;
param["group_name"] = group_name;
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 showgroup_from_state");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval showgroup_from_state()");
}
void MainWindow::show_roster_clicked(const std::string &team_code)
{
map<string, string> param;
param["team_code"] = team_code;
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 showroster_from_state");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval showroster_from_state()");
}
void MainWindow::show_schedule_clicked()
{
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 invoke 1 showschedule");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("eval showschedule()");
}
void MainWindow::show_carousel_clicked()
{
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 invoke 1 showcarousel");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("eval showcarousel()");
}
void MainWindow::show_roster_carousel_clicked()
map<string, string> param;
param["team1"] = escape_html(ui->initials_1_edit->text().toStdString());
param["team2"] = escape_html(ui->initials_2_edit->text().toStdString());
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
- acmp->send_command("cg 1 invoke 1 showrostercarousel_from_state");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("update " + serialize_as_json(param));
+ ws->send_command("eval showrostercarousel_from_state()");
}
void MainWindow::show_nothing_clicked()
{
- acmp->send_command("cg 1 invoke 1 hidescorebug");
- acmp->send_command("cg 1 invoke 1 stopcarousel");
- acmp->send_command("cg 1 invoke 1 hidetable");
+ ws->send_command("eval hidescorebug()");
+ ws->send_command("eval stopcarousel()");
+ ws->send_command("eval hidetable()");
}