Changes
11 changed files (+332/-262)
-
-
@@ -169,6 +169,7 @@ }pub fn destroy(self_ptr: ?*CApi) callconv(.C) void { const self = self_ptr orelse return; std.log.debug("Destroying application...", .{}); self.deinit(std.heap.c_allocator); }
-
-
-
@@ -30,6 +30,7 @@ allocator: std.mem.Allocator,on_change_callbacks: OnChange.Store, capi_lock: std.Thread.Mutex, has_loaded_once: bool = false, scan_result: ?std.StringHashMap(Entry.CApi) = null, pub fn init(allocator: std.mem.Allocator) Self { return .{
-
@@ -46,7 +47,7 @@pub const CApi = extern struct { internal: *Self, state: State = .not_loaded, entries: [*]Entry.CApi, entries: [*]*Entry.CApi, entries_len: usize, pub const State = enum(c_int) {
-
@@ -94,6 +95,10 @@ entry.deinit();} self.internal.allocator.free(entries); self.entries_len = 0; if (self.internal.scan_result) |*old_map| { old_map.deinit(); } } pub fn reset(capi_ptr: ?*CApi) callconv(.C) void {
-
@@ -110,6 +115,7 @@ const capi = capi_ptr orelse return;const thread = std.Thread.spawn(.{}, loadInternal, .{capi}) catch { capi.internal.capi_lock.lock(); defer capi.internal.capi_lock.unlock(); defer capi.internal.on_change_callbacks.runAll(.{}); capi.state = .err_thread_spawn; return; };
-
@@ -118,72 +124,69 @@ }fn loadInternal(self: *CApi) void { { { self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.state = if (self.internal.has_loaded_once) .refreshing else .loading; } self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.state = if (self.internal.has_loaded_once) .refreshing else .loading; self.internal.on_change_callbacks.runAll(.{}); } const entries = scan(self.internal.allocator) catch |err| { { self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.state = switch (err) { error.NetworkUnavailable => State.err_network_unavailable, error.SocketPermissionDenied => State.err_socket_permission, error.UDPRecvError, error.UDPSendError, error.SocketCreationError, => State.err_socket, error.OutOfMemory => State.err_out_of_memory, else => State.err_unexpected, }; } const entries, const map = scan(self.internal.allocator) catch |err| { self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.state = switch (err) { error.NetworkUnavailable => State.err_network_unavailable, error.SocketPermissionDenied => State.err_socket_permission, error.UDPRecvError, error.UDPSendError, error.SocketCreationError, => State.err_socket, error.OutOfMemory => State.err_out_of_memory, else => State.err_unexpected, }; self.internal.on_change_callbacks.runAll(.{}); return; }; { self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.internal.capi_lock.lock(); defer self.internal.capi_lock.unlock(); self.deinitEntries(); self.deinitEntries(); self.entries = entries.ptr; self.entries_len = entries.len; self.state = .loaded; self.internal.has_loaded_once = true; } self.entries = entries.ptr; self.entries_len = entries.len; self.state = .loaded; self.internal.has_loaded_once = true; self.internal.scan_result = map; self.internal.on_change_callbacks.runAll(.{}); } /// Caller owns returned slice. fn scan(allocator: std.mem.Allocator) discovery.Error(Entry.CApi)![]Entry.CApi { fn scan(allocator: std.mem.Allocator) discovery.Error(Entry.CApi)!struct { []*Entry.CApi, std.StringHashMap(Entry.CApi) } { var servers = try discovery.scan(Entry.CApi, allocator, .{}); defer servers.deinit(); errdefer servers.deinit(); const slice = try allocator.alloc(Entry.CApi, servers.count()); const slice = try allocator.alloc(*Entry.CApi, servers.count()); var i: usize = 0; var iter = servers.valueIterator(); while (iter.next()) |server| { std.log.debug("Found server ({s})", .{server.*.getName()}); slice[i] = server.*; slice[i] = server; i += 1; } return slice; return .{ slice, servers }; } pub fn onChange(capi_ptr: ?*CApi, cb: OnChange.Fn, user_data: callback.UserData) callconv(.C) void { const capi = capi_ptr orelse return; // TODO: Notify error to caller capi.internal.on_change_callbacks.add(OnChange.init(cb, user_data)) catch {}; capi.internal.on_change_callbacks.add(OnChange.init(cb, user_data)) catch |err| { std.log.err("Failed to register callback: {s}", .{@errorName(err)}); }; } pub fn onChangeDisarm(capi_ptr: ?*CApi, cb: OnChange.Fn) callconv(.C) void {
-
-
-
@@ -150,11 +150,13 @@plac_app_server_zone_playback_state const playback_state; } plac_app_server_zone; void plac_app_server_zone_on_change(plac_app_server_zone*, void (*)(void*), void*); void plac_app_server_zone_on_change_disarm(plac_app_server_zone*, void (*)(void*)); typedef void (*plac_cb_zone_change)(void*); void plac_app_server_zone_on_change(plac_app_server_zone*, plac_cb_zone_change, void*); void plac_app_server_zone_on_change_disarm(plac_app_server_zone*, plac_cb_zone_change); void plac_app_server_zone_on_remove(plac_app_server_zone*, void (*)(void*), void*); void plac_app_server_zone_on_remove_disarm(plac_app_server_zone*, void (*)(void*)); typedef void (*plac_cb_zone_remove)(void*); void plac_app_server_zone_on_remove(plac_app_server_zone*, plac_cb_zone_remove, void*); void plac_app_server_zone_on_remove_disarm(plac_app_server_zone*, plac_cb_zone_remove); /* App.Server.ZoneListLoading */
-
@@ -186,7 +188,7 @@/** * An array of zones. */ plac_app_server_zone *zones; plac_app_server_zone **zones; unsigned int const zones_len; plac_app_server_zone_list_loading const zones_loading;
-
@@ -196,11 +198,13 @@ } plac_app_server;void plac_app_server_load_zones(plac_app_server*); void plac_app_server_on_zone_add(plac_app_server*, void (*)(plac_app_server_zone*, void*), void*); void plac_app_server_on_zone_add_disarm(plac_app_server*, void (*)(plac_app_server_zone*, void*)); typedef void (*plac_cb_zone_add)(plac_app_server_zone*, void*); void plac_app_server_on_zone_add(plac_app_server*, plac_cb_zone_add, void*); void plac_app_server_on_zone_add_disarm(plac_app_server*, plac_cb_zone_add); void plac_app_server_on_zone_list_loading_change(plac_app_server*, void (*)(void*), void*); void plac_app_server_on_zone_list_loading_change_disarm(plac_app_server*, void (*)(void*)); typedef void (*plac_cb_zone_list_loading_change)(void*); void plac_app_server_on_zone_list_loading_change(plac_app_server*, plac_cb_zone_list_loading_change, void*); void plac_app_server_on_zone_list_loading_change_disarm(plac_app_server*, plac_cb_zone_list_loading_change); /* App.ServerSelector.Entry */
-
@@ -267,8 +271,9 @@/** * Appends a callback that will be invoked everytime "plac_app_server_selector" changes. */ void plac_app_server_selector_on_change(plac_app_server_selector*, void (*)(void*), void*); void plac_app_server_selector_on_change_disarm(plac_app_server_selector*, void(*)(void*)); typedef void (*plac_cb_server_selector_change)(void*); void plac_app_server_selector_on_change(plac_app_server_selector*, plac_cb_server_selector_change, void*); void plac_app_server_selector_on_change_disarm(plac_app_server_selector*, plac_cb_server_selector_change); /* App.ConnectionState */
-
@@ -370,14 +375,16 @@/** * Event fired when "server" field changes (connect/disconnect). */ void plac_app_on_server_change(plac_app*, void (*)(void*), void*); void plac_app_on_server_change_disarm(plac_app*, void (*)(void*)); typedef void (*plac_cb_server_change)(void*); void plac_app_on_server_change(plac_app*, plac_cb_server_change, void*); void plac_app_on_server_change_disarm(plac_app*, plac_cb_server_change); /** * Event fired when "connection" field changes. */ void plac_app_on_connection_change(plac_app*, void (*)(void*), void*); void plac_app_on_connection_change_disarm(plac_app*, void (*)(void*)); typedef void (*plac_cb_connection_change)(void*); void plac_app_on_connection_change(plac_app*, plac_cb_connection_change, void*); void plac_app_on_connection_change_disarm(plac_app*, plac_cb_connection_change); /* Application */ // TODO: Remove me
-
-
-
@@ -17,106 +17,170 @@ * SPDX-License-Identifier: Apache-2.0*/ [CCode (cheader_filename = "plac_core.h")] namespace Plac { [CCode (cname = "plac_server", free_function = "plac_server_free")] namespace PlacCore { [CCode ( cname = "plac_app_server_zone_playback_state", cprefix = "PLAC_APP_PLAYBACK_", has_type_id = false )] public enum ZonePlaybackState { STOPPED, PAUSED, PLAYING, } [CCode (cname = "plac_app_server_zone", free_function = "")] [Compact] public class Server { public Posix.SockAddr sockaddr; public Posix.socklen_t addrlen; public class Zone { [CCode (cname = "plac_cb_zone_change", has_target = true)] public delegate void OnChange (); [CCode (cname = "plac_cb_zone_remove", has_target = true)] public delegate void OnRemove (); public string id; [CCode (cname = "name")] public string name; public string version; public ZonePlaybackState playback_state; [CCode (cname = "plac_server_dupe")] public Server dup (); } [CCode (cname = "plac_app_server_zone_on_change")] public void on_change (OnChange f); [CCode (cname = "plac_server_scan_options", destroy_function = "", has_type_id = false)] public struct ScanOptions { uint count; uint receive_window_ms; [CCode (cname = "plac_server_scan_options_init")] public ScanOptions (); [CCode (cname = "plac_app_server_zone_on_remove")] public void on_remove (OnRemove f); } [CCode (cname = "plac_scan_result_code", cprefix = "PLAC_SCAN_", has_type_id = false)] public enum ScanResultCode { OK, UNKNOWN_ERROR, OUT_OF_MEMORY, SOCKET_SETUP_ERROR, UDP_SEND_ERROR, UDP_RECV_ERROR, NULL_POINTER_ARGS, SOCKET_PERMISSION_ERROR, TOO_MANY_SOCKET_ERROR, INVALID_RECEIVE_WINDOW, NETWORK_UNAVAILABLE, [CCode ( cname = "plac_app_server_zone_list_loading", cprefix = "PLAC_APP_SERVER_ZONE_LIST_LOADING_", has_type_id = false )] public enum ZoneListLoading { NOT_LOADED, LOADING, LOADED, REFRESHING, ERR_UNEXPECTED, ERR_THREAD_SPAWN, ERR_OUT_OF_MEMORY, ERR_NON_SUCCESS, } [CCode (cname = "plac_scan_result", free_function = "plac_scan_result_free")] [CCode (cname = "plac_app_server", free_function = "")] [Compact] public class ScanResult { public ScanResultCode code; public class Server { [CCode (cname = "plac_cb_zone_add", has_target = true)] public delegate void OnZoneAdd (Zone added); [CCode (cname = "plac_scan_result_next")] public Server? next (); [CCode (cname = "plac_cb_zone_list_loading_change", has_target = true)] public delegate void OnZoneListLoadingChange (); [CCode (cname = "plac_scan_result_reset")] public void reset (); public string id; public string name; public string version; [CCode (array_length_cname = "zones_len", array_length_type = "unsigned int")] public Zone[] zones; public ZoneListLoading zones_loading; public string token; [CCode (cname = "plac_app_server_load_zones")] public void load_zones (); [CCode (cname = "plac_app_server_on_zone_add")] public void on_zone_add (OnZoneAdd f); [CCode (cname = "plac_app_server_on_zone_list_loading_change")] public void on_zone_list_loading_change (OnZoneListLoadingChange f); } [CCode (cname = "plac_find_result", free_function = "plac_find_result_free")] [CCode (cname = "plac_app_server_selector_entry", free_function = "")] [Compact] public class FindResult { public ScanResultCode code; public class ServerSelectorEntry { public string id; public string name; public string version; } public Server? server; [CCode ( cname = "plac_app_server_selector_state", cprefix = "PLAC_APP_SERVER_SELECTOR_", has_type_id = false )] public enum ServerSelectorState { NOT_LOADED, LOADING, LOADED, REFRESHING, ERR_UNEXPECTED, ERR_NETWORK_UNAVAILABLE, ERR_SOCKET_PERMISSION, ERR_OUT_OF_MEMORY, ERR_SOCKET, ERR_THREAD_SPAWN, } [CCode (cname = "plac_server_scanner", free_function = "plac_server_scanner_free")] [CCode (cname = "plac_app_server_selector", free_function = "")] [Compact] public class ServerScanner { [CCode (cname = "plac_server_scanner_make")] public ServerScanner (); public class ServerSelector { [CCode (cname = "plac_cb_server_selector_change", has_target = true)] public delegate void OnChange (); [CCode (cname = "plac_server_scanner_scan")] public ScanResult? scan (ScanOptions opts); public ServerSelectorState state; [CCode (array_length_cname = "entries_len", array_length_type = "unsigned int")] public ServerSelectorEntry[] entries; [CCode (cname = "plac_server_scanner_find")] public FindResult? find (string id, uint len); } [CCode (cname = "plac_app_server_selector_reset")] public void reset(); [CCode (cname = "plac_application_connect_result_code", cprefix = "PLAC_APPLICATION_CONNECT_", has_type_id = false)] public enum ConnectResultCode { OK, UNKNOWN_ERROR, NULL_SELF_POINTER, OUT_OF_MEMORY, REGISTRY_DOWN, FAILED_TO_REGISTER, WEBSOCKET_CONNECTION_ERROR, LISTEN_THREAD_SPAWN_ERROR, [CCode (cname = "plac_app_server_selector_load")] public void load(); [CCode (cname = "plac_app_server_selector_on_change")] public void on_change (OnChange f); } [CCode (cname = "plac_application_connect_result", free_function = "plac_application_connect_result_free")] [Compact] public class ConnectResult { public ConnectResultCode code; public string token; [CCode ( cname = "plac_app_connection_state", cprefix = "PLAC_APP_CONNECTION_", has_type_id = false )] public enum ConnectionState { IDLE, BUSY, ERR_UNEXPECTED, ERR_NETWORK_UNAVAILABLE, ERR_SOCKET_PERMISSION, ERR_OUT_OF_MEMORY, ERR_SOCKET, ERR_REGISTRY_DOWN, ERR_FAILED_TO_REGISTER, ERR_THREAD_SPAWN, ERR_WEBSOCKET, ERR_NOT_FOUND, } [CCode (cname = "plac_application", free_function = "plac_application_free")] [CCode (cname = "plac_app", free_function = "plac_app_destroy")] [Compact] public class CoreApp { [CCode (cname = "plac_application_make_from_server")] public CoreApp (Server server); public class App { [CCode (cname = "plac_cb_server_change", has_target = true)] public delegate void OnServerChange (); [CCode (cname = "plac_cb_connection_change", has_target = true)] public delegate void OnConnectionChange (); [CCode (cname = "plac_application_connect")] public ConnectResult? connect (string? token); [CCode (cname = "plac_app_new")] public App (); public ServerSelector server_selector; public ConnectionState connection; public Server? server; [CCode (cname = "plac_app_connect")] public void connect(string* server_id, uint server_id_len, string? saved_token, uint saved_token_len); [CCode (cname = "plac_app_on_server_change")] public void on_server_change (OnServerChange f); [CCode (cname = "plac_app_on_connection_change")] public void on_connection_change (OnConnectionChange f); } }
-
-
-
@@ -101,6 +101,14 @@ for (vala_sources) |src| {valac.addFileArg(b.path(src)); } const t = b.addInstallDirectory(.{ .source_dir = dir, .install_dir = .{ .custom = "src" }, .install_subdir = "", }); const step = b.step("valac", "Compile C source code from Vala files for debugging purpose"); step.dependOn(&t.step); break :vala dir; };
-
-
-
@@ -17,7 +17,7 @@SPDX-License-Identifier: Apache-2.0 --> <interface> <template class="PlacGenericErrorDialog" parent="AdwDialog"> <template class="PlacGtkAdwaitaGenericErrorDialog" parent="AdwDialog"> <property name="content-width">400</property> <child> <object class="AdwToolbarView">
-
-
-
@@ -17,7 +17,7 @@SPDX-License-Identifier: Apache-2.0 --> <interface> <template class="PlacMainWindow" parent="AdwApplicationWindow"> <template class="PlacGtkAdwaitaMainWindow" parent="AdwApplicationWindow"> <property name="default-width">800</property> <property name="default-height">640</property> <property name="title">Plac</property>
-
-
-
@@ -17,7 +17,7 @@SPDX-License-Identifier: Apache-2.0 --> <interface> <template class="PlacServerListNetworkErrorDialog" parent="PlacGenericErrorDialog"> <template class="PlacGtkAdwaitaServerListNetworkErrorDialog" parent="PlacGtkAdwaitaGenericErrorDialog"> <property name="title">Failed to scan Roon Servers</property> <property name="description">Unable to scan Roon Servers due to a network error. Check network connection and this software has permissions for network access.</property> </template>
-
-
-
@@ -17,7 +17,7 @@SPDX-License-Identifier: Apache-2.0 --> <interface> <template class="PlacServerListUnexpectedErrorDialog" parent="PlacGenericErrorDialog"> <template class="PlacGtkAdwaitaServerListUnexpectedErrorDialog" parent="PlacGtkAdwaitaGenericErrorDialog"> <property name="title">Failed to scan Roon Servers</property> <property name="description">Encountered an unexpected error during scan operation. Scan again to see if the problem resolves.</property> </template>
-
-
-
@@ -17,7 +17,7 @@SPDX-License-Identifier: Apache-2.0 --> <interface> <template class="PlacServerSelectorWindow" parent="AdwApplicationWindow"> <template class="PlacGtkAdwaitaServerSelectorWindow" parent="AdwApplicationWindow"> <property name="default-width">300</property> <property name="default-height">300</property> <property name="title">Select Roon Server</property>
-
-
-
@@ -14,18 +14,21 @@ // limitations under the License.// // SPDX-License-Identifier: Apache-2.0 namespace Plac { namespace PlacGtkAdwaita { public class App : Adw.Application { private PlacCore.App app; public App(){ Object( application_id: "jp.pocka.plac.gtk-adwaita", flags : ApplicationFlags.DEFAULT_FLAGS ); this.app = new PlacCore.App(); } protected override void activate() { var main_window = new ServerSelectorWindow(this); main_window.present(); var selector = new ServerSelectorWindow(this, app); selector.start(); } public static int main(string[] args) {
-
@@ -55,7 +58,7 @@ Object();} } public errordomain ScanError { private enum ScanErrorKind { UNEXPECTED_ERROR, NETWORK_ERROR, }
-
@@ -79,135 +82,111 @@ private unowned Adw.StatusPage empty;private ulong error_detail_hid; public ServerSelectorWindow(Gtk.Application app) { Object(application: app); } construct { scan_servers(); private unowned PlacCore.App core; var scan_action = new SimpleAction("scan_servers", null); scan_action.activate.connect(this.scan_servers); this.add_action(scan_action); public ServerSelectorWindow(Gtk.Application app, PlacCore.App core) { Object(application: app); this.core = core; } private void scan_servers() { failure_banner.revealed = false; if (error_detail_hid > 0) { failure_banner.disconnect(error_detail_hid); error_detail_hid = 0; } stack.visible_child_name = "loading"; scan_button.set_sensitive(false); scan_servers_async.begin((_obj, res) => { try { var servers = scan_servers_async.end(res); servers_list.remove_all(); public void start() { core.server_selector.on_change(() => { switch (core.server_selector.state) { case PlacCore.ServerSelectorState.REFRESHING: case PlacCore.ServerSelectorState.LOADING: failure_banner.revealed = false; if (error_detail_hid > 0) { failure_banner.disconnect(error_detail_hid); error_detail_hid = 0; } stack.visible_child_name = "loading"; scan_button.set_sensitive(false); break; case PlacCore.ServerSelectorState.NOT_LOADED: break; case PlacCore.ServerSelectorState.LOADED: servers_list.remove_all(); if (servers.length == 0) { servers_list.visible = false; empty.visible = true; scan_button.add_css_class("suggested-action"); } else { servers_list.visible = true; empty.visible = false; scan_button.remove_css_class("suggested-action"); } if (core.server_selector.entries.length == 0) { servers_list.visible = false; empty.visible = true; scan_button.add_css_class("suggested-action"); } else { servers_list.visible = true; empty.visible = false; scan_button.remove_css_class("suggested-action"); } foreach (unowned Plac.Server server in servers) { var row = new Adw.ActionRow(); row.title = server.name; row.subtitle = server.version; foreach (unowned PlacCore.ServerSelectorEntry entry in core.server_selector.entries) { var row = new Adw.ActionRow(); row.title = entry.name; row.subtitle = entry.version; var main_window = new MainWindow(this.application, server); row.activatable_widget = main_window; var main_window = new MainWindow(this.application, core, entry); row.activatable_widget = main_window; row.activated.connect(() => { row.activated.connect(() => { main_window.start(); main_window.present(); core.connect(entry.id, entry.id.length, null, 0); this.close(); }); servers_list.append(row); } } catch (ScanError e) { // Have to capture, closure can't access `e` here (why? idk). var message = e.message; var is_network_error = e is ScanError.NETWORK_ERROR; servers_list.append(row); } scan_button.add_css_class("suggested-action"); stack.visible_child_name = "idle"; scan_button.set_sensitive(true); this.error_detail_hid = failure_banner.button_clicked.connect(() => { if (is_network_error) { var dialog = new ServerListNetworkErrorDialog(message); dialog.present(this); } else { var dialog = new ServerListUnexpectedErrorDialog(message); dialog.present(this); } }); failure_banner.revealed = true; } finally { stack.visible_child_name = "idle"; scan_button.set_sensitive(true); break; case PlacCore.ServerSelectorState.ERR_UNEXPECTED: show_error(ScanErrorKind.UNEXPECTED_ERROR, "Unexpected error"); break; case PlacCore.ServerSelectorState.ERR_NETWORK_UNAVAILABLE: show_error(ScanErrorKind.NETWORK_ERROR, "Network unavailable"); break; case PlacCore.ServerSelectorState.ERR_SOCKET_PERMISSION: show_error(ScanErrorKind.NETWORK_ERROR, "No permission to create UDP socket"); break; case PlacCore.ServerSelectorState.ERR_OUT_OF_MEMORY: show_error(ScanErrorKind.UNEXPECTED_ERROR, "Out of memory"); break; case PlacCore.ServerSelectorState.ERR_SOCKET: show_error(ScanErrorKind.NETWORK_ERROR, "Failed to operate on a socket"); break; case PlacCore.ServerSelectorState.ERR_THREAD_SPAWN: show_error(ScanErrorKind.UNEXPECTED_ERROR, "Unable to spawn a thread"); break; } }); } private async Plac.Server[] scan_servers_async() throws ScanError { SourceFunc callback = scan_servers_async.callback; Plac.Server[] servers = {}; Plac.ScanResultCode code = Plac.ScanResultCode.UNKNOWN_ERROR; ThreadFunc<void>run = () => { var opts = Plac.ScanOptions(); opts.count = 4; opts.receive_window_ms = 1300; var scanner = new Plac.ServerScanner(); var result = scanner.scan(opts); code = result.code; core.server_selector.load(); while (true) { var next = result.next(); if (next == null) { Idle.add((owned) callback); return; } var scan_action = new SimpleAction("scan_servers", null); scan_action.activate.connect(core.server_selector.load); this.add_action(scan_action); servers += (owned) next; } }; this.present(); } new Thread<void>("server_scanner", (owned) run); yield; private void show_error(ScanErrorKind kind, string message) { scan_button.add_css_class("suggested-action"); switch (code) { case Plac.ScanResultCode.OK: break; case Plac.ScanResultCode.NETWORK_UNAVAILABLE: throw new ScanError.NETWORK_ERROR("Network unavailable"); case Plac.ScanResultCode.SOCKET_SETUP_ERROR: case Plac.ScanResultCode.TOO_MANY_SOCKET_ERROR: throw new ScanError.NETWORK_ERROR("Unable to setup UDP socket"); case Plac.ScanResultCode.SOCKET_PERMISSION_ERROR: throw new ScanError.NETWORK_ERROR("No permission to open UDP socket"); case Plac.ScanResultCode.UDP_RECV_ERROR: throw new ScanError.NETWORK_ERROR("Unable to receive UDP packet"); case Plac.ScanResultCode.UDP_SEND_ERROR: throw new ScanError.NETWORK_ERROR("Unable to send UDP packet"); case Plac.ScanResultCode.OUT_OF_MEMORY: throw new ScanError.UNEXPECTED_ERROR("Out of memory"); case Plac.ScanResultCode.UNKNOWN_ERROR: case Plac.ScanResultCode.INVALID_RECEIVE_WINDOW: case Plac.ScanResultCode.NULL_POINTER_ARGS: default: throw new ScanError.UNEXPECTED_ERROR("Unexpected error"); } return (owned) servers; this.error_detail_hid = failure_banner.button_clicked.connect(() => { switch (kind) { case ScanErrorKind.NETWORK_ERROR: var dialog = new ServerListNetworkErrorDialog(message); dialog.present(this); break; case ScanErrorKind.UNEXPECTED_ERROR: var dialog = new ServerListUnexpectedErrorDialog(message); dialog.present(this); break; } }); failure_banner.revealed = true; stack.visible_child_name = "idle"; scan_button.set_sensitive(true); } }
-
@@ -230,36 +209,44 @@ class MainWindow : Adw.ApplicationWindow {[GtkChild] private unowned Gtk.Label test_label; private Plac.Server server; private Plac.CoreApp app; private unowned PlacCore.App core; private unowned PlacCore.ServerSelectorEntry entry; public MainWindow(Gtk.Application app, Plac.Server server) { public MainWindow(Gtk.Application app, PlacCore.App core, PlacCore.ServerSelectorEntry entry) { Object(application: app); this.server = server.dup(); this.entry = entry; this.core = core; } // NOTE: `server` is `null` in `construct` block. Stupid constructor design forces // manual memory management or ditching constructor. Here, I chose the latter // because window is hidden by default and startup procedures should start // once after the window became visible. public void start() { test_label.label = this.server.name; test_label.label = this.entry.name; core.on_connection_change(() => { switch (core.connection) { case PlacCore.ConnectionState.IDLE: idle_render(); break; case PlacCore.ConnectionState.BUSY: test_label.label = "Loading"; break; default: test_label.label = core.connection.to_string(); break; } }); } this.app = new Plac.CoreApp(this.server); if (this.app != null) { var result = this.app.connect(null); if (result.code != Plac.ConnectResultCode.OK) { stderr.printf("Unable to connect (%d)\n", result.code); return; } private void idle_render() { if (core.server == null) { return; } if (result.token == null) { stderr.printf("Connected, but got null token\n"); return; } if (core.server.id != entry.id) { // TODO: Log or do something? this.close(); return; } stderr.printf("Got token: %s\n", result.token); } test_label.label = core.server.name; } } }
-