Преглед изворни кода

Add HID mouse auto-clicker. (#2627)

* Add HID mouse auto-clicker.

* Add click rate adjustment to HID auto-clicker.

* Fix formatting.

* HidRemote: modify jiggler/clicker event filter and allow repeat to change click rate

---------

Co-authored-by: あく <alleteam@gmail.com>
Raymond Lucke пре 2 година
родитељ
комит
71e85ac367

+ 22 - 0
applications/external/hid_app/hid.c

@@ -11,6 +11,7 @@ enum HidDebugSubmenuIndex {
     HidSubmenuIndexMedia,
     HidSubmenuIndexTikTok,
     HidSubmenuIndexMouse,
+    HidSubmenuIndexMouseClicker,
     HidSubmenuIndexMouseJiggler,
 };
 
@@ -32,6 +33,9 @@ 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 == 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);
@@ -53,6 +57,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
     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);
 }
@@ -114,6 +119,12 @@ Hid* hid_alloc(HidTransport transport) {
             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",
@@ -172,6 +183,15 @@ Hid* hid_app_alloc_view(void* context) {
     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(
@@ -205,6 +225,8 @@ void hid_free(Hid* app) {
     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);

+ 2 - 0
applications/external/hid_app/hid.h

@@ -20,6 +20,7 @@
 #include "views/hid_keyboard.h"
 #include "views/hid_media.h"
 #include "views/hid_mouse.h"
+#include "views/hid_mouse_clicker.h"
 #include "views/hid_mouse_jiggler.h"
 #include "views/hid_tiktok.h"
 
@@ -43,6 +44,7 @@ struct Hid {
     HidKeyboard* hid_keyboard;
     HidMedia* hid_media;
     HidMouse* hid_mouse;
+    HidMouseClicker* hid_mouse_clicker;
     HidMouseJiggler* hid_mouse_jiggler;
     HidTikTok* hid_tiktok;
 

+ 1 - 0
applications/external/hid_app/views.h

@@ -4,6 +4,7 @@ typedef enum {
     HidViewKeyboard,
     HidViewMedia,
     HidViewMouse,
+    HidViewMouseClicker,
     HidViewMouseJiggler,
     BtHidViewTikTok,
     HidViewExitConfirm,

+ 214 - 0
applications/external/hid_app/views/hid_mouse_clicker.c

@@ -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 != InputTypeShort && event->type != InputTypeRepeat) {
+        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);
+}

+ 14 - 0
applications/external/hid_app/views/hid_mouse_clicker.h

@@ -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);

+ 1 - 1
applications/external/hid_app/views/hid_mouse_jiggler.c

@@ -95,7 +95,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
 
     bool consumed = false;
 
-    if(event->key == InputKeyOk) {
+    if(event->type == InputTypeShort && event->key == InputKeyOk) {
         with_view_model(
             hid_mouse_jiggler->view,
             HidMouseJigglerModel * model,