diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/target/ui-experiment/README | 11 | ||||
-rw-r--r-- | src/target/ui-experiment/display.h | 46 | ||||
-rw-r--r-- | src/target/ui-experiment/font.h | bin | 0 -> 61204 bytes | |||
-rw-r--r-- | src/target/ui-experiment/image.h | 166 | ||||
-rw-r--r-- | src/target/ui-experiment/menu.h | 18 | ||||
-rw-r--r-- | src/target/ui-experiment/pixel.h | 113 | ||||
-rw-r--r-- | src/target/ui-experiment/png2tiny.c | 51 | ||||
-rw-r--r-- | src/target/ui-experiment/screen.h | 21 | ||||
-rw-r--r-- | src/target/ui-experiment/sdl.c | 250 | ||||
-rw-r--r-- | src/target/ui-experiment/sdl.h | 9 | ||||
-rw-r--r-- | src/target/ui-experiment/ui.h | 81 |
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 Binary files differnew file mode 100644 index 00000000..5dc6eae7 --- /dev/null +++ b/src/target/ui-experiment/font.h 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; +}; |