ui: Some experimental code snippets i cobbled together several months ago.

This commit is contained in:
Ingo Albrecht 2010-09-01 21:24:34 +02:00
parent 1198f35283
commit dcc3a41036
11 changed files with 766 additions and 0 deletions

View File

@ -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.

View File

@ -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

Binary file not shown.

View File

@ -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

View File

@ -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];
};

View File

@ -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

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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;
}

View File

@ -0,0 +1,9 @@
#ifndef _UI_SDL_H
#define _UI_SDL_H
#include <ui/display.h>
extern struct display display_sdl;
#endif

View File

@ -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;
};