[display] SDL2 display driver for host platform (#6825)

This commit is contained in:
Clyde Stubbs 2024-06-12 11:42:01 +10:00 committed by GitHub
parent e2c1af199c
commit bc408ad08c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 286 additions and 1 deletions

View file

@ -455,7 +455,7 @@ jobs:
file: ${{ fromJson(needs.list-components.outputs.components) }}
steps:
- name: Install libsodium
run: sudo apt-get install libsodium-dev
run: sudo apt-get install libsodium-dev libsdl2-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.1.6

View file

@ -320,6 +320,7 @@ esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
esphome/components/scd4x/* @martgras @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdl/* @clydebarrow
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
esphome/components/seeed_mr24hpc1/* @limengdu

View file

@ -0,0 +1 @@
CODEOWNERS = ["@clydebarrow"]

View file

@ -0,0 +1,72 @@
import subprocess
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display
from esphome.const import (
CONF_ID,
CONF_DIMENSIONS,
CONF_WIDTH,
CONF_HEIGHT,
CONF_LAMBDA,
PLATFORM_HOST,
)
sdl_ns = cg.esphome_ns.namespace("sdl")
Sdl = sdl_ns.class_("Sdl", display.Display, cg.Component)
CONF_SDL_OPTIONS = "sdl_options"
CONF_SDL_ID = "sdl_id"
def get_sdl_options(value):
if value != "":
return value
try:
return subprocess.check_output(["sdl2-config", "--cflags", "--libs"]).decode()
except Exception as e:
raise cv.Invalid("Unable to run sdl2-config - have you installed sdl2?") from e
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Sdl),
cv.Optional(CONF_SDL_OPTIONS, default=""): get_sdl_options,
cv.Required(CONF_DIMENSIONS): cv.Any(
cv.dimensions,
cv.Schema(
{
cv.Required(CONF_WIDTH): cv.int_,
cv.Required(CONF_HEIGHT): cv.int_,
}
),
),
}
)
),
cv.only_on(PLATFORM_HOST),
)
async def to_code(config):
for option in config[CONF_SDL_OPTIONS].split():
cg.add_build_flag(option)
cg.add_build_flag("-DSDL_BYTEORDER=4321")
var = cg.new_Pvariable(config[CONF_ID])
await display.register_display(var, config)
dimensions = config[CONF_DIMENSIONS]
if isinstance(dimensions, dict):
cg.add(var.set_dimensions(dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT]))
else:
(width, height) = dimensions
cg.add(var.set_dimensions(width, height))
if lamb := config.get(CONF_LAMBDA):
lambda_ = await cg.process_lambda(
lamb, [(display.DisplayRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View file

@ -0,0 +1,96 @@
#ifdef USE_HOST
#include "sdl_esphome.h"
#include "esphome/components/display/display_color_utils.h"
namespace esphome {
namespace sdl {
void Sdl::setup() {
ESP_LOGD(TAG, "Starting setup");
SDL_Init(SDL_INIT_VIDEO);
this->window_ = SDL_CreateWindow(App.get_name().c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
this->width_, this->height_, 0);
this->renderer_ = SDL_CreateRenderer(this->window_, -1, SDL_RENDERER_SOFTWARE);
this->texture_ =
SDL_CreateTexture(this->renderer_, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STATIC, this->width_, this->height_);
SDL_SetTextureBlendMode(this->texture_, SDL_BLENDMODE_BLEND);
ESP_LOGD(TAG, "Setup Complete");
}
void Sdl::update() {
this->do_update_();
if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_))
return;
SDL_Rect rect{this->x_low_, this->y_low_, this->x_high_ + 1 - this->x_low_, this->y_high_ + 1 - this->y_low_};
this->x_low_ = this->width_;
this->y_low_ = this->height_;
this->x_high_ = 0;
this->y_high_ = 0;
SDL_RenderCopy(this->renderer_, this->texture_, &rect, &rect);
SDL_RenderPresent(this->renderer_);
}
void Sdl::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
SDL_Rect rect{x_start, y_start, w, h};
if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || big_endian) {
display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
x_pad);
} else {
auto stride = x_offset + w + x_pad;
auto data = ptr + (stride * y_offset + x_offset) * 2;
SDL_UpdateTexture(this->texture_, &rect, data, stride * 2);
}
SDL_RenderCopy(this->renderer_, this->texture_, &rect, &rect);
SDL_RenderPresent(this->renderer_);
}
void Sdl::draw_pixel_at(int x, int y, Color color) {
SDL_Rect rect{x, y, 1, 1};
auto data = (display::ColorUtil::color_to_565(color, display::COLOR_ORDER_RGB));
SDL_UpdateTexture(this->texture_, &rect, &data, 2);
if (x < this->x_low_)
this->x_low_ = x;
if (y < this->y_low_)
this->y_low_ = y;
if (x > this->x_high_)
this->x_high_ = x;
if (y > this->y_high_)
this->y_high_ = y;
}
void Sdl::loop() {
SDL_Event e;
if (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
exit(0);
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (e.button.button == 1) {
this->mouse_x = e.button.x;
this->mouse_y = e.button.y;
this->mouse_down = e.button.state != 0;
}
break;
case SDL_MOUSEMOTION:
if (e.motion.state & 1) {
this->mouse_x = e.button.x;
this->mouse_y = e.button.y;
this->mouse_down = true;
} else {
this->mouse_down = false;
}
break;
default:
ESP_LOGV(TAG, "Event %d", e.type);
break;
}
}
}
} // namespace sdl
} // namespace esphome
#endif

View file

@ -0,0 +1,54 @@
#pragma once
#ifdef USE_HOST
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/components/display/display.h"
#define SDL_MAIN_HANDLED
#include "SDL.h"
namespace esphome {
namespace sdl {
constexpr static const char *const TAG = "sdl";
class Sdl : public display::Display {
public:
display::DisplayType get_display_type() override { return display::DISPLAY_TYPE_COLOR; }
void update() override;
void loop() override;
void setup() override;
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override;
void draw_pixel_at(int x, int y, Color color) override;
void set_dimensions(uint16_t width, uint16_t height) {
this->width_ = width;
this->height_ = height;
}
int get_width() override { return this->width_; }
int get_height() override { return this->height_; }
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void dump_config() override { LOG_DISPLAY("", "SDL", this); }
int mouse_x{};
int mouse_y{};
bool mouse_down{};
protected:
int get_width_internal() override { return this->width_; }
int get_height_internal() override { return this->height_; }
int width_{};
int height_{};
SDL_Renderer *renderer_{};
SDL_Window *window_{};
SDL_Texture *texture_{};
uint16_t x_low_{0};
uint16_t y_low_{0};
uint16_t x_high_{0};
uint16_t y_high_{0};
};
} // namespace sdl
} // namespace esphome
#endif

View file

@ -0,0 +1,22 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.components import touchscreen
from ..display import Sdl, sdl_ns, CONF_SDL_ID
SdlTouchscreen = sdl_ns.class_("SdlTouchscreen", touchscreen.Touchscreen)
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(SdlTouchscreen),
cv.GenerateID(CONF_SDL_ID): cv.use_id(Sdl),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_parented(var, config[CONF_SDL_ID])
await touchscreen.register_touchscreen(var, config)

View file

@ -0,0 +1,26 @@
#pragma once
#ifdef USE_HOST
#include "../sdl_esphome.h"
#include "esphome/components/touchscreen/touchscreen.h"
namespace esphome {
namespace sdl {
class SdlTouchscreen : public touchscreen::Touchscreen, public Parented<Sdl> {
public:
void setup() override {
this->x_raw_max_ = this->display_->get_width();
this->y_raw_max_ = this->display_->get_height();
}
void update_touches() override {
if (this->parent_->mouse_down) {
add_raw_touch_position_(0, this->parent_->mouse_x, this->parent_->mouse_y);
}
}
};
} // namespace sdl
} // namespace esphome
#endif

View file

@ -0,0 +1,12 @@
host:
mac_address: "62:23:45:AF:B3:DD"
display:
- platform: sdl
id: sdl_display
update_interval: 1s
auto_clear_enabled: false
show_test_card: true
dimensions:
width: 450
height: 600

View file

@ -0,0 +1 @@
<<: !include common.yaml