1+ #include < array>
12#include < chrono>
2- #include < deque>
33#include < stdlib.h>
44
55#include " byte90.hpp"
66
77using namespace std ::chrono_literals;
88
9- static constexpr size_t MAX_CIRCLES = 100 ;
10- static std::deque<lv_obj_t *> circles;
9+ static constexpr size_t MAX_CIRCLES = 10 ;
10+ struct Circle {
11+ int x{0 };
12+ int y{0 };
13+ int radius{0 };
14+ bool visible{false };
15+ };
16+ static std::array<Circle, MAX_CIRCLES > circles;
17+ static size_t next_circle_index = 0 ;
18+ static size_t visible_circle_count = 0 ;
19+ static lv_obj_t *circle_layer = nullptr ;
1120
1221static std::recursive_mutex lvgl_mutex;
22+ static bool initialize_circle_layer (int width, int height);
23+ static void draw_circle_layer (lv_event_t *event);
24+ static void invalidate_circle_area (const Circle &circle);
1325static void draw_circle (int x0, int y0, int radius);
1426static void clear_circles ();
1527
@@ -39,6 +51,20 @@ extern "C" void app_main(void) {
3951 }
4052 // initialize the button, which we'll use to cycle the rotation of the display
4153 logger.info (" Initializing the button" );
54+ lv_obj_t *bg = nullptr ;
55+ lv_obj_t *label = nullptr ;
56+ static auto update_layout = [&]() {
57+ int width = byte90.rotated_display_width ();
58+ int height = byte90.rotated_display_height ();
59+ lv_obj_set_size (bg, width, height);
60+ lv_obj_align (label, LV_ALIGN_CENTER , 0 , 0 );
61+ if (circle_layer) {
62+ lv_obj_set_size (circle_layer, width, height);
63+ lv_obj_align (circle_layer, LV_ALIGN_CENTER , 0 , 0 );
64+ lv_obj_move_foreground (circle_layer);
65+ lv_obj_invalidate (circle_layer);
66+ }
67+ };
4268 auto on_button_pressed = [&](const auto &event) {
4369 if (event.active ) {
4470 // increment the brightness by 10%, looping back to 0% after 100%
@@ -50,22 +76,30 @@ extern "C" void app_main(void) {
5076 std::lock_guard<std::recursive_mutex> lock (lvgl_mutex);
5177 static auto rotation = LV_DISPLAY_ROTATION_0 ;
5278 rotation = static_cast <lv_display_rotation_t >((static_cast <int >(rotation) + 1 ) % 4 );
53- lv_display_t *disp = lv_disp_get_default ();
79+ lv_display_t *disp = lv_display_get_default ();
5480 lv_disp_set_rotation (disp, rotation);
81+ update_layout ();
5582 }
5683 };
5784 byte90.initialize_button (on_button_pressed);
5885
5986 // set the background color to black
60- lv_obj_t * bg = lv_obj_create (lv_screen_active ());
61- lv_obj_set_size (bg, byte90.lcd_width (), byte90.lcd_height ());
87+ bg = lv_obj_create (lv_screen_active ());
88+ lv_obj_set_size (bg, byte90.rotated_display_width (), byte90.rotated_display_height ());
6289 lv_obj_set_style_bg_color (bg, lv_color_make (0 , 0 , 0 ), 0 );
90+ if (!initialize_circle_layer (byte90.rotated_display_width (), byte90.rotated_display_height ())) {
91+ logger.error (" Failed to initialize circle layer!" );
92+ return ;
93+ }
6394
6495 // add text in the center of the screen
65- lv_obj_t * label = lv_label_create (lv_screen_active ());
96+ label = lv_label_create (lv_screen_active ());
6697 lv_label_set_text (label, " Drawing circles\n to the screen." );
6798 lv_obj_align (label, LV_ALIGN_CENTER , 0 , 0 );
6899 lv_obj_set_style_text_align (label, LV_TEXT_ALIGN_CENTER , 0 );
100+ update_layout ();
101+
102+ lv_obj_move_foreground (circle_layer);
69103
70104 // start a simple thread to do the lv_task_handler every 16ms
71105 espp::Task lv_task ({.callback = [](std::mutex &m, std::condition_variable &cv) -> bool {
@@ -88,17 +122,16 @@ extern "C" void app_main(void) {
88122 while (true ) {
89123 auto start = esp_timer_get_time ();
90124 // if there are 10 circles on the screen, clear them
91- static constexpr int max_circles = 10 ;
92- if (circles.size () >= max_circles) {
125+ if (visible_circle_count >= MAX_CIRCLES ) {
93126 // lock the lvgl mutex
94127 std::lock_guard<std::recursive_mutex> lock (lvgl_mutex);
95128 clear_circles ();
96129 } else {
97130 // draw a circle of circles on the screen (just draw the next circle)
98- static constexpr int middle_x = byte90.lcd_width () / 2 ;
99- static constexpr int middle_y = byte90.lcd_height () / 2 ;
131+ int middle_x = byte90.rotated_display_width () / 2 ;
132+ int middle_y = byte90.rotated_display_height () / 2 ;
100133 static constexpr int radius = 30 ;
101- float angle = circles. size () * 2 .0f * M_PI / max_circles ;
134+ float angle = visible_circle_count * 2 .0f * M_PI / MAX_CIRCLES ;
102135 int x = middle_x + radius * cos (angle);
103136 int y = middle_y + radius * sin (angle);
104137 // lock the lvgl mutex
@@ -112,28 +145,100 @@ extern "C" void app_main(void) {
112145 // ! [byte90 example]
113146}
114147
148+ static bool initialize_circle_layer (int width, int height) {
149+ if (circle_layer) {
150+ return true ;
151+ }
152+ circle_layer = lv_obj_create (lv_screen_active ());
153+ if (!circle_layer) {
154+ return false ;
155+ }
156+ lv_obj_remove_style_all (circle_layer);
157+ lv_obj_set_size (circle_layer, width, height);
158+ lv_obj_align (circle_layer, LV_ALIGN_CENTER , 0 , 0 );
159+ lv_obj_clear_flag (circle_layer, LV_OBJ_FLAG_CLICKABLE );
160+ lv_obj_clear_flag (circle_layer, LV_OBJ_FLAG_SCROLLABLE );
161+ lv_obj_set_style_bg_opa (circle_layer, LV_OPA_TRANSP , 0 );
162+ lv_obj_set_style_border_width (circle_layer, 0 , 0 );
163+ lv_obj_set_style_outline_width (circle_layer, 0 , 0 );
164+ lv_obj_set_style_shadow_width (circle_layer, 0 , 0 );
165+ lv_obj_add_event_cb (circle_layer, draw_circle_layer, LV_EVENT_DRAW_MAIN , nullptr );
166+ return true ;
167+ }
168+
169+ static void draw_circle_layer (lv_event_t *event) {
170+ if (visible_circle_count == 0 ) {
171+ return ;
172+ }
173+ auto *obj = static_cast <lv_obj_t *>(lv_event_get_current_target (event));
174+ auto *layer = lv_event_get_layer (event);
175+ lv_area_t obj_coords;
176+ lv_obj_get_coords (obj, &obj_coords);
177+
178+ lv_draw_rect_dsc_t rect_dsc;
179+ lv_draw_rect_dsc_init (&rect_dsc);
180+ rect_dsc.base .layer = layer;
181+ rect_dsc.radius = LV_RADIUS_CIRCLE ;
182+ rect_dsc.bg_opa = LV_OPA_70 ;
183+ rect_dsc.bg_color = lv_color_make (0 , 255 , 255 );
184+ rect_dsc.border_width = 0 ;
185+ rect_dsc.outline_width = 0 ;
186+ rect_dsc.shadow_width = 0 ;
187+
188+ for (const auto &circle : circles) {
189+ if (!circle.visible ) {
190+ continue ;
191+ }
192+ lv_area_t coords = {
193+ .x1 = static_cast <lv_coord_t >(obj_coords.x1 + circle.x - circle.radius ),
194+ .y1 = static_cast <lv_coord_t >(obj_coords.y1 + circle.y - circle.radius ),
195+ .x2 = static_cast <lv_coord_t >(obj_coords.x1 + circle.x + circle.radius - 1 ),
196+ .y2 = static_cast <lv_coord_t >(obj_coords.y1 + circle.y + circle.radius - 1 ),
197+ };
198+ lv_draw_rect (layer, &rect_dsc, &coords);
199+ }
200+ }
201+
202+ static void invalidate_circle_area (const Circle &circle) {
203+ if (!circle_layer || circle.radius <= 0 ) {
204+ return ;
205+ }
206+
207+ lv_area_t obj_coords;
208+ lv_obj_get_coords (circle_layer, &obj_coords);
209+ lv_area_t coords = {
210+ .x1 = static_cast <lv_coord_t >(obj_coords.x1 + circle.x - circle.radius ),
211+ .y1 = static_cast <lv_coord_t >(obj_coords.y1 + circle.y - circle.radius ),
212+ .x2 = static_cast <lv_coord_t >(obj_coords.x1 + circle.x + circle.radius - 1 ),
213+ .y2 = static_cast <lv_coord_t >(obj_coords.y1 + circle.y + circle.radius - 1 ),
214+ };
215+ lv_obj_invalidate_area (circle_layer, &coords);
216+ }
217+
115218static void draw_circle (int x0, int y0, int radius) {
116- // if we have too many circles, remove the oldest one
117- if (circles.size () >= MAX_CIRCLES ) {
118- lv_obj_delete (circles.front ());
119- circles.pop_front ();
219+ if (!circle_layer) {
220+ return ;
120221 }
121- lv_obj_t *my_Cir = lv_obj_create (lv_screen_active ());
122- lv_obj_set_scrollbar_mode (my_Cir, LV_SCROLLBAR_MODE_OFF );
123- lv_obj_set_size (my_Cir, radius * 2 , radius * 2 );
124- lv_obj_set_pos (my_Cir, x0 - radius, y0 - radius);
125- lv_obj_set_style_radius (my_Cir, LV_RADIUS_CIRCLE , 0 );
126- // ensure the circle ignores touch events (so things behind it can still be
127- // interacted with)
128- lv_obj_clear_flag (my_Cir, LV_OBJ_FLAG_CLICKABLE );
129- circles.push_back (my_Cir);
222+ lv_obj_move_foreground (circle_layer);
223+ Circle previous_circle = circles[next_circle_index];
224+ circles[next_circle_index] = {.x = x0, .y = y0, .radius = radius, .visible = true };
225+ next_circle_index = (next_circle_index + 1 ) % circles.size ();
226+ if (visible_circle_count < circles.size ()) {
227+ visible_circle_count++;
228+ }
229+ if (previous_circle.visible ) {
230+ invalidate_circle_area (previous_circle);
231+ }
232+ invalidate_circle_area (circles[(next_circle_index + circles.size () - 1 ) % circles.size ()]);
130233}
131234
132235static void clear_circles () {
133- // remove the circles from lvgl
134- for (auto circle : circles) {
135- lv_obj_delete (circle);
236+ for (auto &circle : circles) {
237+ if (circle.visible ) {
238+ invalidate_circle_area (circle);
239+ }
240+ circle.visible = false ;
136241 }
137- // clear the vector
138- circles. clear () ;
242+ next_circle_index = 0 ;
243+ visible_circle_count = 0 ;
139244}
0 commit comments