947 lines
23 KiB
C
947 lines
23 KiB
C
/*
|
|
* Copyright (c) 2006-2026, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2023-02-25 GuEe-GUI the first version
|
|
* 2026-07-23 GuEe-GUI support touch input and page flipping
|
|
*/
|
|
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include <cpuport.h>
|
|
#include <drivers/lcd.h>
|
|
#include <drivers/misc.h>
|
|
|
|
#if defined(RT_USING_GRAPHIC) && defined(RT_USING_INPUT)
|
|
|
|
#define HMI_CURSOR_WIDTH 64
|
|
#define HMI_CURSOR_HEIGHT 64
|
|
#define HMI_MAX_MT_SLOTS 16
|
|
#define HMI_MAX_INPUT_DEVICES 32
|
|
#define HMI_MAX_GRAPHIC_DEVICES 8
|
|
|
|
struct hmi_info
|
|
{
|
|
struct rt_device *gdev;
|
|
struct rt_device *idev;
|
|
|
|
struct rt_device_graphic_info info;
|
|
struct fb_var_screeninfo var;
|
|
struct rt_device_notify event_notify;
|
|
struct rt_input_handler handler;
|
|
struct rt_semaphore wakeup;
|
|
|
|
volatile rt_bool_t running;
|
|
volatile rt_bool_t dirty;
|
|
volatile rt_bool_t reconfigure;
|
|
rt_bool_t powered;
|
|
rt_bool_t notify_installed;
|
|
rt_bool_t handler_installed;
|
|
rt_bool_t keydown;
|
|
rt_bool_t multitouch;
|
|
|
|
rt_uint32_t dx;
|
|
rt_uint32_t dy;
|
|
rt_uint32_t bytes_per_pixel;
|
|
rt_uint32_t mt_x[HMI_MAX_MT_SLOTS];
|
|
rt_uint32_t mt_y[HMI_MAX_MT_SLOTS];
|
|
rt_int32_t mt_slot;
|
|
rt_ubase_t mt_active;
|
|
rt_ubase_t line[2];
|
|
rt_ubase_t colors[4];
|
|
|
|
rt_size_t frame_len;
|
|
rt_size_t page_count;
|
|
rt_size_t front_page;
|
|
void *framebuffer;
|
|
};
|
|
|
|
static struct hmi_info *hmi_active;
|
|
|
|
static struct rt_input_device *to_input_device(struct rt_device *idev)
|
|
{
|
|
return rt_container_of(idev, struct rt_input_device, parent);
|
|
}
|
|
|
|
static rt_ubase_t hmi_color_mask(rt_uint8_t length)
|
|
{
|
|
if (!length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (length >= RT_BITS_PER_TYPE(rt_ubase_t))
|
|
{
|
|
return ~0UL;
|
|
}
|
|
|
|
return (1UL << length) - 1;
|
|
}
|
|
|
|
static rt_ubase_t hmi_color_component(rt_uint8_t value, rt_ubase_t mask)
|
|
{
|
|
return (rt_ubase_t)value * mask / 255;
|
|
}
|
|
|
|
static rt_bool_t hmi_color_field_valid(const struct fb_bitfield *field,
|
|
rt_uint32_t bits_per_pixel)
|
|
{
|
|
if (!field->length)
|
|
{
|
|
return RT_TRUE;
|
|
}
|
|
|
|
return field->offset < bits_per_pixel &&
|
|
field->length <= bits_per_pixel - field->offset &&
|
|
field->length <= RT_BITS_PER_TYPE(rt_ubase_t);
|
|
}
|
|
|
|
static rt_ubase_t hmi_make_color(struct hmi_info *hmi,
|
|
rt_uint8_t red, rt_uint8_t green, rt_uint8_t blue,
|
|
rt_uint8_t alpha)
|
|
{
|
|
rt_ubase_t red_mask = hmi_color_mask(hmi->var.red.length);
|
|
rt_ubase_t green_mask = hmi_color_mask(hmi->var.green.length);
|
|
rt_ubase_t blue_mask = hmi_color_mask(hmi->var.blue.length);
|
|
rt_ubase_t alpha_mask = hmi_color_mask(hmi->var.transp.length);
|
|
|
|
return (hmi_color_component(red, red_mask) << hmi->var.red.offset) |
|
|
(hmi_color_component(green, green_mask) << hmi->var.green.offset) |
|
|
(hmi_color_component(blue, blue_mask) << hmi->var.blue.offset) |
|
|
(hmi_color_component(alpha, alpha_mask) << hmi->var.transp.offset);
|
|
}
|
|
|
|
static void hmi_swap_lines(struct hmi_info *hmi)
|
|
{
|
|
rt_ubase_t color = hmi->line[0];
|
|
|
|
hmi->line[0] = hmi->line[1];
|
|
hmi->line[1] = color;
|
|
}
|
|
|
|
static void hmi_signal(struct hmi_info *hmi)
|
|
{
|
|
rt_bool_t wakeup = !hmi->dirty;
|
|
|
|
hmi->dirty = RT_TRUE;
|
|
rt_hw_wmb();
|
|
|
|
if (wakeup)
|
|
{
|
|
rt_sem_release(&hmi->wakeup);
|
|
}
|
|
}
|
|
|
|
static rt_bool_t hmi_input_supported(struct rt_device *dev,
|
|
rt_bool_t *multitouch)
|
|
{
|
|
const char *name;
|
|
rt_bool_t mt;
|
|
struct rt_input_device *idev;
|
|
|
|
if (!dev)
|
|
{
|
|
return RT_FALSE;
|
|
}
|
|
|
|
name = rt_dm_dev_get_name(dev);
|
|
|
|
if (!name || rt_strncmp(name, "input", sizeof("input") - 1))
|
|
{
|
|
return RT_FALSE;
|
|
}
|
|
|
|
idev = to_input_device(dev);
|
|
|
|
if (!rt_bitmap_test_bit(idev->cap, EV_ABS))
|
|
{
|
|
return RT_FALSE;
|
|
}
|
|
|
|
mt = rt_bitmap_test_bit(idev->abs_map, ABS_MT_SLOT) &&
|
|
rt_bitmap_test_bit(idev->abs_map, ABS_MT_TRACKING_ID) &&
|
|
rt_bitmap_test_bit(idev->abs_map, ABS_MT_POSITION_X) &&
|
|
rt_bitmap_test_bit(idev->abs_map, ABS_MT_POSITION_Y);
|
|
|
|
if (!mt && !(rt_bitmap_test_bit(idev->abs_map, ABS_X) &&
|
|
rt_bitmap_test_bit(idev->abs_map, ABS_Y)))
|
|
{
|
|
return RT_FALSE;
|
|
}
|
|
|
|
if (multitouch)
|
|
{
|
|
*multitouch = mt;
|
|
}
|
|
|
|
return RT_TRUE;
|
|
}
|
|
|
|
static struct rt_device *hmi_find_graphic(const char *name)
|
|
{
|
|
char device_name[16];
|
|
struct rt_device *dev;
|
|
int i;
|
|
|
|
if (name && rt_strcmp(name, "auto"))
|
|
{
|
|
return rt_device_find(name);
|
|
}
|
|
|
|
for (i = 0; i < HMI_MAX_GRAPHIC_DEVICES; ++i)
|
|
{
|
|
rt_snprintf(device_name, sizeof(device_name), "fb%d", i);
|
|
|
|
if ((dev = rt_device_find(device_name)))
|
|
{
|
|
return dev;
|
|
}
|
|
}
|
|
|
|
return RT_NULL;
|
|
}
|
|
|
|
static struct rt_device *hmi_find_input(const char *name,
|
|
rt_bool_t *multitouch)
|
|
{
|
|
char device_name[16];
|
|
struct rt_device *dev;
|
|
int i;
|
|
|
|
if (name && rt_strcmp(name, "auto"))
|
|
{
|
|
dev = rt_device_find(name);
|
|
|
|
return hmi_input_supported(dev, multitouch) ? dev : RT_NULL;
|
|
}
|
|
|
|
for (i = 0; i < HMI_MAX_INPUT_DEVICES; ++i)
|
|
{
|
|
rt_snprintf(device_name, sizeof(device_name), "input%d", i);
|
|
dev = rt_device_find(device_name);
|
|
|
|
if (hmi_input_supported(dev, multitouch))
|
|
{
|
|
return dev;
|
|
}
|
|
}
|
|
|
|
return RT_NULL;
|
|
}
|
|
|
|
static rt_uint32_t hmi_scale_axis(struct rt_input_device *idev,
|
|
rt_uint16_t axis, rt_int32_t value, rt_uint32_t size)
|
|
{
|
|
rt_int32_t min, max;
|
|
struct rt_input_absinfo *absinfo;
|
|
|
|
if (!idev->absinfo || axis >= ABS_CNT || size <= 1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
absinfo = &idev->absinfo[axis];
|
|
min = absinfo->minimum;
|
|
max = absinfo->maximum;
|
|
|
|
if (max <= min)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
value = rt_clamp(value, min, max);
|
|
|
|
return (rt_uint64_t)(value - min) * (size - 1) / (max - min);
|
|
}
|
|
|
|
static void hmi_select_mt_position(struct hmi_info *hmi)
|
|
{
|
|
int slot;
|
|
|
|
for (slot = 0; slot < HMI_MAX_MT_SLOTS; ++slot)
|
|
{
|
|
if (hmi->mt_active & RT_BIT(slot))
|
|
{
|
|
hmi->dx = hmi->mt_x[slot];
|
|
hmi->dy = hmi->mt_y[slot];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static rt_bool_t hmi_input_callback(struct rt_input_handler *handler,
|
|
struct rt_input_event *ev)
|
|
{
|
|
rt_bool_t down;
|
|
rt_ubase_t slot_mask;
|
|
struct hmi_info *hmi = handler->priv;
|
|
|
|
if (ev->type == EV_ABS)
|
|
{
|
|
if (hmi->multitouch && ev->code == ABS_MT_SLOT)
|
|
{
|
|
hmi->mt_slot = ev->value >= 0 &&
|
|
ev->value < HMI_MAX_MT_SLOTS
|
|
? ev->value
|
|
: -1;
|
|
}
|
|
else if (hmi->multitouch && ev->code == ABS_MT_TRACKING_ID &&
|
|
hmi->mt_slot >= 0)
|
|
{
|
|
slot_mask = RT_BIT(hmi->mt_slot);
|
|
|
|
if (ev->value < 0)
|
|
{
|
|
hmi->mt_active &= ~slot_mask;
|
|
}
|
|
else
|
|
{
|
|
hmi->mt_active |= slot_mask;
|
|
}
|
|
}
|
|
else if (ev->code == ABS_X)
|
|
{
|
|
hmi->dx = hmi_scale_axis(handler->idev, ABS_X,
|
|
ev->value, hmi->info.width);
|
|
}
|
|
else if (ev->code == ABS_Y)
|
|
{
|
|
hmi->dy = hmi_scale_axis(handler->idev, ABS_Y,
|
|
ev->value, hmi->info.height);
|
|
}
|
|
else if (hmi->multitouch && hmi->mt_slot >= 0 &&
|
|
ev->code == ABS_MT_POSITION_X)
|
|
{
|
|
hmi->mt_x[hmi->mt_slot] = hmi_scale_axis(handler->idev,
|
|
ABS_MT_POSITION_X, ev->value, hmi->info.width);
|
|
}
|
|
else if (hmi->multitouch && hmi->mt_slot >= 0 &&
|
|
ev->code == ABS_MT_POSITION_Y)
|
|
{
|
|
hmi->mt_y[hmi->mt_slot] = hmi_scale_axis(handler->idev,
|
|
ABS_MT_POSITION_Y, ev->value, hmi->info.height);
|
|
}
|
|
}
|
|
else if (ev->type == EV_KEY && !hmi->multitouch &&
|
|
(ev->code == BTN_LEFT || ev->code == BTN_TOUCH))
|
|
{
|
|
if (hmi->keydown && ev->value == 0)
|
|
{
|
|
hmi_swap_lines(hmi);
|
|
}
|
|
|
|
hmi->keydown = ev->value != 0;
|
|
}
|
|
else if (ev->type == EV_SYN && ev->code == SYN_REPORT)
|
|
{
|
|
if (hmi->multitouch)
|
|
{
|
|
down = hmi->mt_active != 0;
|
|
|
|
if (hmi->keydown && !down)
|
|
{
|
|
hmi_swap_lines(hmi);
|
|
}
|
|
|
|
hmi->keydown = down;
|
|
|
|
if (down)
|
|
{
|
|
hmi_select_mt_position(hmi);
|
|
}
|
|
}
|
|
|
|
hmi_signal(hmi);
|
|
}
|
|
|
|
return RT_FALSE;
|
|
}
|
|
|
|
static void hmi_graphic_notify(rt_device_t dev)
|
|
{
|
|
struct hmi_info *hmi = (void *)dev;
|
|
|
|
hmi->reconfigure = RT_TRUE;
|
|
hmi_signal(hmi);
|
|
}
|
|
|
|
static void hmi_write_pixel(rt_uint8_t *pixel, rt_ubase_t color,
|
|
rt_uint32_t bytes_per_pixel)
|
|
{
|
|
rt_memcpy(pixel, &color, bytes_per_pixel);
|
|
}
|
|
|
|
static void hmi_fill_span(rt_uint8_t *pixel, rt_uint32_t width,
|
|
rt_ubase_t color, rt_uint32_t bytes_per_pixel)
|
|
{
|
|
rt_uint32_t x;
|
|
|
|
switch (bytes_per_pixel)
|
|
{
|
|
case 1:
|
|
rt_memset(pixel, (rt_uint8_t)color, width);
|
|
break;
|
|
|
|
case 2:
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
((rt_uint16_t *)pixel)[x] = (rt_uint16_t)color;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
((rt_uint32_t *)pixel)[x] = (rt_uint32_t)color;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
hmi_write_pixel(pixel, color, bytes_per_pixel);
|
|
pixel += bytes_per_pixel;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void hmi_fill_rect(struct hmi_info *hmi, rt_uint32_t x,
|
|
rt_uint32_t y, rt_uint32_t width, rt_uint32_t height,
|
|
rt_ubase_t color)
|
|
{
|
|
rt_uint8_t *row;
|
|
rt_uint32_t line;
|
|
|
|
if (!width || !height)
|
|
{
|
|
return;
|
|
}
|
|
|
|
row = (rt_uint8_t *)hmi->framebuffer +
|
|
(rt_size_t)y * hmi->info.pitch +
|
|
(rt_size_t)x * hmi->bytes_per_pixel;
|
|
|
|
for (line = 0; line < height; ++line)
|
|
{
|
|
hmi_fill_span(row, width, color, hmi->bytes_per_pixel);
|
|
row += hmi->info.pitch;
|
|
}
|
|
}
|
|
|
|
static void hmi_draw_lines(struct hmi_info *hmi,
|
|
rt_uint32_t x, rt_uint32_t y)
|
|
{
|
|
rt_uint8_t *pixel;
|
|
rt_uint32_t line;
|
|
|
|
pixel = (rt_uint8_t *)hmi->framebuffer +
|
|
(rt_size_t)y * hmi->info.pitch;
|
|
hmi_fill_span(pixel, hmi->info.width, hmi->line[0],
|
|
hmi->bytes_per_pixel);
|
|
|
|
pixel = (rt_uint8_t *)hmi->framebuffer +
|
|
(rt_size_t)x * hmi->bytes_per_pixel;
|
|
|
|
for (line = 0; line < hmi->info.height; ++line)
|
|
{
|
|
hmi_write_pixel(pixel, hmi->line[1], hmi->bytes_per_pixel);
|
|
pixel += hmi->info.pitch;
|
|
}
|
|
}
|
|
|
|
static void hmi_setup_cursor(struct hmi_info *hmi)
|
|
{
|
|
rt_ubase_t color;
|
|
rt_uint8_t *cursor;
|
|
rt_uint8_t *pixel;
|
|
rt_size_t cursor_len;
|
|
rt_uint32_t count;
|
|
|
|
cursor_len = HMI_CURSOR_WIDTH * HMI_CURSOR_HEIGHT *
|
|
hmi->bytes_per_pixel;
|
|
cursor = rt_malloc(cursor_len);
|
|
|
|
if (!cursor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
color = hmi_make_color(hmi, 0x82, 0x50, 0xdf, 0xcc);
|
|
pixel = cursor;
|
|
|
|
for (count = 0; count < HMI_CURSOR_WIDTH * HMI_CURSOR_HEIGHT; ++count)
|
|
{
|
|
hmi_write_pixel(pixel, color, hmi->bytes_per_pixel);
|
|
pixel += hmi->bytes_per_pixel;
|
|
}
|
|
|
|
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_TYPE, cursor);
|
|
rt_free(cursor);
|
|
}
|
|
|
|
static rt_err_t hmi_configure(struct hmi_info *hmi)
|
|
{
|
|
rt_err_t err;
|
|
rt_uint32_t slot;
|
|
rt_size_t frame_len, page_count, front_page;
|
|
struct fb_fix_screeninfo fix;
|
|
void *framebuffer;
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
RTGRAPHIC_CTRL_GET_INFO, &hmi->info)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
FBIOGET_VSCREENINFO, &hmi->var)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
if (!hmi->info.framebuffer || !hmi->info.width || !hmi->info.height ||
|
|
!hmi->info.pitch || !hmi->info.bits_per_pixel ||
|
|
hmi->info.bits_per_pixel % 8)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
hmi->bytes_per_pixel = hmi->info.bits_per_pixel / 8;
|
|
|
|
if (!hmi->bytes_per_pixel ||
|
|
hmi->bytes_per_pixel > sizeof(rt_ubase_t) ||
|
|
hmi->info.width > hmi->info.pitch / hmi->bytes_per_pixel ||
|
|
!hmi_color_field_valid(&hmi->var.red,
|
|
hmi->info.bits_per_pixel) ||
|
|
!hmi_color_field_valid(&hmi->var.green,
|
|
hmi->info.bits_per_pixel) ||
|
|
!hmi_color_field_valid(&hmi->var.blue,
|
|
hmi->info.bits_per_pixel) ||
|
|
!hmi_color_field_valid(&hmi->var.transp,
|
|
hmi->info.bits_per_pixel) ||
|
|
hmi->info.pitch > (rt_size_t)-1 / hmi->info.height)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
frame_len = (rt_size_t)hmi->info.pitch * hmi->info.height;
|
|
page_count = hmi->info.smem_len / frame_len;
|
|
|
|
if (!page_count)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
if (page_count > 1)
|
|
{
|
|
rt_memset(&fix, 0, sizeof(fix));
|
|
|
|
if (rt_device_control(hmi->gdev, FBIOGET_FSCREENINFO, &fix) ||
|
|
!fix.ypanstep)
|
|
{
|
|
page_count = 1;
|
|
}
|
|
}
|
|
|
|
framebuffer = rt_malloc(frame_len);
|
|
|
|
if (!framebuffer)
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
front_page = hmi->var.yoffset / hmi->info.height;
|
|
|
|
if (front_page >= page_count)
|
|
{
|
|
front_page = 0;
|
|
}
|
|
|
|
if (page_count > 1)
|
|
{
|
|
hmi->var.pixclock = 0;
|
|
hmi->var.activate = FB_ACTIVATE_NOW;
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
FBIOPUT_VSCREENINFO, &hmi->var)))
|
|
{
|
|
rt_free(framebuffer);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (hmi->framebuffer)
|
|
{
|
|
rt_free(hmi->framebuffer);
|
|
}
|
|
|
|
hmi->framebuffer = framebuffer;
|
|
hmi->frame_len = frame_len;
|
|
hmi->page_count = page_count;
|
|
hmi->front_page = front_page;
|
|
|
|
hmi->line[0] = hmi_make_color(hmi, 0xff, 0xff, 0xff, 0xff);
|
|
hmi->line[1] = hmi_make_color(hmi, 0x00, 0x00, 0x00, 0xff);
|
|
hmi->colors[0] = hmi_make_color(hmi, 0xff, 0x4b, 0x00, 0xff);
|
|
hmi->colors[1] = hmi_make_color(hmi, 0x7f, 0xdb, 0x3b, 0xff);
|
|
hmi->colors[2] = hmi_make_color(hmi, 0x00, 0xa4, 0xef, 0xff);
|
|
hmi->colors[3] = hmi_make_color(hmi, 0xff, 0xb8, 0x1c, 0xff);
|
|
|
|
if (hmi->dx >= hmi->info.width || hmi->dy >= hmi->info.height)
|
|
{
|
|
hmi->dx = hmi->info.width / 2;
|
|
hmi->dy = hmi->info.height / 2;
|
|
}
|
|
|
|
for (slot = 0; slot < HMI_MAX_MT_SLOTS; ++slot)
|
|
{
|
|
if (hmi->mt_x[slot] >= hmi->info.width)
|
|
{
|
|
hmi->mt_x[slot] = hmi->dx;
|
|
}
|
|
|
|
if (hmi->mt_y[slot] >= hmi->info.height)
|
|
{
|
|
hmi->mt_y[slot] = hmi->dy;
|
|
}
|
|
}
|
|
|
|
rt_memset(hmi->framebuffer, 0, hmi->frame_len);
|
|
hmi_setup_cursor(hmi);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t hmi_present(struct hmi_info *hmi)
|
|
{
|
|
rt_err_t err;
|
|
rt_ubase_t cursor_position;
|
|
rt_uint32_t x, y;
|
|
struct rt_device_rect_info rect;
|
|
struct fb_var_screeninfo var;
|
|
|
|
x = rt_min(hmi->dx, (rt_uint32_t)hmi->info.width - 1);
|
|
y = rt_min(hmi->dy, (rt_uint32_t)hmi->info.height - 1);
|
|
|
|
hmi_fill_rect(hmi, 0, 0, x, y, hmi->colors[0]);
|
|
hmi_fill_rect(hmi, x, 0, hmi->info.width - x, y, hmi->colors[1]);
|
|
hmi_fill_rect(hmi, 0, y, x, hmi->info.height - y, hmi->colors[2]);
|
|
hmi_fill_rect(hmi, x, y, hmi->info.width - x,
|
|
hmi->info.height - y, hmi->colors[3]);
|
|
hmi_draw_lines(hmi, x, y);
|
|
|
|
if (hmi->page_count > 1)
|
|
{
|
|
rt_size_t back_page = (hmi->front_page + 1) % hmi->page_count;
|
|
rt_uint8_t *framebuffer = hmi->info.framebuffer;
|
|
|
|
rt_memcpy(framebuffer + back_page * hmi->frame_len,
|
|
hmi->framebuffer, hmi->frame_len);
|
|
|
|
var = hmi->var;
|
|
var.xoffset = 0;
|
|
var.yoffset = back_page * hmi->info.height;
|
|
var.activate = FB_ACTIVATE_VBL;
|
|
err = rt_device_control(hmi->gdev, FBIOPAN_DISPLAY, &var);
|
|
|
|
if (err)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
hmi->var = var;
|
|
hmi->front_page = back_page;
|
|
}
|
|
else
|
|
{
|
|
rt_memcpy(hmi->info.framebuffer, hmi->framebuffer, hmi->frame_len);
|
|
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = hmi->info.width;
|
|
rect.height = hmi->info.height;
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
RTGRAPHIC_CTRL_RECT_UPDATE, &rect)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
RTGRAPHIC_CTRL_WAIT_VSYNC, RT_NULL)))
|
|
{
|
|
return err;
|
|
}
|
|
}
|
|
|
|
cursor_position = RTGRAPHIC_PIXEL_POSITION(x, y);
|
|
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_POSITION,
|
|
(void *)cursor_position);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void hmi_cleanup(struct hmi_info *hmi)
|
|
{
|
|
struct rt_device_notify notify = { 0 };
|
|
|
|
if (hmi->handler_installed)
|
|
{
|
|
rt_input_del_handler(&hmi->handler);
|
|
}
|
|
|
|
if (hmi->notify_installed)
|
|
{
|
|
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_NOTIFY_SET, ¬ify);
|
|
}
|
|
|
|
if (hmi->powered)
|
|
{
|
|
rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_POWEROFF, RT_NULL);
|
|
}
|
|
|
|
rt_device_close(hmi->idev);
|
|
rt_device_close(hmi->gdev);
|
|
|
|
if (hmi->framebuffer)
|
|
{
|
|
rt_free(hmi->framebuffer);
|
|
}
|
|
|
|
rt_sem_detach(&hmi->wakeup);
|
|
hmi_active = RT_NULL;
|
|
rt_free(hmi);
|
|
}
|
|
|
|
static void hmi_loop(void *param)
|
|
{
|
|
rt_err_t err = RT_EOK;
|
|
rt_uint32_t slot;
|
|
struct hmi_info *hmi = param;
|
|
|
|
hmi->event_notify.notify = hmi_graphic_notify;
|
|
hmi->event_notify.dev = (void *)hmi;
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
RT_DEVICE_CTRL_NOTIFY_SET, &hmi->event_notify)))
|
|
{
|
|
goto _exit;
|
|
}
|
|
hmi->notify_installed = RT_TRUE;
|
|
|
|
if ((err = rt_device_control(hmi->gdev,
|
|
RTGRAPHIC_CTRL_POWERON, RT_NULL)))
|
|
{
|
|
goto _exit;
|
|
}
|
|
hmi->powered = RT_TRUE;
|
|
|
|
if ((err = hmi_configure(hmi)))
|
|
{
|
|
goto _exit;
|
|
}
|
|
|
|
hmi->dx = hmi->info.width / 2;
|
|
hmi->dy = hmi->info.height / 2;
|
|
|
|
for (slot = 0; slot < HMI_MAX_MT_SLOTS; ++slot)
|
|
{
|
|
hmi->mt_x[slot] = hmi->dx;
|
|
hmi->mt_y[slot] = hmi->dy;
|
|
}
|
|
|
|
hmi->handler.idev = to_input_device(hmi->idev);
|
|
hmi->handler.callback = hmi_input_callback;
|
|
hmi->handler.priv = hmi;
|
|
|
|
if ((err = rt_input_add_handler(&hmi->handler)))
|
|
{
|
|
goto _exit;
|
|
}
|
|
hmi->handler_installed = RT_TRUE;
|
|
|
|
while (hmi->running)
|
|
{
|
|
if (hmi->reconfigure)
|
|
{
|
|
hmi->reconfigure = RT_FALSE;
|
|
rt_hw_wmb();
|
|
|
|
if ((err = hmi_configure(hmi)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hmi->dirty = RT_TRUE;
|
|
}
|
|
|
|
if (hmi->dirty)
|
|
{
|
|
hmi->dirty = RT_FALSE;
|
|
rt_hw_wmb();
|
|
|
|
if ((err = hmi_present(hmi)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hmi->running)
|
|
{
|
|
rt_sem_take(&hmi->wakeup, RT_WAITING_FOREVER);
|
|
}
|
|
}
|
|
|
|
_exit:
|
|
if (err)
|
|
{
|
|
rt_kprintf("HMI stopped: %s\n", rt_strerror(err));
|
|
}
|
|
|
|
hmi_cleanup(hmi);
|
|
rt_thread_delete(rt_thread_self());
|
|
}
|
|
|
|
rt_err_t hmi_start(const char *gdev_name, const char *idev_name)
|
|
{
|
|
rt_err_t err;
|
|
struct hmi_info *hmi;
|
|
struct rt_thread *loop;
|
|
|
|
if (hmi_active)
|
|
{
|
|
rt_kprintf("HMI is running\n");
|
|
return -RT_EBUSY;
|
|
}
|
|
|
|
hmi = rt_calloc(1, sizeof(*hmi));
|
|
|
|
if (!hmi)
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
hmi->gdev = hmi_find_graphic(gdev_name);
|
|
hmi->idev = hmi_find_input(idev_name, &hmi->multitouch);
|
|
|
|
if (!hmi->gdev || !hmi->idev)
|
|
{
|
|
rt_kprintf("No compatible graphic/input device found\n");
|
|
rt_free(hmi);
|
|
return -RT_ENOENT;
|
|
}
|
|
|
|
hmi->mt_slot = -1;
|
|
hmi->running = RT_TRUE;
|
|
hmi->dirty = RT_TRUE;
|
|
|
|
if ((err = rt_sem_init(&hmi->wakeup, "hmi", 0, RT_IPC_FLAG_FIFO)))
|
|
{
|
|
rt_free(hmi);
|
|
return err;
|
|
}
|
|
|
|
if ((err = rt_device_open(hmi->gdev, 0)))
|
|
{
|
|
rt_sem_detach(&hmi->wakeup);
|
|
rt_free(hmi);
|
|
return err;
|
|
}
|
|
|
|
if ((err = rt_device_open(hmi->idev, 0)))
|
|
{
|
|
rt_device_close(hmi->gdev);
|
|
rt_sem_detach(&hmi->wakeup);
|
|
rt_free(hmi);
|
|
return err;
|
|
}
|
|
|
|
loop = rt_thread_create("HMI", hmi_loop, hmi,
|
|
DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 3, 10);
|
|
|
|
if (!loop)
|
|
{
|
|
rt_device_close(hmi->idev);
|
|
rt_device_close(hmi->gdev);
|
|
rt_sem_detach(&hmi->wakeup);
|
|
rt_free(hmi);
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
hmi_active = hmi;
|
|
|
|
if ((err = rt_thread_startup(loop)))
|
|
{
|
|
hmi_active = RT_NULL;
|
|
rt_thread_delete(loop);
|
|
rt_device_close(hmi->idev);
|
|
rt_device_close(hmi->gdev);
|
|
rt_sem_detach(&hmi->wakeup);
|
|
rt_free(hmi);
|
|
return err;
|
|
}
|
|
|
|
rt_kprintf("HMI: %s + %s (%s input)\n",
|
|
rt_dm_dev_get_name(hmi->gdev),
|
|
rt_dm_dev_get_name(hmi->idev),
|
|
hmi->multitouch ? "multitouch" : "pointer");
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
rt_err_t hmi_stop(void)
|
|
{
|
|
struct hmi_info *hmi = hmi_active;
|
|
|
|
if (!hmi)
|
|
{
|
|
return -RT_ENOSYS;
|
|
}
|
|
|
|
hmi->running = RT_FALSE;
|
|
rt_hw_wmb();
|
|
rt_sem_release(&hmi->wakeup);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
#ifdef RT_USING_FINSH
|
|
static int _hmi_start(int argc, char **argv)
|
|
{
|
|
const char *gdev = RT_NULL;
|
|
const char *idev = RT_NULL;
|
|
|
|
if (argc > 3)
|
|
{
|
|
rt_kprintf("Usage: hmi_start [fbN|auto] [inputN|auto]\n");
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
if (argc > 1)
|
|
{
|
|
gdev = argv[1];
|
|
}
|
|
|
|
if (argc > 2)
|
|
{
|
|
idev = argv[2];
|
|
}
|
|
|
|
return (int)hmi_start(gdev, idev);
|
|
}
|
|
MSH_CMD_EXPORT_ALIAS(_hmi_start, hmi_start,
|
|
auto select devices e.g.hmi_start or hmi_start fb0 input1);
|
|
|
|
static int _hmi_stop(void)
|
|
{
|
|
return (int)hmi_stop();
|
|
}
|
|
MSH_CMD_EXPORT_ALIAS(_hmi_stop, hmi_stop, stop HMI test);
|
|
#endif /* RT_USING_FINSH */
|
|
#endif /* RT_USING_GRAPHIC && RT_USING_INPUT */
|