analog_clock.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #include <math.h>
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <gui/gui.h>
  5. #include <input/input.h>
  6. #include <locale/locale.h>
  7. #define PI 3.14
  8. typedef struct {
  9. uint8_t x;
  10. uint8_t y;
  11. } Vector2;
  12. typedef enum {
  13. EventTypeTick,
  14. EventTypeInput,
  15. } EventType;
  16. typedef struct {
  17. EventType type;
  18. InputEvent input;
  19. } AppEvent;
  20. typedef struct {
  21. FuriMutex* mutex;
  22. DateTime date_time;
  23. } ClockApp;
  24. static Vector2 angle_to_vector2(float angle_in_degrees, uint8_t distance, Vector2 center) {
  25. float radians = (angle_in_degrees - 90) * (PI / 180);
  26. Vector2 vec = {
  27. .x = center.x + cos(radians) * distance,
  28. .y = center.y + sin(radians) * distance,
  29. };
  30. return vec;
  31. }
  32. static void analog_clock_app_draw_callback(Canvas* canvas, void* context) {
  33. furi_assert(context);
  34. ClockApp* app = context;
  35. furi_mutex_acquire(app->mutex, FuriWaitForever);
  36. uint8_t width = canvas_width(canvas);
  37. uint8_t height = canvas_height(canvas);
  38. Vector2 clock_center = {
  39. .x = 28 + width / 2,
  40. .y = height / 2,
  41. };
  42. uint8_t radius = MIN(width, height) / 2 - 2;
  43. canvas_draw_circle(canvas, clock_center.x, clock_center.y, radius);
  44. FuriString* str = furi_string_alloc();
  45. for(uint8_t i = 3; i <= 12; i += 3) {
  46. Vector2 pos = angle_to_vector2(360 / 12 * i, radius - 4, clock_center);
  47. furi_string_printf(str, "%i", i);
  48. canvas_draw_str_aligned(
  49. canvas, pos.x, pos.y, AlignCenter, AlignCenter, furi_string_get_cstr(str));
  50. }
  51. Vector2 hour_vec =
  52. angle_to_vector2(((app->date_time.hour % 12) / 12.f * 360.f), radius - 8, clock_center);
  53. canvas_draw_line(canvas, clock_center.x, clock_center.y, hour_vec.x, hour_vec.y);
  54. Vector2 minute_vec =
  55. angle_to_vector2((app->date_time.minute / 60.f * 360.f), radius - 4, clock_center);
  56. canvas_draw_line(canvas, clock_center.x, clock_center.y, minute_vec.x, minute_vec.y);
  57. Vector2 second_vec =
  58. angle_to_vector2((app->date_time.second / 60.f * 360.f), radius - 2, clock_center);
  59. canvas_draw_line(canvas, clock_center.x, clock_center.y, second_vec.x, second_vec.y);
  60. canvas_set_font(canvas, FontSecondary);
  61. locale_format_date(str, &app->date_time, locale_get_date_format(), ".");
  62. uint16_t date_str_width = canvas_string_width(canvas, furi_string_get_cstr(str));
  63. canvas_draw_frame(canvas, 0, 51, date_str_width + 6, 13);
  64. canvas_draw_str(canvas, 3, 61, furi_string_get_cstr(str));
  65. furi_string_free(str);
  66. furi_mutex_release(app->mutex);
  67. }
  68. static void analog_clock_app_input_callback(InputEvent* event, void* context) {
  69. furi_assert(context);
  70. FuriMessageQueue* event_queue = context;
  71. AppEvent app_event = {.type = EventTypeInput, .input = *event};
  72. furi_message_queue_put(event_queue, &app_event, FuriWaitForever);
  73. }
  74. static void analog_clock_app_tick(void* context) {
  75. furi_assert(context);
  76. FuriMessageQueue* event_queue = context;
  77. AppEvent app_event = {.type = EventTypeTick};
  78. furi_message_queue_put(event_queue, &app_event, 0);
  79. }
  80. int32_t analog_clock_app(void* p) {
  81. UNUSED(p);
  82. ClockApp* app = malloc(sizeof(ClockApp));
  83. furi_hal_rtc_get_datetime(&app->date_time);
  84. app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  85. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  86. ViewPort* view_port = view_port_alloc();
  87. view_port_draw_callback_set(view_port, analog_clock_app_draw_callback, app);
  88. view_port_input_callback_set(view_port, analog_clock_app_input_callback, event_queue);
  89. Gui* gui = furi_record_open(RECORD_GUI);
  90. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  91. FuriTimer* timer = furi_timer_alloc(analog_clock_app_tick, FuriTimerTypePeriodic, event_queue);
  92. furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
  93. AppEvent event;
  94. for(bool running = true; running;) {
  95. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  96. if(event_status == FuriStatusOk) {
  97. if(event.type == EventTypeInput) {
  98. if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
  99. running = false;
  100. }
  101. } else if(event.type == EventTypeTick) {
  102. furi_mutex_acquire(app->mutex, FuriWaitForever);
  103. furi_hal_rtc_get_datetime(&app->date_time);
  104. furi_mutex_release(app->mutex);
  105. view_port_update(view_port);
  106. }
  107. }
  108. }
  109. furi_timer_free(timer);
  110. gui_remove_view_port(gui, view_port);
  111. furi_record_close(RECORD_GUI);
  112. view_port_free(view_port);
  113. furi_message_queue_free(event_queue);
  114. furi_mutex_free(app->mutex);
  115. free(app);
  116. return 0;
  117. }