key_copier.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. #include "key_copier.h"
  2. #include "key_copier_icons.h"
  3. #include "key_formats.h"
  4. #include <applications/services/dialogs/dialogs.h>
  5. #include <applications/services/storage/storage.h>
  6. #include <flipper_format.h>
  7. #include <furi.h>
  8. #include <furi_hal.h>
  9. #include <gui/gui.h>
  10. #include <gui/modules/submenu.h>
  11. #include <gui/modules/text_input.h>
  12. #include <gui/modules/variable_item_list.h>
  13. #include <gui/modules/widget.h>
  14. #include <gui/view.h>
  15. #include <gui/view_dispatcher.h>
  16. #include <math.h>
  17. #include <notification/notification.h>
  18. #include <notification/notification_messages.h>
  19. #include <stdbool.h>
  20. #define TAG "KeyCopier"
  21. #define BACKLIGHT_ON 1
  22. typedef enum {
  23. KeyCopierSubmenuIndexMeasure,
  24. KeyCopierSubmenuIndexConfigure,
  25. KeyCopierSubmenuIndexSave,
  26. KeyCopierSubmenuIndexLoad,
  27. KeyCopierSubmenuIndexAbout,
  28. } KeyCopierSubmenuIndex;
  29. typedef enum {
  30. KeyCopierViewSubmenu,
  31. KeyCopierViewTextInput,
  32. KeyCopierViewConfigure_i,
  33. KeyCopierViewConfigure_e,
  34. KeyCopierViewSave,
  35. KeyCopierViewLoad,
  36. KeyCopierViewMeasure,
  37. KeyCopierViewAbout,
  38. } KeyCopierView;
  39. typedef struct {
  40. ViewDispatcher *view_dispatcher;
  41. NotificationApp *notifications;
  42. Submenu *submenu;
  43. TextInput *text_input;
  44. VariableItemList *variable_item_list_config;
  45. View *view_measure;
  46. View *view_config_e;
  47. View *view_save;
  48. View *view_load;
  49. Widget *widget_about;
  50. VariableItem *key_name_item;
  51. VariableItem *format_item;
  52. char *temp_buffer;
  53. uint32_t temp_buffer_size;
  54. DialogsApp *dialogs;
  55. FuriString *file_path;
  56. } KeyCopierApp;
  57. typedef struct {
  58. uint32_t format_index;
  59. FuriString *key_name_str;
  60. uint8_t pin_slc; // The pin that is being adjusted
  61. uint8_t *depth; // The cutting depth
  62. bool data_loaded;
  63. KeyFormat format;
  64. } KeyCopierModel;
  65. void initialize_model(KeyCopierModel *model) {
  66. if (model->depth != NULL) {
  67. free(model->depth);
  68. }
  69. model->format_index = 0;
  70. memcpy(&model->format, &all_formats[model->format_index], sizeof(KeyFormat));
  71. model->depth =
  72. (uint8_t *)malloc((model->format.pin_num + 1) * sizeof(uint8_t));
  73. for (uint8_t i = 0; i <= model->format.pin_num; i++) {
  74. model->depth[i] = model->format.min_depth_ind;
  75. }
  76. model->pin_slc = 1;
  77. model->data_loaded = 0;
  78. model->key_name_str = furi_string_alloc();
  79. }
  80. static uint32_t key_copier_navigation_exit_callback(void *_context) {
  81. UNUSED(_context);
  82. return VIEW_NONE;
  83. }
  84. static uint32_t key_copier_navigation_submenu_callback(void *_context) {
  85. UNUSED(_context);
  86. return KeyCopierViewSubmenu;
  87. }
  88. static void key_copier_submenu_callback(void *context, uint32_t index) {
  89. KeyCopierApp *app = (KeyCopierApp *)context;
  90. switch (index) {
  91. case KeyCopierSubmenuIndexMeasure:
  92. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewMeasure);
  93. break;
  94. case KeyCopierSubmenuIndexConfigure:
  95. view_dispatcher_switch_to_view(app->view_dispatcher,
  96. KeyCopierViewConfigure_e);
  97. break;
  98. case KeyCopierSubmenuIndexSave:
  99. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSave);
  100. break;
  101. case KeyCopierSubmenuIndexLoad:
  102. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewLoad);
  103. break;
  104. case KeyCopierSubmenuIndexAbout:
  105. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewAbout);
  106. break;
  107. default:
  108. break;
  109. }
  110. }
  111. char *manufacturers[COUNT_OF(all_formats)];
  112. void initialize_manufacturers(char **manufacturers) {
  113. // Populate the manufacturers array
  114. for (size_t i = 0; i < COUNT_OF(all_formats); i++) {
  115. manufacturers[i] = all_formats[i].manufacturer;
  116. }
  117. }
  118. static void key_copier_format_change(VariableItem *item) {
  119. KeyCopierApp *app = variable_item_get_context(item);
  120. KeyCopierModel *model = view_get_model(app->view_measure);
  121. if (model->data_loaded) {
  122. variable_item_set_current_value_index(item, model->format_index);
  123. }
  124. uint8_t format_index = variable_item_get_current_value_index(item);
  125. if (format_index != model->format_index) {
  126. model->format_index = format_index;
  127. model->format = all_formats[format_index];
  128. if (model->depth != NULL) {
  129. free(model->depth);
  130. }
  131. model->depth =
  132. (uint8_t *)malloc((model->format.pin_num + 1) * sizeof(uint8_t));
  133. for (uint8_t i = 0; i <= model->format.pin_num; i++) {
  134. model->depth[i] = model->format.min_depth_ind;
  135. }
  136. model->pin_slc = 1;
  137. }
  138. model->data_loaded = false;
  139. variable_item_set_current_value_text(item, model->format.format_name);
  140. model->format = all_formats[model->format_index];
  141. }
  142. static const char *format_config_label = "Key Format";
  143. static void key_copier_config_enter_callback(void *context) {
  144. KeyCopierApp *app = (KeyCopierApp *)context;
  145. KeyCopierModel *my_model = view_get_model(app->view_measure);
  146. variable_item_list_reset(app->variable_item_list_config);
  147. // Recreate this view every time we enter it so that it's always updated
  148. app->format_item = variable_item_list_add(
  149. app->variable_item_list_config, format_config_label,
  150. COUNT_OF(all_formats), key_copier_format_change, app);
  151. View *view_config_i =
  152. variable_item_list_get_view(app->variable_item_list_config);
  153. variable_item_set_current_value_index(app->format_item,
  154. my_model->format_index);
  155. key_copier_format_change(app->format_item);
  156. view_set_previous_callback(view_config_i,
  157. key_copier_navigation_submenu_callback);
  158. view_dispatcher_remove_view(app->view_dispatcher,
  159. KeyCopierViewConfigure_i); // delete the last one
  160. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_i,
  161. view_config_i);
  162. view_dispatcher_switch_to_view(app->view_dispatcher,
  163. KeyCopierViewConfigure_i); // recreate it
  164. }
  165. static const char *key_name_entry_text = "Enter name";
  166. static void key_copier_file_saver(void *context) {
  167. KeyCopierApp *app = (KeyCopierApp *)context;
  168. KeyCopierModel *model = view_get_model(app->view_measure);
  169. bool redraw = true;
  170. with_view_model(
  171. app->view_measure, KeyCopierModel * model,
  172. { furi_string_set(model->key_name_str, app->temp_buffer); }, redraw);
  173. FuriString *file_path = furi_string_alloc();
  174. furi_string_printf(file_path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX,
  175. furi_string_get_cstr(model->key_name_str),
  176. KEY_COPIER_FILE_EXTENSION);
  177. Storage *storage = furi_record_open(RECORD_STORAGE);
  178. storage_simply_mkdir(storage, STORAGE_APP_DATA_PATH_PREFIX);
  179. FURI_LOG_D(TAG, "mkdir finished");
  180. FlipperFormat *flipper_format = flipper_format_file_alloc(storage);
  181. do {
  182. const uint32_t version = 1;
  183. const uint32_t pin_num_buffer = (uint32_t)model->format.pin_num;
  184. const uint32_t macs_buffer = (uint32_t)model->format.macs;
  185. FuriString *buffer = furi_string_alloc();
  186. if (!flipper_format_file_open_always(flipper_format,
  187. furi_string_get_cstr(file_path)))
  188. break;
  189. if (!flipper_format_write_header_cstr(flipper_format,
  190. "Flipper Key Copier File", version))
  191. break;
  192. if (!flipper_format_write_string_cstr(flipper_format, "Manufacturer",
  193. model->format.manufacturer))
  194. break;
  195. if (!flipper_format_write_string_cstr(flipper_format, "Format Name",
  196. model->format.format_name))
  197. break;
  198. if (!flipper_format_write_string_cstr(flipper_format, "Data Sheet",
  199. model->format.format_link))
  200. break;
  201. if (!flipper_format_write_uint32(flipper_format, "Number of Pins",
  202. &pin_num_buffer, 1))
  203. break;
  204. if (!flipper_format_write_uint32(
  205. flipper_format, "Maximum Adjacent Cut Specification (MACS)",
  206. &macs_buffer, 1))
  207. break;
  208. for (int i = 0; i < model->format.pin_num; i++) {
  209. if (i < model->format.pin_num - 1) {
  210. furi_string_cat_printf(buffer, "%d-", model->depth[i]);
  211. } else {
  212. furi_string_cat_printf(buffer, "%d", model->depth[i]);
  213. }
  214. }
  215. if (!flipper_format_write_string(flipper_format, "Bitting Pattern", buffer))
  216. break;
  217. furi_string_free(buffer);
  218. // signal that the file was written successfully
  219. } while (0);
  220. flipper_format_free(flipper_format);
  221. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
  222. }
  223. static void key_copier_view_save_callback(void *context) {
  224. KeyCopierApp *app = (KeyCopierApp *)context;
  225. // Header to display on the text input screen.
  226. text_input_set_header_text(app->text_input, key_name_entry_text);
  227. // Copy the current name into the temporary buffer.
  228. bool redraw = false;
  229. with_view_model(
  230. app->view_measure, KeyCopierModel * model,
  231. {
  232. strncpy(app->temp_buffer, furi_string_get_cstr(model->key_name_str),
  233. app->temp_buffer_size);
  234. },
  235. redraw);
  236. // Configure the text input. When user enters text and clicks OK,
  237. // key_copier_file_saver be called.
  238. bool clear_previous_text = false;
  239. text_input_set_result_callback(app->text_input, key_copier_file_saver, app,
  240. app->temp_buffer, app->temp_buffer_size,
  241. clear_previous_text);
  242. view_set_previous_callback(text_input_get_view(app->text_input),
  243. key_copier_navigation_submenu_callback);
  244. // Show text input dialog.
  245. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewTextInput);
  246. }
  247. static void key_copier_view_load_callback(void *context) {
  248. KeyCopierApp *app = (KeyCopierApp *)context;
  249. KeyCopierModel *model = view_get_model(app->view_measure);
  250. DialogsFileBrowserOptions browser_options;
  251. Storage *storage = furi_record_open(RECORD_STORAGE);
  252. storage_simply_mkdir(storage, STORAGE_APP_DATA_PATH_PREFIX);
  253. dialog_file_browser_set_basic_options(&browser_options,
  254. KEY_COPIER_FILE_EXTENSION, &I_icon);
  255. browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
  256. furi_string_set(app->file_path, browser_options.base_path);
  257. if (dialog_file_browser_show(app->dialogs, app->file_path, app->file_path,
  258. &browser_options)) {
  259. FlipperFormat *flipper_format = flipper_format_file_alloc(storage);
  260. do {
  261. if (!flipper_format_file_open_existing(
  262. flipper_format, furi_string_get_cstr(app->file_path)))
  263. break;
  264. FuriString *format_buffer = furi_string_alloc();
  265. FuriString *depth_buffer = furi_string_alloc();
  266. if (!flipper_format_read_string(flipper_format, "Format Name",
  267. format_buffer))
  268. break;
  269. if (!flipper_format_read_string(flipper_format, "Bitting Pattern",
  270. depth_buffer))
  271. break;
  272. for (size_t i = 0; i < COUNT_OF(all_formats); i++) {
  273. if (!strcmp(furi_string_get_cstr(format_buffer),
  274. all_formats[i].format_name)) {
  275. model->format_index = (uint32_t)i;
  276. model->format = all_formats[model->format_index];
  277. }
  278. }
  279. for (int i = 0; i < model->format.pin_num; i++) {
  280. model->depth[i] =
  281. (uint8_t)(furi_string_get_char(depth_buffer, i * 2) - '0');
  282. }
  283. model->data_loaded = true;
  284. // signal that the file was read successfully
  285. } while (0);
  286. flipper_format_free(flipper_format);
  287. furi_record_close(RECORD_STORAGE);
  288. }
  289. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
  290. }
  291. static void key_copier_view_measure_draw_callback(Canvas *canvas, void *model) {
  292. static double inches_per_px = (double)INCHES_PER_PX;
  293. canvas_set_bitmap_mode(canvas, true);
  294. KeyCopierModel *my_model = (KeyCopierModel *)model;
  295. KeyFormat my_format = my_model->format;
  296. FuriString *buffer = furi_string_alloc();
  297. int pin_half_width_px =
  298. (int)round((my_format.pin_width_inch / inches_per_px) / 2);
  299. int pin_step_px = (int)round(my_format.pin_increment_inch / inches_per_px);
  300. double drill_radians = (180 - my_format.drill_angle) / 2 / 180 *
  301. (double)M_PI; // Convert angle to radians
  302. double tangent = tan(drill_radians);
  303. int top_contour_px =
  304. (int)round(62 - my_format.uncut_depth_inch / inches_per_px);
  305. int bottom_contour_px = 0;
  306. // int bottom_contour_px =
  307. // top_contour_px + (int)round(my_format.uncut_depth_inch /
  308. // inches_per_px);
  309. if (my_format.sides == 2)
  310. bottom_contour_px =
  311. top_contour_px + (int)round(my_format.uncut_depth_inch / inches_per_px);
  312. int post_extra_x_px = 0;
  313. int pre_extra_x_px = 0;
  314. int bottom_post_extra_x_px = 0; // new
  315. int bottom_pre_extra_x_px = 0; // new
  316. for (int current_pin = 1; current_pin <= my_model->format.pin_num;
  317. current_pin += 1) {
  318. double current_center_px = my_format.first_pin_inch +
  319. (current_pin - 1) * my_format.pin_increment_inch;
  320. int pin_center_px = (int)round(current_center_px / inches_per_px);
  321. furi_string_printf(buffer, "%d", my_model->depth[current_pin - 1]);
  322. canvas_draw_str_aligned(canvas, pin_center_px, top_contour_px - 12,
  323. AlignCenter, AlignCenter,
  324. furi_string_get_cstr(buffer));
  325. canvas_draw_line(
  326. canvas, pin_center_px, top_contour_px - 5, pin_center_px,
  327. top_contour_px); // the vertical line to indicate pin center
  328. int current_depth =
  329. my_model->depth[current_pin - 1] - my_format.min_depth_ind;
  330. int current_depth_px =
  331. (int)round(current_depth * my_format.depth_step_inch / inches_per_px);
  332. canvas_draw_line(
  333. canvas, pin_center_px - pin_half_width_px,
  334. top_contour_px + current_depth_px, pin_center_px + pin_half_width_px,
  335. top_contour_px +
  336. current_depth_px); // draw top pin width horizontal line
  337. // if(my_format.sides == 2) //old (remove new start to end and remove // for
  338. // this line to fix)
  339. if (my_format.sides == 2) { // new start
  340. int last_depth =
  341. my_model->depth[current_pin - 2] - my_format.min_depth_ind;
  342. int next_depth = my_model->depth[current_pin] - my_format.min_depth_ind;
  343. int current_depth =
  344. my_model->depth[current_pin - 1] - my_format.min_depth_ind;
  345. int current_depth_px =
  346. (int)round(current_depth * my_format.depth_step_inch / inches_per_px);
  347. // Draw horizontal line for bottom pin
  348. canvas_draw_line(canvas, pin_center_px - pin_half_width_px,
  349. bottom_contour_px - current_depth_px,
  350. pin_center_px + pin_half_width_px,
  351. bottom_contour_px - current_depth_px);
  352. // Handle first pin for bottom
  353. if (current_pin == 1) {
  354. canvas_draw_line(canvas, 0, bottom_contour_px,
  355. pin_center_px - pin_half_width_px - current_depth_px,
  356. bottom_contour_px);
  357. last_depth = 0;
  358. bottom_pre_extra_x_px = max(current_depth_px + pin_half_width_px, 0);
  359. }
  360. // Handle left side intersection for bottom
  361. if ((last_depth + current_depth) > my_format.clearance) {
  362. if (current_pin != 1) {
  363. bottom_pre_extra_x_px =
  364. min(max(pin_step_px - bottom_post_extra_x_px, pin_half_width_px),
  365. pin_step_px - pin_half_width_px);
  366. }
  367. canvas_draw_line(
  368. canvas, pin_center_px - bottom_pre_extra_x_px,
  369. bottom_contour_px -
  370. max((int)round((current_depth_px -
  371. (bottom_pre_extra_x_px - pin_half_width_px)) *
  372. tangent),
  373. 0),
  374. pin_center_px - pin_half_width_px,
  375. bottom_contour_px - (int)round(current_depth_px * tangent));
  376. } else {
  377. int last_depth_px =
  378. (int)round(last_depth * my_format.depth_step_inch / inches_per_px);
  379. int up_slope_start_x_px =
  380. pin_center_px - pin_half_width_px - current_depth_px;
  381. canvas_draw_line(
  382. canvas, pin_center_px - pin_half_width_px - current_depth_px,
  383. bottom_contour_px, pin_center_px - pin_half_width_px,
  384. bottom_contour_px - (int)round(current_depth_px * tangent));
  385. canvas_draw_line(
  386. canvas,
  387. min(pin_center_px - pin_step_px + pin_half_width_px + last_depth_px,
  388. up_slope_start_x_px),
  389. bottom_contour_px, up_slope_start_x_px, bottom_contour_px);
  390. }
  391. // Handle right side intersection for bottom
  392. if ((current_depth + next_depth) > my_format.clearance) {
  393. double numerator = (double)current_depth;
  394. double denominator = (double)(current_depth + next_depth);
  395. double product = (numerator / denominator) * pin_step_px;
  396. bottom_post_extra_x_px = (int)min(max(product, pin_half_width_px),
  397. pin_step_px - pin_half_width_px);
  398. canvas_draw_line(
  399. canvas, pin_center_px + pin_half_width_px,
  400. bottom_contour_px - current_depth_px,
  401. pin_center_px + bottom_post_extra_x_px,
  402. bottom_contour_px -
  403. max(current_depth_px - (int)round((bottom_post_extra_x_px -
  404. pin_half_width_px) *
  405. tangent),
  406. 0));
  407. } else {
  408. canvas_draw_line(canvas, pin_center_px + pin_half_width_px,
  409. bottom_contour_px -
  410. (int)round(current_depth_px * tangent),
  411. pin_center_px + pin_half_width_px + current_depth_px,
  412. bottom_contour_px);
  413. }
  414. }
  415. // new end
  416. // canvas_draw_line(
  417. // canvas,
  418. // pin_center_px - pin_half_width_px,
  419. // bottom_contour_px - current_depth_px,
  420. // pin_center_px + pin_half_width_px,
  421. // bottom_contour_px - current_depth_px); // draw bottom pin width
  422. // horizontal line
  423. int last_depth = my_model->depth[current_pin - 2] - my_format.min_depth_ind;
  424. int next_depth = my_model->depth[current_pin] - my_format.min_depth_ind;
  425. if (current_pin == 1) {
  426. canvas_draw_line(canvas, 0, top_contour_px,
  427. pin_center_px - pin_half_width_px - current_depth_px,
  428. top_contour_px); // draw top shoulder
  429. last_depth = 0;
  430. pre_extra_x_px = max(current_depth_px + pin_half_width_px, 0);
  431. // canvas_draw_line(
  432. // canvas,
  433. // 0,
  434. // bottom_contour_px,
  435. // pin_center_px - pin_half_width_px - current_depth_px,
  436. // bottom_contour_px); // draw bottom shoulder (hidden by
  437. // level contour)
  438. }
  439. if (current_pin == my_model->format.pin_num) {
  440. next_depth = 0;
  441. }
  442. if ((last_depth + current_depth) > my_format.clearance) { // yes
  443. // intersection
  444. if (current_pin != 1) {
  445. pre_extra_x_px =
  446. min(max(pin_step_px - post_extra_x_px, pin_half_width_px),
  447. pin_step_px - pin_half_width_px);
  448. }
  449. canvas_draw_line(
  450. canvas, pin_center_px - pre_extra_x_px,
  451. top_contour_px +
  452. max((int)round((current_depth_px -
  453. (pre_extra_x_px - pin_half_width_px)) *
  454. tangent),
  455. 0),
  456. pin_center_px - pin_half_width_px,
  457. top_contour_px + (int)round(current_depth_px * tangent));
  458. } else {
  459. int last_depth_px =
  460. (int)round(last_depth * my_format.depth_step_inch / inches_per_px);
  461. int down_slope_start_x_px =
  462. pin_center_px - pin_half_width_px - current_depth_px;
  463. canvas_draw_line(canvas,
  464. pin_center_px - pin_half_width_px - current_depth_px,
  465. top_contour_px, pin_center_px - pin_half_width_px,
  466. top_contour_px + (int)round(current_depth_px * tangent));
  467. canvas_draw_line(
  468. canvas,
  469. min(pin_center_px - pin_step_px + pin_half_width_px + last_depth_px,
  470. down_slope_start_x_px),
  471. top_contour_px, down_slope_start_x_px, top_contour_px);
  472. }
  473. if ((current_depth + next_depth) > my_format.clearance) { // yes
  474. // intersection
  475. double numerator = (double)current_depth;
  476. double denominator = (double)(current_depth + next_depth);
  477. double product = (numerator / denominator) * pin_step_px;
  478. post_extra_x_px = (int)min(max(product, pin_half_width_px),
  479. pin_step_px - pin_half_width_px);
  480. canvas_draw_line(
  481. canvas, pin_center_px + pin_half_width_px,
  482. top_contour_px + current_depth_px, pin_center_px + post_extra_x_px,
  483. top_contour_px +
  484. max(current_depth_px -
  485. (int)round((post_extra_x_px - pin_half_width_px) *
  486. tangent),
  487. 0));
  488. } else { // no intersection
  489. canvas_draw_line(canvas, pin_center_px + pin_half_width_px,
  490. top_contour_px + (int)round(current_depth_px * tangent),
  491. pin_center_px + pin_half_width_px + current_depth_px,
  492. top_contour_px);
  493. }
  494. }
  495. int level_contour_px = (int)round(
  496. (my_format.last_pin_inch + my_format.elbow_inch) / inches_per_px);
  497. int elbow_px = (int)round(my_format.elbow_inch / inches_per_px);
  498. canvas_draw_line(canvas, 0, 62, level_contour_px, 62);
  499. canvas_draw_line(canvas, level_contour_px, 62, level_contour_px + elbow_px,
  500. 62 - elbow_px);
  501. if (my_format.stop == 2) {
  502. // Draw a line using level_contour_px if stop equals tip
  503. canvas_draw_line(
  504. canvas, level_contour_px, top_contour_px, level_contour_px,
  505. 63); // change to if stop ==tip and measure from this point back.
  506. } else {
  507. // Otherwise, draw a default line
  508. canvas_draw_line(canvas, 1, top_contour_px, 1, 63);
  509. }
  510. int slc_pin_px =
  511. (int)round((my_format.first_pin_inch +
  512. (my_model->pin_slc - 1) * my_format.pin_increment_inch) /
  513. inches_per_px);
  514. canvas_draw_icon(canvas, slc_pin_px - 2, top_contour_px - 25, &I_arrow_down);
  515. furi_string_printf(buffer, "%s", my_format.format_name);
  516. canvas_draw_str(canvas, 100, 10, furi_string_get_cstr(buffer));
  517. furi_string_free(buffer);
  518. }
  519. static bool key_copier_view_measure_input_callback(InputEvent *event,
  520. void *context) {
  521. KeyCopierApp *app = (KeyCopierApp *)context;
  522. if (event->type == InputTypeShort) {
  523. switch (event->key) {
  524. case InputKeyLeft: {
  525. bool redraw = true;
  526. with_view_model(
  527. app->view_measure, KeyCopierModel * model,
  528. {
  529. if (model->pin_slc > 1) {
  530. model->pin_slc--;
  531. }
  532. },
  533. redraw);
  534. break;
  535. }
  536. case InputKeyRight: {
  537. bool redraw = true;
  538. with_view_model(
  539. app->view_measure, KeyCopierModel * model,
  540. {
  541. if (model->pin_slc < model->format.pin_num) {
  542. model->pin_slc++;
  543. }
  544. },
  545. redraw);
  546. break;
  547. }
  548. case InputKeyUp: {
  549. bool redraw = true;
  550. with_view_model(
  551. app->view_measure, KeyCopierModel * model,
  552. {
  553. if (model->depth[model->pin_slc - 1] >
  554. model->format.min_depth_ind) {
  555. if (model->pin_slc ==
  556. 1) { // first pin only limited by the next one
  557. if (model->depth[model->pin_slc] -
  558. model->depth[model->pin_slc - 1] <
  559. model->format.macs)
  560. model->depth[model->pin_slc - 1]--;
  561. } else if (model->pin_slc ==
  562. model->format.pin_num) { // last pin only limited by
  563. // the previous one
  564. if (model->depth[model->pin_slc - 2] -
  565. model->depth[model->pin_slc - 1] <
  566. model->format.macs) {
  567. model->depth[model->pin_slc - 1]--;
  568. }
  569. } else {
  570. if (model->depth[model->pin_slc] -
  571. model->depth[model->pin_slc - 1] <
  572. model->format.macs &&
  573. model->depth[model->pin_slc - 2] -
  574. model->depth[model->pin_slc - 1] <
  575. model->format.macs) {
  576. model->depth[model->pin_slc - 1]--;
  577. }
  578. }
  579. }
  580. },
  581. redraw);
  582. break;
  583. }
  584. case InputKeyDown: {
  585. bool redraw = true;
  586. with_view_model(
  587. app->view_measure, KeyCopierModel * model,
  588. {
  589. if (model->depth[model->pin_slc - 1] <
  590. model->format.max_depth_ind) {
  591. if (model->pin_slc ==
  592. 1) { // first pin only limited by the next one
  593. if (model->depth[model->pin_slc - 1] -
  594. model->depth[model->pin_slc] <
  595. model->format.macs)
  596. model->depth[model->pin_slc - 1]++;
  597. } else if (model->pin_slc ==
  598. model->format.pin_num) { // last pin only limited by
  599. // the previous one
  600. if (model->depth[model->pin_slc - 1] -
  601. model->depth[model->pin_slc - 2] <
  602. model->format.macs) {
  603. model->depth[model->pin_slc - 1]++;
  604. }
  605. } else {
  606. if (model->depth[model->pin_slc - 1] -
  607. model->depth[model->pin_slc] <
  608. model->format.macs &&
  609. model->depth[model->pin_slc - 1] -
  610. model->depth[model->pin_slc - 2] <
  611. model->format.macs) {
  612. model->depth[model->pin_slc - 1]++;
  613. }
  614. }
  615. }
  616. },
  617. redraw);
  618. break;
  619. }
  620. default:
  621. // Handle other keys or do nothing
  622. break;
  623. }
  624. }
  625. return false;
  626. }
  627. static KeyCopierApp *key_copier_app_alloc() {
  628. KeyCopierApp *app = (KeyCopierApp *)malloc(sizeof(KeyCopierApp));
  629. Gui *gui = furi_record_open(RECORD_GUI);
  630. app->view_dispatcher = view_dispatcher_alloc();
  631. view_dispatcher_attach_to_gui(app->view_dispatcher, gui,
  632. ViewDispatcherTypeFullscreen);
  633. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  634. app->dialogs = furi_record_open(RECORD_DIALOGS);
  635. app->file_path = furi_string_alloc();
  636. app->submenu = submenu_alloc();
  637. submenu_add_item(app->submenu, "Measure", KeyCopierSubmenuIndexMeasure,
  638. key_copier_submenu_callback, app);
  639. submenu_add_item(app->submenu, "Config", KeyCopierSubmenuIndexConfigure,
  640. key_copier_submenu_callback, app);
  641. submenu_add_item(app->submenu, "Save", KeyCopierSubmenuIndexSave,
  642. key_copier_submenu_callback, app);
  643. submenu_add_item(app->submenu, "Load", KeyCopierSubmenuIndexLoad,
  644. key_copier_submenu_callback, app);
  645. submenu_add_item(app->submenu, "About", KeyCopierSubmenuIndexAbout,
  646. key_copier_submenu_callback, app);
  647. view_set_previous_callback(submenu_get_view(app->submenu),
  648. key_copier_navigation_exit_callback);
  649. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewSubmenu,
  650. submenu_get_view(app->submenu));
  651. view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
  652. app->text_input = text_input_alloc();
  653. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewTextInput,
  654. text_input_get_view(app->text_input));
  655. app->temp_buffer_size = 32;
  656. app->temp_buffer = (char *)malloc(app->temp_buffer_size);
  657. app->temp_buffer = "";
  658. app->view_measure = view_alloc();
  659. view_set_draw_callback(app->view_measure,
  660. key_copier_view_measure_draw_callback);
  661. view_set_input_callback(app->view_measure,
  662. key_copier_view_measure_input_callback);
  663. view_set_previous_callback(app->view_measure,
  664. key_copier_navigation_submenu_callback);
  665. view_set_context(app->view_measure, app);
  666. view_allocate_model(app->view_measure, ViewModelTypeLockFree,
  667. sizeof(KeyCopierModel));
  668. KeyCopierModel *model = view_get_model(app->view_measure);
  669. initialize_model(model);
  670. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewMeasure,
  671. app->view_measure);
  672. app->variable_item_list_config = variable_item_list_alloc();
  673. app->view_config_e = view_alloc();
  674. view_set_context(app->view_config_e, app);
  675. view_set_previous_callback(app->view_config_e,
  676. key_copier_navigation_submenu_callback);
  677. view_set_enter_callback(app->view_config_e, key_copier_config_enter_callback);
  678. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_e,
  679. app->view_config_e);
  680. View *view_buffer = view_alloc();
  681. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_i,
  682. view_buffer);
  683. app->view_save = view_alloc();
  684. view_set_context(app->view_save, app);
  685. view_set_enter_callback(app->view_save, key_copier_view_save_callback);
  686. view_set_previous_callback(app->view_save,
  687. key_copier_navigation_submenu_callback);
  688. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewSave,
  689. app->view_save);
  690. app->view_load = view_alloc();
  691. view_set_context(app->view_load, app);
  692. view_set_enter_callback(app->view_load, key_copier_view_load_callback);
  693. view_set_previous_callback(app->view_load,
  694. key_copier_navigation_submenu_callback);
  695. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewLoad,
  696. app->view_load);
  697. app->widget_about = widget_alloc();
  698. widget_add_text_scroll_element(
  699. app->widget_about, 0, 0, 128, 64,
  700. "Key Maker App 1.0\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place "
  701. "it on top of the screen.\n\n2. Use the contour to align your key.\n\n3. "
  702. "Adjust each pin's depth until they match. It's easier if you look with "
  703. "one eye closed.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial "
  704. "thanks to Derek Jamison's Skeleton App Template.");
  705. view_set_previous_callback(widget_get_view(app->widget_about),
  706. key_copier_navigation_submenu_callback);
  707. view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewAbout,
  708. widget_get_view(app->widget_about));
  709. app->notifications = furi_record_open(RECORD_NOTIFICATION);
  710. #ifdef BACKLIGHT_ON
  711. notification_message(app->notifications,
  712. &sequence_display_backlight_enforce_on);
  713. #endif
  714. return app;
  715. }
  716. static void key_copier_app_free(KeyCopierApp *app) {
  717. #ifdef BACKLIGHT_ON
  718. notification_message(app->notifications,
  719. &sequence_display_backlight_enforce_auto);
  720. #endif
  721. furi_record_close(RECORD_NOTIFICATION);
  722. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewTextInput);
  723. text_input_free(app->text_input);
  724. free(app->temp_buffer);
  725. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewAbout);
  726. widget_free(app->widget_about);
  727. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewMeasure);
  728. with_view_model(
  729. app->view_measure, KeyCopierModel * model,
  730. {
  731. if (model->depth != NULL) {
  732. free(model->depth);
  733. }
  734. },
  735. false);
  736. view_free(app->view_measure);
  737. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewConfigure_e);
  738. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewConfigure_i);
  739. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewSave);
  740. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewLoad);
  741. variable_item_list_free(app->variable_item_list_config);
  742. view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewSubmenu);
  743. submenu_free(app->submenu);
  744. view_dispatcher_free(app->view_dispatcher);
  745. furi_record_close(RECORD_GUI);
  746. free(app);
  747. }
  748. int32_t main_key_copier_app(void *_p) {
  749. UNUSED(_p);
  750. KeyCopierApp *app = key_copier_app_alloc();
  751. view_dispatcher_run(app->view_dispatcher);
  752. key_copier_app_free(app);
  753. return 0;
  754. }