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).
now()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:
FuguUI()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
Init()78 void Init() {
79 SetupDisplayMode();
80 ScreenRecoveryUI::Init();
81 }
82
SetupDisplayMode()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
SetBackground(Icon icon)145 void SetBackground(Icon icon) {
146 ScreenRecoveryUI::SetBackground(icon);
147
148 background_mode = icon;
149 }
150
SetColor(UIElement e)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
ShowText(bool visible)183 void ShowText(bool visible) {
184 ScreenRecoveryUI::ShowText(visible);
185
186 text_ever_visible = text_ever_visible || visible;
187 text_visible = visible;
188 }
189
IsTextVisible()190 bool IsTextVisible() {
191 return text_visible;
192 }
193
WasTextEverVisible()194 bool WasTextEverVisible() {
195 return text_ever_visible;
196 }
197
Print(const char * fmt,...)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
StartMenu(const char * const * headers,const char * const * items,int initial_selection)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
SelectMenu(int sel)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
NextCheckKeyIsLong(bool is_long_press)226 void NextCheckKeyIsLong(bool is_long_press) {
227 long_press = is_long_press;
228 }
229
KeyLongPress(int key)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
CheckKey(int key)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:
FuguDevice()281 FuguDevice() :
282 ui(new FuguUI) {
283 }
284
GetUI()285 RecoveryUI* GetUI() { return ui; }
286
HandleMenuKey(int key,int visible)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
InvokeMenuItem(int menu_position)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
GetMenuHeaders()320 const char* const* GetMenuHeaders() { return HEADERS; }
GetMenuItems()321 const char* const* GetMenuItems() { return ITEMS; }
322
323 private:
324 RecoveryUI* ui;
325 };
326
make_device()327 Device* make_device() {
328 return new FuguDevice();
329 }
330