platform layer window

This commit is contained in:
2026-04-21 17:36:13 -05:00
commit 01bb975fb5
8 changed files with 458 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) [2026] [Randy Jordan]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

152
Makefile Normal file
View File

@@ -0,0 +1,152 @@
# Compiler Flags
CC := gcc
CFLAGS := -g -Wall -Wextra -Werror -pedantic -fno-omit-frame-pointer
DEPFLAGS = -MMD -MP
# Directory Variables
LIBDIR := lib
OBJ := obj
INC := include
SRC := src
TEST := tests
# Install Paths (override with: make install PREFIX=/usr/local)
PREFIX ?= /usr/local
INSTALL_LIB = $(PREFIX)/lib
INSTALL_INC = $(PREFIX)/include
# Platform Detection and Library Linking
ifeq ($(OS), Windows_NT)
PLATFORM := win32
PLATFORM_SRCS := $(wildcard $(SRC)/win32_*.c)
PLATFORM_OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
PLATFORM_FLAGS := -D WIN32
PLATFORM_LIBS := -mwindows
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Linux)
PLATFORM := linux
PLATFORM_SRCS := $(wildcard $(SRC)/linux_*.c)
PLATFORM_OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
PLATFORM_FLAGS := -D LINUX
PLATFORM_LIBS := -lX11
else ifeq ($(UNAME_S), Darwin)
PLATFORM := mac
PLATFORM_SRCS := $(wildcard $(SRC)/mac_*.c)
PLATFORM_OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
PLATFORM_FLAGS := -D MACOS
PLATFORM_LIBS :=
else
$(error Unsupported platform: $(UNAME_S))
endif
endif
# Add platform flags
CFLAGS += $(PLATFORM_FLAGS)
# Filepath Pattern Matching
LIB := $(LIBDIR)/lib.a
# Filter out ALL platform-prefixed files, then add back the correct platform's
SRCS := $(filter-out \
$(wildcard $(SRC)/win32_*.c) \
$(wildcard $(SRC)/linux_*.c) \
$(wildcard $(SRC)/macos_*.c), \
$(wildcard $(SRC)/*.c)) \
$(PLATFORM_SRCS)
OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS))
TESTS := $(wildcard $(TEST)/*.c)
TESTBINS := $(patsubst $(TEST)/%.c, $(TEST)/bin/%, $(TESTS))
# Commands must be labeled PHONY
.PHONY: all release clean test install uninstall
# Compiler Release Flags (no sanitizers — those belong in CI/debug builds)
release: CFLAGS := -Wall -Wextra -Werror -pedantic -fno-omit-frame-pointer -O2 -DNDEBUG $(PLATFORM_FLAGS)
release: clean $(LIB)
@echo "\nRelease build complete for platform: $(PLATFORM)\n"
# Target for compilation
all: $(LIB)
@echo "\nBuilt for platform: $(PLATFORM)\n"
# Target / Dependencies
$(LIB): $(OBJS) | $(LIBDIR)
ar -cvrs $@ $^
# Compile with automatic dependency tracking
$(OBJ)/%.o: $(SRC)/%.c $(SRC)/%.h | $(OBJ)
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
$(OBJ)/%.o: $(SRC)/%.c | $(OBJ)
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
$(TEST)/bin/%: $(TEST)/%.c $(LIB) | $(TEST)/bin
$(CC) $(CFLAGS) $< $(LIB) $(PLATFORM_LIBS) -o $@
# Make directories if none
$(LIBDIR):
mkdir $@
$(OBJ):
mkdir $@
$(TEST)/bin:
mkdir -p $@
# Run the tests in the bin folder and track results
# Uses perl for nanosecond timing — portable across Linux and macOS
test: $(LIB) $(TESTBINS)
@SUCCESS=0; FAILURE=0; \
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'; \
for t in $(TESTBINS); do \
NAME=$$(basename $$t); \
START=$$(perl -MTime::HiRes=time -e 'printf "%d\n", time()*1000'); \
if $$t; then \
RET=0; \
else \
RET=$$?; \
fi; \
END=$$(perl -MTime::HiRes=time -e 'printf "%d\n", time()*1000'); \
ELAPSED_MS=$$((END - START)); \
if [ $$RET -eq 0 ]; then \
printf "%-20s %bPASS%b (%b%4d ms%b)\n" "$$NAME" "$$GREEN" "$$NC" "$$YELLOW" "$$ELAPSED_MS" "$$NC"; \
SUCCESS=$$((SUCCESS + 1)); \
else \
printf "%-20s %bFAIL%b (%b%4d ms%b)\n" "$$NAME" "$$RED" "$$NC" "$$YELLOW" "$$ELAPSED_MS" "$$NC"; \
FAILURE=$$((FAILURE + 1)); \
fi; \
done; \
printf "\nTests completed\n"; \
printf "SUCCESS: %b%d%b\n" "$$GREEN" "$$SUCCESS" "$$NC"; \
printf "FAILURE: %b%d%b\n" "$$RED" "$$FAILURE" "$$NC"; \
test $$FAILURE -eq 0
# Install library and public headers
install: $(LIB)
@echo "Installing to $(PREFIX)..."
install -d $(INSTALL_LIB)
install -d $(INSTALL_INC)
install -m 644 $(LIB) $(INSTALL_LIB)/
@if [ -d $(INC) ]; then \
install -m 644 $(INC)/*.h $(INSTALL_INC)/; \
echo "Installed headers from $(INC)/"; \
else \
echo "Warning: no $(INC)/ directory found — skipping header install"; \
fi
@echo "Install complete: lib -> $(INSTALL_LIB) headers -> $(INSTALL_INC)"
uninstall:
@echo "Removing installed files from $(PREFIX)..."
$(RM) $(INSTALL_LIB)/lib.a
@if [ -d $(INC) ]; then \
for h in $(INC)/*.h; do \
$(RM) $(INSTALL_INC)/$$(basename $$h); \
done; \
fi
@echo "Uninstall complete"
clean:
$(RM) -r $(LIBDIR) $(OBJ) $(TEST)/bin/
# Pull in generated dependency files (silently ignored if absent)
-include $(DEPS)

70
README.md Normal file
View File

@@ -0,0 +1,70 @@
# ci2
## Description
Inspired by the [book](https://github.com/drh/cii), and others, this is my
personal C codebase. I wanted to improve my C and DSA knowledge, and make some
useful tools since C doesn't have many language features. This is meant to be
opinionated, and not necessarily modular. It's not meant to be the best way,
but I hope to improve it overtime.
## Table of Contents
* [Features](#features)
* [Todos](#todos)
* [Usage](#usage)
* [Acknowledgments](#acknowledgments)
* [License](#license)
## Features
## Todos
- [ ] Better makefile
- [ ] Better Usage Examples
- [ ] Basic Window
- [ ] Exceptions & Try/Catch
- [ ] Memory Allocator
- [ ] Arena Allocator
- [ ] Arrays
- [ ] Atoms / Quarks
- [ ] Linked List
- [ ] Stacks
- [ ] Queues
- [ ] Hash Table
- [ ] Serialization
## Usage
Provide instructions and examples on how to use your project.
```bash
# Example installation
git clone https://github.com/your-username/your-repo.git
# Navigate into the directory
cd your-repo
# Run the project
npm install
npm start
```
You can also include code snippets or screenshots demonstrating usage.
---
## Acknowledgments
[C Interfaces and Implementations](https://github.com/drh/cii)<br>
[Sean Barrett - Advice for Writing Small Programs in C](https://www.youtube.com/watch?v=eAhWIO1Ra6M)<br>
[Eskil Steenberg - How I Program C](https://www.youtube.com/watch?v=443UNeGrFoM)<br>
[Handmade Hero](https://www.youtube.com/playlist?list=PLnuhp3Xd9PYTt6svyQPyRO_AAuMWGxPzU)<br>
[Mr. 4th Programming](https://www.youtube.com/playlist?list=PLT6InxK-XQvNKTyLXk6H6KKy12UYS_KDL)<br>
[Chris Wellons](https://nullprogram.com/)<br>
[Ginger Bill](https://www.gingerbill.org/)<br>
[Tsoding Daily](https://www.youtube.com/@TsodingDaily)<br>
[Jacob Sorber](https://www.youtube.com/channel/UCwd5VFu4KoJNjkWJZMFJGHQ)<br>
## License
This project is licensed under the MIT License - see the [MIT License](LICENSE) file for details.

6
include/ci2.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef CI2_INCLUDED
#define CI2_INCLUDED
extern void ci2_printf()
#endif

25
include/ci2_window.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef CI2_WINDOW_H
#define CI2_WINDOW_H
#include <stdint.h>
#include <stdbool.h>
// Opaque type for a window
typedef struct CI2_Window CI2_Window;
// Initializes the platform layer (must be called before creating a window)
bool platform_init(void);
// Creates a window, returns NULL on failure
CI2_Window* platform_create_window(const char* title, int width, int height);
// Polls platform events. Returns false if the window should close
bool platform_poll_events(CI2_Window* window);
// Destroys a window
void platform_destroy_window(CI2_Window* window);
// Shuts down the platform layer
void platform_shutdown(void);
#endif // CI2_WINDOW_H

78
src/linux_window.c Normal file
View File

@@ -0,0 +1,78 @@
#include "../include/ci2_window.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct CI2_Window {
Display* display;
Window window;
Atom wm_delete_window;
bool should_close;
};
bool platform_init(void) {
return true; // Nothing global needed for X11
}
CI2_Window* platform_create_window(const char* title, int width, int height) {
Display* display = XOpenDisplay(NULL);
if (!display) return NULL;
int screen = DefaultScreen(display);
Window win = XCreateSimpleWindow(display, RootWindow(display, screen),
0, 0, width, height, 1,
BlackPixel(display, screen),
WhitePixel(display, screen));
XStoreName(display, win, title);
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, win, &wm_delete_window, 1);
XMapWindow(display, win);
XFlush(display);
CI2_Window* window = malloc(sizeof(CI2_Window));
if (!window) {
XDestroyWindow(display, win);
XCloseDisplay(display);
return NULL;
}
window->display = display;
window->window = win;
window->wm_delete_window = wm_delete_window;
window->should_close = false;
return window;
}
bool platform_poll_events(CI2_Window* window) {
if (!window) return false;
while (XPending(window->display)) {
XEvent event;
XNextEvent(window->display, &event);
if (event.type == ClientMessage &&
(Atom)event.xclient.data.l[0] == window->wm_delete_window) {
window->should_close = true;
}
}
return !window->should_close;
}
void platform_destroy_window(CI2_Window* window) {
if (!window) return;
XDestroyWindow(window->display, window->window);
XCloseDisplay(window->display);
free(window);
}
void platform_shutdown(void) {
// Nothing global needed
}

89
src/win32_window.c Normal file
View File

@@ -0,0 +1,89 @@
#include "../include/ci2_window.h"
#include <windows.h>
#include <stdlib.h>
#include <stdbool.h>
struct CI2_Window {
HWND hwnd;
bool should_close;
};
// Forward declaration of WndProc
static LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
bool platform_init(void) {
// Nothing to do for Win32
return true;
}
CI2_Window* platform_create_window(const char* title, int width, int height) {
static bool class_registered = false;
if (!class_registered) {
WNDCLASS wc = {0};
wc.lpfnWndProc = window_proc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "CI2_WindowClass";
RegisterClass(&wc);
class_registered = true;
}
HWND hwnd = CreateWindowEx(
0,
"CI2_WindowClass",
title,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
NULL, NULL,
GetModuleHandle(NULL),
NULL
);
if (!hwnd) return NULL;
CI2_Window* window = malloc(sizeof(CI2_Window));
if (!window) {
DestroyWindow(hwnd);
return NULL;
}
window->hwnd = hwnd;
window->should_close = false;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window);
return window;
}
bool platform_poll_events(CI2_Window* window) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return !window->should_close;
}
void platform_destroy_window(CI2_Window* window) {
if (!window) return;
DestroyWindow(window->hwnd);
free(window);
}
void platform_shutdown(void) {
// Nothing to do
}
static LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
CI2_Window* window = (CI2_Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (uMsg) {
case WM_CLOSE:
if (window) window->should_close = true;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}

17
tests/01_window.c Normal file
View File

@@ -0,0 +1,17 @@
#include "../include/ci2_window.h"
#include <stdio.h>
int main(void) {
if (!platform_init()) return 1;
CI2_Window* window = platform_create_window("Hello", 800, 600);
if (!window) return 1;
while (platform_poll_events(window)) {
// Your rendering / game loop goes here
}
platform_destroy_window(window);
platform_shutdown();
return 0;
}