-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
/*
* Copyright 2025 Shota FUJI
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
* ===
*
* C99 header file for Plac core's C API.
* This file is not checked against a generated library file: carefully write and review
* definitions and implementations.
*/
#ifndef PLAC_CORE_H
#define PLAC_CORE_H
#include <stddef.h>
#include <sys/socket.h>
/* App.Server.Zone.PlaybackState */
typedef enum {
PLAC_APP_PLAYBACK_STOPPED = 0,
PLAC_APP_PLAYBACK_PAUSED = 1,
PLAC_APP_PLAYBACK_PLAYING = 2,
} plac_app_server_zone_playback_state;
/* App.Server.Zone */
typedef struct {
void* __private;
char const * const id;
size_t const id_len;
char const * const name;
size_t const name_len;
plac_app_server_zone_playback_state const playback_state;
} plac_app_server_zone;
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*);
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, void*);
/* App.Server.ZoneListLoading */
typedef enum {
PLAC_APP_SERVER_ZONE_LIST_LOADING_NOT_LOADED = 0,
PLAC_APP_SERVER_ZONE_LIST_LOADING_LOADING = 1,
PLAC_APP_SERVER_ZONE_LIST_LOADING_LOADED = 2,
PLAC_APP_SERVER_ZONE_LIST_LOADING_REFRESHING = 3,
PLAC_APP_SERVER_ZONE_LIST_LOADING_ERR_UNEXPECTED = 4,
PLAC_APP_SERVER_ZONE_LIST_LOADING_ERR_THREAD_SPAWN = 5,
PLAC_APP_SERVER_ZONE_LIST_LOADING_ERR_OUT_OF_MEMORY = 6,
PLAC_APP_SERVER_ZONE_LIST_LOADING_ERR_NON_SUCCESS = 7,
} plac_app_server_zone_list_loading;
/* App.Server */
typedef struct {
void* __private;
char const * const id;
size_t const id_len;
char const * const name;
size_t const name_len;
char const * const version;
size_t const version_len;
/**
* An array of zones.
*/
plac_app_server_zone **zones;
size_t const zones_len;
plac_app_server_zone_list_loading const zones_loading;
char const * const token;
size_t const token_len;
} plac_app_server;
void plac_app_server_load_zones(plac_app_server*);
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*);
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, void*);
/* App.ServerSelector.Entry */
typedef struct {
void* __private;
struct sockaddr sockaddr;
socklen_t const addrlen;
char const * const id;
size_t const id_len;
char const * const name;
size_t const name_len;
const char * const version;
size_t const version_len;
} plac_app_server_selector_entry;
/* App.ServerSelector.State */
typedef enum {
PLAC_APP_SERVER_SELECTOR_NOT_LOADED = 0,
PLAC_APP_SERVER_SELECTOR_LOADING = 1,
PLAC_APP_SERVER_SELECTOR_LOADED = 2,
PLAC_APP_SERVER_SELECTOR_REFRESHING = 3,
PLAC_APP_SERVER_SELECTOR_ERR_UNEXPECTED = 4,
PLAC_APP_SERVER_SELECTOR_ERR_NETWORK_UNAVAILABLE = 5,
PLAC_APP_SERVER_SELECTOR_ERR_SOCKET_PERMISSION = 6,
PLAC_APP_SERVER_SELECTOR_ERR_OUT_OF_MEMORY = 7,
PLAC_APP_SERVER_SELECTOR_ERR_SOCKET = 8,
PLAC_APP_SERVER_SELECTOR_ERR_THREAD_SPAWN = 9,
} plac_app_server_selector_state;
/* App.ServerSelector */
typedef struct {
void* __private;
plac_app_server_selector_state const state;
/**
* An array of server entries.
*/
plac_app_server_selector_entry **entries;
size_t const entries_len;
} plac_app_server_selector;
/**
* Resets state to NOT_LOADED and Releases server entries.
* Triggers callbacks registered via "plac_app_server_selector_on_change".
* Ongoing network operation won't be canceled, but that will not update the state
* on complete.
*/
void plac_app_server_selector_reset(plac_app_server_selector*);
/**
* Starts loading of server lists on a network.
* Triggers callbacks registered via "plac_app_server_selector_on_change".
* If there is an ongoing loading task, this call will be ignored.
*/
void plac_app_server_selector_load(plac_app_server_selector*);
/**
* Locks server selector's mutex.
* Do not forget to unlock after using.
* Locking more than once from the same thread without unlocking is undefined behavior.
*/
void plac_app_server_selector_lock(plac_app_server_selector*);
/**
* Unlocks server selector's mutex.
*/
void plac_app_server_selector_unlock(plac_app_server_selector*);
/**
* Appends a callback that will be invoked everytime "plac_app_server_selector" changes.
*/
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, void*);
/* App.ConnectionState */
typedef enum {
/**
* Not connected ("server" is NULL) or already connected ("server" is not NULL).
*/
PLAC_APP_CONNECTION_IDLE = 0,
/**
* Connecting to a server.
*/
PLAC_APP_CONNECTION_BUSY = 1,
/**
* Failed to connect due to unknown reasons.
*/
PLAC_APP_CONNECTION_ERR_UNEXPECTED = 2,
/**
* This being a dedicated error because this might be resolvable by a user.
* Also, UI can serve a retry action to a user.
*/
PLAC_APP_CONNECTION_ERR_NETWORK_UNAVAILABLE = 3,
/**
* This being a dedicated error because this might be resolvable by a user.
*/
PLAC_APP_CONNECTION_ERR_SOCKET_PERMISSION = 4,
/**
* OOM.
*/
PLAC_APP_CONNECTION_ERR_OUT_OF_MEMORY = 5,
/**
* Catch-all error for socket related errors.
* Internal socket errors returned by WebSocket will use
* "PLAC_APP_CONNECTION_ERR_WEBSOCKET".
*/
PLAC_APP_CONNECTION_ERR_SOCKET = 6,
/**
* Roon Server's registry did not return healty response.
*/
PLAC_APP_CONNECTION_ERR_REGISTRY_DOWN = 7,
/**
* Unable to register Roon Extension on a server.
*/
PLAC_APP_CONNECTION_ERR_FAILED_TO_REGISTER = 8,
/**
* Unable to spawn a worker thread.
*/
PLAC_APP_CONNECTION_ERR_THREAD_SPAWN = 9,
/**
* Catch-all error for WebSocket related errors.
*/
PLAC_APP_CONNECTION_ERR_WEBSOCKET = 10,
/**
* No server matches the given ID.
*/
PLAC_APP_CONNECTION_ERR_NOT_FOUND = 11,
} plac_app_connection_state;
/* App */
typedef struct {
void* __private;
/**
* Non NULL-able.
*/
plac_app_server_selector *server_selector;
/**
* Describes "server"'s current state.
*
* If "server" is non-NULL and this field is "_BUSY", then it means the app is
* refreshing server state or connecting to an another server.
*/
plac_app_connection_state const connection;
/**
* NULL by default. Call "plac_app_connect" to fill-in.
*/
plac_app_server *server;
} plac_app;
/**
* Creates a new app instance. This merely creates a struct: caller must invoke
* functions against the struct's child fields, such as "plac_app_server_selector_load".
* Returns NULL on out-of-memory.
*/
plac_app *plac_app_new();
void plac_app_destroy(plac_app*);
/**
* Sets a filepath for application state file. Path must be an absolute path.
* If this function is not called or called but arguments are invalid (e.g. parent directory
* does not exist,) plac_app does neither write to nor read from a file.
*
* As plac_app copies pointer destination, pointer `path` does not need to be stable.
*/
void plac_app_set_state_path(plac_app*, char *path, size_t path_len);
/**
* Reconnect and restore previous state if any.
*/
void plac_app_restore_state(plac_app*);
/**
* Connects to a server.
*/
void plac_app_connect(plac_app*,
char *server_id, size_t server_id_len,
char *saved_token, size_t saved_token_len);
/**
* Locks app's mutex.
* Do not forget to unlock after using.
* Locking more than once from the same thread without unlocking is undefined behavior.
*/
void plac_app_lock(plac_app*);
/**
* Unlocks app's mutex.
*/
void plac_app_unlock(plac_app*);
/**
* Event fired when "server" field changes (connect/disconnect).
*/
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, void*);
/**
* Event fired when "connection" field changes.
*/
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, void*);
/**
* Event fired when `plac_app_restore_state` finished its job, regardless of state
* was restored or not.
*/
typedef void (*plac_cb_restore_complete)(void*);
void plac_app_on_restore_complete(plac_app*, plac_cb_restore_complete, void*);
void plac_app_on_restore_complete_disarm(plac_app*, plac_cb_restore_complete, void*);
#endif