Compare commits

..

5 Commits

Author SHA1 Message Date
6b22d8465e Added ability to supply JSON from standard input 2026-03-30 17:28:36 +03:00
a80e23614d Fixed missing semicolon 2026-03-30 17:27:46 +03:00
7c5cec7d8a Updated README. Fixed gitignore 2026-01-19 16:00:16 +02:00
3f4e42d7ce Added setters for Widget properties 2025-12-18 12:24:19 +02:00
40df0f6e0c Added info to README for WidgetImage 2025-12-15 17:07:44 +02:00
11 changed files with 146 additions and 8 deletions

9
.gitignore vendored
View File

@@ -1,4 +1,13 @@
# Exclude the executable
trmnl_sdl trmnl_sdl
# Exclude configuration files
*.json *.json
# Exclude images
*.bmp
*.jpg
*.png *.png
# Exclude fonts
*.ttf *.ttf

View File

@@ -9,13 +9,26 @@ You will need the following packages: `build-essential` `libsdl2-dev` `libsdl2-i
Just run `make` and an executable called `trmnl_sdl` should be produced. Just run `make` and an executable called `trmnl_sdl` should be produced.
# How to use # How to use
1. You will need to write a JSON config file. ### General use
It can either be named `config.json` or you will need to pass it as the first command line parameter to the executable. 1. You will need to write a JSON config file named `config.json`.
An example structure of the config file can be found in `config_example.json`. An example structure of the config file can be found in `config_example.json`.
You can find more info on the available Widgets and their parameters at the end of this file. You can find more info on the available Widgets and their parameters at the end of this file.
2. Run the executable 2. Run the executable
3. Convert the image to a suitable format via an ImageMagick command from below. 3. Convert the image to a suitable format via an ImageMagick command from below.
### Different ways to supply JSON
1. Default
* Command: `./trmnl_sdl`
* JSON Used: `config.json`
2. Specified file
* Command: `./trmnl_sdl path/to/json/file.json`
* JSON Used: `path/to/json/file.json`
3. Standard input
* Command: `./trmnl_sdl -`
* JSON Used: Supplied on the standard input for the process.
# ImageMagick commands to prepare image for TRMNL device # ImageMagick commands to prepare image for TRMNL device
### Convert image without dithering ### Convert image without dithering
`convert trmnl.png -monochrome -colors 2 -depth 1 -strip png:output.png` `convert trmnl.png -monochrome -colors 2 -depth 1 -strip png:output.png`
@@ -63,13 +76,35 @@ Controls vertical alignment of elements. **String** type. Has several options:
* center - Align objects to be centered * center - Align objects to be centered
* bottom - Align objects to the bottom * bottom - Align objects to the bottom
### ImageResize
Controls image resizing. **String** type. Has several options:
* none - Image is not resized and is only clipped by the bounding box
* fit - Image is uniformly scaled to fit inside the box
* stretch - Image is scaled (with possible stretching) to fully fill the box
### TextFit ### TextFit
Controls automatic change the text size depending on contents. **String** type. Has several options: Controls the automatic change of the text size depending on contents. **String** type. Has several options:
* none - Text is not changed in any way * none - Text is not changed in any way
* shrink - Renders text with desired size and shrinks it to fit if contents are too large * shrink - Renders text with desired size and shrinks it to fit if contents are too large
* auto - Renders text with desired size and enlarges/shrinks it to fit if contents are too small/large * auto - Renders text with desired size and enlarges/shrinks it to fit if contents are too small/large
# List of Widgets and their parameters # List of Widgets and their parameters
### image
Renders an image with optional scaling
| Name | Type | Required | Default | Description |
| ---: | :--: | :------: | ------: | :---------- |
| x | int | | 0 | Horizontal position in pixels (from left to right) |
| y | int | | 0 | Vertical position in pixels (from top to bottom) |
| width | int | REQ | | Width in pixels |
| height | int | REQ | | Height in pixels |
| filename | string | | | Filename of the image to render |
| resize_type | ImageResize | | fit | Whether to resize the image and how to do so |
| bg_color | color | | 255, 255, 255, 0 (Transparent WHITE) | The background color to fill around the image if it is not fully in the box |
| halign | HAlign | | center | Horizontal alignment of the image |
| valign | VAlign | | center | Vertical alignment of the image |
Supported image formats: **JPG**, **PNG**, **BMP**
### rect ### rect
Renders a rectangle with optional rounded corners using either fill or internal stroke. Renders a rectangle with optional rounded corners using either fill or internal stroke.
| Name | Type | Required | Default | Description | | Name | Type | Required | Default | Description |
@@ -84,7 +119,6 @@ Renders a rectangle with optional rounded corners using either fill or internal
### text ### text
Renders text within a specified box Renders text within a specified box
| Name | Type | Required | Default | Description | | Name | Type | Required | Default | Description |
| ---: | :--: | :------: | ------: | :---------- | | ---: | :--: | :------: | ------: | :---------- |
| x | int | | 0 | Horizontal position in pixels (from left to right) | | x | int | | 0 | Horizontal position in pixels (from left to right) |

View File

@@ -22,6 +22,43 @@ m_bg_color(bg_color)
} }
} }
void WidgetImage::set_filename(const string& filename)
{
if (m_image_surface != nullptr)
{
SDL_FreeSurface(m_image_surface);
m_image_surface = nullptr;
}
m_filename = filename;
m_image_surface = IMG_Load(m_filename.c_str());
if (nullptr == m_image_surface)
{
// TODO: Print errors
}
}
void WidgetImage::set_resize(ImageResize type)
{
m_resize_type = type;
}
void WidgetImage::set_halign(HorizontalAlign halign)
{
m_halign = halign;
}
void WidgetImage::set_valign(VerticalAlign valign)
{
m_valign = valign;
}
void WidgetImage::set_bg_color(SDL_Color bg_color)
{
m_bg_color = bg_color;
}
void WidgetImage::draw() void WidgetImage::draw()
{ {
if (nullptr == m_surface) if (nullptr == m_surface)

View File

@@ -7,6 +7,7 @@
#include "../sdl_helpers.h" #include "../sdl_helpers.h"
// Renders an image with optional scaling
class WidgetImage : public Widget class WidgetImage : public Widget
{ {
protected: protected:
@@ -39,6 +40,12 @@ public:
VerticalAlign valign = VALIGN_CENTER, VerticalAlign valign = VALIGN_CENTER,
SDL_Color bg_color = SDL_Color{.r = 255, .g = 255, .b = 255, .a = SDL_ALPHA_TRANSPARENT}); SDL_Color bg_color = SDL_Color{.r = 255, .g = 255, .b = 255, .a = SDL_ALPHA_TRANSPARENT});
void set_filename(const std::string& filename);
void set_resize(ImageResize type);
void set_halign(HorizontalAlign halign);
void set_valign(VerticalAlign valign);
void set_bg_color(SDL_Color bg_color);
virtual void draw() override; virtual void draw() override;
static std::unique_ptr<Widget> builder(const nlohmann::json& j); static std::unique_ptr<Widget> builder(const nlohmann::json& j);

View File

@@ -24,6 +24,21 @@ m_radius(radius)
{ {
} }
void WidgetRect::set_color(SDL_Color color)
{
m_color = color;
}
void WidgetRect::set_stroke_size(int stroke_size)
{
m_stroke_size = stroke_size;
}
void WidgetRect::set_radius(int radius)
{
m_radius = radius;
}
void WidgetRect::draw() void WidgetRect::draw()
{ {
if (nullptr == m_surface) if (nullptr == m_surface)

View File

@@ -27,6 +27,10 @@ public:
WidgetRect(int x, int y, int width, int height, WidgetRect(int x, int y, int width, int height,
int radius = 0, int stroke_size = -1); int radius = 0, int stroke_size = -1);
void set_color(SDL_Color color);
void set_stroke_size(int stroke_size);
void set_radius(int radius);
virtual void draw() override; virtual void draw() override;
static std::unique_ptr<Widget> builder(const nlohmann::json& j); static std::unique_ptr<Widget> builder(const nlohmann::json& j);

View File

@@ -168,6 +168,15 @@ void WidgetText::set_color(SDL_Color text_color)
m_text_color = text_color; m_text_color = text_color;
} }
void WidgetText::set_halign_via_visible(bool value)
{
m_halign_via_visible = value;
}
void WidgetText::set_valign_via_visible(bool value)
{
m_valign_via_visible = value;
}
void WidgetText::draw() void WidgetText::draw()
{ {
if (nullptr == m_surface) if (nullptr == m_surface)

View File

@@ -70,6 +70,8 @@ public:
void set_halign(HorizontalAlign halign); void set_halign(HorizontalAlign halign);
void set_valign(VerticalAlign valign); void set_valign(VerticalAlign valign);
void set_color(SDL_Color text_color); void set_color(SDL_Color text_color);
void set_halign_via_visible(bool value);
void set_valign_via_visible(bool value);
virtual void draw() override; virtual void draw() override;

View File

@@ -60,8 +60,18 @@ int main(int argc, char **argv)
cfg_filename = argv[1]; cfg_filename = argv[1];
} }
// Read JSON CFG // Read config
if ("-" == cfg_filename)
{
// Read JSON from std input
ok = read_config_json(cfg, std::cin);
}
else
{
// Read JSON from config file
ok = read_config_json(cfg, cfg_filename); ok = read_config_json(cfg, cfg_filename);
}
if (!ok) if (!ok)
{ {
result = -1; result = -1;

View File

@@ -141,15 +141,20 @@ bool read_config_json(json& cfg, const string& filename, ostream* log)
{ {
if (nullptr != log) if (nullptr != log)
{ {
*log << "Could not open config.json" << endl; *log << "Could not open config file" << endl;
} }
return false; return false;
} }
return read_config_json(cfg, cfg_file, log);
}
bool read_config_json(json& cfg, istream& in, ostream* log)
{
// Parse with comments // Parse with comments
try try
{ {
cfg = json::parse(cfg_file, nullptr, true, true); cfg = json::parse(in, nullptr, true, true);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {

View File

@@ -90,6 +90,12 @@ SDL_Rect surface_align(const SDL_Surface* base, const SDL_Surface* applied,
// log - output errors to this ostream, silent if NULL // log - output errors to this ostream, silent if NULL
bool read_config_json(nlohmann::json& cfg, const std::string& filename, std::ostream* log = &std::cout); bool read_config_json(nlohmann::json& cfg, const std::string& filename, std::ostream* log = &std::cout);
// Reads the stream and tries to parse JSON from it with comments
// cfg - output json struct
// in - input stream to read
// log - output errors to this ostream, silent if NULL
bool read_config_json(nlohmann::json& cfg, std::istream& in, std::ostream* log = &std::cout);
// JSON Extractors - They do not override already set values if key is not present // JSON Extractors - They do not override already set values if key is not present
void json_extract(const nlohmann::json& j, const std::string& key, std::string& out); void json_extract(const nlohmann::json& j, const std::string& key, std::string& out);