-
-
Notifications
You must be signed in to change notification settings - Fork 56
/
intervalometer.c
773 lines (701 loc) · 25.6 KB
/
intervalometer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
// An intervalometer application written for the Flipper Zero
//
// author: nitepone <sierra>
#include "intervalometer.h"
#include <stdlib.h>
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include <core/string.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/icon.h>
#include <infrared_transmit.h>
#include <infrared/infrared_app.h>
#include <toolbox/saved_struct.h>
#include <input/input.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <ir_intervalometer_icons.h>
// app ui scenes
enum flipvalo_ui_scene {
FVSceneMain,
FVSceneConfig,
};
// defines a flipvalo camera trigger
struct flipvalo_trigger {
const char* display_name;
int (*send)(void* output_config);
};
enum flipvalo_trigger_variants {
FvTrigMin = 0,
FvTrigSony = 0,
FvTrigCanon = 1,
FvTrigNikon = 2,
FvTrigMax = 2,
};
// run config for intervalometer
struct flipvalo_config {
int init_delay_msec; // initial delay to start capture
int interval_delay_msec; // time between shots
int shot_count; // total shots in run
int burst_count; // number of triggers in a shot
int burst_delay_msec; // time between triggers in a shot
int tickrate; // tick rate in "ticks per second"
enum flipvalo_trigger_variants trigger; // current trigger
void* output_config;
};
// run time states for intervalometer
enum flipvalo_state {
FVDone = 0, // done, 0 so it is default if state struct is zeroed
FVWaitInitShot, // waiting for first shot
FVWaitContShot, // waiting between "bursts" or "shots"
FVWaitBurst, // waiting between shots in a "burst"
};
// run time data for intervalometer
// (this can be safely cleared between runs of the intervalometer)
struct flipvalo_run_state {
enum flipvalo_state state; // current state of the run
int tick_cur; // current tick count
int tick_next; // tick when next action will occur
int shot_cur; // current shot
int burst_cur; // current trigger in a burst
};
enum flipvalo_config_edit_lines {
FvConfigEditInitDelay,
FvConfigEditMIN = FvConfigEditInitDelay,
FvConfigEditShotCount,
FvConfigEditShotDelay,
FvConfigEditBurstCount,
FvConfigEditBurstDelay,
FvConfigEditTrigger,
FvConfigEditMAX = FvConfigEditTrigger,
};
struct flipvalo_config_edit_view {
// the `config` that is under edit
struct flipvalo_config* config;
// the `cur_index` of the selection
// (e.g. editing the 3rd value of a number)
int cur_index;
// the `cur_line` of the selection
enum flipvalo_config_edit_lines cur_line;
// the current line that is at the top of the scrolled view
enum flipvalo_config_edit_lines scroll_pos;
// are we editing the selection?
// (this is really only needed for number fields)
bool edit_mode;
};
// private data of app
struct flipvalo_priv {
struct flipvalo_config config;
struct flipvalo_config_edit_view config_edit_view;
struct flipvalo_run_state run_state;
enum flipvalo_ui_scene ui_scene;
int gui_shutter_blink;
FuriTimer* timer;
NotificationApp* notifications;
FuriMutex* mutex;
};
enum event_type {
EventTypeTick,
EventTypeKey,
};
struct plugin_event {
enum event_type type;
InputEvent input;
};
enum flipvalo_config_edit_line_type {
FvConfigEditTypeTimer,
FvConfigEditTypeCount,
FvConfigEditTypeEnum,
};
static void flipvalo_config_edit_view_init(struct flipvalo_config_edit_view* view) {
view->config = NULL;
view->cur_index = 0;
view->cur_line = 0;
view->scroll_pos = 0;
view->edit_mode = false;
}
static int sony_ir_trigger_send(void* ctx) {
UNUSED(ctx);
InfraredMessage message = {
.address = 0x1E3A,
.command = 0x2D,
.protocol = InfraredProtocolSIRC20,
};
infrared_send(&message, 1);
return 0;
}
uint32_t canon_ir_timings[] = {594, 7182, 593};
static int canon_ir_trigger_send(void* ctx) {
UNUSED(ctx);
infrared_send_raw_ext(canon_ir_timings, 3, true, 38000, 0.33);
return 0;
}
uint32_t nikon_ir_timings[] =
{1945, 28253, 404, 1513, 410, 3611, 460, 70144, 1974, 28213, 455, 1493, 461, 3591, 409};
static int nikon_ir_trigger_send(void* ctx) {
UNUSED(ctx);
infrared_send_raw_ext(nikon_ir_timings, 15, true, 38000, 0.33);
return 0;
}
struct flipvalo_trigger sony_ir_trigger = {.send = sony_ir_trigger_send, .display_name = "Sony IR"};
struct flipvalo_trigger canon_ir_trigger = {
.send = canon_ir_trigger_send,
.display_name = "Canon IR"};
struct flipvalo_trigger nikon_ir_trigger = {
.send = nikon_ir_trigger_send,
.display_name = "Nikon IR"};
static struct flipvalo_trigger* flipvalo_get_trigger(enum flipvalo_trigger_variants variant) {
switch(variant) {
case FvTrigSony:
return &sony_ir_trigger;
case FvTrigCanon:
return &canon_ir_trigger;
case FvTrigNikon:
return &nikon_ir_trigger;
}
return NULL;
}
#define ITEM_H 64 / 3
#define ITEM_W 128
#define VALUE_X 100
#define VALUE_W 45
static void flipvalo_config_edit_draw(Canvas* canvas, struct flipvalo_config_edit_view* view) {
int* line_value;
char* line_label = NULL;
const char* line_disp_str = "";
FuriString* temp_str = furi_string_alloc();
enum flipvalo_config_edit_line_type line_type;
enum flipvalo_config_edit_lines selected_line;
for(size_t line = 0; line < 3; line++) {
selected_line = view->scroll_pos + line;
switch(selected_line) {
case FvConfigEditInitDelay:
line_value = &view->config->init_delay_msec;
line_type = FvConfigEditTypeTimer;
line_label = "Init Time";
break;
case FvConfigEditShotDelay:
line_value = &view->config->interval_delay_msec;
line_type = FvConfigEditTypeTimer;
line_label = "Seq Time";
break;
case FvConfigEditShotCount:
line_value = &view->config->shot_count;
line_type = FvConfigEditTypeCount;
line_label = "Seq Count";
break;
case FvConfigEditBurstDelay:
line_value = &view->config->burst_delay_msec;
line_type = FvConfigEditTypeTimer;
line_label = "Brst Time";
break;
case FvConfigEditBurstCount:
line_value = &view->config->burst_count;
line_type = FvConfigEditTypeCount;
line_label = "Brst Count";
break;
case FvConfigEditTrigger:
line_value = NULL;
line_type = FvConfigEditTypeEnum;
line_label = "Trig Type";
line_disp_str = flipvalo_get_trigger(view->config->trigger)->display_name;
break;
default:
continue;
};
canvas_set_color(canvas, ColorBlack);
if((selected_line) == view->cur_line) {
elements_slightly_rounded_box(canvas, 0, ITEM_H * line + 1, ITEM_W, ITEM_H - 1);
canvas_set_color(canvas, ColorWhite);
}
uint8_t text_y = ITEM_H * line + ITEM_H / 2 + 2;
canvas_draw_str_aligned(canvas, 6, text_y, AlignLeft, AlignCenter, line_label);
switch(line_type) {
case FvConfigEditTypeTimer:
furi_string_printf(
temp_str,
"%02d:%02d:%02d:%03d",
*line_value / 3600000,
(*line_value / 60000) % 60,
(*line_value / 1000) % 60,
*line_value % 1000);
canvas_set_font(canvas, FontKeyboard);
canvas_draw_str_aligned(
canvas, 124, text_y, AlignRight, AlignCenter, furi_string_get_cstr(temp_str));
canvas_set_font(canvas, FontSecondary);
if(view->edit_mode && view->cur_line == selected_line) {
switch(view->cur_index) {
case 0:
canvas_draw_icon(canvas, 117, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 117, text_y + 5, &I_ArrowDown_3x5);
canvas_draw_icon(canvas, 112, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 112, text_y + 5, &I_ArrowDown_3x5);
canvas_draw_icon(canvas, 107, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 107, text_y + 5, &I_ArrowDown_3x5);
break;
case 1:
canvas_draw_icon(canvas, 93, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 93, text_y + 5, &I_ArrowDown_3x5);
canvas_draw_icon(canvas, 89, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 89, text_y + 5, &I_ArrowDown_3x5);
break;
case 2:
canvas_draw_icon(canvas, 75, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 75, text_y + 5, &I_ArrowDown_3x5);
canvas_draw_icon(canvas, 71, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 71, text_y + 5, &I_ArrowDown_3x5);
break;
case 3:
canvas_draw_icon(canvas, 57, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 57, text_y + 5, &I_ArrowDown_3x5);
canvas_draw_icon(canvas, 53, text_y - 9, &I_ArrowUp_3x5);
canvas_draw_icon(canvas, 53, text_y + 5, &I_ArrowDown_3x5);
break;
}
}
break;
case FvConfigEditTypeCount:
furi_string_printf(temp_str, "%d", *line_value);
canvas_draw_str_aligned(
canvas, VALUE_X, text_y, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str));
// TODO(luna) 0 values are actually more special for shot count and burst count.
// former being infinite, latter being uh.. nothing? not allowed?.. review this logic later.
if(*line_value > 0) {
canvas_draw_str_aligned(
canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<");
}
canvas_draw_str_aligned(
canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">");
break;
case FvConfigEditTypeEnum:
furi_string_printf(temp_str, "%s", line_disp_str);
canvas_draw_str_aligned(
canvas, VALUE_X, text_y, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str));
canvas_draw_str_aligned(
canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<");
canvas_draw_str_aligned(
canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">");
break;
}
}
furi_string_free(temp_str);
}
static void
flipvalo_config_edit_input_move_cursor(struct flipvalo_config_edit_view* view, int dx, int dy) {
enum flipvalo_config_edit_lines new_line = 0;
int* line_value = NULL;
enum flipvalo_config_edit_line_type line_type;
// only used for enum type
int max_value;
int min_value;
switch(view->cur_line) {
case FvConfigEditInitDelay:
line_value = &view->config->init_delay_msec;
line_type = FvConfigEditTypeTimer;
break;
case FvConfigEditShotDelay:
line_value = &view->config->interval_delay_msec;
line_type = FvConfigEditTypeTimer;
break;
case FvConfigEditShotCount:
line_value = &view->config->shot_count;
line_type = FvConfigEditTypeCount;
break;
case FvConfigEditBurstDelay:
line_value = &view->config->burst_delay_msec;
line_type = FvConfigEditTypeTimer;
break;
case FvConfigEditBurstCount:
line_value = &view->config->burst_count;
line_type = FvConfigEditTypeCount;
break;
case FvConfigEditTrigger:
line_value = (int*)(&view->config->trigger);
line_type = FvConfigEditTypeEnum;
min_value = FvTrigMin;
max_value = FvTrigMax;
break;
default:
return;
};
if(!view->edit_mode) {
// Do `dy` behaviors
new_line = view->cur_line + dy;
if(new_line > FvConfigEditMAX) {
// Out of bound cursor. No-op.
return;
}
view->cur_line = new_line;
// Handle moving scroll position.
if(new_line < view->scroll_pos) {
view->scroll_pos = new_line;
} else if(new_line >= (view->scroll_pos + 3)) {
view->scroll_pos += dy;
}
// Do `dx` behavior
switch(line_type) {
case FvConfigEditTypeTimer:
// no-op unless edit mode
break;
case FvConfigEditTypeCount:
min_value = 0;
max_value = INT_MAX;
// fall through.
case FvConfigEditTypeEnum:
if((*line_value + dx) >= min_value && (*line_value + dx) <= max_value) {
*line_value += dx;
}
break;
}
} else /* edit mode */ {
switch(line_type) {
case FvConfigEditTypeCount:
case FvConfigEditTypeEnum:
// If current line does not edit mode.. why are we in edit mode?
// Reaching this would be a bug, so lets go back to normal mode.
view->edit_mode = false;
return;
case FvConfigEditTypeTimer:
switch(view->cur_index) {
case 0:
if(*line_value + (dy * -10) >= 0) {
*line_value += (dy * -10);
}
break;
case 1:
if(*line_value + (dy * -1000) >= 0) {
*line_value += (dy * -1000);
}
break;
case 2:
if(*line_value + (dy * -60000) >= 0) {
*line_value += (dy * -60000);
}
break;
case 3:
if(*line_value + (dy * -3600000) >= 0) {
*line_value += (dy * -3600000);
}
break;
}
view->cur_index -= dx;
if(view->cur_index < 0) {
view->cur_index = 0;
}
if(view->cur_index > 3) {
view->cur_index = 3;
}
break;
}
}
}
static int flipvalo_config_edit_input(InputEvent* event, struct flipvalo_config_edit_view* view) {
// ignore all but short and repeats
if(!(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
return 0;
}
switch(event->key) {
case InputKeyRight:
flipvalo_config_edit_input_move_cursor(view, 1, 0);
break;
case InputKeyLeft:
flipvalo_config_edit_input_move_cursor(view, -1, 0);
break;
case InputKeyUp:
flipvalo_config_edit_input_move_cursor(view, 0, -1);
break;
case InputKeyDown:
flipvalo_config_edit_input_move_cursor(view, 0, 1);
break;
case InputKeyOk:
//TODO(luna) Check if line supports edit mode before doing this.
view->edit_mode = !view->edit_mode;
break;
case InputKeyBack:
if(view->edit_mode) {
view->edit_mode = false;
} else {
// exit config edit view
return 1;
}
default:
break;
}
return 0;
}
// XXX(luna) back to app
static void flipvalo_run_state_init(struct flipvalo_run_state* fv_run_state) {
fv_run_state->burst_cur = 1;
fv_run_state->shot_cur = 1;
fv_run_state->tick_next = 0;
fv_run_state->state = FVDone;
fv_run_state->tick_next = 0;
fv_run_state->tick_cur = 0;
}
static void input_callback(InputEvent* input_event, void* event_queue_void) {
FuriMessageQueue* event_queue = (FuriMessageQueue*)event_queue_void;
furi_assert(event_queue);
struct plugin_event event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static inline bool flipvalo_intv_running(struct flipvalo_priv* fv_priv) {
return fv_priv->run_state.state != FVDone;
}
static void flipvalo_intv_tick(struct flipvalo_priv* fv_priv) {
struct flipvalo_config* conf = &fv_priv->config;
struct flipvalo_run_state* run = &fv_priv->run_state;
// check if action required
if(run->tick_cur++ >= run->tick_next) {
// call trigger function
flipvalo_get_trigger(conf->trigger)->send(conf->output_config);
fv_priv->gui_shutter_blink = 3;
// end of burst, prepare next shot
if(run->burst_cur >= conf->burst_count) {
run->burst_cur = 1;
run->shot_cur++;
run->state = FVWaitContShot;
run->tick_next = run->tick_cur + ((conf->interval_delay_msec * conf->tickrate) / 1000);
} else /*continue burst */ {
run->burst_cur++;
run->state = FVWaitBurst;
run->tick_next = run->tick_cur + ((conf->burst_delay_msec * conf->tickrate) / 1000);
}
}
if(run->shot_cur > conf->shot_count) {
run->state = FVDone;
}
}
static void flipvalo_intv_stop(struct flipvalo_priv* fv_priv) {
fv_priv->run_state.state = FVDone;
}
static void flipvalo_intv_start(struct flipvalo_priv* fv_priv) {
// clear struct
furi_assert(fv_priv);
flipvalo_run_state_init(&fv_priv->run_state);
fv_priv->run_state.state = FVWaitInitShot;
fv_priv->run_state.tick_next =
((fv_priv->config.init_delay_msec * fv_priv->config.tickrate) / 1000);
}
static void timer_callback(void* ctx) {
furi_assert(ctx);
struct flipvalo_priv* fv_priv = ctx;
furi_mutex_acquire(fv_priv->mutex, FuriWaitForever);
if(flipvalo_intv_running(fv_priv)) {
flipvalo_intv_tick(fv_priv);
}
furi_mutex_release(fv_priv->mutex);
}
static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
struct flipvalo_priv* fv_priv = ctx;
FuriString* temp_str = furi_string_alloc();
furi_mutex_acquire(fv_priv->mutex, FuriWaitForever);
// invert screen if blinking
if(fv_priv->gui_shutter_blink > 0) {
fv_priv->gui_shutter_blink--;
canvas_draw_box(canvas, 0, 0, 127, 63);
canvas_set_color(canvas, ColorWhite);
}
if(fv_priv->ui_scene == FVSceneMain) {
int countdown_msec =
(1000 * (fv_priv->run_state.tick_next - fv_priv->run_state.tick_cur)) /
fv_priv->config.tickrate;
int elapsed_msec = (1000 * fv_priv->run_state.tick_cur) / fv_priv->config.tickrate;
canvas_draw_frame(canvas, 0, 0, 128, 64);
// draw countdown
canvas_set_font(canvas, FontPrimary);
furi_string_printf(
temp_str,
"%02d:%02d:%02d:%03d",
countdown_msec / 3600000,
(countdown_msec / 60000) % 60,
(countdown_msec / 1000) % 60,
countdown_msec % 1000);
canvas_draw_str_aligned(
canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str));
// draw top and bottom status bars
canvas_set_font(canvas, FontSecondary);
furi_string_printf(
temp_str,
"%02d:%02d:%02d",
elapsed_msec / 3600000,
(elapsed_msec / 60000) % 60,
(elapsed_msec / 1000) % 60);
canvas_draw_str_aligned(
canvas, 4, 8, AlignLeft, AlignCenter, furi_string_get_cstr(temp_str));
furi_string_printf(temp_str, "Shot: %d", fv_priv->run_state.shot_cur);
canvas_draw_str_aligned(
canvas, 124, 8, AlignRight, AlignCenter, furi_string_get_cstr(temp_str));
elements_button_left(canvas, "Cfg");
elements_button_right(canvas, "Snap");
if(fv_priv->run_state.state == FVDone) {
elements_button_center(canvas, "Start");
} else {
elements_button_center(canvas, "Stop ");
}
} else if(fv_priv->ui_scene == FVSceneConfig) {
flipvalo_config_edit_draw(canvas, &fv_priv->config_edit_view);
}
furi_string_free(temp_str);
furi_mutex_release(fv_priv->mutex);
}
static void flipvalo_config_init(struct flipvalo_config* fv_conf) {
fv_conf->init_delay_msec = 2000;
fv_conf->interval_delay_msec = 0;
fv_conf->shot_count = 1;
fv_conf->burst_count = 1;
fv_conf->burst_delay_msec = 0;
fv_conf->tickrate = 125;
fv_conf->trigger = FvTrigSony;
fv_conf->output_config = NULL;
}
static void flipvalo_priv_init(struct flipvalo_priv* fv_priv) {
flipvalo_config_init(&fv_priv->config);
flipvalo_config_edit_view_init(&fv_priv->config_edit_view);
flipvalo_run_state_init(&fv_priv->run_state);
fv_priv->gui_shutter_blink = 0;
fv_priv->timer = NULL;
fv_priv->notifications = NULL;
fv_priv->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
fv_priv->gui_shutter_blink = 0;
}
int32_t flipvalo_app() {
int ret = 0;
ViewPort* view_port = NULL;
Gui* gui = NULL;
FuriStatus event_status = {0};
struct plugin_event event = {0};
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(struct plugin_event));
struct flipvalo_priv* fv_priv = malloc(sizeof(*fv_priv));
flipvalo_priv_init(fv_priv);
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
InfraredSettings settings = {0};
saved_struct_load(
INFRARED_SETTINGS_PATH,
&settings,
sizeof(InfraredSettings),
INFRARED_SETTINGS_MAGIC,
INFRARED_SETTINGS_VERSION);
if(settings.tx_pin < FuriHalInfraredTxPinMax) {
furi_hal_infrared_set_tx_output(settings.tx_pin);
if(settings.otg_enabled != otg_was_enabled) {
if(settings.otg_enabled) {
furi_hal_power_enable_otg();
} else {
furi_hal_power_disable_otg();
}
}
} else {
FuriHalInfraredTxPin tx_pin_detected = furi_hal_infrared_detect_tx_output();
furi_hal_infrared_set_tx_output(tx_pin_detected);
if(tx_pin_detected != FuriHalInfraredTxPinInternal) {
furi_hal_power_enable_otg();
}
}
if(!fv_priv->mutex) {
FURI_LOG_E("Flipvalo", "Cannot create mutex\r\n");
ret = 1;
goto cleanup;
}
view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, fv_priv);
view_port_input_callback_set(view_port, input_callback, event_queue);
fv_priv->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, fv_priv);
furi_timer_start(
fv_priv->timer, (uint32_t)furi_kernel_get_tick_frequency() / fv_priv->config.tickrate);
gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
while(true) {
event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(fv_priv->mutex, FuriWaitForever);
// catch event_status that is not Ok
if(event_status == FuriStatusErrorTimeout) {
// timeout, ignore
goto next_event;
} else if(event_status != FuriStatusOk) {
FURI_LOG_E("Flipvalo", "Event Queue Error: %d\r\n", event_status);
goto next_event;
// TODO(luna) evaluate if we should exit here.
//goto cleanup;
}
// handle input
if(/* long press back */
event.type == EventTypeKey && event.input.type == InputTypeLong &&
event.input.key == InputKeyBack) {
goto cleanup;
}
switch(fv_priv->ui_scene) {
case FVSceneMain:
// TODO(luna) Maybe give this a function.. look howl clean FVSceneConfig is...
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort || event.input.type == InputTypeLong) {
switch(event.input.key) {
case InputKeyUp:
break;
case InputKeyDown:
break;
case InputKeyLeft:
flipvalo_intv_stop(fv_priv);
fv_priv->config_edit_view.config = &fv_priv->config;
fv_priv->ui_scene = FVSceneConfig;
break;
case InputKeyRight:
fv_priv->gui_shutter_blink = 3;
flipvalo_get_trigger(fv_priv->config.trigger)
->send(fv_priv->config.output_config);
break;
case InputKeyOk:
if(flipvalo_intv_running(fv_priv)) {
flipvalo_intv_stop(fv_priv);
} else {
flipvalo_intv_start(fv_priv);
}
break;
case InputKeyMAX:
break;
case InputKeyBack:
break;
}
}
}
break;
case FVSceneConfig:
ret = flipvalo_config_edit_input(&event.input, &fv_priv->config_edit_view);
if(ret) {
fv_priv->ui_scene = FVSceneMain;
}
break;
}
next_event:
furi_mutex_release(fv_priv->mutex);
view_port_update(view_port);
}
cleanup:
if(view_port) {
view_port_enabled_set(view_port, false);
if(gui) {
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
}
view_port_free(view_port);
}
furi_hal_infrared_set_tx_output(FuriHalInfraredTxPinInternal);
if(furi_hal_power_is_otg_enabled() != otg_was_enabled) {
if(otg_was_enabled) {
furi_hal_power_enable_otg();
} else {
furi_hal_power_disable_otg();
}
}
if(event_queue) {
furi_message_queue_free(event_queue);
}
if(fv_priv) {
furi_mutex_free(fv_priv->mutex);
furi_timer_free(fv_priv->timer);
}
free(fv_priv);
return ret;
}