Compare commits
4 Commits
e941db978c
...
78773a54a7
Author | SHA1 | Date |
---|---|---|
|
78773a54a7 | |
|
0a57e30ead | |
|
da0b9ab2c7 | |
|
2ffbc3a65e |
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
2
justfile
2
justfile
|
@ -3,7 +3,7 @@ alias r := run
|
|||
|
||||
build: transpile_shaders_metal
|
||||
mkdir -p bin
|
||||
cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
|
||||
cc -Ivendor/ -O0 -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
|
||||
# cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor
|
||||
# cc bin/*.o -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ fragment float4 ui_rect_fs(UiRectFragment in [[stage_in]])
|
|||
|
||||
float distance = rect_sdf(pixel_pos, in.position, in.size, in.border_size.x);
|
||||
if (distance <= 0.0) {
|
||||
return float4(1,1,1,0.5);
|
||||
return float4(0.2,0.2,0.2,1.0);
|
||||
} else {
|
||||
return float4(0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
#ifndef ED_BUFFER_INCLUDED
|
||||
#define ED_BUFFER_INCLUDED
|
||||
#include "ed_array.h"
|
||||
#include "string.h"
|
||||
#include "ui.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
size_t link_index;
|
||||
size_t content_index;
|
||||
} ed_buffer_offset;
|
||||
|
||||
typedef struct {
|
||||
uint64_t column;
|
||||
uint64_t line;
|
||||
ed_buffer_offset offset;
|
||||
} ed_buffer_cursor;
|
||||
|
||||
typedef enum {
|
||||
ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL,
|
||||
ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
|
||||
} ed_buffer_content_link_t;
|
||||
|
||||
typedef struct {
|
||||
ed_buffer_content_link_t type;
|
||||
size_t start;
|
||||
size_t end;
|
||||
size_t len;
|
||||
} ed_buffer_content_link;
|
||||
arrayTemplate(ed_buffer_content_link);
|
||||
|
||||
typedef struct {
|
||||
array(uint8_t) original_content;
|
||||
array(uint8_t) added_content;
|
||||
|
||||
array(ed_buffer_content_link) linked_content;
|
||||
} ed_buffer_data;
|
||||
|
||||
typedef struct {
|
||||
ed_buffer_data data;
|
||||
ed_buffer_cursor cursor;
|
||||
} ed_buffer;
|
||||
|
||||
#ifdef ED_BUFFER_IMPLEMENTATION
|
||||
|
||||
static ed_buffer_data _ed_buffer_init_buffer_data(slice(uint8_t) data) {
|
||||
array(ed_buffer_content_link) linked_content = newArray(ed_buffer_content_link, 16);
|
||||
pushArray(ed_buffer_content_link, &linked_content, ((ed_buffer_content_link) {
|
||||
.type = ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL,
|
||||
.start = 0,
|
||||
.end = data.len,
|
||||
.len = data.len,
|
||||
}));
|
||||
|
||||
return (ed_buffer_data) {
|
||||
.original_content = newArrayFromSlice(uint8_t, data),
|
||||
.added_content = newArray(uint8_t, 512),
|
||||
.linked_content = linked_content,
|
||||
};
|
||||
}
|
||||
|
||||
uint8_t ed_buffer_get_char_at(ed_buffer *buffer, ed_buffer_cursor cursor) {
|
||||
ed_buffer_content_link link = buffer->data.linked_content.data[cursor.offset.link_index];
|
||||
uint8_t character;
|
||||
|
||||
if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL) {
|
||||
character = buffer->data.original_content.data[link.start+cursor.offset.content_index];
|
||||
} else if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ADDED) {
|
||||
character = buffer->data.added_content.data[link.start+cursor.offset.content_index];
|
||||
} else {
|
||||
character = 0;
|
||||
}
|
||||
|
||||
return character;
|
||||
}
|
||||
|
||||
ed_buffer ed_buffer_from_file(string path) {
|
||||
char text[] =
|
||||
"bool ht_set(ed_ht *ht, string key, void *value) {\n"
|
||||
" ht_hash_t hash = ht_hash(key, ht->capacity);\n"
|
||||
"\n"
|
||||
" for (size_t i=hash; i<ht->capacity; ++i) {\n"
|
||||
" if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {\n"
|
||||
" if (ht->key_slots[i].key.data != NULL) {\n"
|
||||
" free(ht->key_slots[i].key.data);\n"
|
||||
" }\n"
|
||||
" ht->key_slots[i].key = string_copy(key);\n"
|
||||
"\n"
|
||||
" memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);\n"
|
||||
" return true;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return false;\n"
|
||||
"}\n";
|
||||
|
||||
slice(uint8_t) data = {
|
||||
.data = text,
|
||||
.len = sizeof(text),
|
||||
};
|
||||
ed_buffer buffer = {
|
||||
.data = _ed_buffer_init_buffer_data(data),
|
||||
.cursor = {
|
||||
.column = 0,
|
||||
.line = 0,
|
||||
.offset = {
|
||||
.link_index = 0,
|
||||
.content_index = 0,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ed_buffer_content_iter(ed_buffer *buffer, ed_buffer_cursor *cursor) {
|
||||
if (cursor->offset.link_index >= buffer->data.linked_content.size
|
||||
|| cursor->offset.content_index >= (buffer->data.linked_content.data[cursor->offset.link_index].len))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t character = ed_buffer_get_char_at(buffer, *cursor);
|
||||
|
||||
ed_buffer_content_link link = buffer->data.linked_content.data[cursor->offset.link_index];
|
||||
if (cursor->offset.content_index < link.len-1) {
|
||||
cursor->offset.content_index += 1;
|
||||
} else if (cursor->offset.link_index < buffer->data.linked_content.size-1) {
|
||||
cursor->offset.content_index = 0;
|
||||
cursor->offset.link_index += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (character == '\n') {
|
||||
cursor->column = 0;
|
||||
cursor->line += 1;
|
||||
} else {
|
||||
cursor->column += 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ed_buffer_insert_text(ed_buffer *buffer, string text, ed_buffer_cursor cursor) {
|
||||
if (text.len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t content_start = buffer->data.added_content.size;
|
||||
pushArrayMulti(uint8_t, &buffer->data.added_content, text.data, text.len);
|
||||
|
||||
if (cursor.offset.content_index == 0) {
|
||||
insertArrayAt(ed_buffer_content_link,
|
||||
&buffer->data.linked_content,
|
||||
cursor.offset.link_index,
|
||||
((ed_buffer_content_link) {
|
||||
.type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
|
||||
.start = content_start,
|
||||
.end = content_start+text.len,
|
||||
.len = text.len,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
ed_buffer_content_link end_link = buffer->data.linked_content.data[cursor.offset.link_index];
|
||||
end_link.start += cursor.offset.content_index;
|
||||
end_link.len -= cursor.offset.content_index;
|
||||
|
||||
buffer->data.linked_content.data[cursor.offset.link_index].end = end_link.start;
|
||||
buffer->data.linked_content.data[cursor.offset.link_index].len = buffer->data.linked_content.data[cursor.offset.link_index].end - buffer->data.linked_content.data[cursor.offset.link_index].start;
|
||||
|
||||
// insert new content
|
||||
insertArrayAt(ed_buffer_content_link,
|
||||
&buffer->data.linked_content,
|
||||
cursor.offset.link_index+1,
|
||||
((ed_buffer_content_link) {
|
||||
.type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
|
||||
.start = content_start,
|
||||
.end = content_start+text.len,
|
||||
.len = text.len,
|
||||
})
|
||||
);
|
||||
|
||||
// insert cut slice
|
||||
insertArrayAt(ed_buffer_content_link,
|
||||
&buffer->data.linked_content,
|
||||
cursor.offset.link_index+2,
|
||||
end_link
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ed_buffer_render(ui_context *cx, ed_buffer *buffer) {
|
||||
ui_element(cx, _String("Buffer"), UI_AXIS_VERTICAL, ui_make_size(ui_fill, ui_fill), UI_FLAG_DRAW_BACKGROUND);
|
||||
ui_push_parent(cx);
|
||||
{
|
||||
ed_buffer_cursor cursor = buffer->cursor;
|
||||
do {
|
||||
uint8_t line_buffer[512];
|
||||
size_t line_buffer_index = 0;
|
||||
|
||||
do {
|
||||
uint8_t character = ed_buffer_get_char_at(buffer, cursor);
|
||||
if (character == '\n' || line_buffer_index >= 512) {
|
||||
break;
|
||||
}
|
||||
|
||||
line_buffer[line_buffer_index] = character;
|
||||
line_buffer_index += 1;
|
||||
} while(ed_buffer_content_iter(buffer, &cursor));
|
||||
|
||||
uint8_t *str = malloc(line_buffer_index * sizeof(uint8_t));
|
||||
memcpy(str, line_buffer, line_buffer_index * sizeof(uint8_t));
|
||||
|
||||
ui_element(cx,
|
||||
(string){ .data = str,
|
||||
.len = line_buffer_index,
|
||||
.owned = true
|
||||
},
|
||||
UI_AXIS_HORIZONTAL,
|
||||
ui_make_size(ui_fit_text, ui_fit_text),
|
||||
UI_FLAG_DRAW_TEXT|UI_FLAG_DRAW_BACKGROUND
|
||||
);
|
||||
} while(ed_buffer_content_iter(buffer, &cursor));
|
||||
}
|
||||
ui_pop_parent(cx);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -2,5 +2,7 @@
|
|||
-DED_HT_IMPLEMENTATION
|
||||
-DED_STRING_IMPLEMENTATION
|
||||
-DED_GFX_IMPLEMENTATION
|
||||
-DED_BUFFER_IMPLEMENTATION
|
||||
-DED_FILE_IO_IMPLEMENTATION
|
||||
-I../vendor/
|
||||
-ObjC
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#ifndef ED_ARRAY_INCLUDED
|
||||
#define ED_ARRAY_INCLUDED
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
|
||||
#define array(T) struct T ## Array
|
||||
#define array(T) struct T ## _Array
|
||||
#define arrayTemplate(T) array(T) {\
|
||||
size_t size, capacity;\
|
||||
T *data;\
|
||||
};\
|
||||
array(T) T ## ArrayConstruct(size_t size) {\
|
||||
array(T) T ## _ArrayConstruct(size_t size) {\
|
||||
array(T) this;\
|
||||
this.size = 0;\
|
||||
this.capacity = size;\
|
||||
|
@ -18,16 +20,61 @@ array(T) T ## ArrayConstruct(size_t size) {\
|
|||
}\
|
||||
return this;\
|
||||
};\
|
||||
void T ## PushArray(array(T) *arr, T value) {\
|
||||
void T ## _PushArray(array(T) *arr, T value) {\
|
||||
if (arr->size+1 <= arr->capacity) {\
|
||||
arr->data[arr->size] = value;\
|
||||
arr->size += 1;\
|
||||
} else {\
|
||||
fprintf(stderr, "failed to push to u8 array, size+num > capacity\n");\
|
||||
}\
|
||||
};\
|
||||
void T ## _PushArrayMulti(array(T) *arr, T *values, size_t len) {\
|
||||
for (size_t i = 0; i < len; ++i) {\
|
||||
T ## _PushArray(arr, values[i]);\
|
||||
}\
|
||||
};\
|
||||
void T ## _InsertArrayAt(array(T) *arr, size_t loc, T value) {\
|
||||
if (arr->size == arr->capacity) {\
|
||||
arr->capacity *= 2;\
|
||||
void *new_data = realloc(arr->data, arr->capacity);\
|
||||
if (new_data == NULL) {\
|
||||
fprintf(stderr, "out of memory when reallocating array\n");\
|
||||
}\
|
||||
arr->data = new_data;\
|
||||
}\
|
||||
memcpy(&arr->data[loc+1], &arr->data[loc], arr->size * sizeof(T));\
|
||||
arr->data[loc] = value;\
|
||||
arr->size += 1;\
|
||||
};
|
||||
|
||||
#define newArray(T, size) T ## ArrayConstruct(size)
|
||||
#define pushArray(T, arr, value) T ## PushArray(arr, (value))
|
||||
#define slice(T) struct T ## _Slice
|
||||
#define sliceTemplate(T) slice(T) {\
|
||||
size_t len;\
|
||||
T *data;\
|
||||
};\
|
||||
array(T) T ## _FromSlice(slice(T) s) {\
|
||||
array(T) arr = T ## _ArrayConstruct(s.len);\
|
||||
memcpy(arr.data, s.data, sizeof(T) * s.len);\
|
||||
arr.size = s.len;\
|
||||
return arr;\
|
||||
}\
|
||||
slice(T) T ## _SliceConstruct(void *data, size_t len) {\
|
||||
slice(T) this;\
|
||||
this.len = len;\
|
||||
this.data = data;\
|
||||
return this;\
|
||||
}
|
||||
|
||||
#define newArray(T, size) T ## _ArrayConstruct(size)
|
||||
#define newArrayFromSlice(T, s) T ## _FromSlice(s)
|
||||
#define pushArray(T, arr, value) T ## _PushArray(arr, (value))
|
||||
#define pushArrayMulti(T, arr, values, len) T ## _PushArrayMulti(arr, values, len)
|
||||
|
||||
#define insertArrayAt(T, arr, loc, value) T ## _InsertArrayAt(arr, loc, (value))
|
||||
|
||||
#define newSlice(T, data, len) T ## _SliceConstruct(data, len)
|
||||
|
||||
arrayTemplate(uint8_t);
|
||||
sliceTemplate(uint8_t);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
#ifndef ED_FILE_IO_INCLUDED
|
||||
#define ED_FILE_IO_INCLUDED
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "string.h"
|
||||
|
||||
uint64_t get_file_size(string file_path);
|
||||
bool load_file(string file_path, size_t size, void *buffer);
|
||||
|
||||
#ifdef ED_FILE_IO_IMPLEMENTATION
|
||||
|
||||
#if defined(__unix__)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
unsigned long get_file_size(const char *filePath) {
|
||||
FILE *file = fopen(filePath, "r");
|
||||
assert(file && "get_file_size: failed to open file");
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fsize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return fsize;
|
||||
}
|
||||
|
||||
bool load_file(const char *filePath, unsigned long size, void *buffer) {
|
||||
FILE *file = fopen(filePath, "r");
|
||||
assert(file && "load_file: failed to open file");
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fsize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
fread(buffer, fsize, 1, file);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h>
|
||||
#define assertm(exp, msg) assert(((void)msg, exp))
|
||||
|
||||
VOID CALLBACK FileIOCompletionRoutine(
|
||||
__in DWORD dwErrorCode,
|
||||
__in DWORD dwNumberOfBytesTransfered,
|
||||
__in LPOVERLAPPED lpOverlapped )
|
||||
{
|
||||
printf("Error code: %li", dwErrorCode);
|
||||
printf("Number of bytes: %li", dwNumberOfBytesTransfered);
|
||||
}
|
||||
|
||||
unsigned long get_file_size(const char *filePath) {
|
||||
HANDLE hFile = CreateFile(filePath,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
|
||||
LARGE_INTEGER size;
|
||||
assert(GetFileSizeEx(hFile, &size) && "get_file_size: failed to get file size");
|
||||
|
||||
return size.LowPart;
|
||||
}
|
||||
|
||||
bool load_file(const char *filePath, unsigned long size, void *buffer) {
|
||||
HANDLE hFile = CreateFile(filePath,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
OVERLAPPED ol = {0};
|
||||
unsigned long bytesRead = 0;
|
||||
if (!ReadFile(hFile, buffer, size, &bytesRead, NULL)) {
|
||||
unsigned long error = GetLastError();
|
||||
unsigned long msg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, size, NULL);
|
||||
|
||||
printf("failed to load file: %s\n", (char *)buffer);
|
||||
assert(false && "failed to read file");
|
||||
}
|
||||
assert(bytesRead == size && "load_file: didn't one-shot read the whole file");
|
||||
|
||||
return true;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
uint64_t get_file_size(string file_path) {
|
||||
NSFileManager* file_manager = [NSFileManager defaultManager];
|
||||
NSString *path = [file_manager stringWithFileSystemRepresentation:(const char *)file_path.data length:file_path.len];
|
||||
|
||||
NSError *error = NULL;
|
||||
NSDictionary<NSFileAttributeKey, id> *attributes = [file_manager attributesOfItemAtPath:path error:&error];
|
||||
|
||||
if (error) {
|
||||
NSLog(@"error getting file attributes: %@\n", error.description);
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSNumber *file_size = [attributes objectForKey:NSFileSize];
|
||||
|
||||
return file_size.unsignedLongLongValue;
|
||||
}
|
||||
|
||||
bool load_file(string file_path, size_t size, void *buffer) {
|
||||
NSFileManager* file_manager = [NSFileManager defaultManager];
|
||||
NSString *path = [file_manager stringWithFileSystemRepresentation:(const char *)file_path.data length:file_path.len];
|
||||
|
||||
NSData *data = [file_manager contentsAtPath:path];
|
||||
if (data == NULL) {
|
||||
fprintf(stderr, "failed to load file: %.*s\n", (int)file_path.len, file_path.data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.length > size) {
|
||||
fprintf(stderr, "buffer not large enough for file: %.*s\n", (int)file_path.len, file_path.data);
|
||||
}
|
||||
|
||||
memcpy(buffer, data.bytes, data.length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
48
src/gfx.h
48
src/gfx.h
|
@ -58,6 +58,9 @@ typedef struct {
|
|||
uint32_t frame_width;
|
||||
uint32_t frame_height;
|
||||
_gfx_frame_func frame_func;
|
||||
|
||||
size_t num_glyphs;
|
||||
size_t num_ui_rects;
|
||||
} gfx_context_t;
|
||||
static gfx_context_t _gfx_context;
|
||||
|
||||
|
@ -88,15 +91,13 @@ static void _metal_gfx_send_events(_metal_gfx_context *cx);
|
|||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)notification {
|
||||
NSLog(@"did resize\n");
|
||||
|
||||
_gfx_context.frame_width = _gfx_context.backend.window.contentView.frame.size.width;
|
||||
_gfx_context.frame_height = _gfx_context.backend.window.contentView.frame.size.height;
|
||||
|
||||
CGFloat scale = _gfx_context.backend.metal_layer.contentsScale;
|
||||
[_gfx_context.backend.metal_layer setDrawableSize:CGSizeMake(_gfx_context.frame_width * scale, _gfx_context.frame_height * scale)];
|
||||
|
||||
_metal_gfx_present(&_gfx_context.backend);
|
||||
_gfx_context.backend.view.needsDisplay = true;
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -259,7 +260,6 @@ void _metal_gfx_send_events(_metal_gfx_context *cx) {
|
|||
untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
|
||||
[cx->application sendEvent:event];
|
||||
[cx->application updateWindows];
|
||||
}
|
||||
|
||||
void _metal_gfx_present(_metal_gfx_context *cx) {
|
||||
|
@ -276,24 +276,28 @@ void _metal_gfx_present(_metal_gfx_context *cx) {
|
|||
|
||||
id<MTLRenderCommandEncoder> encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_desc];
|
||||
|
||||
// UI Text
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[0]];
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1]; // glyph data
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
[encoder setFragmentTexture:cx->textures.data[0] atIndex:0];
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:36];
|
||||
if (_gfx_context.num_ui_rects > 0) {
|
||||
// UI Rects
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[1]];
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[4] offset:0 atIndex:1]; // ui rects
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:_gfx_context.num_ui_rects];
|
||||
}
|
||||
|
||||
// UI Rects
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[1]];
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[4] offset:0 atIndex:1]; // ui rects
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:3];
|
||||
if (_gfx_context.num_glyphs > 0) {
|
||||
// UI Text
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[0]];
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1]; // glyph data
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
[encoder setFragmentTexture:cx->textures.data[0] atIndex:0];
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:_gfx_context.num_glyphs];
|
||||
}
|
||||
|
||||
[encoder endEncoding];
|
||||
[command_buffer presentDrawable:drawable];
|
||||
|
@ -355,6 +359,8 @@ void * gfx_init_context(_gfx_frame_func frame_func, uint32_t width, uint32_t hei
|
|||
_gfx_context.frame_func = frame_func;
|
||||
_gfx_context.frame_width = width;
|
||||
_gfx_context.frame_height = height;
|
||||
_gfx_context.num_glyphs = 0;
|
||||
_gfx_context.num_ui_rects = 0;
|
||||
|
||||
return &_gfx_context;
|
||||
}
|
||||
|
|
5
src/ht.h
5
src/ht.h
|
@ -61,7 +61,10 @@ bool ht_set(ed_ht *ht, string key, void *value) {
|
|||
|
||||
for (size_t i=hash; i<ht->capacity; ++i) {
|
||||
if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {
|
||||
ht->key_slots[i].key = key;
|
||||
if (ht->key_slots[i].key.data != NULL) {
|
||||
free(ht->key_slots[i].key.data);
|
||||
}
|
||||
ht->key_slots[i].key = string_copy(key);
|
||||
|
||||
memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);
|
||||
return true;
|
||||
|
|
65
src/main.c
65
src/main.c
|
@ -11,11 +11,15 @@
|
|||
#define ED_STRING_IMPLEMENTATION
|
||||
#define ED_HT_IMPLEMENTATION
|
||||
#define ED_UI_IMPLEMENTATION
|
||||
#define ED_BUFFER_IMPLEMENTATION
|
||||
#define ED_FILE_IO_IMPLEMENTATION
|
||||
#include "string.h"
|
||||
#include "ht.h"
|
||||
#include "ui.h"
|
||||
#include "ed_array.h"
|
||||
#include "gfx.h"
|
||||
#include "buffer.h"
|
||||
#include "file_io.h"
|
||||
|
||||
// static Arena default_arena = {0};
|
||||
// static Arena temporary_arena = {0};
|
||||
|
@ -56,6 +60,8 @@ static struct {
|
|||
|
||||
ui_context ui_cx;
|
||||
gfx_context_t *gfx_cx;
|
||||
|
||||
ed_buffer test_buffer;
|
||||
} state;
|
||||
|
||||
void queue_text(string text, float position[2]) {
|
||||
|
@ -66,18 +72,18 @@ void queue_text(string text, float position[2]) {
|
|||
|
||||
glyph.position[0] = x+position[0];
|
||||
glyph.position[1] = position[1];
|
||||
x += glyph.size[0]/2;
|
||||
x += glyph.size[0]/2+4;
|
||||
|
||||
pushArray(GpuGlyph, &state.gpu_glyphs, glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queue_ui_rect(uint32_t position[2], uint32_t size[2], uint32_t border_size) {
|
||||
void queue_ui_rect(float position[2], float size[2], float border_size) {
|
||||
GpuUiRect rect = (GpuUiRect) {
|
||||
.position = { (float)position[0], (float)position[1] },
|
||||
.size = { (float)size[0], (float)size[1] },
|
||||
.border_size = { (float)border_size, (float)border_size },
|
||||
.position = { position[0], position[1] },
|
||||
.size = { size[0], size[1] },
|
||||
.border_size = { border_size, border_size },
|
||||
};
|
||||
|
||||
pushArray(GpuUiRect, &state.gpu_ui_rects, rect);
|
||||
|
@ -101,22 +107,16 @@ void ed_init(_gfx_frame_func frame_func) {
|
|||
gfx_push_vertex_buffer(state.gfx_cx, indices, sizeof(indices));
|
||||
|
||||
state.gpu_ui_rects = newArray(GpuUiRect, 2000);
|
||||
state.gpu_glyphs = newArray(GpuGlyph, 1024);
|
||||
state.gpu_glyphs = newArray(GpuGlyph, 2048);
|
||||
state.glyph_cache = newArray(GpuGlyph, 97);
|
||||
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_glyphs.capacity * sizeof(GpuGlyph));
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, sizeof(GpuUniformParams));
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_ui_rects.capacity * sizeof(GpuUiRect));
|
||||
|
||||
uint8_t ttf_buffer[1<<20];
|
||||
// TODO: grab default font from the system
|
||||
FILE *ttf_file = fopen("./bin/JetBrainsMono-Medium.ttf", "rb");
|
||||
if (!ttf_file) {
|
||||
fprintf(stderr, "failed to load font\n");
|
||||
exit(1);
|
||||
}
|
||||
assert(fread(ttf_buffer, 1, 1<<20, ttf_file));
|
||||
fclose(ttf_file);
|
||||
uint8_t ttf_buffer[1<<20];
|
||||
assert("failed to load font" && load_file(_String("./bin/JetBrainsMono-Medium.ttf"), 1<<20, ttf_buffer));
|
||||
|
||||
stbtt_fontinfo font;
|
||||
stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0));
|
||||
|
@ -177,37 +177,48 @@ void ed_init(_gfx_frame_func frame_func) {
|
|||
|
||||
gfx_push_texture_buffer(state.gfx_cx, font_bitmap_size, font_bitmap_size, font_bitmap, font_bitmap_size*font_bitmap_size * sizeof(uint8_t));
|
||||
|
||||
state.test_buffer = ed_buffer_from_file(_String(""));
|
||||
|
||||
ed_buffer_insert_text(&state.test_buffer, _String("_hehe i'm inserting_"), (ed_buffer_cursor) { .offset = (ed_buffer_offset) { .link_index = 0, .content_index = 5 } });
|
||||
}
|
||||
|
||||
void render_ui_text(string text, float position[2]) {
|
||||
queue_text(text, position);
|
||||
}
|
||||
void render_ui_rect(float position[2], float size[2]) {
|
||||
queue_ui_rect(position, size, 0);
|
||||
}
|
||||
|
||||
void ed_frame() {
|
||||
state.ui_cx.frame_elements.data[0].size.computed_size[0] = state.gfx_cx->frame_width;
|
||||
state.ui_cx.frame_elements.data[0].size.computed_size[1] = state.gfx_cx->frame_height;
|
||||
|
||||
ui_element(&state.ui_cx, _String("Number 1"));
|
||||
ui_element(&state.ui_cx, _String("ui element 2"));
|
||||
ui_element(&state.ui_cx, _String("ui element 3"));
|
||||
ui_element(&state.ui_cx, _String("Number 1"), UI_AXIS_VERTICAL, ui_make_size(ui_children_sum, ui_fill), 0);
|
||||
ui_push_parent(&state.ui_cx);
|
||||
{
|
||||
ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fill), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
|
||||
ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fit_text), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
|
||||
}
|
||||
ui_pop_parent(&state.ui_cx);
|
||||
|
||||
// Test buffer
|
||||
ed_buffer_render(&state.ui_cx, &state.test_buffer);
|
||||
|
||||
ui_compute_layout(&state.ui_cx, 0);
|
||||
|
||||
state.gpu_glyphs.size = 0;
|
||||
state.gpu_ui_rects.size = 0;
|
||||
for (size_t i = 1; i < state.ui_cx.frame_elements.size; ++i) {
|
||||
string text = state.ui_cx.frame_elements.data[i].key;
|
||||
ui_element_frame_data *elm = &state.ui_cx.frame_elements.data[i];
|
||||
|
||||
queue_text(text, (float[]){ (float)elm->size.computed_pos[0], (float)elm->size.computed_pos[1] });
|
||||
|
||||
fprintf(stderr, "size[0]: %d, size[1]: %d\n", elm->size.computed_size[0], elm->size.computed_size[1]);
|
||||
queue_ui_rect(elm->size.computed_pos, elm->size.computed_size, 16);
|
||||
}
|
||||
ui_render(&state.ui_cx, render_ui_text, render_ui_rect);
|
||||
|
||||
ui_update_cache(&state.ui_cx, 0);
|
||||
ui_prune(&state.ui_cx);
|
||||
|
||||
state.gfx_cx->num_glyphs = state.gpu_glyphs.size;
|
||||
if (state.gpu_glyphs.size > 0) {
|
||||
gfx_update_buffer(state.gfx_cx, 2, state.gpu_glyphs.data, state.gpu_glyphs.size * sizeof(GpuGlyph));
|
||||
fprintf(stderr, "updated glyph buffer: %zu\n", state.gpu_glyphs.size);
|
||||
}
|
||||
|
||||
state.gfx_cx->num_ui_rects = state.gpu_ui_rects.size;
|
||||
if (state.gpu_ui_rects.size > 0) {
|
||||
gfx_update_buffer(state.gfx_cx, 4, state.gpu_ui_rects.data, state.gpu_ui_rects.size * sizeof(GpuUiRect));
|
||||
}
|
||||
|
|
18
src/string.h
18
src/string.h
|
@ -1,13 +1,17 @@
|
|||
#ifndef ED_STRING_INCLUDED
|
||||
#define ED_STRING_INCLUDED
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text) })
|
||||
#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text), .owned = false })
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
|
||||
bool owned;
|
||||
} string;
|
||||
|
||||
|
||||
|
@ -22,5 +26,17 @@ bool string_eq(string a, string b) {
|
|||
return true;
|
||||
}
|
||||
|
||||
string string_copy(string a) {
|
||||
string new_string;
|
||||
|
||||
new_string.data = malloc(a.len * sizeof(uint8_t));
|
||||
new_string.len = a.len;
|
||||
new_string.owned = true;
|
||||
|
||||
memcpy(new_string.data, a.data, new_string.len * sizeof(uint8_t));
|
||||
|
||||
return new_string;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
72
src/ui.h
72
src/ui.h
|
@ -52,6 +52,13 @@ typedef struct {
|
|||
};
|
||||
} ui_semantic_size;
|
||||
|
||||
#define ui_make_size(horizontal, vertical) ((ui_semantic_size[2]) { horizontal, vertical })
|
||||
|
||||
#define ui_fit_text ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_FIT_TEXT })
|
||||
#define ui_fill ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_FILL })
|
||||
#define ui_children_sum ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_CHILDREN_SUM })
|
||||
#define ui_exact(value) ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_EXACT, .integer = value })
|
||||
|
||||
typedef struct {
|
||||
ui_axis axis;
|
||||
ui_semantic_size semantic_size[2];
|
||||
|
@ -142,7 +149,19 @@ ui_context ui_init_context() {
|
|||
};
|
||||
}
|
||||
|
||||
size_t ui_element(ui_context *cx, string label) {
|
||||
void ui_push_parent(ui_context *cx) {
|
||||
if (cx->frame_elements.size > 0) {
|
||||
cx->current_parent = cx->frame_elements.size-1;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_pop_parent(ui_context *cx) {
|
||||
if (_parent(cx->current_parent) < SIZE_MAX) {
|
||||
cx->current_parent = _parent(cx->current_parent);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ui_element(ui_context *cx, string label, ui_axis axis, ui_semantic_size size[2], ui_flags flags) {
|
||||
ui_element_frame_data frame_data = (ui_element_frame_data) {
|
||||
.index = cx->frame_elements.size,
|
||||
// TODO: don't just set this to label, because then elements
|
||||
|
@ -154,8 +173,10 @@ size_t ui_element(ui_context *cx, string label) {
|
|||
.next = -1,
|
||||
.prev = cx->frame_elements.data[cx->current_parent].last,
|
||||
.parent = cx->current_parent,
|
||||
.size.semantic_size[0].type = UI_SEMANTIC_SIZE_FILL,
|
||||
.size.semantic_size[1].type = UI_SEMANTIC_SIZE_FIT_TEXT,
|
||||
.size.axis = axis,
|
||||
.size.semantic_size[0] = size[0],
|
||||
.size.semantic_size[1] = size[1],
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
// Get cached element data
|
||||
|
@ -163,13 +184,18 @@ size_t ui_element(ui_context *cx, string label) {
|
|||
if (cache_data) {
|
||||
cache_data->last_instantiated_index = cx->frame_index;
|
||||
|
||||
frame_data.size = cache_data->size;
|
||||
frame_data.size.computed_pos[0] = cache_data->size.computed_pos[0];
|
||||
frame_data.size.computed_pos[1] = cache_data->size.computed_pos[1];
|
||||
|
||||
frame_data.size.computed_size[0] = cache_data->size.computed_size[0];
|
||||
frame_data.size.computed_size[1] = cache_data->size.computed_size[1];
|
||||
} else {
|
||||
assert("couldn't insert into ui element cache" && ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
|
||||
bool did_insert = ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
|
||||
.label = label,
|
||||
.size = { 0 },
|
||||
.last_instantiated_index = cx->frame_index,
|
||||
}));
|
||||
});
|
||||
assert("couldn't insert into ui element cache" && did_insert);
|
||||
}
|
||||
|
||||
pushArray(ui_element_frame_data, &cx->frame_elements, frame_data);
|
||||
|
@ -386,6 +412,32 @@ void ui_update_cache(ui_context *cx, size_t element_index) {
|
|||
}
|
||||
}
|
||||
|
||||
typedef void (*_ui_render_text_func)(string text, float position[2]);
|
||||
typedef void (*_ui_render_rect_func)(float position[2], float size[2]);
|
||||
void ui_render(ui_context *cx, _ui_render_text_func text_func, _ui_render_rect_func rect_func) {
|
||||
for (size_t i = 1; i < cx->frame_elements.size; ++i) {
|
||||
string text = cx->frame_elements.data[i].key;
|
||||
ui_element_frame_data *elm = &cx->frame_elements.data[i];
|
||||
|
||||
if (_flags(i, UI_FLAG_DRAW_TEXT)) {
|
||||
text_func(text, (float[]){ (float)elm->size.computed_pos[0], (float)elm->size.computed_pos[1] });
|
||||
}
|
||||
|
||||
if (_flags(i, UI_FLAG_DRAW_BACKGROUND)) {
|
||||
rect_func(
|
||||
(float[]){
|
||||
(float)elm->size.computed_pos[0],
|
||||
(float)elm->size.computed_pos[1]
|
||||
},
|
||||
(float[]){
|
||||
(float)elm->size.computed_size[0],
|
||||
(float)elm->size.computed_size[1]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_prune(ui_context *cx) {
|
||||
for (size_t i = 0; i < cx->cached_elements.capacity; ++i) {
|
||||
if (cx->cached_elements.key_slots[i].key.data != NULL) {
|
||||
|
@ -401,6 +453,14 @@ void ui_prune(ui_context *cx) {
|
|||
}
|
||||
}
|
||||
|
||||
size_t child_index = _elm(0)->first;
|
||||
do {
|
||||
__auto_type elm = _elm(child_index);
|
||||
if (elm->label.owned) {
|
||||
free(elm->label.data);
|
||||
}
|
||||
} while ((child_index = _next(child_index)) < SIZE_MAX);
|
||||
|
||||
cx->frame_index += 1;
|
||||
cx->frame_elements.size = 1;
|
||||
cx->frame_elements.data[0].first = SIZE_MAX;
|
||||
|
|
Loading…
Reference in New Issue