mirror of
https://git.planet-casio.com/Lephenixnoir/fxsdk.git
synced 2024-12-28 04:23:37 +01:00
fxlink: start implementing TUI commands (mainly gintctl tests)
This commit is contained in:
parent
f83ea7e3d3
commit
cef9d21076
4 changed files with 295 additions and 73 deletions
|
@ -55,14 +55,15 @@ add_executable(fxlink
|
|||
fxlink/modes/interactive.c
|
||||
fxlink/modes/list.c
|
||||
fxlink/modes/push.c
|
||||
fxlink/modes/tui-interactive.c
|
||||
fxlink/modes/udisks2.c
|
||||
fxlink/tooling/libpng.c
|
||||
fxlink/tooling/sdl2.c
|
||||
fxlink/tooling/udisks2.c
|
||||
fxlink/tui/commands.c
|
||||
fxlink/tui/input.c
|
||||
fxlink/tui/layout.c
|
||||
fxlink/tui/render.c)
|
||||
fxlink/tui/render.c
|
||||
fxlink/tui/tui-interactive.c)
|
||||
target_link_libraries(fxlink
|
||||
PkgConfig::libpng PkgConfig::libusb PkgConfig::ncurses -lm)
|
||||
target_include_directories(fxlink PRIVATE
|
||||
|
|
240
fxlink/tui/commands.c
Normal file
240
fxlink/tui/commands.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
// ==>/[_]\ fxlink: A community communication tool for CASIO calculators. //
|
||||
// |:::| Made by Lephe' as part of the fxSDK. //
|
||||
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include "tui.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
//---
|
||||
// Parsing utilities
|
||||
//---
|
||||
|
||||
struct command {
|
||||
int argc;
|
||||
char const **argv;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static struct command parse_command(char const *input)
|
||||
{
|
||||
struct command cmd;
|
||||
cmd.argc = 0;
|
||||
cmd.argv = NULL;
|
||||
cmd.data = malloc(strlen(input) + 1);
|
||||
if(!cmd.data)
|
||||
return cmd;
|
||||
|
||||
char const *escapes1 = "\\nter";
|
||||
char const *escapes2 = "\\\n\t\e\r";
|
||||
|
||||
/* Whether a new word needs to be created at the next character */
|
||||
bool word_finished = true;
|
||||
/* Offset into cmd.data */
|
||||
int i = 0;
|
||||
|
||||
/* Read words eagerly, appending to cmd.data as we go */
|
||||
for(int j = 0; input[j]; j++) {
|
||||
int c = input[j];
|
||||
|
||||
/* Stop words at spaces */
|
||||
if(isspace(c)) {
|
||||
if(!word_finished)
|
||||
cmd.data[i++] = 0;
|
||||
word_finished = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Translate escapes */
|
||||
if(c == '\\') {
|
||||
char *p = strchr(escapes1, input[j+1]);
|
||||
if(p) {
|
||||
c = escapes2[p - escapes1];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a new word if necessary */
|
||||
if(word_finished) {
|
||||
cmd.argv = realloc(cmd.argv, (++cmd.argc) * sizeof *cmd.argv);
|
||||
cmd.argv[cmd.argc - 1] = cmd.data + i;
|
||||
word_finished = false;
|
||||
}
|
||||
|
||||
/* Copy literals */
|
||||
cmd.data[i++] = c;
|
||||
}
|
||||
|
||||
cmd.data[i++] = 0;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void dump_command(struct command const *cmd)
|
||||
{
|
||||
print(TUI.wConsole, "command(%d)", cmd->argc);
|
||||
for(int i = 0; i < cmd->argc; i++) {
|
||||
char const *arg = cmd->argv[i];
|
||||
print(TUI.wConsole, " '%s'(%d)", arg, (int)strlen(arg));
|
||||
}
|
||||
print(TUI.wConsole, "\n");
|
||||
}
|
||||
|
||||
static void free_command(struct command *cmd)
|
||||
{
|
||||
free(cmd->argv);
|
||||
free(cmd->data);
|
||||
}
|
||||
|
||||
/* Parse a list of arguments into strings/integers/etc. The format is a string
|
||||
of argument specifiers, which can be:
|
||||
s String
|
||||
d Integer
|
||||
* Other variadic arguments (integer holding first index)
|
||||
(-- will probably be expanded later.)
|
||||
Returns true if parsing succeeded, false otherwise (including if arguments
|
||||
are missing) after printing an error message. */
|
||||
static bool parse_arguments(int argc, char const **argv, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
int i = 1;
|
||||
char const *spec = fmt;
|
||||
|
||||
while(*spec) {
|
||||
if(i >= argc) {
|
||||
fprint(TUI.wConsole, FMT_RED, "error: ");
|
||||
print(TUI.wConsole, "too few arguments\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Parse an argument specifier */
|
||||
if(*spec == 's') {
|
||||
char const **ptr = va_arg(args, char const **);
|
||||
*ptr = argv[i];
|
||||
}
|
||||
else if(*spec == 'd') {
|
||||
int *ptr = va_arg(args, int *);
|
||||
char *endptr;
|
||||
long l = strtol(argv[i], &endptr, 0);
|
||||
if(*endptr) {
|
||||
fprint(TUI.wConsole, FMT_RED, "error: ");
|
||||
print(TUI.wConsole, "not a valid integer: '%s'\n", argv[i]);
|
||||
goto failure;
|
||||
}
|
||||
*ptr = l;
|
||||
}
|
||||
|
||||
spec++;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return true;
|
||||
|
||||
failure:
|
||||
va_end(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool run_no_device(int argc, char const **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void run_gintctl_command(int argc, char const **argv,
|
||||
struct fxlink_device *fdev)
|
||||
{
|
||||
if(!strcmp(argv[0], "read-long")) {
|
||||
int count = 0;
|
||||
if(!parse_arguments(argc, argv, "d", &count))
|
||||
return;
|
||||
if(count < 0 || count > 8192)
|
||||
return;
|
||||
|
||||
uint32_t *data = malloc(count * 4);
|
||||
for(int i = 0; i < count; i++)
|
||||
data[i] = i;
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"gintctl", "echo-bounds", data, count * 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
static bool run_with_device(int argc, char const **argv,
|
||||
struct fxlink_device *fdev)
|
||||
{
|
||||
if(!strcmp(argv[0], "/echo")) {
|
||||
int l = 5, j = 5;
|
||||
for(int i = 1; i < argc; i++)
|
||||
l += strlen(argv[i]) + 1;
|
||||
|
||||
char *concat = malloc(l + 1);
|
||||
strcpy(concat, "echo ");
|
||||
for(int i = 1; i < argc; i++) {
|
||||
strcpy(concat + j, argv[i]);
|
||||
j += strlen(argv[i]);
|
||||
concat[j++] = (i == argc - 1) ? '\n' : ' ';
|
||||
}
|
||||
concat[j] = '\0';
|
||||
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"fxlink", "command", concat, l, true);
|
||||
}
|
||||
else if(!strcmp(argv[0], "/identify")) {
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"fxlink", "command", "identify", 8, false);
|
||||
}
|
||||
else if(!strcmp(argv[0], "gintctl")) {
|
||||
if(argc <= 1) {
|
||||
fprint(TUI.wConsole, FMT_RED, "error: ");
|
||||
print(TUI.wConsole, "gintctl command needs sub-command\n");
|
||||
return true;
|
||||
}
|
||||
run_gintctl_command(argc-1, argv+1, fdev);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TUI_execute_command(char const *command)
|
||||
{
|
||||
struct command cmd = parse_command(command);
|
||||
(void)dump_command;
|
||||
|
||||
/* Connected device (TODO: Use the "selected" device) */
|
||||
struct fxlink_device *fdev = NULL;
|
||||
for(int i = 0; i < TUI.devices.count; i++) {
|
||||
if(TUI.devices.devices[i].status == FXLINK_FDEV_STATUS_CONNECTED) {
|
||||
fdev = &TUI.devices.devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool b = run_no_device(cmd.argc, cmd.argv);
|
||||
if(b) {
|
||||
free_command(&cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The following commands require a connected device */
|
||||
if(!fdev) {
|
||||
print(TUI.wConsole, "no connected device!\n");
|
||||
return;
|
||||
}
|
||||
print(TUI.wConsole, "using device %s (%s)\n",
|
||||
fxlink_device_id(fdev), fdev->calc->serial);
|
||||
|
||||
b = run_with_device(cmd.argc, cmd.argv, fdev);
|
||||
if(!b) {
|
||||
fprint(TUI.wConsole, FMT_RED, "error: ");
|
||||
print(TUI.wConsole, "unrecognized command '%s'\n", cmd.argv[0]);
|
||||
}
|
||||
free_command(&cmd);
|
||||
}
|
|
@ -5,17 +5,10 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include "../fxlink.h"
|
||||
#include <fxlink/tui/layout.h>
|
||||
#include <fxlink/tui/render.h>
|
||||
#include <fxlink/tui/input.h>
|
||||
#include <fxlink/devices.h>
|
||||
#include <fxlink/logging.h>
|
||||
#include "tui.h"
|
||||
#include <fxlink/tooling/libpng.h>
|
||||
#include <fxlink/tooling/sdl2.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include <ncurses.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
@ -40,22 +33,7 @@ Application-specific commands:
|
|||
gintctl bench USB transfer speed benchmark
|
||||
*/
|
||||
|
||||
static struct TUIData {
|
||||
/* SIGWINCH flag */
|
||||
bool resize_needed;
|
||||
/* ncurses window panels */
|
||||
WINDOW *wStatus;
|
||||
WINDOW *wLogs;
|
||||
WINDOW *wTransfers;
|
||||
WINDOW *wTextOutput;
|
||||
WINDOW *wConsole;
|
||||
/* Root box */
|
||||
struct fxlink_TUI_box *bRoot;
|
||||
/* Application data */
|
||||
struct fxlink_pollfds polled_fds;
|
||||
struct fxlink_device_list devices;
|
||||
|
||||
} TUI = { 0 };
|
||||
struct TUIData TUI = { 0 };
|
||||
|
||||
//---
|
||||
// TUI management and rendering
|
||||
|
@ -421,50 +399,6 @@ static void handle_fxlink_log(int display_fmt, char const *str)
|
|||
wattroff(TUI.wLogs, attr);
|
||||
}
|
||||
|
||||
static void execute_command(char const *command)
|
||||
{
|
||||
/* Connected device (TODO: Use the "selected" device) */
|
||||
struct fxlink_device *fdev = NULL;
|
||||
for(int i = 0; i < TUI.devices.count; i++) {
|
||||
if(TUI.devices.devices[i].status == FXLINK_FDEV_STATUS_CONNECTED) {
|
||||
fdev = &TUI.devices.devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The following commands require a connected device */
|
||||
if(!fdev) {
|
||||
print(TUI.wConsole, "no connected device!\n");
|
||||
return;
|
||||
}
|
||||
print(TUI.wConsole, "using device %s (%s)\n",
|
||||
fxlink_device_id(fdev), fdev->calc->serial);
|
||||
|
||||
if(!strncmp(command, "/echo", 5)) {
|
||||
int len = strlen(command+1);
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"fxlink", "command", strdup(command+1), len, true);
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(command, "/identify")) {
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"fxlink", "command", "identify", 8, false);
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(command, "gintctl read-long")) {
|
||||
int count = 128; // 2048;
|
||||
uint32_t *data = malloc(count * 4);
|
||||
for(int i = 0; i < count; i++)
|
||||
data[i] = i;
|
||||
fxlink_device_start_bulk_OUT(fdev,
|
||||
"gintctl", "echo-bounds", data, count * 4, true);
|
||||
return;
|
||||
}
|
||||
|
||||
fprint(TUI.wConsole, FMT_RED, "error: ");
|
||||
print(TUI.wConsole, "unrecognized command '%s'\n", command);
|
||||
}
|
||||
|
||||
int main_tui_interactive(libusb_context *ctx)
|
||||
{
|
||||
if(!TUI_setup())
|
||||
|
@ -541,18 +475,29 @@ int main_tui_interactive(libusb_context *ctx)
|
|||
|
||||
if(finished) {
|
||||
char *command = input.data;
|
||||
bool refresh_all = false;
|
||||
|
||||
if(command[0] != 0)
|
||||
log_("command: '%s'\n", command);
|
||||
if(!strcmp(command, ""))
|
||||
{}
|
||||
else if(!strcmp(command, "q") || !strcmp(command, "quit"))
|
||||
break;
|
||||
else
|
||||
execute_command(command);
|
||||
else {
|
||||
TUI_execute_command(command);
|
||||
refresh_all = true;
|
||||
}
|
||||
|
||||
fxlink_TUI_input_free(&input);
|
||||
print(TUI.wConsole, "%s", prompt);
|
||||
fxlink_TUI_input_init(&input, TUI.wConsole, 16);
|
||||
TUI_refresh_console();
|
||||
|
||||
if(refresh_all) {
|
||||
TUI_render_all(false);
|
||||
TUI_refresh_all(false);
|
||||
}
|
||||
else
|
||||
TUI_refresh_console();
|
||||
}
|
||||
}
|
||||
|
36
fxlink/tui/tui.h
Normal file
36
fxlink/tui/tui.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
// ==>/[_]\ fxlink: A community communication tool for CASIO calculators. //
|
||||
// |:::| Made by Lephe' as part of the fxSDK. //
|
||||
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
|
||||
//---------------------------------------------------------------------------//
|
||||
// fxlink.tui.tui: Global data and main functions for the interactive TUI
|
||||
|
||||
#pragma once
|
||||
#include <fxlink/tui/layout.h>
|
||||
#include <fxlink/tui/render.h>
|
||||
#include <fxlink/tui/input.h>
|
||||
#include <fxlink/devices.h>
|
||||
#include <fxlink/logging.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
struct TUIData {
|
||||
/* SIGWINCH flag */
|
||||
bool resize_needed;
|
||||
/* ncurses window panels */
|
||||
WINDOW *wStatus;
|
||||
WINDOW *wLogs;
|
||||
WINDOW *wTransfers;
|
||||
WINDOW *wTextOutput;
|
||||
WINDOW *wConsole;
|
||||
/* Root box */
|
||||
struct fxlink_TUI_box *bRoot;
|
||||
/* Application data */
|
||||
struct fxlink_pollfds polled_fds;
|
||||
struct fxlink_device_list devices;
|
||||
};
|
||||
|
||||
extern struct TUIData TUI;
|
||||
|
||||
void TUI_execute_command(char const *command);
|
Loading…
Reference in a new issue