Browse Source

recovery: Fix redraws, flickering, and animation

Rewrite screen drawing code to behave as follows:

 * draw_screen_locked has exactly two code paths: menu and non-menu.
    Non-menu code path draws background, foreground, and text.
 * Define screen background as the stage marker.
 * Define screen foreground as either the animation and progress bar
   or the icon and text.
 * Restore redraw optimizations in update_progress_locked.
 * Get rid of spurious screen updates.  The screen is only updated
    in StartMenu, SelectMenu, Redraw, and the progress thread.
 * Various changes and fixes to support the above.

Additionally:

 * Show error text on screen after failed install.
 * Rewrite graphics test code to better exercise the above.

Change-Id: I598bb11d5eef28275a6501cd44d514afe9939885
Tom Marshall 3 years ago
parent
commit
41547c3131
7 changed files with 119 additions and 97 deletions
  1. 13
    15
      recovery.cpp
  2. 98
    77
      screen_ui.cpp
  3. 2
    2
      screen_ui.h
  4. 1
    0
      stub_ui.h
  5. 1
    1
      ui.cpp
  6. 2
    0
      ui.h
  7. 2
    2
      wear_ui.cpp

+ 13
- 15
recovery.cpp View File

@@ -1166,36 +1166,31 @@ static int choose_recovery_file(Device* device) {
1166 1166
 }
1167 1167
 
1168 1168
 static void run_graphics_test() {
1169
-  // Switch to graphics screen.
1170
-  ui->ShowText(false);
1171
-
1172
-  ui->SetProgressType(RecoveryUI::INDETERMINATE);
1173
-  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1174
-  sleep(1);
1175
-
1176 1169
   ui->SetBackground(RecoveryUI::ERROR);
1170
+  ui->Redraw();
1177 1171
   sleep(1);
1178 1172
 
1179
-  ui->SetBackground(RecoveryUI::NO_COMMAND);
1173
+  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1174
+  ui->Redraw();
1180 1175
   sleep(1);
1181 1176
 
1182 1177
   ui->SetBackground(RecoveryUI::ERASING);
1178
+  ui->Redraw();
1183 1179
   sleep(1);
1184 1180
 
1185
-  // Calling SetBackground() after SetStage() to trigger a redraw.
1186 1181
   ui->SetStage(1, 3);
1187
-  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1182
+  ui->Redraw();
1188 1183
   sleep(1);
1189 1184
   ui->SetStage(2, 3);
1190
-  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1185
+  ui->Redraw();
1191 1186
   sleep(1);
1192 1187
   ui->SetStage(3, 3);
1193
-  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1188
+  ui->Redraw();
1194 1189
   sleep(1);
1195 1190
 
1196 1191
   ui->SetStage(-1, -1);
1197
-  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1198 1192
 
1193
+  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
1199 1194
   ui->SetProgressType(RecoveryUI::DETERMINATE);
1200 1195
   ui->ShowProgress(1.0, 10.0);
1201 1196
   float fraction = 0.0;
@@ -1204,8 +1199,6 @@ static void run_graphics_test() {
1204 1199
     ui->SetProgress(fraction);
1205 1200
     usleep(100000);
1206 1201
   }
1207
-
1208
-  ui->ShowText(true);
1209 1202
 }
1210 1203
 
1211 1204
 static int apply_from_storage(Device* device, VolumeInfo& vi, bool* wipe_cache) {
@@ -1383,9 +1376,13 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
1383 1376
             }
1384 1377
 
1385 1378
             if (status != INSTALL_SUCCESS) {
1379
+              ui->SetProgressType(RecoveryUI::EMPTY);
1386 1380
               ui->SetBackground(RecoveryUI::ERROR);
1387 1381
               ui->Print("Installation aborted.\n");
1382
+              ui->Redraw();
1388 1383
               copy_logs();
1384
+              ui->FlushKeys();
1385
+              ui->WaitInputEvent();
1389 1386
             } else if (!ui->IsTextVisible()) {
1390 1387
               return Device::NO_ACTION;  // reboot if logs aren't visible
1391 1388
             } else {
@@ -1962,6 +1959,7 @@ int main(int argc, char **argv) {
1962 1959
   if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
1963 1960
     ui->SetBackground(RecoveryUI::ERROR);
1964 1961
     if (!ui->IsTextVisible()) {
1962
+      ui->Redraw();
1965 1963
       sleep(5);
1966 1964
     }
1967 1965
   }

+ 98
- 77
screen_ui.cpp View File

@@ -159,7 +159,7 @@ GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
159 159
   if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
160 160
     return intro_done ? loopFrames[current_frame] : introFrames[current_frame];
161 161
   }
162
-  return error_icon;
162
+  return nullptr;
163 163
 }
164 164
 
165 165
 GRSurface* ScreenRecoveryUI::GetCurrentText() const {
@@ -222,80 +222,94 @@ int ScreenRecoveryUI::GetProgressBaseline() const {
222 222
   return ScreenHeight() - bottom_gap - gr_get_height(progressBarFill);
223 223
 }
224 224
 
225
-// Clear the screen and draw the currently selected background icon (if any).
226
-// Should only be called with updateMutex locked.
225
+// Draw the currently selected stage icon(s) (if any).
226
+// Does not flip pages. Should only be called with updateMutex locked.
227 227
 void ScreenRecoveryUI::draw_background_locked() {
228
-  pagesIdentical = false;
229
-  gr_color(0, 0, 0, 255);
230
-  gr_clear();
231
-  if (currentIcon != NONE) {
228
+  if (currentIcon != NONE && currentIcon != NO_COMMAND) {
232 229
     if (max_stage != -1) {
233 230
       int stage_height = gr_get_height(stageMarkerEmpty);
234 231
       int stage_width = gr_get_width(stageMarkerEmpty);
235
-      int x = kMarginWidth + (ScreenWidth() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
236
-      int y = kMarginHeight + ScreenHeight() - stage_height;
232
+      int stage_x = kMarginWidth + (ScreenWidth() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
233
+      int stage_y = kMarginHeight;
237 234
       for (int i = 0; i < max_stage; ++i) {
238 235
         GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty;
239
-        DrawSurface(stage_surface, 0, 0, stage_width, stage_height, x, y);
240
-        x += stage_width;
236
+        DrawSurface(stage_surface, 0, 0, stage_width, stage_height, stage_x, stage_y);
237
+        stage_x += stage_width;
241 238
       }
242 239
     }
243
-
244
-    GRSurface* text_surface = GetCurrentText();
245
-    int text_x = kMarginWidth + (ScreenWidth() - gr_get_width(text_surface)) / 2;
246
-    int text_y = kMarginHeight + GetTextBaseline();
247
-    gr_color(255, 255, 255, 255);
248
-    DrawTextIcon(text_x, text_y, text_surface);
249 240
   }
250 241
 }
251 242
 
252
-// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
253
-// called with updateMutex locked.
254
-void ScreenRecoveryUI::draw_foreground_locked() {
255
-  if (currentIcon != NONE && currentIcon != NO_COMMAND) {
256
-    gr_color(0, 0, 0, 255);
257
-    gr_clear();
258
-    GRSurface* frame = GetCurrentFrame();
243
+// Draws either the animation and progress bar or the currently
244
+// selected icon and text on the screen.
245
+// Does not flip pages. Should only be called with updateMutex locked.
246
+void ScreenRecoveryUI::draw_foreground_locked(int& y) {
247
+  GRSurface* frame = GetCurrentFrame();
248
+  if (frame) {
249
+    // Show animation frame and progress bar
259 250
     int frame_width = gr_get_width(frame);
260 251
     int frame_height = gr_get_height(frame);
261 252
     int frame_x = kMarginWidth + (ScreenWidth() - frame_width) / 2;
262 253
     int frame_y = kMarginHeight + GetAnimationBaseline();
263 254
     DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
264
-  }
255
+    y = frame_y + frame_height;
265 256
 
266
-  if (progressBarType != EMPTY) {
267
-    int width = gr_get_width(progressBarEmpty);
268
-    int height = gr_get_height(progressBarEmpty);
257
+    if (progressBarType != EMPTY) {
258
+      int width = gr_get_width(progressBarEmpty);
259
+      int height = gr_get_height(progressBarEmpty);
269 260
 
270
-    int progress_x = kMarginWidth + (ScreenWidth() - width) / 2;
271
-    int progress_y = kMarginHeight + GetProgressBaseline();
261
+      int progress_x = kMarginWidth + (ScreenWidth() - width) / 2;
262
+      int progress_y = kMarginHeight + GetProgressBaseline();
272 263
 
273
-    // Erase behind the progress bar (in case this was a progress-only update)
274
-    gr_color(0, 0, 0, 255);
275
-    DrawFill(progress_x, progress_y, width, height);
264
+      // Erase behind the progress bar (in case this was a progress-only update)
265
+      gr_color(0, 0, 0, 255);
266
+      DrawFill(progress_x, progress_y, width, height);
276 267
 
277
-    if (progressBarType == DETERMINATE) {
278
-      float p = progressScopeStart + progress * progressScopeSize;
279
-      int pos = static_cast<int>(p * width);
268
+      if (progressBarType == DETERMINATE) {
269
+        float p = progressScopeStart + progress * progressScopeSize;
270
+        int pos = static_cast<int>(p * width);
280 271
 
281
-      if (rtl_locale_) {
282
-        // Fill the progress bar from right to left.
283
-        if (pos > 0) {
284
-          DrawSurface(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos,
285
-                      progress_y);
286
-        }
287
-        if (pos < width - 1) {
288
-          DrawSurface(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y);
289
-        }
290
-      } else {
291
-        // Fill the progress bar from left to right.
292
-        if (pos > 0) {
293
-          DrawSurface(progressBarFill, 0, 0, pos, height, progress_x, progress_y);
294
-        }
295
-        if (pos < width - 1) {
296
-          DrawSurface(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y);
272
+        if (rtl_locale_) {
273
+          // Fill the progress bar from right to left.
274
+          if (pos > 0) {
275
+            DrawSurface(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos,
276
+                        progress_y);
277
+          }
278
+          if (pos < width - 1) {
279
+            DrawSurface(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y);
280
+          }
281
+        } else {
282
+          // Fill the progress bar from left to right.
283
+          if (pos > 0) {
284
+            DrawSurface(progressBarFill, 0, 0, pos, height, progress_x, progress_y);
285
+          }
286
+          if (pos < width - 1) {
287
+            DrawSurface(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y);
288
+          }
297 289
         }
298 290
       }
291
+      y = progress_y + height;
292
+    }
293
+  }
294
+  else {
295
+    // Show icon and text
296
+    if (currentIcon != NONE && currentIcon != NO_COMMAND) {
297
+      GRSurface* icon_surface = error_icon;
298
+      int icon_width = gr_get_width(icon_surface);
299
+      int icon_height = gr_get_height(icon_surface);
300
+      int icon_x = kMarginWidth + (gr_fb_width() - icon_width) / 2;
301
+      int icon_y = kMarginHeight + GetAnimationBaseline();
302
+      gr_blit(icon_surface, 0, 0, icon_width, icon_height, icon_x, icon_y);
303
+
304
+      GRSurface* text_surface = GetCurrentText();
305
+      int text_width = gr_get_width(text_surface);
306
+      int text_height = gr_get_height(text_surface);
307
+      int text_x = kMarginWidth + (gr_fb_width() - text_width) / 2;
308
+      int text_y = kMarginHeight + GetTextBaseline();
309
+      gr_color(255, 255, 255, 255);
310
+      gr_texticon(text_x, text_y, text_surface);
311
+
312
+      y = text_y + text_height;
299 313
     }
300 314
   }
301 315
 }
@@ -691,12 +705,7 @@ void ScreenRecoveryUI::draw_grid_menu_locked(int& y) {
691 705
 // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
692 706
 // locked.
693 707
 void ScreenRecoveryUI::draw_screen_locked() {
694
-  if (!show_text) {
695
-    draw_background_locked();
696
-    draw_foreground_locked();
697
-    return;
698
-  }
699
-
708
+  pagesIdentical = false;
700 709
   gr_color(0, 0, 0, 255);
701 710
   gr_clear();
702 711
 
@@ -726,16 +735,21 @@ void ScreenRecoveryUI::draw_screen_locked() {
726 735
     }
727 736
   }
728 737
   else {
729
-    // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
730
-    // we've displayed the entire text buffer.
731
-    SetColor(LOG);
732
-    int row = (text_rows_ - 1) % text_rows_;
733
-    size_t count = 0;
734
-    for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_;
735
-         ty -= char_height_, ++count) {
736
-      DrawTextLine(kMarginWidth, ty, text_[row], false);
737
-      --row;
738
-      if (row < 0) row = text_rows_ - 1;
738
+    draw_background_locked();
739
+    draw_foreground_locked(y);
740
+
741
+    if (show_text) {
742
+      // Display from the bottom up, until we hit the top of the screen, the
743
+      // bottom of the foreground, or we've displayed the entire text buffer.
744
+      SetColor(LOG);
745
+      int row = (text_rows_ - 1) % text_rows_;
746
+      size_t count = 0;
747
+      for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_;
748
+           ty -= char_height_, ++count) {
749
+        DrawTextLine(kMarginWidth, ty, text_[row], false);
750
+        --row;
751
+        if (row < 0) row = text_rows_ - 1;
752
+      }
739 753
     }
740 754
   }
741 755
 }
@@ -750,7 +764,14 @@ void ScreenRecoveryUI::update_screen_locked() {
750 764
 // Updates only the progress bar, if possible, otherwise redraws the screen.
751 765
 // Should only be called with updateMutex locked.
752 766
 void ScreenRecoveryUI::update_progress_locked() {
753
-  draw_foreground_locked();
767
+  if (!pagesIdentical) {
768
+    draw_screen_locked();
769
+    pagesIdentical = true;
770
+  }
771
+  else {
772
+    int y = kMarginHeight;
773
+    draw_foreground_locked(y);
774
+  }
754 775
   gr_flip();
755 776
 }
756 777
 
@@ -846,7 +867,6 @@ void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
846 867
   } else {
847 868
     LoadLocalizedBitmap("installing_text", &installing_text);
848 869
   }
849
-  Redraw();
850 870
 }
851 871
 
852 872
 bool ScreenRecoveryUI::InitTextParams() {
@@ -953,7 +973,6 @@ void ScreenRecoveryUI::SetBackground(Icon icon) {
953 973
 
954 974
   if (icon != currentIcon) {
955 975
     currentIcon = icon;
956
-    update_screen_locked();
957 976
   }
958 977
 
959 978
   pthread_mutex_unlock(&updateMutex);
@@ -963,14 +982,14 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) {
963 982
   pthread_mutex_lock(&updateMutex);
964 983
   if (progressBarType != type) {
965 984
     progressBarType = type;
985
+    progressScopeStart = 0;
986
+    progressScopeSize = 0;
987
+    progress = 0;
966 988
     if (progressBarType != EMPTY) {
989
+      update_screen_locked();
967 990
       pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
968 991
     }
969 992
   }
970
-  progressScopeStart = 0;
971
-  progressScopeSize = 0;
972
-  progress = 0;
973
-  update_progress_locked();
974 993
   pthread_mutex_unlock(&updateMutex);
975 994
 }
976 995
 
@@ -1028,7 +1047,6 @@ void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap)
1028 1047
       if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
1029 1048
     }
1030 1049
     text_[text_row_][text_col_] = '\0';
1031
-    update_screen_locked();
1032 1050
   }
1033 1051
   pthread_mutex_unlock(&updateMutex);
1034 1052
 }
@@ -1131,6 +1149,9 @@ int ScreenRecoveryUI::ShowFile(const char* filename) {
1131 1149
     return -1;
1132 1150
   }
1133 1151
 
1152
+  Icon oldIcon = currentIcon;
1153
+  currentIcon = NONE;
1154
+
1134 1155
   char** old_text = text_;
1135 1156
   size_t old_text_col = text_col_;
1136 1157
   size_t old_text_row = text_row_;
@@ -1145,6 +1166,7 @@ int ScreenRecoveryUI::ShowFile(const char* filename) {
1145 1166
   text_ = old_text;
1146 1167
   text_col_ = old_text_col;
1147 1168
   text_row_ = old_text_row;
1169
+  currentIcon = oldIcon;
1148 1170
   return key;
1149 1171
 }
1150 1172
 
@@ -1273,7 +1295,6 @@ void ScreenRecoveryUI::ShowText(bool visible) {
1273 1295
   pthread_mutex_lock(&updateMutex);
1274 1296
   show_text = visible;
1275 1297
   if (show_text) show_text_ever = true;
1276
-  update_screen_locked();
1277 1298
   pthread_mutex_unlock(&updateMutex);
1278 1299
 }
1279 1300
 

+ 2
- 2
screen_ui.h View File

@@ -113,7 +113,7 @@ class ScreenRecoveryUI : public RecoveryUI {
113 113
 
114 114
   void KeyLongPress(int) override;
115 115
 
116
-  void Redraw();
116
+  void Redraw() override;
117 117
 
118 118
   void SetColor(UIElement e) const;
119 119
 
@@ -136,7 +136,7 @@ class ScreenRecoveryUI : public RecoveryUI {
136 136
   virtual bool InitTextParams();
137 137
 
138 138
   virtual void draw_background_locked();
139
-  virtual void draw_foreground_locked();
139
+  virtual void draw_foreground_locked(int& y);
140 140
   virtual void draw_statusbar_locked();
141 141
   virtual void draw_header_locked(int& y);
142 142
   virtual void draw_text_menu_locked(int& y);

+ 1
- 0
stub_ui.h View File

@@ -52,6 +52,7 @@ class StubRecoveryUI : public RecoveryUI {
52 52
   }
53 53
   void PrintOnScreenOnly(const char* /* fmt */, ...) override {}
54 54
   int ShowFile(const char* /* filename */) override { return -1; }
55
+  void Redraw() override {}
55 56
 
56 57
   // menu display
57 58
   void StartMenu(bool /* is_main */,

+ 1
- 1
ui.cpp View File

@@ -532,7 +532,6 @@ RecoveryUI::InputEvent RecoveryUI::WaitInputEvent() {
532 532
 
533 533
     int rc = 0;
534 534
     while (event_queue_len == 0 && rc != ETIMEDOUT) {
535
-      Print(""); // Force screen update
536 535
       struct timespec key_timeout;
537 536
       gettimeofday(&now, nullptr);
538 537
       key_timeout.tv_sec = now.tv_sec + 1;
@@ -546,6 +545,7 @@ RecoveryUI::InputEvent RecoveryUI::WaitInputEvent() {
546 545
         }
547 546
         if (key_timeout.tv_sec <= timeout.tv_sec) {
548 547
           rc = 0;
548
+          ui->Redraw();
549 549
         }
550 550
       }
551 551
     }

+ 2
- 0
ui.h View File

@@ -152,6 +152,8 @@ class RecoveryUI {
152 152
 
153 153
   virtual int ShowFile(const char* filename) = 0;
154 154
 
155
+  virtual void Redraw() = 0;
156
+
155 157
   // --- event handling ---
156 158
 
157 159
   enum event_type_t { EVENT_TYPE_NONE, EVENT_TYPE_KEY, EVENT_TYPE_TOUCH };

+ 2
- 2
wear_ui.cpp View File

@@ -71,15 +71,15 @@ static const char* SWIPE_HELP[] = {
71 71
 // TODO merge drawing routines with screen_ui
72 72
 void WearRecoveryUI::draw_screen_locked() {
73 73
   char cur_selection_str[50];
74
+  int y = kMarginHeight;
74 75
 
75 76
   draw_background_locked();
76 77
   if (!show_text) {
77
-    draw_foreground_locked();
78
+    draw_foreground_locked(y);
78 79
   } else {
79 80
     SetColor(TEXT_FILL);
80 81
     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
81 82
 
82
-    int y = kMarginHeight;
83 83
     int x = kMarginWidth;
84 84
     if (show_menu) {
85 85
       std::string recovery_fingerprint =