#include #include #include #include #include #include "version.h" // Uncomment to be able to make a screenshot //#define USB_HID_AUTOFIRE_SCREENSHOT typedef enum { EventTypeInput, } EventType; typedef struct { union { InputEvent input; }; EventType type; } UsbMouseEvent; bool btn_left_autofire = false; uint32_t autofire_delay = 10; #define SWAP(a, b) \ do { \ typeof(a) temp = a; \ a = b; \ b = temp; \ } while(0) void reverse(char str[], int length) { int start = 0; int end = length - 1; while(start < end) { SWAP(*(str + start), *(str + end)); start++; end--; } } // Implementation of itoa() char* itoa(int num, char* str, int base) { int i = 0; bool isNegative = false; /* Handle 0 explicitly, otherwise empty string is printed for 0 */ if(num == 0) { str[i++] = '0'; str[i] = '\0'; return str; } // In standard itoa(), negative numbers are handled only with // base 10. Otherwise numbers are considered unsigned. if(num < 0 && base == 10) { isNegative = true; num = -num; } // Process individual digits while(num != 0) { int rem = num % base; str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; num = num / base; } // If number is negative, append '-' if(isNegative) str[i++] = '-'; str[i] = '\0'; // Append string terminator // Reverse the string reverse(str, i); return str; } static void usb_hid_autofire_render_callback(Canvas* canvas, void* ctx) { UNUSED(ctx); char autofire_delay_str[12]; itoa(autofire_delay, autofire_delay_str, 10); canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 10, "USB HID Autofire"); canvas_draw_str(canvas, 0, 34, btn_left_autofire ? "" : ""); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 90, 10, "v"); canvas_draw_str(canvas, 96, 10, VERSION); canvas_draw_str(canvas, 0, 22, "Press [ok] for auto left clicking"); canvas_draw_str(canvas, 0, 46, "delay [ms]:"); canvas_draw_str(canvas, 50, 46, autofire_delay_str); // I can't even canvas_draw_str(canvas, 0, 63, "Press [back] to exit"); } static void usb_hid_autofire_input_callback(InputEvent* input_event, void* ctx) { FuriMessageQueue* event_queue = ctx; UsbMouseEvent event; event.type = EventTypeInput; event.input = *input_event; furi_message_queue_put(event_queue, &event, FuriWaitForever); } int32_t usb_hid_autofire_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); furi_check(event_queue); ViewPort* view_port = view_port_alloc(); FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); #ifndef USB_HID_AUTOFIRE_SCREENSHOT furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); #endif view_port_draw_callback_set(view_port, usb_hid_autofire_render_callback, NULL); view_port_input_callback_set(view_port, usb_hid_autofire_input_callback, event_queue); // Open GUI and register view_port Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); UsbMouseEvent event; while(1) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); if(event_status == FuriStatusOk) { if(event.type == EventTypeInput) { if(event.input.key == InputKeyBack) { break; } if(event.input.type != InputTypeRelease) { continue; } switch(event.input.key) { case InputKeyOk: btn_left_autofire = !btn_left_autofire; break; case InputKeyLeft: if(autofire_delay > 0) { autofire_delay -= 10; } break; case InputKeyRight: autofire_delay += 10; break; default: break; } } } if(btn_left_autofire) { furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); // TODO: Don't wait, but use the timer directly to just don't send the release event (see furi_hal_cortex_delay_us) furi_delay_us(autofire_delay * 500); furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); furi_delay_us(autofire_delay * 500); } view_port_update(view_port); } furi_hal_usb_set_config(usb_mode_prev, NULL); // remove & free all stuff created by app gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); return 0; }