Просмотр исходного кода

Message builder: User input progresses.

antirez 3 лет назад
Родитель
Сommit
e38ad0f7ca
5 измененных файлов с 201 добавлено и 69 удалено
  1. 1 0
      app.c
  2. 3 0
      app.h
  3. 124 0
      fields.c
  4. 55 21
      view_build.c
  5. 18 48
      view_info.c

+ 1 - 0
app.c

@@ -99,6 +99,7 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
     if (old == ViewDirectSampling) view_exit_direct_sampling(app);
     if (old == ViewDirectSampling) view_exit_direct_sampling(app);
     if (new == ViewDirectSampling) view_enter_direct_sampling(app);
     if (new == ViewDirectSampling) view_enter_direct_sampling(app);
     if (old == ViewBuildMessage) view_exit_build_message(app);
     if (old == ViewBuildMessage) view_exit_build_message(app);
+    if (old == ViewInfo) view_exit_info(app);
     /* The frequency/modulation settings are actually a single view:
     /* The frequency/modulation settings are actually a single view:
      * as long as the user stays between the two modes of this view we
      * as long as the user stays between the two modes of this view we
      * don't need to call the exit-view callback. */
      * don't need to call the exit-view callback. */

+ 3 - 0
app.h

@@ -282,6 +282,7 @@ void view_exit_build_message(ProtoViewApp *app);
 void view_enter_direct_sampling(ProtoViewApp *app);
 void view_enter_direct_sampling(ProtoViewApp *app);
 void view_exit_direct_sampling(ProtoViewApp *app);
 void view_exit_direct_sampling(ProtoViewApp *app);
 void view_exit_settings(ProtoViewApp *app);
 void view_exit_settings(ProtoViewApp *app);
+void view_exit_info(ProtoViewApp *app);
 void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur);
 void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur);
 
 
 /* ui.c */
 /* ui.c */
@@ -307,6 +308,8 @@ void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s);
 void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count);
 void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count);
 void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot);
 void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot);
 const char *field_get_type_name(ProtoViewField *f);
 const char *field_get_type_name(ProtoViewField *f);
+int field_to_string(char *buf, size_t len, ProtoViewField *f);
+bool field_set_from_string(ProtoViewField *f, char *buf, size_t len);
 
 
 /* crc.c */
 /* crc.c */
 uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);
 uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);

+ 124 - 0
fields.c

@@ -39,6 +39,130 @@ const char *field_get_type_name(ProtoViewField *f) {
     return "unknown";
     return "unknown";
 }
 }
 
 
+/* Set a string representation of the specified field in buf. */
+int field_to_string(char *buf, size_t len, ProtoViewField *f) {
+    switch(f->type) {
+    case FieldTypeStr:
+        return snprintf(buf,len,"%s", f->str);
+    case FieldTypeSignedInt:
+        return snprintf(buf,len,"%lld", (long long) f->value);
+    case FieldTypeUnsignedInt:
+        return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue);
+    case FieldTypeBinary:
+        {
+            uint64_t test_bit = (1 << (f->len-1));
+            uint64_t idx = 0;
+            while(idx < len-1 && test_bit) {
+                buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
+                test_bit >>= 1;
+            }
+            buf[idx] = 0;
+            return idx;
+        }
+    case FieldTypeHex:
+        return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue);
+    case FieldTypeFloat:
+        return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue);
+    case FieldTypeBytes:
+        {
+            uint64_t idx = 0;
+            uint32_t nibble_num = 0;
+            while(idx < len-1 && nibble_num < f->len) {
+                const char *charset = "0123456789ABCDEF";
+                uint32_t nibble = nibble_num & 1 ?
+                    (f->bytes[nibble_num/2] >> 4) :
+                    (f->bytes[nibble_num/2] & 0xf);
+                buf[idx++] = charset[nibble];
+                nibble_num++;
+            }
+            buf[idx] = 0;
+            return idx;
+        }
+    }
+    return 0;
+}
+
+/* Set the field value from its string representation in 'buf'.
+ * The field type must already be set and the field should be valid.
+ * The string represenation 'buf' must be null termianted. Note that
+ * even when representing binary values containing zero, this values
+ * are taken as representations, so that would be the string "00" as
+ * the Bytes type representation.
+ *
+ * The function returns true if the filed was successfully set to the
+ * new value, otherwise if the specified value is invalid for the
+ * field type, false is returned. */
+bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) {
+    long long val;
+    unsigned long long uval;
+    float fval;
+
+    switch(f->type) {
+    case FieldTypeStr:
+        free(f->str);
+        f->len = len;
+        f->str = malloc(len+1);
+        memcpy(f->str,buf,len+1);
+        break;
+    case FieldTypeSignedInt:
+        if (!sscanf(buf,"%lld",&val)) return false;
+        f->value = val;
+        break;
+    case FieldTypeUnsignedInt:
+        if (!sscanf(buf,"%llu",&uval)) return false;
+        f->uvalue = uval;
+        break;
+    case FieldTypeBinary:
+        {
+            uint64_t bit_to_set = (1 << (len-1));
+            uint64_t idx = 0;
+            uval = 0;
+            while(buf[idx]) {
+                if (buf[idx] == '1') uval |= bit_to_set;
+                else if (buf[idx] != '0') return false;
+                bit_to_set >>= 1;
+                idx++;
+            }
+        }
+        break;
+    case FieldTypeHex:
+        if (!sscanf(buf,"%llx",&uval) &&
+            !sscanf(buf,"%llX",&uval)) return false;
+        f->uvalue = uval;
+        break;
+    case FieldTypeFloat:
+        if (!sscanf(buf,"%f",&fval)) return false;
+        f->fvalue = fval;
+        break;
+    case FieldTypeBytes:
+        {
+            if (len > f->len) return false;
+            uint64_t idx = 0;
+            uint64_t nibble_idx = len-1;
+            while(buf[idx]) {
+                uint8_t nibble = 0;
+                char c = toupper(buf[idx]);
+                if (c >= '0' && c <= '9') nibble = c-'0';
+                else if (c >= 'A' && c <= 'F') nibble = c-'A';
+                else return false;
+
+                if (nibble_idx & 1) {
+                    f->bytes[idx/2] = 
+                        (f->bytes[idx/2] & 0x0F) | (nibble<<4);
+                } else {
+                    f->bytes[idx/2] = 
+                        (f->bytes[idx/2] & 0xF0) | nibble;
+                }
+                nibble_idx--;
+                idx++;
+            }
+            buf[idx] = 0;
+        }
+        break;
+    }
+    return true;
+}
+
 /* Free a field set and its contained fields. */
 /* Free a field set and its contained fields. */
 void fieldset_free(ProtoViewFieldSet *fs) {
 void fieldset_free(ProtoViewFieldSet *fs) {
     for (uint32_t j = 0; j < fs->numfields; j++)
     for (uint32_t j = 0; j < fs->numfields; j++)

+ 55 - 21
view_build.c

@@ -6,14 +6,18 @@
 extern ProtoViewDecoder *Decoders[];    // Defined in signal.c.
 extern ProtoViewDecoder *Decoders[];    // Defined in signal.c.
 
 
 /* Our view private data. */
 /* Our view private data. */
+#define USER_VALUE_LEN 64
 typedef struct {
 typedef struct {
-    ProtoViewDecoder *decoder;      // Decoder we are using to create a message.
-    uint32_t cur_decoder;           // Decoder index when we are yet selecting
-                                    // a decoder. Used when decoder is NULL.
-    ProtoViewFieldSet *fieldset;    // The fields to populate.
-    uint32_t cur_field;             // Field we are editing right now. This
-                                    // is the index inside the 'fieldset'
-                                    // fields.
+    ProtoViewDecoder *decoder;      /* Decoder we are using to create a
+                                       message. */
+    uint32_t cur_decoder;           /* Decoder index when we are yet selecting
+                                       a decoder. Used when decoder is NULL. */
+    ProtoViewFieldSet *fieldset;    /* The fields to populate. */
+    uint32_t cur_field;             /* Field we are editing right now. This
+                                       is the index inside the 'fieldset'
+                                       fields. */
+    char *user_value;               /* Keyboard input to replace the current
+                                       field value goes here. */
 } BuildViewPrivData;
 } BuildViewPrivData;
 
 
 /* Not all the decoders support message bulding, so we can't just
 /* Not all the decoders support message bulding, so we can't just
@@ -44,7 +48,7 @@ static void select_prev_decoder(ProtoViewApp *app) {
 static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) {
 static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) {
     BuildViewPrivData *privdata = app->view_privdata;
     BuildViewPrivData *privdata = app->view_privdata;
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 0, 9, "Signal builder");
+    canvas_draw_str(canvas, 0, 9, "Signal creator");
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
     canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
     canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
 
 
@@ -54,9 +58,7 @@ static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app)
         select_next_decoder(app);
         select_next_decoder(app);
 
 
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 0, 9, "Signal builder");
-
-    canvas_draw_str_aligned(canvas,64,36,AlignCenter,AlignCenter,
+    canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter,
         Decoders[privdata->cur_decoder]->name);
         Decoders[privdata->cur_decoder]->name);
 }
 }
 
 
@@ -68,26 +70,32 @@ static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) {
     snprintf(buf,sizeof(buf), "%s field %d/%d",
     snprintf(buf,sizeof(buf), "%s field %d/%d",
         privdata->decoder->name, (int)privdata->cur_field+1,
         privdata->decoder->name, (int)privdata->cur_field+1,
         (int)privdata->fieldset->numfields);
         (int)privdata->fieldset->numfields);
+    canvas_set_color(canvas,ColorBlack);
+    canvas_draw_box(canvas,0,0,128,21);
+    canvas_set_color(canvas,ColorWhite);
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 0, 9, buf);
+    canvas_draw_str(canvas, 1, 9, buf);
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 0, 19, "up/down: next field, ok: edit");
-    canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr");
+    canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit");
 
 
-    /* Write the field name, type, current content. For this part we
-     * write white text on black screen, to visually separate the UI
-     * description part from the UI current field editing part. */
+    /* Write the field name, type, current content. */
     canvas_set_color(canvas,ColorBlack);
     canvas_set_color(canvas,ColorBlack);
-    canvas_draw_box(canvas,0,21,128,32);
-
-    canvas_set_color(canvas,ColorWhite);
     ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field];
     ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field];
     snprintf(buf,sizeof(buf), "%s %s:%d", field->name,
     snprintf(buf,sizeof(buf), "%s %s:%d", field->name,
         field_get_type_name(field), (int)field->len);
         field_get_type_name(field), (int)field->len);
+    buf[0] = toupper(buf[0]);
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
     canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf);
     canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf);
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,"\"foobar\"");
+
+    /* Render the current value between "" */
+    unsigned int written = (unsigned int) field_to_string(buf+1,sizeof(buf)-1,field);
+    buf[0] = '"';
+    if (written+3 < sizeof(buf)) memcpy(buf+written+1,"\"\x00",2);
+    canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,buf);
+
+    /* Footer instructions. */
+    canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr");
 }
 }
 
 
 /* Render the build message view. */
 /* Render the build message view. */
@@ -122,12 +130,37 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
     }
     }
 }
 }
 
 
+/* Called after the user typed the new field value in the keyboard.
+ * Let's save it and remove the keyboard view. */
+static void text_input_done_callback(void* context) {
+    ProtoViewApp *app = context;
+    BuildViewPrivData *privdata = app->view_privdata;
+
+    if (field_set_from_string(privdata->fieldset->fields[privdata->cur_field],
+            privdata->user_value, strlen(privdata->user_value)) == false)
+    {
+        ui_show_alert(app, "Invalid value", 1500);
+    }
+
+    free(privdata->user_value);
+    privdata->user_value = NULL;
+    ui_dismiss_keyboard(app);
+}
+
 /* Handle input for fields editing mode. */
 /* Handle input for fields editing mode. */
 static void process_input_set_fields(ProtoViewApp *app, InputEvent input) {
 static void process_input_set_fields(ProtoViewApp *app, InputEvent input) {
     BuildViewPrivData *privdata = app->view_privdata;
     BuildViewPrivData *privdata = app->view_privdata;
     ProtoViewFieldSet *fs = privdata->fieldset;
     ProtoViewFieldSet *fs = privdata->fieldset;
     if (input.type == InputTypeShort) {
     if (input.type == InputTypeShort) {
         if (input.key == InputKeyOk) {
         if (input.key == InputKeyOk) {
+            /* Show the keyboard to let the user type the new
+             * value. */
+            if (privdata->user_value == NULL)
+                privdata->user_value = malloc(USER_VALUE_LEN);
+            field_to_string(privdata->user_value, USER_VALUE_LEN,
+                            fs->fields[privdata->cur_field]);
+            ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN,
+                             text_input_done_callback);
         } else if (input.key == InputKeyDown) {
         } else if (input.key == InputKeyDown) {
             privdata->cur_field = (privdata->cur_field+1) % fs->numfields;
             privdata->cur_field = (privdata->cur_field+1) % fs->numfields;
         } else if (input.key == InputKeyUp) {
         } else if (input.key == InputKeyUp) {
@@ -152,4 +185,5 @@ void process_input_build_message(ProtoViewApp *app, InputEvent input) {
 void view_exit_build_message(ProtoViewApp *app) {
 void view_exit_build_message(ProtoViewApp *app) {
     BuildViewPrivData *privdata = app->view_privdata;
     BuildViewPrivData *privdata = app->view_privdata;
     if (privdata->fieldset) fieldset_free(privdata->fieldset);
     if (privdata->fieldset) fieldset_free(privdata->fieldset);
+    if (privdata->user_value) free(privdata->user_value);
 }
 }

+ 18 - 48
view_info.c

@@ -31,53 +31,11 @@ static void render_info_field(Canvas *const canvas,
                               ProtoViewField *f, uint8_t x, uint8_t y)
                               ProtoViewField *f, uint8_t x, uint8_t y)
 {
 {
     char buf[64];
     char buf[64];
+    char strval[32];
+
+    field_to_string(strval,sizeof(strval),f);
+    snprintf(buf,sizeof(buf),"%s: %s", f->name, strval);
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-    switch(f->type) {
-    case FieldTypeStr:
-        snprintf(buf,sizeof(buf),"%s: %s", f->name, f->str);
-        break;
-    case FieldTypeSignedInt:
-        snprintf(buf,sizeof(buf),"%s: %lld", f->name, (long long) f->value);
-        break;
-    case FieldTypeUnsignedInt:
-        snprintf(buf,sizeof(buf),"%s: %llu", f->name,
-                    (unsigned long long) f->uvalue);
-        break;
-    case FieldTypeBinary:
-        {
-            uint64_t test_bit = (1 << (f->len-1));
-            uint64_t idx = snprintf(buf,sizeof(buf),"%s: ", f->name);
-            while(idx < sizeof(buf)-1 && test_bit) {
-                buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
-                test_bit >>= 1;
-            }
-            buf[idx] = 0;
-        }
-        break;
-    case FieldTypeHex:
-        snprintf(buf, sizeof(buf), "%s: 0x%*llX", f->name,
-            (int)(f->len+7)/8, f->uvalue);
-        break;
-    case FieldTypeFloat:
-        snprintf(buf, sizeof(buf), "%s: 0x%.*f", f->name,
-            (int)f->len, (double)f->fvalue);
-        break;
-    case FieldTypeBytes:
-        {
-            uint64_t idx = snprintf(buf,sizeof(buf),"%s: ", f->name);
-            uint32_t nibble_num = 0;
-            while(idx < sizeof(buf)-1 && nibble_num < f->len) {
-                const char *charset = "0123456789ABCDEF";
-                uint32_t nibble = nibble_num & 1 ?
-                    (f->bytes[nibble_num/2] >> 4) :
-                    (f->bytes[nibble_num/2] & 0xf);
-                buf[idx++] = charset[nibble];
-                nibble_num++;
-            }
-            buf[idx] = 0;
-        }
-        break;
-    }
     canvas_draw_str(canvas, x, y, buf);
     canvas_draw_str(canvas, x, y, buf);
 }
 }
 
 
@@ -176,7 +134,7 @@ void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
 
 
 /* The user typed the file name. Let's save it and remove the keyboard
 /* The user typed the file name. Let's save it and remove the keyboard
  * view. */
  * view. */
-void text_input_done_callback(void* context) {
+static void text_input_done_callback(void* context) {
     ProtoViewApp *app = context;
     ProtoViewApp *app = context;
     InfoViewPrivData *privdata = app->view_privdata;
     InfoViewPrivData *privdata = app->view_privdata;
 
 
@@ -186,6 +144,7 @@ void text_input_done_callback(void* context) {
     furi_string_free(save_path);
     furi_string_free(save_path);
 
 
     free(privdata->filename);
     free(privdata->filename);
+    privdata->filename = NULL; // Don't free it again on view exit
     ui_dismiss_keyboard(app);
     ui_dismiss_keyboard(app);
     ui_show_alert(app, "Signal saved", 1500);
     ui_show_alert(app, "Signal saved", 1500);
 }
 }
@@ -351,7 +310,10 @@ void process_input_info(ProtoViewApp *app, InputEvent input) {
                 privdata->signal_display_start_row--;
                 privdata->signal_display_start_row--;
         } else if (input.type == InputTypeLong && input.key == InputKeyOk)
         } else if (input.type == InputTypeLong && input.key == InputKeyOk)
         {
         {
-            privdata->filename = malloc(SAVE_FILENAME_LEN);
+            // We have have the buffer already allocated, in case the
+            // user aborted with BACK a previous saving.
+            if (privdata->filename == NULL)
+                privdata->filename = malloc(SAVE_FILENAME_LEN);
             set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
             set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
             ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
             ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
                              text_input_done_callback);
                              text_input_done_callback);
@@ -363,3 +325,11 @@ void process_input_info(ProtoViewApp *app, InputEvent input) {
         }
         }
     }
     }
 }
 }
+
+/* Called on view exit. */
+void view_exit_info(ProtoViewApp *app) {
+    InfoViewPrivData *privdata = app->view_privdata;
+    // When the user aborts the keyboard input, we are left with the
+    // filename buffer allocated.
+    if (privdata->filename) free(privdata->filename);
+}