mirror of
https://github.com/whowechina/chu_pico.git
synced 2024-11-24 19:10:11 +01:00
Command line functional but not completed
This commit is contained in:
parent
1b95be1144
commit
979fbd48e6
@ -4,7 +4,7 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip)
|
||||
function(make_firmware board board_def)
|
||||
pico_sdk_init()
|
||||
add_executable(${board}
|
||||
main.c slider.c air.c rgb.c save.c lzfx.c usb_descriptors.c)
|
||||
main.c slider.c air.c rgb.c save.c config.c cmd.c lzfx.c usb_descriptors.c)
|
||||
target_compile_definitions(${board} PUBLIC ${board_def})
|
||||
pico_enable_stdio_usb(${board} 1)
|
||||
pico_enable_stdio_uart(${board} 0)
|
||||
|
183
firmware/src/cmd.c
Normal file
183
firmware/src/cmd.c
Normal file
@ -0,0 +1,183 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "pico/stdio.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define MAX_COMMANDS 10
|
||||
#define MAX_COMMAND_LENGTH 20
|
||||
#define MAX_PARAMETERS 5
|
||||
#define MAX_PARAMETER_LENGTH 20
|
||||
|
||||
const char *chu_prompt = "chu_pico>";
|
||||
|
||||
typedef void (*cmd_handler_t)(int argc, char *argv[]);
|
||||
|
||||
typedef struct {
|
||||
char name[MAX_COMMAND_LENGTH];
|
||||
cmd_handler_t handler;
|
||||
} command_t;
|
||||
|
||||
command_t commands[MAX_COMMANDS];
|
||||
int num_commands = 0;
|
||||
|
||||
void register_command(char *name, cmd_handler_t handler)
|
||||
{
|
||||
if (num_commands < MAX_COMMANDS) {
|
||||
strcpy(commands[num_commands].name, name);
|
||||
commands[num_commands].handler = handler;
|
||||
num_commands++;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_help(int argc, char *argv[])
|
||||
{
|
||||
printf("Available commands:\n");
|
||||
for (int i = 0; i < num_commands; i++) {
|
||||
printf("%s\n", commands[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_list(int argc, char *argv[])
|
||||
{
|
||||
printf("[Colors]\n");
|
||||
printf(" Key upper: %6x, lower: %6x, both: %6x, off: %6x\n",
|
||||
chu_cfg->colors.key_on_upper, chu_cfg->colors.key_on_lower,
|
||||
chu_cfg->colors.key_on_both, chu_cfg->colors.key_off);
|
||||
printf(" Gap: %6x\n", chu_cfg->colors.gap);
|
||||
printf("[Style]\n");
|
||||
printf(" Key: %d, Gap: %d, ToF: %d, Level: %d\n",
|
||||
chu_cfg->style.key, chu_cfg->style.gap,
|
||||
chu_cfg->style.tof, chu_cfg->style.level);
|
||||
printf("[ToF]\n");
|
||||
printf(" Offset: %d, Pitch: %d\n", chu_cfg->tof.offset, chu_cfg->tof.pitch);
|
||||
printf("[Sense]\n");
|
||||
printf(" Global: %d, debounce: %d\n", chu_cfg->sense.global, chu_cfg->sense.debounce);
|
||||
printf(" | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |\n");
|
||||
printf(" -----------------------------------------------------------------\n");
|
||||
printf(" A |");
|
||||
for (int i = 0; i < 16; i++) {
|
||||
printf("%3d|", chu_cfg->sense.keys[i * 2]);
|
||||
}
|
||||
printf("\n B |");
|
||||
for (int i = 0; i < 16; i++) {
|
||||
printf("%3d|", chu_cfg->sense.keys[i * 2 + 1]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("[HID]\n");
|
||||
printf(" Joy: %s, NKRO: %s.\n",
|
||||
chu_cfg->hid.joy ? "on" : "off",
|
||||
chu_cfg->hid.nkro ? "on" : "off" );
|
||||
}
|
||||
|
||||
static int fps[2];
|
||||
|
||||
void fps_count(int core)
|
||||
{
|
||||
static uint32_t last[2] = {0};
|
||||
static int counter[2] = {0};
|
||||
|
||||
counter[core]++;
|
||||
|
||||
uint32_t now = time_us_32();
|
||||
if (now - last[core] < 1000000) {
|
||||
return;
|
||||
}
|
||||
last[core] = now;
|
||||
fps[core] = counter[core];
|
||||
counter[core] = 0;
|
||||
}
|
||||
|
||||
void handle_fps(int argc, char *argv[])
|
||||
{
|
||||
printf("FPS: core 0: %d, core 1: %d\n", fps[0], fps[1]);
|
||||
}
|
||||
|
||||
void handle_hid(int argc, char *argv[])
|
||||
{
|
||||
const char *usage = "Usage: hid <joy|nkro>\n";
|
||||
if (argc != 1) {
|
||||
printf(usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "joy") == 0) {
|
||||
chu_cfg->hid.joy = 1;
|
||||
chu_cfg->hid.nkro = 0;
|
||||
} else if (strcmp(argv[0], "nkro") == 0) {
|
||||
chu_cfg->hid.joy = 0;
|
||||
chu_cfg->hid.nkro = 1;
|
||||
} else if (strcmp(argv[0], "both") == 0) {
|
||||
chu_cfg->hid.joy = 1;
|
||||
chu_cfg->hid.nkro = 1;
|
||||
} else {
|
||||
printf(usage);
|
||||
return;
|
||||
}
|
||||
config_changed();
|
||||
}
|
||||
|
||||
void cmd_init()
|
||||
{
|
||||
fcntl(0, F_SETFL, O_NONBLOCK);
|
||||
register_command("?", handle_help);
|
||||
register_command("list", handle_list);
|
||||
register_command("fps", handle_fps);
|
||||
register_command("hid", handle_hid);
|
||||
}
|
||||
|
||||
static char cmd_buf[256];
|
||||
static int cmd_len = 0;
|
||||
|
||||
static void process_cmd()
|
||||
{
|
||||
char *command;
|
||||
char *argv[MAX_PARAMETERS];
|
||||
int argc;
|
||||
|
||||
command = strtok(cmd_buf, " \n");
|
||||
argc = 0;
|
||||
while ((argc < MAX_PARAMETERS) &&
|
||||
(argv[argc] = strtok(NULL, " \n")) != NULL) {
|
||||
argc++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_commands; i++) {
|
||||
if (strcmp(commands[i].name, command) == 0) {
|
||||
commands[i].handler(argc, argv);
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_run()
|
||||
{
|
||||
int c = getchar_timeout_us(0);
|
||||
if (c == EOF) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((c != '\n') && (c != '\r')) {
|
||||
if (cmd_len < sizeof(cmd_buf) - 2) {
|
||||
cmd_buf[cmd_len] = c;
|
||||
printf("%c", c);
|
||||
cmd_len++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_buf[cmd_len] = '\0';
|
||||
cmd_len = 0;
|
||||
|
||||
printf("\n");
|
||||
|
||||
process_cmd();
|
||||
|
||||
printf(chu_prompt);
|
||||
}
|
13
firmware/src/cmd.h
Normal file
13
firmware/src/cmd.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Chu Controller Command Line
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef CMD_H
|
||||
#define CMD_H
|
||||
|
||||
void cmd_init();
|
||||
void cmd_run();
|
||||
void fps_count(int core);
|
||||
|
||||
#endif
|
@ -1,69 +1,59 @@
|
||||
/*
|
||||
* Controller Config Data
|
||||
* Controller Config and Runtime Data
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Config is a global data structure that stores all the configuration
|
||||
* Runtime is something to share between files.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "save.h"
|
||||
|
||||
iidx_cfg_t *iidx_cfg;
|
||||
chu_cfg_t *chu_cfg;
|
||||
|
||||
static iidx_cfg_t default_cfg = {
|
||||
.key_off = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}},
|
||||
.key_on = { {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40},
|
||||
{40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40},
|
||||
static chu_cfg_t default_cfg = {
|
||||
.colors = {
|
||||
.key_on_upper = 0x00FF00,
|
||||
.key_on_lower = 0xff0000,
|
||||
.key_on_both = 0xff0000,
|
||||
.key_off = 0x000000,
|
||||
.gap = 0x000000,
|
||||
},
|
||||
.tt_led = {
|
||||
.start = 0,
|
||||
.num = 24,
|
||||
.effect = 0,
|
||||
.param = 0,
|
||||
.mode = 0,
|
||||
.style = {
|
||||
.key = 0,
|
||||
.gap = 0,
|
||||
.tof = 0,
|
||||
.level = 7,
|
||||
},
|
||||
.tt_sensor = {
|
||||
.mode = 2,
|
||||
.deadzone = 1,
|
||||
.ppr = 1,
|
||||
.tof = {
|
||||
.offset = 64,
|
||||
.pitch = 16,
|
||||
},
|
||||
.effects = {
|
||||
.e1 = 255,
|
||||
.e2 = 128,
|
||||
.e3 = 128,
|
||||
.e4 = 128,
|
||||
.sense = {
|
||||
.debounce = 4,
|
||||
},
|
||||
.hid = {
|
||||
.joy = 0x0f,
|
||||
.nkro = 0,
|
||||
},
|
||||
.level = 128,
|
||||
.konami = false,
|
||||
};
|
||||
|
||||
chu_runtime_t *chu_runtime;
|
||||
|
||||
static void config_loaded()
|
||||
{
|
||||
if (iidx_cfg->tt_led.num == 0) {
|
||||
iidx_cfg->tt_led.num = 24;
|
||||
if (chu_cfg->style.level > 10) {
|
||||
chu_cfg->style.level = default_cfg.style.level;
|
||||
config_changed();
|
||||
}
|
||||
if ((iidx_cfg->tt_led.start > 8) ||
|
||||
(iidx_cfg->tt_led.start + iidx_cfg->tt_led.num > 128)) {
|
||||
iidx_cfg->tt_led.start = 0;
|
||||
iidx_cfg->tt_led.num = 24;
|
||||
if ((chu_cfg->tof.offset < 400) || (chu_cfg->tof.offset > 2000) ||
|
||||
(chu_cfg->tof.pitch < 50) || (chu_cfg->tof.pitch > 500)) {
|
||||
chu_cfg->tof = default_cfg.tof;
|
||||
config_changed();
|
||||
}
|
||||
if (iidx_cfg->tt_sensor.deadzone > 2) {
|
||||
iidx_cfg->tt_sensor.deadzone = 0;
|
||||
config_changed();
|
||||
}
|
||||
if (iidx_cfg->tt_led.mode > 2) {
|
||||
iidx_cfg->tt_led.mode = 0;
|
||||
config_changed();
|
||||
}
|
||||
if (iidx_cfg->tt_sensor.mode > 3) {
|
||||
iidx_cfg->tt_sensor.mode = 2;
|
||||
config_changed();
|
||||
}
|
||||
if (iidx_cfg->tt_sensor.ppr > 3) {
|
||||
iidx_cfg->tt_sensor.ppr = 1;
|
||||
config_changed();
|
||||
|
||||
if (chu_cfg->sense.debounce > 32) {
|
||||
chu_cfg->sense.debounce = default_cfg.sense.debounce;
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,11 +64,11 @@ void config_changed()
|
||||
|
||||
void config_factory_reset()
|
||||
{
|
||||
*iidx_cfg = default_cfg;
|
||||
*chu_cfg = default_cfg;
|
||||
save_request(true);
|
||||
}
|
||||
|
||||
void config_init()
|
||||
{
|
||||
iidx_cfg = (iidx_cfg_t *)save_alloc(sizeof(iidx_cfg), &default_cfg, config_loaded);
|
||||
chu_cfg = (chu_cfg_t *)save_alloc(sizeof(*chu_cfg), &default_cfg, config_loaded);
|
||||
}
|
||||
|
@ -9,38 +9,41 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct __attribute ((packed)) {
|
||||
uint8_t h; // hue;
|
||||
uint8_t s; // saturation;
|
||||
uint8_t v; // value;
|
||||
} hsv_t;
|
||||
typedef struct __attribute__((packed)) {
|
||||
struct {
|
||||
uint32_t key_on_upper;
|
||||
uint32_t key_on_lower;
|
||||
uint32_t key_on_both;
|
||||
uint32_t key_off;
|
||||
uint32_t gap;
|
||||
} colors;
|
||||
struct {
|
||||
uint8_t key;
|
||||
uint8_t gap;
|
||||
uint8_t tof;
|
||||
uint8_t level;
|
||||
} style;
|
||||
struct {
|
||||
uint8_t offset;
|
||||
uint8_t pitch;
|
||||
} tof;
|
||||
struct {
|
||||
int8_t global;
|
||||
uint8_t debounce;
|
||||
int8_t keys[32];
|
||||
} sense;
|
||||
struct {
|
||||
uint8_t joy : 4;
|
||||
uint8_t nkro : 4;
|
||||
} hid;
|
||||
} chu_cfg_t;
|
||||
|
||||
typedef struct __attribute ((packed)) {
|
||||
hsv_t key_off[11];
|
||||
hsv_t key_on[11];
|
||||
struct {
|
||||
uint8_t start;
|
||||
uint8_t num;
|
||||
uint8_t effect;
|
||||
uint8_t param;
|
||||
uint8_t mode; /* 0: on, 1: reversed, 2: off */
|
||||
} tt_led;
|
||||
struct {
|
||||
uint8_t mode; /* 0: analog, 1: analog reversed, 2: i2c, 3: i2c reversed */
|
||||
uint8_t deadzone; /* only for analog */
|
||||
uint8_t ppr; /* 0: 256, 1: 128, 2: 96, 3: 64, other: 256 */
|
||||
} tt_sensor;
|
||||
struct {
|
||||
uint8_t e1;
|
||||
uint8_t e2;
|
||||
uint8_t e3;
|
||||
uint8_t e4;
|
||||
} effects;
|
||||
uint8_t level; /* led brightness limit */
|
||||
bool konami; /* konami spoof */
|
||||
} iidx_cfg_t;
|
||||
typedef struct {
|
||||
uint16_t fps[2];
|
||||
} chu_runtime_t;
|
||||
|
||||
extern iidx_cfg_t *iidx_cfg;
|
||||
extern chu_cfg_t *chu_cfg;
|
||||
extern chu_runtime_t *chu_runtime;
|
||||
|
||||
void config_init();
|
||||
void config_changed(); // Notify the config has changed
|
||||
|
@ -22,16 +22,16 @@
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#include "board_defs.h"
|
||||
|
||||
#include "save.h"
|
||||
#include "config.h"
|
||||
#include "cmd.h"
|
||||
|
||||
#include "slider.h"
|
||||
#include "air.h"
|
||||
#include "rgb.h"
|
||||
#include "lzfx.h"
|
||||
|
||||
/* Measure the time of a function call */
|
||||
#define RUN_TIME(func) \
|
||||
{ uint64_t _t = time_us_64(); func; \
|
||||
printf(#func ":%lld\n", time_us_64() - _t); }
|
||||
|
||||
struct __attribute__((packed)) {
|
||||
uint16_t buttons; // 16 buttons; see JoystickButtons_t for bit mapping
|
||||
uint8_t HAT; // HAT switch; one nibble w/ unused nibble
|
||||
@ -49,10 +49,13 @@ void report_usb_hid()
|
||||
if (tud_hid_ready()) {
|
||||
hid_joy.HAT = 0;
|
||||
hid_joy.VendorSpec = 0;
|
||||
if (chu_cfg->hid.joy) {
|
||||
tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
|
||||
if (memcmp(&hid_nkro, &sent_hid_nkro, sizeof(hid_nkro)) != 0) {
|
||||
}
|
||||
if (chu_cfg->hid.nkro &&
|
||||
(memcmp(&hid_nkro, &sent_hid_nkro, sizeof(hid_nkro)) != 0)) {
|
||||
sent_hid_nkro = hid_nkro;
|
||||
//tud_hid_n_report(0x02, 0, &sent_hid_nkro, sizeof(sent_hid_nkro));
|
||||
tud_hid_n_report(0x02, 0, &sent_hid_nkro, sizeof(sent_hid_nkro));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,41 +69,6 @@ static void pause_core1(bool pause)
|
||||
sleep_ms(5); /* wait for any IO ops to finish */
|
||||
}
|
||||
}
|
||||
|
||||
static int fps[2] = {0};
|
||||
|
||||
static int get_fps(int core)
|
||||
{
|
||||
return fps[core];
|
||||
}
|
||||
|
||||
static void fps_count(int core)
|
||||
{
|
||||
static uint32_t last[2] = {0};
|
||||
static int counter[2] = {0};
|
||||
|
||||
counter[core]++;
|
||||
|
||||
uint32_t now = time_us_32();
|
||||
if (now - last[core] < 1000000) {
|
||||
return;
|
||||
}
|
||||
last[core] = now;
|
||||
fps[core] = counter[core];
|
||||
counter[core] = 0;
|
||||
}
|
||||
|
||||
static void print_fps()
|
||||
{
|
||||
static uint32_t last = 0;
|
||||
uint32_t now = time_us_32();
|
||||
if (now - last < 5000000) {
|
||||
return;
|
||||
}
|
||||
last = now;
|
||||
printf("FPS: %d %d\n", get_fps(0), get_fps(1));
|
||||
}
|
||||
|
||||
static void gen_joy_report()
|
||||
{
|
||||
hid_joy.axis = 0;
|
||||
@ -182,7 +150,6 @@ static void core1_loop()
|
||||
|
||||
slider_update_baseline();
|
||||
fps_count(1);
|
||||
print_fps();
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
@ -190,6 +157,8 @@ static void core1_loop()
|
||||
static void core0_loop()
|
||||
{
|
||||
while(1) {
|
||||
cmd_run();
|
||||
save_loop();
|
||||
fps_count(0);
|
||||
|
||||
slider_update();
|
||||
@ -209,9 +178,15 @@ void init()
|
||||
board_init();
|
||||
tusb_init();
|
||||
stdio_init_all();
|
||||
|
||||
config_init();
|
||||
save_init(0xca34cafe, pause_core1);
|
||||
|
||||
slider_init();
|
||||
air_init();
|
||||
rgb_init();
|
||||
|
||||
cmd_init();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Controller Save Save and Load
|
||||
* Controller Config Save and Load
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Save is stored in last sector of flash
|
||||
* Config is stored in last sector of flash
|
||||
*/
|
||||
|
||||
#include "save.h"
|
||||
@ -27,7 +27,8 @@ static struct {
|
||||
} modules[8] = {0};
|
||||
static int module_num = 0;
|
||||
|
||||
#define SAVE_PAGE_MAGIC 0x13424321
|
||||
static uint32_t my_magic = 0xcafecafe;
|
||||
|
||||
#define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
|
||||
|
||||
typedef struct __attribute ((packed)) {
|
||||
@ -66,7 +67,7 @@ static void load_default()
|
||||
{
|
||||
printf("Load Default\n");
|
||||
new_data = default_data;
|
||||
new_data.magic = SAVE_PAGE_MAGIC;
|
||||
new_data.magic = my_magic;
|
||||
}
|
||||
|
||||
static const page_t *get_page(int id)
|
||||
@ -78,7 +79,7 @@ static const page_t *get_page(int id)
|
||||
static void save_load()
|
||||
{
|
||||
for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) {
|
||||
if (get_page(i)->magic != SAVE_PAGE_MAGIC) {
|
||||
if (get_page(i)->magic != my_magic) {
|
||||
break;
|
||||
}
|
||||
data_page = i;
|
||||
@ -102,8 +103,9 @@ static void save_loaded()
|
||||
}
|
||||
}
|
||||
|
||||
void save_init(io_locker_func locker)
|
||||
void save_init(uint32_t magic, io_locker_func locker)
|
||||
{
|
||||
my_magic = magic;
|
||||
io_lock = locker;
|
||||
save_load();
|
||||
save_loop();
|
||||
@ -142,7 +144,7 @@ void save_request(bool immediately)
|
||||
if (!requesting_save) {
|
||||
printf("Save marked.\n");
|
||||
requesting_save = true;
|
||||
new_data.magic = SAVE_PAGE_MAGIC;
|
||||
new_data.magic = my_magic;
|
||||
requesting_time = time_us_64();
|
||||
}
|
||||
if (immediately) {
|
||||
|
@ -7,11 +7,12 @@
|
||||
#define SAVE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* It's safer to lock other I/O ops during saving, so we need a locker */
|
||||
typedef void (*io_locker_func)(bool pause);
|
||||
void save_init(io_locker_func locker);
|
||||
void save_init(uint32_t magic, io_locker_func locker);
|
||||
|
||||
void save_loop();
|
||||
|
||||
|
@ -15,9 +15,10 @@
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
#include "board_defs.h"
|
||||
|
||||
#include "mpr121.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define TOUCH_THRESHOLD 17
|
||||
#define RELEASE_THRESHOLD 8
|
||||
|
||||
@ -145,19 +146,23 @@ bool slider_touched(unsigned key)
|
||||
}
|
||||
int delta = baseline[key] - readout[key];
|
||||
|
||||
int bias = chu_cfg->sense.global + chu_cfg->sense.keys[key];
|
||||
int touch_thre = TOUCH_THRESHOLD + bias;
|
||||
int release_thre = RELEASE_THRESHOLD + bias / 2;
|
||||
|
||||
if (touched[key]) {
|
||||
if (delta > TOUCH_THRESHOLD) {
|
||||
if (delta > touch_thre) {
|
||||
debounce[key] = 0;
|
||||
}
|
||||
if (debounce[key] > 4) {
|
||||
if (delta < RELEASE_THRESHOLD) {
|
||||
if (debounce[key] > chu_cfg->sense.debounce) {
|
||||
if (delta < release_thre) {
|
||||
touched[key] = false;
|
||||
}
|
||||
} else {
|
||||
debounce[key]++;
|
||||
}
|
||||
} else if (!touched[key]) {
|
||||
if (delta > TOUCH_THRESHOLD) {
|
||||
if (delta > touch_thre) {
|
||||
touched[key] = true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user