first commit

This commit is contained in:
cyy_mac
2026-06-16 08:44:45 +08:00
commit 938e3e04ac
57 changed files with 2396 additions and 0 deletions

19
Makefile Normal file
View File

@@ -0,0 +1,19 @@
CC = arm-linux-gcc
CFLAGS = -Wall -O2 -I .
SRCS = main.c \
desktop/desktop.c album/album.c ir_app/ir_app.c button/button.c \
common/device.c common/tools.c
TARGET = mini_desktop
.PHONY: all static clean
static: $(TARGET)
@file $(TARGET)
$(TARGET): $(SRCS)
$(CC) $(CFLAGS) -static -o $@ $(SRCS)
clean:
rm -f $(TARGET) test/*_static

42
README.md Normal file
View File

@@ -0,0 +1,42 @@
# mini_desktop — GEC6818 Desktop System
800×480 framebuffer + touch desktop with photo album and IR remote control.
## Architecture
```
desktop → album (swipe photo browser)
→ ir_app (learn / send / receive IR codes)
```
## Non-blocking I/O pattern
All I/O uses `O_NONBLOCK` + drain-events-into-cache. Never block on `read()`.
Touch events are drained per-frame via `ts_read()` — after the `while(ts_read > 0)`
loop exits (no more events), remaining logic (serial polling, etc.) runs once,
then the outer loop repeats. This keeps the UI responsive to both touch and serial.
### Do NOT let `ts_read()` return 1 with stale data
`ts_read()` uses a `static int has_new` flag. It MUST be reset to `0` at the top
of every call. If you forget this, `has_new` sticks at `1` after the first touch,
every `ts_read()` call returns 1 forever, and `while (ts_read(...) > 0)` loops
spin endlessly — **code after the loop is never reached**. This bit us when
`page_learn()` could never read from the IR serial port.
## Build
```bash
docker run --platform linux/amd64 --rm \
-v /path/to/toolchain:/opt/gec6818-toolchain:ro \
-v /path/to/mini_desktop:/work -w /work \
gec6818-dev:ubuntu18 bash -c 'make static'
```
## Deploy
```bash
ssh root@192.168.1.88 'killall mini_desktop 2>/dev/null; sleep 1'
scp -O mini_desktop root@192.168.1.88:/home/cyy/
```

60
album/album.c Normal file
View File

@@ -0,0 +1,60 @@
#include "album.h"
#include "../common/device.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static unsigned int *g_pixels[ALBUM_MAX];
static int g_w[ALBUM_MAX], g_h[ALBUM_MAX];
static int g_idx = 0;
static int g_count = 0;
void album_init(const char *paths[], int count)
{
int i;
for (i = 0; i < count && i < ALBUM_MAX; i++)
{
int w, h;
unsigned int *p = bmp_load(paths[i], &w, &h);
if (p)
{
g_pixels[i] = p;
g_w[i] = w;
g_h[i] = h;
printf("Album[%d]: %s (%dx%d)\n", i, paths[i], w, h);
}
else
{
g_pixels[i] = NULL;
printf("Album[%d]: FAILED to load %s\n", i, paths[i]);
}
}
g_count = count;
g_idx = 0;
}
void album_show(int idx)
{
if (idx < 0 || idx >= g_count) return;
if (g_pixels[idx] == NULL) return;
g_idx = idx;
bmp_display(g_pixels[idx], g_w[idx], g_h[idx]);
}
void album_prev(void)
{
if (g_count == 0) return;
g_idx = (g_idx > 0) ? g_idx - 1 : g_count - 1;
album_show(g_idx);
}
void album_next(void)
{
if (g_count == 0) return;
g_idx = (g_idx + 1 < g_count) ? g_idx + 1 : 0;
album_show(g_idx);
}
int album_current(void) { return g_idx; }
int album_count(void) { return g_count; }

13
album/album.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef ALBUM_H
#define ALBUM_H
#define ALBUM_MAX 10
void album_init(const char *paths[], int count);
void album_show(int idx);
void album_prev(void);
void album_next(void);
int album_current(void);
int album_count(void);
#endif

151
button/button.c Normal file
View File

@@ -0,0 +1,151 @@
#include "button.h"
#include "../common/device.h"
#include "../fonts/font_16x16.h"
#include <stddef.h>
void button_init(button_t *b, int cx, int cy, int w, int h,
unsigned int bg, unsigned int border)
{
b->cx = cx;
b->cy = cy;
b->half_w = w / 2;
b->half_h = h / 2;
b->bg_color = bg;
b->border_color = border;
b->visible = 1;
b->learned = 0;
b->filled = 1;
b->corner_radius = 0;
b->label = NULL;
b->label_color = 0;
b->bg_bmp = NULL;
b->bg_bmp_w = 0;
b->bg_bmp_h = 0;
}
void button_init_rounded(button_t *b, int cx, int cy, int w, int h,
unsigned int bg, unsigned int border, int cr)
{
button_init(b, cx, cy, w, h, bg, border);
b->corner_radius = cr;
}
void button_draw(button_t *b)
{
if (!b->visible) return;
int x0 = b->cx - b->half_w;
int y0 = b->cy - b->half_h;
int w = b->half_w * 2;
int h = b->half_h * 2;
int cr = b->corner_radius;
/* BMP image background (pre-rendered Apple-style button) */
if (b->bg_bmp)
{
int bx = b->cx - b->bg_bmp_w / 2;
int by = b->cy - b->bg_bmp_h / 2;
show_bmp(b->bg_bmp, b->bg_bmp_w, b->bg_bmp_h, bx, by);
/* skip regular fill/border — fall through to label + learned dot */
}
else if (cr > 0)
{
/* Rounded rect: border (full size) then fill (inset by 2px) */
if (b->filled)
{
if (b->border_color != b->bg_color)
{
fb_fill_rounded(x0, y0, w, h, cr, b->border_color);
fb_fill_rounded(x0 + 2, y0 + 2, w - 4, h - 4,
cr > 2 ? cr - 2 : 0, b->bg_color);
}
else
{
fb_fill_rounded(x0, y0, w, h, cr, b->bg_color);
}
}
}
else
{
/* Original sharp-rectangle path */
if (b->filled)
fb_fill(x0, y0, w, h, b->bg_color);
int i;
for (i = 0; i < 2; i++)
{
fb_fill(x0 + i, y0, w - i * 2, 1, b->border_color);
fb_fill(x0 + i, y0 + h - 1, w - i * 2, 1, b->border_color);
}
for (i = 0; i < 2; i++)
{
fb_fill(x0, y0 + i, 1, h - i * 2, b->border_color);
fb_fill(x0 + w - 1, y0 + i, 1, h - i * 2, b->border_color);
}
}
/* Label */
if (b->label && b->label[0])
{
int len = 0;
const char *p;
for (p = b->label; *p; p++) len++;
int avail_w = w - 8;
int avail_h = h - 6;
int target_dh = avail_h;
if (target_dh > 22) target_dh = 22;
int target_dw = target_dh;
int total_w = target_dw * len;
if (total_w > avail_w)
{
target_dw = avail_w / len;
target_dh = target_dw;
}
int start_x = b->cx - (target_dw * len) / 2;
int start_y = b->cy - target_dh / 2;
show_string(font_16x16, FONT_FW, FONT_FH,
b->label, start_x, start_y, target_dw, target_dh,
b->label_color);
}
/* Learned dot */
if (b->learned)
{
int dx = x0 + w - 10;
int dy = y0 + 8;
int r = 4;
int x, y;
for (y = dy - r; y <= dy + r; y++)
{
for (x = dx - r; x <= dx + r; x++)
{
if ((x - dx) * (x - dx) + (y - dy) * (y - dy) <= r * r)
{
fb_put(x, y, RGB(0, 255, 0));
}
}
}
}
}
int button_hit(button_t *b, int sx, int sy)
{
if (!b->visible) return 0;
return (sx >= b->cx - b->half_w && sx <= b->cx + b->half_w &&
sy >= b->cy - b->half_h && sy <= b->cy + b->half_h);
}
void button_set_learned(button_t *b, int on)
{
b->learned = on;
}
void button_set_filled(button_t *b, int filled)
{
b->filled = filled;
}

29
button/button.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef BUTTON_H
#define BUTTON_H
typedef struct
{
int cx, cy;
int half_w, half_h;
unsigned int bg_color;
unsigned int border_color;
int visible;
int learned;
int filled;
int corner_radius;
const char *label;
unsigned int label_color;
unsigned int *bg_bmp;
int bg_bmp_w, bg_bmp_h;
} button_t;
void button_init(button_t *b, int cx, int cy, int w, int h,
unsigned int bg, unsigned int border);
void button_init_rounded(button_t *b, int cx, int cy, int w, int h,
unsigned int bg, unsigned int border, int cr);
void button_draw(button_t *b);
int button_hit(button_t *b, int sx, int sy);
void button_set_learned(button_t *b, int on);
void button_set_filled(button_t *b, int filled);
#endif

613
common/device.c Normal file
View File

@@ -0,0 +1,613 @@
#include "device.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fb.h>
#include <errno.h>
#include <linux/input.h>
/* ================================================================
* Unified init / deinit
* ================================================================ */
void device_init(void)
{
if (fb_open() < 0) exit(1);
if (ts_open() < 0) exit(1);
beep_open();
led_open();
}
void device_deinit(void)
{
led_close();
beep_close();
ts_close();
fb_close();
}
/* ================================================================
* Globals
* ================================================================ */
static int fb_fd = -1;
static int fb_w = 0;
static int fb_h = 0;
static int fb_line = 0;
static long fb_size = 0;
static char *fb_map = NULL;
static int ts_fd = -1;
static int beep_fd = -1;
/* ================================================================
* Framebuffer (/dev/fb0)
* ================================================================ */
int fb_open(void)
{
fb_fd = open("/dev/fb0", O_RDWR);
if (fb_fd < 0)
{
perror("open /dev/fb0");
return -1;
}
struct fb_var_screeninfo vi;
struct fb_fix_screeninfo fi;
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vi);
ioctl(fb_fd, FBIOGET_FSCREENINFO, &fi);
vi.yoffset = 0;
ioctl(fb_fd, FBIOPAN_DISPLAY, &vi);
fb_w = vi.xres;
fb_h = vi.yres;
fb_line = fi.line_length;
fb_size = fi.smem_len;
fb_map = (char *)mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
if (fb_map == MAP_FAILED)
{
perror("mmap");
close(fb_fd);
fb_fd = -1;
return -1;
}
memset(fb_map, 0, fb_size);
return 0;
}
void fb_close(void)
{
if (fb_map)
{
memset(fb_map, 0, fb_size);
munmap(fb_map, fb_size);
fb_map = NULL;
}
if (fb_fd >= 0)
{
close(fb_fd);
fb_fd = -1;
}
}
void fb_reset(void)
{
if (fb_fd < 0) return;
struct fb_var_screeninfo vi;
struct fb_fix_screeninfo fi;
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vi);
ioctl(fb_fd, FBIOGET_FSCREENINFO, &fi);
vi.yoffset = 0;
ioctl(fb_fd, FBIOPAN_DISPLAY, &vi);
fb_w = vi.xres;
fb_h = vi.yres;
fb_line = fi.line_length;
fb_size = fi.smem_len;
memset(fb_map, 0, fb_size);
}
void fb_put(int x, int y, unsigned int color)
{
if (x < 0 || x >= fb_w || y < 0 || y >= fb_h)
return;
unsigned int off = y * fb_line + x * 4;
*(unsigned int *)(fb_map + off) = color;
}
void fb_fill(int x, int y, int w, int h, unsigned int color)
{
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x + w > fb_w) w = fb_w - x;
if (y + h > fb_h) h = fb_h - y;
if (w <= 0 || h <= 0) return;
int row, col;
for (row = 0; row < h; row++)
{
unsigned int off = (y + row) * fb_line + x * 4;
for (col = 0; col < w; col++)
*(unsigned int *)(fb_map + off + col * 4) = color;
}
}
void fb_fill_rounded(int x, int y, int w, int h, int r, unsigned int color)
{
if (r <= 0) { fb_fill(x, y, w, h, color); return; }
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* Center + four edge rectangles */
fb_fill(x + r, y, w - 2 * r, h, color);
fb_fill(x, y + r, r, h - 2 * r, color);
fb_fill(x + w - r, y + r, r, h - 2 * r, color);
/* Four corner quarter-circles */
int dx, dy, rr = r * r;
for (dy = 0; dy < r; dy++)
{
for (dx = 0; dx < r; dx++)
{
if (dx * dx + dy * dy > rr) continue;
int cx_tl = x + r - 1 - dx; int cy_tl = y + r - 1 - dy;
fb_put(cx_tl, cy_tl, color); /* top-left */
fb_put(x + w - r + dx, cy_tl, color); /* top-right */
fb_put(cx_tl, y + h - r + dy, color); /* bottom-left */
fb_put(x + w - r + dx, y + h - r + dy, color); /* bottom-right */
}
}
}
void fb_clear(void)
{
if (fb_map)
memset(fb_map, 0, fb_size);
}
int fb_get_w(void) { return fb_w; }
int fb_get_h(void) { return fb_h; }
/* ================================================================
* Touchscreen (/dev/input/event0)
* ================================================================ */
int ts_open(void)
{
ts_fd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK);
if (ts_fd < 0)
{
perror("open /dev/input/event0");
return -1;
}
return 0;
}
void ts_close(void)
{
if (ts_fd >= 0)
{
close(ts_fd);
ts_fd = -1;
}
}
void ts_raw_to_screen(int raw_x, int raw_y, int *sx, int *sy)
{
*sx = raw_x * SCREEN_W / TS_RAW_W;
*sy = (raw_y - TS_Y_OFF) * SCREEN_H / (TS_RAW_H - TS_Y_OFF);
if (*sx < 0) *sx = 0;
if (*sx >= SCREEN_W) *sx = SCREEN_W - 1;
if (*sy < 0) *sy = 0;
if (*sy >= SCREEN_H) *sy = SCREEN_H - 1;
}
int ts_read(int *sx, int *sy, int *touching)
{
struct input_event ev;
static int raw_x = 0;
static int raw_y = 0;
static int down = 0;
static int has_new = 0;
has_new = 0;
while (read(ts_fd, &ev, sizeof(ev)) == sizeof(ev))
{
if (ev.type == EV_ABS)
{
if (ev.code == ABS_X) raw_x = ev.value;
if (ev.code == ABS_Y) raw_y = ev.value;
}
if (ev.type == EV_KEY && ev.code == BTN_TOUCH)
{
down = ev.value;
ts_raw_to_screen(raw_x, raw_y, sx, sy);
if (down)
{
*touching = 1;
has_new = 1;
}
else
{
*touching = 0;
has_new = 1;
}
if (has_new) return 1;
}
if (ev.type == EV_SYN && ev.code == SYN_REPORT && down)
{
ts_raw_to_screen(raw_x, raw_y, sx, sy);
*touching = 1;
has_new = 1;
if (has_new) return 1;
}
}
return has_new;
}
int get_touch_dir(void)
{
static int start_x = -1, start_y = -1;
static int cur_x = 0, cur_y = 0;
struct input_event ev;
static int raw_x, raw_y;
while (ts_fd >= 0)
{
int n = read(ts_fd, &ev, sizeof(ev));
if (n != sizeof(ev))
{
if (n < 0 && errno == EAGAIN) break;
break;
}
if (ev.type == EV_ABS)
{
if (ev.code == ABS_X) raw_x = ev.value;
if (ev.code == ABS_Y) raw_y = ev.value;
}
if (ev.type == EV_KEY && ev.code == BTN_TOUCH)
{
if (ev.value == 1)
{
ts_raw_to_screen(raw_x, raw_y, &start_x, &start_y);
cur_x = start_x;
cur_y = start_y;
}
else
{
ts_raw_to_screen(raw_x, raw_y, &cur_x, &cur_y);
int dx = cur_x - start_x;
int dy = cur_y - start_y;
int ax = abs(dx);
int ay = abs(dy);
start_x = -1;
if (ax < SWIPE_MIN_DIST && ay < SWIPE_MIN_DIST)
return 0;
if (ax > ay * 2)
return dx > 0 ? 4 : 3; /* RIGHT=4 : LEFT=3 */
if (ay > ax * 2)
return dy > 0 ? 2 : 1; /* DOWN=2 : UP=1 */
return 0;
}
}
if (ev.type == EV_SYN && ev.code == SYN_REPORT && start_x >= 0)
{
ts_raw_to_screen(raw_x, raw_y, &cur_x, &cur_y);
}
}
return 0;
}
/* ================================================================
* BMP loader
* ================================================================ */
unsigned int *bmp_load(const char *filename, int *w, int *h)
{
int fd = open(filename, O_RDONLY);
if (fd < 0)
{
printf("bmp_load: cannot open %s\n", filename);
return NULL;
}
struct stat st;
fstat(fd, &st);
char *raw = malloc(st.st_size);
read(fd, raw, st.st_size);
close(fd);
int data_off = *(int *)(raw + 10);
int bmp_w = *(int *)(raw + 18);
int bmp_h = abs(*(int *)(raw + 22));
int bpp = *(short *)(raw + 28);
int row_size = ((bmp_w * bpp + 31) / 32) * 4;
printf("bmp_load: %dx%d @ %dbpp\n", bmp_w, bmp_h, bpp);
unsigned int *pixels = malloc(bmp_w * bmp_h * 4);
int y;
for (y = 0; y < bmp_h; y++)
{
int bmp_y = bmp_h - 1 - y;
unsigned char *row = (unsigned char *)(raw + data_off + bmp_y * row_size);
int x;
for (x = 0; x < bmp_w; x++)
{
int stride = (bpp == 32) ? 4 : 3;
unsigned char b = row[x * stride + 0];
unsigned char g = row[x * stride + 1];
unsigned char r = row[x * stride + 2];
unsigned char a = (bpp == 32) ? row[x * stride + 3] : 255;
pixels[y * bmp_w + x] = ((unsigned int)a << 24) | RGB(r, g, b);
}
}
free(raw);
*w = bmp_w;
*h = bmp_h;
return pixels;
}
void show_bmp(unsigned int *pixels, int w, int h, int x0, int y0)
{
int y, x;
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
int sx = x0 + x;
int sy = y0 + y;
unsigned int c = pixels[y * w + x];
if ((c >> 24) == 0) continue; /* skip fully transparent */
if (sx >= 0 && sx < fb_w && sy >= 0 && sy < fb_h)
{
fb_put(sx, sy, c & 0x00FFFFFF);
}
}
}
}
void bmp_display(unsigned int *pixels, int w, int h)
{
show_bmp(pixels, w, h, (fb_w - w) / 2, (fb_h - h) / 2);
}
unsigned int *bmp_reshape(unsigned int *src, int sw, int sh, int dw, int dh)
{
unsigned int *dst = malloc(dw * dh * 4);
int y, x;
for (y = 0; y < dh; y++)
{
int sy = y * sh / dh;
for (x = 0; x < dw; x++)
{
int sx = x * sw / dw;
dst[y * dw + x] = src[sy * sw + sx];
}
}
return dst;
}
/* ================================================================
* Font rendering (monochrome bitmap, 8 pixels per byte)
* ================================================================ */
void show_char(const unsigned char *font, int idx, int fw, int fh,
int x0, int y0, unsigned int color)
{
int bytes_per_col = (fh + 7) / 8;
int char_bytes = bytes_per_col * fw;
const unsigned char *data = font + idx * char_bytes;
int row, col;
for (row = 0; row < fh; row++)
{
for (col = 0; col < fw; col++)
{
int byte_idx = col * bytes_per_col + row / 8;
int bit_idx = row % 8;
if (data[byte_idx] & (1 << bit_idx))
{
fb_put(x0 + col, y0 + row, color);
}
}
}
}
void show_char_scale(const unsigned char *font, int idx, int fw, int fh,
int x0, int y0, int dw, int dh, unsigned int color)
{
int bytes_per_col = (fh + 7) / 8;
int char_bytes = bytes_per_col * fw;
const unsigned char *data = font + idx * char_bytes;
int row, col;
for (row = 0; row < dh; row++)
{
int src_row = row * fh / dh;
for (col = 0; col < dw; col++)
{
int src_col = col * fw / dw;
int byte_idx = src_col * bytes_per_col + src_row / 8;
int bit_idx = src_row % 8;
if (data[byte_idx] & (1 << bit_idx))
{
fb_put(x0 + col, y0 + row, color);
}
}
}
}
void show_number(const unsigned char *num_font, int stride, int num,
int fw, int fh, int x0, int y0, int dw, int dh,
unsigned int color)
{
char buf[16];
sprintf(buf, "%d", num);
int i;
for (i = 0; buf[i]; i++)
{
int d = buf[i] - '0';
show_char_scale(num_font + d * stride, 0, fw, fh,
x0 + i * dw, y0, dw, dh, color);
}
}
static int ascii_to_glyph(char c)
{
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
if (c >= '0' && c <= '9') return c - '0' + 26;
return -1;
}
void show_string(const unsigned char *font, int fw, int fh,
const char *str, int x0, int y0, int dw, int dh,
unsigned int color)
{
if (!str || !font) return;
int i;
for (i = 0; str[i]; i++)
{
int idx = ascii_to_glyph(str[i]);
if (idx >= 0)
show_char_scale(font, idx, fw, fh, x0 + i * dw, y0, dw, dh, color);
}
}
/* ================================================================
* Buzzer (sysfs: /sys/kernel/gec_ctrl/beep, expects 4-byte write)
* ================================================================ */
int beep_open(void)
{
beep_fd = open("/sys/kernel/gec_ctrl/beep", O_WRONLY);
if (beep_fd < 0)
{
perror("open /sys/kernel/gec_ctrl/beep");
return -1;
}
return 0;
}
void beep_close(void)
{
if (beep_fd >= 0)
{
beep_off();
close(beep_fd);
beep_fd = -1;
}
}
void beep_on(void)
{
if (beep_fd < 0) return;
int v = 1;
lseek(beep_fd, 0, SEEK_SET);
write(beep_fd, &v, sizeof(v));
}
void beep_off(void)
{
if (beep_fd < 0) return;
int v = 0;
lseek(beep_fd, 0, SEEK_SET);
write(beep_fd, &v, sizeof(v));
}
void beep_freq(int hz)
{
(void)hz;
beep_on();
}
void beep_note(int hz, int ms)
{
(void)hz;
beep_on();
usleep(ms * 1000);
beep_off();
usleep(20 * 1000);
}
/* ================================================================
* LED (standard Linux LED class: /sys/class/leds/ledN/brightness)
* ================================================================ */
static int led_fds[6]; /* 0=all, 1-5 = led1..led5 */
static const char *led_path(int n)
{
static char buf[64];
if (n <= 0) snprintf(buf, sizeof(buf), "/sys/class/leds/led1/brightness");
else snprintf(buf, sizeof(buf), "/sys/class/leds/led%d/brightness", n);
return buf;
}
int led_open(void)
{
int i;
for (i = 1; i <= 5; i++)
{
led_fds[i] = open(led_path(i), O_WRONLY);
if (led_fds[i] < 0) perror(led_path(i));
}
return led_fds[1] >= 0 ? 0 : -1;
}
void led_close(void)
{
int i;
for (i = 1; i <= 5; i++)
{
if (led_fds[i] >= 0) { close(led_fds[i]); led_fds[i] = -1; }
}
}
void led_on(int n)
{
int i;
if (n <= 0 || n > 5)
{
for (i = 1; i <= 5; i++) led_on(i);
return;
}
if (led_fds[n] < 0) return;
lseek(led_fds[n], 0, SEEK_SET);
write(led_fds[n], "1", 1);
}
void led_off(int n)
{
int i;
if (n <= 0 || n > 5)
{
for (i = 1; i <= 5; i++) led_off(i);
return;
}
if (led_fds[n] < 0) return;
lseek(led_fds[n], 0, SEEK_SET);
write(led_fds[n], "0", 1);
}

71
common/device.h Normal file
View File

@@ -0,0 +1,71 @@
#ifndef DEVICE_H
#define DEVICE_H
#include "param.h"
/* ================================================================
* Unified init / deinit
* ================================================================ */
void device_init(void);
void device_deinit(void);
/* ================================================================
* Framebuffer (/dev/fb0)
* ================================================================ */
int fb_open(void);
void fb_close(void);
void fb_reset(void);
void fb_put(int x, int y, unsigned int color);
void fb_fill(int x, int y, int w, int h, unsigned int color);
void fb_fill_rounded(int x, int y, int w, int h, int r, unsigned int color);
void fb_clear(void);
int fb_get_w(void);
int fb_get_h(void);
/* ================================================================
* Touchscreen (/dev/input/event0)
* ================================================================ */
int ts_open(void);
void ts_close(void);
void ts_raw_to_screen(int raw_x, int raw_y, int *sx, int *sy);
int ts_read(int *sx, int *sy, int *touching);
int get_touch_dir(void);
/* ================================================================
* BMP loader
* ================================================================ */
unsigned int *bmp_load(const char *filename, int *w, int *h);
void bmp_display(unsigned int *pixels, int w, int h);
void show_bmp(unsigned int *pixels, int w, int h, int x0, int y0);
unsigned int *bmp_reshape(unsigned int *src, int sw, int sh, int dw, int dh);
void show_char(const unsigned char *font, int idx, int fw, int fh,
int x0, int y0, unsigned int color);
void show_char_scale(const unsigned char *font, int idx, int fw, int fh,
int x0, int y0, int dw, int dh, unsigned int color);
void show_number(const unsigned char *num_font, int stride, int num,
int fw, int fh, int x0, int y0, int dw, int dh,
unsigned int color);
void show_string(const unsigned char *font, int fw, int fh,
const char *str, int x0, int y0, int dw, int dh,
unsigned int color);
/* ================================================================
* Buzzer (/dev/beep)
* ================================================================ */
int beep_open(void);
void beep_close(void);
void beep_on(void);
void beep_off(void);
void beep_freq(int hz);
void beep_note(int hz, int ms);
/* ================================================================
* LED (sysfs: /sys/kernel/gec_ctrl/led_*)
* ================================================================ */
int led_open(void);
void led_close(void);
void led_on(int n);
void led_off(int n);
/* n: 1-5 individual, <=0 all */
#endif

22
common/param.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef PARAM_H
#define PARAM_H
/* --- Screen --- */
#define SCREEN_W 800
#define SCREEN_H 480
/* --- Touch calibration (GEC6818 gslX680) --- */
#define TS_RAW_W 1024
#define TS_RAW_H 600
#define TS_Y_OFF 95
/* --- Buzzer ioctl magic --- */
#define BEEP_IOCTL_CMD 2
/* --- Swipe threshold (pixels) --- */
#define SWIPE_MIN_DIST 30
/* --- Color helpers (GEC6818 32bpp: byte2=R, byte1=G, byte0=B) --- */
#define RGB(r, g, b) (((unsigned int)(r) << 16) | ((unsigned int)(g) << 8) | (unsigned int)(b))
#endif

97
common/tools.c Normal file
View File

@@ -0,0 +1,97 @@
#include "device.h"
#include <stdlib.h>
/* --- Circle outline (midpoint algorithm) --- */
void draw_circle(int cx, int cy, int r, unsigned int color)
{
int x = 0;
int y = r;
int d = 1 - r;
while (x <= y)
{
fb_put(cx + x, cy + y, color);
fb_put(cx + y, cy + x, color);
fb_put(cx - x, cy + y, color);
fb_put(cx - y, cy + x, color);
fb_put(cx + x, cy - y, color);
fb_put(cx + y, cy - x, color);
fb_put(cx - x, cy - y, color);
fb_put(cx - y, cy - x, color);
if (d < 0)
d += 2 * x + 3;
else
{
d += 2 * (x - y) + 5;
y--;
}
x++;
}
}
/* --- Filled circle --- */
void draw_circle_fill(int cx, int cy, int r, unsigned int color)
{
int x = 0;
int y = r;
int d = 1 - r;
while (x <= y)
{
int i;
for (i = cx - x; i <= cx + x; i++) fb_put(i, cy - y, color);
for (i = cx - y; i <= cx + y; i++) fb_put(i, cy - x, color);
for (i = cx - x; i <= cx + x; i++) fb_put(i, cy + y, color);
for (i = cx - y; i <= cx + y; i++) fb_put(i, cy + x, color);
if (d < 0)
d += 2 * x + 3;
else
{
d += 2 * (x - y) + 5;
y--;
}
x++;
}
}
/* --- Rectangle outline --- */
void draw_rect(int x, int y, int w, int h, unsigned int color)
{
int i;
for (i = x; i < x + w; i++) { fb_put(i, y, color); fb_put(i, y + h - 1, color); }
for (i = y; i < y + h; i++) { fb_put(x, i, color); fb_put(x + w - 1, i, color); }
}
/* --- Filled rectangle --- */
void draw_rect_fill(int x, int y, int w, int h, unsigned int color)
{
fb_fill(x, y, w, h, color);
}
/* --- Crosshair --- */
void draw_cross(int cx, int cy, int sz, unsigned int color)
{
int i;
for (i = cx - sz; i <= cx + sz; i++) fb_put(i, cy, color);
for (i = cy - sz; i <= cy + sz; i++) fb_put(cx, i, color);
}
/* --- Line (Bresenham) --- */
void draw_line(int x0, int y0, int x1, int y1, unsigned int color)
{
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2;
int x = x0, y = y0;
while (1)
{
fb_put(x, y, color);
if (x == x1 && y == y1) break;
e2 = 2 * err;
if (e2 >= dy) { err += dy; x += sx; }
if (e2 <= dx) { err += dx; y += sy; }
}
}

13
common/tools.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef TOOLS_H
#define TOOLS_H
/* Drawing utilities (depend on device.h for fb_put) */
void draw_circle(int cx, int cy, int r, unsigned int color);
void draw_circle_fill(int cx, int cy, int r, unsigned int color);
void draw_rect(int x, int y, int w, int h, unsigned int color);
void draw_rect_fill(int x, int y, int w, int h, unsigned int color);
void draw_cross(int cx, int cy, int sz, unsigned int color);
void draw_line(int x0, int y0, int x1, int y1, unsigned int color);
#endif

326
desktop/desktop.c Normal file
View File

@@ -0,0 +1,326 @@
#include "desktop.h"
#include "../common/device.h"
#include "../button/button.h"
#include "../album/album.h"
#include "../ir_app/ir_app.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CLR_BG RGB(28, 28, 30)
#define CLR_SURFACE RGB(44, 44, 46)
#define CLR_BLUE RGB(0, 122, 255)
#define CLR_GREEN RGB(52, 199, 89)
#define CLR_RED RGB(255, 59, 48)
#define CLR_GRAY RGB(142, 142, 147)
#define CLR_WHITE RGB(255, 255, 255)
#define TOP_H 44
static unsigned int *g_bg = NULL;
static int g_bg_w, g_bg_h;
static unsigned int *g_icon_photo = NULL;
static int g_icon_photo_w, g_icon_photo_h;
static unsigned int *g_icon_game = NULL;
static int g_icon_game_w, g_icon_game_h;
static unsigned int *g_icon_ir = NULL;
static int g_icon_ir_w, g_icon_ir_h;
static button_t g_photo_btn;
static button_t g_game_btn;
static button_t g_ir_btn;
static int g_selected = -1; /* -1=none, 0=photo, 1=game, 2=ir */
void desktop_init(void)
{
g_bg = bmp_load("icon/background.bmp", &g_bg_w, &g_bg_h);
g_icon_photo = bmp_load("icon/photo_icon.bmp", &g_icon_photo_w, &g_icon_photo_h);
g_icon_game = bmp_load("icon/game_icon.bmp", &g_icon_game_w, &g_icon_game_h);
g_icon_ir = bmp_load("icon/ir_icon.bmp", &g_icon_ir_w, &g_icon_ir_h);
button_init_rounded(&g_photo_btn, 160, 260, 100, 100, CLR_BG, CLR_GRAY, 12);
button_set_filled(&g_photo_btn, 0);
button_init_rounded(&g_game_btn, 400, 260, 100, 100, CLR_BG, CLR_GRAY, 12);
button_set_filled(&g_game_btn, 0);
button_init_rounded(&g_ir_btn, 640, 260, 100, 100, CLR_BG, CLR_GRAY, 12);
button_set_filled(&g_ir_btn, 0);
printf("Desktop init OK\n");
}
static void draw_sel_box(button_t *btn)
{
int x0 = btn->cx - btn->half_w - 4;
int y0 = btn->cy - btn->half_h - 4;
int w = btn->half_w * 2 + 8;
int h = btn->half_h * 2 + 8;
int t = 3;
fb_fill(x0, y0, w, t, CLR_WHITE);
fb_fill(x0, y0 + h - t, w, t, CLR_WHITE);
fb_fill(x0, y0, t, h, CLR_WHITE);
fb_fill(x0 + w - t, y0, t, h, CLR_WHITE);
}
static void draw_desktop(void)
{
if (g_bg)
bmp_display(g_bg, g_bg_w, g_bg_h);
else
fb_fill(0, 0, 800, 480, CLR_BG);
if (g_icon_photo)
{
int x = g_photo_btn.cx - g_icon_photo_w / 2;
int y = g_photo_btn.cy - g_icon_photo_h / 2;
show_bmp(g_icon_photo, g_icon_photo_w, g_icon_photo_h, x, y);
}
if (g_icon_game)
{
int x = g_game_btn.cx - g_icon_game_w / 2;
int y = g_game_btn.cy - g_icon_game_h / 2;
show_bmp(g_icon_game, g_icon_game_w, g_icon_game_h, x, y);
}
if (g_icon_ir)
{
int x = g_ir_btn.cx - g_icon_ir_w / 2;
int y = g_ir_btn.cy - g_icon_ir_h / 2;
show_bmp(g_icon_ir, g_icon_ir_w, g_icon_ir_h, x, y);
}
if (g_selected == 0) draw_sel_box(&g_photo_btn);
if (g_selected == 1) draw_sel_box(&g_game_btn);
if (g_selected == 2) draw_sel_box(&g_ir_btn);
}
/* Wait for finger to lift, then drain any stray IR */
static void wait_finger_up(void)
{
int _sx, _sy, _t = 1;
while (_t) { while (ts_read(&_sx, &_sy, &_t) > 0) {} if (_t) usleep(20000); }
}
/* Drain touch + IR after returning from a sub-app */
static void drain_both(void)
{
int _sx, _sy, _t = 1;
while (_t) { while (ts_read(&_sx, &_sy, &_t) > 0) {} if (_t) usleep(20000); }
ir_flush();
}
/* Handle remote action in desktop context. Returns 1 if action consumed. */
static int desktop_remote(int action)
{
switch (action)
{
case 2: /* LEFT */
if (g_selected < 0) g_selected = 2;
else if (g_selected > 0) g_selected--;
draw_desktop();
return 1;
case 3: /* RIGHT */
if (g_selected < 0) g_selected = 0;
else if (g_selected < 2) g_selected++;
draw_desktop();
return 1;
case 4: /* CENTER — no finger on remote, launch directly */
if (g_selected < 0) return 1;
if (g_selected == 0) return 2; /* album */
else if (g_selected == 1) return 4; /* game */
else return 3; /* ir */
}
return 1;
}
void desktop_run(void)
{
draw_desktop();
printf("Desktop: touch Photo(left) or IR(right) icon\n");
const char *photos[] = {
"images/photo_1.bmp",
"images/photo_2.bmp",
"images/photo_3.bmp",
};
int sx, sy, touching;
int swipe_start_x = -1, swipe_start_y = -1;
int swipe_tracking = 0;
while (1)
{
while (ts_read(&sx, &sy, &touching) > 0)
{
if (touching)
{
if (!swipe_tracking)
{
swipe_start_x = sx;
swipe_start_y = sy;
swipe_tracking = 1;
}
if (button_hit(&g_photo_btn, sx, sy))
{
g_selected = 0;
printf("→ Album\n");
wait_finger_up();
album_init(photos, 3);
album_show(0);
int in_album = 1;
int a_start_x = -1, a_start_y = -1, a_tracking = 0;
while (in_album)
{
/* Poll IR for album remote control */
int ract;
while (ir_poll(&ract))
{
if (ract == 2) album_next();
else if (ract == 3) album_prev();
else if (ract == 0) { in_album = 0; break; }
usleep(50000);
}
int asx, asy, at;
while (ts_read(&asx, &asy, &at) > 0)
{
if (at)
{
if (!a_tracking)
{
a_start_x = asx; a_start_y = asy;
a_tracking = 1;
}
if (asy < TOP_H)
{
in_album = 0;
break;
}
}
else
{
if (a_tracking && a_start_x >= 0)
{
int dx = asx - a_start_x;
int dy = asy - a_start_y;
if (abs(dx) > abs(dy) && abs(dx) > 30)
{
if (dx < 0) album_next();
else album_prev();
}
}
a_tracking = 0;
a_start_x = -1;
}
}
/* Check IR again after touch drain */
if (ir_poll(&ract))
{
if (ract == 2) album_next();
else if (ract == 3) album_prev();
else if (ract == 0) { in_album = 0; break; }
}
}
drain_both();
draw_desktop();
swipe_tracking = 0;
}
if (button_hit(&g_game_btn, sx, sy))
{
g_selected = 1;
printf("→ Game\n");
wait_finger_up();
system("./raycast_game_static");
fb_reset();
drain_both();
draw_desktop();
}
if (button_hit(&g_ir_btn, sx, sy))
{
g_selected = 2;
printf("→ IR\n");
wait_finger_up();
ir_app_run();
drain_both();
draw_desktop();
}
}
else
{
if (swipe_tracking && swipe_start_x >= 0)
{
int dx = sx - swipe_start_x;
int dy = sy - swipe_start_y;
if (abs(dx) > abs(dy) && abs(dx) > 30)
printf("Desktop swipe: %s\n", dx > 0 ? "RIGHT" : "LEFT");
}
swipe_tracking = 0;
swipe_start_x = -1;
}
}
/* Background IR polling on desktop */
int ract;
while (ir_poll(&ract))
{
int ret = desktop_remote(ract);
if (ret == 2)
{
/* Launch album via remote */
album_init(photos, 3);
album_show(0);
int in_album = 1;
while (in_album)
{
int ract2;
while (ir_poll(&ract2))
{
if (ract2 == 2) album_next();
else if (ract2 == 3) album_prev();
else if (ract2 == 0) { in_album = 0; break; }
usleep(50000);
}
int asx, asy, at;
while (ts_read(&asx, &asy, &at) > 0)
{
if (at && asy < TOP_H) { in_album = 0; break; }
if (!at) continue;
}
if (ir_poll(&ract2))
{
if (ract2 == 2) album_next();
else if (ract2 == 3) album_prev();
else if (ract2 == 0) { in_album = 0; break; }
}
}
drain_both();
draw_desktop();
swipe_tracking = 0;
}
else if (ret == 3)
{
/* Launch IR app via remote */
ir_app_run();
drain_both();
draw_desktop();
swipe_tracking = 0;
}
else if (ret == 4)
{
/* Launch game via remote */
printf("→ Game (remote)\n");
system("./raycast_game_static");
fb_reset();
drain_both();
draw_desktop();
swipe_tracking = 0;
}
}
}
}

7
desktop/desktop.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef DESKTOP_H
#define DESKTOP_H
void desktop_init(void);
void desktop_run(void);
#endif

229
fonts/font_16x16.h Normal file
View File

@@ -0,0 +1,229 @@
#ifndef FONT_16X16_H
#define FONT_16X16_H
#define FONT_FW 16
#define FONT_FH 16
#define FONT_GLYPH_BYTES ((FONT_FH + 7) / 8 * FONT_FW) /* 32 */
/* 36 glyphs: A-Z (0-25) then 0-9 (26-35) */
/* Column-major, 2 bytes/col, LSB=top row */
static const unsigned char font_16x16[36 * FONT_GLYPH_BYTES] = {
/* [0] 'A' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x18,0x00,0x07,0xE0,0x04,0x1C,0x04,
0x1C,0x04,0xE0,0x04,0x00,0x07,0x00,0x18,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [1] 'B' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x84,0x10,0x84,0x10,
0x84,0x10,0x84,0x10,0x78,0x0F,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [2] 'C' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xF0,0x07,0x08,0x08,0x04,0x10,
0x04,0x10,0x04,0x10,0x04,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [3] 'D' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x04,0x10,0x04,0x10,
0x04,0x10,0x08,0x08,0xF0,0x07,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [4] 'E' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x84,0x10,0x84,0x10,
0x84,0x10,0x84,0x10,0x84,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [5] 'F' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x84,0x00,0x84,0x00,
0x84,0x00,0x84,0x00,0x84,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [6] 'G' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xF0,0x07,0x08,0x08,0x04,0x10,
0x04,0x10,0x04,0x11,0x04,0x1F,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [7] 'H' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x80,0x00,0x80,0x00,
0x80,0x00,0x80,0x00,0xFC,0x1F,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [8] 'I' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x04,0x10,0x04,0x10,0xFC,0x1F,
0x04,0x10,0x04,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [9] 'J' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x00,0x10,0x04,0x10,
0x04,0x10,0xFC,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [10] 'K' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x80,0x00,0x40,0x01,
0x30,0x02,0x08,0x04,0x04,0x08,0x00,0x10,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [11] 'L' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFC,0x1F,0x00,0x10,
0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [12] 'M' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x38,0x00,0xC0,0x01,
0x00,0x06,0xC0,0x01,0x38,0x00,0xFC,0x1F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [13] 'N' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x18,0x00,0xE0,0x00,
0x00,0x03,0x00,0x0C,0xFC,0x1F,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [14] 'O' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xF0,0x07,0x08,0x08,0x04,0x10,
0x04,0x10,0x08,0x08,0xF0,0x07,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [15] 'P' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x04,0x01,0x04,0x01,
0x04,0x01,0x84,0x00,0x78,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [16] 'Q' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xF8,0x03,0x04,0x04,0x02,0x08,
0x02,0x38,0x04,0x44,0xF8,0x43,0x00,0x40,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [17] 'R' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x1F,0x84,0x00,0x84,0x01,
0x44,0x06,0x38,0x18,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [18] 'S' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x38,0x10,0x44,0x10,0x84,0x10,
0x84,0x10,0x04,0x11,0x00,0x0E,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [19] 'T' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x00,0x04,0x00,0x04,0x00,0xFC,0x1F,
0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [20] 'U' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFC,0x0F,0x00,0x10,0x00,0x10,
0x00,0x10,0x00,0x10,0xFC,0x0F,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [21] 'V' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x1C,0x00,0xE0,0x00,0x00,0x07,
0x00,0x18,0x00,0x07,0xE0,0x00,0x1C,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [22] 'W' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFC,0x00,0x00,0x1F,0xE0,0x01,0x1C,0x00,
0xE0,0x01,0x00,0x1F,0xFC,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [23] 'X' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x04,0x18,0x18,0x06,0xE0,0x01,
0xE0,0x01,0x18,0x06,0x04,0x18,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [24] 'Y' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x0C,0x00,0x10,0x00,0x60,0x00,
0x80,0x1F,0x60,0x00,0x10,0x00,0x0C,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [25] 'Z' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x04,0x18,0x04,0x16,0x04,0x11,
0xC4,0x10,0x34,0x10,0x0C,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [26] '0' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xF0,0x07,0x08,0x0A,0x04,0x11,0x84,0x10,
0x44,0x10,0x28,0x08,0xF0,0x07,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [27] '1' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x10,0x10,0x08,0x10,0x04,0x10,
0xFC,0x1F,0x00,0x10,0x00,0x10,0x00,0x10,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [28] '2' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x08,0x1C,0x04,0x12,0x04,0x11,
0x84,0x10,0x44,0x10,0x38,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [29] '3' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x04,0x10,0x84,0x10,0x84,0x10,
0xC4,0x10,0x38,0x09,0x00,0x06,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [30] '4' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x03,0x80,0x02,0x60,0x02,0x10,0x02,
0x08,0x02,0xFC,0x1F,0x00,0x02,0x00,0x02,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [31] '5' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x7C,0x10,0x44,0x10,0x44,0x10,
0x44,0x10,0x84,0x08,0x04,0x07,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [32] '6' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xF0,0x07,0x88,0x08,0x44,0x10,0x44,0x10,
0x44,0x10,0x44,0x08,0x80,0x07,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [33] '7' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x04,0x00,0x04,0x1E,0x84,0x01,
0x64,0x00,0x14,0x00,0x0C,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [34] '8' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x38,0x0E,0x44,0x11,0x84,0x10,
0x84,0x10,0x44,0x11,0x38,0x0E,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* [35] '9' */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xF0,0x00,0x08,0x11,0x04,0x11,0x04,0x11,
0x04,0x11,0x88,0x0C,0xF0,0x03,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
#endif

BIN
icon/background.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
icon/background.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
icon/bar_top.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
icon/bg_page.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
icon/btn_blue.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
icon/btn_dir.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icon/btn_gray.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
icon/btn_green.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
icon/btn_pill.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icon/btn_red.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
icon/game_icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
icon/image copy 2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB

BIN
icon/image copy 3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
icon/image copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
icon/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

BIN
icon/ir_control_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
icon/ir_icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icon/ir_learn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
icon/ir_send_control.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
icon/photo_icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icon/photo_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
images/photo_1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/photo_2.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/photo_3.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

468
ir_app/ir_app.c Normal file
View File

@@ -0,0 +1,468 @@
#include "ir_app.h"
#include "../common/device.h"
#include "../button/button.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#define IR_HEAD1 0xA1
#define IR_HEAD2 0xF1
/* ── Apple-style dark palette ── */
#define CLR_BG RGB(28, 28, 30) /* system background */
#define CLR_SURFACE RGB(44, 44, 46) /* card / surface */
#define CLR_BLUE RGB(0, 122, 255) /* accent blue */
#define CLR_GREEN RGB(52, 199, 89) /* system green */
#define CLR_RED RGB(255, 59, 48) /* system red */
#define CLR_GRAY RGB(142, 142, 147) /* secondary text */
#define CLR_SEP RGB(56, 56, 58) /* separator */
#define CLR_DOT RGB(48, 209, 88) /* learned dot */
#define TOP_H 44 /* nav bar height */
/* ── Static data ── */
static int ir_fd = -1;
static button_t g_btns[5]; /* up/down/left/right/center */
static button_t g_menu[4]; /* Send Recv Learn Back */
static button_t g_mode_btn; /* learn-page SEND↔RECV */
static unsigned char g_send_cmds[5][3];
static unsigned char g_recv_cmds[5][3];
static int g_send_learned[5];
static int g_recv_learned[5];
/* Pre-rendered Apple-style assets */
static unsigned int *g_bmp_page_bg = NULL;
static int g_bmp_page_bg_w, g_bmp_page_bg_h;
static unsigned int *g_bmp_bar = NULL;
static int g_bmp_bar_w, g_bmp_bar_h;
/* ── Serial ── */
static int ir_serial_open(void)
{
int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) { perror("ttySAC1"); return -1; }
struct termios opt;
tcgetattr(fd, &opt);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~PARENB; opt.c_cflag &= ~CSTOPB; opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opt.c_iflag &= ~(IXON | IXOFF | IXANY);
opt.c_oflag &= ~OPOST;
opt.c_cc[VMIN] = 0; opt.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &opt);
return fd;
}
static void ir_save_cmd(int idx, const unsigned char *data, int is_recv)
{
char name[32];
snprintf(name, sizeof(name), is_recv ? "ir_recv_%d.txt" : "ir_send_%d.txt", idx);
FILE *f = fopen(name, "w");
if (f == NULL) return;
fprintf(f, "%02X %02X %02X\n", data[0], data[1], data[2]);
fclose(f);
if (is_recv)
{
g_recv_cmds[idx][0] = data[0]; g_recv_cmds[idx][1] = data[1]; g_recv_cmds[idx][2] = data[2];
g_recv_learned[idx] = 1;
}
else
{
g_send_cmds[idx][0] = data[0]; g_send_cmds[idx][1] = data[1]; g_send_cmds[idx][2] = data[2];
g_send_learned[idx] = 1;
}
}
static int ir_load_cmd(int idx, unsigned char *data, int is_recv)
{
char name[32];
snprintf(name, sizeof(name), is_recv ? "ir_recv_%d.txt" : "ir_send_%d.txt", idx);
FILE *f = fopen(name, "r");
if (f == NULL) return 0;
unsigned int v[3];
int n = fscanf(f, "%02X %02X %02X", &v[0], &v[1], &v[2]);
fclose(f);
if (n == 3) { data[0] = v[0]; data[1] = v[1]; data[2] = v[2]; return 1; }
return 0;
}
static void ir_send(unsigned char d0, unsigned char d1, unsigned char d2)
{
unsigned char pkt[5] = {IR_HEAD1, IR_HEAD2, d0, d1, d2};
write(ir_fd, pkt, 5);
}
static unsigned char ir_buf[3];
static int ir_buf_idx = 0;
static int ir_poll_3bytes(unsigned char *out)
{
if (ir_fd < 0) return 0;
int n = read(ir_fd, ir_buf + ir_buf_idx, 3 - ir_buf_idx);
if (n < 0 && errno != EAGAIN)
printf(" ir: read error %d\n", errno);
else if (n > 0)
{
printf(" ir: got %d byte(s)\n", n);
ir_buf_idx += n;
if (ir_buf_idx == 3)
{
memcpy(out, ir_buf, 3);
ir_buf_idx = 0;
return 1;
}
}
return 0;
}
static int recv_match(const unsigned char *code)
{
int i;
for (i = 0; i < 5; i++)
{
if (!g_recv_learned[i]) continue;
if (code[0] == g_recv_cmds[i][0] &&
code[1] == g_recv_cmds[i][1] &&
code[2] == g_recv_cmds[i][2])
return i;
}
return -1;
}
/* ── Drawing helpers ── */
static void draw_top_bar(const char *title, unsigned int color)
{
if (g_bmp_bar)
show_bmp(g_bmp_bar, g_bmp_bar_w, g_bmp_bar_h, 0, 0);
else
fb_fill(0, 0, 800, TOP_H, color);
printf("=== %s ===\n", title);
}
static void draw_page_bg(void)
{
if (g_bmp_page_bg)
show_bmp(g_bmp_page_bg, g_bmp_page_bg_w, g_bmp_page_bg_h, 0, 0);
else
fb_fill(0, 0, 800, 480, CLR_BG);
}
static void draw_dir_buttons(int *learned)
{
int i;
for (i = 0; i < 5; i++)
button_set_learned(&g_btns[i], learned[i]);
for (i = 0; i < 5; i++)
button_draw(&g_btns[i]);
}
/* ── Pages ── */
static void page_send(void)
{
draw_page_bg();
draw_top_bar("Send", CLR_BLUE);
draw_dir_buttons(g_send_learned);
int sx, sy, t, btn_ok = 0;
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) return;
if (!t) { btn_ok = 0; continue; }
if (btn_ok) continue;
int i;
for (i = 0; i < 5; i++)
{
if (button_hit(&g_btns[i], sx, sy) && g_send_learned[i])
{
ir_send(g_send_cmds[i][0], g_send_cmds[i][1], g_send_cmds[i][2]);
printf("IR TX: %02X %02X %02X\n",
g_send_cmds[i][0], g_send_cmds[i][1], g_send_cmds[i][2]);
beep_on(); usleep(50000); beep_off();
btn_ok = 1;
}
}
}
}
}
static void page_learn(void)
{
static int is_recv = 0;
draw_top_bar(is_recv ? "Learn · Receive" : "Learn · Send", CLR_GREEN);
draw_page_bg();
button_draw(&g_mode_btn);
int i;
int *learned = is_recv ? g_recv_learned : g_send_learned;
draw_dir_buttons(learned);
int learn_idx = -1, btn_handled = 0;
int sx, sy, t;
ir_buf_idx = 0;
int lfd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
{ struct termios o; tcgetattr(lfd, &o);
cfsetispeed(&o, B9600); cfsetospeed(&o, B9600);
o.c_cflag |= (CLOCAL | CREAD);
o.c_cflag &= ~PARENB; o.c_cflag &= ~CSTOPB; o.c_cflag &= ~CSIZE;
o.c_cflag |= CS8;
o.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
o.c_iflag &= ~(IXON | IXOFF | IXANY);
o.c_oflag &= ~OPOST;
o.c_cc[VMIN] = 0; o.c_cc[VTIME] = 0;
tcsetattr(lfd, TCSANOW, &o); }
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) { close(lfd); return; }
if (!t) { btn_handled = 0; continue; }
if (btn_handled) continue;
if (button_hit(&g_mode_btn, sx, sy))
{
btn_handled = 1;
is_recv = !is_recv;
g_mode_btn.label = is_recv ? "RECV" : "SEND";
draw_top_bar(is_recv ? "Learn · Receive" : "Learn · Send", CLR_GREEN);
button_draw(&g_mode_btn);
learned = is_recv ? g_recv_learned : g_send_learned;
draw_dir_buttons(learned);
printf("Learn mode: %s\n", is_recv ? "RECV" : "SEND");
continue;
}
for (i = 0; i < 5; i++)
{
if (button_hit(&g_btns[i], sx, sy))
{
learn_idx = i;
ir_buf_idx = 0;
btn_handled = 1;
printf("Learn(%s): waiting IR for btn %d...\n",
is_recv ? "RECV" : "SEND", i);
break;
}
}
}
if (learn_idx >= 0)
{
int n = read(lfd, ir_buf + ir_buf_idx, 3 - ir_buf_idx);
if (n > 0)
{
printf(" ir: got %d byte(s)\n", n);
ir_buf_idx += n;
if (ir_buf_idx == 3)
{
ir_save_cmd(learn_idx, ir_buf, is_recv);
printf(" Learned %s btn %d: %02X %02X %02X\n",
is_recv ? "RECV" : "SEND", learn_idx,
ir_buf[0], ir_buf[1], ir_buf[2]);
learned = is_recv ? g_recv_learned : g_send_learned;
button_set_learned(&g_btns[learn_idx], learned[learn_idx]);
button_draw(&g_btns[learn_idx]);
beep_on(); usleep(100000); beep_off();
learn_idx = -1;
ir_buf_idx = 0;
}
}
}
}
}
static void page_recv(void)
{
draw_page_bg();
draw_top_bar("Receive", CLR_RED);
/* Draw 4 rounded indicator boxes on the left */
int i, y0;
for (i = 0; i < 5; i++)
{
y0 = 70 + i * 90;
unsigned int c = g_recv_learned[i] ? CLR_GREEN : CLR_SEP;
fb_fill_rounded(10, y0, 80, 60, 8, c);
/* Subtle border */
fb_fill(10, y0, 80, 1, CLR_SURFACE);
fb_fill(10, y0 + 59, 80, 1, CLR_SURFACE);
}
int sx, sy, t;
while (read(ir_fd, ir_buf, 3) > 0) {}
ir_buf_idx = 0;
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) return;
}
unsigned char buf[3];
if (ir_poll_3bytes(buf))
{
printf("IR RX: %02X %02X %02X\n", buf[0], buf[1], buf[2]);
beep_on(); usleep(80000); beep_off(); usleep(40000);
beep_on(); usleep(80000); beep_off();
int m = recv_match(buf);
if (m >= 0)
{
printf(" → matched recv[%d]\n", m);
y0 = 70 + m * 90;
fb_fill_rounded(10, y0, 80, 60, 8, RGB(0, 255, 0));
beep_on(); usleep(200000); beep_off();
fb_fill_rounded(10, y0, 80, 60, 8, CLR_GREEN);
switch (m)
{
case 0: led_on(1); break;
case 1: led_on(2); break;
case 2: led_on(0); break;
case 3: led_off(0); break;
}
}
}
}
}
static void show_menu(void)
{
draw_page_bg();
draw_top_bar("Infrared", CLR_BG);
int i;
for (i = 0; i < 4; i++)
button_draw(&g_menu[i]);
printf("[Send] [Recv] [Learn] [Back]\n");
}
/* ── Public ── */
int ir_poll(int *action)
{
unsigned char buf[3];
if (!ir_poll_3bytes(buf)) return 0;
int m = recv_match(buf);
if (m < 0) return 0;
if (action) *action = m;
printf("IR remote: btn[%d] %02X%02X%02X\n", m, buf[0], buf[1], buf[2]);
return 1;
}
void ir_flush(void)
{
unsigned char drain[64];
while (read(ir_fd, drain, sizeof(drain)) > 0) {}
ir_buf_idx = 0;
}
void ir_app_init(void)
{
ir_fd = ir_serial_open();
int i;
for (i = 0; i < 5; i++)
{
unsigned char cmd[3];
if (ir_load_cmd(i, cmd, 0))
{
g_send_cmds[i][0] = cmd[0]; g_send_cmds[i][1] = cmd[1]; g_send_cmds[i][2] = cmd[2];
g_send_learned[i] = 1;
}
else g_send_learned[i] = 0;
if (ir_load_cmd(i, cmd, 1))
{
g_recv_cmds[i][0] = cmd[0]; g_recv_cmds[i][1] = cmd[1]; g_recv_cmds[i][2] = cmd[2];
g_recv_learned[i] = 1;
}
else g_recv_learned[i] = 0;
}
/* Load Apple-style BMP assets */
g_bmp_page_bg = bmp_load("icon/bg_page.bmp", &g_bmp_page_bg_w, &g_bmp_page_bg_h);
g_bmp_bar = bmp_load("icon/bar_top.bmp", &g_bmp_bar_w, &g_bmp_bar_h);
/* Direction buttons — cross layout, rounded */
button_init_rounded(&g_btns[0], 400, 150, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[0].label = "UP"; g_btns[0].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[1], 400, 330, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[1].label = "DN"; g_btns[1].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[2], 300, 240, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[2].label = "LT"; g_btns[2].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[3], 500, 240, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[3].label = "RT"; g_btns[3].label_color = RGB(255, 255, 255);
/* Center button — circle */
button_init_rounded(&g_btns[4], 400, 240, 60, 60, CLR_SURFACE, CLR_SEP, 30);
g_btns[4].label = "OK"; g_btns[4].label_color = RGB(255, 255, 255);
/* Mode toggle — pill shape at bottom */
button_init_rounded(&g_mode_btn, 400, 430, 160, 38, CLR_SEP, CLR_GRAY, 19);
g_mode_btn.label = "SEND"; g_mode_btn.label_color = RGB(255, 255, 255);
/* Menu buttons — rounded, accent colors, evenly spaced */
button_init_rounded(&g_menu[0], 160, 260, 130, 90, CLR_BLUE, CLR_BLUE, 14);
g_menu[0].label = "SEND"; g_menu[0].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[1], 320, 260, 130, 90, CLR_RED, CLR_RED, 14);
g_menu[1].label = "RECV"; g_menu[1].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[2], 480, 260, 130, 90, CLR_GREEN, CLR_GREEN, 14);
g_menu[2].label = "LEARN"; g_menu[2].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[3], 640, 260, 130, 90, CLR_GRAY, CLR_GRAY, 14);
g_menu[3].label = "BACK"; g_menu[3].label_color = RGB(255, 255, 255);
printf("IR app init OK\n");
}
void ir_app_run(void)
{
int sx, sy, t;
/* Flush any residual touch before showing menu */
{
int _t = 1;
while (_t)
{
while (ts_read(&sx, &sy, &_t) > 0) {}
if (_t) usleep(20000);
}
usleep(50000);
while (ts_read(&sx, &sy, &_t) > 0) {}
}
while (1)
{
show_menu();
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (!t) continue;
printf(" touch at (%d,%d)\n", sx, sy);
if (button_hit(&g_menu[0], sx, sy))
{ page_send(); show_menu(); break; }
if (button_hit(&g_menu[1], sx, sy))
{ page_recv(); show_menu(); break; }
if (button_hit(&g_menu[2], sx, sy))
{ page_learn(); show_menu(); break; }
if (button_hit(&g_menu[3], sx, sy))
{ printf("IR exit\n"); return; }
}
}
}
}

9
ir_app/ir_app.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef IR_APP_H
#define IR_APP_H
void ir_app_init(void);
void ir_app_run(void);
int ir_poll(int *action);
void ir_flush(void);
#endif

13
main.c Normal file
View File

@@ -0,0 +1,13 @@
#include "common/device.h"
#include "desktop/desktop.h"
#include "ir_app/ir_app.h"
int main(void)
{
device_init();
ir_app_init();
desktop_init();
desktop_run();
device_deinit();
return 0;
}

BIN
mini_desktop Executable file

Binary file not shown.

20
test/Makefile Normal file
View File

@@ -0,0 +1,20 @@
CC = arm-linux-gcc
CFLAGS = -Wall -O2 -I ..
COMMON = ../common/device.c ../common/tools.c
.PHONY: all clean
all: test_swipe_static test_image_static test_album_static
test_swipe_static: test_swipe.c ../button/button.c $(COMMON)
$(CC) $(CFLAGS) -static -o $@ test_swipe.c ../button/button.c $(COMMON)
test_image_static: test_image.c ../album/album.c $(COMMON)
$(CC) $(CFLAGS) -static -o $@ test_image.c ../album/album.c $(COMMON)
test_album_static: test_album.c ../desktop/desktop.c ../album/album.c ../button/button.c $(COMMON)
$(CC) $(CFLAGS) -static -o $@ test_album.c ../desktop/desktop.c ../album/album.c ../button/button.c $(COMMON)
clean:
rm -f *_static

12
test/test_album.c Normal file
View File

@@ -0,0 +1,12 @@
#include "../common/device.h"
#include "../desktop/desktop.h"
#include <stdio.h>
int main(void)
{
device_init();
desktop_init();
desktop_run();
device_deinit();
return 0;
}

BIN
test/test_album_static Executable file

Binary file not shown.

45
test/test_image.c Normal file
View File

@@ -0,0 +1,45 @@
#include "../common/device.h"
#include "../album/album.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
device_init();
fb_clear();
const char *photos[] = {
"images/photo_1.bmp",
"images/photo_2.bmp",
"images/photo_3.bmp",
};
int photo_count = 3;
album_init(photos, photo_count);
album_show(0);
printf("=== Photo Album Test ===\n");
printf("Swipe LEFT = next, RIGHT = prev\n");
printf("Q to quit.\n\n");
int running = 1;
while (running)
{
int dir = get_touch_dir();
switch (dir)
{
case 3:
printf("LEFT → next photo\n");
album_next();
break;
case 4:
printf("RIGHT → prev photo\n");
album_prev();
break;
}
}
device_deinit();
return 0;
}

BIN
test/test_image_static Executable file

Binary file not shown.

58
test/test_ir.c Normal file
View File

@@ -0,0 +1,58 @@
#include "../common/device.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
int main(void)
{
device_init();
int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
struct termios opt;
tcgetattr(fd, &opt);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~PARENB; opt.c_cflag &= ~CSTOPB; opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opt.c_iflag &= ~(IXON | IXOFF | IXANY);
opt.c_oflag &= ~OPOST;
opt.c_cc[VMIN] = 0; opt.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &opt);
printf("=== IR Raw Test ===\n");
printf("Press IR remote. Ctrl+C to exit.\n");
unsigned char buf[3];
int idx = 0;
while (1)
{
int n = read(fd, buf + idx, 3 - idx);
if (n > 0)
{
printf("read %d bytes: ", n);
int i;
for (i = 0; i < n; i++)
printf("%02X ", buf[idx + i]);
printf("\n");
idx += n;
if (idx == 3)
{
printf(" → GOT 3: %02X %02X %02X\n", buf[0], buf[1], buf[2]);
beep_on(); usleep(100000); beep_off();
idx = 0;
}
}
}
close(fd);
device_deinit();
return 0;
}

BIN
test/test_ir_static Executable file

Binary file not shown.

12
test/test_ir_ui.c Normal file
View File

@@ -0,0 +1,12 @@
#include "../common/device.h"
#include "../ir_app/ir_app.h"
#include <stdio.h>
int main(void)
{
device_init();
ir_app_init();
ir_app_run();
device_deinit();
return 0;
}

67
test/test_swipe.c Normal file
View File

@@ -0,0 +1,67 @@
#include "../common/device.h"
#include "../button/button.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
device_init();
fb_clear();
/* Create 2 test buttons */
button_t btn1, btn2;
button_init(&btn1, 200, 300, 160, 80, RGB(255, 255, 255), RGB(0, 0, 0));
button_init(&btn2, 600, 300, 160, 80, RGB(255, 255, 255), RGB(0, 0, 0));
printf("=== Swipe + Button Test ===\n");
printf("Left button = learn toggle, Right button = quit\n");
printf("Swipe left/right to change button color.\n\n");
int running = 1;
while (running)
{
fb_clear();
/* Check swipe */
int dir = get_touch_dir();
switch (dir)
{
case 3:
printf("LEFT swipe\n");
btn1.bg_color = RGB(255, 200, 200);
break;
case 4:
printf("RIGHT swipe\n");
btn1.bg_color = RGB(200, 200, 255);
break;
case 0:
break;
default:
btn1.bg_color = RGB(255, 255, 255);
}
/* Check touch for buttons via ts_read */
int sx, sy, touching;
while (ts_read(&sx, &sy, &touching) > 0)
{
if (touching && button_hit(&btn1, sx, sy))
{
printf("BTN1 pressed\n");
button_set_learned(&btn1, !btn1.learned);
}
if (touching && button_hit(&btn2, sx, sy))
{
printf("BTN2 pressed → quit\n");
running = 0;
}
}
btn1.bg_color = btn1.learned ? RGB(200, 255, 200) : btn1.bg_color;
button_draw(&btn1);
button_draw(&btn2);
}
device_deinit();
return 0;
}

BIN
test/test_swipe_static Executable file

Binary file not shown.