diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e9f46a8..7c536c8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,14 +19,17 @@ jobs: with: submodules: 'recursive' - - name: Compile mousebuttons example - run: make -C examples/mousebuttons/ + - name: Compile Examples + run: | + mkdir build/ + make -C examples/mousebuttons/ + make -C examples/shapes/ + make -C examples/ttf/ + make -C examples/cookieclicker/ DESTDIR=../../build shell: bash - - name: Compile shapes example - run: make -C examples/shapes/ - shell: bash - - - name: Compile ttf example - run: make -C examples/ttf/ - shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: examples-${{ runner.os }} + path: build/ diff --git a/examples/cookieclicker/main.c b/examples/cookieclicker/main.c new file mode 100644 index 0000000..5270251 --- /dev/null +++ b/examples/cookieclicker/main.c @@ -0,0 +1,142 @@ +#include "fenster.h" +#include + +#define COOKIE_BASE_SIZE 60 +#define PULSE_SPEED 2 +#define PULSE_AMOUNT 10 + +struct AutoClicker { + const char* name; + unsigned long long int cost; + unsigned long long int cps; + unsigned long long int owned; +}; + +static struct AutoClicker autoclickers[] = { + {"Cursor", 15, 1, 0}, + {"Grandma", 100, 5, 0}, + {"Farm", 1100, 25, 0}, + {"Mine", 12000, 100, 0}, + {"Factory", 130000, 500, 0} +}; + +void fenster_background_gradient(struct fenster *f, uint64_t time_ms) { + float t = time_ms / 100.0f; + uint8_t r1 = (uint8_t)(128 + 64 * sin(t * 1.0)); + uint8_t g1 = (uint8_t)(128 + 64 * sin(t * 1.2)); + uint8_t b1 = (uint8_t)(128 + 64 * sin(t * 1.4)); + uint8_t r2 = (uint8_t)(128 + 64 * sin(t * 1.5)); + uint8_t g2 = (uint8_t)(128 + 64 * sin(t * 1.7)); + uint8_t b2 = (uint8_t)(128 + 64 * sin(t * 1.9)); + + for (int y = 0; y < f->height; y++) { + float blend = (float)y / f->height; + uint8_t r = r1 + (r2 - r1) * blend; + uint8_t g = g1 + (g2 - g1) * blend; + uint8_t b = b1 + (b2 - b1) * blend; + uint32_t line_color = (r << 16) | (g << 8) | b; + size_t offset = y * f->width; + vector_fill(&f->buf[offset], f->width, line_color); + } +} + + +static int run() { + struct fenster f = {.title = "Cookie Clicker", .width = 800, .height = 600}; + fenster_open(&f); + + const uint32_t BG_COLOR = 0x2A2A2A; + const uint32_t COOKIE_COLOR = 0x8B4513; + const uint32_t TEXT_COLOR = 0xFFFFFF; + const uint32_t BUTTON_COLOR = 0x404040; + const uint32_t HIGHLIGHT_COLOR = 0x606060; + + FensterFontList fonts = fenster_loadfontlist(); + FensterFont* font = fenster_loadfont(fonts.paths[0]); + + int pulse = 0; + unsigned long long int cookies = 0; + unsigned long long int last_time = 0; + unsigned long long int cookies_per_sec = 0; + + while (fenster_loop(&f) == 0 && f.keys[27] == 0) { + fenster_background_gradient(&f, f.lastsync); + int hovering_cursor = 1; + + pulse += PULSE_SPEED; + int current_size = COOKIE_BASE_SIZE + (int)(sin(pulse / 30.0f) * PULSE_AMOUNT); + + int cookie_x = f.width / 2; + int cookie_y = f.height / 2; + fenster_drawcirc(&f, cookie_x, cookie_y, current_size, COOKIE_COLOR); + + int dx = f.mpos[0] - cookie_x; + int dy = f.mpos[1] - cookie_y; + + if (dx * dx + dy * dy < current_size * current_size) { + hovering_cursor = 2; + if (f.mclick[0]) { + cookies++; + } + } + + unsigned long long int current_time = f.lastsync; + unsigned long long int dt = current_time - last_time; + if (dt >= 1000) { + cookies_per_sec = 0; + for (int i = 0; i < 5; i++) { + cookies_per_sec += autoclickers[i].owned * autoclickers[i].cps; + } + cookies += cookies_per_sec; + last_time = current_time; + } + + fenster_drawtext(&f, font, vsformat("Cookies: %llu", cookies), 10, 30); + fenster_drawtext(&f, font, vsformat("Per Second: %llu", cookies_per_sec), 10, 60); + + const int button_width = 200; + const int button_height = 80; + const int start_y = 100; + + for (int i = 0; i < 5; i++) { + int x = 10; + int y = start_y + i * (button_height + 10); + + int hover = f.mpos[0] >= x && f.mpos[0] <= x + button_width && + f.mpos[1] >= y && f.mpos[1] <= y + button_height; + + if (hover) { + hovering_cursor = 2; + } + + fenster_drawrec(&f, x, y, button_width, button_height, hover ? HIGHLIGHT_COLOR : BUTTON_COLOR); + + fenster_drawtext(&f, font, vsformat("%s\\n Cost: %llu\\n Owned: %llu", + autoclickers[i].name, autoclickers[i].cost, autoclickers[i].owned), + x + 10, y + 10); + + if (f.mclick[0] && hover && cookies >= autoclickers[i].cost) { + cookies -= autoclickers[i].cost; + autoclickers[i].owned++; + autoclickers[i].cost = autoclickers[i].cost * 115 / 100; + } + } + + fenster_cursor(&f, hovering_cursor); + fenster_sync(&f, 60); + } + + fenster_freefont(font); + fenster_freefontlist(&fonts); + fenster_close(&f); + return 0; +} + +#if defined(_WIN32) +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) { + (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; + return run(); +} +#else +int main() { return run(); } +#endif diff --git a/examples/cookieclicker/makefile b/examples/cookieclicker/makefile new file mode 100644 index 0000000..e80d616 --- /dev/null +++ b/examples/cookieclicker/makefile @@ -0,0 +1,22 @@ +CFLAGS ?= -mavx2 -lm -DUSE_FONTS -O3 -flto -Wall -Wextra -std=c99 +DESTDIR ?= . + +ifeq ($(OS),Windows_NT) + CC = gcc + LDFLAGS = -mwindows -lgdi32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LDFLAGS = -framework Cocoa + else + LDFLAGS = -lX11 + endif +endif + +all: main + +main: main.c ../../src/fenster/fenster.h + $(CC) main.c -I../../src/fenster/ -o $(DESTDIR)/$@ $(CFLAGS) $(LDFLAGS) + +clean: + rm -f main diff --git a/src/fenster/fenster_linux.h b/src/fenster/fenster_linux.h index be1a132..4b595c8 100644 --- a/src/fenster/fenster_linux.h +++ b/src/fenster/fenster_linux.h @@ -8,6 +8,7 @@ static Atom wm_state; static Atom wm_fullscreen; static Cursor cursors[6]; static int cursors_initialized = 0; +static int current_cursor_type = 1; // clang-format on FENSTER_API int fenster_open(struct fenster *f) { @@ -174,6 +175,7 @@ FENSTER_API void fenster_fullscreen(struct fenster *f, int enabled) { } FENSTER_API void fenster_cursor(struct fenster *f, int type) { + if (type == current_cursor_type) return; if (!cursors_initialized) { cursors[0] = 0; // None/hidden cursors[1] = XCreateFontCursor(f->dpy, XC_left_ptr); // Normal arrow @@ -196,7 +198,8 @@ FENSTER_API void fenster_cursor(struct fenster *f, int type) { // Set the cursor from our pre-created cursors XDefineCursor(f->dpy, f->w, cursors[type]); } - + + current_cursor_type = type; XFlush(f->dpy); } #endif /* FENSTER_LINUX_H */ diff --git a/src/fenster/fenster_mac.h b/src/fenster/fenster_mac.h index c4e6032..fd90d9f 100644 --- a/src/fenster/fenster_mac.h +++ b/src/fenster/fenster_mac.h @@ -13,6 +13,7 @@ extern id const NSDefaultRunLoopMode; extern id const NSApp; extern id NSCursor; static id cursors[6]; +static int current_cursor_type = 1; static void fenster_window_resize(id v, SEL s, id note) { (void)s; @@ -223,6 +224,7 @@ FENSTER_API void fenster_fullscreen(struct fenster *f, int enabled) { } FENSTER_API void fenster_cursor(struct fenster *f, int type) { + if (type == current_cursor_type) return; // Initialize cursors on first use if (!cursors[1]) { // Hide cursor (NULL represents hidden cursor) @@ -248,6 +250,7 @@ FENSTER_API void fenster_cursor(struct fenster *f, int type) { // Set and push the new cursor msg(void, cursors[type], "set"); } + current_cursor_type = type; } #endif /* FENSTER_MAC_H */ diff --git a/src/fenster/fenster_windows.h b/src/fenster/fenster_windows.h index 3f9d4e0..6a35664 100644 --- a/src/fenster/fenster_windows.h +++ b/src/fenster/fenster_windows.h @@ -16,6 +16,7 @@ static WINDOWPLACEMENT g_wpPrev = { static HCURSOR cursors[6]; static int cursors_initialized = 0; +static int current_cursor_type = 1; typedef struct BINFO { BITMAPINFOHEADER bmiHeader; @@ -221,6 +222,7 @@ FENSTER_API void fenster_fullscreen(struct fenster *f, int enabled) { } FENSTER_API void fenster_cursor(struct fenster *f, int type) { + if (type == current_cursor_type) return; // Initialize cursors on first use if (!cursors_initialized) { cursors[0] = NULL; // Will be used for hidden cursor @@ -253,5 +255,6 @@ FENSTER_API void fenster_cursor(struct fenster *f, int type) { wc.hCursor = cursors[type]; SetClassLongPtr(f->hwnd, GCLP_HCURSOR, (LONG_PTR)cursors[type]); } + current_cursor_type = type; } #endif /* FENSTER_WINDOWS_H */ diff --git a/src/fenster_font.h b/src/fenster_font.h index 66c1b76..45360b9 100644 --- a/src/fenster_font.h +++ b/src/fenster_font.h @@ -152,15 +152,18 @@ FensterFontList fenster_loadfontlist(void) { } } #elif defined(_WIN32) - char path[MAX_PATH]; - if (GetEnvironmentVariable("SYSTEMROOT", path, MAX_PATH)) { - strcat(path, "\\Fonts\\*.ttf"); + char base_path[MAX_PATH]; + if (GetEnvironmentVariable("SYSTEMROOT", base_path, MAX_PATH)) { + strcat(base_path, "\\Fonts"); + char search_path[MAX_PATH]; + sprintf(search_path, "%s\\*.ttf", base_path); + WIN32_FIND_DATA fd; - HANDLE h = FindFirstFile(path, &fd); + HANDLE h = FindFirstFile(search_path, &fd); if (h != INVALID_HANDLE_VALUE) { char full[MAX_PATH]; do { - sprintf(full, "%s\\Fonts\\%s", path, fd.cFileName); + sprintf(full, "%s\\%s", base_path, fd.cFileName); add_font_path(&fonts, full); } while (FindNextFile(h, &fd)); FindClose(h);