merge in ics-release history after reset to master
diff --git a/minui/events.c b/minui/events.c
index 3aed2a8..c533a48 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -25,11 +25,23 @@
 #include "minui.h"
 
 #define MAX_DEVICES 16
+#define MAX_MISC_FDS 16
 
-static struct pollfd ev_fds[MAX_DEVICES];
+#define test_bit(bit, array)   ((array)[(bit)/8] & (1<<((bit)%8)))
+
+struct fd_info {
+    ev_callback cb;
+    void *data;
+};
+
+static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS];
+static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
+
 static unsigned ev_count = 0;
+static unsigned ev_dev_count = 0;
+static unsigned ev_misc_count = 0;
 
-int ev_init(void)
+int ev_init(ev_callback input_cb, void *data)
 {
     DIR *dir;
     struct dirent *de;
@@ -38,45 +50,92 @@
     dir = opendir("/dev/input");
     if(dir != 0) {
         while((de = readdir(dir))) {
+            uint8_t ev_bits[(EV_MAX + 1) / 8];
+
 //            fprintf(stderr,"/dev/input/%s\n", de->d_name);
             if(strncmp(de->d_name,"event",5)) continue;
             fd = openat(dirfd(dir), de->d_name, O_RDONLY);
             if(fd < 0) continue;
 
+            /* read the evbits of the input device */
+            if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {
+                close(fd);
+                continue;
+            }
+
+            /* TODO: add ability to specify event masks. For now, just assume
+             * that only EV_KEY and EV_REL event types are ever needed. */
+            if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {
+                close(fd);
+                continue;
+            }
+
             ev_fds[ev_count].fd = fd;
             ev_fds[ev_count].events = POLLIN;
+            ev_fdinfo[ev_count].cb = input_cb;
+            ev_fdinfo[ev_count].data = data;
             ev_count++;
-            if(ev_count == MAX_DEVICES) break;
+            ev_dev_count++;
+            if(ev_dev_count == MAX_DEVICES) break;
         }
     }
 
     return 0;
 }
 
+int ev_add_fd(int fd, ev_callback cb, void *data)
+{
+    if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
+        return -1;
+
+    ev_fds[ev_count].fd = fd;
+    ev_fds[ev_count].events = POLLIN;
+    ev_fdinfo[ev_count].cb = cb;
+    ev_fdinfo[ev_count].data = data;
+    ev_count++;
+    ev_misc_count++;
+    return 0;
+}
+
 void ev_exit(void)
 {
     while (ev_count > 0) {
         close(ev_fds[--ev_count].fd);
     }
+    ev_misc_count = 0;
+    ev_dev_count = 0;
 }
 
-int ev_get(struct input_event *ev, unsigned dont_wait)
+int ev_wait(int timeout)
 {
     int r;
+
+    r = poll(ev_fds, ev_count, timeout);
+    if (r <= 0)
+        return -1;
+    return 0;
+}
+
+void ev_dispatch(void)
+{
     unsigned n;
+    int ret;
 
-    do {
-        r = poll(ev_fds, ev_count, dont_wait ? 0 : -1);
+    for (n = 0; n < ev_count; n++) {
+        ev_callback cb = ev_fdinfo[n].cb;
+        if (cb && (ev_fds[n].revents & ev_fds[n].events))
+            cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data);
+    }
+}
 
-        if(r > 0) {
-            for(n = 0; n < ev_count; n++) {
-                if(ev_fds[n].revents & POLLIN) {
-                    r = read(ev_fds[n].fd, ev, sizeof(*ev));
-                    if(r == sizeof(*ev)) return 0;
-                }
-            }
-        }
-    } while(dont_wait == 0);
+int ev_get_input(int fd, short revents, struct input_event *ev)
+{
+    int r;
 
+    if (revents & POLLIN) {
+        r = read(fd, ev, sizeof(*ev));
+        if (r == sizeof(*ev))
+            return 0;
+    }
     return -1;
 }
diff --git a/minui/graphics.c b/minui/graphics.c
index b79631a..fa8e510 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -201,6 +202,12 @@
     return gr_font->cwidth * strlen(s);
 }
 
+void gr_font_size(int *x, int *y)
+{
+    *x = gr_font->cwidth;
+    *y = gr_font->cheight;
+}
+
 int gr_text(int x, int y, const char *s)
 {
     GGLContext *gl = gr_context;
@@ -358,3 +365,12 @@
 {
     return (unsigned short *) gr_mem_surface.data;
 }
+
+void gr_fb_blank(bool blank)
+{
+    int ret;
+
+    ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
+    if (ret < 0)
+        perror("ioctl(): blank");
+}
diff --git a/minui/minui.h b/minui/minui.h
index 567d421..cb1ed65 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -17,6 +17,8 @@
 #ifndef _MINUI_H_
 #define _MINUI_H_
 
+#include <stdbool.h>
+
 typedef void* gr_surface;
 typedef unsigned short gr_pixel;
 
@@ -27,11 +29,13 @@
 int gr_fb_height(void);
 gr_pixel *gr_fb_data(void);
 void gr_flip(void);
+void gr_fb_blank(bool blank);
 
 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
 void gr_fill(int x, int y, int w, int h);
 int gr_text(int x, int y, const char *s);
 int gr_measure(const char *s);
+void gr_font_size(int *x, int *y);
 
 void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
 unsigned int gr_get_width(gr_surface surface);
@@ -41,9 +45,21 @@
 // see http://www.mjmwired.net/kernel/Documentation/input/ for info.
 struct input_event;
 
-int ev_init(void);
+typedef int (*ev_callback)(int fd, short revents, void *data);
+
+int ev_init(ev_callback input_cb, void *data);
 void ev_exit(void);
-int ev_get(struct input_event *ev, unsigned dont_wait);
+int ev_add_fd(int fd, ev_callback cb, void *data);
+
+/* timeout has the same semantics as for poll
+ *    0 : don't block
+ *  < 0 : block forever
+ *  > 0 : block for 'timeout' milliseconds
+ */
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, short revents, struct input_event *ev);
+void ev_dispatch(void);
 
 // Resources
 
diff --git a/ui.c b/ui.c
index 4e41bc9..d08f7d3 100644
--- a/ui.c
+++ b/ui.c
@@ -295,71 +295,83 @@
     return NULL;
 }
 
+static int rel_sum = 0;
+
+static int input_callback(int fd, short revents, void *data)
+{
+    struct input_event ev;
+    int ret;
+    int fake_key = 0;
+
+    ret = ev_get_input(fd, revents, &ev);
+    if (ret)
+        return -1;
+
+    if (ev.type == EV_SYN) {
+        return 0;
+    } else if (ev.type == EV_REL) {
+        if (ev.code == REL_Y) {
+            // accumulate the up or down motion reported by
+            // the trackball.  When it exceeds a threshold
+            // (positive or negative), fake an up/down
+            // key event.
+            rel_sum += ev.value;
+            if (rel_sum > 3) {
+                fake_key = 1;
+                ev.type = EV_KEY;
+                ev.code = KEY_DOWN;
+                ev.value = 1;
+                rel_sum = 0;
+            } else if (rel_sum < -3) {
+                fake_key = 1;
+                ev.type = EV_KEY;
+                ev.code = KEY_UP;
+                ev.value = 1;
+                rel_sum = 0;
+            }
+        }
+    } else {
+        rel_sum = 0;
+    }
+
+    if (ev.type != EV_KEY || ev.code > KEY_MAX)
+        return 0;
+
+    pthread_mutex_lock(&key_queue_mutex);
+    if (!fake_key) {
+        // our "fake" keys only report a key-down event (no
+        // key-up), so don't record them in the key_pressed
+        // table.
+        key_pressed[ev.code] = ev.value;
+    }
+    const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
+    if (ev.value > 0 && key_queue_len < queue_max) {
+        key_queue[key_queue_len++] = ev.code;
+        pthread_cond_signal(&key_queue_cond);
+    }
+    pthread_mutex_unlock(&key_queue_mutex);
+
+    if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
+        pthread_mutex_lock(&gUpdateMutex);
+        show_text = !show_text;
+        if (show_text) show_text_ever = 1;
+        update_screen_locked();
+        pthread_mutex_unlock(&gUpdateMutex);
+    }
+
+    if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
+        android_reboot(ANDROID_RB_RESTART, 0, 0);
+    }
+
+    return 0;
+}
+
 // Reads input events, handles special hot keys, and adds to the key queue.
 static void *input_thread(void *cookie)
 {
-    int rel_sum = 0;
-    int fake_key = 0;
     for (;;) {
-        // wait for the next key event
-        struct input_event ev;
-        do {
-            ev_get(&ev, 0);
-
-            if (ev.type == EV_SYN) {
-                continue;
-            } else if (ev.type == EV_REL) {
-                if (ev.code == REL_Y) {
-                    // accumulate the up or down motion reported by
-                    // the trackball.  When it exceeds a threshold
-                    // (positive or negative), fake an up/down
-                    // key event.
-                    rel_sum += ev.value;
-                    if (rel_sum > 3) {
-                        fake_key = 1;
-                        ev.type = EV_KEY;
-                        ev.code = KEY_DOWN;
-                        ev.value = 1;
-                        rel_sum = 0;
-                    } else if (rel_sum < -3) {
-                        fake_key = 1;
-                        ev.type = EV_KEY;
-                        ev.code = KEY_UP;
-                        ev.value = 1;
-                        rel_sum = 0;
-                    }
-                }
-            } else {
-                rel_sum = 0;
-            }
-        } while (ev.type != EV_KEY || ev.code > KEY_MAX);
-
-        pthread_mutex_lock(&key_queue_mutex);
-        if (!fake_key) {
-            // our "fake" keys only report a key-down event (no
-            // key-up), so don't record them in the key_pressed
-            // table.
-            key_pressed[ev.code] = ev.value;
-        }
-        fake_key = 0;
-        const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
-        if (ev.value > 0 && key_queue_len < queue_max) {
-            key_queue[key_queue_len++] = ev.code;
-            pthread_cond_signal(&key_queue_cond);
-        }
-        pthread_mutex_unlock(&key_queue_mutex);
-
-        if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
-            pthread_mutex_lock(&gUpdateMutex);
-            show_text = !show_text;
-            if (show_text) show_text_ever = 1;
-            update_screen_locked();
-            pthread_mutex_unlock(&gUpdateMutex);
-        }
-
-        if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
-            android_reboot(ANDROID_RB_RESTART, 0, 0);
-        }
+        if (!ev_wait(-1))
+            ev_dispatch();
     }
     return NULL;
 }
@@ -367,7 +379,7 @@
 void ui_init(void)
 {
     gr_init();
-    ev_init();
+    ev_init(input_callback, NULL);
 
     text_col = text_row = 0;
     text_rows = gr_fb_height() / CHAR_HEIGHT;