Introduce an event loop mode in wayvncctl

Signed-off-by: Jim Ramsay <i.am@jimramsay.com>
pull/183/head
Jim Ramsay 2022-11-09 09:11:21 -05:00 committed by Andri Yngvason
parent debd8a67cb
commit d8239109e5
4 changed files with 149 additions and 8 deletions

View File

@ -52,6 +52,8 @@ struct jsonipc_request* jsonipc_request_parse_new(json_t* root,
struct jsonipc_error* err);
struct jsonipc_request* jsonipc_request_new(const char* method, json_t* params);
struct jsonipc_request* jsonipc_event_new(const char* method, json_t* params);
struct jsonipc_request* jsonipc_event_parse_new(json_t* root,
struct jsonipc_error* err);
json_t* jsonipc_request_pack(struct jsonipc_request*, json_error_t* err);
void jsonipc_request_destroy(struct jsonipc_request*);

View File

@ -200,6 +200,8 @@ static json_t* read_one_object(struct ctl_client* self, int timeout_ms)
while (root == NULL) {
int n = poll(&pfd, 1, timeout_ms);
if (n == -1) {
if (errno == EINTR && self->wait_for_events)
continue;
WARN("Error waiting for a response: %m");
break;
} else if (n == 0) {
@ -395,17 +397,121 @@ static void setup_signals(struct ctl_client* self)
sigaction(SIGTERM, &sa, NULL);
}
static void ctl_client_event_loop(struct ctl_client* self)
static void print_indent(int level)
{
for (int i = 0; i < level; ++i)
printf(" ");
}
static bool json_has_content(json_t* root)
{
if (!root)
return false;
size_t i;
const char* key;
json_t* value;
switch (json_typeof(root)) {
case JSON_NULL:
return false;
case JSON_INTEGER:
case JSON_REAL:
case JSON_TRUE:
case JSON_FALSE:
return true;
case JSON_STRING:
return json_string_value(root)[0] != '\0';
case JSON_OBJECT:
json_object_foreach(root, key, value)
if (json_has_content(value))
return true;
return false;
case JSON_ARRAY:
json_array_foreach(root, i, value)
if (json_has_content(value))
return true;
return false;
}
return false;
}
static void print_as_yaml(json_t* data, int level, bool needs_leading_newline)
{
size_t i;
const char* key;
json_t* value;
bool needs_indent = needs_leading_newline;
switch(json_typeof(data)) {
case JSON_NULL:
printf("<null>\n");
break;
case JSON_OBJECT:
if (json_object_size(data) > 0 && needs_leading_newline)
printf("\n");
json_object_foreach(data, key, value) {
if (!json_has_content(value))
continue;
if (needs_indent)
print_indent(level);
else
needs_indent = true;
printf("%s: ", key);
print_as_yaml(value, level + 1, true);
}
break;
case JSON_ARRAY:
if (json_array_size(data) > 0 && needs_leading_newline)
printf("\n");
json_array_foreach(data, i, value) {
if (!json_has_content(value))
continue;
print_indent(level);
printf("- ");
print_as_yaml(value, level + 1, json_is_array(value));
}
break;
case JSON_STRING:
printf("%s\n", json_string_value(data));
break;
case JSON_INTEGER:
printf("%" JSON_INTEGER_FORMAT "\n", json_integer_value(data));
break;
case JSON_REAL:
printf("%f\n", json_real_value(data));
break;
case JSON_TRUE:
printf("true\n");
break;
case JSON_FALSE:
printf("false\n");
break;
}
}
static void print_event(struct jsonipc_request* event, unsigned flags)
{
if (flags & PRINT_JSON) {
print_compact_json(event->json);
} else {
printf("\n%s:", event->method);
print_as_yaml(event->params, 1, true);
}
fflush(stdout);
}
static void ctl_client_event_loop(struct ctl_client* self, unsigned flags)
{
self->wait_for_events = true;
setup_signals(self);
while (self->wait_for_events) {
DEBUG("Waiting for an event");
json_t* root = read_one_object(self, -1);
json_dumpf(root, stdout, 0);
printf("\n");
fflush(stdout);
if (!root)
break;
struct jsonipc_error err = JSONIPC_ERR_INIT;
struct jsonipc_request* event = jsonipc_event_parse_new(root, &err);
json_decref(root);
print_event(event, flags);
jsonipc_request_destroy(event);
}
}
@ -427,7 +533,7 @@ int ctl_client_run_command(struct ctl_client* self,
result = ctl_client_print_response(self, request, response, flags);
if (result == 0 && strcmp(request->method, "event-receive") == 0)
ctl_client_event_loop(self);
ctl_client_event_loop(self, flags);
jsonipc_response_destroy(response);
receive_failure:

View File

@ -111,6 +111,12 @@ struct jsonipc_request* jsonipc_event_new(const char* method, json_t* params)
return jsonipc_request__new(method, params, NULL);
}
struct jsonipc_request* jsonipc_event_parse_new(json_t* root,
struct jsonipc_error* err)
{
return jsonipc_request_parse_new(root, err);
}
json_t* jsonipc_request_pack(struct jsonipc_request* self, json_error_t* err)
{
return json_pack_ex(err, 0, "{s:s, s:O*, s:O*}",

View File

@ -46,9 +46,36 @@ command and its available parameters.
While *wayvncctl* normally terminates after sending one request and receiving
the corresponding reply, the *event-receive* command acts differently. Instead
of exiting immediately, *wayvncctl* waits for any events fr the server, printing
each to stdout as they arrive. This mode of operation will block until either
it receives a signal to terminate, or until the wayvnc server terminates.
of exiting immediately, *wayvncctl* waits for any events from the server,
printing each to stdout as they arrive. This mode of operation will block until
either it receives a signal to terminate, or until the wayvnc server terminates.
In _--json_ mode, each event is printed on one line, with a newline character at
the end, for ease in scripting:
```
$ wayvncctl --json event-receive
{"method":"client-connected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":1}}
{"method":"client-disconnected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":0}}
```
The default human-readible output is a multi-line yaml-like format, with two
newline characters between each event:
```
$ wayvncctl event-receive
client-connected:
id: 0x10ef670
hostname: 192.168.1.18
connection_count: 1
client-disconnected:
id: 0x10ef670
hostname: 192.168.1.18
connection_count: 0
```
# EXAMPLES