Ray 3 лет назад
Родитель
Сommit
582432093e
11 измененных файлов с 363 добавлено и 0 удалено
  1. 10 0
      application.fam
  2. 67 0
      cli_control.c
  3. 8 0
      cli_control.h
  4. BIN
      cligui.png
  5. 125 0
      cligui_main.c
  6. 93 0
      cligui_main_i.h
  7. 13 0
      console_output.c
  8. 4 0
      console_output.h
  9. 7 0
      events.h
  10. 31 0
      text_input.c
  11. 5 0
      text_input.h

+ 10 - 0
application.fam

@@ -0,0 +1,10 @@
+App(
+    appid="cli_gui",
+    name="CLI-GUI Bridge",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="cligui_main",
+    requires=["gui","cli"],
+    stack_size=8 * 1024,
+    fap_icon="cligui.png",
+    fap_category="Tools",
+)

+ 67 - 0
cli_control.c

@@ -0,0 +1,67 @@
+#include "cli_control.h"
+
+#include <cli/cli.h>
+#include <cli/cli_i.h>
+#include <cli/cli_vcp.h>
+#include "cligui_main_i.h"
+
+volatile bool gotCallbackSet = false;
+
+FuriStreamBuffer* tx_stream;
+FuriStreamBuffer* rx_stream;
+static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
+    size_t rx_cnt = 0;
+    while(size > 0) {
+        size_t batch_size = size;
+        if(batch_size > 128) batch_size = 128;
+        size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout);
+        if(len == 0) break;
+        size -= len;
+        buffer += len;
+        rx_cnt += len;
+    }
+    return rx_cnt;
+}
+static void tx_handler_stdout(const char* buffer, size_t size) {
+    furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
+}
+
+static void tx_handler(const uint8_t* buffer, size_t size) {
+    furi_thread_set_stdout_callback(tx_handler_stdout);
+    furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
+}
+
+static void session_init(void) {}
+static void session_deinit(void) {}
+static bool session_connected(void) {return true;}
+static CliSession session;
+void latch_tx_handler() {
+    Cli* global_cli = furi_record_open(RECORD_CLI);
+    furi_thread_set_stdout_callback(tx_handler_stdout);
+    rx_stream = furi_stream_buffer_alloc(128, 1);
+    tx_stream = furi_stream_buffer_alloc(128, 1);
+
+    session.tx = &tx_handler;
+    session.rx = &real_rx_handler;
+    session.tx_stdout  = &tx_handler_stdout;
+    session.init = &session_init;
+    session.deinit = &session_deinit;
+    session.is_connected = &session_connected;
+    cli_session_close(global_cli);
+    cli_session_open(global_cli, &session);
+    // furi_stream_buffer_send(rx_stream, "help\r\n", 6, FuriWaitForever);
+    // Unlock loader-lock
+    Loader* loader = furi_record_open(RECORD_LOADER);
+    Loader_internal* loader_i = (Loader_internal*)loader;
+    loader_i->lock_count = 0;
+    furi_record_close(RECORD_CLI);
+    furi_record_close(RECORD_LOADER);
+}
+void unlatch_tx_handler() {
+    Cli* global_cli = furi_record_open(RECORD_CLI);
+    cli_session_close(global_cli);
+    cli_session_open(global_cli, &cli_vcp);
+    furi_stream_buffer_free(rx_stream);
+    furi_stream_buffer_free(tx_stream);
+    furi_record_close(RECORD_CLI);
+}

+ 8 - 0
cli_control.h

@@ -0,0 +1,8 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+extern void latch_tx_handler();
+extern void unlatch_tx_handler();
+extern FuriStreamBuffer* tx_stream;
+extern FuriStreamBuffer* rx_stream;


+ 125 - 0
cligui_main.c

@@ -0,0 +1,125 @@
+#include "cligui_main_i.h"
+#include "cli_control.h"
+#include "events.h"
+#include "text_input.h"
+#include "console_output.h"
+
+static bool cligui_custom_event_cb(void* context, uint32_t event) {
+    UNUSED(event);
+    CliguiApp* app = context;
+    UNUSED(app);
+    return true;
+}
+static bool cligui_back_event_cb(void* context) {
+    CliguiApp* app = context;
+    UNUSED(app);
+    return true;
+}
+static void cligui_tick_event_cb(void* context) {
+    CliguiApp* app = context;
+    size_t available = furi_stream_buffer_bytes_available(app->data->streams.app_rx);
+    for(size_t i = 0; i < available; i++) {
+        char c = 0;
+        size_t len = furi_stream_buffer_receive(app->data->streams.app_rx, &c, 1, 100);
+        if(len > 0) {
+            furi_string_push_back(app->text_box_store, c);
+        }
+    }
+    if (available > 0) {
+        text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
+    }
+    // Set input header stuff
+    size_t len = furi_string_size(app->text_box_store);
+    size_t idx = len - 2;
+    while (idx > 0) {
+        if (furi_string_get_char(app->text_box_store, idx) == '\n') {
+            idx++;
+            break;
+        } 
+        idx--;
+    }
+    text_input_set_header_text(app->text_input, furi_string_get_cstr(app->text_box_store) + idx);
+    UNUSED(app);
+}
+
+ViewPortInputCallback prev_input_callback;
+static void input_callback_wrapper(InputEvent* event, void* context) {
+    CliguiApp* app = context;
+    if(event->type == InputTypeLong && event->key == InputKeyBack) {
+        view_dispatcher_stop(app->view_dispatcher);
+    }
+    if(app->data->state == ViewTextInput) {
+        text_input_input_handler(app, event);
+    } else {
+        console_output_input_handler(app, event);
+    }
+    prev_input_callback(event, app->view_dispatcher);
+}
+
+int32_t cligui_main(void* p) {
+    UNUSED(p);
+    CliguiApp* cligui = malloc(sizeof(CliguiApp));
+    cligui->data = malloc(sizeof(CliguiData));
+
+    latch_tx_handler();
+    cligui->data->streams.app_tx = rx_stream;
+    cligui->data->streams.app_rx = tx_stream;
+
+    cligui->gui = furi_record_open(RECORD_GUI);
+    cligui->view_dispatcher = view_dispatcher_alloc();
+    cligui->view_dispatcher_i = (ViewDispatcher_internal*)(cligui->view_dispatcher);
+    prev_input_callback =
+        ((ViewPort_internal*)cligui->view_dispatcher_i->view_port)->input_callback;
+    view_port_input_callback_set(
+        cligui->view_dispatcher_i->view_port, input_callback_wrapper, cligui);
+    view_dispatcher_enable_queue(cligui->view_dispatcher);
+    view_dispatcher_set_event_callback_context(cligui->view_dispatcher, cligui);
+    view_dispatcher_set_custom_event_callback(cligui->view_dispatcher, cligui_custom_event_cb);
+    view_dispatcher_set_navigation_event_callback(cligui->view_dispatcher, cligui_back_event_cb);
+    view_dispatcher_set_tick_event_callback(cligui->view_dispatcher, cligui_tick_event_cb, 100);
+
+    view_dispatcher_attach_to_gui(
+        cligui->view_dispatcher, cligui->gui, ViewDispatcherTypeFullscreen);
+
+    view_dispatcher_send_to_front(cligui->view_dispatcher);
+
+    cligui->text_box = text_box_alloc();
+    view_dispatcher_add_view(
+        cligui->view_dispatcher, ViewConsoleOutput, text_box_get_view(cligui->text_box));
+    cligui->text_box_store = furi_string_alloc();
+    furi_string_reserve(cligui->text_box_store, TEXT_BOX_STORE_SIZE);
+    text_box_set_text(cligui->text_box, furi_string_get_cstr(cligui->text_box_store));
+    text_box_set_focus(cligui->text_box, TextBoxFocusEnd);
+
+    cligui->text_input = text_input_alloc();
+    text_input_set_result_callback(
+        cligui->text_input,
+        text_input_result_callback,
+        cligui,
+        cligui->text_input_store,
+        TEXT_INPUT_STORE_SIZE,
+        true);
+    view_dispatcher_add_view(
+        cligui->view_dispatcher, ViewTextInput, text_input_get_view(cligui->text_input));
+
+    view_dispatcher_switch_to_view(cligui->view_dispatcher, ViewTextInput);
+    cligui->data->state = ViewTextInput;
+
+    view_dispatcher_run(cligui->view_dispatcher);
+
+    unlatch_tx_handler();
+
+    view_dispatcher_remove_view(cligui->view_dispatcher, ViewConsoleOutput);
+    view_dispatcher_remove_view(cligui->view_dispatcher, ViewTextInput);
+    text_box_free(cligui->text_box);
+    furi_string_free(cligui->text_box_store);
+    text_input_free(cligui->text_input);
+    view_dispatcher_free(cligui->view_dispatcher);
+
+    furi_record_close(RECORD_GUI);
+
+    free(cligui->data);
+    free(cligui);
+
+    return 0;
+}

+ 93 - 0
cligui_main_i.h

@@ -0,0 +1,93 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_usb_cdc.h>
+#include <furi_hal_usb.h>
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/text_box.h>
+#include <gui/modules/text_input.h>
+#include <m-dict.h>
+#include <loader/loader.h>
+
+DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST)
+
+#define TEXT_BOX_STORE_SIZE (4096)
+#define TEXT_INPUT_STORE_SIZE (512)
+
+typedef struct {
+    FuriMessageQueue* queue;
+    Gui* gui;
+    ViewPort* view_port;
+    ViewDict_t views;
+
+    View* current_view;
+
+    View* ongoing_input_view;
+    uint8_t ongoing_input;
+
+    ViewDispatcherCustomEventCallback custom_event_callback;
+    ViewDispatcherNavigationEventCallback navigation_event_callback;
+    ViewDispatcherTickEventCallback tick_event_callback;
+    uint32_t tick_period;
+    void* event_context;
+} ViewDispatcher_internal;
+typedef struct {
+    Gui* gui;
+    bool is_enabled;
+    ViewPortOrientation orientation;
+
+    uint8_t width;
+    uint8_t height;
+
+    ViewPortDrawCallback draw_callback;
+    void* draw_callback_context;
+
+    ViewPortInputCallback input_callback;
+    void* input_callback_context;
+} ViewPort_internal;
+typedef struct {
+    FuriThreadId loader_thread;
+
+    const void* application;
+    FuriThread* application_thread;
+    char* application_arguments;
+
+    void* cli;
+    void* gui;
+
+    void* view_dispatcher;
+    void* primary_menu;
+    void* plugins_menu;
+    void* debug_menu;
+    void* settings_menu;
+
+    volatile uint8_t lock_count;
+
+    void* pubsub;
+} Loader_internal;
+
+typedef enum {
+    ViewTextInput,
+    ViewConsoleOutput,
+} CliguiState;
+
+typedef struct {
+    CliguiState state;
+    struct {
+        FuriStreamBuffer* app_tx;
+        FuriStreamBuffer* app_rx;
+    } streams;
+} CliguiData;
+
+typedef struct {
+    CliguiData* data;
+    Gui* gui;
+    TextBox* text_box;
+    FuriString* text_box_store;
+    char text_input_store[TEXT_INPUT_STORE_SIZE + 1];
+    TextInput* text_input;
+    ViewDispatcher* view_dispatcher;
+    ViewDispatcher_internal* view_dispatcher_i;
+} CliguiApp;

+ 13 - 0
console_output.c

@@ -0,0 +1,13 @@
+#include "console_output.h"
+
+void console_output_input_handler(CliguiApp* app, InputEvent* event) {
+    if(event->type == InputTypeShort && (event->key == InputKeyOk || event->key == InputKeyLeft)) {
+        view_dispatcher_switch_to_view(app->view_dispatcher, ViewTextInput);
+        app->data->state = ViewTextInput;
+    }
+    if(event->type == InputTypeShort && event->key == InputKeyBack) {
+        char eot = 0x03;
+        furi_stream_buffer_send(app->data->streams.app_tx, &eot, 1, FuriWaitForever);
+    }
+    
+}

+ 4 - 0
console_output.h

@@ -0,0 +1,4 @@
+#pragma once
+#include "cligui_main_i.h"
+
+extern void console_output_input_handler(CliguiApp*, InputEvent*);

+ 7 - 0
events.h

@@ -0,0 +1,7 @@
+#pragma once
+
+typedef enum {
+    RefreshConsoleOutput = 0,
+    StartConsole,
+    StartKeyboard,
+} CliguiCustomEvent;

+ 31 - 0
text_input.c

@@ -0,0 +1,31 @@
+#include "text_input.h"
+#include "cligui_main_i.h"
+
+void text_input_result_callback(void* ctx) {
+    CliguiApp* app = ctx;
+    char* data = app->text_input_store;
+    size_t len = strlen(data);
+    for(size_t i = 0; i < len; i++) {
+        if(data[i] >= 0x41 && data[i] <= 0x5A) {
+            // Char is uppercase
+            data[i] += 0x20;
+        }
+    }
+    furi_stream_buffer_send(app->data->streams.app_tx, data, len, FuriWaitForever);
+    furi_stream_buffer_send(app->data->streams.app_tx, "\r\n", 2, FuriWaitForever);
+    data[0] = 0;
+    view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput);
+    app->data->state = ViewConsoleOutput;
+}
+
+void text_input_input_handler(CliguiApp* app, InputEvent* event) {
+    UNUSED(app);
+    UNUSED(event);
+    if(event->type == InputTypeShort && event->key == InputKeyBack) {
+        // view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput);
+        // app->data->state = ViewConsoleOutput;
+        size_t len = strlen(app->text_input_store);
+        app->text_input_store[len] = ' ';
+        app->text_input_store[len + 1] = 0;
+    }
+}

+ 5 - 0
text_input.h

@@ -0,0 +1,5 @@
+#pragma once
+#include "cligui_main_i.h"
+
+extern void text_input_result_callback(void* ctx);
+extern void text_input_input_handler(CliguiApp*, InputEvent*);