1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <fcntl.h> 18 #include <linux/fb.h> 19 #include <linux/input.h> 20 #include <pthread.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <sys/ioctl.h> 25 #include <sys/time.h> 26 #include <sys/types.h> 27 #include <time.h> 28 #include <unistd.h> 29 #include <linux/ioctl.h> 30 31 #include "common.h" 32 #include "device.h" 33 #include "ui.h" 34 #include "screen_ui.h" 35 36 /* CEA-861 specifies a maximum of 65 modes in an EDID */ 37 #define CEA_MODEDB_SIZE 65 38 static const char* HEADERS[] = { "Use hardware button to move cursor; long-press to select item.", 39 "", 40 NULL }; 41 42 // these strings are never actually displayed 43 static const char* ITEMS[] = {"reboot system now", 44 "apply update from ADB", 45 "wipe data/factory reset", 46 "wipe cache partition", 47 "view recovery logs", 48 NULL }; 49 50 #define kFBDevice "/dev/graphics/fb0" 51 #define FBIO_PSB_SET_RGBX _IOWR('F', 0x42, struct fb_var_screeninfo) 52 #define FBIO_PSB_SET_RMODE _IOWR('F', 0x43, struct fb_var_screeninfo) 53 54 struct led_rgb_vals { 55 uint8_t rgb[3]; 56 }; 57 58 // Return the current time as a double (including fractions of a second). 59 static double now() { 60 struct timeval tv; 61 gettimeofday(&tv, NULL); 62 return tv.tv_sec + tv.tv_usec / 1000000.0; 63 } 64 65 class FuguUI : public ScreenRecoveryUI { 66 public: 67 FuguUI() : 68 text_visible(false), 69 text_ever_visible(false), 70 background_mode(NONE), 71 up_keys(0), 72 next_key_pos(0), 73 pending_select(false), 74 long_press(false) { 75 pthread_mutex_init(&long_mu, NULL); 76 memset(last_keys, 0, kKeyBufferSize * sizeof(int)); 77 } 78 79 void Init() { 80 SetupDisplayMode(); 81 ScreenRecoveryUI::Init(); 82 } 83 84 void SetupDisplayMode() { 85 int fb_dev = open(kFBDevice, O_RDWR); 86 int res; 87 uint32_t i; 88 printf("opening fb %s\n", kFBDevice); 89 if (fb_dev < 0) { 90 fprintf(stderr, "FAIL: failed to open \"%s\" (errno = %d)\n", kFBDevice, errno); 91 return; 92 } 93 94 struct fb_var_screeninfo current_mode; 95 96 res = ioctl(fb_dev, FBIO_PSB_SET_RMODE, ¤t_mode); 97 if (res) { 98 fprintf(stderr, 99 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n", 100 errno); 101 return; 102 } 103 104 res = ioctl(fb_dev, FBIOGET_VSCREENINFO, ¤t_mode); 105 if (res) { 106 fprintf(stderr, "FAIL: unable to get mode, err %d\n", res); 107 return; 108 } 109 110 res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_POWERDOWN); 111 if (res) { 112 fprintf(stderr, "FAIL: unable to blank display, err %d\n", res); 113 return; 114 } 115 116 current_mode.bits_per_pixel = 32; 117 current_mode.red.offset = 0; 118 current_mode.red.length = 8; 119 current_mode.green.offset = 8; 120 current_mode.green.length = 8; 121 current_mode.blue.offset = 16; 122 current_mode.blue.length = 8; 123 124 res = ioctl(fb_dev, FBIOPUT_VSCREENINFO, ¤t_mode); 125 if (res) { 126 fprintf(stderr, "FAIL: unable to set mode, err %d\n", res); 127 return; 128 } 129 130 /* set our display controller for RGBX */ 131 res = ioctl(fb_dev, FBIO_PSB_SET_RGBX, ¤t_mode); 132 if (res) { 133 fprintf(stderr, 134 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n", 135 errno); 136 return; 137 } 138 139 res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_UNBLANK); 140 if (res) { 141 fprintf(stderr, "FAIL: unable to unblank display, err %d\n", res); 142 return; 143 } 144 } 145 146 void SetBackground(Icon icon) { 147 ScreenRecoveryUI::SetBackground(icon); 148 149 background_mode = icon; 150 } 151 152 void SetColor(UIElement e) { 153 switch (e) { 154 case HEADER: 155 gr_color(247, 0, 6, 255); 156 break; 157 case MENU: 158 gr_color(0, 106, 157, 255); 159 break; 160 case MENU_SEL_BG: 161 pthread_mutex_lock(&long_mu); 162 if (pending_select) { 163 gr_color(0, 156, 100, 255); 164 } else { 165 gr_color(0, 106, 157, 255); 166 } 167 pthread_mutex_unlock(&long_mu); 168 break; 169 case MENU_SEL_FG: 170 gr_color(255, 255, 255, 255); 171 break; 172 case LOG: 173 gr_color(249, 194, 0, 255); 174 break; 175 case TEXT_FILL: 176 gr_color(0, 0, 0, 160); 177 break; 178 default: 179 gr_color(255, 255, 255, 255); 180 break; 181 } 182 } 183 184 void ShowText(bool visible) { 185 ScreenRecoveryUI::ShowText(visible); 186 187 text_ever_visible = text_ever_visible || visible; 188 text_visible = visible; 189 } 190 191 bool IsTextVisible() { 192 return text_visible; 193 } 194 195 bool WasTextEverVisible() { 196 return text_ever_visible; 197 } 198 199 void Print(const char* fmt, ...) { 200 char buf[256]; 201 va_list ap; 202 va_start(ap, fmt); 203 vsnprintf(buf, 256, fmt, ap); 204 va_end(ap); 205 ScreenRecoveryUI::Print("%s", buf); 206 } 207 208 void StartMenu(const char* const * headers, const char* const * items, 209 int initial_selection) { 210 ScreenRecoveryUI::StartMenu(headers, items, initial_selection); 211 212 menu_items = 0; 213 for (const char* const * p = items; *p; ++p) { 214 ++menu_items; 215 } 216 } 217 218 int SelectMenu(int sel) { 219 if (sel < 0) { 220 sel += menu_items; 221 } 222 sel %= menu_items; 223 ScreenRecoveryUI::SelectMenu(sel); 224 return sel; 225 } 226 227 void NextCheckKeyIsLong(bool is_long_press) { 228 long_press = is_long_press; 229 } 230 231 void KeyLongPress(int key) { 232 pthread_mutex_lock(&long_mu); 233 pending_select = true; 234 pthread_mutex_unlock(&long_mu); 235 236 Redraw(); 237 } 238 239 KeyAction CheckKey(int key) { 240 pthread_mutex_lock(&long_mu); 241 pending_select = false; 242 pthread_mutex_unlock(&long_mu); 243 244 if (key == KEY_F1) { 245 return MOUNT_SYSTEM; 246 } 247 248 if (long_press) { 249 if (text_visible) { 250 EnqueueKey(KEY_ENTER); 251 return IGNORE; 252 } else { 253 return TOGGLE; 254 } 255 } else { 256 return text_visible ? ENQUEUE : IGNORE; 257 } 258 } 259 260 private: 261 static const int kKeyBufferSize = 100; 262 263 int text_visible; 264 int text_ever_visible; 265 266 Icon background_mode; 267 268 int up_keys; 269 int next_key_pos; 270 int last_keys[kKeyBufferSize]; 271 272 int menu_items; 273 274 pthread_mutex_t long_mu; 275 bool pending_select; 276 277 bool long_press; 278 }; 279 280 class FuguDevice : public Device { 281 public: 282 FuguDevice() : 283 ui(new FuguUI) { 284 } 285 286 RecoveryUI* GetUI() { return ui; } 287 288 int HandleMenuKey(int key, int visible) { 289 static int running = 0; 290 291 if (visible) { 292 switch (key) { 293 case KEY_ENTER: 294 return kInvokeItem; 295 break; 296 297 case KEY_UP: 298 return kHighlightUp; 299 break; 300 301 case KEY_DOWN: 302 case KEY_CONNECT: // the Fugu hardware button 303 return kHighlightDown; 304 break; 305 } 306 } 307 308 return kNoAction; 309 } 310 311 BuiltinAction InvokeMenuItem(int menu_position) { 312 switch (menu_position) { 313 case 0: return REBOOT; 314 case 1: return APPLY_ADB_SIDELOAD; 315 case 2: return WIPE_DATA; 316 case 3: return WIPE_CACHE; 317 case 4: return READ_RECOVERY_LASTLOG; 318 default: return NO_ACTION; 319 } 320 } 321 322 const char* const* GetMenuHeaders() { return HEADERS; } 323 const char* const* GetMenuItems() { return ITEMS; } 324 325 private: 326 RecoveryUI* ui; 327 }; 328 329 Device* make_device() { 330 return new FuguDevice(); 331 } 332