Refactoring, license, Box UI framework

This commit is contained in:
2025-10-17 13:41:37 +02:00
parent 0222bc1f90
commit eeee5aba7d
10 changed files with 322 additions and 319 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.o
main

172
box.c
View File

@@ -1,112 +1,136 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "box.h" #include "box.h"
#include "ui.h"
#include "util.h"
static void draw_rectangle(size_t x, size_t y, size_t width, size_t height)
static void draw_rectangle(size_t x, size_t y, size_t width, size_t height);
static void draw_vertical(struct box *, size_t x, size_t y, size_t width, size_t height);
static void draw_horizontal(struct box *, size_t x, size_t y, size_t width, size_t height);
static void draw_button(struct box *, size_t x, size_t y, size_t width, size_t height);
static void
draw_rectangle(size_t x, size_t y, size_t width, size_t height)
{ {
mvaddch(y, x, '+'); size_t w = width - 1;
mvaddch(y + height - 1, x, '+'); size_t h = height - 1;
mvaddch(y + height - 1, x + width - 1, '+');
mvaddch(y, x + width - 1, '+');
for (size_t i = 1; i < height - 1; i++) { /* corners */
mvaddch(y + i, x, '|');
ui_char(x, y, '+');
ui_char(x, y + h, '+');
ui_char(x + w, y + h, '+');
ui_char(x + w, y, '+');
/* vertical borders */
for (size_t i = 1; i < h; i++) {
ui_char(x, y + i, '|');
} }
for (size_t i = 1; i < height - 1; i++) { for (size_t i = 1; i < h; i++) {
mvaddch(y + i, x + width - 1, '|'); ui_char(x + w, y + i, '|');
} }
for (size_t i = 1; i < width - 1; i++) { /* horizontal borders */
mvaddch(y, x + i, '-');
for (size_t i = 1; i < w; i++) {
ui_char(x + i, y, '-');
} }
for (size_t i = 1; i < width - 1; i++) { for (size_t i = 1; i < w; i++) {
mvaddch(y + height - 1, x + i, '-'); ui_char(x + i, y + h, '-');
} }
} }
void draw_button(struct box *b)
{ static void
draw_rectangle(x, y, b->width, b->height); draw_button(struct box *b, size_t x, size_t y, size_t width, size_t height)
if (b->highlighted) {
attron(A_UNDERLINE);
}
attron(COLOR_PAIR(b->color));
mvaddstr(y + b->height / 2, x + b->width / 2 - strlen(b->name) / 2, b->name);
if (b->highlighted) {
attroff(A_UNDERLINE);
}
attroff(COLOR_PAIR(b->color));
}
void draw_chart(struct box *b, size_t x, size_t y, size_t width, size_t height)
{ {
ui_color_on(b->color);
draw_rectangle(x, y, width, height); draw_rectangle(x, y, width, height);
ui_string(x + width / 2 - strlen(b->name) / 2, y + height / 2, b->name);
size_t chart_width = width - 2; ui_color_off(b->color);
size_t chart_height = height - 2;
size_t chart_x = x + 1;
size_t chart_y = y + 1;
for (size_t i = 0; i < chart->length; i++) {
for (size_t j = 0; j < chart->prices[i]; j++) {
mvaddch(chart_height - (chart_y + j), chart_x * i, '?');
}
}
} }
void draw_vertical(struct box *b, size_t x, size_t y, size_t width, size_t height)
static void
draw_horizontal(struct box *b, size_t x, size_t y, size_t width, size_t height)
{
die("Don't call me");
}
static void
draw_vertical(struct box *b, size_t x, size_t y, size_t width, size_t height)
{ {
size_t total_pixels = 0;
double total_fills = 0; double total_fills = 0;
for (size_t i = 0; i < b->length; i++) { for (size_t i = 0; i < b->length; i++) {
struct box *child = b->children[i]; total_fills += b->children[i]->fills;
switch (child.length.type) {
case LENGTH_PIXEL:
total_pixels += child.length.pixels;
break;
case LENGTH_FILL:
total_fills += child.length.fills;
break;
}
} }
size_t fill_pixels = height - total_pixels; double pixels_per_fill = (double)height / total_fills;
double pixels_per_fill = fill_pixels / total_fills;
double current_y = 0; double current_y = y;
for (size_t i = 0; i < b->length; i++) { for (size_t i = 0; i < b->length; i++) {
struct box *child = b->children[i]; struct box *child = b->children[i];
double height = child->fills * pixels_per_fill;
double width; draw_box(child, x, current_y, width, height);
current_y += height;
switch (child.length.type) {
case LENGTH_PIXEL:
width = child.length.pixels;
break;
case LENGTH_FILL:
width = child.length.fills * pixels_per_fill;
break;
}
} }
} }
void draw_box(struct box *b, size_t x, size_t y, size_t width, size_t height)
struct box *
new_button_box(char *name)
{
struct box *button = malloc(sizeof(struct box));
button->type = BOX_BUTTON;
button->name = name;
button->fills = 1;
button->color = UI_RED;
return button;
}
struct box *
new_vertical_box(void)
{
struct box *box = emalloc(sizeof(struct box));
box->type = BOX_VERTICAL;
box->fills = 1;
box->color = UI_RED;
box->children = emalloc(sizeof(struct box *) * 100);
box->length = 0;
return box;
}
void
draw_box(struct box *b, size_t x, size_t y, size_t width, size_t height)
{ {
switch (b->type) { switch (b->type) {
case BOX_BUTTON: case BOX_BUTTON:
draw_button(b, x, y, width, height); draw_button(b, x, y, width, height);
break; break;
case BOX_CHART:
draw_chart(b, x, y, width, height);
break;
case BOX_VERTICAL: case BOX_VERTICAL:
draw_vertical(b, x, y, width, height); draw_vertical(b, x, y, width, height);
break; break;

72
box.h
View File

@@ -1,54 +1,44 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#pragma once #pragma once
#include "chart.h" #include "ui.h"
enum box_type { enum box_type {
BOX_VERTICAL, BOX_VERTICAL,
BOX_HORIZONTAL, BOX_HORIZONTAL,
BOX_CHART, BOX_BUTTON,
BOX_BUTTON,
};
enum length_type {
LENGTH_FILL,
LENGTH_PIXEL,
};
struct length {
enum length_type type;
union {
double fill;
double pixel;
};
}; };
struct box { struct box {
enum box_type; enum box_type type;
enum ui_color color;
double fills;
size_t width; union {
size_t height; /* button */
struct {
char *name;
};
enum color color; /* container boxes */
struct {
union { struct box **children;
struct chart *chart; size_t length;
};
/* button */
struct {
char *name;
}; };
/* container boxes */
struct {
struct box *children;
size_t length;
};
};
}; };
box *new_chart_box(struct box *, size_t width, size_t height);
box *new_button_box(struct button *, size_t width, size_t height);
box *new_vertical_box(size_t width, size_t height);
box *new_horizontal_box(size_t width, size_t height);
void draw_box(struct box *); struct box *new_button_box(char *name);
struct box *new_vertical_box(void);
struct box *new_horizontal_box(void);
void draw_box(struct box *, size_t x, size_t y, size_t width, size_t height);

14
license Normal file
View File

@@ -0,0 +1,14 @@
Copyright (c) 2025 shit-co.de
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

225
main.c
View File

@@ -1,214 +1,35 @@
#include <stdio.h>
#include <unistd.h> /*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <unistd.h>
#include <curses.h>
#include <math.h>
#include <time.h>
struct coin { #include "box.h"
const char *name; #include "ui.h"
const char *description;
struct chart chart;
};
struct chart {
double *prices;
size_t length;
};
struct chart new_chart(size_t length) int
main(void)
{ {
double *prices = malloc(length * sizeof(double)); struct box *parent;
prices[0] = 20; ui_init();
for (size_t i = 1; i < length; i++) {
prices[i] = prices[i - 1] + (rand() % 3 - 1.5);
}
return (struct chart) { parent = new_vertical_box();
.length = length,
.prices = prices
};
}
enum game_state { parent->length = 2;
GAME_BEGIN, parent->children[0] = new_button_box("hello!");
GAME_IDLE, parent->children[0]->color = UI_BLUE;
GAME_BUY, parent->children[1] = new_button_box("quit");
GAME_SELL,
GAME_END,
};
enum color { draw_box(parent, 0, 0, 20, 30);
color_red, ui_refresh();
color_green,
color_blue,
};
struct button *new_button(char *name, enum color color, size_t width, size_t height)
{
struct button *button = malloc(sizeof(struct button));
button->name = name;
button->width = width;
button->height = height;
button->next = NULL;
button->color = color;
button->highlighted = false;
return button;
}
struct game {
enum game_state state;
int bank;
struct coin coin;
struct button *button;
};
void draw_button(struct button *b, size_t x, size_t y)
{
draw_box(x, y, b->width, b->height);
if (b->highlighted) {
attron(A_UNDERLINE);
}
attron(COLOR_PAIR(b->color));
mvaddstr(y + b->height / 2, x + b->width / 2 - strlen(b->name) / 2, b->name);
if (b->highlighted) {
attroff(A_UNDERLINE);
}
attroff(COLOR_PAIR(b->color));
}
void draw_chart(struct chart *chart, size_t x, size_t y)
{
const size_t height = 30;
const size_t width = chart->length * 2 + 1;
draw_box(x, y, width, height + 1);
for (size_t i = 0; i < chart->length; i++) {
for (size_t j = 0; j < chart->prices[i]; j++) {
mvaddch(30 - (y + j), x + (i * 2) + 1, '?');
}
}
const size_t button_width = (double)(width) / 3;
struct button *buy = new_button("buy", color_red, button_width, 5);
struct button *sell = new_button("sell", color_blue, button_width, 5);
struct button *scam = new_button("scam", color_green, button_width, 5);
buy->next = sell;
sell->next = scam;
scam->next = buy;
buy->highlighted = true;
draw_button(buy, x, y + height + 1);
draw_button(sell, x + button_width, y + height + 1);
draw_button(scam, x + button_width * 2, y + height + 1);
}
enum box_type {
BOX_TYPE_BUTTON,
BOX_TYPE_CHART,
BOX_TYPE_HORIZONTAL,
BOX_TYPE_VERTICAL,
};
struct box {
enum box_type type;
size_t width;
size_t height;
enum color color;
union {
/* chart */
struct {
double *prices;
size_t length;
};
/* button */
char *name;
/* horizontal/vertical box */
struct {
struct box *children;
size_t length;
};
}
};
void draw_game(struct game *game)
{
clear();
mvaddstr(0, 0, game->coin.name);
mvaddstr(1, 0, game->coin.description);
draw_chart(&game->coin.chart, 5, 5);
refresh();
}
struct box *new_chart_box()
struct box *new_container_box(enum box_type type)
{
struct box *box = malloc(sizeof(box));
box->width = 0;
box->height = 0;
box->type = type;
box->children = malloc(sizeof(box) * 100);
box->length = 0;
return box;
}
struct box *new_vertical_box(void)
{
return new_container_box(BOX_TYPE_VERTICAL);
}
struct box *new_horizontal_box(void)
{
return new_container_box(BOX_TYPE_HORIZONTAL);
}
struct box *new
int main(void)
{
srand(time(NULL));
initscr();
start_color();
init_pair(color_green, COLOR_GREEN, COLOR_BLACK);
init_pair(color_red, COLOR_RED, COLOR_BLACK);
init_pair(color_blue, COLOR_BLUE, COLOR_BLACK);
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
clear();
refresh();
struct game game = {
.state = GAME_BEGIN,
.bank = 1000,
.coin = (struct coin) {
.name = "Bitcoin",
.description = "A fucking scam.",
.chart = new_chart(30)
}
};
draw_game(&game);
refresh();
sleep(300); sleep(300);
endwin();
ui_end();
} }

View File

@@ -1,2 +1,14 @@
CFLAGS = -lcurses -std=c23 -Wextra -pedantic -Wall CFLAGS = -std=c23 -Wextra -pedantic -Wall -pedantic
main: main.c LDFLAGS = -lncurses
main: box.o main.o ui.o util.o
.PHONY: clean
box.o: box.c
main.o: main.c
ui.o: ui.c
util.o: util.c
clean:
rm *.o main

64
ui.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#include <curses.h>
#include <stdlib.h>
#include <time.h>
#include "ui.h"
void ui_init(void)
{
srand(time(NULL));
initscr();
start_color();
init_pair(UI_GREEN, COLOR_GREEN, COLOR_BLACK);
init_pair(UI_RED, COLOR_RED, COLOR_BLACK);
init_pair(UI_BLUE, COLOR_BLUE, COLOR_BLACK);
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
clear();
refresh();
}
void ui_end()
{
endwin();
}
void ui_string(size_t x, size_t y, char *s)
{
mvaddstr(y, x, s);
}
void ui_char(size_t x, size_t y, char c)
{
mvaddch(y, x, c);
}
void ui_color_on(enum ui_color c)
{
attron(COLOR_PAIR(c));
}
void ui_color_off(enum ui_color c)
{
attroff(COLOR_PAIR(c));
}
void ui_refresh(void)
{
refresh();
}
void ui_clear(void)
{
clear();
}

28
ui.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#pragma once
enum ui_color {
UI_RED = 1,
UI_GREEN,
UI_BLUE,
};
void ui_init(void);
void ui_end(void);
void ui_color_on(enum ui_color);
void ui_color_off(enum ui_color);
void ui_char(size_t x, size_t y, char c);
void ui_string(size_t x, size_t y, char *s);
void ui_refresh(void);
void ui_clear(void);

34
util.c Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "util.h"
void *
emalloc(size_t n)
{
void *p = malloc(n);
if (p == NULL) {
die("malloc fail\n");
}
return p;
}
void
die(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
exit(1);
}

14
util.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* Copyright (C) Artsiom D.
* Copyright (C) shit-co.de
*/
#pragma once
#include <stddef.h>
void *emalloc(size_t n);
void die(char *fmt, ...);