1010#include <notification/notification.h>
1111#include <notification/notification_messages.h>
1212
13+ typedef struct ButtonEvent {
14+ int8_t button ;
15+ bool state ;
16+ } ButtonEvent ;
17+
18+ #define BTN_EVT_QUEUE_SIZE 32
19+
1320struct BtMouse {
1421 View * view ;
1522 ViewDispatcher * view_dispatcher ;
1623 Bt * bt ;
1724 NotificationApp * notifications ;
25+ FuriMutex * mutex ;
26+ FuriThread * thread ;
27+ bool connected ;
28+
29+ // Current mouse state
30+ uint8_t btn ;
31+ int dx ;
32+ int dy ;
33+ int wheel ;
34+
35+ // Circular buffer;
36+ // (qhead == qtail) means either empty or overflow.
37+ // We'll ignore overflow and treat it as empty.
38+ int qhead ;
39+ int qtail ;
40+ ButtonEvent queue [BTN_EVT_QUEUE_SIZE ];
1841};
1942
43+ #define BT_MOUSE_FLAG_INPUT_EVENT (1UL << 0)
44+ #define BT_MOUSE_FLAG_KILL_THREAD (1UL << 1)
45+ #define BT_MOUSE_FLAG_ALL (BT_MOUSE_FLAG_INPUT_EVENT | BT_MOUSE_FLAG_KILL_THREAD)
46+
2047#define MOUSE_MOVE_SHORT 5
2148#define MOUSE_MOVE_LONG 20
2249
50+ static void bt_mouse_notify_event (BtMouse * bt_mouse ) {
51+ FuriThreadId thread_id = furi_thread_get_id (bt_mouse -> thread );
52+ furi_assert (thread_id );
53+ furi_thread_flags_set (thread_id , BT_MOUSE_FLAG_INPUT_EVENT );
54+ }
55+
2356static void bt_mouse_draw_callback (Canvas * canvas , void * context ) {
2457 UNUSED (context );
2558 canvas_clear (canvas );
@@ -29,6 +62,20 @@ static void bt_mouse_draw_callback(Canvas* canvas, void* context) {
2962 canvas_draw_str (canvas , 0 , 63 , "Hold [back] to exit" );
3063}
3164
65+ static void bt_mouse_button_state (BtMouse * bt_mouse , int8_t button , bool state ) {
66+ ButtonEvent event ;
67+ event .button = button ;
68+ event .state = state ;
69+
70+ if (bt_mouse -> connected ) {
71+ furi_mutex_acquire (bt_mouse -> mutex , FuriWaitForever );
72+ bt_mouse -> queue [bt_mouse -> qtail ++ ] = event ;
73+ bt_mouse -> qtail %= BTN_EVT_QUEUE_SIZE ;
74+ furi_mutex_release (bt_mouse -> mutex );
75+ bt_mouse_notify_event (bt_mouse );
76+ }
77+ }
78+
3279static void bt_mouse_process (BtMouse * bt_mouse , InputEvent * event ) {
3380 with_view_model (
3481 bt_mouse -> view ,
@@ -37,21 +84,21 @@ static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) {
3784 UNUSED (model );
3885 if (event -> key == InputKeyUp ) {
3986 if (event -> type == InputTypePress ) {
40- furi_hal_bt_hid_mouse_press ( HID_MOUSE_BTN_LEFT );
87+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_LEFT , true );
4188 } else if (event -> type == InputTypeRelease ) {
42- furi_hal_bt_hid_mouse_release ( HID_MOUSE_BTN_LEFT );
89+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_LEFT , false );
4390 }
4491 } else if (event -> key == InputKeyDown ) {
4592 if (event -> type == InputTypePress ) {
46- furi_hal_bt_hid_mouse_press ( HID_MOUSE_BTN_RIGHT );
93+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_RIGHT , true );
4794 } else if (event -> type == InputTypeRelease ) {
48- furi_hal_bt_hid_mouse_release ( HID_MOUSE_BTN_RIGHT );
95+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_RIGHT , false );
4996 }
5097 } else if (event -> key == InputKeyOk ) {
5198 if (event -> type == InputTypePress ) {
52- furi_hal_bt_hid_mouse_press ( HID_MOUSE_BTN_WHEEL );
99+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_WHEEL , true );
53100 } else if (event -> type == InputTypeRelease ) {
54- furi_hal_bt_hid_mouse_release ( HID_MOUSE_BTN_WHEEL );
101+ bt_mouse_button_state ( bt_mouse , HID_MOUSE_BTN_WHEEL , false );
55102 }
56103 }
57104 },
@@ -77,17 +124,35 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context)
77124 furi_assert (context );
78125 BtMouse * bt_mouse = context ;
79126
80- bool connected = (status == BtStatusConnected );
81- if (connected ) {
127+ bt_mouse -> connected = (status == BtStatusConnected );
128+ if (bt_mouse -> connected ) {
82129 notification_internal_message (bt_mouse -> notifications , & sequence_set_blue_255 );
130+ tracking_begin ();
131+ view_dispatcher_send_custom_event (bt_mouse -> view_dispatcher , 0 );
83132 } else {
133+ tracking_end ();
84134 notification_internal_message (bt_mouse -> notifications , & sequence_reset_blue );
85135 }
86136
87137 //with_view_model(
88138 // bt_mouse->view, void * model, { model->connected = connected; }, true);
89139}
90140
141+ bool bt_mouse_move (int8_t dx , int8_t dy , void * context ) {
142+ furi_assert (context );
143+ BtMouse * bt_mouse = context ;
144+
145+ if (bt_mouse -> connected ) {
146+ furi_mutex_acquire (bt_mouse -> mutex , FuriWaitForever );
147+ bt_mouse -> dx += dx ;
148+ bt_mouse -> dy += dy ;
149+ furi_mutex_release (bt_mouse -> mutex );
150+ bt_mouse_notify_event (bt_mouse );
151+ }
152+
153+ return true;
154+ }
155+
91156void bt_mouse_enter_callback (void * context ) {
92157 furi_assert (context );
93158 BtMouse * bt_mouse = context ;
@@ -98,18 +163,15 @@ void bt_mouse_enter_callback(void* context) {
98163 bt_mouse -> bt , bt_mouse_connection_status_changed_callback , bt_mouse );
99164 furi_assert (bt_set_profile (bt_mouse -> bt , BtProfileHidKeyboard ));
100165 furi_hal_bt_start_advertising ();
101-
102- tracking_begin ();
103-
104- view_dispatcher_send_custom_event (bt_mouse -> view_dispatcher , 0 );
105166}
106167
107168bool bt_mouse_custom_callback (uint32_t event , void * context ) {
108169 UNUSED (event );
109170 furi_assert (context );
110171 BtMouse * bt_mouse = context ;
111172
112- tracking_step (furi_hal_bt_hid_mouse_move );
173+ tracking_step (bt_mouse_move , context );
174+ furi_delay_ms (3 ); // Magic! Removing this will break the buttons
113175
114176 view_dispatcher_send_custom_event (bt_mouse -> view_dispatcher , 0 );
115177 return true;
@@ -120,7 +182,6 @@ void bt_mouse_exit_callback(void* context) {
120182 BtMouse * bt_mouse = context ;
121183
122184 tracking_end ();
123-
124185 notification_internal_message (bt_mouse -> notifications , & sequence_reset_blue );
125186
126187 furi_hal_bt_stop_advertising ();
@@ -132,8 +193,91 @@ void bt_mouse_exit_callback(void* context) {
132193 bt_mouse -> bt = NULL ;
133194}
134195
196+ static int8_t clamp (int t ) {
197+ if (t < -128 ) {
198+ return -128 ;
199+ }
200+ else if (t > 127 ) {
201+ return 127 ;
202+ }
203+ return t ;
204+ }
205+
206+ static int32_t bt_mouse_thread_callback (void * context ) {
207+ furi_assert (context );
208+ BtMouse * bt_mouse = (BtMouse * )context ;
209+
210+ while (1 ) {
211+ uint32_t flags = furi_thread_flags_wait (BT_MOUSE_FLAG_ALL , FuriFlagWaitAny , FuriWaitForever );
212+ if (flags & BT_MOUSE_FLAG_KILL_THREAD ) {
213+ break ;
214+ }
215+ if (flags & BT_MOUSE_FLAG_INPUT_EVENT ) {
216+ furi_mutex_acquire (bt_mouse -> mutex , FuriWaitForever );
217+
218+ ButtonEvent event ;
219+ bool send_buttons = false;
220+ if (bt_mouse -> qhead != bt_mouse -> qtail ) {
221+ event = bt_mouse -> queue [bt_mouse -> qhead ++ ];
222+ bt_mouse -> qhead %= BTN_EVT_QUEUE_SIZE ;
223+ send_buttons = true;
224+ }
225+
226+ int8_t dx = clamp (bt_mouse -> dx );
227+ bt_mouse -> dx -= dx ;
228+ int8_t dy = clamp (bt_mouse -> dy );
229+ bt_mouse -> dy -= dy ;
230+ int8_t wheel = clamp (bt_mouse -> wheel );
231+ bt_mouse -> wheel -= wheel ;
232+
233+ furi_mutex_release (bt_mouse -> mutex );
234+
235+ if (bt_mouse -> connected && send_buttons ) {
236+ if (event .state ) {
237+ furi_hal_bt_hid_mouse_press (event .button );
238+ } else {
239+ furi_hal_bt_hid_mouse_release (event .button );
240+ }
241+ }
242+
243+ if (bt_mouse -> connected && (dx != 0 || dy != 0 )) {
244+ furi_hal_bt_hid_mouse_move (dx , dy );
245+ }
246+
247+ if (bt_mouse -> connected && wheel != 0 ) {
248+ furi_hal_bt_hid_mouse_scroll (wheel );
249+ }
250+ }
251+ }
252+
253+ return 0 ;
254+ }
255+
256+ void bt_mouse_thread_start (BtMouse * bt_mouse ) {
257+ furi_assert (bt_mouse );
258+ bt_mouse -> mutex = furi_mutex_alloc (FuriMutexTypeNormal );
259+ bt_mouse -> thread = furi_thread_alloc ();
260+ furi_thread_set_name (bt_mouse -> thread , "BtSender" );
261+ furi_thread_set_stack_size (bt_mouse -> thread , 1024 );
262+ furi_thread_set_context (bt_mouse -> thread , bt_mouse );
263+ furi_thread_set_callback (bt_mouse -> thread , bt_mouse_thread_callback );
264+ furi_thread_start (bt_mouse -> thread );
265+ }
266+
267+ void bt_mouse_thread_stop (BtMouse * bt_mouse ) {
268+ furi_assert (bt_mouse );
269+ FuriThreadId thread_id = furi_thread_get_id (bt_mouse -> thread );
270+ furi_assert (thread_id );
271+ furi_thread_flags_set (thread_id , BT_MOUSE_FLAG_KILL_THREAD );
272+ furi_thread_join (bt_mouse -> thread );
273+ furi_thread_free (bt_mouse -> thread );
274+ furi_mutex_free (bt_mouse -> mutex );
275+ }
276+
135277BtMouse * bt_mouse_alloc (ViewDispatcher * view_dispatcher ) {
136278 BtMouse * bt_mouse = malloc (sizeof (BtMouse ));
279+ memset (bt_mouse , 0 , sizeof (BtMouse ));
280+
137281 bt_mouse -> view = view_alloc ();
138282 bt_mouse -> view_dispatcher = view_dispatcher ;
139283 view_set_context (bt_mouse -> view , bt_mouse );
@@ -142,11 +286,13 @@ BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) {
142286 view_set_enter_callback (bt_mouse -> view , bt_mouse_enter_callback );
143287 view_set_custom_callback (bt_mouse -> view , bt_mouse_custom_callback );
144288 view_set_exit_callback (bt_mouse -> view , bt_mouse_exit_callback );
289+ bt_mouse_thread_start (bt_mouse );
145290 return bt_mouse ;
146291}
147292
148293void bt_mouse_free (BtMouse * bt_mouse ) {
149294 furi_assert (bt_mouse );
295+ bt_mouse_thread_stop (bt_mouse );
150296 view_free (bt_mouse -> view );
151297 free (bt_mouse );
152298}
0 commit comments