diff --git a/gui/include/discoverymanager.h b/gui/include/discoverymanager.h index 5147899..f876835 100644 --- a/gui/include/discoverymanager.h +++ b/gui/include/discoverymanager.h @@ -19,20 +19,42 @@ #define CHIAKI_DISCOVERYMANAGER_H #include +#include #include +struct DiscoveryHost +{ + ChiakiDiscoveryHostState state; + uint16_t host_request_port; +#define STRING_MEMBER(name) QString name; + CHIAKI_DISCOVERY_HOST_STRING_FOREACH(STRING_MEMBER) +#undef STRING_MEMBER +}; + +Q_DECLARE_METATYPE(DiscoveryHost) + class DiscoveryManager : public QObject { Q_OBJECT + friend class DiscoveryManagerPrivate; + private: ChiakiDiscoveryService service; + QList hosts; + + private slots: + void DiscoveryServiceHosts(QList hosts); public: explicit DiscoveryManager(QObject *parent = nullptr); ~DiscoveryManager(); + const QList GetHosts() const { return hosts; } + + signals: + void HostsUpdated(); }; #endif //CHIAKI_DISCOVERYMANAGER_H diff --git a/gui/include/mainwindow.h b/gui/include/mainwindow.h index 2685213..157d58c 100644 --- a/gui/include/mainwindow.h +++ b/gui/include/mainwindow.h @@ -20,6 +20,8 @@ #include +#include "discoverymanager.h" + class DynamicGridWidget; class ServerItemWidget; @@ -31,6 +33,8 @@ class MainWindow : public QMainWindow DynamicGridWidget *grid_widget; QList server_item_widgets; + DiscoveryManager discovery_manager; + public: explicit MainWindow(QWidget *parent = nullptr); @@ -40,6 +44,8 @@ class MainWindow : public QMainWindow void RunDiscovery(); void ShowSettings(); + + void DiscoveryHostsUpdated(); }; #endif //CHIAKI_MAINWINDOW_H diff --git a/gui/src/discoverymanager.cpp b/gui/src/discoverymanager.cpp index 9911a91..befc624 100644 --- a/gui/src/discoverymanager.cpp +++ b/gui/src/discoverymanager.cpp @@ -1,3 +1,5 @@ +#include + /* * This file is part of Chiaki. * @@ -17,14 +19,29 @@ #include +#include + +#define PING_MS 500 +#define HOSTS_MAX 16 +#define DROP_PINGS 3 + +static void DiscoveryServiceHostsCallback(ChiakiDiscoveryHost *hosts, size_t hosts_count, void *user); + DiscoveryManager::DiscoveryManager(QObject *parent) : QObject(parent) { ChiakiDiscoveryServiceOptions options; - options.ping_ms = 500; - options.hosts_max = 16; + options.ping_ms = PING_MS; + options.hosts_max = HOSTS_MAX; + options.host_drop_pings = DROP_PINGS; + options.cb = DiscoveryServiceHostsCallback; + options.cb_user = this; - options.send_addr = nullptr; // TODO - options.send_addr_size = 0; // TODO + sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_port = htons(CHIAKI_DISCOVERY_PORT); + addr.sin_addr.s_addr = 0xffffffff; // 255.255.255.255 + options.send_addr = reinterpret_cast(&addr); + options.send_addr_size = sizeof(addr); ChiakiErrorCode err = chiaki_discovery_service_init(&service, &options, nullptr /* TODO */); if(err != CHIAKI_ERR_SUCCESS) @@ -35,3 +52,40 @@ DiscoveryManager::~DiscoveryManager() { chiaki_discovery_service_fini(&service); } + +void DiscoveryManager::DiscoveryServiceHosts(QList hosts) +{ + this->hosts = std::move(hosts); + emit HostsUpdated(); +} + +#include + +class DiscoveryManagerPrivate +{ + public: + static void DiscoveryServiceHosts(DiscoveryManager *discovery_manager, const QList &hosts) + { + QMetaObject::invokeMethod(discovery_manager, "DiscoveryServiceHosts", Qt::ConnectionType::QueuedConnection, Q_ARG(QList, hosts)); + } +}; + +static void DiscoveryServiceHostsCallback(ChiakiDiscoveryHost *hosts, size_t hosts_count, void *user) +{ + QList hosts_list; + hosts_list.reserve(hosts_count); + + for(size_t i=0; istate; + o.host_request_port = o.host_request_port; +#define CONVERT_STRING(name) if(h->name) { o.name = QString::fromLocal8Bit(h->name); } + CHIAKI_DISCOVERY_HOST_STRING_FOREACH(CONVERT_STRING) +#undef CONVERT_STRING + hosts_list.append(o); + } + + DiscoveryManagerPrivate::DiscoveryServiceHosts(reinterpret_cast(user), hosts_list); +} diff --git a/gui/src/main.cpp b/gui/src/main.cpp index e9f3b3f..0064b96 100644 --- a/gui/src/main.cpp +++ b/gui/src/main.cpp @@ -32,6 +32,7 @@ int RunMain(QApplication &app); int main(int argc, char *argv[]) { + qRegisterMetaType(); qRegisterMetaType(); ChiakiErrorCode err = chiaki_lib_init(); diff --git a/gui/src/mainwindow.cpp b/gui/src/mainwindow.cpp index 4172f44..7e3635d 100644 --- a/gui/src/mainwindow.cpp +++ b/gui/src/mainwindow.cpp @@ -74,6 +74,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) } resize(800, 600); + + connect(&discovery_manager, &DiscoveryManager::HostsUpdated, this, &MainWindow::DiscoveryHostsUpdated); } void MainWindow::ServerItemWidgetSelected() @@ -108,3 +110,8 @@ void MainWindow::ShowSettings() { qDebug() << "TODO: ShowSettings()"; } + +void MainWindow::DiscoveryHostsUpdated() +{ + qDebug() << "updated hosts" << discovery_manager.GetHosts().count(); +} \ No newline at end of file diff --git a/lib/include/chiaki/discovery.h b/lib/include/chiaki/discovery.h index 9e3be7f..9409d28 100644 --- a/lib/include/chiaki/discovery.h +++ b/lib/include/chiaki/discovery.h @@ -55,6 +55,7 @@ const char *chiaki_discovery_host_state_string(ChiakiDiscoveryHostState state); typedef struct chiaki_discovery_host_t { + // All string members here must be in sync with CHIAKI_DISCOVERY_HOST_STRING_FOREACH ChiakiDiscoveryHostState state; const char *system_version; const char *device_discovery_protocol_version; @@ -66,6 +67,18 @@ typedef struct chiaki_discovery_host_t const char *running_app_name; } ChiakiDiscoveryHost; +/** + * Apply A on all names of string members in ChiakiDiscoveryHost + */ +#define CHIAKI_DISCOVERY_HOST_STRING_FOREACH(A) \ + A(system_version); \ + A(device_discovery_protocol_version); \ + A(host_name); \ + A(host_type); \ + A(host_id); \ + A(running_app_titleid); \ + A(running_app_name); + CHIAKI_EXPORT int chiaki_discovery_packet_fmt(char *buf, size_t buf_size, ChiakiDiscoveryPacket *packet); typedef struct chiaki_discovery_t diff --git a/lib/include/chiaki/discoveryservice.h b/lib/include/chiaki/discoveryservice.h index e857db4..497f413 100644 --- a/lib/include/chiaki/discoveryservice.h +++ b/lib/include/chiaki/discoveryservice.h @@ -25,6 +25,8 @@ extern "C" { #endif +typedef void (*ChiakiDiscoveryServiceCb)(ChiakiDiscoveryHost *hosts, size_t hosts_count, void *user); + typedef struct chiaki_discovery_service_options_t { size_t hosts_max; @@ -32,6 +34,8 @@ typedef struct chiaki_discovery_service_options_t uint64_t ping_ms; struct sockaddr *send_addr; size_t send_addr_size; + ChiakiDiscoveryServiceCb cb; + void *cb_user; } ChiakiDiscoveryServiceOptions; typedef struct chiaki_discovery_service_host_discovery_info_t diff --git a/lib/src/discovery.c b/lib/src/discovery.c index 11f1a4c..43ee9ff 100644 --- a/lib/src/discovery.c +++ b/lib/src/discovery.c @@ -241,7 +241,7 @@ static void *discovery_thread_func(void *user) buf[n] = '\00'; CHIAKI_LOGV(discovery->log, "Discovery received:\n%s", buf); - chiaki_log_hexdump_raw(discovery->log, CHIAKI_LOG_VERBOSE, (const uint8_t *)buf, n); + //chiaki_log_hexdump_raw(discovery->log, CHIAKI_LOG_VERBOSE, (const uint8_t *)buf, n); ChiakiDiscoveryHost response; err = chiaki_discovery_srch_response_parse(&response, buf, n); diff --git a/lib/src/discoveryservice.c b/lib/src/discoveryservice.c index 36beabe..d77f761 100644 --- a/lib/src/discoveryservice.c +++ b/lib/src/discoveryservice.c @@ -24,6 +24,7 @@ static void *discovery_service_thread_func(void *user); static void discovery_service_ping(ChiakiDiscoveryService *service); static void discovery_service_drop_old_hosts(ChiakiDiscoveryService *service); static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *user); +static void discovery_service_report_state(ChiakiDiscoveryService *service); CHIAKI_EXPORT ChiakiErrorCode chiaki_discovery_service_init(ChiakiDiscoveryService *service, ChiakiDiscoveryServiceOptions *options, ChiakiLog *log) { @@ -111,7 +112,7 @@ static void *discovery_service_thread_func(void *user) if(err != CHIAKI_ERR_SUCCESS) goto beach; - while(service->stop_cond.pred) + while(true) { err = chiaki_bool_pred_cond_timedwait(&service->stop_cond, service->options.ping_ms); if(err != CHIAKI_ERR_TIMEOUT) @@ -119,6 +120,8 @@ static void *discovery_service_thread_func(void *user) discovery_service_ping(service); } + chiaki_discovery_thread_stop(&discovery_thread); + beach: chiaki_bool_pred_cond_unlock(&service->stop_cond); return NULL; @@ -137,25 +140,31 @@ static void discovery_service_ping(ChiakiDiscoveryService *service) CHIAKI_LOGV(service->log, "Discovery Service sending ping"); ChiakiDiscoveryPacket packet = { 0 }; packet.cmd = CHIAKI_DISCOVERY_CMD_SRCH; - chiaki_discovery_send(&service->discovery, &packet, service->options.send_addr, service->options.send_addr_size); + err = chiaki_discovery_send(&service->discovery, &packet, service->options.send_addr, service->options.send_addr_size); + if(err != CHIAKI_ERR_SUCCESS) + CHIAKI_LOGE(service->log, "Discovery Service failed to send ping"); } static void discovery_service_drop_old_hosts(ChiakiDiscoveryService *service) { // service->state_mutex must be locked + bool change = false; + for(size_t i=0; ihosts_count; i++) { - if(service->host_discovery_infos[i].last_ping_index >= service->ping_index - service->options.host_drop_pings) + if(service->host_discovery_infos[i].last_ping_index + service->options.host_drop_pings >= service->ping_index) continue; - CHIAKI_LOGI(service->log, "Discovery Service: Host with id %s is no longer available", service->hosts[i].host_id); + ChiakiDiscoveryHost *host = &service->hosts[i]; + CHIAKI_LOGI(service->log, "Discovery Service: Host with id %s is no longer available", host->host_id ? host->host_id : ""); - // TODO: free inner strings of service->hosts[i] +#define FREE_STRING(name) do { free((char *)host->name); } while(0) + CHIAKI_DISCOVERY_HOST_STRING_FOREACH(FREE_STRING) +#undef FREE_STRING if(i < service->hosts_count - 1) { - // TODO: check again if correct memmove(service->host_discovery_infos + i, service->host_discovery_infos + i + 1, sizeof(ChiakiDiscoveryServiceHostDiscoveryInfo) * (service->hosts_count - i - 1)); @@ -165,9 +174,14 @@ static void discovery_service_drop_old_hosts(ChiakiDiscoveryService *service) sizeof(ChiakiDiscoveryHost) * (service->hosts_count - i - 1)); } + change = true; + i--; service->hosts_count--; } + + if(change) + discovery_service_drop_old_hosts(service); } static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *user) @@ -180,9 +194,13 @@ static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *use return; } + CHIAKI_LOGV(service->log, "Discovery Service Received host with id %s", host->host_id); + ChiakiErrorCode err = chiaki_mutex_lock(&service->state_mutex); assert(err == CHIAKI_ERR_SUCCESS); + bool change = false; + size_t index = SIZE_MAX; for(size_t i=0; ihosts_count; i++) { @@ -204,6 +222,7 @@ static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *use goto r2con; } + change = true; index = service->hosts_count++; memset(&service->hosts[index], 0, sizeof(ChiakiDiscoveryHost)); } @@ -211,12 +230,19 @@ static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *use service->host_discovery_infos[index].last_ping_index = service->ping_index; ChiakiDiscoveryHost *host_slot = &service->hosts[index]; + + if(host_slot->state != host->state || host_slot->host_request_port != host->host_request_port) + change = true; + host_slot->state = host->state; host_slot->host_request_port = host->host_request_port; #define UPDATE_STRING(name) do { \ if(host_slot->name && host->name && strcmp(host_slot->name, host->name) == 0) \ + break; \ + if(!host_slot->name && !host->name) \ break; \ + change = true; \ if(host_slot->name) \ free((char *)host_slot->name); \ if(host->name) \ @@ -225,14 +251,20 @@ static void discovery_service_host_received(ChiakiDiscoveryHost *host, void *use host_slot->name = NULL; \ } while(0) - UPDATE_STRING(system_version); - UPDATE_STRING(device_discovery_protocol_version); - UPDATE_STRING(host_name); - UPDATE_STRING(host_type); - UPDATE_STRING(host_id); - UPDATE_STRING(running_app_titleid); - UPDATE_STRING(running_app_name); + CHIAKI_DISCOVERY_HOST_STRING_FOREACH(UPDATE_STRING) + +#undef UPDATE_STRING + + if(change) + discovery_service_report_state(service); r2con: chiaki_mutex_unlock(&service->state_mutex); +} + +static void discovery_service_report_state(ChiakiDiscoveryService *service) +{ + // service->state_mutex must be locked + if(service->options.cb) + service->options.cb(service->hosts, service->hosts_count, service->options.cb_user); } \ No newline at end of file