Handle all wayvncctl help commands locally

This means that wayvncctl can now provide full descriptions of all
commands and events without wayvnc running.

Signed-off-by: Jim Ramsay <i.am@jimramsay.com>
pull/201/head
Jim Ramsay 2022-12-06 16:45:51 -05:00
parent d75ca4bf51
commit cb116cc980
4 changed files with 138 additions and 104 deletions

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
struct ctl_client; struct ctl_client;
@ -26,9 +27,12 @@ struct ctl_client* ctl_client_new(const char* socket_path, void* userdata);
void ctl_client_destroy(struct ctl_client*); void ctl_client_destroy(struct ctl_client*);
void* ctl_client_userdata(struct ctl_client*); void* ctl_client_userdata(struct ctl_client*);
#define PRINT_JSON 0x00000001 #define CTL_CLIENT_PRINT_JSON (1 << 0)
#define RECONNECT 0x00000002 #define CTL_CLIENT_SOCKET_WAIT (1 << 1)
#define CTL_CLIENT_RECONNECT (1 << 2)
int ctl_client_connect(struct ctl_client* self, int timeout);
int ctl_client_run_command(struct ctl_client* self, int ctl_client_run_command(struct ctl_client* self,
int argc, char* argv[], unsigned flags); int argc, char* argv[], unsigned flags);
void ctl_client_print_command_list(FILE* stream);
void ctl_client_print_event_list(FILE* stream);

View File

@ -144,7 +144,7 @@ static int try_connect(struct ctl_client* self, int timeout)
return 0; return 0;
} }
int ctl_client_connect(struct ctl_client* self, int timeout) static int ctl_client_connect(struct ctl_client* self, int timeout)
{ {
// TODO: Support arbitrary timeouts? // TODO: Support arbitrary timeouts?
assert(timeout == 0 || timeout == -1); assert(timeout == 0 || timeout == -1);
@ -311,79 +311,6 @@ out:
printf("\n"); printf("\n");
} }
static void print_command_usage(const char* name, json_t* data)
{
char* desc = NULL;
json_t* params = NULL;
json_unpack(data, "{s:s, s?o}", "description", &desc,
"params", &params);
printf("Usage: wayvncctl [options] %s%s\n\n%s\n", name,
params ? " [params]" : "",
desc);
if (params) {
printf("\nParameters:");
const char* param_name;
json_t* param_value;
json_object_foreach(params, param_name, param_value) {
printf("\n --%s=...\n %s\n", param_name,
json_string_value(param_value));
}
}
printf("\nRun 'wayvncctl --help' for allowed Options\n");
}
static void print_event_details(const char* name, json_t* data)
{
char* desc = NULL;
json_t* params = NULL;
json_unpack(data, "{s:s, s?o}", "description", &desc,
"params", &params);
printf("Event: %s\n\n%s\n", name,
desc);
if (params) {
printf("\nParameters:");
const char* param_name;
json_t* param_value;
json_object_foreach(params, param_name, param_value) {
printf("\n %s:...\n %s\n", param_name,
json_string_value(param_value));
}
}
}
static void print_help(json_t* data, json_t* request)
{
if (json_object_get(data, "commands")) {
printf("Allowed commands:\n");
json_t* cmd_list = json_object_get(data, "commands");
size_t index;
json_t* value;
json_array_foreach(cmd_list, index, value) {
printf(" - %s\n", json_string_value(value));
}
printf("\nRun 'wayvncctl command-name --help' for command-specific details.\n");
printf("\nSupported events:\n");
json_t* evt_list = json_object_get(data, "events");
json_array_foreach(evt_list, index, value) {
printf(" - %s\n", json_string_value(value));
}
printf("\nRun 'wayvncctl help --event=event-name' for event-specific details.\n");
return;
}
bool is_command = json_object_get(request, "command");
const char* key;
json_t* value;
json_object_foreach(data, key, value) {
if (is_command)
print_command_usage(key, value);
else
print_event_details(key, value);
}
}
static void pretty_version(json_t* data) static void pretty_version(json_t* data)
{ {
printf("wayvnc is running:\n"); printf("wayvnc is running:\n");
@ -441,9 +368,6 @@ static void pretty_print(json_t* data,
{ {
enum cmd_type cmd = ctl_command_parse_name(request->method); enum cmd_type cmd = ctl_command_parse_name(request->method);
switch (cmd) { switch (cmd) {
case CMD_HELP:
print_help(data, request->params);
break;
case CMD_VERSION: case CMD_VERSION:
pretty_version(data); pretty_version(data);
break; break;
@ -459,7 +383,8 @@ static void pretty_print(json_t* data,
printf("Ok\n"); printf("Ok\n");
break; break;
case CMD_EVENT_RECEIVE: case CMD_EVENT_RECEIVE:
abort(); // Event loop code handles this one case CMD_HELP:
abort(); // Handled directly by ctl_client_run_command
case CMD_UNKNOWN: case CMD_UNKNOWN:
json_dumpf(data, stdout, JSON_INDENT(2)); json_dumpf(data, stdout, JSON_INDENT(2));
} }
@ -477,7 +402,7 @@ static int ctl_client_print_response(struct ctl_client* self,
{ {
DEBUG("Response code: %d", response->code); DEBUG("Response code: %d", response->code);
if (response->data) { if (response->data) {
if (self->flags & PRINT_JSON) if (self->flags & CTL_CLIENT_PRINT_JSON)
print_compact_json(response->data); print_compact_json(response->data);
else if (response->code == 0) else if (response->code == 0)
pretty_print(response->data, request); pretty_print(response->data, request);
@ -594,7 +519,7 @@ static void print_as_yaml(json_t* data, int level, bool needs_leading_newline)
static void print_event(struct jsonipc_request* event, unsigned flags) static void print_event(struct jsonipc_request* event, unsigned flags)
{ {
if (flags & PRINT_JSON) { if (flags & CTL_CLIENT_PRINT_JSON) {
print_compact_json(event->json); print_compact_json(event->json);
} else { } else {
printf("\n%s:", event->method); printf("\n%s:", event->method);
@ -641,7 +566,7 @@ static ssize_t ctl_client_send_request(struct ctl_client* self,
} }
static struct jsonipc_response* ctl_client_run_single_command(struct ctl_client* self, static struct jsonipc_response* ctl_client_run_single_command(struct ctl_client* self,
struct jsonipc_request *request) struct jsonipc_request* request)
{ {
if (ctl_client_send_request(self, request) < 0) if (ctl_client_send_request(self, request) < 0)
return NULL; return NULL;
@ -686,7 +611,7 @@ static int ctl_client_event_loop(struct ctl_client* self,
if (!root) { if (!root) {
if (errno == ECONNRESET) { if (errno == ECONNRESET) {
send_shutdown_event(self); send_shutdown_event(self);
if (self->flags & RECONNECT && if (self->flags & CTL_CLIENT_RECONNECT &&
ctl_client_reconnect_event_loop( ctl_client_reconnect_event_loop(
self, request, -1) == 0) self, request, -1) == 0)
continue; continue;
@ -714,6 +639,93 @@ static int ctl_client_print_single_command(struct ctl_client* self,
return result; return result;
} }
void ctl_client_print_command_list(FILE* stream)
{
fprintf(stream, "Commands:\n");
for (size_t i = 0; i < CMD_LIST_LEN; ++i)
fprintf(stream, " - %s\n", ctl_command_list[i].name);
fprintf(stream, "\nRun 'wayvncctl command-name --help' for command-specific details.\n");
}
void ctl_client_print_event_list(FILE* stream)
{
printf("Events:\n");
for (size_t i = 0; i < EVT_LIST_LEN; ++i)
printf(" - %s\n", ctl_event_list[i].name);
printf("\nRun 'wayvncctl help --event=event-name' for event-specific details.\n");
}
static void print_cmd_info_params(const struct cmd_info* info)
{
if (info->params[0].name != NULL) {
printf("\nParameters:");
for (int i = 0; info->params[i].name != NULL; ++i)
printf("\n --%s=...\n %s\n", info->params[i].name,
info->params[i].description);
}
}
static int print_command_usage(const char* cmd_name)
{
enum cmd_type type = ctl_command_parse_name(cmd_name);
struct cmd_info* info = ctl_command_by_type(type);
if (!info) {
WARN("No such command \"%s\"\n", cmd_name);
return 1;
}
bool params = info->params[0].name != NULL;
printf("Usage: wayvncctl [options] %s%s\n\n%s\n", info->name,
params ? " [params]" : "",
info->description);
print_cmd_info_params(info);
printf("\nRun 'wayvncctl --help' for allowed options\n");
if (type == CMD_EVENT_RECEIVE) {
printf("\n");
ctl_client_print_event_list(stdout);
}
return 0;
}
static int print_event_details(const char* evt_name)
{
struct cmd_info* info = ctl_event_by_name(evt_name);
if (!info) {
WARN("No such event \"%s\"\n", evt_name);
return 1;
}
printf("Event: %s\n\n%s\n", info->name,
info->description);
print_cmd_info_params(info);
return 0;
}
static int ctl_client_print_help(struct ctl_client* self,
struct jsonipc_request* request)
{
if (self->flags & CTL_CLIENT_PRINT_JSON) {
WARN("JSON output is not supported for the \"help\" command");
return 1;
}
json_t* params = request->params;
const char* cmd_name = NULL;
const char* evt_name = NULL;
if (params)
json_unpack(params, "{s?s, s?s}", "command", &cmd_name,
"event", &evt_name);
if (cmd_name)
return print_command_usage(cmd_name);
if (evt_name)
return print_event_details(evt_name);
ctl_client_print_command_list(stdout);
printf("\n");
ctl_client_print_event_list(stdout);
return 0;
}
int ctl_client_run_command(struct ctl_client* self, int ctl_client_run_command(struct ctl_client* self,
int argc, char* argv[], unsigned flags) int argc, char* argv[], unsigned flags)
{ {
@ -725,7 +737,17 @@ int ctl_client_run_command(struct ctl_client* self,
goto parse_failure; goto parse_failure;
enum cmd_type cmd = ctl_command_parse_name(request->method); enum cmd_type cmd = ctl_command_parse_name(request->method);
if (cmd != CMD_HELP) {
int timeout = (flags & CTL_CLIENT_SOCKET_WAIT) ? -1 : 0;
result = ctl_client_connect(self, timeout);
if (result != 0)
return result;
}
switch (cmd) { switch (cmd) {
case CMD_HELP:
result = ctl_client_print_help(self, request);
break;
case CMD_EVENT_RECEIVE: case CMD_EVENT_RECEIVE:
result = ctl_client_event_loop(self, request); result = ctl_client_event_loop(self, request);
break; break;

View File

@ -47,11 +47,11 @@ static int wayvncctl_usage(FILE* stream, struct option_parser* options, int rc)
static const char* usage = static const char* usage =
"Usage: wayvncctl [options] [command [--param1=value1 ...]]\n" "Usage: wayvncctl [options] [command [--param1=value1 ...]]\n"
"\n" "\n"
"Connects to and interacts with a running wayvnc instance." "Connects to and interacts with a running wayvnc instance.";
"\n"
"Try 'wayvncctl help' for a list of available commands.";
fprintf(stream, "%s\n\n", usage); fprintf(stream, "%s\n\n", usage);
option_parser_print_options(options, stream); option_parser_print_options(options, stream);
fprintf(stream, "\n");
ctl_client_print_command_list(stream);
return rc; return rc;
} }
@ -67,7 +67,6 @@ int main(int argc, char* argv[])
bool verbose = false; bool verbose = false;
const char* socket_path = NULL; const char* socket_path = NULL;
int wait_for_socket = 0;
unsigned flags = 0; unsigned flags = 0;
@ -103,13 +102,13 @@ int main(int argc, char* argv[])
socket_path = optarg; socket_path = optarg;
break; break;
case 'w': case 'w':
wait_for_socket = -1; flags |= CTL_CLIENT_SOCKET_WAIT;
break; break;
case 'r': case 'r':
flags |= RECONNECT; flags |= CTL_CLIENT_RECONNECT;
break; break;
case 'j': case 'j':
flags |= PRINT_JSON; flags |= CTL_CLIENT_PRINT_JSON;
break; break;
case 'v': case 'v':
verbose = true; verbose = true;
@ -133,13 +132,8 @@ int main(int argc, char* argv[])
if (!self.ctl) if (!self.ctl)
goto ctl_client_failure; goto ctl_client_failure;
int result = ctl_client_connect(self.ctl, wait_for_socket); int result = ctl_client_run_command(self.ctl, argc, argv, flags);
if (result != 0)
goto socket_failure;
result = ctl_client_run_command(self.ctl, argc, argv, flags);
socket_failure:
ctl_client_destroy(self.ctl); ctl_client_destroy(self.ctl);
return result; return result;

View File

@ -44,12 +44,14 @@ For a full list of currently supported commands, see
*wayvnc(1)* section _IPC COMMANDS_, or run the *wayvnc(1)* section _IPC COMMANDS_, or run the
*wayvncctl help* command. *wayvncctl help* command.
Running *wayvncctl help* contacts the server over the control socket and returns Running *wayvncctl help* returns a list of the available commands and events.
a list of the available commands.
Running *wayvncctl command-name --help* returns a description of the server-side Running *wayvncctl command-name --help* returns a description of the server-side
command and its available parameters. command and its available parameters.
Running *wayvncctl help --event=event-name* returns a description of the
server-side event and expected parameters.
# ASYNCHRONOUS EVENTS # ASYNCHRONOUS EVENTS
While *wayvncctl* normally terminates after sending one request and receiving While *wayvncctl* normally terminates after sending one request and receiving
@ -109,12 +111,24 @@ Query the server for all available IPC command names:
``` ```
$ wayvncctl help $ wayvncctl help
Allowed commands: Commands:
- help - help
- version - version
- event-receive
- set-output - set-output
- get-clients
- get-outputs
- disconnect-client
- wayvnc-exit
Run 'wayvncctl command-name --help' for command-specific details. Run 'wayvncctl command-name --help' for command-specific details.
Events:
- client-connected
- client-disconnected
- capture-changed
Run 'wayvncctl help --event=event-name' for event-specific details.
``` ```
Get help on the "set-output" IPC command: Get help on the "set-output" IPC command: