Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
libmarl.so
|
||||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2026 Maxwell Jeffress
|
||||||
|
|
||||||
|
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.
|
||||||
29
Makefile
Normal file
29
Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
HEADER = src/marl.h
|
||||||
|
CC = cc
|
||||||
|
TARGET = libmarl.so
|
||||||
|
FLAGS = -shared -fPIC
|
||||||
|
|
||||||
|
SRCS_X11 = src/x11/marl.c
|
||||||
|
FLAGS_X11 = -lxcb -lm
|
||||||
|
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
DESTDIR ?=
|
||||||
|
|
||||||
|
.PHONY: default
|
||||||
|
default: x11
|
||||||
|
|
||||||
|
.PHONY: x11
|
||||||
|
x11: $(SRCS_X11)
|
||||||
|
$(CC) $(SRCS_X11) $(FLAGS_X11) $(FLAGS) -o $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: $(TARGET)
|
||||||
|
mkdir -p $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/lib
|
||||||
|
cp $(HEADER) $(DESTDIR)$(PREFIX)/include/
|
||||||
|
cp $(TARGET) $(DESTDIR)$(PREFIX)/lib/
|
||||||
|
echo "$(DESTDIR)$(PREFIX)/lib" | tee $(DESTDIR)/etc/ld.so.conf.d/marl.conf
|
||||||
|
ldconfig
|
||||||
82
README.md
Normal file
82
README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Max's Awesome Rendering Library
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <marl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Marl_Window* window = Marl.createWindow("dingus", 800, 600);
|
||||||
|
if (window == NULL) {
|
||||||
|
printf("Window didn't create lmao\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl_Font* font = Marl.Font.load("/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 24.0f);
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
Marl_Event e = Marl.poll(window);
|
||||||
|
if (e.type == Marl_Quit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (e.type == Marl_MouseDown) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
char buf[256];
|
||||||
|
if (counter == 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Click the window!");
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "You clicked %d times!", counter);
|
||||||
|
}
|
||||||
|
Marl.clearScreen(window, 0xFF000000);
|
||||||
|
Marl.drawRect(window, 100, 100, 200, 150, 0xFF0000FF);
|
||||||
|
Marl.drawText(window, font, buf, 100, 80, 0xFFFFFFFF);
|
||||||
|
Marl.render(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl.Font.destroy(&font);
|
||||||
|
Marl.destroyWindow(&window);
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# make <backend>
|
||||||
|
|
||||||
|
# Example
|
||||||
|
make x11
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently supported backends:
|
||||||
|
|
||||||
|
* `x11`
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Build your preferred backend, then:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
gcc myprogram.c -lmarl -o myprogram
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating your own backend
|
||||||
|
|
||||||
|
Create a new folder in `src` with the name of your backend, and copy the `template/marl.c` file into the folder.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Marl is licensed to you under the MIT license.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* stb_truetype.h: Font rendering on x11
|
||||||
|
* XCB: x11 backend
|
||||||
5079
src/include/stb_truetype.h
Normal file
5079
src/include/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
88
src/marl.h
Normal file
88
src/marl.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#ifndef MARL_H
|
||||||
|
#define MARL_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marl
|
||||||
|
* Copyright 2026 Maxwell Jeffress
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct Marl_Window Marl_Window;
|
||||||
|
typedef struct Marl_Font Marl_Font;
|
||||||
|
|
||||||
|
typedef enum Marl_EventType {
|
||||||
|
Marl_None,
|
||||||
|
Marl_Quit,
|
||||||
|
Marl_MouseMove,
|
||||||
|
Marl_MouseDown,
|
||||||
|
Marl_MouseUp,
|
||||||
|
} Marl_EventType;
|
||||||
|
|
||||||
|
typedef enum Marl_MouseButton {
|
||||||
|
Marl_MouseLeft = 1,
|
||||||
|
Marl_MouseMiddle = 2,
|
||||||
|
Marl_MouseRight = 3,
|
||||||
|
} Marl_MouseButton;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Marl_EventType type;
|
||||||
|
int x, y;
|
||||||
|
Marl_MouseButton button;
|
||||||
|
} Marl_Event;
|
||||||
|
|
||||||
|
struct _Marl {
|
||||||
|
/*
|
||||||
|
* Creates a heap-allocated Marl window.
|
||||||
|
* Marl owns this memory, you do not need to free it,
|
||||||
|
* only destroy it with Marl.destroyWindow when required.
|
||||||
|
*/
|
||||||
|
Marl_Window* (*createWindow)(const char* title, int width, int height);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls the backend for events.
|
||||||
|
*/
|
||||||
|
Marl_Event (*poll)(Marl_Window* window);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clears the screen using your colour of choice.
|
||||||
|
*/
|
||||||
|
void (*clearScreen)(Marl_Window* window, uint32_t colour);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draws the specified rectangle in the window.
|
||||||
|
*/
|
||||||
|
void (*drawRect)(Marl_Window* window, int x, int y, int width, int height, uint32_t colour);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draws the specified text in the window.
|
||||||
|
*/
|
||||||
|
void (*drawText)(Marl_Window* window, Marl_Font* font, const char* text, int x, int y, uint32_t colour);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Renders a Marl_Window frame.
|
||||||
|
*/
|
||||||
|
int (*render)(Marl_Window* window);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroys a Marl_Window.
|
||||||
|
* You do not need to free the pointer afterwards.
|
||||||
|
*/
|
||||||
|
void (*destroyWindow)(Marl_Window** window);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Marl_Font* (*load)(const char* path, float size);
|
||||||
|
void (*destroy)(Marl_Font** font);
|
||||||
|
int (*textWidth)(Marl_Font* font, const char* text);
|
||||||
|
} Font;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _Marl Marl;
|
||||||
|
|
||||||
|
#endif // MARL_H
|
||||||
59
src/template/marl.c
Normal file
59
src/template/marl.c
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Template for a Marl implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "marl.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct Marl_Window {};
|
||||||
|
struct Marl_Font {};
|
||||||
|
|
||||||
|
Marl_Window* _marl_createWindow(const char* title, int width, int height) {
|
||||||
|
return malloc(sizeof(Marl_Window));
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl_Event _marl_poll(Marl_Window* window) {
|
||||||
|
return (Marl_Event){Marl_None, 0, 0, Marl_MouseLeft};
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_clearScreen(Marl_Window* window, uint32_t colour) {}
|
||||||
|
|
||||||
|
void _marl_drawRect(Marl_Window* window, int x, int y, int width, int height, uint32_t colour) {}
|
||||||
|
|
||||||
|
void _marl_drawText(Marl_Window* window, Marl_Font* font, const char* text, int x, int y, uint32_t colour) {}
|
||||||
|
|
||||||
|
int _marl_render(Marl_Window* window) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_destroyWindow(Marl_Window** windowptr) {
|
||||||
|
free(*windowptr);
|
||||||
|
*windowptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl_Font* _marl_font_load(const char* path, float size) {
|
||||||
|
return malloc(sizeof(Marl_Font));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_font_destroy(Marl_Font** font) {
|
||||||
|
free(*font);
|
||||||
|
*font = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _marl_font_textWidth(Marl_Font* font, const char* text) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _Marl Marl = {
|
||||||
|
/*
|
||||||
|
* General rendering
|
||||||
|
*/
|
||||||
|
_marl_createWindow, _marl_poll, _marl_clearScreen, _marl_drawRect, _marl_drawText, _marl_render, _marl_destroyWindow,
|
||||||
|
/*
|
||||||
|
* Text and fonts
|
||||||
|
*/
|
||||||
|
{ _marl_font_load, _marl_font_destroy, _marl_font_textWidth }
|
||||||
|
};
|
||||||
|
|
||||||
336
src/x11/marl.c
Normal file
336
src/x11/marl.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* Marl
|
||||||
|
* Copyright 2026 Maxwell Jeffress
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marl implementation using X11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../marl.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include "../include/stb_truetype.h"
|
||||||
|
|
||||||
|
struct Marl_Window {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XCB variables and pointers
|
||||||
|
*/
|
||||||
|
|
||||||
|
xcb_connection_t* conn;
|
||||||
|
const xcb_setup_t* setup;
|
||||||
|
xcb_screen_t* screen;
|
||||||
|
xcb_window_t window;
|
||||||
|
xcb_gcontext_t gc;
|
||||||
|
xcb_atom_t delete_atom;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marl pixel buffer
|
||||||
|
*/
|
||||||
|
int width, height;
|
||||||
|
uint32_t* pixels;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Marl_Font {
|
||||||
|
stbtt_fontinfo info;
|
||||||
|
unsigned char* data; // raw TTF file contents
|
||||||
|
float scale;
|
||||||
|
int ascent, descent, line_gap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t _marl_blend(uint32_t dst, uint32_t src, uint8_t alpha) {
|
||||||
|
uint8_t sr = (src >> 16) & 0xFF;
|
||||||
|
uint8_t sg = (src >> 8) & 0xFF;
|
||||||
|
uint8_t sb = (src >> 0) & 0xFF;
|
||||||
|
uint8_t dr = (dst >> 16) & 0xFF;
|
||||||
|
uint8_t dg = (dst >> 8) & 0xFF;
|
||||||
|
uint8_t db = (dst >> 0) & 0xFF;
|
||||||
|
|
||||||
|
uint8_t rr = (sr * alpha + dr * (255 - alpha)) / 255;
|
||||||
|
uint8_t rg = (sg * alpha + dg * (255 - alpha)) / 255;
|
||||||
|
uint8_t rb = (sb * alpha + db * (255 - alpha)) / 255;
|
||||||
|
|
||||||
|
return 0xFF000000 | (rr << 16) | (rg << 8) | rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================
|
||||||
|
* GENERAL FUNCTIONS
|
||||||
|
* =================
|
||||||
|
*/
|
||||||
|
|
||||||
|
Marl_Window* _marl_createWindow(const char* title, int width, int height) {
|
||||||
|
Marl_Window* window = malloc(sizeof(Marl_Window));
|
||||||
|
|
||||||
|
window->conn = xcb_connect(NULL, NULL);
|
||||||
|
if (xcb_connection_has_error(window->conn)) {
|
||||||
|
fprintf(stderr, "marl: couldn't connect to X server, returning\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
window->setup = xcb_get_setup(window->conn);
|
||||||
|
window->screen = xcb_setup_roots_iterator(window->setup).data;
|
||||||
|
|
||||||
|
window->window = xcb_generate_id(window->conn);
|
||||||
|
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
||||||
|
uint32_t values[] = {
|
||||||
|
window->screen->black_pixel,
|
||||||
|
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION
|
||||||
|
};
|
||||||
|
|
||||||
|
xcb_create_window(
|
||||||
|
window->conn,
|
||||||
|
XCB_COPY_FROM_PARENT,
|
||||||
|
window->window,
|
||||||
|
window->screen->root,
|
||||||
|
0, 0, width, height,
|
||||||
|
0,
|
||||||
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
|
window->screen->root_visual,
|
||||||
|
mask,
|
||||||
|
values
|
||||||
|
);
|
||||||
|
|
||||||
|
xcb_change_property(
|
||||||
|
window->conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
window->window,
|
||||||
|
XCB_ATOM_WM_NAME,
|
||||||
|
XCB_ATOM_STRING,
|
||||||
|
8,
|
||||||
|
strlen(title),
|
||||||
|
title
|
||||||
|
);
|
||||||
|
|
||||||
|
xcb_intern_atom_reply_t *proto_reply = xcb_intern_atom_reply(window->conn,
|
||||||
|
xcb_intern_atom(window->conn, 1, 12, "WM_PROTOCOLS"), NULL);
|
||||||
|
xcb_intern_atom_reply_t *delete_reply = xcb_intern_atom_reply(window->conn,
|
||||||
|
xcb_intern_atom(window->conn, 0, 16, "WM_DELETE_WINDOW"), NULL);
|
||||||
|
xcb_change_property(window->conn, XCB_PROP_MODE_REPLACE, window->window,
|
||||||
|
proto_reply->atom, XCB_ATOM_ATOM, 32, 1, &delete_reply->atom);
|
||||||
|
|
||||||
|
window->delete_atom = delete_reply->atom;
|
||||||
|
free(proto_reply);
|
||||||
|
free(delete_reply);
|
||||||
|
|
||||||
|
window->gc = xcb_generate_id(window->conn);
|
||||||
|
xcb_create_gc(window->conn, window->gc, window->window, 0, NULL);
|
||||||
|
|
||||||
|
xcb_map_window(window->conn, window->window);
|
||||||
|
xcb_flush(window->conn);
|
||||||
|
|
||||||
|
window->width = width;
|
||||||
|
window->height = height;
|
||||||
|
window->pixels = malloc(width * height * sizeof(uint32_t));
|
||||||
|
|
||||||
|
for (int i = 0; i < width * height; i++) {
|
||||||
|
window->pixels[i] = 0xFFFF0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl_Event _marl_poll(Marl_Window* window) {
|
||||||
|
xcb_generic_event_t* event;
|
||||||
|
Marl_Event result = {Marl_None, 0, 0, 0};
|
||||||
|
|
||||||
|
while ((event = xcb_poll_for_event(window->conn)) != NULL) {
|
||||||
|
switch (event->response_type & ~0x80) {
|
||||||
|
case XCB_MOTION_NOTIFY: {
|
||||||
|
xcb_motion_notify_event_t* e = (xcb_motion_notify_event_t*)event;
|
||||||
|
result.type = Marl_MouseMove;
|
||||||
|
result.x = e->event_x;
|
||||||
|
result.y = e->event_y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XCB_BUTTON_PRESS: {
|
||||||
|
xcb_button_press_event_t* e = (xcb_button_press_event_t*)event;
|
||||||
|
result.type = Marl_MouseDown;
|
||||||
|
result.x = e->event_x;
|
||||||
|
result.y = e->event_y;
|
||||||
|
result.button = e->detail;
|
||||||
|
free(event);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case XCB_BUTTON_RELEASE: {
|
||||||
|
xcb_button_press_event_t* e = (xcb_button_press_event_t*)event;
|
||||||
|
result.type = Marl_MouseUp;
|
||||||
|
result.x = e->event_x;
|
||||||
|
result.y = e->event_y;
|
||||||
|
result.button = e->detail;
|
||||||
|
free(event);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case XCB_CLIENT_MESSAGE: {
|
||||||
|
if (((xcb_client_message_event_t*)event)->data.data32[0] == window->delete_atom) {
|
||||||
|
free(event);
|
||||||
|
result.type = Marl_Quit;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_clearScreen(Marl_Window* window, uint32_t colour) {
|
||||||
|
for (int i = 0; i < window->width * window->height; i++) {
|
||||||
|
window->pixels[i] = colour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_drawRect(Marl_Window* window, int x, int y, int w, int h, uint32_t colour) {
|
||||||
|
// clamp to window bounds
|
||||||
|
int x0 = x < 0 ? 0 : x;
|
||||||
|
int y0 = y < 0 ? 0 : y;
|
||||||
|
int x1 = (x + w) > window->width ? window->width : (x + w);
|
||||||
|
int y1 = (y + h) > window->height ? window->height : (y + h);
|
||||||
|
|
||||||
|
for (int row = y0; row < y1; row++)
|
||||||
|
for (int col = x0; col < x1; col++)
|
||||||
|
window->pixels[row * window->width + col] = colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _marl_render(Marl_Window* window) {
|
||||||
|
xcb_put_image(
|
||||||
|
window->conn,
|
||||||
|
XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||||
|
window->window,
|
||||||
|
window->gc,
|
||||||
|
window->width,
|
||||||
|
window->height,
|
||||||
|
0, 0,
|
||||||
|
0,
|
||||||
|
window->screen->root_depth,
|
||||||
|
window->width * window->height * sizeof(uint32_t),
|
||||||
|
(uint8_t*) window->pixels
|
||||||
|
);
|
||||||
|
xcb_flush(window->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_drawText(Marl_Window* window, Marl_Font* font, const char* text, int x, int y, uint32_t colour) {
|
||||||
|
int cx = x; // current x cursor
|
||||||
|
|
||||||
|
for (const char* ch = text; *ch; ch++) {
|
||||||
|
int advance, lsb;
|
||||||
|
stbtt_GetCodepointHMetrics(&font->info, *ch, &advance, &lsb);
|
||||||
|
|
||||||
|
// get the bitmap for this glyph
|
||||||
|
int gx, gy, gw, gh;
|
||||||
|
unsigned char* bitmap = stbtt_GetCodepointBitmap(
|
||||||
|
&font->info, font->scale, font->scale,
|
||||||
|
*ch, &gw, &gh, &gx, &gy
|
||||||
|
);
|
||||||
|
|
||||||
|
// gy is the offset from the baseline
|
||||||
|
int draw_x = cx + gx;
|
||||||
|
int draw_y = y + font->ascent + gy;
|
||||||
|
|
||||||
|
for (int row = 0; row < gh; row++) {
|
||||||
|
for (int col = 0; col < gw; col++) {
|
||||||
|
int px = draw_x + col;
|
||||||
|
int py = draw_y + row;
|
||||||
|
if (px < 0 || px >= window->width || py < 0 || py >= window->height)
|
||||||
|
continue;
|
||||||
|
uint8_t alpha = bitmap[row * gw + col];
|
||||||
|
if (alpha == 0) continue;
|
||||||
|
uint32_t dst = window->pixels[py * window->width + px];
|
||||||
|
window->pixels[py * window->width + px] = _marl_blend(dst, colour, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbtt_FreeBitmap(bitmap, NULL);
|
||||||
|
cx += (int)(advance * font->scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_destroyWindow(Marl_Window** windowptr) {
|
||||||
|
Marl_Window* window = *windowptr;
|
||||||
|
free(window->pixels);
|
||||||
|
xcb_disconnect(window->conn);
|
||||||
|
|
||||||
|
free(window);
|
||||||
|
*windowptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===============
|
||||||
|
* FONT MANAGEMENT
|
||||||
|
* ===============
|
||||||
|
*/
|
||||||
|
|
||||||
|
Marl_Font* _marl_font_load(const char* path, float size) {
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
if (!f) { fprintf(stderr, "marl: couldn't open font %s\n", path); return NULL; }
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long len = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
unsigned char* data = malloc(len);
|
||||||
|
fread(data, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
Marl_Font* font = malloc(sizeof(Marl_Font));
|
||||||
|
font->data = data;
|
||||||
|
|
||||||
|
stbtt_InitFont(&font->info, data, stbtt_GetFontOffsetForIndex(data, 0));
|
||||||
|
font->scale = stbtt_ScaleForPixelHeight(&font->info, size);
|
||||||
|
|
||||||
|
int ascent, descent, line_gap;
|
||||||
|
stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
|
||||||
|
font->ascent = (int)(ascent * font->scale);
|
||||||
|
font->descent = (int)(descent * font->scale);
|
||||||
|
font->line_gap = (int)(line_gap * font->scale);
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _marl_font_destroy(Marl_Font** fontptr) {
|
||||||
|
Marl_Font* font = *fontptr;
|
||||||
|
free(font->data);
|
||||||
|
free(font);
|
||||||
|
*fontptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _marl_font_textWidth(Marl_Font* font, const char* text) {
|
||||||
|
int width = 0;
|
||||||
|
for (const char* ch = text; *ch; ch++) {
|
||||||
|
int advance, lsb;
|
||||||
|
stbtt_GetCodepointHMetrics(&font->info, *ch, &advance, &lsb);
|
||||||
|
width += (int)(advance * font->scale);
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _Marl Marl = {
|
||||||
|
/*
|
||||||
|
* General rendering
|
||||||
|
*/
|
||||||
|
_marl_createWindow, _marl_poll, _marl_clearScreen, _marl_drawRect, _marl_drawText, _marl_render, _marl_destroyWindow,
|
||||||
|
/*
|
||||||
|
* Text and fonts
|
||||||
|
*/
|
||||||
|
{ _marl_font_load, _marl_font_destroy, _marl_font_textWidth }
|
||||||
|
};
|
||||||
1
test/.gitignore
vendored
Normal file
1
test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
test
|
||||||
2
test/Makefile
Normal file
2
test/Makefile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
test: main.c
|
||||||
|
cc main.c -lmarl -o test
|
||||||
38
test/main.c
Normal file
38
test/main.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include <marl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Marl_Window* window = Marl.createWindow("Click Me!", 800, 600);
|
||||||
|
if (window == NULL) {
|
||||||
|
printf("Window didn't create lmao\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl_Font* font = Marl.Font.load("/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 24.0f);
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
Marl_Event e = Marl.poll(window);
|
||||||
|
if (e.type == Marl_Quit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (e.type == Marl_MouseDown) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
char buf[256];
|
||||||
|
if (counter == 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Click the window!");
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "You clicked %d times!", counter);
|
||||||
|
}
|
||||||
|
Marl.clearScreen(window, 0xFF000000);
|
||||||
|
Marl.drawRect(window, 100, 100, 200, 150, 0xFF0000FF);
|
||||||
|
Marl.drawText(window, font, buf, 100, 80, 0xFFFFFFFF);
|
||||||
|
Marl.render(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
Marl.Font.destroy(&font);
|
||||||
|
Marl.destroyWindow(&window);
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user