summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Albrecht <prom@berlin.ccc.de>2010-09-01 21:24:34 +0200
committerIngo Albrecht <prom@berlin.ccc.de>2010-09-01 21:24:34 +0200
commitdcc3a41036f374a48b2b1791575e11e5e3f09d02 (patch)
tree2261d8ce1395d2cab815f91de96a92b809843186
parent1198f35283b5fb026249c6881c01038d2a486613 (diff)
ui: Some experimental code snippets i cobbled together several months ago.
-rw-r--r--src/target/ui-experiment/README11
-rw-r--r--src/target/ui-experiment/display.h46
-rw-r--r--src/target/ui-experiment/font.hbin0 -> 61204 bytes
-rw-r--r--src/target/ui-experiment/image.h166
-rw-r--r--src/target/ui-experiment/menu.h18
-rw-r--r--src/target/ui-experiment/pixel.h113
-rw-r--r--src/target/ui-experiment/png2tiny.c51
-rw-r--r--src/target/ui-experiment/screen.h21
-rw-r--r--src/target/ui-experiment/sdl.c250
-rw-r--r--src/target/ui-experiment/sdl.h9
-rw-r--r--src/target/ui-experiment/ui.h81
11 files changed, 766 insertions, 0 deletions
diff --git a/src/target/ui-experiment/README b/src/target/ui-experiment/README
new file mode 100644
index 00000000..81b34d72
--- /dev/null
+++ b/src/target/ui-experiment/README
@@ -0,0 +1,11 @@
+
+This is some exploratory and experimental code related
+to the development of a proper lcd-based ui.
+
+It has been written by Ingo Albrecht <prom@berlin.ccc.de>
+and, lacking further arrangements, should be considered to
+be licensed under the GNU GPL v2 or later.
+
+I have placed this here due to general interest. All of it
+can safely be removed once we have a proper ui.
+
diff --git a/src/target/ui-experiment/display.h b/src/target/ui-experiment/display.h
new file mode 100644
index 00000000..55913700
--- /dev/null
+++ b/src/target/ui-experiment/display.h
@@ -0,0 +1,46 @@
+
+#ifndef _UI_DISPLAY_H
+#define _UI_DISPLAY_H
+
+#include <ui/pixel.h>
+#include <ui/image.h>
+
+/**
+ * Displays - physical display devices
+ *
+ * This layer is introduced tentatively, expecting use
+ * of OSMOCOM on multi-display phones, most likely
+ * with a main screen and a cover screen.
+ *
+ */
+struct display {
+ const char *name;
+
+ pxtype_t pixeltype;
+ pxsize_t width;
+ pxsize_t height;
+
+ /* We always operate on an in-memory frame buffer that
+ * can be put on display using damage functions provided
+ * by the image class.
+ */
+ struct image *fbuf;
+
+ /*
+ * We display a top-level widget.
+ */
+ struct widget *widget;
+
+ /*
+ * We hold a graphics context, configured for the target
+ * pixel format.
+ */
+ struct graphics *graphics;
+
+
+ void (*draw) (struct display *display);
+
+ void *priv;
+};
+
+#endif
diff --git a/src/target/ui-experiment/font.h b/src/target/ui-experiment/font.h
new file mode 100644
index 00000000..5dc6eae7
--- /dev/null
+++ b/src/target/ui-experiment/font.h
Binary files differ
diff --git a/src/target/ui-experiment/image.h b/src/target/ui-experiment/image.h
new file mode 100644
index 00000000..13c11f5b
--- /dev/null
+++ b/src/target/ui-experiment/image.h
@@ -0,0 +1,166 @@
+
+#ifndef _UI_IMAGE_H
+#define _UI_IMAGE_H
+
+/* for exit() */
+#include <stdlib.h>
+
+#include <ui/pixel.h>
+#include <ui/font.h>
+
+struct image {
+ pxtype_t type;
+ pxdims_t size;
+ unsigned char *data;
+};
+
+
+struct image font_img_v = {
+ .type = PXTYPE_MONO_V8,
+ .size = {8, 2048},
+ .data = &fontdata_r8x8,
+};
+
+struct image font_img_h = {
+ .type = PXTYPE_MONO_H8,
+ .size = {8, 2048},
+ .data = &fontdata_r8x8_horiz,
+};
+
+px_t
+image_get_pixel(struct image *img, pxoff_t x, pxoff_t y) {
+ unsigned stride, base, offset;
+ const uint8_t *p8;
+ const uint16_t *p16;
+
+ switch(img->type) {
+ case PXTYPE_MONO_V8:
+ stride = img->size.w;
+ base = y / 8;
+ offset = y % 8;
+ p8 = (uint8_t*)(img->data + x + base * stride);
+ return px_from_mono(((*p8) >> offset) & 1);
+ case PXTYPE_MONO_H8:
+ stride = img->size.w / 8;
+ base = x / 8;
+ offset = x % 8;
+ p8 = (uint8_t*)(img->data + base + y * stride);
+ return px_from_mono(((*p8) >> offset) & 1);
+ case PXTYPE_RGB444:
+ stride = img->size.w * 2;
+ p16 = (uint16_t*)(img->data + x * 2 + y * stride);
+ return px_from_rgb444(*p16);
+ }
+
+ return 0;
+}
+
+void
+image_set_pixel(struct image *img, pxoff_t x, pxoff_t y, px_t v) {
+ unsigned stride, base, offset;
+ uint8_t *p8;
+ uint16_t *p16;
+
+ switch(img->type) {
+ case PXTYPE_MONO_V8:
+ stride = img->size.w;
+ base = y / 8;
+ offset = y % 8;
+ p8 = (uint8_t*)(img->data + x + base * stride);
+ *p8 |= (px_to_mono(v) << offset);
+ break;
+ case PXTYPE_MONO_H8:
+ stride = img->size.w / 8;
+ base = x / 8;
+ offset = x % 8;
+ p8 = (uint8_t*)(img->data + base + y * stride);
+ *p8 |= (px_to_mono(v) << offset);
+ break;
+ case PXTYPE_RGB444:
+ stride = img->size.w * 2;
+ p16 = (uint16_t*)(img->data + x * 2 + y * stride);
+ *p16 = px_to_rgb444(v);
+ break;
+ }
+}
+
+void
+image_blit(struct image *dst, pxposn_t dstp,
+ struct image *src, pxposn_t srcp,
+ pxdims_t d)
+{
+ unsigned x, y, s;
+
+ printf("blit %dx%d from %dx%d to %dx%d\n", d.w, d.h, srcp.x, srcp.y, dstp.x, dstp.y);
+
+ // *cough* slow.
+ for(y = 0; y < d.h; y++) {
+ for(x = 0; x < d.w; x++) {
+ px_t p = image_get_pixel(src, srcp.x + x, srcp.y + y);
+ image_set_pixel(dst, dstp.x + x, dstp.y + y, p);
+ }
+ }
+}
+
+void
+image_draw_char(struct image *dst, pxposn_t p, char chr) {
+ unsigned char c = (unsigned char)chr;
+ pxposn_t pf = {0,c*8};
+ pxdims_t d = {8,8};
+ image_blit(dst, p, &font_img_h, pf, d);
+}
+
+void
+image_draw_string(struct image *dst, pxposn_t p, char *str) {
+ while(*str) {
+ image_draw_char(dst, p, *str);
+ p.x += 8;
+ str++;
+ }
+}
+
+static void
+image_fill_rect_rgb444(struct image *dst, pxrect_t rect, uint16_t color) {
+ unsigned x, y, s;
+ uint16_t *p;
+
+ unsigned stride = dst->size.w * 2;
+
+ for(y = rect.p.y; y < rect.p.y + rect.d.h; y++) {
+ for(x = rect.p.x; x < rect.p.x + rect.d.w; x++) {
+ p = (uint16_t*)&dst->data[x * 2 + y * stride];
+ *p = color;
+ }
+ }
+}
+
+void
+image_fill_rect(struct image *dst, pxrect_t rect, px_t color)
+{
+ switch(dst->type) {
+ case PXTYPE_MONO_V8:
+ break;
+ case PXTYPE_MONO_H8:
+ break;
+ case PXTYPE_RGB444:
+ image_fill_rect_rgb444(dst, rect, px_to_rgb444(color));
+ break;
+ }
+}
+
+void
+image_draw_hline(struct image *dst, pxposn_t posn, pxoff_t len, px_t color)
+{
+}
+
+void
+image_draw_vline(struct image *dst, pxposn_t posn, pxoff_t len, px_t color)
+{
+}
+
+void
+image_draw_rect(struct image *dst, pxrect_t rect, px_t color)
+{
+}
+
+#endif
diff --git a/src/target/ui-experiment/menu.h b/src/target/ui-experiment/menu.h
new file mode 100644
index 00000000..d9cc97b3
--- /dev/null
+++ b/src/target/ui-experiment/menu.h
@@ -0,0 +1,18 @@
+
+/**
+ * Menus - menus and menu items
+ *
+ * We represent both menus and menu items in a single structure.
+ *
+ * They share the properties of having a title as well as having
+ * interaction callbacks such as on_select.
+ *
+ * Menus have a child item array that is indexed by menu position.
+ * The position of items in this array is used for numeric menu navigation.
+ *
+ */
+struct menu {
+ const char *title;
+ void (*on_select)(void);
+ struct menu *children[10];
+};
diff --git a/src/target/ui-experiment/pixel.h b/src/target/ui-experiment/pixel.h
new file mode 100644
index 00000000..dde28e2a
--- /dev/null
+++ b/src/target/ui-experiment/pixel.h
@@ -0,0 +1,113 @@
+
+#ifndef _UI_PIXEL_H
+#define _UI_PIXEL_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+/** Supported pixel types */
+typedef enum {
+ _PXTYPE_INVALID,
+
+ /** "Generic" pixel type (24/32bit RGB) */
+ PXTYPE_GENERIC,
+
+ /** 8 horizontal mono pixels per byte */
+ PXTYPE_MONO_H8,
+
+ /** 8 vertical mono pixels per byte */
+ PXTYPE_MONO_V8,
+
+ /** 12bit RGB444 colors */
+ PXTYPE_RGB444,
+
+} pxtype_t;
+
+
+/** Generic pixel type */
+typedef uint32_t px_t;
+
+#define PX_R(p) ((uint8_t)((v) >> 16 & 0xFF))
+#define PX_G(p) ((uint8_t)((v) >> 8 & 0xFF))
+#define PX_B(p) ((uint8_t)((v) >> 0 / 0xFF))
+
+#define PX_RGB(r,g,b) ((px_t)((r)<<16|(g)<<8|(b)))
+
+#define PX_BLACK ((px_t)0x000000)
+#define PX_RED ((px_t)0xFF0000)
+#define PX_GREEN ((px_t)0x00FF00)
+#define PX_BLUE ((px_t)0x0000FF)
+#define PX_WHITE ((px_t)0xFFFFFF)
+
+
+/* Mono types */
+typedef uint8_t px_mono_t;
+
+#define PX_MONO_BLACK ((px_mono_t)0)
+#define PX_MONO_WHITE ((px_mono_t)1)
+
+inline px_t
+px_from_mono(uint8_t v) {
+ return v ? PX_WHITE : PX_BLACK;
+}
+
+inline uint8_t
+px_to_mono(px_t v) {
+ uint16_t a = (PX_R(v) + PX_G(v) + PX_B(v)) / 3;
+ return (a >= 0x7f) ? 1 : 0;
+}
+
+/* RGB444 */
+typedef uint16_t px_rgb444_t;
+
+#define PX_RGB444_R(p) ((p) >> 8 & 0xf)
+#define PX_RGB444_G(p) ((p) >> 4 & 0xf)
+#define PX_RGB444_B(p) ((p) >> 0 & 0xf)
+
+#define PX_RGB444_RGB(r,g,b) ((px_rgb444_t)((r)<<8|(g)<<4|(b)))
+
+inline px_t
+px_from_rgb444(px_rgb444_t v) {
+ return
+ PX_RGB444_R(v) << 16 | PX_RGB444_R(v) << 20
+ | PX_RGB444_G(v) << 8 | PX_RGB444_G(v) << 12
+ | PX_RGB444_B(v) << 0 | PX_RGB444_B(v) << 4;
+}
+
+inline uint16_t
+px_to_rgb444(px_t v) {
+ uint8_t r = (v >> 20) & 0xF;
+ uint8_t g = (v >> 12) & 0xF;
+ uint8_t b = (v >> 4) & 0xF;
+
+ uint16_t res = (r<< 8) | (g << 4) | (b << 0);
+
+ return res;
+}
+
+
+/** Size in pixels */
+typedef uint16_t pxsize_t;
+
+/** Offset in pixels */
+typedef int16_t pxoff_t;
+
+/** 2D position in pixels */
+typedef struct {
+ pxoff_t x;
+ pxoff_t y;
+} pxposn_t;
+
+/** 2D dimensions in pixels */
+typedef struct {
+ pxsize_t w;
+ pxsize_t h;
+} pxdims_t;
+
+/** 2D rectangle in pixels */
+typedef struct {
+ pxposn_t p;
+ pxdims_t d;
+} pxrect_t;
+
+#endif
diff --git a/src/target/ui-experiment/png2tiny.c b/src/target/ui-experiment/png2tiny.c
new file mode 100644
index 00000000..d314b9a2
--- /dev/null
+++ b/src/target/ui-experiment/png2tiny.c
@@ -0,0 +1,51 @@
+
+#include <stdlib.h>
+
+#include <SDL_image.h>
+
+enum {
+ FORMAT_NONE,
+ FORMAT_C
+};
+
+void
+version(const char *name) {
+ puts(name);
+ //printf("%s rev %s\n", name, REVISION);
+ exit(2);
+}
+
+void
+usage(const char *name) {
+ printf("Usage: %s [-hv] [-f outfmt] [-s outsym] <infile> <outfile>\n");
+ exit(2);
+}
+
+int
+main(int argc, char **argv) {
+ int opt, outfmt;
+ const char *outsym = NULL;
+ SDL_Surface *img;
+
+ while((opt = getopt(argc, argv, "f:s:hv")) != -1) {
+ switch(opt) {
+ case 'f':
+ if(!strcmp(optarg, "c")) {
+ outfmt = FORMAT_C;
+ }
+ break;
+ case 's':
+ outsym = optarg;
+ break;
+ case 'v':
+ version(argv[0]);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/target/ui-experiment/screen.h b/src/target/ui-experiment/screen.h
new file mode 100644
index 00000000..1174d9e7
--- /dev/null
+++ b/src/target/ui-experiment/screen.h
@@ -0,0 +1,21 @@
+
+/**
+ * Screens - full-screen dialogs
+ *
+ * These compose the first level of interaction in the UI.
+ *
+ * There is always exactly one active screen, which is in
+ * control of the entire display on which it is displayed.
+ *
+ * Screen activations are stacked, providing interaction depth.
+ *
+ */
+struct screen {
+ const char *name;
+ void (*on_enter)(void);
+ void (*on_leave)(void);
+ void (*on_render)(void);
+ void (*on_key_press)(void);
+ void (*on_key_release)(void);
+};
+
diff --git a/src/target/ui-experiment/sdl.c b/src/target/ui-experiment/sdl.c
new file mode 100644
index 00000000..e6daac70
--- /dev/null
+++ b/src/target/ui-experiment/sdl.c
@@ -0,0 +1,250 @@
+
+#include <ui/display.h>
+#include <ui/image.h>
+#include <ui/sdl.h>
+
+#include <stdio.h>
+
+#include <SDL.h>
+
+#define SDL_PRIV(d) ((struct sdl_display*)(d)->priv)
+
+#define REFRESH_INTERVAL_MSEC 50
+
+struct sdl_display {
+ SDL_Surface *display;
+ SDL_TimerID refresh;
+ unsigned width;
+ unsigned height;
+ unsigned scale;
+};
+
+static Uint32 sdl_redraw_callback(Uint32 interval, void *param) {
+ struct display *display = (struct display*)param;
+
+ display->draw(display);
+
+ return interval;
+}
+
+void
+sdl_init(struct display *display,
+ unsigned width, unsigned height, unsigned scale)
+{
+ if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) {
+ printf("Failed to initialize SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ atexit(&SDL_Quit);
+
+ struct sdl_display *priv = SDL_PRIV(display);
+
+ priv->width = width;
+ priv->height = height;
+ priv->scale = scale;
+
+ priv->display = SDL_SetVideoMode(width * scale, height * scale, 32, 0);
+ if(!priv->display) {
+ printf("Failed to set SDL video mode: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ priv->refresh = SDL_AddTimer(REFRESH_INTERVAL_MSEC,
+ &sdl_redraw_callback, display);
+ if(!priv->refresh) {
+ printf("Failed to add refresh timer: %s\n", SDL_GetError());
+ exit(1);
+ }
+}
+
+void
+sdl_draw(struct display *display)
+{
+ struct sdl_display *priv = SDL_PRIV(display);
+
+ struct image *img = display->fbuf;
+
+ SDL_Rect r;
+
+ r.w = priv->scale;
+ r.h = priv->scale;
+
+ if(img->type == PXTYPE_RGB444) {
+ unsigned stride = img->size.w * 2;
+
+ unsigned x, y;
+ for(y = 0; y < img->size.h; y++) {
+ for(x = 0; x < img->size.w; x++) {
+ px_t color = image_get_pixel(img, x, y);
+
+ r.x = x * priv->scale;
+ r.y = y * priv->scale;
+
+ SDL_FillRect(priv->display, &r, color);
+ }
+ }
+ } else {
+ puts("Unsupported framebuffer type for SDL emulator.");
+ exit(1);
+ }
+
+ SDL_UpdateRect(priv->display, 0, 0, 0, 0);
+}
+
+
+static struct sdl_display display_sdl_priv;
+
+uint8_t sdl_fbuf[96*64*2];
+
+struct image display_sdl_fbuf = {
+ .type = PXTYPE_RGB444,
+ .size = {96, 64},
+ .data = &sdl_fbuf
+};
+
+struct display display_sdl = {
+ .name = "Main Display",
+ .fbuf = &display_sdl_fbuf,
+ .priv = &display_sdl_priv,
+ .draw = &sdl_draw
+};
+
+uint16_t fnord_buf[] = {
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF,
+ 0x0F00,
+ 0x00F0,
+ 0x000F,
+ 0x00FF
+
+};
+
+struct image fnord = {
+ .type = PXTYPE_RGB444,
+ .size = {8,4},
+ .data = &fnord_buf
+};
+
+uint8_t fubar_img[] = {
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x0f
+
+};
+
+struct image fubar = {
+ .type = PXTYPE_MONO_V8,
+ .size = {8,16},
+ .data = &fubar_img
+};
+
+void
+sdl_run(void)
+{
+ int r;
+ SDL_Event e;
+
+ while((r = SDL_WaitEvent(&e))) {
+
+ if(e.type == SDL_KEYDOWN) {
+ if(e.key.keysym.sym == SDLK_ESCAPE) {
+ puts("Bloody quitter!");
+ break;
+ }
+ if(e.key.keysym.sym == SDLK_SPACE) {
+ pxposn_t dp = {0,0};
+ pxposn_t sp = {0,0};
+ pxdims_t d = {8,4};
+
+ image_blit(&display_sdl_fbuf, dp,
+ &fnord, sp,
+ d);
+
+ sp.x = 0;
+ sp.y = 0;
+ dp.x = 5;
+ dp.y = 10;
+ d.w = 8;
+ d.h = 16;
+
+ image_blit(&display_sdl_fbuf, dp,
+ &fubar, sp,
+ d);
+
+ pxrect_t r = {{12,0},{40,20}};
+ image_fill_rect(&display_sdl_fbuf,
+ r,
+ 0xFF00FF);
+
+
+#if 0
+ dp.x = 0;
+ dp.y = 0;
+
+ image_draw_string(&display_sdl_fbuf, dp,
+ "ABCDEFGHI");
+
+ dp.y += 10;
+
+ image_draw_string(&display_sdl_fbuf, dp,
+ "abcdefghi");
+
+#endif
+
+ sdl_draw(&display_sdl);
+
+ }
+ }
+
+ switch(e.type) {
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ printf("Key %d %d\n", e.key.keysym.sym, e.key.state);
+ break;
+ }
+ }
+
+ if(!r) {
+ printf("Failed to wait for SDL event: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+}
+
+int
+main(void)
+{
+ sdl_init(&display_sdl, 96, 64, 4);
+
+ sdl_run();
+
+ return 0;
+}
diff --git a/src/target/ui-experiment/sdl.h b/src/target/ui-experiment/sdl.h
new file mode 100644
index 00000000..5a3d4752
--- /dev/null
+++ b/src/target/ui-experiment/sdl.h
@@ -0,0 +1,9 @@
+
+#ifndef _UI_SDL_H
+#define _UI_SDL_H
+
+#include <ui/display.h>
+
+extern struct display display_sdl;
+
+#endif
diff --git a/src/target/ui-experiment/ui.h b/src/target/ui-experiment/ui.h
new file mode 100644
index 00000000..9bc7c324
--- /dev/null
+++ b/src/target/ui-experiment/ui.h
@@ -0,0 +1,81 @@
+
+/****** MESSAGING MENU ******/
+
+struct menu menu_message_compose = {
+ .title = "Compose",
+ .help = "Write a new text message."
+};
+
+struct menu menu_message_inbox = {
+ .title = "Inbox",
+ .help = "Incoming text messages"
+};
+
+struct menu menu_message_outbox = {
+ .title = "Outbox",
+ .help = "Outgoing text messages"
+};
+
+struct menu menu_message_sent = {
+ .title = "Sent",
+ .help = "Previously sent text messages"
+};
+
+struct menu menu_messages = {
+ .title = "Messages",
+ .help = "Short message service options",
+ .children = {
+ [0] = &menu_message_compose,
+ [1] = &menu_message_inbox,
+ [2] = &menu_message_outbox,
+ [3] = &menu_message_sent
+ }
+};
+
+/****** NETWORK MENU ******/
+
+struct menu menu_network_about = {
+ .title = "About this network",
+ .help = "Information about your current network",
+};
+
+struct menu menu_network = {
+ .title = "Network",
+ .help = "Network interaction options",
+ .children = {
+ }
+};
+
+/****** SETTINGS MENU ******/
+
+struct menu menu_settings = {
+ .title = "Settings",
+ .help = "Configure your phone",
+ .children = {
+ }
+};
+
+/****** MAIN MENU ******/
+
+struct menu menu_about = {
+ .title = "About",
+ .help = "Information about this phone",
+};
+
+struct menu menu_main = {
+ .title = "Main Menu",
+ .children = {
+ [0] = &menu_messages,
+ [7] = &menu_network,
+ [8] = &menu_settings,
+ [9] = &menu_about,
+ },
+};
+
+
+
+int
+main(void) {
+ &menu_main;
+ return 0;
+};