view_info.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
  2. * See the LICENSE file for information about the license. */
  3. #include "app.h"
  4. #include <gui/view_i.h>
  5. #include <lib/toolbox/random_name.h>
  6. enum {
  7. SubViewInfoMain,
  8. SubViewInfoSave,
  9. SubViewInfoLast, /* Just a sentinel. */
  10. };
  11. /* Our view private data. */
  12. #define SAVE_FILENAME_LEN 64
  13. typedef struct {
  14. /* Our save view displays an oscilloscope-alike resampled signal,
  15. * so that the user can see what they are saving. With left/right
  16. * you can move to next rows. Here we store where we are. */
  17. uint32_t signal_display_start_row;
  18. char *filename;
  19. } InfoViewPrivData;
  20. /* Render the view with the detected message information. */
  21. static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) {
  22. /* Protocol name as title. */
  23. canvas_set_font(canvas, FontPrimary);
  24. uint8_t y = 8, lineheight = 10;
  25. canvas_draw_str(canvas, 0, y, app->msg_info->name);
  26. y += lineheight;
  27. /* Info fields. */
  28. char buf[128];
  29. canvas_set_font(canvas, FontSecondary);
  30. if (app->msg_info->raw[0]) {
  31. snprintf(buf,sizeof(buf),"Raw: %s", app->msg_info->raw);
  32. canvas_draw_str(canvas, 0, y, buf);
  33. y += lineheight;
  34. }
  35. canvas_draw_str(canvas, 0, y, app->msg_info->info1); y += lineheight;
  36. canvas_draw_str(canvas, 0, y, app->msg_info->info2); y += lineheight;
  37. canvas_draw_str(canvas, 0, y, app->msg_info->info3); y += lineheight;
  38. canvas_draw_str(canvas, 0, y, app->msg_info->info4); y += lineheight;
  39. y = 37;
  40. lineheight = 7;
  41. canvas_draw_str(canvas, 119, y, "s"); y += lineheight;
  42. canvas_draw_str(canvas, 119, y, "a"); y += lineheight;
  43. canvas_draw_str(canvas, 119, y, "v"); y += lineheight;
  44. canvas_draw_str(canvas, 119, y, "e"); y += lineheight;
  45. }
  46. /* Render view with save option. */
  47. static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
  48. InfoViewPrivData *privdata = app->view_privdata;
  49. /* Display our signal in digital form: here we don't show the
  50. * signal with the exact timing of the received samples, but as it
  51. * is in its logic form, in exact multiples of the short pulse length. */
  52. uint8_t rows = 6;
  53. uint8_t rowheight = 11;
  54. uint8_t bitwidth = 4;
  55. uint8_t bitheight = 5;
  56. uint32_t idx = privdata->signal_display_start_row * (128/4);
  57. bool prevbit = false;
  58. for (uint8_t y = bitheight+12; y <= rows*rowheight; y += rowheight) {
  59. for (uint8_t x = 0; x < 128; x += 4) {
  60. bool bit = bitmap_get(app->msg_info->bits,
  61. app->msg_info->bits_bytes,idx);
  62. uint8_t prevy = y + prevbit*(bitheight*-1) - 1;
  63. uint8_t thisy = y + bit*(bitheight*-1) - 1;
  64. canvas_draw_line(canvas,x,prevy,x,thisy);
  65. canvas_draw_line(canvas,x,thisy,x+bitwidth-1,thisy);
  66. prevbit = bit;
  67. if (idx >= app->msg_info->pulses_count) {
  68. canvas_set_color(canvas, ColorWhite);
  69. canvas_draw_dot(canvas, x+1,thisy);
  70. canvas_draw_dot(canvas, x+3,thisy);
  71. canvas_set_color(canvas, ColorBlack);
  72. }
  73. idx++; // Draw next bit
  74. }
  75. }
  76. canvas_set_font(canvas, FontSecondary);
  77. canvas_draw_str(canvas, 0, 6, "ok: save, < >: slide rows");
  78. }
  79. /* Render the selected subview of this view. */
  80. void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
  81. if (app->signal_decoded == false) {
  82. canvas_set_font(canvas, FontSecondary);
  83. canvas_draw_str(canvas, 30,36,"No signal decoded");
  84. return;
  85. }
  86. show_available_subviews(canvas,app,SubViewInfoLast);
  87. switch(app->current_subview[app->current_view]) {
  88. case SubViewInfoMain: render_subview_main(canvas,app); break;
  89. case SubViewInfoSave: render_subview_save(canvas,app); break;
  90. }
  91. }
  92. /* The user typed the file name. Let's save it and remove the keyboard
  93. * view. */
  94. void text_input_done_callback(void* context) {
  95. ProtoViewApp *app = context;
  96. InfoViewPrivData *privdata = app->view_privdata;
  97. FuriString *save_path = furi_string_alloc_printf(
  98. "%s/%s.sub", EXT_PATH("subghz"), privdata->filename);
  99. save_signal(app, furi_string_get_cstr(save_path));
  100. furi_string_free(save_path);
  101. free(privdata->filename);
  102. dismiss_keyboard(app);
  103. }
  104. /* Replace all the occurrences of character c1 with c2 in the specified
  105. * string. */
  106. void str_replace(char *buf, char c1, char c2) {
  107. char *p = buf;
  108. while(*p) {
  109. if (*p == c1) *p = c2;
  110. p++;
  111. }
  112. }
  113. /* Set a random filename the user can edit. */
  114. void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) {
  115. char suffix[6];
  116. set_random_name(suffix,sizeof(suffix));
  117. snprintf(buf,buflen,"%.10s-%s-%d",app->msg_info->name,suffix,rand()%1000);
  118. str_replace(buf,' ','_');
  119. str_replace(buf,'-','_');
  120. str_replace(buf,'/','_');
  121. }
  122. /* Handle input for the info view. */
  123. void process_input_info(ProtoViewApp *app, InputEvent input) {
  124. if (process_subview_updown(app,input,SubViewInfoLast)) return;
  125. InfoViewPrivData *privdata = app->view_privdata;
  126. int subview = get_current_subview(app);
  127. /* Main subview. */
  128. if (subview == SubViewInfoMain) {
  129. if (input.type == InputTypeShort && input.key == InputKeyOk) {
  130. /* Reset the current sample to capture the next. */
  131. reset_current_signal(app);
  132. }
  133. } else if (subview == SubViewInfoSave) {
  134. /* Save subview. */
  135. if (input.type == InputTypePress && input.key == InputKeyRight) {
  136. privdata->signal_display_start_row++;
  137. } else if (input.type == InputTypePress && input.key == InputKeyLeft) {
  138. if (privdata->signal_display_start_row != 0)
  139. privdata->signal_display_start_row--;
  140. } else if (input.type == InputTypePress && input.key == InputKeyOk) {
  141. privdata->filename = malloc(SAVE_FILENAME_LEN);
  142. set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
  143. show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
  144. text_input_done_callback);
  145. }
  146. }
  147. }