commit c9d2b90a0bb14c3b0a3aff469e8456e2dd4fbed9 Author: cyy_mac Date: Tue Jun 16 08:44:45 2026 +0800 first commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9b9e8ec --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b1118b --- /dev/null +++ b/README.md @@ -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/ +``` diff --git a/album/album.c b/album/album.c new file mode 100644 index 0000000..f2b9c18 --- /dev/null +++ b/album/album.c @@ -0,0 +1,60 @@ +#include "album.h" +#include "../common/device.h" +#include +#include +#include + +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; } diff --git a/album/album.h b/album/album.h new file mode 100644 index 0000000..b2e4bd6 --- /dev/null +++ b/album/album.h @@ -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 diff --git a/button/button.c b/button/button.c new file mode 100644 index 0000000..55d3cbe --- /dev/null +++ b/button/button.c @@ -0,0 +1,151 @@ +#include "button.h" +#include "../common/device.h" +#include "../fonts/font_16x16.h" +#include + +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; +} diff --git a/button/button.h b/button/button.h new file mode 100644 index 0000000..81236d1 --- /dev/null +++ b/button/button.h @@ -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 diff --git a/common/device.c b/common/device.c new file mode 100644 index 0000000..2c191a5 --- /dev/null +++ b/common/device.c @@ -0,0 +1,613 @@ +#include "device.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ================================================================ + * 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); +} diff --git a/common/device.h b/common/device.h new file mode 100644 index 0000000..6247e81 --- /dev/null +++ b/common/device.h @@ -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 diff --git a/common/param.h b/common/param.h new file mode 100644 index 0000000..f0f6628 --- /dev/null +++ b/common/param.h @@ -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 diff --git a/common/tools.c b/common/tools.c new file mode 100644 index 0000000..c8e3d24 --- /dev/null +++ b/common/tools.c @@ -0,0 +1,97 @@ +#include "device.h" +#include + +/* --- 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; } + } +} diff --git a/common/tools.h b/common/tools.h new file mode 100644 index 0000000..2329ded --- /dev/null +++ b/common/tools.h @@ -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 diff --git a/desktop/desktop.c b/desktop/desktop.c new file mode 100644 index 0000000..d4d0b03 --- /dev/null +++ b/desktop/desktop.c @@ -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 +#include +#include +#include + +#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; + } + } + } +} diff --git a/desktop/desktop.h b/desktop/desktop.h new file mode 100644 index 0000000..636c34c --- /dev/null +++ b/desktop/desktop.h @@ -0,0 +1,7 @@ +#ifndef DESKTOP_H +#define DESKTOP_H + +void desktop_init(void); +void desktop_run(void); + +#endif diff --git a/fonts/font_16x16.h b/fonts/font_16x16.h new file mode 100644 index 0000000..5ee5439 --- /dev/null +++ b/fonts/font_16x16.h @@ -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 \ No newline at end of file diff --git a/icon/background.bmp b/icon/background.bmp new file mode 100644 index 0000000..328d11b Binary files /dev/null and b/icon/background.bmp differ diff --git a/icon/bar_top.bmp b/icon/bar_top.bmp new file mode 100644 index 0000000..eed9af8 Binary files /dev/null and b/icon/bar_top.bmp differ diff --git a/icon/bg_page.bmp b/icon/bg_page.bmp new file mode 100644 index 0000000..6b39cbd Binary files /dev/null and b/icon/bg_page.bmp differ diff --git a/icon/game_icon.bmp b/icon/game_icon.bmp new file mode 100644 index 0000000..5f4f5c0 Binary files /dev/null and b/icon/game_icon.bmp differ diff --git a/icon/ir_icon.bmp b/icon/ir_icon.bmp new file mode 100644 index 0000000..b614a6f Binary files /dev/null and b/icon/ir_icon.bmp differ diff --git a/icon/photo_icon.bmp b/icon/photo_icon.bmp new file mode 100644 index 0000000..4a6462b Binary files /dev/null and b/icon/photo_icon.bmp differ diff --git a/images/photo_1.bmp b/images/photo_1.bmp new file mode 100644 index 0000000..2bac257 Binary files /dev/null and b/images/photo_1.bmp differ diff --git a/images/photo_2.bmp b/images/photo_2.bmp new file mode 100644 index 0000000..76cf02e Binary files /dev/null and b/images/photo_2.bmp differ diff --git a/images/photo_3.bmp b/images/photo_3.bmp new file mode 100644 index 0000000..f8b4168 Binary files /dev/null and b/images/photo_3.bmp differ diff --git a/ir_app/ir_app.c b/ir_app/ir_app.c new file mode 100644 index 0000000..36d73fa --- /dev/null +++ b/ir_app/ir_app.c @@ -0,0 +1,468 @@ +#include "ir_app.h" +#include "../common/device.h" +#include "../button/button.h" +#include +#include +#include +#include +#include +#include +#include + +#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; } + } + } + } +} diff --git a/ir_app/ir_app.h b/ir_app/ir_app.h new file mode 100644 index 0000000..19f4f78 --- /dev/null +++ b/ir_app/ir_app.h @@ -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 diff --git a/main.c b/main.c new file mode 100644 index 0000000..29ab9cb --- /dev/null +++ b/main.c @@ -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; +} diff --git a/mini_desktop b/mini_desktop new file mode 100755 index 0000000..6d5949a Binary files /dev/null and b/mini_desktop differ diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..43188c3 --- /dev/null +++ b/test/Makefile @@ -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 diff --git a/test/test_album.c b/test/test_album.c new file mode 100644 index 0000000..611bbc9 --- /dev/null +++ b/test/test_album.c @@ -0,0 +1,12 @@ +#include "../common/device.h" +#include "../desktop/desktop.h" +#include + +int main(void) +{ + device_init(); + desktop_init(); + desktop_run(); + device_deinit(); + return 0; +} diff --git a/test/test_album_static b/test/test_album_static new file mode 100755 index 0000000..39aeb85 Binary files /dev/null and b/test/test_album_static differ diff --git a/test/test_image.c b/test/test_image.c new file mode 100644 index 0000000..b4158e8 --- /dev/null +++ b/test/test_image.c @@ -0,0 +1,45 @@ +#include "../common/device.h" +#include "../album/album.h" +#include +#include +#include + +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; +} diff --git a/test/test_image_static b/test/test_image_static new file mode 100755 index 0000000..07feea2 Binary files /dev/null and b/test/test_image_static differ diff --git a/test/test_ir.c b/test/test_ir.c new file mode 100644 index 0000000..7734221 --- /dev/null +++ b/test/test_ir.c @@ -0,0 +1,58 @@ +#include "../common/device.h" +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/test/test_ir_static b/test/test_ir_static new file mode 100755 index 0000000..4ca77d1 Binary files /dev/null and b/test/test_ir_static differ diff --git a/test/test_ir_ui.c b/test/test_ir_ui.c new file mode 100644 index 0000000..142a8e6 --- /dev/null +++ b/test/test_ir_ui.c @@ -0,0 +1,12 @@ +#include "../common/device.h" +#include "../ir_app/ir_app.h" +#include + +int main(void) +{ + device_init(); + ir_app_init(); + ir_app_run(); + device_deinit(); + return 0; +} diff --git a/test/test_swipe.c b/test/test_swipe.c new file mode 100644 index 0000000..556e74d --- /dev/null +++ b/test/test_swipe.c @@ -0,0 +1,67 @@ +#include "../common/device.h" +#include "../button/button.h" +#include +#include +#include + +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; +} diff --git a/test/test_swipe_static b/test/test_swipe_static new file mode 100755 index 0000000..49c30a0 Binary files /dev/null and b/test/test_swipe_static differ