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 NULL }; 48 49 #define kFBDevice "/dev/graphics/fb0" 50 #define FBIO_PSB_SET_RGBX _IOWR('F', 0x42, struct fb_var_screeninfo) 51 #define FBIO_PSB_SET_RMODE _IOWR('F', 0x43, struct fb_var_screeninfo) 52 53 struct led_rgb_vals { 54 uint8_t rgb[3]; 55 }; 56 57 // Return the current time as a double (including fractions of a second). 58 static double now() { 59 struct timeval tv; 60 gettimeofday(&tv, NULL); 61 return tv.tv_sec + tv.tv_usec / 1000000.0; 62 } 63 64 class FuguUI : public ScreenRecoveryUI { 65 public: 66 FuguUI() : 67 text_visible(false), 68 text_ever_visible(false), 69 background_mode(NONE), 70 up_keys(0), 71 next_key_pos(0), 72 pending_select(false), 73 long_press(false) { 74 pthread_mutex_init(&long_mu, NULL); 75 memset(last_keys, 0, kKeyBufferSize * sizeof(int)); 76 } 77 78 void Init() { 79 SetupDisplayMode(); 80 ScreenRecoveryUI::Init(); 81 } 82 83 void SetupDisplayMode() { 84 int fb_dev = open(kFBDevice, O_RDWR); 85 int res; 86 uint32_t i; 87 printf("opening fb %s\n", kFBDevice); 88 if (fb_dev < 0) { 89 fprintf(stderr, "FAIL: failed to open \"%s\" (errno = %d)\n", kFBDevice, errno); 90 return; 91 } 92 93 struct fb_var_screeninfo current_mode; 94 95 res = ioctl(fb_dev, FBIO_PSB_SET_RMODE, ¤t_mode); 96 if (res) { 97 fprintf(stderr, 98 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n", 99 errno); 100 return; 101 } 102 103 res = ioctl(fb_dev, FBIOGET_VSCREENINFO, ¤t_mode); 104 if (res) { 105 fprintf(stderr, "FAIL: unable to get mode, err %d\n", res); 106 return; 107 } 108 109 res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_POWERDOWN); 110 if (res) { 111 fprintf(stderr, "FAIL: unable to blank display, err %d\n", res); 112 return; 113 } 114 115 current_mode.bits_per_pixel = 32; 116 current_mode.red.offset = 0; 117 current_mode.red.length = 8; 118 current_mode.green.offset = 8; 119 current_mode.green.length = 8; 120 current_mode.blue.offset = 16; 121 current_mode.blue.length = 8; 122 123 res = ioctl(fb_dev, FBIOPUT_VSCREENINFO, ¤t_mode); 124 if (res) { 125 fprintf(stderr, "FAIL: unable to set mode, err %d\n", res); 126 return; 127 } 128 129 /* set our display controller for RGBX */ 130 res = ioctl(fb_dev, FBIO_PSB_SET_RGBX, ¤t_mode); 131 if (res) { 132 fprintf(stderr, 133 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n", 134 errno); 135 return; 136 } 137 138 res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_UNBLANK); 139 if (res) { 140 fprintf(stderr, "FAIL: unable to unblank display, err %d\n", res); 141 return; 142 } 143 } 144 145 void SetBackground(Icon icon) { 146 ScreenRecoveryUI::SetBackground(icon); 147 148 background_mode = icon; 149 } 150 151 void SetColor(UIElement e) { 152 switch (e) { 153 case HEADER: 154 gr_color(247, 0, 6, 255); 155 break; 156 case MENU: 157 gr_color(0, 106, 157, 255); 158 break; 159 case MENU_SEL_BG: 160 pthread_mutex_lock(&long_mu); 161 if (pending_select) { 162 gr_color(0, 156, 100, 255); 163 } else { 164 gr_color(0, 106, 157, 255); 165 } 166 pthread_mutex_unlock(&long_mu); 167 break; 168 case MENU_SEL_FG: 169 gr_color(255, 255, 255, 255); 170 break; 171 case LOG: 172 gr_color(249, 194, 0, 255); 173 break; 174 case TEXT_FILL: 175 gr_color(0, 0, 0, 160); 176 break; 177 default: 178 gr_color(255, 255, 255, 255); 179 break; 180 } 181 } 182 183 void ShowText(bool visible) { 184 ScreenRecoveryUI::ShowText(visible); 185 186 text_ever_visible = text_ever_visible || visible; 187 text_visible = visible; 188 } 189 190 bool IsTextVisible() { 191 return text_visible; 192 } 193 194 bool WasTextEverVisible() { 195 return text_ever_visible; 196 } 197 198 void Print(const char* fmt, ...) { 199 char buf[256]; 200 va_list ap; 201 va_start(ap, fmt); 202 vsnprintf(buf, 256, fmt, ap); 203 va_end(ap); 204 ScreenRecoveryUI::Print("%s", buf); 205 } 206 207 void StartMenu(const char* const * headers, const char* const * items, 208 int initial_selection) { 209 ScreenRecoveryUI::StartMenu(headers, items, initial_selection); 210 211 menu_items = 0; 212 for (const char* const * p = items; *p; ++p) { 213 ++menu_items; 214 } 215 } 216 217 int SelectMenu(int sel) { 218 if (sel < 0) { 219 sel += menu_items; 220 } 221 sel %= menu_items; 222 ScreenRecoveryUI::SelectMenu(sel); 223 return sel; 224 } 225 226 void NextCheckKeyIsLong(bool is_long_press) { 227 long_press = is_long_press; 228 } 229 230 void KeyLongPress(int key) { 231 pthread_mutex_lock(&long_mu); 232 pending_select = true; 233 pthread_mutex_unlock(&long_mu); 234 235 Redraw(); 236 } 237 238 KeyAction CheckKey(int key) { 239 pthread_mutex_lock(&long_mu); 240 pending_select = false; 241 pthread_mutex_unlock(&long_mu); 242 243 if (key == KEY_F1) { 244 return MOUNT_SYSTEM; 245 } 246 247 if (long_press) { 248 if (text_visible) { 249 EnqueueKey(KEY_ENTER); 250 return IGNORE; 251 } else { 252 return TOGGLE; 253 } 254 } else { 255 return text_visible ? ENQUEUE : IGNORE; 256 } 257 } 258 259 private: 260 static const int kKeyBufferSize = 100; 261 262 int text_visible; 263 int text_ever_visible; 264 265 Icon background_mode; 266 267 int up_keys; 268 int next_key_pos; 269 int last_keys[kKeyBufferSize]; 270 271 int menu_items; 272 273 pthread_mutex_t long_mu; 274 bool pending_select; 275 276 bool long_press; 277 }; 278 279 class FuguDevice : public Device { 280 public: 281 FuguDevice() : 282 ui(new FuguUI) { 283 } 284 285 RecoveryUI* GetUI() { return ui; } 286 287 int HandleMenuKey(int key, int visible) { 288 static int running = 0; 289 290 if (visible) { 291 switch (key) { 292 case KEY_ENTER: 293 return kInvokeItem; 294 break; 295 296 case KEY_UP: 297 return kHighlightUp; 298 break; 299 300 case KEY_DOWN: 301 case KEY_CONNECT: // the Fugu hardware button 302 return kHighlightDown; 303 break; 304 } 305 } 306 307 return kNoAction; 308 } 309 310 BuiltinAction InvokeMenuItem(int menu_position) { 311 switch (menu_position) { 312 case 0: return REBOOT; 313 case 1: return APPLY_ADB_SIDELOAD; 314 case 2: return WIPE_DATA; 315 case 3: return WIPE_CACHE; 316 default: return NO_ACTION; 317 } 318 } 319 320 const char* const* GetMenuHeaders() { return HEADERS; } 321 const char* const* GetMenuItems() { return ITEMS; } 322 323 private: 324 RecoveryUI* ui; 325 }; 326 327 Device* make_device() { 328 return new FuguDevice(); 329 } 330