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
38static 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
43static 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
54struct led_rgb_vals {
55        uint8_t rgb[3];
56};
57
58// Return the current time as a double (including fractions of a second).
59static double now() {
60    struct timeval tv;
61    gettimeofday(&tv, NULL);
62    return tv.tv_sec + tv.tv_usec / 1000000.0;
63}
64
65class FuguUI : public ScreenRecoveryUI {
66public:
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, &current_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, &current_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, &current_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, &current_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
260private:
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
280class 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
329Device* make_device() {
330    return new FuguDevice();
331}
332