2020-11-17 14:44:06 +00:00
|
|
|
|
/***************************************************************************
|
2025-04-04 17:06:57 +00:00
|
|
|
|
* Copyright (C) 2020 - 2025 by Federico Amedeo Izzo IU2NUO, *
|
2022-06-02 07:56:05 +00:00
|
|
|
|
* Niccolò Izzo IU2KIN *
|
|
|
|
|
* Frederik Saraci IU2NRO *
|
|
|
|
|
* Silvano Seva IU2KWO *
|
2020-11-17 14:44:06 +00:00
|
|
|
|
* *
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or *
|
|
|
|
|
* (at your option) any later version. *
|
|
|
|
|
* *
|
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
|
* *
|
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2021-12-01 14:02:45 +00:00
|
|
|
|
#include <string.h>
|
2021-07-04 19:17:25 +00:00
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
|
|
|
|
|
#include <readline/readline.h>
|
|
|
|
|
#include <readline/history.h>
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
#include "emulator.h"
|
|
|
|
|
#include "sdl_engine.h"
|
|
|
|
|
|
2021-12-01 14:02:45 +00:00
|
|
|
|
/* Custom SDL Event to request a screenshot */
|
|
|
|
|
extern Uint32 SDL_Screenshot_Event;
|
2020-11-17 14:44:06 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
emulator_state_t emulator_state =
|
|
|
|
|
{
|
2022-06-09 21:11:01 +00:00
|
|
|
|
-100.0f, // RSSI
|
2022-06-09 20:48:09 +00:00
|
|
|
|
8.2f, // Vbat
|
|
|
|
|
3, // mic level
|
|
|
|
|
4, // volume level
|
|
|
|
|
1, // chSelector
|
|
|
|
|
false, // PTT status
|
|
|
|
|
false // power off
|
|
|
|
|
};
|
2020-11-17 20:58:24 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
typedef int (*_climenu_fn)(void *self, int argc, char **argv);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
char *description;
|
|
|
|
|
void *var;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
_climenu_fn fn;
|
2022-06-09 20:48:09 +00:00
|
|
|
|
}
|
|
|
|
|
_climenu_option;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
enum shell_retvals
|
|
|
|
|
{
|
|
|
|
|
SH_ERR = -1,
|
|
|
|
|
SH_CONTINUE = 0,
|
|
|
|
|
SH_WHAT = 1,
|
|
|
|
|
SH_EXIT_OK = 2,
|
2021-07-04 19:17:25 +00:00
|
|
|
|
};
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static keyboard_t _shellkeyq[25] = {0};
|
|
|
|
|
static int _skq_cap = 25;
|
|
|
|
|
static int _skq_head;
|
|
|
|
|
static int _skq_tail;
|
|
|
|
|
static int _skq_in;
|
|
|
|
|
static int _skq_out;
|
|
|
|
|
|
|
|
|
|
// NOTE: unused function
|
|
|
|
|
// static void _dump_skq()
|
|
|
|
|
// {
|
|
|
|
|
// for(int i = 0; i < _skq_cap; i++)
|
|
|
|
|
// {
|
|
|
|
|
// printf("skq[%d] == %d\n", i, _shellkeyq[i]);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// NOTE: unused function
|
|
|
|
|
// static void _test_skq()
|
|
|
|
|
// {
|
|
|
|
|
// for(int i = 0; i < 257; i++)
|
|
|
|
|
// {
|
|
|
|
|
// shellkeyq_put(i + 1);
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// //clear it out now
|
|
|
|
|
// while(emulator_getKeys());
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
static void shellkeyq_put(keyboard_t keys)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-12-05 17:16:22 +00:00
|
|
|
|
// note - we must allow keys == 0 to be inserted because otherwise a queue
|
|
|
|
|
// full of [1,1,1,1,1] is simulating HOLDING 1, and we sometimes
|
|
|
|
|
// (well, often) want [1,0,1,0,1,0] to simulate separate keypresses
|
|
|
|
|
// this, of course, relies on the kbd_thread getting just one element off
|
|
|
|
|
// the queue for every kbd_getKeys().
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(_skq_in > _skq_out + _skq_cap)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("too many keys!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
_shellkeyq[ _skq_tail ] = keys;
|
|
|
|
|
_skq_in++;
|
2021-08-25 05:58:36 +00:00
|
|
|
|
_skq_tail = (_skq_tail + 1) % _skq_cap;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
2020-11-17 14:44:06 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int shell_ready(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
while(_skq_in > _skq_out)
|
|
|
|
|
{
|
|
|
|
|
usleep(10 * 1000); //sleep until keyboard is caught up
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
|
|
|
|
return SH_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static keyboard_t keyname2keyboard(char *name)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-12-05 17:16:22 +00:00
|
|
|
|
/* The line noise at the end of this comment is a vim macro for taking the
|
|
|
|
|
* keyboard.h interface and putting it into the format further below.
|
|
|
|
|
* You can load it into vim register k with "kyy and run the macro with @k
|
|
|
|
|
* (and then you can repeat a macro register application with @@ )
|
|
|
|
|
* (substitute k with any register you like)
|
|
|
|
|
* Once you've got all the names quoted, you can J them all together into
|
|
|
|
|
* a nice block.
|
|
|
|
|
*
|
|
|
|
|
* _i"ElC",
|
|
|
|
|
*
|
|
|
|
|
*/
|
2021-08-25 05:58:36 +00:00
|
|
|
|
char *names[] =
|
|
|
|
|
{
|
|
|
|
|
"KEY_0", "KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_5", "KEY_6", "KEY_7",
|
2021-12-05 17:16:22 +00:00
|
|
|
|
"KEY_8", "KEY_9", "KEY_STAR", "KEY_HASH", "KEY_ENTER", "KEY_ESC",
|
|
|
|
|
"KEY_UP","KEY_DOWN", "KEY_LEFT", "KEY_RIGHT", "KEY_MONI", "KEY_F1",
|
2023-09-10 11:04:38 +00:00
|
|
|
|
"KEY_F2", "KEY_F3", "KEY_F4", "KEY_F5", "KEY_F6", "KEY_VOLUP", "KEY_VOLDOWN",
|
2021-12-05 17:16:22 +00:00
|
|
|
|
"KNOB_LEFT", "KNOB_RIGHT",
|
2021-07-04 19:17:25 +00:00
|
|
|
|
};
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
int numnames = sizeof(names) / sizeof(char *);
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
for(int i = 0; i < numnames; i++)
|
|
|
|
|
{
|
2021-12-05 17:16:22 +00:00
|
|
|
|
/*
|
|
|
|
|
* +4 to skip the KEY_ on all the names, non +4 to allow for KNOB_LEFT.
|
|
|
|
|
* This also means you can write KEY_LEFT as "KEY_LEFT", or "LEFT" and
|
|
|
|
|
* KNOB_LEFT as "KNOB_LEFT" or "_LEFT"
|
|
|
|
|
*
|
|
|
|
|
* so if name == "2", this whole function will return equivalent to KEY_2 cpp define
|
|
|
|
|
* and if name=="LEFT", then you get equivalent to KEY_LEFT cpp define
|
|
|
|
|
* and if name=="_LEFT", then you get equivalent to KNOB_LEFT cpp define
|
|
|
|
|
* and if name=="KNOB_LEFT", then you get equivalent to KNOB_LEFT cpp define
|
|
|
|
|
* and if name=="KEY_2", then you get equivalent to KEY_2 cpp define.
|
|
|
|
|
*
|
|
|
|
|
* Of course order matters a great deal in names array, has to match the
|
|
|
|
|
* bit field generated in interface/keyboard.h so double check that with
|
|
|
|
|
* every update
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if((strcasecmp(name, names[i] + 4) == 0) ||
|
|
|
|
|
(strcasecmp(name, names[i]) == 0)) //notice case insensitive
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
return (1 << i);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int pressKey(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("Press Keys: [\n");
|
|
|
|
|
keyboard_t last = 0;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
for(int i = 0; i < _argc; i++)
|
|
|
|
|
{
|
|
|
|
|
if(_argv[i] != NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("\t%s, \n", _argv[i]);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
keyboard_t press = keyname2keyboard(_argv[i]);
|
|
|
|
|
if(press == last)
|
|
|
|
|
{
|
2021-12-05 17:16:22 +00:00
|
|
|
|
/* otherwise if you send key ENTER DOWN DOWN DOWN DOWN DOWN
|
|
|
|
|
* it will just hold DOWN for (5/(kbd_task_hz)) seconds
|
|
|
|
|
* so we need to give it a 0 value to get a 'release'
|
|
|
|
|
* so the next input is recognized as separate
|
|
|
|
|
* we only need to do this if we have two identical keys back
|
|
|
|
|
* to back, because keyboard_t will have a zero for this key's
|
|
|
|
|
* flag on other keys, which gives us the release we need
|
|
|
|
|
*/
|
2021-08-25 05:58:36 +00:00
|
|
|
|
shellkeyq_put(0);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
|
|
|
|
shellkeyq_put(press);
|
|
|
|
|
last = press;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("\t]\n");
|
2021-08-25 05:58:36 +00:00
|
|
|
|
shell_ready(NULL, 0, NULL);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_CONTINUE; // continue
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
|
|
|
|
// pressMultiKeys allows for key combos by sending all the keys specified in
|
|
|
|
|
// one keyboard_t
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int pressMultiKeys(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("Press Keys: [\n");
|
|
|
|
|
keyboard_t combo = 0;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
for(int i = 0; i < _argc; i++)
|
|
|
|
|
{
|
|
|
|
|
if(_argv[i] != NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("\t%s, \n", _argv[i]);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
combo |= keyname2keyboard(_argv[i]);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
shellkeyq_put(combo);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("\t]\n");
|
2021-08-25 05:58:36 +00:00
|
|
|
|
shell_ready(NULL, 0, NULL);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_CONTINUE; // continue
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
2021-07-04 19:17:25 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
// NOTE: unused function
|
|
|
|
|
// static int template(void *_self, int _argc, char **_argv)
|
|
|
|
|
// {
|
|
|
|
|
// _climenu_option *self = (_climenu_option *) _self;
|
|
|
|
|
// printf("%s\n\t%s\n", self->name, self->description);
|
|
|
|
|
//
|
|
|
|
|
// for(int i = 0; i < _argc; i++)
|
|
|
|
|
// {
|
|
|
|
|
// if(_argv[i] != NULL)
|
|
|
|
|
// {
|
|
|
|
|
// printf("\tArgs:\t%s\n", _argv[i]);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return SH_CONTINUE; // continue
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
static int screenshot(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
2021-08-25 05:58:36 +00:00
|
|
|
|
char *filename = "screenshot.bmp";
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(_argc && _argv[0] != NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
filename = _argv[0];
|
|
|
|
|
}
|
2021-12-01 14:02:45 +00:00
|
|
|
|
|
2023-09-10 05:02:41 +00:00
|
|
|
|
int len = strlen(filename);
|
|
|
|
|
|
2021-12-01 14:02:45 +00:00
|
|
|
|
SDL_Event e;
|
|
|
|
|
SDL_zero(e);
|
|
|
|
|
e.type = SDL_Screenshot_Event;
|
2023-09-10 05:02:41 +00:00
|
|
|
|
e.user.data1 = malloc(len+1);
|
|
|
|
|
memset(e.user.data1, 0, len+1);
|
2021-12-01 14:02:45 +00:00
|
|
|
|
strcpy(e.user.data1, filename);
|
|
|
|
|
|
|
|
|
|
return SDL_PushEvent(&e) == 1 ? SH_CONTINUE : SH_ERR;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int setFloat(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
_climenu_option *self = (_climenu_option *) _self;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(_argc <= 0 || _argv[0] == NULL)
|
|
|
|
|
{
|
|
|
|
|
printf("%s is %f\n", self->name, *(float *)(self->var));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
sscanf(_argv[0], "%f", (float *)self->var);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
printf("%s is %f\n", self->name, *(float *)(self->var));
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_CONTINUE; // continue
|
|
|
|
|
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int toggleVariable(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
2021-08-25 05:58:36 +00:00
|
|
|
|
_climenu_option *self = (_climenu_option *) _self;
|
|
|
|
|
*(int *)self->var = ! *(int *)self->var; //yeah, maybe this got a little out of hand
|
2021-07-04 19:17:25 +00:00
|
|
|
|
|
2021-12-05 17:16:22 +00:00
|
|
|
|
return SH_CONTINUE; // continue
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int shell_sleep(void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(! _argc || _argv[0] == NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("Provide a number in milliseconds to sleep as an argument\n");
|
|
|
|
|
return SH_ERR;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
useconds_t sleepus = atoi(_argv[0]) * 1000;
|
|
|
|
|
usleep(sleepus);
|
|
|
|
|
return SH_CONTINUE;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int shell_quit( void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("QUIT: 73!\n");
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
//could remove history entries here, if we wanted
|
|
|
|
|
return SH_EXIT_OK; //normal quit
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int printState( void *_self, int _argc, char **_argv)
|
2021-03-12 13:15:27 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
2020-11-17 14:44:06 +00:00
|
|
|
|
printf("\nCurrent state\n");
|
2022-06-09 20:48:09 +00:00
|
|
|
|
printf("RSSI : %f\n", emulator_state.RSSI);
|
|
|
|
|
printf("Battery: %f\n", emulator_state.vbat);
|
|
|
|
|
printf("Mic : %f\n", emulator_state.micLevel);
|
|
|
|
|
printf("Volume : %f\n", emulator_state.volumeLevel);
|
|
|
|
|
printf("Channel: %f\n", emulator_state.chSelector);
|
|
|
|
|
printf("PTT : %s\n\n", emulator_state.PTTstatus ? "true" : "false");
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_CONTINUE;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int shell_nop( void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
//do nothing! what it says on the tin
|
2021-08-25 05:58:36 +00:00
|
|
|
|
return SH_CONTINUE;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-05 17:16:22 +00:00
|
|
|
|
// Forward declaration needed to include function pointer in the table below
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int shell_help( void *_self, int _argc, char **_argv);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static _climenu_option _options[] =
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
/* name/shortcut description var reference, if available method to call */
|
2022-06-09 20:48:09 +00:00
|
|
|
|
{"rssi", "Set rssi", (void *) &emulator_state.RSSI, setFloat },
|
|
|
|
|
{"vbat", "Set vbat", (void *) &emulator_state.vbat, setFloat },
|
|
|
|
|
{"mic", "Set miclevel", (void *) &emulator_state.micLevel, setFloat },
|
|
|
|
|
{"volume", "Set volume", (void *) &emulator_state.volumeLevel, setFloat },
|
|
|
|
|
{"channel", "Set channel", (void *) &emulator_state.chSelector, setFloat },
|
|
|
|
|
{"ptt", "Toggle PTT", (void *) &emulator_state.PTTstatus, toggleVariable },
|
2021-12-05 17:16:22 +00:00
|
|
|
|
{"key", "Press keys in sequence (e.g. 'key ENTER DOWN ENTER' will descend through two menus)",
|
|
|
|
|
NULL, pressKey
|
2021-08-25 05:58:36 +00:00
|
|
|
|
},
|
2021-12-05 17:16:22 +00:00
|
|
|
|
{"keycombo", "Press a bunch of keys simultaneously", NULL, pressMultiKeys },
|
|
|
|
|
{"show", "Show current radio state (ptt, rssi, etc)", NULL, printState},
|
|
|
|
|
{"screenshot", "[screenshot.bmp] Save screenshot to first arg or screenshot.bmp if none given",
|
|
|
|
|
NULL, screenshot
|
2021-08-25 05:58:36 +00:00
|
|
|
|
},
|
2021-12-05 17:16:22 +00:00
|
|
|
|
{"sleep", "Wait some number of ms", NULL, shell_sleep },
|
|
|
|
|
{"help", "Print this help", NULL, shell_help },
|
|
|
|
|
{"nop", "Do nothing (useful for comments)", NULL, shell_nop},
|
|
|
|
|
{"quit", "Quit, close the emulator", NULL, shell_quit },
|
2021-07-04 19:17:25 +00:00
|
|
|
|
/*{"ready", */
|
2021-08-25 05:58:36 +00:00
|
|
|
|
/*"Wait until ready. Currently supports keyboard, so will wait until all keyboard events are processed,"*/
|
|
|
|
|
/*"but is already implied by key and keycombo so there's not much direct use for it right now",*/
|
|
|
|
|
/*NULL, shell_ready },*/
|
2021-07-04 19:17:25 +00:00
|
|
|
|
};
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static const int num_options = (sizeof(_options) / sizeof(_climenu_option));
|
|
|
|
|
|
|
|
|
|
static int shell_help( void *_self, int _argc, char **_argv)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
2021-08-25 05:37:52 +00:00
|
|
|
|
(void) _self;
|
|
|
|
|
(void) _argc;
|
|
|
|
|
(void) _argv;
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("OpenRTX emulator shell\n\n");
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
for(int i = 0; i < num_options; i++)
|
|
|
|
|
{
|
|
|
|
|
_climenu_option *o = &_options[i];
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("%10s -> %s\n", o->name, o->description);
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static _climenu_option *findMenuOption(char *tok)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < num_options; i++)
|
|
|
|
|
{
|
|
|
|
|
_climenu_option *o = &_options[i];
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* strncmp like this allows for typing shortcuts like just "r" instead
|
|
|
|
|
* of the full "rssi". Priority for conflicts (like if there's "s"
|
|
|
|
|
* which could mean either "show" or "screenshot" ) is set by ordering
|
|
|
|
|
* in the _options array
|
|
|
|
|
*/
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(strncmp(tok, o->name, strlen(tok)) == 0)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return NULL;
|
2020-11-17 14:44:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static void striptoken(char *token)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
for(size_t i = 0; i < strlen(token); i++)
|
|
|
|
|
{
|
|
|
|
|
if(token[i] == '\n')
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
token[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
static int process_line(char *line)
|
2021-08-25 05:58:36 +00:00
|
|
|
|
{
|
|
|
|
|
char *token = strtok(line, " ");
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(token == NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_ERR;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
striptoken(token);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
_climenu_option *o = findMenuOption(token);
|
|
|
|
|
char *args[12] = {NULL};
|
2021-07-04 19:17:25 +00:00
|
|
|
|
int i = 0;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
for(i = 0; i < 12; i++)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
//immediately strtok again since first is a command rest are args
|
|
|
|
|
token = strtok(NULL, " ");
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(token == NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
striptoken(token);
|
|
|
|
|
args[i] = token;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(token != NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("\nGot too many arguments, args truncated \n");
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(o != NULL)
|
|
|
|
|
{
|
|
|
|
|
if(o->fn != NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return o->fn(o, i, args);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("Bad fn for o, check option array for bad data\n");
|
|
|
|
|
return SH_ERR;
|
|
|
|
|
}
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
return SH_WHAT; //not understood
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2025-07-08 19:17:33 +00:00
|
|
|
|
void *startCLIMenu(void *arg)
|
2021-03-12 13:15:27 +00:00
|
|
|
|
{
|
2025-07-08 19:17:33 +00:00
|
|
|
|
(void) arg;
|
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
printf("\n\n");
|
2021-08-25 05:58:36 +00:00
|
|
|
|
char *histfile = ".emulatorsh_history";
|
|
|
|
|
shell_help(NULL, 0, NULL);
|
2021-07-04 19:17:25 +00:00
|
|
|
|
int ret = SH_CONTINUE;
|
|
|
|
|
using_history();
|
|
|
|
|
read_history(histfile);
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
char *r = readline(">");
|
2022-06-09 20:48:09 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
if(r == NULL)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
ret = SH_EXIT_OK;
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
|
|
|
|
else if(strlen(r) > 0)
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
add_history(r);
|
|
|
|
|
ret = process_line(r);
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-07-04 19:17:25 +00:00
|
|
|
|
ret = SH_CONTINUE;
|
|
|
|
|
}
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-08-25 05:58:36 +00:00
|
|
|
|
switch(ret)
|
|
|
|
|
{
|
2021-12-05 17:16:22 +00:00
|
|
|
|
default:
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SH_WHAT:
|
|
|
|
|
printf("?\n(type h or help for help)\n");
|
|
|
|
|
ret = SH_CONTINUE;
|
|
|
|
|
/*printf("\n>");*/
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SH_CONTINUE:
|
|
|
|
|
/*printf("\n>");*/
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SH_EXIT_OK:
|
|
|
|
|
//normal quit
|
2022-06-09 20:48:09 +00:00
|
|
|
|
emulator_state.powerOff = true;
|
2021-12-05 17:16:22 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SH_ERR:
|
|
|
|
|
//error
|
|
|
|
|
printf("Error running that command\n");
|
|
|
|
|
ret = SH_CONTINUE;
|
|
|
|
|
break;
|
2020-11-17 14:44:06 +00:00
|
|
|
|
}
|
2022-06-09 20:48:09 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
free(r); //free the string allocated by readline
|
2021-08-25 05:58:36 +00:00
|
|
|
|
}
|
2022-06-09 20:48:09 +00:00
|
|
|
|
while((ret == SH_CONTINUE) && (emulator_state.powerOff == false));
|
2021-12-05 17:16:22 +00:00
|
|
|
|
|
2021-07-04 19:17:25 +00:00
|
|
|
|
fflush(stdout);
|
|
|
|
|
write_history(histfile);
|
2022-06-09 20:48:09 +00:00
|
|
|
|
|
|
|
|
|
return NULL;
|
2020-11-29 12:29:11 +00:00
|
|
|
|
}
|
2020-11-17 14:44:06 +00:00
|
|
|
|
|
2022-06-09 20:48:09 +00:00
|
|
|
|
|
|
|
|
|
|
2021-03-12 13:15:27 +00:00
|
|
|
|
void emulator_start()
|
|
|
|
|
{
|
2022-06-09 20:17:20 +00:00
|
|
|
|
sdlEngine_init();
|
2021-12-01 14:02:45 +00:00
|
|
|
|
|
2021-03-12 13:15:27 +00:00
|
|
|
|
pthread_t cli_thread;
|
|
|
|
|
int err = pthread_create(&cli_thread, NULL, startCLIMenu, NULL);
|
2020-11-17 14:44:06 +00:00
|
|
|
|
|
2021-03-12 13:15:27 +00:00
|
|
|
|
if(err)
|
|
|
|
|
{
|
2021-12-01 14:02:45 +00:00
|
|
|
|
printf("An error occurred starting the emulator CLI thread: %d\n", err);
|
2021-11-28 17:45:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-09 20:48:09 +00:00
|
|
|
|
|
|
|
|
|
keyboard_t emulator_getKeys()
|
|
|
|
|
{
|
|
|
|
|
if(_skq_in > _skq_out)
|
|
|
|
|
{
|
|
|
|
|
//only if we've fallen behind and there's data in there:
|
|
|
|
|
keyboard_t out = _shellkeyq[ _skq_head ];
|
|
|
|
|
_shellkeyq[ _skq_head ] = 0;
|
|
|
|
|
_skq_out++;
|
|
|
|
|
_skq_head = (_skq_head + 1) % _skq_cap;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return 0; //no keys
|
|
|
|
|
}
|
|
|
|
|
}
|