188 lines
6.4 KiB
C++
188 lines
6.4 KiB
C++
#include <SFML/Graphics.hpp>
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include <cmath>
|
|
#include <unistd.h>
|
|
#include <sys/select.h>
|
|
#include <termios.h>
|
|
#include "low_level_render.h"
|
|
#include "uiexample1.h"
|
|
#include "screens/girl_at_desk.h" // only works with 400x300 images
|
|
|
|
const int V_WIDTH = 400;
|
|
const int V_HEIGHT = 300;
|
|
uint8_t bit_buffer[V_WIDTH * V_HEIGHT / 8];
|
|
|
|
char command_buffer[256];
|
|
int command_buffer_index = 0;
|
|
|
|
int main()
|
|
{
|
|
// Save original terminal settings
|
|
struct termios old_tio, new_tio;
|
|
tcgetattr(STDIN_FILENO, &old_tio);
|
|
new_tio = old_tio;
|
|
new_tio.c_lflag &= (~ICANON & ~ECHO); // Disable canonical mode and echo
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
|
|
|
|
sf::RenderWindow window(sf::VideoMode(400, 300), "0.5Hz Update Emulator");
|
|
|
|
std::vector<sf::Uint8> display_pixels(V_WIDTH * V_HEIGHT * 4);
|
|
sf::Texture texture;
|
|
texture.create(V_WIDTH, V_HEIGHT);
|
|
texture.setSmooth(false); // Keeps the 1-bit pixels sharp and blocky
|
|
sf::Sprite sprite(texture);
|
|
sprite.setScale(1.f, 1.f);
|
|
|
|
LowLevelRenderer renderer(bit_buffer, V_WIDTH, V_HEIGHT);
|
|
|
|
sf::Clock clock;
|
|
bool toggle = false;
|
|
bool inverted = false;
|
|
|
|
while (window.isOpen())
|
|
{
|
|
sf::Event event;
|
|
while (window.pollEvent(event))
|
|
{
|
|
if (event.type == sf::Event::Closed)
|
|
window.close();
|
|
}
|
|
|
|
// Check for console input with timeout
|
|
fd_set readfds;
|
|
FD_ZERO(&readfds);
|
|
FD_SET(STDIN_FILENO, &readfds);
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 10000; // 10ms timeout
|
|
int retval = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
|
|
if (retval > 0)
|
|
{
|
|
char c = getchar();
|
|
//printf("Key Pressed: %c\n", c);
|
|
if (c == '\n' || c == '\r')
|
|
{
|
|
printf("Command entered: %s\n", command_buffer);
|
|
command_buffer_index = 0;
|
|
command_buffer[0] = '\0';
|
|
}
|
|
else if (c == '\b' || c == 127) // Backspace
|
|
{
|
|
if (command_buffer_index > 0)
|
|
{
|
|
command_buffer_index--;
|
|
command_buffer[command_buffer_index] = '\0';
|
|
}
|
|
}
|
|
else if (c >= 32 && c <= 126) // Printable characters
|
|
{
|
|
if (command_buffer_index < sizeof(command_buffer) - 1)
|
|
{
|
|
command_buffer[command_buffer_index++] = c;
|
|
command_buffer[command_buffer_index] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- TIMING LOGIC: Update twice per second (500ms) ---
|
|
if (clock.getElapsedTime().asMilliseconds() >= 500)
|
|
{
|
|
clock.restart();
|
|
toggle = !toggle; // Change something every half second
|
|
|
|
// Invert display every 4 seconds (8 cycles)
|
|
static int cycle_count = 0;
|
|
cycle_count++;
|
|
if (cycle_count % 8 == 0) {
|
|
inverted = !inverted;
|
|
}
|
|
|
|
// Clear buffer
|
|
for (int i = 0; i < sizeof(bit_buffer); i++)
|
|
bit_buffer[i] = 0;
|
|
|
|
/*
|
|
// Showcase drawing functions
|
|
// Rectangles
|
|
renderer.draw_rectangle(10, 50, 80, 60, true);
|
|
renderer.draw_filled_rectangle(110, 50, 80, 60, true);
|
|
renderer.draw_rounded_rectangle(210, 50, 80, 60, 10, true);
|
|
|
|
// Triangles
|
|
renderer.draw_triangle(20, 130, 80, 130, 50, 180, true);
|
|
renderer.draw_filled_triangle(120, 130, 180, 130, 150, 180, true);
|
|
|
|
// Polygons
|
|
std::vector<std::pair<int, int>> hex_points = {
|
|
{250, 130}, {280, 145}, {280, 175}, {250, 190}, {220, 175}, {220, 145}
|
|
};
|
|
renderer.draw_polygon(hex_points, true);
|
|
|
|
std::vector<std::pair<int, int>> star_points = {
|
|
{320, 140}, {325, 155}, {340, 155}, {330, 170}, {335, 185},
|
|
{320, 175}, {305, 185}, {310, 170}, {300, 155}, {315, 155}
|
|
};
|
|
renderer.draw_filled_polygon(star_points, true);
|
|
|
|
// Arcs
|
|
renderer.draw_arc(60, 270, 25, 45, 315, true); // Pac-man shape
|
|
renderer.draw_arc(160, 270, 25, 0, 180, true); // Semicircle
|
|
|
|
// Ellipses
|
|
renderer.draw_ellipse(60, 220, 30, 20, true);
|
|
renderer.draw_filled_ellipse(160, 220, 30, 20, true);
|
|
|
|
// Circles
|
|
renderer.draw_circle(60, 160, 25, true);
|
|
renderer.draw_filled_circle(160, 160, 25, true);
|
|
renderer.draw_circle(260, 160, 15, true); // Smaller circle
|
|
|
|
// Lines
|
|
renderer.draw_line(10, 200, 90, 250, true);
|
|
renderer.draw_line(110, 200, 190, 250, true);
|
|
|
|
// Demonstrate clipping - draw a circle that gets clipped
|
|
renderer.set_clip_rect(300, 200, 80, 60); // Small rectangle in bottom-right
|
|
renderer.draw_filled_circle(340, 230, 40, true); // Circle that extends outside clip rect
|
|
renderer.reset_clip_rect(); // Reset to full screen
|
|
|
|
// Text with different fonts
|
|
renderer.set_font(&font_acme_5_outlines);
|
|
renderer.draw_string_scaled(10, 10, "Drawing Demo", 2);
|
|
|
|
*/
|
|
|
|
renderer.set_font(&font_SUPERDIG);
|
|
renderer.draw_string_scaled(10, 270, command_buffer, 1);
|
|
|
|
|
|
|
|
// Draw the bitmap image
|
|
renderer.set_text_color(true);
|
|
renderer.draw_bitmap(girl_at_desk, 0, 0, 400, 300, true);
|
|
|
|
if(inverted) renderer.invert_buffer();
|
|
|
|
// Bridge: 1-bit to RGBA
|
|
for (int i = 0; i < V_WIDTH * V_HEIGHT; ++i)
|
|
{
|
|
bool is_on = (bit_buffer[i / 8] >> (7 - (i % 8))) & 1;
|
|
int base = i * 4;
|
|
sf::Uint8 color = is_on ? 0xFF : 0x00;
|
|
display_pixels[base] = display_pixels[base + 1] = display_pixels[base + 2] = color;
|
|
display_pixels[base + 3] = 255;
|
|
}
|
|
texture.update(display_pixels.data());
|
|
}
|
|
|
|
window.clear();
|
|
window.draw(sprite);
|
|
window.display();
|
|
|
|
}
|
|
|
|
// Restore original terminal settings
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
|
|
return 0;
|
|
} |