V45 Release Candidate Changes (#227)
@@ -30,7 +30,7 @@ bindings/
|
||||
Brewfile.lock.json
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
/.vscode/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
|
||||
@@ -1 +1 @@
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/xtreme -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
|
||||
|
||||
@@ -32,18 +32,23 @@ Note, the below mentioned changes are only a few things we did. For a full list
|
||||
|
||||
We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented:
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/49810075/228392945-1e68b996-4e2c-46c6-8aae-d0aadd8ea001.gif" align="left" width="400px"/>
|
||||
<!--
|
||||
|
||||
<ins><b>Interface:</b></ins>
|
||||
Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode(yes, we thought about you :3 ).
|
||||
This image needs to be updated!
|
||||
Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it fits all our bulletpoints without issues
|
||||
|
||||
<ins><b>Protocols:</b></ins>
|
||||
Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options.
|
||||
-->
|
||||
|
||||
<ins><b>Misc:</b></ins>
|
||||
All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight.
|
||||
<img src="https://user-images.githubusercontent.com/49810075/228392945-1e68b996-4e2c-46c6-8aae-d0aadd8ea001.gif" align="left" height="160vh"/>
|
||||
<img align="left" height="180vh" width="10" src="https://upload.wikimedia.org/wikipedia/commons/3/3d/1_120_transparent.png">
|
||||
|
||||
<br clear="left"/>
|
||||
- <ins><b>Interface:</b></ins> Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc.
|
||||
|
||||
- <ins><b>Protocols:</b></ins> Here you can toggle between USB & Bluetooth mode for <a href="https://github.com/ClaraCrazy/Flipper-Xtreme/wiki/Generic-Guides#badbt--kb">BadKB</a>, and manage custom Subghz frequencies.
|
||||
|
||||
- <ins><b>Misc:</b></ins> All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and configure the <a href="https://github.com/Z3BRO/Flipper-Zero-RGB-Backlight">RGB backlight</a>.
|
||||
|
||||
<br>
|
||||
|
||||
-----
|
||||
<br>
|
||||
|
||||
@@ -23,7 +23,7 @@ void AccessorApp::run(void) {
|
||||
exit = switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
scenes[current_scene]->on_exit(this);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val)
|
||||
canvas_draw_box(canvas, x - 4, y + 16, 24, 6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);
|
||||
};
|
||||
}
|
||||
|
||||
static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
|
||||
char emote[20] = {};
|
||||
@@ -85,7 +85,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
|
||||
canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);
|
||||
canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);
|
||||
canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);
|
||||
};
|
||||
}
|
||||
|
||||
static void battery_info_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
App(
|
||||
appid="UART_Echo",
|
||||
name="[GPIO] UART Echo",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="uart_echo_app",
|
||||
cdefines=["APP_UART_ECHO"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_icon="uart_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
#include <lib/nfc/helpers/mf_classic_dict.h>
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
#include <lib/pulse_reader/pulse_reader.h>
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
#include <lib/nfc/helpers/nfc_generators.h>
|
||||
|
||||
@@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) {
|
||||
"NFC long digital signal test failed\r\n");
|
||||
}
|
||||
|
||||
static bool nfc_test_pulse_reader_toggle(
|
||||
uint32_t usec_low,
|
||||
uint32_t usec_high,
|
||||
uint32_t period_count,
|
||||
uint32_t tolerance) {
|
||||
furi_assert(nfc_test);
|
||||
|
||||
bool success = false;
|
||||
uint32_t pulses = 0;
|
||||
const GpioPin* gpio_in = &gpio_ext_pa6;
|
||||
const GpioPin* gpio_out = &gpio_ext_pa7;
|
||||
PulseReader* reader = NULL;
|
||||
|
||||
do {
|
||||
reader = pulse_reader_alloc(gpio_in, 512);
|
||||
|
||||
if(!reader) {
|
||||
FURI_LOG_E(TAG, "failed to allocate pulse reader");
|
||||
break;
|
||||
}
|
||||
|
||||
/* use TIM1 to create a specific number of pulses with defined duty cycle
|
||||
but first set the IO to high, so the low/high pulse can get detected */
|
||||
furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(gpio_out, true);
|
||||
|
||||
LL_TIM_DeInit(TIM1);
|
||||
|
||||
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetRepetitionCounter(TIM1, 0);
|
||||
LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
LL_TIM_DisableARRPreload(TIM1);
|
||||
|
||||
LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1);
|
||||
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2);
|
||||
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
|
||||
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1);
|
||||
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N);
|
||||
|
||||
LL_TIM_EnableAllOutputs(TIM1);
|
||||
|
||||
/* now calculate the TIM1 period and compare values */
|
||||
uint32_t freq_div = 64 * (usec_low + usec_high);
|
||||
uint32_t prescaler = freq_div / 0x10000LU;
|
||||
uint32_t period = freq_div / (prescaler + 1);
|
||||
uint32_t compare = 64 * usec_low / (prescaler + 1);
|
||||
|
||||
LL_TIM_SetPrescaler(TIM1, prescaler);
|
||||
LL_TIM_SetAutoReload(TIM1, period - 1);
|
||||
LL_TIM_SetCounter(TIM1, period - 1);
|
||||
LL_TIM_OC_SetCompareCH1(TIM1, compare);
|
||||
|
||||
/* timer is ready to launch, now start the pulse reader */
|
||||
pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond);
|
||||
pulse_reader_start(reader);
|
||||
|
||||
/* and quickly enable and switch over the GPIO to the generated signal */
|
||||
LL_TIM_EnableCounter(TIM1);
|
||||
furi_hal_gpio_init_ex(
|
||||
gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1);
|
||||
|
||||
/* now it's time to parse the pulses received by the reader */
|
||||
uint32_t timer_pulses = period_count;
|
||||
uint32_t prev_cnt = 0;
|
||||
|
||||
while(timer_pulses > 0) {
|
||||
/* whenever the counter gets reset, we went through a full period */
|
||||
uint32_t cur_cnt = LL_TIM_GetCounter(TIM1);
|
||||
if(cur_cnt < prev_cnt) {
|
||||
timer_pulses--;
|
||||
}
|
||||
prev_cnt = cur_cnt;
|
||||
}
|
||||
/* quickly halt the counter to keep a static signal */
|
||||
LL_TIM_DisableCounter(TIM1);
|
||||
|
||||
do {
|
||||
/* as all edges were sampled asynchronously, the timeout can be zero */
|
||||
uint32_t length = pulse_reader_receive(reader, 0);
|
||||
|
||||
/* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */
|
||||
if(pulses > period_count * 2) {
|
||||
if(length != PULSE_READER_NO_EDGE) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.",
|
||||
length);
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* else we shall never see "no edge" or "lost edge" */
|
||||
if(length == PULSE_READER_NO_EDGE) {
|
||||
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses);
|
||||
break;
|
||||
}
|
||||
if(length == PULSE_READER_LOST_EDGE) {
|
||||
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses);
|
||||
break;
|
||||
}
|
||||
|
||||
if(pulses > 0) {
|
||||
/* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */
|
||||
bool phase = ((pulses - 1) % 2) == 1;
|
||||
uint32_t expected = phase ? usec_high : usec_low;
|
||||
uint32_t deviation = abs((int32_t)length - (int32_t)expected);
|
||||
|
||||
if(deviation > tolerance) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"%lu. pulse expected %lu, but pulse was %lu.",
|
||||
pulses,
|
||||
expected,
|
||||
length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pulses++;
|
||||
} while(true);
|
||||
} while(false);
|
||||
|
||||
if(reader != NULL) {
|
||||
pulse_reader_stop(reader);
|
||||
pulse_reader_free(reader);
|
||||
}
|
||||
|
||||
LL_TIM_DeInit(TIM1);
|
||||
furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog);
|
||||
furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
MU_TEST(nfc_pulse_reader_test) {
|
||||
mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n");
|
||||
mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n");
|
||||
mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n");
|
||||
mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n");
|
||||
mu_assert(
|
||||
nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n");
|
||||
mu_assert(
|
||||
nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
MfClassicDict* instance = NULL;
|
||||
uint64_t key = 0;
|
||||
@@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) {
|
||||
MU_TEST_SUITE(nfc) {
|
||||
nfc_test_alloc();
|
||||
|
||||
MU_RUN_TEST(nfc_pulse_reader_test);
|
||||
MU_RUN_TEST(nfca_file_test);
|
||||
MU_RUN_TEST(mf_mini_file_test);
|
||||
MU_RUN_TEST(mf_classic_1k_4b_file_test);
|
||||
|
||||
@@ -84,7 +84,7 @@ static void test_rpc_setup(void) {
|
||||
|
||||
rpc = furi_record_open(RECORD_RPC);
|
||||
for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) {
|
||||
rpc_session[0].session = rpc_session_open(rpc);
|
||||
rpc_session[0].session = rpc_session_open(rpc, RpcOwnerUnknown);
|
||||
furi_delay_tick(1);
|
||||
}
|
||||
furi_check(rpc_session[0].session);
|
||||
@@ -104,7 +104,7 @@ static void test_rpc_setup_second_session(void) {
|
||||
furi_check(!(rpc_session[1].session));
|
||||
|
||||
for(int i = 0; !(rpc_session[1].session) && (i < 10000); ++i) {
|
||||
rpc_session[1].session = rpc_session_open(rpc);
|
||||
rpc_session[1].session = rpc_session_open(rpc, RpcOwnerUnknown);
|
||||
furi_delay_tick(1);
|
||||
}
|
||||
furi_check(rpc_session[1].session);
|
||||
|
||||
@@ -34,4 +34,4 @@ private:
|
||||
|
||||
} // namespace cardboard
|
||||
|
||||
#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_
|
||||
#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_
|
||||
|
||||
@@ -8,8 +8,8 @@ App(
|
||||
stack_size=8 * 1024,
|
||||
order=50,
|
||||
fap_icon="appicon.png",
|
||||
fap_icon_assets="assets", # Image assets to compile for this application
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets", # Image assets to compile for this application
|
||||
fap_description="An implementation of the classic arcade game Asteroids",
|
||||
fap_author="antirez, SimplyMinimal",
|
||||
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids",
|
||||
|
||||
@@ -152,7 +152,12 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
|
||||
|
||||
if(instance->spi) {
|
||||
avr_isp_spi_sw_free(instance->spi);
|
||||
instance->spi = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -169,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) {
|
||||
while((furi_get_tick() - starttime) < 30) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -372,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) {
|
||||
ret = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -387,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -407,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) {
|
||||
ret = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -422,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -442,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) {
|
||||
ret = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -457,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -477,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) {
|
||||
ret = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) {
|
||||
uint8_t data[1] = {0};
|
||||
while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) {
|
||||
if(instance->exit) break;
|
||||
};
|
||||
}
|
||||
return data[0];
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ static void avr_isp_prog_set_cfg(AvrIspProg* instance) {
|
||||
instance->cfg->lockbytes = instance->buff[6];
|
||||
instance->cfg->fusebytes = instance->buff[7];
|
||||
instance->cfg->flashpoll = instance->buff[8];
|
||||
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll”
|
||||
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as �flashpoll�
|
||||
instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11];
|
||||
instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13];
|
||||
instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15];
|
||||
@@ -317,7 +317,12 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
|
||||
|
||||
if(instance->spi) {
|
||||
avr_isp_spi_sw_free(instance->spi);
|
||||
instance->spi = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -343,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat
|
||||
while((furi_get_tick() - starttime) < 30) {
|
||||
if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ const char* get_error_code_name(ErrorCode error_code) {
|
||||
return "OK";
|
||||
default:
|
||||
return "Unknown Code";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const char* get_error_code_message(ErrorCode error_code) {
|
||||
@@ -121,5 +121,5 @@ const char* get_error_code_message(ErrorCode error_code) {
|
||||
return "OK";
|
||||
default:
|
||||
return "Could not read barcode data";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 лень
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
App(
|
||||
appid="bomberduck",
|
||||
name="Bomberduck",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="bomberduck_app",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="bomb.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
)
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,643 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/canvas_i.h>
|
||||
#include "bomberduck_icons.h"
|
||||
|
||||
int max(int a, int b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
int min(int a, int b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
#define WorldSizeX 12
|
||||
#define WorldSizeY 6
|
||||
#define BombRange 1
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
} BomberState;
|
||||
|
||||
typedef struct {
|
||||
int row;
|
||||
int col;
|
||||
} Cell;
|
||||
|
||||
typedef struct {
|
||||
Cell cells[WorldSizeY * WorldSizeX];
|
||||
int front;
|
||||
int rear;
|
||||
} Queue;
|
||||
|
||||
void enqueue(Queue* q, Cell c) {
|
||||
q->cells[q->rear] = c;
|
||||
q->rear++;
|
||||
}
|
||||
|
||||
Cell dequeue(Queue* q) {
|
||||
Cell c = q->cells[q->front];
|
||||
q->front++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool is_empty(Queue* q) {
|
||||
return q->front == q->rear;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
int planted;
|
||||
} Bomb;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
bool side;
|
||||
} Player;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
int last;
|
||||
bool side;
|
||||
int level;
|
||||
} Enemy;
|
||||
|
||||
typedef struct {
|
||||
int matrix[WorldSizeY][WorldSizeX];
|
||||
Player* player;
|
||||
bool running;
|
||||
int level;
|
||||
|
||||
Enemy enemies[10];
|
||||
int enemies_count;
|
||||
|
||||
Bomb bombs[100];
|
||||
int bombs_count;
|
||||
|
||||
int endx;
|
||||
int endy;
|
||||
} World;
|
||||
|
||||
Player player = {0, 0, 1};
|
||||
World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0};
|
||||
bool vibration = false;
|
||||
|
||||
void init() {
|
||||
player.x = 1;
|
||||
player.y = 1;
|
||||
|
||||
world.endx = 4 + rand() % 8;
|
||||
world.endy = rand() % 6;
|
||||
for(int i = 0; i < WorldSizeY; i++) {
|
||||
for(int j = 0; j < WorldSizeX; j++) {
|
||||
world.matrix[i][j] = rand() % 3;
|
||||
}
|
||||
}
|
||||
world.running = 1;
|
||||
world.bombs_count = 0;
|
||||
vibration = false;
|
||||
for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) {
|
||||
world.matrix[j][player.x] = 0;
|
||||
}
|
||||
|
||||
for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) {
|
||||
world.matrix[player.y][j] = 0;
|
||||
}
|
||||
|
||||
world.enemies_count = 0;
|
||||
for(int j = 0; j < rand() % 4 + world.level / 5; j++) {
|
||||
Enemy enemy;
|
||||
enemy.x = 4 + rand() % 7;
|
||||
enemy.y = rand() % 6;
|
||||
enemy.last = 0;
|
||||
enemy.side = 1;
|
||||
enemy.level = 0;
|
||||
|
||||
world.enemies[j] = enemy;
|
||||
world.enemies_count++;
|
||||
|
||||
for(int m = max(0, world.enemies[j].y - BombRange);
|
||||
m < min(WorldSizeY, world.enemies[j].y + BombRange + 1);
|
||||
m++) {
|
||||
world.matrix[m][world.enemies[j].x] = 0;
|
||||
}
|
||||
|
||||
for(int m = max(0, world.enemies[j].x - BombRange);
|
||||
m < min(WorldSizeX, world.enemies[j].x + BombRange + 1);
|
||||
m++) {
|
||||
world.matrix[world.enemies[j].y][m] = 0;
|
||||
}
|
||||
}
|
||||
world.matrix[world.endy][world.endx] = 1;
|
||||
}
|
||||
|
||||
const NotificationSequence end = {
|
||||
&message_vibro_on,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence bomb2 = {
|
||||
&message_vibro_on,
|
||||
&message_delay_25,
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence bomb_explore = {
|
||||
&message_vibro_on,
|
||||
&message_delay_50,
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence vibr1 = {
|
||||
&message_vibro_on,
|
||||
&message_delay_10,
|
||||
&message_vibro_off,
|
||||
&message_delay_10,
|
||||
&message_vibro_on,
|
||||
&message_delay_10,
|
||||
&message_vibro_off,
|
||||
&message_delay_10,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
void intToStr(int num, char* str) {
|
||||
int i = 0, sign = 0;
|
||||
|
||||
if(num < 0) {
|
||||
num = -num;
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
do {
|
||||
str[i++] = num % 10 + '0';
|
||||
num /= 10;
|
||||
} while(num > 0);
|
||||
|
||||
if(sign) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
// Reverse the string
|
||||
int j, len = i;
|
||||
char temp;
|
||||
for(j = 0; j < len / 2; j++) {
|
||||
temp = str[j];
|
||||
str[j] = str[len - j - 1];
|
||||
str[len - j - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
bool BFS() {
|
||||
// Initialize visited array and queue
|
||||
int visited[WorldSizeY][WorldSizeX] = {0};
|
||||
Queue q = {.front = 0, .rear = 0};
|
||||
// Mark the starting cell as visited and enqueue it
|
||||
visited[world.player->y][world.player->x] = 1;
|
||||
Cell startCell = {.row = world.player->y, .col = world.player->x};
|
||||
enqueue(&q, startCell);
|
||||
// Traverse the field
|
||||
while(!is_empty(&q)) {
|
||||
// Dequeue a cell from the queue
|
||||
Cell currentCell = dequeue(&q);
|
||||
// Check if the current cell is the destination cell
|
||||
if(currentCell.row == world.endy && currentCell.col == world.endx) {
|
||||
return true;
|
||||
}
|
||||
// Check the neighboring cells
|
||||
for(int rowOffset = -1; rowOffset <= 1; rowOffset++) {
|
||||
for(int colOffset = -1; colOffset <= 1; colOffset++) {
|
||||
// Skip diagonals and the current cell
|
||||
if(rowOffset == 0 && colOffset == 0) {
|
||||
continue;
|
||||
}
|
||||
if(rowOffset != 0 && colOffset != 0) {
|
||||
continue;
|
||||
}
|
||||
// Calculate the row and column of the neighboring cell
|
||||
int neighborRow = currentCell.row + rowOffset;
|
||||
int neighborCol = currentCell.col + colOffset;
|
||||
// Skip out-of-bounds cells and already visited cells
|
||||
if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 ||
|
||||
neighborCol >= WorldSizeX) {
|
||||
continue;
|
||||
}
|
||||
if(visited[neighborRow][neighborCol]) {
|
||||
continue;
|
||||
}
|
||||
// Mark the neighboring cell as visited and enqueue it
|
||||
if(world.matrix[neighborRow][neighborCol] != 2) {
|
||||
visited[neighborRow][neighborCol] = 1;
|
||||
Cell neighborCell = {.row = neighborRow, .col = neighborCol};
|
||||
enqueue(&q, neighborCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
const BomberState* bomber_state = ctx;
|
||||
|
||||
furi_mutex_acquire(bomber_state->mutex, FuriWaitForever);
|
||||
if(!BFS()) {
|
||||
init();
|
||||
}
|
||||
canvas_clear(canvas);
|
||||
|
||||
canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end);
|
||||
|
||||
if(world.running) {
|
||||
for(size_t i = 0; i < WorldSizeY; i++) {
|
||||
for(size_t j = 0; j < WorldSizeX; j++) {
|
||||
switch(world.matrix[i][j]) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box);
|
||||
break;
|
||||
case 2:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox);
|
||||
break;
|
||||
case 3:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0);
|
||||
break;
|
||||
case 4:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1);
|
||||
break;
|
||||
case 5:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2);
|
||||
break;
|
||||
case 6:
|
||||
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore);
|
||||
world.matrix[i][j] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(world.player->side) {
|
||||
canvas_draw_icon(
|
||||
canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright);
|
||||
} else {
|
||||
canvas_draw_icon(
|
||||
canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft);
|
||||
}
|
||||
|
||||
for(int i = 0; i < world.enemies_count; i++) {
|
||||
if(world.enemies[i].level > 0) {
|
||||
canvas_draw_icon(
|
||||
canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1);
|
||||
} else {
|
||||
if(world.enemies[i].side) {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
world.enemies[i].x * 10 + 4,
|
||||
world.enemies[i].y * 10 + 2,
|
||||
&I_enemyright);
|
||||
} else {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
world.enemies[i].x * 10 + 4,
|
||||
world.enemies[i].y * 10 + 2,
|
||||
&I_enemyleft);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if(world.player->x == world.endx && world.player->y == world.endy) {
|
||||
if(world.level == 20) {
|
||||
canvas_draw_str(canvas, 30, 35, "You win!");
|
||||
} else {
|
||||
canvas_draw_str(canvas, 30, 35, "Next level!");
|
||||
char str[20];
|
||||
intToStr(world.level, str);
|
||||
canvas_draw_str(canvas, 90, 35, str);
|
||||
}
|
||||
|
||||
} else {
|
||||
canvas_draw_str(canvas, 30, 35, "You died :(");
|
||||
}
|
||||
}
|
||||
|
||||
furi_mutex_release(bomber_state->mutex);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
// Проверяем, что контекст не нулевой
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t bomberduck_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// Текущее событие типа InputEvent
|
||||
InputEvent event;
|
||||
// Очередь событий на 8 элементов размера InputEvent
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
BomberState* bomber_state = malloc(sizeof(BomberState));
|
||||
|
||||
bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex
|
||||
if(!bomber_state->mutex) {
|
||||
FURI_LOG_E("BomberDuck", "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
free(bomber_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Создаем новый view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
// Создаем callback отрисовки, без контекста
|
||||
view_port_draw_callback_set(view_port, draw_callback, bomber_state);
|
||||
// Создаем callback нажатий на клавиши, в качестве контекста передаем
|
||||
// нашу очередь сообщений, чтоб запихивать в неё эти события
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Создаем GUI приложения
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
// Подключаем view port к GUI в полноэкранном режиме
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message_block(notification, &sequence_display_backlight_enforce_on);
|
||||
|
||||
init();
|
||||
|
||||
// Бесконечный цикл обработки очереди событий
|
||||
while(1) {
|
||||
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
|
||||
furi_mutex_acquire(bomber_state->mutex, FuriWaitForever);
|
||||
// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
|
||||
|
||||
if(event.type == InputTypePress) {
|
||||
if(event.key == InputKeyOk) {
|
||||
if(world.running) {
|
||||
if(world.matrix[world.player->y][world.player->x] == 0 &&
|
||||
world.bombs_count < 2) {
|
||||
notification_message(notification, &bomb2);
|
||||
world.matrix[world.player->y][world.player->x] = 3;
|
||||
Bomb bomb = {world.player->x, world.player->y, furi_get_tick()};
|
||||
world.bombs[world.bombs_count] = bomb;
|
||||
world.bombs_count++;
|
||||
}
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}
|
||||
if(world.running) {
|
||||
if(event.key == InputKeyUp) {
|
||||
if(world.player->y > 0 &&
|
||||
world.matrix[world.player->y - 1][world.player->x] == 0)
|
||||
world.player->y--;
|
||||
}
|
||||
if(event.key == InputKeyDown) {
|
||||
if(world.player->y < WorldSizeY - 1 &&
|
||||
world.matrix[world.player->y + 1][world.player->x] == 0)
|
||||
world.player->y++;
|
||||
}
|
||||
if(event.key == InputKeyLeft) {
|
||||
world.player->side = 0;
|
||||
if(world.player->x > 0 &&
|
||||
world.matrix[world.player->y][world.player->x - 1] == 0)
|
||||
world.player->x--;
|
||||
}
|
||||
if(event.key == InputKeyRight) {
|
||||
world.player->side = 1;
|
||||
if(world.player->x < WorldSizeX - 1 &&
|
||||
world.matrix[world.player->y][world.player->x + 1] == 0)
|
||||
world.player->x++;
|
||||
}
|
||||
}
|
||||
} else if(event.type == InputTypeLong) {
|
||||
if(event.key == InputKeyBack) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(world.running) {
|
||||
if(world.player->x == world.endx && world.player->y == world.endy) {
|
||||
notification_message(notification, &end);
|
||||
world.running = 0;
|
||||
world.level += 1;
|
||||
}
|
||||
for(int i = 0; i < world.bombs_count; i++) {
|
||||
if(furi_get_tick() - world.bombs[i].planted >
|
||||
(unsigned long)max((3000 - world.level * 150), 1000)) {
|
||||
vibration = false;
|
||||
world.matrix[world.bombs[i].y][world.bombs[i].x] = 6;
|
||||
notification_message(notification, &bomb_explore);
|
||||
|
||||
for(int j = max(0, world.bombs[i].y - BombRange);
|
||||
j < min(WorldSizeY, world.bombs[i].y + BombRange + 1);
|
||||
j++) {
|
||||
if(world.matrix[j][world.bombs[i].x] != 2) {
|
||||
world.matrix[j][world.bombs[i].x] = 6;
|
||||
if(j == world.player->y && world.bombs[i].x == world.player->x) {
|
||||
notification_message(notification, &end);
|
||||
world.running = 0;
|
||||
}
|
||||
for(int e = 0; e < world.enemies_count; e++) {
|
||||
if(j == world.enemies[e].y &&
|
||||
world.bombs[i].x == world.enemies[e].x) {
|
||||
if(world.enemies[e].level > 0) {
|
||||
world.enemies[e].level--;
|
||||
} else {
|
||||
for(int l = e; l < world.enemies_count - 1; l++) {
|
||||
world.enemies[l] = world.enemies[l + 1];
|
||||
}
|
||||
world.enemies_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int j = max(0, world.bombs[i].x - BombRange);
|
||||
j < min(WorldSizeX, world.bombs[i].x + BombRange + 1);
|
||||
j++) {
|
||||
if(world.matrix[world.bombs[i].y][j] != 2) {
|
||||
world.matrix[world.bombs[i].y][j] = 6;
|
||||
if(world.bombs[i].y == world.player->y && j == world.player->x) {
|
||||
notification_message(notification, &end);
|
||||
world.running = 0;
|
||||
}
|
||||
for(int e = 0; e < world.enemies_count; e++) {
|
||||
if(world.bombs[i].y == world.enemies[e].y &&
|
||||
j == world.enemies[e].x) {
|
||||
if(world.enemies[e].level > 0) {
|
||||
world.enemies[e].level--;
|
||||
} else {
|
||||
for(int l = e; l < world.enemies_count - 1; l++) {
|
||||
world.enemies[l] = world.enemies[l + 1];
|
||||
}
|
||||
world.enemies_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int j = i; j < world.bombs_count - 1; j++) {
|
||||
world.bombs[j] = world.bombs[j + 1];
|
||||
}
|
||||
world.bombs_count--;
|
||||
} else if(
|
||||
furi_get_tick() - world.bombs[i].planted >
|
||||
(unsigned long)max((3000 - world.level * 150) * 2 / 3, 666) &&
|
||||
world.matrix[world.bombs[i].y][world.bombs[i].x] != 5) {
|
||||
world.matrix[world.bombs[i].y][world.bombs[i].x] = 5;
|
||||
vibration = true;
|
||||
|
||||
} else if(
|
||||
furi_get_tick() - world.bombs[i].planted >
|
||||
(unsigned long)max((3000 - world.level * 150) / 3, 333) &&
|
||||
world.matrix[world.bombs[i].y][world.bombs[i].x] != 4) {
|
||||
world.matrix[world.bombs[i].y][world.bombs[i].x] = 4;
|
||||
}
|
||||
}
|
||||
for(int e = 0; e < world.enemies_count; e++) {
|
||||
if(world.player->y == world.enemies[e].y &&
|
||||
world.player->x == world.enemies[e].x) {
|
||||
notification_message(notification, &end);
|
||||
world.running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(int e = 0; e < world.enemies_count; e++) {
|
||||
if(world.enemies[e].level > 0) {
|
||||
if(furi_get_tick() - world.enemies[e].last >
|
||||
(unsigned long)max((2000 - world.level * 100), 1000)) {
|
||||
world.enemies[e].last = furi_get_tick();
|
||||
int move = rand() % 4;
|
||||
switch(move) {
|
||||
case 0:
|
||||
if(world.enemies[e].y > 0 &&
|
||||
world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2)
|
||||
world.enemies[e].y--;
|
||||
break;
|
||||
case 1:
|
||||
if(world.enemies[e].y < WorldSizeY - 1 &&
|
||||
world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2)
|
||||
world.enemies[e].y++;
|
||||
break;
|
||||
case 2:
|
||||
world.enemies[e].side = 0;
|
||||
if(world.enemies[e].x > 0 &&
|
||||
world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2)
|
||||
world.enemies[e].x--;
|
||||
break;
|
||||
case 3:
|
||||
world.enemies[e].side = 1;
|
||||
if(world.enemies[e].x < WorldSizeX - 1 &&
|
||||
world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2)
|
||||
world.enemies[e].x++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(furi_get_tick() - world.enemies[e].last >
|
||||
(unsigned long)max((1000 - world.level * 50), 500)) {
|
||||
world.enemies[e].last = furi_get_tick();
|
||||
int move = rand() % 4;
|
||||
switch(move) {
|
||||
case 0:
|
||||
if(world.enemies[e].y > 0 &&
|
||||
world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0)
|
||||
world.enemies[e].y--;
|
||||
break;
|
||||
case 1:
|
||||
if(world.enemies[e].y < WorldSizeY - 1 &&
|
||||
world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0)
|
||||
world.enemies[e].y++;
|
||||
break;
|
||||
case 2:
|
||||
world.enemies[e].side = 0;
|
||||
if(world.enemies[e].x > 0 &&
|
||||
world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0)
|
||||
world.enemies[e].x--;
|
||||
break;
|
||||
case 3:
|
||||
world.enemies[e].side = 1;
|
||||
if(world.enemies[e].x < WorldSizeX - 1 &&
|
||||
world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0)
|
||||
world.enemies[e].x++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int e = 0; e < world.enemies_count; e++) {
|
||||
for(int h = e + 1; h < world.enemies_count; h++) {
|
||||
if(world.enemies[e].y == world.enemies[h].y &&
|
||||
world.enemies[e].x == world.enemies[h].x) {
|
||||
world.enemies[h].level++;
|
||||
for(int l = e; l < world.enemies_count - 1; l++) {
|
||||
world.enemies[l] = world.enemies[l + 1];
|
||||
}
|
||||
world.enemies_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(vibration) {
|
||||
notification_message(notification, &vibr1);
|
||||
}
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
furi_mutex_release(bomber_state->mutex);
|
||||
}
|
||||
|
||||
// Return to normal backlight settings
|
||||
notification_message_block(notification, &sequence_display_backlight_enforce_auto);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
// Специальная очистка памяти, занимаемой очередью
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
// Чистим созданные объекты, связанные с интерфейсом
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
|
||||
furi_mutex_free(bomber_state->mutex);
|
||||
furi_record_close(RECORD_GUI);
|
||||
free(bomber_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -111,7 +111,7 @@ void rShift() {
|
||||
|
||||
memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE);
|
||||
bfStack = (uint8_t*)tmp;
|
||||
};
|
||||
}
|
||||
if(stackPtr > stackSizeReal) {
|
||||
stackSizeReal = stackPtr;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ void generate_calculator_layout(Canvas* canvas) {
|
||||
canvas_draw_str(canvas, 19, 118, " 0");
|
||||
canvas_draw_str(canvas, 35, 118, " .");
|
||||
canvas_draw_str(canvas, 51, 118, " =");
|
||||
};
|
||||
}
|
||||
|
||||
void calculator_draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
@@ -79,8 +79,8 @@ typedef struct {
|
||||
|
||||
void* view_dispatcher;
|
||||
void* primary_menu;
|
||||
void* plugins_menu;
|
||||
void* debug_menu;
|
||||
// void* plugins_menu;
|
||||
// void* debug_menu;
|
||||
void* settings_menu;
|
||||
|
||||
volatile uint8_t lock_count;
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const
|
||||
if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_E(TAG, "Unable to open file");
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
uint16_t ret = 0;
|
||||
do {
|
||||
|
||||
@@ -4,7 +4,7 @@ App(
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="flipfrid_start",
|
||||
requires=["gui", "storage", "dialogs", "input", "notification"],
|
||||
stack_size=1 * 1024,
|
||||
stack_size=2 * 1024,
|
||||
order=180,
|
||||
fap_icon="rfid_10px.png",
|
||||
fap_category="Tools",
|
||||
|
||||
@@ -61,6 +61,16 @@ FlipFridState* flipfrid_alloc() {
|
||||
flipfrid->proto_name = furi_string_alloc();
|
||||
flipfrid->data_str = furi_string_alloc();
|
||||
|
||||
flipfrid->main_menu_items[0] = furi_string_alloc_set("Default Values");
|
||||
flipfrid->main_menu_items[1] = furi_string_alloc_set("BF Customer ID");
|
||||
flipfrid->main_menu_items[2] = furi_string_alloc_set("Load File");
|
||||
flipfrid->main_menu_items[3] = furi_string_alloc_set("Load UIDs from file");
|
||||
|
||||
flipfrid->main_menu_proto_items[0] = furi_string_alloc_set("EM4100");
|
||||
flipfrid->main_menu_proto_items[1] = furi_string_alloc_set("HIDProx");
|
||||
flipfrid->main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley");
|
||||
flipfrid->main_menu_proto_items[3] = furi_string_alloc_set("H10301");
|
||||
|
||||
flipfrid->previous_scene = NoneScene;
|
||||
flipfrid->current_scene = SceneEntryPoint;
|
||||
flipfrid->is_running = true;
|
||||
@@ -103,8 +113,13 @@ void flipfrid_free(FlipFridState* flipfrid) {
|
||||
furi_string_free(flipfrid->proto_name);
|
||||
furi_string_free(flipfrid->data_str);
|
||||
|
||||
free(flipfrid->data);
|
||||
free(flipfrid->payload);
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
furi_string_free(flipfrid->main_menu_items[i]);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
furi_string_free(flipfrid->main_menu_proto_items[i]);
|
||||
}
|
||||
|
||||
// The rest
|
||||
free(flipfrid);
|
||||
|
||||
@@ -75,6 +75,8 @@ typedef struct {
|
||||
FlipFridProtos proto;
|
||||
FuriString* attack_name;
|
||||
FuriString* proto_name;
|
||||
FuriString* main_menu_items[4];
|
||||
FuriString* main_menu_proto_items[4];
|
||||
|
||||
DialogsApp* dialogs;
|
||||
FuriString* notification_msg;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "flipfrid_scene_entrypoint.h"
|
||||
|
||||
FuriString* main_menu_items[4];
|
||||
FuriString* main_menu_proto_items[4];
|
||||
|
||||
void flipfrid_scene_entrypoint_menu_callback(
|
||||
FlipFridState* context,
|
||||
uint32_t index,
|
||||
@@ -68,31 +65,14 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
|
||||
menu_items[i] = furi_string_alloc();
|
||||
}*/
|
||||
|
||||
main_menu_items[0] = furi_string_alloc_set("Default Values");
|
||||
main_menu_items[1] = furi_string_alloc_set("BF Customer ID");
|
||||
main_menu_items[2] = furi_string_alloc_set("Load File");
|
||||
main_menu_items[3] = furi_string_alloc_set("Load UIDs from file");
|
||||
|
||||
context->menu_proto_index = 0;
|
||||
/*for(uint32_t i = 0; i < 4; i++) {
|
||||
menu_proto_items[i] = furi_string_alloc();
|
||||
}*/
|
||||
|
||||
main_menu_proto_items[0] = furi_string_alloc_set("EM4100");
|
||||
main_menu_proto_items[1] = furi_string_alloc_set("HIDProx");
|
||||
main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley");
|
||||
main_menu_proto_items[3] = furi_string_alloc_set("H10301");
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
furi_string_free(main_menu_items[i]);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
furi_string_free(main_menu_proto_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
|
||||
@@ -145,73 +125,77 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(main_menu_items[context->menu_index] != NULL) {
|
||||
if(context->menu_index > FlipFridAttackDefaultValues) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(context->main_menu_items != NULL) {
|
||||
if(context->main_menu_items[context->menu_index] != NULL) {
|
||||
if(context->menu_index > FlipFridAttackDefaultValues) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
24,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(context->main_menu_items[context->menu_index - 1]));
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
24,
|
||||
36,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_items[context->menu_index - 1]));
|
||||
}
|
||||
furi_string_get_cstr(context->main_menu_items[context->menu_index]));
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
36,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_items[context->menu_index]));
|
||||
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
48,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(context->main_menu_items[context->menu_index + 1]));
|
||||
}
|
||||
|
||||
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(context->menu_proto_index > EM4100) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(
|
||||
context->main_menu_proto_items[context->menu_proto_index - 1]));
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<");
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
48,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_items[context->menu_index + 1]));
|
||||
}
|
||||
furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index]));
|
||||
|
||||
if(context->menu_proto_index > EM4100) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index - 1]));
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">");
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<");
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index]));
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">");
|
||||
|
||||
if(context->menu_proto_index < H10301) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index + 1]));
|
||||
if(context->menu_proto_index < H10301) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(
|
||||
context->main_menu_proto_items[context->menu_proto_index + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,7 +356,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
stream_rewind(context->uids_stream);
|
||||
end_of_list = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
if(furi_string_get_char(context->data_str, 0) == '#') continue;
|
||||
if(furi_string_size(context->data_str) != 11) break;
|
||||
break;
|
||||
@@ -370,7 +370,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_error);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// string is valid, parse it in context->payload
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
@@ -394,7 +394,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
stream_rewind(context->uids_stream);
|
||||
end_of_list = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
if(furi_string_get_char(context->data_str, 0) == '#') continue;
|
||||
if(furi_string_size(context->data_str) != 9) break;
|
||||
break;
|
||||
@@ -408,7 +408,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_error);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// string is valid, parse it in context->payload
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
|
||||
@@ -39,33 +39,53 @@ typedef struct {
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
mutexStruct displayStruct;
|
||||
mutexStruct* geigerMutex = ctx;
|
||||
furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever);
|
||||
memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct));
|
||||
furi_mutex_release(geigerMutex->mutex);
|
||||
mutexStruct* mutexVal = ctx;
|
||||
mutexStruct mutexDraw;
|
||||
furi_mutex_acquire(mutexVal->mutex, FuriWaitForever);
|
||||
memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct));
|
||||
furi_mutex_release(mutexVal->mutex);
|
||||
|
||||
char buffer[32];
|
||||
if(displayStruct.data == 0)
|
||||
snprintf(
|
||||
buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm);
|
||||
else if(displayStruct.data == 1)
|
||||
if(mutexDraw.data == 0)
|
||||
snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm);
|
||||
else if(mutexDraw.data == 1)
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%ld cps - %.2f uSv/h",
|
||||
displayStruct.cps,
|
||||
((double)displayStruct.cpm * (double)CONVERSION_FACTOR));
|
||||
else
|
||||
mutexDraw.cps,
|
||||
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR));
|
||||
else if(mutexDraw.data == 2)
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%ld cps - %.2f mSv/y",
|
||||
displayStruct.cps,
|
||||
(((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
|
||||
mutexDraw.cps,
|
||||
(((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
|
||||
else if(mutexDraw.data == 3)
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%ld cps - %.4f Rad/h",
|
||||
mutexDraw.cps,
|
||||
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10000);
|
||||
else if(mutexDraw.data == 4)
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%ld cps - %.2f mR/h",
|
||||
mutexDraw.cps,
|
||||
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10);
|
||||
else
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%ld cps - %.2f uR/h",
|
||||
mutexDraw.cps,
|
||||
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100);
|
||||
|
||||
for(int i = 0; i < SCREEN_SIZE_X; i += 2) {
|
||||
float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef);
|
||||
float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef);
|
||||
|
||||
canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y);
|
||||
canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y);
|
||||
@@ -103,8 +123,7 @@ static void gpiocallback(void* ctx) {
|
||||
furi_message_queue_put(queue, &event, 0);
|
||||
}
|
||||
|
||||
int32_t flipper_geiger_app(void* p) {
|
||||
UNUSED(p);
|
||||
int32_t flipper_geiger_app() {
|
||||
EventApp event;
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
|
||||
|
||||
@@ -127,7 +146,7 @@ int32_t flipper_geiger_app(void* p) {
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, draw_callback, &mutexVal);
|
||||
view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
|
||||
@@ -167,7 +186,7 @@ int32_t flipper_geiger_app(void* p) {
|
||||
if(mutexVal.data != 0)
|
||||
mutexVal.data--;
|
||||
else
|
||||
mutexVal.data = 2;
|
||||
mutexVal.data = 5;
|
||||
|
||||
screenRefresh = 1;
|
||||
furi_mutex_release(mutexVal.mutex);
|
||||
@@ -175,7 +194,7 @@ int32_t flipper_geiger_app(void* p) {
|
||||
event.input.type == InputTypeShort)) {
|
||||
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
|
||||
|
||||
if(mutexVal.data != 2)
|
||||
if(mutexVal.data != 5)
|
||||
mutexVal.data++;
|
||||
else
|
||||
mutexVal.data = 0;
|
||||
|
||||
|
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 135 B |
|
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 135 B |
@@ -82,7 +82,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
|
||||
if(model->rx_active)
|
||||
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||
else
|
||||
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
|
||||
}
|
||||
|
||||
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
||||
@@ -139,20 +139,14 @@ int32_t gps_app(void* p) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
gps_uart_deinit_thread(gps_uart);
|
||||
switch(gps_uart->baudrate) {
|
||||
case GPS_BAUDRATE_9k:
|
||||
gps_uart->baudrate = GPS_BAUDRATE_57k;
|
||||
break;
|
||||
case GPS_BAUDRATE_57k:
|
||||
gps_uart->baudrate = GPS_BAUDRATE_115k;
|
||||
break;
|
||||
case GPS_BAUDRATE_115k:
|
||||
gps_uart->baudrate = GPS_BAUDRATE_9k;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
const int baudrate_length =
|
||||
sizeof(gps_baudrates) / sizeof(gps_baudrates[0]);
|
||||
current_gps_baudrate++;
|
||||
if(current_gps_baudrate >= baudrate_length) {
|
||||
current_gps_baudrate = 0;
|
||||
}
|
||||
gps_uart->baudrate = gps_baudrates[current_gps_baudrate];
|
||||
|
||||
gps_uart_init_thread(gps_uart);
|
||||
gps_uart->changing_baudrate = true;
|
||||
view_port_update(view_port);
|
||||
|
||||
@@ -169,7 +169,7 @@ GpsUart* gps_uart_enable() {
|
||||
|
||||
gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
gps_uart->baudrate = GPS_BAUDRATE_57k;
|
||||
gps_uart->baudrate = gps_baudrates[current_gps_baudrate];
|
||||
|
||||
gps_uart_init_thread(gps_uart);
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#include <furi_hal.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#define GPS_BAUDRATE_9k 9600
|
||||
#define GPS_BAUDRATE_57k 57600
|
||||
#define GPS_BAUDRATE_115k 115200
|
||||
#define RX_BUF_SIZE 1024
|
||||
|
||||
static const int gps_baudrates[5] = {9600, 19200, 38400, 57600, 115200};
|
||||
static int current_gps_baudrate = 3;
|
||||
|
||||
typedef struct {
|
||||
bool valid;
|
||||
float latitude;
|
||||
|
||||
@@ -163,7 +163,7 @@ static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) {
|
||||
FURI_LOG_E(TAG, "Unable to open stream: %s", file_path);
|
||||
isOk = false;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
hex_viewer->model->file_size = stream_size(hex_viewer->model->stream);
|
||||
} while(false);
|
||||
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -7,10 +7,13 @@
|
||||
|
||||
enum HidDebugSubmenuIndex {
|
||||
HidSubmenuIndexKeynote,
|
||||
HidSubmenuIndexKeynoteVertical,
|
||||
HidSubmenuIndexKeyboard,
|
||||
HidSubmenuIndexMedia,
|
||||
HidSubmenuIndexTikTok,
|
||||
HidSubmenuIndexYTShorts,
|
||||
HidSubmenuIndexMouse,
|
||||
HidSubmenuIndexMouseClicker,
|
||||
HidSubmenuIndexMouseJiggler,
|
||||
};
|
||||
|
||||
@@ -20,6 +23,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
|
||||
if(index == HidSubmenuIndexKeynote) {
|
||||
app->view_id = HidViewKeynote;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
|
||||
} else if(index == HidSubmenuIndexKeynoteVertical) {
|
||||
app->view_id = HidViewKeynoteVertical;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical);
|
||||
} else if(index == HidSubmenuIndexKeyboard) {
|
||||
app->view_id = HidViewKeyboard;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
|
||||
@@ -32,6 +38,12 @@ static void hid_submenu_callback(void* context, uint32_t index) {
|
||||
} else if(index == HidSubmenuIndexTikTok) {
|
||||
app->view_id = BtHidViewTikTok;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
|
||||
} else if(index == HidSubmenuIndexYTShorts) {
|
||||
app->view_id = BtHidViewYTShorts;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts);
|
||||
} else if(index == HidSubmenuIndexMouseClicker) {
|
||||
app->view_id = HidViewMouseClicker;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
|
||||
} else if(index == HidSubmenuIndexMouseJiggler) {
|
||||
app->view_id = HidViewMouseJiggler;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
|
||||
@@ -50,11 +62,14 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
|
||||
}
|
||||
}
|
||||
hid_keynote_set_connected_status(hid->hid_keynote, connected);
|
||||
hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected);
|
||||
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
|
||||
hid_media_set_connected_status(hid->hid_media, connected);
|
||||
hid_mouse_set_connected_status(hid->hid_mouse, connected);
|
||||
hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
|
||||
hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
|
||||
hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
|
||||
hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected);
|
||||
}
|
||||
|
||||
static void hid_dialog_callback(DialogExResult result, void* context) {
|
||||
@@ -100,6 +115,12 @@ Hid* hid_alloc(HidTransport transport) {
|
||||
app->device_type_submenu = submenu_alloc();
|
||||
submenu_add_item(
|
||||
app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->device_type_submenu,
|
||||
"Keynote Vertical",
|
||||
HidSubmenuIndexKeynoteVertical,
|
||||
hid_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
@@ -113,7 +134,19 @@ Hid* hid_alloc(HidTransport transport) {
|
||||
HidSubmenuIndexTikTok,
|
||||
hid_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->device_type_submenu,
|
||||
"YT Shorts Controller",
|
||||
HidSubmenuIndexYTShorts,
|
||||
hid_submenu_callback,
|
||||
app);
|
||||
}
|
||||
submenu_add_item(
|
||||
app->device_type_submenu,
|
||||
"Mouse Clicker",
|
||||
HidSubmenuIndexMouseClicker,
|
||||
hid_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->device_type_submenu,
|
||||
"Mouse Jiggler",
|
||||
@@ -148,6 +181,15 @@ Hid* hid_app_alloc_view(void* context) {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
|
||||
|
||||
// Keynote Vertical view
|
||||
app->hid_keynote_vertical = hid_keynote_vertical_alloc(app);
|
||||
view_set_previous_callback(
|
||||
hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
HidViewKeynoteVertical,
|
||||
hid_keynote_vertical_get_view(app->hid_keynote_vertical));
|
||||
|
||||
// Keyboard view
|
||||
app->hid_keyboard = hid_keyboard_alloc(app);
|
||||
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
|
||||
@@ -166,12 +208,26 @@ Hid* hid_app_alloc_view(void* context) {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok));
|
||||
|
||||
// YTShorts view
|
||||
app->hid_ytshorts = hid_ytshorts_alloc(app);
|
||||
view_set_previous_callback(hid_ytshorts_get_view(app->hid_ytshorts), hid_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BtHidViewYTShorts, hid_ytshorts_get_view(app->hid_ytshorts));
|
||||
|
||||
// Mouse view
|
||||
app->hid_mouse = hid_mouse_alloc(app);
|
||||
view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
|
||||
|
||||
// Mouse clicker view
|
||||
app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);
|
||||
view_set_previous_callback(
|
||||
hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
HidViewMouseClicker,
|
||||
hid_mouse_clicker_get_view(app->hid_mouse_clicker));
|
||||
// Mouse jiggler view
|
||||
app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
|
||||
view_set_previous_callback(
|
||||
@@ -199,16 +255,22 @@ void hid_free(Hid* app) {
|
||||
dialog_ex_free(app->dialog);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
|
||||
hid_keynote_free(app->hid_keynote);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical);
|
||||
hid_keynote_vertical_free(app->hid_keynote_vertical);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
|
||||
hid_keyboard_free(app->hid_keyboard);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
|
||||
hid_media_free(app->hid_media);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
|
||||
hid_mouse_free(app->hid_mouse);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);
|
||||
hid_mouse_clicker_free(app->hid_mouse_clicker);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
|
||||
hid_mouse_jiggler_free(app->hid_mouse_jiggler);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
|
||||
hid_tiktok_free(app->hid_tiktok);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewYTShorts);
|
||||
hid_ytshorts_free(app->hid_ytshorts);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Close records
|
||||
|
||||
@@ -17,11 +17,14 @@
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "views/hid_keynote.h"
|
||||
#include "views/hid_keynote_vertical.h"
|
||||
#include "views/hid_keyboard.h"
|
||||
#include "views/hid_media.h"
|
||||
#include "views/hid_mouse.h"
|
||||
#include "views/hid_mouse_jiggler.h"
|
||||
#include "views/hid_tiktok.h"
|
||||
#include "views/hid_ytshorts.h"
|
||||
#include "views/hid_mouse_clicker.h"
|
||||
|
||||
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
||||
|
||||
@@ -40,11 +43,14 @@ struct Hid {
|
||||
Submenu* device_type_submenu;
|
||||
DialogEx* dialog;
|
||||
HidKeynote* hid_keynote;
|
||||
HidKeynoteVertical* hid_keynote_vertical;
|
||||
HidKeyboard* hid_keyboard;
|
||||
HidMedia* hid_media;
|
||||
HidMouse* hid_mouse;
|
||||
HidMouseClicker* hid_mouse_clicker;
|
||||
HidMouseJiggler* hid_mouse_jiggler;
|
||||
HidTikTok* hid_tiktok;
|
||||
HidYTShorts* hid_ytshorts;
|
||||
|
||||
HidTransport transport;
|
||||
uint32_t view_id;
|
||||
@@ -62,4 +68,4 @@ void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy);
|
||||
void hid_hal_mouse_scroll(Hid* instance, int8_t delta);
|
||||
void hid_hal_mouse_press(Hid* instance, uint16_t event);
|
||||
void hid_hal_mouse_release(Hid* instance, uint16_t event);
|
||||
void hid_hal_mouse_release_all(Hid* instance);
|
||||
void hid_hal_mouse_release_all(Hid* instance);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
typedef enum {
|
||||
HidViewSubmenu,
|
||||
HidViewKeynote,
|
||||
HidViewKeynoteVertical,
|
||||
HidViewKeyboard,
|
||||
HidViewMedia,
|
||||
HidViewMouse,
|
||||
HidViewMouseClicker,
|
||||
HidViewMouseJiggler,
|
||||
BtHidViewTikTok,
|
||||
BtHidViewYTShorts,
|
||||
HidViewExitConfirm,
|
||||
} HidView;
|
||||
} HidView;
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
#include "hid_keynote_vertical.h"
|
||||
#include <gui/elements.h>
|
||||
#include "../hid.h"
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#define TAG "HidKeynoteVertical"
|
||||
|
||||
struct HidKeynoteVertical {
|
||||
View* view;
|
||||
Hid* hid;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool left_pressed;
|
||||
bool up_pressed;
|
||||
bool right_pressed;
|
||||
bool down_pressed;
|
||||
bool ok_pressed;
|
||||
bool back_pressed;
|
||||
bool connected;
|
||||
HidTransport transport;
|
||||
} HidKeynoteVerticalModel;
|
||||
|
||||
static void
|
||||
hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
||||
if(dir == CanvasDirectionBottomToTop) {
|
||||
canvas_draw_line(canvas, x, y + 6, x, y - 1);
|
||||
} else if(dir == CanvasDirectionTopToBottom) {
|
||||
canvas_draw_line(canvas, x, y - 6, x, y + 1);
|
||||
} else if(dir == CanvasDirectionRightToLeft) {
|
||||
canvas_draw_line(canvas, x + 6, y, x - 1, y);
|
||||
} else if(dir == CanvasDirectionLeftToRight) {
|
||||
canvas_draw_line(canvas, x - 6, y, x + 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidKeynoteVerticalModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->");
|
||||
|
||||
canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit");
|
||||
|
||||
// Up
|
||||
canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);
|
||||
if(model->up_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
canvas_draw_icon(canvas, 21, 45, &I_Button_18x18);
|
||||
if(model->down_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
canvas_draw_icon(canvas, 0, 35, &I_Button_18x18);
|
||||
if(model->left_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 3, 37, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
canvas_draw_icon(canvas, 42, 35, &I_Button_18x18);
|
||||
if(model->right_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 45, 37, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
|
||||
if(model->ok_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
|
||||
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
canvas_draw_icon(canvas, 63, 45, &I_Space_65x18);
|
||||
if(model->back_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
|
||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
||||
}
|
||||
|
||||
static void
|
||||
hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) {
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = true;
|
||||
}
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = false;
|
||||
}
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyBack) {
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
|
||||
hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
|
||||
hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
HidKeynoteVertical* hid_keynote_vertical = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||
hid_hal_keyboard_release_all(hid_keynote_vertical->hid);
|
||||
} else {
|
||||
hid_keynote_vertical_process(hid_keynote_vertical, event);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) {
|
||||
HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical));
|
||||
hid_keynote_vertical->view = view_alloc();
|
||||
hid_keynote_vertical->hid = hid;
|
||||
view_set_context(hid_keynote_vertical->view, hid_keynote_vertical);
|
||||
view_allocate_model(
|
||||
hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel));
|
||||
view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback);
|
||||
view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{ model->transport = hid->transport; },
|
||||
true);
|
||||
|
||||
return hid_keynote_vertical;
|
||||
}
|
||||
|
||||
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
view_free(hid_keynote_vertical->view);
|
||||
free(hid_keynote_vertical);
|
||||
}
|
||||
|
||||
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
return hid_keynote_vertical->view;
|
||||
}
|
||||
|
||||
void hid_keynote_vertical_set_connected_status(
|
||||
HidKeynoteVertical* hid_keynote_vertical,
|
||||
bool connected) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{ model->connected = connected; },
|
||||
true);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct Hid Hid;
|
||||
typedef struct HidKeynoteVertical HidKeynoteVertical;
|
||||
|
||||
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid);
|
||||
|
||||
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical);
|
||||
|
||||
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical);
|
||||
|
||||
void hid_keynote_vertical_set_connected_status(
|
||||
HidKeynoteVertical* hid_keynote_vertical,
|
||||
bool connected);
|
||||
@@ -21,6 +21,7 @@ typedef struct {
|
||||
bool down_pressed;
|
||||
bool ok_pressed;
|
||||
bool connected;
|
||||
bool back_pressed;
|
||||
HidTransport transport;
|
||||
} HidMediaModel;
|
||||
|
||||
@@ -55,61 +56,72 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// Keypad circles
|
||||
canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
|
||||
canvas_draw_icon(canvas, 58, 3, &I_OutCircles);
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 6, &I_S_UP);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6);
|
||||
canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6);
|
||||
canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
|
||||
hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
|
||||
hid_media_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft);
|
||||
hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
|
||||
hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
|
||||
hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight);
|
||||
hid_media_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
if(model->ok_pressed) {
|
||||
canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
|
||||
canvas_draw_line(canvas, 100, 29, 100, 33);
|
||||
canvas_draw_line(canvas, 102, 29, 102, 33);
|
||||
hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight);
|
||||
canvas_draw_line(canvas, 84, 26, 84, 30);
|
||||
canvas_draw_line(canvas, 86, 26, 86, 30);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Exit
|
||||
if(model->back_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||
@@ -135,6 +147,8 @@ static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) {
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = true;
|
||||
}
|
||||
},
|
||||
true);
|
||||
@@ -160,6 +174,8 @@ static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) {
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = false;
|
||||
}
|
||||
},
|
||||
true);
|
||||
@@ -176,12 +192,7 @@ static bool hid_media_input_callback(InputEvent* event, void* context) {
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
hid_media_process_release(hid_media, event);
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyBack) {
|
||||
hid_hal_consumer_key_release_all(hid_media->hid);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,61 +49,67 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
}
|
||||
|
||||
// Keypad circles
|
||||
canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47);
|
||||
canvas_draw_icon(canvas, 58, 3, &I_OutCircles);
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 6, &I_S_UP);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9);
|
||||
canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9);
|
||||
canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7);
|
||||
canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7);
|
||||
canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
if(model->left_mouse_pressed) {
|
||||
canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9);
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
if(model->right_mouse_pressed) {
|
||||
canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9);
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) {
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "hid_mouse_clicker.h"
|
||||
#include <gui/elements.h>
|
||||
#include "../hid.h"
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#define TAG "HidMouseClicker"
|
||||
#define DEFAULT_CLICK_RATE 1
|
||||
#define MAXIMUM_CLICK_RATE 60
|
||||
|
||||
struct HidMouseClicker {
|
||||
View* view;
|
||||
Hid* hid;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool connected;
|
||||
bool running;
|
||||
int rate;
|
||||
HidTransport transport;
|
||||
} HidMouseClickerModel;
|
||||
|
||||
static void hid_mouse_clicker_start_or_restart_timer(void* context) {
|
||||
furi_assert(context);
|
||||
HidMouseClicker* hid_mouse_clicker = context;
|
||||
|
||||
if(furi_timer_is_running(hid_mouse_clicker->timer)) {
|
||||
furi_timer_stop(hid_mouse_clicker->timer);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
hid_mouse_clicker->view,
|
||||
HidMouseClickerModel * model,
|
||||
{
|
||||
furi_timer_start(
|
||||
hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidMouseClickerModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker");
|
||||
|
||||
// Ok
|
||||
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
|
||||
if(model->running) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
FuriString* rate_label = furi_string_alloc();
|
||||
furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate);
|
||||
elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label));
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
furi_string_free(rate_label);
|
||||
|
||||
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
}
|
||||
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
|
||||
if(model->running) {
|
||||
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
|
||||
} else {
|
||||
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
|
||||
}
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
|
||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
|
||||
}
|
||||
|
||||
static void hid_mouse_clicker_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
HidMouseClicker* hid_mouse_clicker = context;
|
||||
with_view_model(
|
||||
hid_mouse_clicker->view,
|
||||
HidMouseClickerModel * model,
|
||||
{
|
||||
if(model->running) {
|
||||
hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
|
||||
hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
static void hid_mouse_clicker_enter_callback(void* context) {
|
||||
hid_mouse_clicker_start_or_restart_timer(context);
|
||||
}
|
||||
|
||||
static void hid_mouse_clicker_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
HidMouseClicker* hid_mouse_clicker = context;
|
||||
furi_timer_stop(hid_mouse_clicker->timer);
|
||||
}
|
||||
|
||||
static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
HidMouseClicker* hid_mouse_clicker = context;
|
||||
|
||||
bool consumed = false;
|
||||
bool rate_changed = false;
|
||||
|
||||
if(event->type != InputTypeRelease) {
|
||||
return false;
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
hid_mouse_clicker->view,
|
||||
HidMouseClickerModel * model,
|
||||
{
|
||||
switch(event->key) {
|
||||
case InputKeyOk:
|
||||
model->running = !model->running;
|
||||
consumed = true;
|
||||
break;
|
||||
case InputKeyUp:
|
||||
if(model->rate < MAXIMUM_CLICK_RATE) {
|
||||
model->rate++;
|
||||
}
|
||||
rate_changed = true;
|
||||
consumed = true;
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if(model->rate > 1) {
|
||||
model->rate--;
|
||||
}
|
||||
rate_changed = true;
|
||||
consumed = true;
|
||||
break;
|
||||
default:
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
if(rate_changed) {
|
||||
hid_mouse_clicker_start_or_restart_timer(context);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {
|
||||
HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker));
|
||||
|
||||
hid_mouse_clicker->view = view_alloc();
|
||||
view_set_context(hid_mouse_clicker->view, hid_mouse_clicker);
|
||||
view_allocate_model(
|
||||
hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel));
|
||||
view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback);
|
||||
view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback);
|
||||
view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback);
|
||||
view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback);
|
||||
|
||||
hid_mouse_clicker->hid = hid;
|
||||
|
||||
hid_mouse_clicker->timer = furi_timer_alloc(
|
||||
hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker);
|
||||
|
||||
with_view_model(
|
||||
hid_mouse_clicker->view,
|
||||
HidMouseClickerModel * model,
|
||||
{
|
||||
model->transport = hid->transport;
|
||||
model->rate = DEFAULT_CLICK_RATE;
|
||||
},
|
||||
true);
|
||||
|
||||
return hid_mouse_clicker;
|
||||
}
|
||||
|
||||
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) {
|
||||
furi_assert(hid_mouse_clicker);
|
||||
|
||||
furi_timer_stop(hid_mouse_clicker->timer);
|
||||
furi_timer_free(hid_mouse_clicker->timer);
|
||||
|
||||
view_free(hid_mouse_clicker->view);
|
||||
|
||||
free(hid_mouse_clicker);
|
||||
}
|
||||
|
||||
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) {
|
||||
furi_assert(hid_mouse_clicker);
|
||||
return hid_mouse_clicker->view;
|
||||
}
|
||||
|
||||
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) {
|
||||
furi_assert(hid_mouse_clicker);
|
||||
with_view_model(
|
||||
hid_mouse_clicker->view,
|
||||
HidMouseClickerModel * model,
|
||||
{ model->connected = connected; },
|
||||
true);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct Hid Hid;
|
||||
typedef struct HidMouseClicker HidMouseClicker;
|
||||
|
||||
HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid);
|
||||
|
||||
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker);
|
||||
|
||||
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker);
|
||||
|
||||
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected);
|
||||
@@ -19,6 +19,7 @@ typedef struct {
|
||||
bool ok_pressed;
|
||||
bool connected;
|
||||
bool is_cursor_set;
|
||||
bool back_mouse_pressed;
|
||||
HidTransport transport;
|
||||
} HidTikTokModel;
|
||||
|
||||
@@ -40,54 +41,68 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// Keypad circles
|
||||
canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
|
||||
canvas_draw_icon(canvas, 58, 3, &I_OutCircles);
|
||||
|
||||
// Pause
|
||||
if(model->back_mouse_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 6, &I_S_UP);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9);
|
||||
canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9);
|
||||
canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6);
|
||||
canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
|
||||
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6);
|
||||
canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
if(model->ok_pressed) {
|
||||
canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9);
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Exit
|
||||
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
@@ -102,7 +117,8 @@ static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
// Move cursor from the corner
|
||||
hid_hal_mouse_move(hid_tiktok->hid, 20, 120);
|
||||
hid_hal_mouse_move(hid_tiktok->hid, 40, 120);
|
||||
hid_hal_mouse_move(hid_tiktok->hid, 0, 120);
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
@@ -120,6 +136,8 @@ static void
|
||||
hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_mouse_pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +155,8 @@ static void
|
||||
hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_mouse_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,32 +182,27 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) {
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyOk) {
|
||||
hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
furi_delay_ms(25);
|
||||
hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
furi_delay_ms(100);
|
||||
hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(25);
|
||||
hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
// Swipe to previous video
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 19);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyUp) {
|
||||
// Swipe to new video
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -19);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
// Pause
|
||||
hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyUp) {
|
||||
// Emulate up swipe
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -6);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -12);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -19);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -12);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, -6);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
// Emulate down swipe
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 6);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 12);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 19);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 12);
|
||||
hid_hal_mouse_scroll(hid_tiktok->hid, 6);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
hid_hal_consumer_key_release_all(hid_tiktok->hid);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if(event->key == InputKeyBack) {
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
#include "hid_ytshorts.h"
|
||||
#include "../hid.h"
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#define TAG "HidYTShorts"
|
||||
|
||||
struct HidYTShorts {
|
||||
View* view;
|
||||
Hid* hid;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool left_pressed;
|
||||
bool up_pressed;
|
||||
bool right_pressed;
|
||||
bool down_pressed;
|
||||
bool ok_pressed;
|
||||
bool connected;
|
||||
bool is_cursor_set;
|
||||
bool back_mouse_pressed;
|
||||
HidTransport transport;
|
||||
} HidYTShortsModel;
|
||||
|
||||
static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidYTShortsModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Shorts");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// Keypad circles
|
||||
canvas_draw_icon(canvas, 58, 3, &I_OutCircles);
|
||||
|
||||
// Pause
|
||||
if(model->back_mouse_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 68, 6, &I_S_UP);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
if(model->ok_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Exit
|
||||
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||
}
|
||||
|
||||
static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) {
|
||||
// Set cursor to the phone's left up corner
|
||||
// Delays to guarantee one packet per connection interval
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
hid_hal_mouse_move(hid_ytshorts->hid, -127, -127);
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
// Move cursor from the corner
|
||||
hid_hal_mouse_move(hid_ytshorts->hid, 40, 120);
|
||||
hid_hal_mouse_move(hid_ytshorts->hid, 0, 120);
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
static void hid_ytshorts_process_press(
|
||||
HidYTShorts* hid_ytshorts,
|
||||
HidYTShortsModel* model,
|
||||
InputEvent* event) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = true;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = true;
|
||||
hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = true;
|
||||
hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_mouse_pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_ytshorts_process_release(
|
||||
HidYTShorts* hid_ytshorts,
|
||||
HidYTShortsModel* model,
|
||||
InputEvent* event) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = false;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = false;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = false;
|
||||
hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = false;
|
||||
hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_mouse_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hid_ytshorts_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
HidYTShorts* hid_ytshorts = context;
|
||||
bool consumed = false;
|
||||
|
||||
with_view_model(
|
||||
hid_ytshorts->view,
|
||||
HidYTShortsModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
hid_ytshorts_process_press(hid_ytshorts, model, event);
|
||||
if(model->connected && !model->is_cursor_set) {
|
||||
hid_ytshorts_reset_cursor(hid_ytshorts);
|
||||
model->is_cursor_set = true;
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
hid_ytshorts_process_release(hid_ytshorts, model, event);
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyOk) {
|
||||
hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
// Swipe to new video
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, 6);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, 8);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, 10);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, 8);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, 6);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyUp) {
|
||||
// Swipe to previous video
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, -6);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, -8);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, -10);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, -8);
|
||||
hid_hal_mouse_scroll(hid_ytshorts->hid, -6);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
// Pause
|
||||
hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT);
|
||||
furi_delay_ms(50);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if(event->key == InputKeyBack) {
|
||||
hid_hal_consumer_key_release_all(hid_ytshorts->hid);
|
||||
model->is_cursor_set = false;
|
||||
consumed = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid) {
|
||||
HidYTShorts* hid_ytshorts = malloc(sizeof(HidYTShorts));
|
||||
hid_ytshorts->hid = bt_hid;
|
||||
hid_ytshorts->view = view_alloc();
|
||||
view_set_context(hid_ytshorts->view, hid_ytshorts);
|
||||
view_allocate_model(hid_ytshorts->view, ViewModelTypeLocking, sizeof(HidYTShortsModel));
|
||||
view_set_draw_callback(hid_ytshorts->view, hid_ytshorts_draw_callback);
|
||||
view_set_input_callback(hid_ytshorts->view, hid_ytshorts_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_ytshorts->view,
|
||||
HidYTShortsModel * model,
|
||||
{ model->transport = bt_hid->transport; },
|
||||
true);
|
||||
|
||||
return hid_ytshorts;
|
||||
}
|
||||
|
||||
void hid_ytshorts_free(HidYTShorts* hid_ytshorts) {
|
||||
furi_assert(hid_ytshorts);
|
||||
view_free(hid_ytshorts->view);
|
||||
free(hid_ytshorts);
|
||||
}
|
||||
|
||||
View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts) {
|
||||
furi_assert(hid_ytshorts);
|
||||
return hid_ytshorts->view;
|
||||
}
|
||||
|
||||
void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected) {
|
||||
furi_assert(hid_ytshorts);
|
||||
with_view_model(
|
||||
hid_ytshorts->view,
|
||||
HidYTShortsModel * model,
|
||||
{
|
||||
model->connected = connected;
|
||||
model->is_cursor_set = false;
|
||||
},
|
||||
true);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct Hid Hid;
|
||||
typedef struct HidYTShorts HidYTShorts;
|
||||
|
||||
HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid);
|
||||
|
||||
void hid_ytshorts_free(HidYTShorts* hid_ytshorts);
|
||||
|
||||
View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts);
|
||||
|
||||
void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected);
|
||||