first commit
19
Makefile
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
After Width: | Height: | Size: 1.5 MiB |
BIN
icon/bar_top.bmp
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
icon/bg_page.bmp
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
icon/game_icon.bmp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
icon/ir_icon.bmp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
icon/photo_icon.bmp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
images/photo_1.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
images/photo_2.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
images/photo_3.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
468
ir_app/ir_app.c
Normal 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
@@ -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
@@ -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
20
test/Makefile
Normal 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
@@ -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
45
test/test_image.c
Normal 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
58
test/test_ir.c
Normal 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
12
test/test_ir_ui.c
Normal 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
@@ -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;
|
||||||
|
}
|
||||||