Unity build 0-dependency clean up

This commit is contained in:
David Gonzalez Martin 2025-01-05 21:50:13 -06:00 committed by David
parent 85480c11aa
commit d74eeb6d98
448 changed files with 4316 additions and 182239 deletions

View File

@ -10,13 +10,18 @@ on:
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
env:
BB_CI: 1
jobs: jobs:
generate-config: generate-config:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: write-all permissions: write-all
outputs: outputs:
BIRTH_GITHUB_TARGETS: ${{ steps.generate-config.outputs.BIRTH_GITHUB_TARGETS }} BIRTH_GITHUB_TARGETS: ${{ steps.generate-config.outputs.BIRTH_GITHUB_TARGETS }}
BIRTH_BUILD_TYPES: ${{ steps.generate-config.outputs.BIRTH_BUILD_TYPES }}
BIRTH_CMAKE_BUILD_TYPES: ${{ steps.generate-config.outputs.BIRTH_CMAKE_BUILD_TYPES }} BIRTH_CMAKE_BUILD_TYPES: ${{ steps.generate-config.outputs.BIRTH_CMAKE_BUILD_TYPES }}
BIRTH_COMPILERS: ${{ steps.generate-config.outputs.BIRTH_COMPILERS }}
BIRTH_MACOS_IMAGE: ${{ steps.generate-config.outputs.BIRTH_LINUX_IMAGE }} BIRTH_MACOS_IMAGE: ${{ steps.generate-config.outputs.BIRTH_LINUX_IMAGE }}
BIRTH_LINUX_IMAGE: ${{ steps.generate-config.outputs.BIRTH_LINUX_IMAGE }} BIRTH_LINUX_IMAGE: ${{ steps.generate-config.outputs.BIRTH_LINUX_IMAGE }}
BIRTH_WINDOWS_IMAGE: ${{ steps.generate-config.outputs.BIRTH_WINDOWS_IMAGE }} BIRTH_WINDOWS_IMAGE: ${{ steps.generate-config.outputs.BIRTH_WINDOWS_IMAGE }}
@ -26,7 +31,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Generate config - name: Generate config
id: generate-config id: generate-config
uses: birth-software/github-config@v3 uses: birth-software/github-config@v4
- name: Create tag - name: Create tag
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
shell: bash shell: bash
@ -52,8 +57,24 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: ${{ fromJSON(needs.generate-config.outputs.BIRTH_GITHUB_TARGETS) }} os: [ ubuntu-latest, macos-latest, windows-latest ]
CMAKE_BUILD_TYPE: ${{ fromJSON(needs.generate-config.outputs.BIRTH_CMAKE_BUILD_TYPES) }} C_COMPILER: ${{ fromJSON(needs.generate-config.outputs.BIRTH_COMPILERS) }}
BIRTH_BUILD_TYPE: ${{ fromJSON(needs.generate-config.outputs.BIRTH_BUILD_TYPES) }}
exclude:
- C_COMPILER: gcc
os: macos-latest
- C_COMPILER: gcc
os: windows-latest
- C_COMPILER: cl
os: macos-latest
- C_COMPILER: cl
os: ubuntu-latest
- C_COMPILER: tcc
BIRTH_BUILD_TYPE: release_safe
- C_COMPILER: tcc
BIRTH_BUILD_TYPE: release_fast
- C_COMPILER: tcc
BIRTH_BUILD_TYPE: release_small
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
BIRTH_LINUX_IMAGE: ${{ needs.generate-config.outputs.BIRTH_LINUX_IMAGE }} BIRTH_LINUX_IMAGE: ${{ needs.generate-config.outputs.BIRTH_LINUX_IMAGE }}
@ -61,96 +82,48 @@ jobs:
BIRTH_WINDOWS_IMAGE: ${{ needs.generate-config.outputs.BIRTH_WINDOWS_IMAGE }} BIRTH_WINDOWS_IMAGE: ${{ needs.generate-config.outputs.BIRTH_WINDOWS_IMAGE }}
RELEASE_TAG_NAME: ${{ needs.generate-config.outputs.RELEASE_TAG_NAME }} RELEASE_TAG_NAME: ${{ needs.generate-config.outputs.RELEASE_TAG_NAME }}
steps: steps:
- name: Maximize build space
if: matrix.os == env.BIRTH_LINUX_IMAGE
uses: AdityaGarg8/remove-unwanted-software@v4.1
with:
remove-android: 'true'
remove-dotnet: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'
remove-large-packages: 'true'
remove-cached-tools: 'true'
remove-swapfile: 'true'
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: birth-software/github-setup@v3 - name: Fetch dependencies
- name: Fetch LLVM if: matrix.os == 'ubuntu-latest'
id: fetch_llvm run: |
run: ./fetch-llvm.sh if [[ "${{matrix.C_COMPILER}}" == "tcc" ]]; then
wget https://github.com/birth-software/tinycc/releases/download/dev/tcc-x86_64-linux.7z
7z x tcc-x86_64-linux.7z
echo $PWD/tinycc/bin >> $GITHUB_PATH
fi
- name: Fetch dependencies
if: matrix.os == 'macos-latest'
run: |
if [[ "${{matrix.C_COMPILER}}" == "tcc" ]]; then
wget https://github.com/birth-software/tinycc/releases/download/dev/tcc-aarch64-macos.7z
7z x tcc-aarch64-macos.7z
echo $PWD/tinycc/bin >> $GITHUB_PATH
fi
- name: Fetch dependencies
if: matrix.os == 'windows-latest'
shell: bash shell: bash
env: run: |
CMAKE_BUILD_TYPE: ${{matrix.CMAKE_BUILD_TYPE}} set -eux
if [[ "${{matrix.C_COMPILER}}" == "tcc" ]]; then
curl -L https://github.com/birth-software/tinycc/releases/download/dev/tcc-x86_64-windows.7z --output tcc-x86_64-windows.7z
7z x tcc-x86_64-windows.7z
TCC_PATH=$(cygpath -w "$PWD/tinycc")
echo $TCC_PATH >> $GITHUB_PATH
fi
- name: Build - name: Build
id: build if: matrix.os != 'windows-latest'
run: ./project.sh
shell: bash
env: env:
CMAKE_BUILD_TYPE: ${{matrix.CMAKE_BUILD_TYPE}} CC: ${{matrix.C_COMPILER}}
CMAKE_PREFIX_PATH: ${{steps.fetch_llvm.outputs.CMAKE_PREFIX_PATH}} BB_BUILD_TYPE: ${{matrix.BIRTH_BUILD_TYPE}}
BB_IS_CI: true run: ./build.sh
- name: Test - name: Build
run: ./project.sh test all if: matrix.os == 'windows-latest'
shell: bash shell: cmd
env: env:
CMAKE_BUILD_TYPE: ${{matrix.CMAKE_BUILD_TYPE}} CC: ${{matrix.C_COMPILER}}
CMAKE_PREFIX_PATH: ${{steps.fetch_llvm.outputs.CMAKE_PREFIX_PATH}} BB_BUILD_TYPE: ${{matrix.BIRTH_BUILD_TYPE}}
BB_IS_CI: true run: |
- name: Package call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
id: package call build.bat || exit /b 1
if: ${{ always() && steps.build.outcome == 'success' }} - name: Run
run: ./package.sh run: ./cache/bb
shell: bash
env:
CMAKE_BUILD_TYPE: ${{matrix.CMAKE_BUILD_TYPE}}
BB_IS_CI: true
- name: Upload artifact
if: ${{ always() && steps.package.outcome == 'success' }}
uses: actions/upload-artifact@v4
with:
name: ${{env.BLOAT_BUSTER_RELEASE_NAME_BASE}}
if-no-files-found: 'error'
path: ${{env.BLOAT_BUSTER_RELEASE_PATH_BASE}}.7z*
- name: Release - Attempt 1
id: release_attempt_1
uses: softprops/action-gh-release@v2
if: github.ref == 'refs/heads/main'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: "Development build"
tag_name: ${{ env.RELEASE_TAG_NAME }}
fail_on_unmatched_files: true
make_latest: true
files: |
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z.b2sum
continue-on-error: true
- name: Release - Attempt 2
id: release_attempt_2
if: ${{ steps.release_attempt_1.outcome == 'failure' && github.ref == 'refs/heads/main' }}
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: "Development build"
tag_name: ${{ env.RELEASE_TAG_NAME }}
fail_on_unmatched_files: true
make_latest: true
files: |
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z.b2sum
continue-on-error: true
- name: Release - Attempt 3
if: ${{ steps.release_attempt_2.outcome == 'failure' && github.ref == 'refs/heads/main' }}
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: "Development build"
tag_name: ${{ env.RELEASE_TAG_NAME }}
fail_on_unmatched_files: true
make_latest: true
files: |
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z
${{ env.BLOAT_BUSTER_RELEASE_PATH_BASE }}.7z.b2sum

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ project
/project.dSYM/ /project.dSYM/
/.cache/ /.cache/
imgui.ini imgui.ini
/cache/
.DS_Store

281
bootstrap/bloat-buster/bb.c Normal file
View File

@ -0,0 +1,281 @@
#if 0
#include <std/base.h>
#include <std/project.h>
#include <std/os.h>
#include <std/virtual_buffer.h>
#include <std/windowing.h>
#include <std/rendering.h>
#include <std/ui_core.h>
#include <std/ui_builder.h>
#include <std/base.c>
#include <std/os.c>
#include <std/virtual_buffer.c>
#include <std/windowing.c>
#include <std/rendering.c>
#include <std/ui_core.c>
#include <std/ui_builder.c>
#define default_font_height (24)
global_variable u32 proportional_font_height = default_font_height;
global_variable u32 monospace_font_height = default_font_height;
fn TextureIndex white_texture_create(Arena* arena, Renderer* renderer)
{
u32 white_texture_width = 1024;
u32 white_texture_height = white_texture_width;
let(white_texture_buffer, arena_allocate(arena, u32, white_texture_width * white_texture_height));
memset(white_texture_buffer, 0xff, white_texture_width * white_texture_height * sizeof(u32));
let(white_texture, renderer_texture_create(renderer, (TextureMemory) {
.pointer = white_texture_buffer,
.width = white_texture_width,
.height = white_texture_height,
.depth = 1,
.format = TEXTURE_FORMAT_R8G8B8A8_SRGB,
}));
return white_texture;
}
STRUCT(BBPanel)
{
BBPanel* first;
BBPanel* last;
BBPanel* next;
BBPanel* previous;
BBPanel* parent;
f32 parent_percentage;
Axis2 split_axis;
};
STRUCT(BBWindow)
{
WindowingInstance* handle;
RenderWindow* render;
BBWindow* previous;
BBWindow* next;
BBPanel* root_panel;
UI_State* ui;
};
STRUCT(BBGUIState)
{
Arena* arena;
Timestamp last_frame_timestamp;
BBWindow* first_window;
BBWindow* last_window;
Renderer* renderer;
// TODO: should this not be thread local?
WindowingEventQueue event_queue;
};
global_variable BBGUIState state;
fn void ui_top_bar()
{
ui_push(pref_height, ui_em(1, 1));
{
ui_push(child_layout_axis, AXIS2_X);
let(top_bar, ui_widget_make((UI_WidgetFlags) {
}, strlit("top_bar")));
ui_push(parent, top_bar);
{
ui_button(strlit("Button 1"));
ui_button(strlit("Button 2"));
ui_button(strlit("Button 3"));
}
ui_pop(parent);
ui_pop(child_layout_axis);
}
ui_pop(pref_height);
}
STRUCT(UI_Node)
{
String name;
String type;
String value;
String namespace;
String function;
};
fn void ui_node(UI_Node node)
{
let(node_widget, ui_widget_make_format((UI_WidgetFlags) {
.draw_background = 1,
.draw_text = 1,
}, "{s} : {s} = {s}##{s}{s}", node.name, node.type, node.value, node.function, node.namespace));
}
fn void app_update()
{
let(frame_end, os_timestamp());
windowing_poll_events(/* &state.event_queue */);
let(frame_ms, os_resolve_timestamps(state.last_frame_timestamp, frame_end, TIME_UNIT_MILLISECONDS));
state.last_frame_timestamp = frame_end;
Renderer* renderer = state.renderer;
BBWindow* window = state.first_window;
while (likely(window))
{
let(previous, window->previous);
let(next, window->next);
let(render_window, window->render);
renderer_window_frame_begin(renderer, render_window);
ui_state_select(window->ui);
if (likely(ui_build_begin(window->handle, frame_ms, &state.event_queue)))
{
ui_push(font_size, default_font_height);
ui_top_bar();
ui_push(child_layout_axis, AXIS2_X);
let(workspace_widget, ui_widget_make_format((UI_WidgetFlags) {}, "workspace{u64}", window->handle));
ui_push(parent, workspace_widget);
{
// Node visualizer
ui_push(child_layout_axis, AXIS2_Y);
let(node_visualizer_widget, ui_widget_make_format((UI_WidgetFlags) {
.draw_background = 1,
}, "node_visualizer{u64}", window->handle));
ui_push(parent, node_visualizer_widget);
{
ui_node((UI_Node) {
.name = strlit("a"),
.type = strlit("s32"),
.value = strlit("1"),
.namespace = strlit("foo"),
.function = strlit("main"),
});
ui_node((UI_Node) {
.name = strlit("b"),
.type = strlit("s32"),
.value = strlit("2"),
.namespace = strlit("foo"),
.function = strlit("main"),
});
}
ui_pop(parent);
ui_pop(child_layout_axis);
// Side-panel stub
ui_button(strlit("Options"));
}
ui_pop(parent);
ui_pop(child_layout_axis);
ui_build_end();
ui_draw();
ui_pop(font_size);
renderer_window_frame_end(renderer, render_window);
}
else
{
if (previous)
{
previous->next = next;
}
if (next)
{
next->previous = previous;
}
if (state.first_window == window)
{
state.first_window = next;
}
if (state.last_window == window)
{
state.last_window = previous;
}
}
window = next;
}
}
fn void window_refresh_callback(WindowingInstance* window, void* context)
{
unused(window);
unused(context);
app_update();
}
int main()
{
state.arena = arena_initialize_default(MB(2));
if (!windowing_initialize())
{
return 1;
}
state.renderer = rendering_initialize(state.arena);
if (!state.renderer)
{
return 1;
}
WindowingInstantiate window_create_options = {
.name = strlit("Bloat Buster"),
.size = { .width = 1600, .height = 900 },
};
state.first_window = state.last_window = arena_allocate(state.arena, BBWindow, 1);
state.first_window->handle = windowing_instantiate(window_create_options);
state.first_window->render = rendering_initialize_window(state.renderer, state.first_window->handle);
state.first_window->ui = ui_state_allocate(state.renderer, state.first_window->render);
state.first_window->root_panel = arena_allocate(state.arena, BBPanel, 1);
state.first_window->root_panel->parent_percentage = 1.0f;
state.first_window->root_panel->split_axis = AXIS2_X;
#ifndef __APPLE__
window_rect_texture_update_begin(state.first_window->render);
let(white_texture, white_texture_create(state.arena, state.renderer));
TextureAtlasCreate monospace_font_create = {
#ifdef _WIN32
.font_path = strlit("C:/Users/David/Downloads/Fira_Sans/FiraSans-Regular.ttf"),
#elif defined(__linux__)
.font_path = strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf"),
#elif defined(__APPLE__)
.font_path = strlit("/Users/david/Library/Fonts/FiraSans-Regular.ttf"),
#else
.font_path = strlit("WRONG_PATH"),
#endif
.text_height = monospace_font_height,
};
let(monospace_font, font_texture_atlas_create(state.arena, state.renderer, monospace_font_create));
let(proportional_font, monospace_font);
window_queue_rect_texture_update(state.first_window->render, RECT_TEXTURE_SLOT_WHITE, white_texture);
renderer_queue_font_update(state.renderer, state.first_window->render, RENDER_FONT_TYPE_MONOSPACE, monospace_font);
renderer_queue_font_update(state.renderer, state.first_window->render, RENDER_FONT_TYPE_PROPORTIONAL, proportional_font);
window_rect_texture_update_end(state.renderer, state.first_window->render);
#endif
state.last_frame_timestamp = os_timestamp();
while (state.first_window)
{
app_update();
}
return 0;
}
#else
int main()
{
return 0;
}
#endif

View File

@ -1,343 +0,0 @@
#pragma once
#ifdef NDEBUG
#define BB_DEBUG 0
#else
#define BB_DEBUG 1
#endif
#ifdef STATIC
#define LINK_LIBC 0
#else
#define LINK_LIBC 1
#endif
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#if defined(__x86_64__)
#include <immintrin.h>
#endif
#include <math.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef __uint128_t u128;
typedef unsigned int uint;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
typedef __int128_t s128;
typedef size_t usize;
typedef _Float16 f16;
typedef float f32;
typedef double f64;
typedef u32 Hash32;
typedef u64 Hash64;
#define STRUCT_FORWARD_DECL(S) typedef struct S S
#define STRUCT(S) STRUCT_FORWARD_DECL(S); struct S
#define UNION_FORWARD_DECL(U) typedef union U U
#define UNION(U) UNION_FORWARD_DECL(U); union U
typedef enum Corner
{
CORNER_00,
CORNER_01,
CORNER_10,
CORNER_11,
CORNER_COUNT,
} Corner;
typedef float float2 __attribute__((ext_vector_type(2)));
typedef float float3 __attribute__((ext_vector_type(3)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef float2 vec2;
typedef float3 vec3;
typedef float4 vec4;
typedef u32 uint2 __attribute__((ext_vector_type(2)));
typedef u32 uint3 __attribute__((ext_vector_type(3)));
typedef u32 uint4 __attribute__((ext_vector_type(4)));
UNION(F32Interval2)
{
struct
{
float2 min;
float2 max;
};
struct
{
float2 p0;
float2 p1;
};
struct
{
f32 x0;
f32 y0;
f32 x1;
f32 y1;
};
float2 v[2];
};
static_assert(sizeof(F32Interval2) == 4 * sizeof(f32));
typedef enum Axis2
{
AXIS2_X,
AXIS2_Y,
AXIS2_COUNT,
} Axis2;
#ifdef __cplusplus
#define EXPORT extern "C"
#else
#define EXPORT
#endif
#if defined(__cplusplus) && defined(__linux__)
#define NO_EXCEPT __THROW
#else
#define NO_EXCEPT
#endif
#define Slice(T) Slice_ ## T
#define SliceP(T) SliceP_ ## T
#define declare_slice_ex(T, StructName) STRUCT(StructName) \
{\
T* pointer;\
u64 length;\
}
#define declare_slice(T) declare_slice_ex(T, Slice(T))
#define declare_slice_p(T) declare_slice_ex(T*, SliceP(T))
declare_slice(u8);
declare_slice(u16);
declare_slice(u32);
declare_slice(u64);
declare_slice(s8);
declare_slice(s16);
declare_slice(s32);
declare_slice(s64);
declare_slice_p(char);
declare_slice_p(void);
typedef Slice(u8) String;
declare_slice(String);
#define NamedEnumMemberEnum(e, enum_member) e ## _ ## enum_member
#define NamedEnumMemberString(e, enum_member) strlit(#enum_member)
typedef SliceP(char) CStringSlice;
#ifdef _WIN32
typedef void* FileDescriptor;
#else
typedef int FileDescriptor;
#endif
#define FOR_N(it, start, end) \
for (u32 it = (start), end__ = (end); it < end__; ++it)
#define FOR_REV_N(it, start, end) \
for (u32 it = (end), start__ = (start); (it--) > start__;)
#define FOR_BIT(it, start, bits) \
for (typeof(bits) _bits_ = (bits), it = (start); _bits_; _bits_ >>= 1, ++it) if (_bits_ & 1)
#define FOREACH_SET(it, set) \
FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.pointer[_i])
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define CLAMP(a, x, b) (((a)>(x))?(a):((b)<(x))?(b):(x))
#ifndef INFINITY
#define INFINITY __builtin_inff()
#endif
#ifndef NAN
#define NAN __builtin_nanf("")
#endif
#define fn static
#define method __attribute__((visibility("internal")))
#define global_variable static
#define forceinline __attribute__((always_inline))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define breakpoint() __builtin_debugtrap()
#define failed_execution() my_panic("Failed execution at {cstr}:{u32}\n", __FILE__, __LINE__)
#define trap() __builtin_trap()
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
#define KB(n) ((n) * 1024)
#define MB(n) ((n) * 1024 * 1024)
#define GB(n) ((u64)(n) * 1024 * 1024 * 1024)
#define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024)
#define unused(x) (void)(x)
#define may_be_unused __attribute__((unused))
#define truncate_value(Destination, source) (Destination)(source)
#define cast_to(Destination, Source, source) cast_ ## Source ## _to_ ## Destination (source, __FILE__, __LINE__)
#define size_until_end(T, field_name) (sizeof(T) - offsetof(T, field_name))
#define SWAP(a, b) \
do {\
auto temp = a;\
a = b;\
b = temp;\
} while (0)
#define slice_from_pointer_range(T, start, end) (Slice(T)) { .pointer = start, .length = (u64)(end - start), }
#define strlit_len(s) (sizeof(s) - 1)
#define strlit(s) (String){ .pointer = (u8*)(s), .length = strlit_len(s), }
#define ch_to_str(ch) (String){ .pointer = &ch, .length = 1 }
#define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) }
#define array_to_bytes(arr) { .pointer = (u8*)(arr), .length = sizeof(arr) }
#define pointer_to_bytes(p) (String) { .pointer = (u8*)(p), .length = sizeof(*p) }
#define scalar_to_bytes(s) pointer_to_bytes(&(s))
#define string_to_c(s) ((char*)((s).pointer))
#define cstr(s) ((String) { .pointer = (u8*)(s), .length = strlen((char*)s), } )
#define case_to_name(prefix, e) case prefix ## e: return strlit(#e)
const may_be_unused global_variable u8 brace_open = '{';
const may_be_unused global_variable u8 brace_close = '}';
const may_be_unused global_variable u8 parenthesis_open = '(';
const may_be_unused global_variable u8 parenthesis_close = ')';
const may_be_unused global_variable u8 bracket_open = '[';
const may_be_unused global_variable u8 bracket_close = ']';
#define s_get(s, i) (s).pointer[i]
#define s_get_pointer(s, i) &((s).pointer[i])
#define s_get_slice(T, s, start, end) (Slice(T)){ .pointer = ((s).pointer) + (start), .length = (end) - (start) }
#define s_equal(a, b) ((a).length == (b).length && memcmp((a).pointer, (b).pointer, sizeof(*((a).pointer)) * (a).length) == 0)
#if BB_DEBUG
#define assert(x) if (unlikely(!(x))) { my_panic("Assert failed: \"" # x "\" at {cstr}:{u32}\n", __FILE__, __LINE__); }
#else
#define assert(x) unlikely(!(x))
#endif
#ifndef __cplusplus
// Undefine unreachable if needed to provide a more safe-guard implementation
#ifdef unreachable
#undef unreachable
#endif
#if BB_DEBUG
#define unreachable() my_panic("Unreachable triggered\n", __FILE__, __LINE__)
#else
#define unreachable() __builtin_unreachable()
#endif
#define static_assert(x) _Static_assert((x), "Static assert failed!")
#define alignof(x) _Alignof(x)
#define auto __auto_type
#else
#define restrict __restrict
#endif
#define todo() my_panic("TODO at {cstr}:{u32}\n", __FILE__, __LINE__)
EXPORT u64 align_forward(u64 value, u64 alignment);
EXPORT u64 align_backward(u64 value, u64 alignment);
EXPORT u8 log2_alignment(u64 alignment);
EXPORT u8 is_power_of_two(u64 value);
EXPORT u8 first_bit_set_32(u32 value);
EXPORT u64 first_bit_set_64(u64 value);
EXPORT void* memcpy(void* const restrict dst, const void* const restrict src, usize size) NO_EXCEPT;
EXPORT void* memmove(void* const dst, const void* const src, usize n) NO_EXCEPT;
EXPORT void* memset(void* dst, int n, usize size) NO_EXCEPT;
EXPORT int memcmp(const void* a, const void* b, usize n) NO_EXCEPT;
EXPORT usize strlen (const char* c_string) NO_EXCEPT;
EXPORT int strcmp(const char* s1, const char* s2) NO_EXCEPT;
EXPORT int strncmp(const char* s1, const char* s2, usize length) NO_EXCEPT;
EXPORT u8 cast_u32_to_u8(u32 source, const char* name, int line);
EXPORT u16 cast_u32_to_u16(u32 source, const char* name, int line);
EXPORT s16 cast_u32_to_s16(u32 source, const char* name, int line);
EXPORT s32 cast_u32_to_s32(u32 source, const char* name, int line);
EXPORT u8 cast_u64_to_u8(u64 source, const char* name, int line);
EXPORT u16 cast_u64_to_u16(u64 source, const char* name, int line);
EXPORT u32 cast_u64_to_u32(u64 source, const char* name, int line);
EXPORT s32 cast_u64_to_s32(u64 source, const char* name, int line);
EXPORT s64 cast_u64_to_s64(u64 source, const char* name, int line);
EXPORT u8 cast_s32_to_u8(s32 source, const char* name, int line);
EXPORT u16 cast_s32_to_u16(s32 source, const char* name, int line);
EXPORT u32 cast_s32_to_u32(s32 source, const char* name, int line);
EXPORT u64 cast_s32_to_u64(s32 source, const char* name, int line);
EXPORT s16 cast_s32_to_s16(s32 source, const char* name, int line);
EXPORT u16 cast_s64_to_u16(s64 source, const char* name, int line);
EXPORT u32 cast_s64_to_u32(s64 source, const char* name, int line);
EXPORT u64 cast_s64_to_u64(s64 source, const char* name, int line);
EXPORT s32 cast_s64_to_s32(s64 source, const char* name, int line);
EXPORT u32 format_decimal(String buffer, u64 decimal);
EXPORT u32 format_hexadecimal(String buffer, u64 hexadecimal);
EXPORT u64 format_float(String buffer, f64 value_double);
EXPORT u64 is_decimal_digit(u8 ch);
EXPORT u32 is_space(u8 ch, u8 next_ch);
EXPORT u8 get_next_ch_safe(String string, u64 index);
EXPORT u64 is_identifier_start(u8 ch);
EXPORT u64 is_identifier_ch(u8 ch);
EXPORT u64 is_alphabetic(u8 ch);
EXPORT u64 parse_decimal(String string);
global_variable const Hash64 fnv_offset = 14695981039346656037ull;
global_variable const u64 fnv_prime = 1099511628211ull;
EXPORT Hash32 hash32_fib_end(Hash32 hash);
EXPORT Hash32 hash64_fib_end(Hash64 hash);
EXPORT Hash64 hash_byte(Hash64 source, u8 ch);
EXPORT Hash64 hash_bytes(String bytes);
EXPORT Hash32 hash64_to_hash32(Hash64 hash64);
EXPORT u64 round_up_to_next_power_of_2(u64 n);
STRUCT(TextureIndex)
{
u32 value;
};
EXPORT void print(const char* format, ...);
EXPORT u8 os_is_being_debugged();
fn u64 safe_flag(u64 value, u64 flag)
{
u64 result = value & ((u64)0 - flag);
return result;
}
#define member_from_offset(pointer, type, memory_offset) (*(type*)((u8*)pointer + memory_offset))
#define offset_of(T, member) __builtin_offsetof(T, member)
#define my_panic(...) do \
{\
print(__VA_ARGS__);\
if (os_is_being_debugged())\
{\
trap();\
}\
else\
{\
exit(1);\
}\
} while (0)

View File

@ -1,6 +0,0 @@
#pragma once
#include <std/base.h>
EXPORT String format_string(String buffer, const char* format, ...);
EXPORT String format_string_va(String buffer, const char* format, va_list args);

View File

@ -1,120 +0,0 @@
#pragma once
#include <std/base.h>
typedef enum TimeUnit
{
TIME_UNIT_NANOSECONDS,
TIME_UNIT_MICROSECONDS,
TIME_UNIT_MILLISECONDS,
TIME_UNIT_SECONDS,
} TimeUnit;
STRUCT(Timestamp)
{
u128 value;
};
STRUCT(OSFileOpenFlags)
{
u32 truncate:1;
u32 executable:1;
u32 write:1;
u32 read:1;
u32 create:1;
u32 directory:1;
};
STRUCT(OSFilePermissions)
{
u8 readable:1;
u8 writable:1;
u8 executable:1;
};
STRUCT(OSReserveProtectionFlags)
{
u32 read:1;
u32 write:1;
u32 execute:1;
u32 reserved:29;
};
STRUCT(OSReserveMapFlags)
{
u32 priv:1;
u32 anon:1;
u32 noreserve:1;
u32 reserved:29;
};
STRUCT(Arena)
{
u64 reserved_size;
u64 position;
u64 os_position;
u64 granularity;
u8 reserved[4 * 8];
};
STRUCT(FileWriteOptions)
{
String path;
String content;
u8 executable;
};
#if __APPLE__
const global_variable u64 page_size = KB(16);
#else
const global_variable u64 page_size = KB(4);
#endif
global_variable u64 minimum_granularity = page_size;
// global_variable u64 middle_granularity = MB(2);
global_variable u64 default_size = GB(4);
EXPORT void print(const char* format, ...);
EXPORT void run_command(Arena* arena, CStringSlice arguments, char* envp[]);
EXPORT String file_read(Arena* arena, String path);
EXPORT void file_write(FileWriteOptions options);
EXPORT String path_dir(String string);
EXPORT String path_base(String string);
EXPORT String path_no_extension(String string);
EXPORT Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size);
EXPORT Arena* arena_init_default(u64 initial_size);
EXPORT void arena_clear(Arena* arena);
EXPORT String arena_join_string(Arena* arena, Slice(String) pieces);
EXPORT u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment);
EXPORT void arena_reset(Arena* arena);
#define arena_allocate(arena, T, count) (T*)(arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)))
#define arena_allocate_slice(arena, T, count) (Slice(T)){ .pointer = arena_allocate(arena, T, count), .length = count }
EXPORT u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map);
EXPORT void os_commit(void* address, u64 size);
EXPORT u8 os_file_descriptor_is_valid(FileDescriptor fd);
EXPORT FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions);
EXPORT void os_file_close(FileDescriptor fd);
EXPORT u64 os_file_get_size(FileDescriptor fd);
EXPORT void os_file_write(FileDescriptor fd, String content);
EXPORT FileDescriptor os_stdout_get();
EXPORT void os_directory_make(String path);
EXPORT void calibrate_cpu_timer();
EXPORT void print_string(String string);
EXPORT Timestamp os_timestamp();
EXPORT f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit);
EXPORT u8 os_is_being_debugged();
#if _WIN32
typedef void* HANDLE;
EXPORT HANDLE os_windows_get_module_handle();
#endif

View File

@ -1,182 +0,0 @@
#pragma once
#include <std/base.h>
typedef struct Renderer Renderer;
#include <std/window.h>
#include <std/font_provider.h>
typedef struct RenderWindow RenderWindow;
typedef struct Pipeline Pipeline;
STRUCT(RectDraw)
{
F32Interval2 vertex;
F32Interval2 texture;
vec4 colors[4];
u32 texture_index;
};
#include "../std/shaders/rect.inc"
typedef struct RectVertex RectVertex;
decl_vb(RectVertex);
typedef enum BBPipeline
{
BB_PIPELINE_RECT,
BB_PIPELINE_COUNT,
} BBPipeline;
typedef enum RenderFontType
{
RENDER_FONT_TYPE_MONOSPACE,
RENDER_FONT_TYPE_PROPORTIONAL,
RENDER_FONT_TYPE_COUNT,
} RenderFontType;
typedef enum RectTextureSlot
{
RECT_TEXTURE_SLOT_WHITE,
RECT_TEXTURE_SLOT_MONOSPACE_FONT,
RECT_TEXTURE_SLOT_PROPORTIONAL_FONT,
RECT_TEXTURE_SLOT_COUNT
} RectTextureSlot;
typedef enum IndexType : u8
{
INDEX_TYPE_U32,
} IndexType;
typedef enum TextureFormat : u8
{
TEXTURE_FORMAT_R8_UNORM,
TEXTURE_FORMAT_R8G8B8A8_SRGB,
} TextureFormat;
STRUCT(TextureMemory)
{
void* pointer;
u32 width;
u32 height;
u32 depth;
TextureFormat format;
};
typedef enum ShaderStage : u8
{
SHADER_STAGE_VERTEX,
SHADER_STAGE_FRAGMENT,
} ShaderStage;
STRUCT(PipelineCreate)
{
Slice(u16) shader_source_indices;
u16 layout_index;
};
declare_slice(PipelineCreate);
STRUCT(PushConstantRange)
{
u16 offset;
u16 size;
ShaderStage stage;
};
declare_slice(PushConstantRange);
typedef enum DescriptorType : u8
{
DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER,
DESCRIPTOR_TYPE_COUNT,
} DescriptorType;
STRUCT(DescriptorSetLayoutBinding)
{
u8 binding;
DescriptorType type;
ShaderStage stage;
u8 count;
};
declare_slice(DescriptorSetLayoutBinding);
STRUCT(DescriptorSetLayoutCreate)
{
Slice(DescriptorSetLayoutBinding) bindings;
};
declare_slice(DescriptorSetLayoutCreate);
STRUCT(PipelineLayoutCreate)
{
Slice(PushConstantRange) push_constant_ranges;
Slice(DescriptorSetLayoutCreate) descriptor_set_layouts;
};
declare_slice(PipelineLayoutCreate);
STRUCT(GraphicsPipelinesCreate)
{
Slice(String) shader_binaries;
Slice(PipelineLayoutCreate) layouts;
Slice(PipelineCreate) pipelines;
};
STRUCT(PipelineIndex)
{
u32 value;
};
STRUCT(PipelineLayoutIndex)
{
u32 value;
};
STRUCT(DescriptorSetIndex)
{
u32 value;
};
typedef enum BufferType : u8
{
BUFFER_TYPE_VERTEX,
BUFFER_TYPE_INDEX,
BUFFER_TYPE_STAGING,
} BufferType;
STRUCT(HostBufferCopy)
{
String source;
u64 destination_offset;
};
declare_slice(HostBufferCopy);
STRUCT(LocalBufferCopyRegion)
{
u64 source_offset;
u64 destination_offset;
u64 size;
};
declare_slice(LocalBufferCopyRegion);
EXPORT Renderer* renderer_initialize(Arena* arena);
EXPORT RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window);
EXPORT PipelineIndex renderer_graphics_pipelines_create(Renderer* renderer, Arena* arena, GraphicsPipelinesCreate create_data);
EXPORT PipelineLayoutIndex renderer_pipeline_get_layout(PipelineIndex pipeline);
EXPORT void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window);
EXPORT void renderer_window_frame_end(Renderer* renderer, RenderWindow* window);
EXPORT TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory);
EXPORT uint2 renderer_font_compute_string_rect(Renderer* renderer, RenderFontType type, String string);
EXPORT void window_command_begin(RenderWindow* window);
EXPORT void window_command_end(RenderWindow* window);
EXPORT void window_render_begin(RenderWindow* window);
EXPORT void window_render_end(RenderWindow* window);
EXPORT void window_draw_indexed(RenderWindow* window, u32 index_count, u32 instance_count, u32 first_index, s32 vertex_offset, u32 first_instance);
EXPORT void window_rect_texture_update_begin(RenderWindow* window);
EXPORT void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas);
EXPORT void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index);
EXPORT void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window);
EXPORT u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index, String vertex_memory, u32 vertex_count);
EXPORT void window_pipeline_add_indices(RenderWindow* window, BBPipeline pipeline_index, Slice(u32) indices);
EXPORT void window_render_rect(RenderWindow* window, RectDraw draw);
EXPORT void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset);

View File

@ -1,8 +0,0 @@
#include <std/base.h>
EXPORT s32 string_first_ch(String string, u8 ch);
EXPORT s64 string_last_ch(String string, u8 ch);
EXPORT u8 string_starts_with(String string, String start);
EXPORT u8 string_ends_with(String string, String end);
EXPORT u64 string_first_ocurrence(String string, String substring);
EXPORT u64 string_last_ocurrence(String string, String substring);

View File

@ -1,192 +0,0 @@
#pragma once
#include <std/base.h>
#include <std/os.h>
#include <std/virtual_buffer.h>
typedef enum OSEventType
{
OS_EVENT_TYPE_MOUSE_BUTTON,
OS_EVENT_TYPE_CURSOR_POSITION,
OS_EVENT_TYPE_CURSOR_ENTER,
OS_EVENT_TYPE_WINDOW_FOCUS,
OS_EVENT_TYPE_WINDOW_POSITION,
OS_EVENT_TYPE_WINDOW_CLOSE,
} OSEventType;
STRUCT(OSEventDescriptor)
{
u32 index:24;
OSEventType type:8;
};
static_assert(sizeof(OSEventDescriptor) == 4);
decl_vb(OSEventDescriptor);
typedef enum OSEventMouseButtonKind : u8
{
OS_EVENT_MOUSE_BUTTON_1 = 0,
OS_EVENT_MOUSE_BUTTON_2 = 1,
OS_EVENT_MOUSE_BUTTON_3 = 2,
OS_EVENT_MOUSE_BUTTON_4 = 3,
OS_EVENT_MOUSE_BUTTON_5 = 4,
OS_EVENT_MOUSE_BUTTON_6 = 5,
OS_EVENT_MOUSE_BUTTON_7 = 6,
OS_EVENT_MOUSE_BUTTON_8 = 7,
OS_EVENT_MOUSE_BUTTON_COUNT = 8,
OS_EVENT_MOUSE_LEFT = OS_EVENT_MOUSE_BUTTON_1,
OS_EVENT_MOUSE_RIGHT = OS_EVENT_MOUSE_BUTTON_2,
OS_EVENT_MOUSE_MIDDLE = OS_EVENT_MOUSE_BUTTON_3,
} OSEventMouseButtonKind;
typedef enum OSEventMouseButtonAction : u8
{
OS_EVENT_MOUSE_RELAX = 0,
OS_EVENT_MOUSE_RELEASE = 1,
OS_EVENT_MOUSE_PRESS = 2,
OS_EVENT_MOUSE_REPEAT = 3,
} OSEventMouseButtonAction;
STRUCT(OSEventMouseButtonEvent)
{
OSEventMouseButtonAction action:2;
u8 mod_shift:1;
u8 mod_control:1;
u8 mod_alt:1;
u8 mod_super:1;
u8 mod_caps_lock:1;
u8 mod_num_lock:1;
};
STRUCT(OSEventMouseButton)
{
OSEventMouseButtonKind button:3;
u8 reserved:5;
OSEventMouseButtonEvent event;
};
static_assert(sizeof(OSEventMouseButton) == sizeof(u16));
decl_vb(OSEventMouseButton);
#define OS_EVENT_BITSET_SIZE (64)
STRUCT(OSEventBitset)
{
u64 value;
};
decl_vb(OSEventBitset);
STRUCT(OSEventCursorPosition)
{
f64 x;
f64 y;
};
decl_vb(OSEventCursorPosition);
STRUCT(OSEventWindowPosition)
{
u32 x;
u32 y;
};
decl_vb(OSEventWindowPosition);
STRUCT(OSEventQueue)
{
VirtualBuffer(OSEventDescriptor) descriptors;
VirtualBuffer(OSEventMouseButton) mouse_buttons;
VirtualBuffer(OSEventBitset) window_focuses;
u32 window_focuses_count;
u32 cursor_enter_count;
VirtualBuffer(OSEventBitset) cursor_enters;
VirtualBuffer(OSEventCursorPosition) cursor_positions;
VirtualBuffer(OSEventWindowPosition) window_positions;
};
typedef void* OSWindow;
typedef void OSFramebufferResize(OSWindow window, void* context, u32 width, u32 height);
typedef void OSWindowResize(OSWindow window, void* context, u32 width, u32 height);
typedef void OSWindowRefresh(OSWindow window, void* context);
typedef void OSWindowPosition(OSWindow window, void* context, u32 x, u32 y);
typedef void OSWindowClose(OSWindow window, void* context);
typedef void OSWindowFocus(OSWindow window, void* context, u8 focused);
typedef void OSWindowIconify(OSWindow window, void* context, u8 iconified);
typedef void OSWindowMaximize(OSWindow window, void* context, u8 maximized);
typedef void OSWindowContentScale(OSWindow window, void* context, f32 x, f32 y);
typedef void OSWindowKey(OSWindow window, void* context, s32 key, s32 scancode, s32 action, s32 mods);
typedef void OSWindowCharacter(OSWindow window, void* context, u32 codepoint);
typedef void OSWindowCharacterModifier(OSWindow window, void* context, u32 codepoint, s32 mods);
typedef void OSWindowMouseButton(OSWindow window, void* context, s32 button, s32 action, s32 mods);
typedef void OSWindowCursorPosition(OSWindow window, void* context, f64 x, f64 y);
typedef void OSWindowCursorEnter(OSWindow window, void* context, u8 entered);
typedef void OSWindowScroll(OSWindow window, void* context, f64 x, f64 y);
typedef void OSWindowDrop(OSWindow window, void* context, CStringSlice paths);
STRUCT(OSWindowingCallbacks)
{
OSFramebufferResize* framebuffer_resize;
OSWindowResize* window_resize;
OSWindowRefresh* window_refresh;
OSWindowPosition* window_position;
OSWindowClose* window_close;
OSWindowFocus* window_focus;
OSWindowIconify* window_iconify;
OSWindowMaximize* window_maximize;
OSWindowContentScale* window_content_scale;
OSWindowKey* window_key;
OSWindowCharacter* window_character;
OSWindowCharacterModifier* window_character_modifier;
OSWindowMouseButton* window_mouse_button;
OSWindowCursorPosition* window_cursor_position;
OSWindowCursorEnter* window_cursor_enter;
OSWindowScroll* window_scroll;
OSWindowDrop* window_drop;
};
STRUCT(OSWindowingInitializationOptions)
{
OSWindowingCallbacks callback;
u8 should_use_x11;
};
STRUCT(OSWindowSize)
{
u32 width;
u32 height;
};
STRUCT(OSWindowCreate)
{
String name;
OSWindowSize size;
void* context;
OSWindowResize* resize_callback;
OSWindowRefresh* refresh_callback;
};
STRUCT(OSCursorPosition)
{
f64 x;
f64 y;
};
EXPORT void os_windowing_init(OSWindowingInitializationOptions options);
EXPORT OSWindow os_window_create(OSWindowCreate create);
EXPORT u8 os_window_should_close(OSWindow window);
EXPORT void os_poll_events(OSEventQueue* event_queue);
EXPORT OSCursorPosition os_window_cursor_position_get(OSWindow window);
EXPORT OSWindowSize os_window_framebuffer_size_get(OSWindow window);
EXPORT u8 os_event_queue_get_window_focus(OSEventQueue* queue, u32 index);
#ifdef __linux__
typedef unsigned long XID;
typedef struct _XDisplay Display;
typedef XID Window;
EXPORT Display* x11_display_get();
EXPORT Window x11_window_get(OSWindow window);
#endif
#ifdef _WIN32
EXPORT HANDLE win32_window_get(OSWindow window);
#endif
int window_create_surface(void* instance, OSWindow window, const void* allocator, void** surface);

View File

@ -1,317 +1,160 @@
#include <std/base.h> #pragma once
#include <std/os.h>
u8 cast_u32_to_u8(u32 source, const char* name, int line) fn u8 log2_alignment(u64 alignment)
{
#if BB_DEBUG
if (source > UINT8_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u8)source;
return result;
}
u16 cast_u32_to_u16(u32 source, const char* name, int line)
{
#if BB_DEBUG
if (source > UINT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u16)source;
return result;
}
s16 cast_u32_to_s16(u32 source, const char* name, int line)
{
#if BB_DEBUG
if (source > INT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s16)source;
return result;
}
s32 cast_u32_to_s32(u32 source, const char* name, int line)
{
#if BB_DEBUG
if (source > INT32_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s32)source;
return result;
}
u8 cast_u64_to_u8(u64 source, const char* name, int line)
{
#if BB_DEBUG
if (source > UINT8_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u8)source;
return result;
}
u16 cast_u64_to_u16(u64 source, const char* name, int line)
{
#if BB_DEBUG
if (source > UINT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u16)source;
return result;
}
u32 cast_u64_to_u32(u64 source, const char* name, int line)
{
#if BB_DEBUG
if (source > UINT32_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u32)source;
return result;
}
s32 cast_u64_to_s32(u64 source, const char* name, int line)
{
#if BB_DEBUG
if (source > INT32_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s32)source;
return result;
}
s64 cast_u64_to_s64(u64 source, const char* name, int line)
{
#if BB_DEBUG
if (source > INT64_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s64)source;
return result;
}
u8 cast_s32_to_u8(s32 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
if ((u32)source > UINT8_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u8)source;
return result;
}
u16 cast_s32_to_u16(s32 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
if ((u32)source > UINT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u16)source;
return result;
}
u32 cast_s32_to_u32(s32 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u32)source;
return result;
}
u64 cast_s32_to_u64(s32 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u64)source;
return result;
}
s16 cast_s32_to_s16(s32 source, const char* name, int line)
{
#if BB_DEBUG
if (source > INT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
if (source < INT16_MIN)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s16)source;
return result;
}
u16 cast_s64_to_u16(s64 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
if (source > UINT16_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u16)source;
return result;
}
u32 cast_s64_to_u32(s64 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u32)source;
return result;
}
u64 cast_s64_to_u64(s64 source, const char* name, int line)
{
#if BB_DEBUG
if (source < 0)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (u64)source;
return result;
}
s32 cast_s64_to_s32(s64 source, const char* name, int line)
{
#if BB_DEBUG
if (source < INT32_MIN)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
if (source > INT32_MAX)
{
my_panic("Cast failed at {cstr}:{u32}\n", name, line);
}
#else
unused(name);
unused(line);
#endif
auto result = (s32)source;
return result;
}
u8 log2_alignment(u64 alignment)
{ {
assert(alignment != 0); assert(alignment != 0);
assert((alignment & (alignment - 1)) == 0); assert((alignment & (alignment - 1)) == 0);
u64 left = (sizeof(alignment) * 8) - 1; u64 left = (sizeof(alignment) * 8) - 1;
auto right = cast_to(u64, s32, __builtin_clzll(alignment)); let_cast(u64, right, __builtin_clzll(alignment));
auto result = cast_to(u8, u64, left - right); let_cast(u8, result, left - right);
return result; return result;
} }
fn u128 u128_from_u64(u64 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = { .low = n };
return result;
#else
return n;
#endif
}
fn u64 u64_from_u128(u128 n)
{
#if defined (__TINYC__) || defined(_MSC_VER)
return n.low;
#else
return (u64)n;
#endif
}
fn u128 u128_shift_right(u128 value, u16 n)
{
#if defined (__TINYC__) || defined(_MSC_VER)
u128 result = {0, 0};
if (n < 128)
{
if (n >= 64)
{
// If n >= 64, only the high part contributes to the low part
result.low = value.high >> (n - 64);
result.high = 0;
}
else
{
// Standard case: n < 64
result.low = (value.low >> n) | (value.high << (64 - n));
result.high = value.high >> n;
}
}
return result;
#else
return value >> n;
#endif
}
fn u128 u128_shift_left(u128 value, u16 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = {0, 0};
if (n < 128)
{
if (n >= 64)
{
// If n >= 64, only the low part contributes to the high part
result.high = value.low << (n - 64);
result.low = 0;
}
else
{
// Standard case: n < 64
result.high = (value.high << n) | (value.low >> (64 - n));
result.low = value.low << n;
}
}
return result;
#else
return value << n;
#endif
}
fn u128 u128_u64_or(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
a.low |= b;
return a;
#else
return a | b;
#endif
}
fn u128 u128_u64_add(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result;
// Add the lower 64 bits and check for overflow
result.low = a.low + b;
u64 carry = (result.low < a.low) ? 1 : 0;
// Add the carry to the upper 64 bits
result.high = a.high + carry;
return result;
#else
return a + b;
#endif
}
// Multiply two u128 values
fn u128 u128_u64_mul(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = {0, 0};
// Compute low and high parts of the product
u64 low_low = (a.low & 0xFFFFFFFF) * (b & 0xFFFFFFFF);
u64 low_high = (a.low >> 32) * (b & 0xFFFFFFFF);
u64 high_low = (a.low & 0xFFFFFFFF) * (b >> 32);
u64 high_high = (a.low >> 32) * (b >> 32);
// Combine partial products for the lower 64 bits
u64 carry = (low_low >> 32) + (low_high & 0xFFFFFFFF) + (high_low & 0xFFFFFFFF);
result.low = (low_low & 0xFFFFFFFF) | (carry << 32);
// Add carry from lower to the high product
result.high = a.high * b + (low_high >> 32) + (high_low >> 32) + (carry >> 32) + high_high;
return result;
#else
return a * b;
#endif
}
fn u64 u128_shift_right_by_64(u128 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
return n.high;
#else
return n >> 64;
#endif
}
// Lehmer's generator // Lehmer's generator
// https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/ // https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/
may_be_unused global_variable u128 rn_state; may_be_unused global_variable u128 rn_state;
may_be_unused fn u64 generate_random_number() may_be_unused fn u64 generate_random_number()
{ {
rn_state *= 0xda942042e4dd58b5; rn_state = u128_u64_mul(rn_state, 0xda942042e4dd58b5);
return rn_state >> 64; return u128_shift_right_by_64(rn_state);
} }
u64 round_up_to_next_power_of_2(u64 n) fn u64 round_up_to_next_power_of_2(u64 n)
{ {
n -= 1; n -= 1;
n |= n >> 1; n |= n >> 1;
@ -326,10 +169,10 @@ u64 round_up_to_next_power_of_2(u64 n)
may_be_unused fn u64 absolute_int(s64 n) may_be_unused fn u64 absolute_int(s64 n)
{ {
return n < 0 ? cast_to(u64, s64, -n) : cast_to(u64, s64, n); return n < 0 ? cast_to(u64, -n) : cast_to(u64, n);
} }
u64 parse_decimal(String string) fn u64 parse_decimal(String string)
{ {
u64 value = 0; u64 value = 0;
for (u64 i = 0; i < string.length; i += 1) for (u64 i = 0; i < string.length; i += 1)
@ -342,7 +185,7 @@ u64 parse_decimal(String string)
return value; return value;
} }
u8 get_next_ch_safe(String string, u64 index) fn u8 get_next_ch_safe(String string, u64 index)
{ {
u64 next_index = index + 1; u64 next_index = index + 1;
u64 is_in_range = next_index < string.length; u64 is_in_range = next_index < string.length;
@ -353,7 +196,7 @@ u8 get_next_ch_safe(String string, u64 index)
return (u8)safe_result; return (u8)safe_result;
} }
u32 is_space(u8 ch, u8 next_ch) fn u32 is_space(u8 ch, u8 next_ch)
{ {
u32 is_comment = (ch == '/') & (next_ch == '/'); u32 is_comment = (ch == '/') & (next_ch == '/');
u32 is_whitespace = ch == ' '; u32 is_whitespace = ch == ' ';
@ -375,12 +218,12 @@ fn u64 is_upper(u8 ch)
return (ch >= 'A') & (ch <= 'Z'); return (ch >= 'A') & (ch <= 'Z');
} }
u64 is_alphabetic(u8 ch) fn u64 is_alphabetic(u8 ch)
{ {
return is_lower(ch) | is_upper(ch); return is_lower(ch) | is_upper(ch);
} }
u64 is_decimal_digit(u8 ch) fn u64 is_decimal_digit(u8 ch)
{ {
return (ch >= '0') & (ch <= '9'); return (ch >= '0') & (ch <= '9');
} }
@ -390,29 +233,28 @@ fn u64 is_hex_digit(u8 ch)
return (is_decimal_digit(ch) | ((ch == 'a' | ch == 'A') | (ch == 'b' | ch == 'B'))) | (((ch == 'c' | ch == 'C') | (ch == 'd' | ch == 'D')) | ((ch == 'e' | ch == 'E') | (ch == 'f' | ch == 'F'))); return (is_decimal_digit(ch) | ((ch == 'a' | ch == 'A') | (ch == 'b' | ch == 'B'))) | (((ch == 'c' | ch == 'C') | (ch == 'd' | ch == 'D')) | ((ch == 'e' | ch == 'E') | (ch == 'f' | ch == 'F')));
} }
fn u64 is_identifier_start(u8 ch)
u64 is_identifier_start(u8 ch)
{ {
u64 alphabetic = is_alphabetic(ch); u64 alphabetic = is_alphabetic(ch);
u64 is_underscore = ch == '_'; u64 is_underscore = ch == '_';
return alphabetic | is_underscore; return alphabetic | is_underscore;
} }
u64 is_identifier_ch(u8 ch) fn u64 is_identifier_ch(u8 ch)
{ {
u64 identifier_start = is_identifier_start(ch); u64 identifier_start = is_identifier_start(ch);
u64 decimal = is_decimal_digit(ch); u64 decimal = is_decimal_digit(ch);
return identifier_start | decimal; return identifier_start | decimal;
} }
Hash64 hash_byte(Hash64 source, u8 ch) fn Hash64 hash_byte(Hash64 source, u8 ch)
{ {
source ^= ch; source ^= ch;
source *= fnv_prime; source *= fnv_prime;
return source; return source;
} }
Hash64 hash_bytes(String bytes) fn Hash64 hash_bytes(String bytes)
{ {
u64 result = fnv_offset; u64 result = fnv_offset;
for (u64 i = 0; i < bytes.length; i += 1) for (u64 i = 0; i < bytes.length; i += 1)
@ -423,7 +265,7 @@ Hash64 hash_bytes(String bytes)
return result; return result;
} }
Hash32 hash64_to_hash32(Hash64 hash64) fn Hash32 hash64_to_hash32(Hash64 hash64)
{ {
Hash32 low = hash64 & 0xffff; Hash32 low = hash64 & 0xffff;
Hash32 high = (hash64 >> 32) & 0xffff; Hash32 high = (hash64 >> 32) & 0xffff;
@ -431,148 +273,46 @@ Hash32 hash64_to_hash32(Hash64 hash64)
return result; return result;
} }
u64 align_forward(u64 value, u64 alignment) fn u64 align_forward(u64 value, u64 alignment)
{ {
u64 mask = alignment - 1; u64 mask = alignment - 1;
u64 result = (value + mask) & ~mask; u64 result = (value + mask) & ~mask;
return result; return result;
} }
u64 align_backward(u64 value, u64 alignment) fn u64 align_backward(u64 value, u64 alignment)
{ {
u64 result = value & ~(alignment - 1); u64 result = value & ~(alignment - 1);
return result; return result;
} }
u8 is_power_of_two(u64 value) fn u8 is_power_of_two(u64 value)
{ {
return (value & (value - 1)) == 0; return (value & (value - 1)) == 0;
} }
u8 first_bit_set_32(u32 value) fn u8 first_bit_set_32(u32 value)
{ {
auto result = (u8)__builtin_ffs((s32)value); let(result, (u8)__builtin_ffs((s32)value));
result -= result != 0; result -= result != 0;
return result; return result;
} }
u64 first_bit_set_64(u64 value) fn u64 first_bit_set_64(u64 value)
{ {
auto result = (u8) __builtin_ffs((s64)value); let(result, (u8) __builtin_ffs((s64)value));
result -= result != 0; result -= result != 0;
return result; return result;
} }
Hash32 hash32_fib_end(Hash32 hash) fn Hash32 hash32_fib_end(Hash32 hash)
{ {
auto result = truncate_value(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32); let(result, TRUNCATE(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32));
return result; return result;
} }
Hash32 hash64_fib_end(Hash64 hash) fn Hash32 hash64_fib_end(Hash64 hash)
{ {
auto result = truncate_value(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32); let(result, TRUNCATE(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32));
return result; return result;
} }
#if LINK_LIBC == 0
int strcmp(const char* s1, const char* s2)
{
auto diff = 0;
while (1)
{
auto ch1 = *s1;
auto ch2 = *s2;
diff = ch1 - ch2;
if (ch1 == 0 || ch2 == 0 || diff)
{
break;
}
s1 += 1;
s2 += 1;
}
return diff;
}
void* memcpy(void* const restrict dst, const void* const restrict src, usize size)
{
auto* destination = (u8*)dst;
auto* source = (u8*)src;
for (u64 i = 0; i < size; i += 1)
{
destination[i] = source[i];
}
return dst;
}
void* memmove(void* const dst, const void* const src, usize n)
{
// Implementation
// https://opensource.apple.com/source/network_cmds/network_cmds-481.20.1/unbound/compat/memmove.c.auto.html
uint8_t* from = (uint8_t*) src;
uint8_t* to = (uint8_t*) dst;
if (from == to || n == 0)
return dst;
if (to > from && to-from < (s64)n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy in reverse, to avoid overwriting from */
u64 i;
for(i=n-1; i>=0; i--)
to[i] = from[i];
return dst;
}
if (from > to && from-to < (int)n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy forwards, to avoid overwriting from */
u64 i;
for(i=0; i<n; i++)
to[i] = from[i];
return dst;
}
memcpy(dst, src, n);
return dst;
}
void* memset(void* dst, int n, usize size)
{
u8 ch = cast_to(u8, s32, n);
auto* destination = (u8*)dst;
for (u64 i = 0; i < size; i += 1)
{
destination[i] = ch;
}
return dst;
}
int memcmp(const void* a, const void* b, usize n)
{
auto *s1 = (u8*)a;
auto *s2 = (u8*)b;
while (n-- > 0)
{
if (*s1++ != *s2++)
return s1[-1] < s2[-1] ? -1 : 1;
}
return 0;
}
usize strlen(const char* c_string)
{
auto* it = c_string;
while (*it)
{
it += 1;
}
return (u64)(it - c_string);
}
#endif

479
bootstrap/std/base.h Normal file
View File

@ -0,0 +1,479 @@
#pragma once
#define LINK_LIBC 1
#ifdef NDEBUG
#define BB_DEBUG 0
#else
#define BB_DEBUG 1
#endif
#define BB_INCLUDE_INTRINSIC 0
#if BB_DEBUG == 0
#undef BB_INCLUDE_INTRINSIC
#define BB_INCLUDE_INTRINSIC 1
#endif
#if BB_INCLUDE_INTRINSIC
#if defined(__x86_64__)
#include <immintrin.h>
#endif
#endif
#include <stdint.h>
#include <stddef.h>
#define BB_SAFETY BB_DEBUG
#define STRUCT_FORWARD_DECL(S) typedef struct S S
#define STRUCT(S) STRUCT_FORWARD_DECL(S); struct S
#define UNION_FORWARD_DECL(U) typedef union U U
#define UNION(U) UNION_FORWARD_DECL(U); union U
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define CLAMP(a, x, b) (((a)>(x))?(a):((b)<(x))?(b):(x))
#if _MSC_VER
#define ENUM_START(EnumName, T) typedef T EnumName; typedef enum EnumName ## Flags
#define ENUM_END(EnumName) EnumName ## Flags
#else
#define ENUM_START(EnumName, T) typedef enum EnumName : T
#define ENUM_END(EnumName) EnumName
#endif
#define ENUM(EnumName, T, ...) \
ENUM_START(EnumName, T)\
{\
__VA_ARGS__\
} ENUM_END(EnumName)
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#if defined (__TINYC__) || defined(_MSC_VER)
UNION(u128)
{
struct
{
u64 low;
u64 high;
};
u64 v[2];
};
#else
typedef __uint128_t u128;
#endif
typedef unsigned int uint;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
#if !defined(__TINYC__) && !defined(_MSC_VER)
typedef __int128_t s128;
#endif
typedef size_t usize;
#if !defined(__TINYC__) && !defined(_MSC_VER)
typedef _Float16 f16;
#endif
typedef float f32;
typedef double f64;
typedef u32 Hash32;
typedef u64 Hash64;
#if BB_DEBUG
#define assert(x) if (unlikely(!(x))) { my_panic("Assert failed: \"" # x "\" at {cstr}:{u32}\n", __FILE__, __LINE__); }
#else
#define assert(x) unused(likely(x))
#endif
#ifndef __cplusplus
#if _MSC_VER
#define unreachable_raw() __assume(0)
#else
#define unreachable_raw() __builtin_unreachable()
#endif
// Undefine unreachable if needed to provide a more safe-guard implementation
#ifdef unreachable
#undef unreachable
#endif
#if BB_DEBUG
#define unreachable() my_panic("Unreachable triggered\n", __FILE__, __LINE__)
#else
#define unreachable() unreachable_raw()
#endif
#ifdef __TINYC__
#define fix_unreachable() unreachable_raw()
#else
#define fix_unreachable()
#endif
#define static_assert(x) _Static_assert((x), "Static assert failed!")
#define alignof(x) _Alignof(x)
#else
#define restrict __restrict
#endif
#ifndef BB_INFINITY
#define BB_INFINITY __builtin_inff()
#endif
#ifndef BB_NAN
#define BB_NAN __builtin_nanf("")
#endif
#define fn static
#define method __attribute__((visibility("internal")))
#define global_variable static
#define forceinline __attribute__((always_inline))
#if _MSC_VER
#define expect(x, b) (!!(x))
#else
#define expect(x, b) __builtin_expect(!!(x), b)
#endif
#define likely(x) expect(x, 1)
#define unlikely(x) expect(x, 0)
#define breakpoint() __builtin_debugtrap()
#define failed_execution() my_panic("Failed execution at {cstr}:{u32}\n", __FILE__, __LINE__)
#define todo() my_panic("TODO at {cstr}:{u32}\n", __FILE__, __LINE__)
fn void print(const char* format, ...);
fn u8 os_is_being_debugged();
fn void os_exit(u32 exit_code);
#define my_panic(...) do \
{\
print(__VA_ARGS__);\
if (os_is_being_debugged())\
{\
trap();\
fix_unreachable();\
}\
else\
{\
os_exit(1);\
fix_unreachable();\
}\
} while (0)
#if _MSC_VER
#define trap() __fastfail(1)
#elif __has_builtin(__builtin_trap)
#define trap() __builtin_trap()
#else
fn void trap()
{
asm volatile("ud2");
}
#endif
#define let_pointer_cast(PointerChildType, var_name, value) PointerChildType* var_name = (PointerChildType*)(value)
#if defined(__TINYC__) || defined(_MSC_VER)
#define let(name, value) typeof(value) name = (value)
#else
#define let(name, value) __auto_type name = (value)
#endif
#define let_cast_unchecked(name, T, value) T name = (T)(value)
#define let_cast(T, name, value) T name = cast_to(T, value)
#define let_va_arg(T, name, args) T name = va_arg(args, T)
#define assign_cast(to, from) to = cast_to(typeof(to), from)
#define transmute(D, source) *(D*)&source
UNION(SafeInteger)
{
s64 signed_value;
u64 unsigned_value;
};
fn SafeInteger safe_integer_cast(SafeInteger value, u64 to_size, u64 to_signedness, u64 from_size, u64 from_signedness)
{
SafeInteger result;
let(shifter, to_size * 8 - to_signedness);
let(to_max, (u64)(1 << shifter) - 1);
// A fix for 64-bit wrapping
to_max = to_max == 0 ? UINT64_MAX : to_max;
let(to_signed_min, -((s64)1 << shifter));
if (from_signedness == to_signedness)
{
if (to_size < from_size)
{
switch (to_signedness)
{
case 0:
{
if (value.unsigned_value > to_max)
{
todo();
}
} break;
case 1:
{
if (value.signed_value < to_signed_min)
{
todo();
}
if (value.signed_value > (s64)to_max)
{
todo();
}
}
}
}
}
else
{
if (from_signedness)
{
if (value.signed_value < 0)
{
todo();
}
else if (value.unsigned_value > to_max)
{
todo();
}
}
else
{
if (value.unsigned_value > to_max)
{
todo();
}
}
}
result = value;
return result;
}
#define type_is_signed(T) ((T)(-1) < 0)
#if BB_SAFETY
#define safe_integer_cast_function(To, value) (To) ((value) < 0 ? (safe_integer_cast((SafeInteger) { .signed_value = (value) }, sizeof(To), type_is_signed(To), sizeof(typeof(value)), type_is_signed(typeof(value)))).signed_value : (safe_integer_cast((SafeInteger) { .signed_value = (value) }, sizeof(To), type_is_signed(To), sizeof(typeof(value)), type_is_signed(typeof(value)))).unsigned_value)
#endif
#if BB_SAFETY
#define cast_to(T, value) safe_integer_cast_function(T, value)
#else
#define cast_to(T, value) (T)(value)
#endif
typedef enum Corner
{
CORNER_00,
CORNER_01,
CORNER_10,
CORNER_11,
CORNER_COUNT,
} Corner;
typedef enum Axis2
{
AXIS2_X,
AXIS2_Y,
AXIS2_COUNT,
} Axis2;
// #ifdef __cplusplus
// #define EXPORT extern "C"
// #else
// #define EXPORT
// #endif
#if defined(__cplusplus) && defined(__linux__)
#define NO_EXCEPT __THROW
#else
#define NO_EXCEPT
#endif
#define Slice(T) Slice_ ## T
#define SliceP(T) SliceP_ ## T
#define declare_slice_ex(T, StructName) STRUCT(StructName) \
{\
T* pointer;\
u64 length;\
}
#define declare_slice(T) declare_slice_ex(T, Slice(T))
#define declare_slice_p(T) declare_slice_ex(T*, SliceP(T))
declare_slice(u8);
declare_slice(u16);
declare_slice(u32);
declare_slice(u64);
declare_slice(s8);
declare_slice(s16);
declare_slice(s32);
declare_slice(s64);
declare_slice_p(char);
declare_slice_p(void);
typedef Slice(u8) String;
declare_slice(String);
#define NamedEnumMemberEnum(e, enum_member) e ## _ ## enum_member
#define NamedEnumMemberString(e, enum_member) strlit(#enum_member)
typedef SliceP(char) CStringSlice;
#ifdef _WIN32
typedef void* FileDescriptor;
#else
typedef int FileDescriptor;
#endif
#define FOR_N(it, start, end) \
for (u32 it = (start), end__ = (end); it < end__; ++it)
#define FOR_REV_N(it, start, end) \
for (u32 it = (end), start__ = (start); (it--) > start__;)
#define FOR_BIT(it, start, bits) \
for (typeof(bits) _bits_ = (bits), it = (start); _bits_; _bits_ >>= 1, ++it) if (_bits_ & 1)
#define FOREACH_SET(it, set) \
FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.pointer[_i])
#ifdef __TINYC__
#define declare_vector_type #error
#else
#ifdef __clang__
#define declare_vector_type(T, count, name) typedef T name __attribute__((ext_vector_type(count)))
#else
#define declare_vector_type(T, count, name) typedef T name __attribute__((vector_size(count)))
#endif
#endif
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
#define KB(n) ((n) * 1024)
#define MB(n) ((n) * 1024 * 1024)
#define GB(n) ((u64)(n) * 1024 * 1024 * 1024)
#define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024)
#define unused(x) (void)(x)
#ifdef __clang__
#define may_be_unused __attribute__((unused))
#else
#define may_be_unused
#endif
#if _MSC_VER
#define BB_NORETURN __declspec(noreturn)
#define BB_COLD __declspec(noinline)
#elif defined(__TINYC__)
#define BB_NORETURN __attribute__((noreturn))
#define BB_COLD __attribute__((cold))
#else
#define BB_NORETURN [[noreturn]]
#define BB_COLD [[gnu::cold]]
#endif
#define TRUNCATE(Destination, source) (Destination)(source)
#define size_until_end(T, field_name) (sizeof(T) - offsetof(T, field_name))
#define SWAP(a, b) \
do {\
static_assert(typeof(a) == typeof(b));\
let(temp, a);\
a = b;\
b = temp;\
} while (0)
#define slice_from_pointer_range(T, start, end) (Slice(T)) { .pointer = start, .length = (u64)(end - start), }
#define strlit_len(s) (sizeof(s) - 1)
#define strlit(s) (String){ .pointer = (u8*)(s), .length = strlit_len(s), }
#define ch_to_str(ch) (String){ .pointer = &ch, .length = 1 }
#define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) }
#define array_to_bytes(arr) { .pointer = (u8*)(arr), .length = sizeof(arr) }
#define pointer_to_bytes(p) (String) { .pointer = (u8*)(p), .length = sizeof(*p) }
#define scalar_to_bytes(s) pointer_to_bytes(&(s))
#define string_to_c(s) ((char*)((s).pointer))
#define cstr(s) ((String) { .pointer = (u8*)(s), .length = strlen((char*)s), } )
#define case_to_name(prefix, e) case prefix ## e: return strlit(#e)
const may_be_unused global_variable u8 brace_open = '{';
const may_be_unused global_variable u8 brace_close = '}';
const may_be_unused global_variable u8 parenthesis_open = '(';
const may_be_unused global_variable u8 parenthesis_close = ')';
const may_be_unused global_variable u8 bracket_open = '[';
const may_be_unused global_variable u8 bracket_close = ']';
#define s_get(s, i) (s).pointer[i]
#define s_get_pointer(s, i) &((s).pointer[i])
#define s_get_slice(T, s, start, end) (Slice(T)){ .pointer = ((s).pointer) + (start), .length = (end) - (start) }
#define s_equal(a, b) ((a).length == (b).length && memcmp((a).pointer, (b).pointer, sizeof(*((a).pointer)) * (a).length) == 0)
fn u64 align_forward(u64 value, u64 alignment);
fn u64 align_backward(u64 value, u64 alignment);
fn u8 log2_alignment(u64 alignment);
fn u8 is_power_of_two(u64 value);
fn u8 first_bit_set_32(u32 value);
fn u64 first_bit_set_64(u64 value);
fn u8 cast_u32_to_u8(u32 source, const char* name, int line);
fn u16 cast_u32_to_u16(u32 source, const char* name, int line);
fn s16 cast_u32_to_s16(u32 source, const char* name, int line);
fn s32 cast_u32_to_s32(u32 source, const char* name, int line);
fn u8 cast_u64_to_u8(u64 source, const char* name, int line);
fn u16 cast_u64_to_u16(u64 source, const char* name, int line);
fn u32 cast_u64_to_u32(u64 source, const char* name, int line);
fn s32 cast_u64_to_s32(u64 source, const char* name, int line);
fn s64 cast_u64_to_s64(u64 source, const char* name, int line);
fn u8 cast_s32_to_u8(s32 source, const char* name, int line);
fn u16 cast_s32_to_u16(s32 source, const char* name, int line);
fn u32 cast_s32_to_u32(s32 source, const char* name, int line);
fn u64 cast_s32_to_u64(s32 source, const char* name, int line);
fn s16 cast_s32_to_s16(s32 source, const char* name, int line);
fn u16 cast_s64_to_u16(s64 source, const char* name, int line);
fn u32 cast_s64_to_u32(s64 source, const char* name, int line);
fn u64 cast_s64_to_u64(s64 source, const char* name, int line);
fn s32 cast_s64_to_s32(s64 source, const char* name, int line);
fn u32 format_decimal(String buffer, u64 decimal);
fn u32 format_hexadecimal(String buffer, u64 hexadecimal);
fn u64 format_float(String buffer, f64 value_double);
fn u64 is_decimal_digit(u8 ch);
fn u32 is_space(u8 ch, u8 next_ch);
fn u8 get_next_ch_safe(String string, u64 index);
fn u64 is_identifier_start(u8 ch);
fn u64 is_identifier_ch(u8 ch);
fn u64 is_alphabetic(u8 ch);
fn u64 parse_decimal(String string);
global_variable const Hash64 fnv_offset = 14695981039346656037ull;
global_variable const u64 fnv_prime = 1099511628211ull;
fn Hash32 hash32_fib_end(Hash32 hash);
fn Hash32 hash64_fib_end(Hash64 hash);
fn Hash64 hash_byte(Hash64 source, u8 ch);
fn Hash64 hash_bytes(String bytes);
fn Hash32 hash64_to_hash32(Hash64 hash64);
fn u64 round_up_to_next_power_of_2(u64 n);
STRUCT(TextureIndex)
{
u32 value;
};
fn u64 safe_flag(u64 value, u64 flag)
{
u64 result = value & ((u64)0 - flag);
return result;
}
#define member_from_offset(pointer, type, memory_offset) (*(type*)((u8*)pointer + memory_offset))
#if _MSC_VER
#define offset_of(T, member) offsetof(T, member)
#else
#define offset_of(T, member) __builtin_offsetof(T, member)
#endif

View File

@ -0,0 +1,65 @@
#pragma once
@implementation AppleApplicationDelegate
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
}
@end
@implementation AppleWindow
@end
global_variable WindowConnection window_connection;
fn u8 windowing_initialize()
{
u8 result = 1;
window_connection.application = [NSApplication sharedApplication];
AppleApplicationDelegate* application_delegate = [[AppleApplicationDelegate alloc] init];
NSApp.delegate = application_delegate;
return result;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate)
{
NSRect rect = { { 0, 0 }, { 800, 600 } };
AppleWindow* window = [[AppleWindow alloc] initWithContentRect:rect styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:NO];
window.title = @"Hello Metal";
[window_connection.application activate];
[window orderFrontRegardless];
return window;
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
WindowingSize size;
@autoreleasepool {
const NSRect contentRect = instance.contentView.frame;
const NSRect fbRect = [instance.contentView convertRectToBacking:contentRect];
size = (WindowingSize) {
.width = fbRect.size.width,
.height = fbRect.size.height,
};
} // autoreleasepool
return size;
}
fn void windowing_poll_events()
{
@autoreleasepool {
while (1)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event == nil)
{
break;
}
[NSApp sendEvent:event];
}
} // autoreleasepool
}

View File

@ -0,0 +1,18 @@
#pragma once
#import <Cocoa/Cocoa.h>
#import <QuartzCore/CAMetalLayer.h>
@interface AppleApplicationDelegate : NSObject<NSApplicationDelegate>
@end
@interface AppleWindow : NSWindow
@end
@interface AppleWindowDelegate : NSObject<NSWindowDelegate>
@end
typedef AppleWindow WindowingInstance;
STRUCT(WindowConnection)
{
NSApplication* application;
};

View File

@ -0,0 +1 @@
#pragma once

View File

@ -0,0 +1 @@
#pragma once

View File

@ -2,7 +2,7 @@
#include <std/os.h> #include <std/os.h>
#if LINK_LIBC == 0 #if LINK_LIBC == 0
[[gnu::naked]] [[noreturn]] void _start() [[gnu::naked]] BB_NORETURN void _start()
{ {
__asm__ __volatile__( __asm__ __volatile__(
"\nxor %ebp, %ebp" "\nxor %ebp, %ebp"

View File

@ -3,7 +3,7 @@
void entry_point(int argc, char* argv[], char* envp[]); void entry_point(int argc, char* argv[], char* envp[]);
#if LINK_LIBC == 0 #if LINK_LIBC == 0
[[gnu::naked]] [[noreturn]] void _start(); [[gnu::naked]] BB_NORETURN void _start();
#endif #endif
#if LINK_LIBC == 0 #if LINK_LIBC == 0

View File

@ -1 +0,0 @@

View File

@ -1,15 +1,56 @@
#pragma once
#include <std/font_provider.h> #include <std/font_provider.h>
#define STBTT_STATIC #define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#define stbtt_uint8 u8
#define stbtt_uint16 u16
#define stbtt_uint32 u32
#define stbtt_int8 s8
#define stbtt_int16 s16
#define stbtt_int32 s32
extern float sqrtf(float x);
extern float roundf(float x);
extern float floorf(float x);
extern double sqrt(double x);
extern double fabs(double x);
extern double floor(double x);
extern double ceil(double x);
extern double fmod(double x, double y);
extern double pow(double x, double y);
extern double acos(double x);
extern double cos(double x);
#define STBTT_ifloor(x) ((int) floor(x))
#define STBTT_iceil(x) ((int) ceil(x))
#define STBTT_sqrt(x) sqrt(x)
#define STBTT_pow(x,y) pow(x,y)
#define STBTT_fmod(x,y) fmod(x,y)
#define STBTT_cos(x) cos(x)
#define STBTT_acos(x) acos(x)
#define STBTT_fabs(x) fabs(x)
#define STBTT_malloc(x,u) ((void)(u),malloc(x))
#define STBTT_free(x,u) ((void)(u),free(x))
#define STBTT_assert(x) assert(x)
#define STBTT_strlen(x) strlen(x)
#define STBTT_memcpy memcpy
#define STBTT_memset memset
#ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wunused-function"
#endif
#include <stb_truetype.h> #include <stb_truetype.h>
#ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif
TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create) fn TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create)
{ {
auto font_file = file_read(arena, create.font_path); let(font_file, file_read(arena, create.font_path));
stbtt_fontinfo font_info; stbtt_fontinfo font_info;
if (!stbtt_InitFont(&font_info, font_file.pointer, stbtt_GetFontOffsetForIndex(font_file.pointer, 0))) if (!stbtt_InitFont(&font_info, font_file.pointer, stbtt_GetFontOffsetForIndex(font_file.pointer, 0)))
{ {
@ -23,7 +64,7 @@ TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, Texture
result.height = (u32)sqrtf((f32)(create.text_height * create.text_height * character_count)); result.height = (u32)sqrtf((f32)(create.text_height * create.text_height * character_count));
result.width = result.height; result.width = result.height;
result.pointer = arena_allocate(arena, u32, result.width * result.height); result.pointer = arena_allocate(arena, u32, result.width * result.height);
auto scale_factor = stbtt_ScaleForPixelHeight(&font_info, create.text_height); let(scale_factor, stbtt_ScaleForPixelHeight(&font_info, create.text_height));
int ascent; int ascent;
int descent; int descent;
@ -40,25 +81,25 @@ TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, Texture
u32 first_character = ' '; u32 first_character = ' ';
u32 last_character = '~'; u32 last_character = '~';
for (auto i = first_character; i <= last_character; ++i) for (let(i, first_character); i <= last_character; ++i)
{ {
u32 width; u32 width;
u32 height; u32 height;
int advance; int advance;
int left_bearing; int left_bearing;
auto ch = (u8)i; let(ch, (u8)i);
auto* character = &result.characters[i]; let(character, &result.characters[i]);
stbtt_GetCodepointHMetrics(&font_info, ch, &advance, &left_bearing); stbtt_GetCodepointHMetrics(&font_info, ch, &advance, &left_bearing);
character->advance = (u32)roundf(advance * scale_factor); character->advance = (u32)roundf(advance * scale_factor);
character->left_bearing = (u32)roundf(left_bearing * scale_factor); character->left_bearing = (u32)roundf(left_bearing * scale_factor);
u8* bitmap = stbtt_GetCodepointBitmap(&font_info, 0.0f, scale_factor, ch, (int*)&width, (int*)&height, &character->x_offset, &character->y_offset); u8* bitmap = stbtt_GetCodepointBitmap(&font_info, 0.0f, scale_factor, ch, (int*)&width, (int*)&height, &character->x_offset, &character->y_offset);
auto* kerning_table = result.kerning_tables + i * character_count; let(kerning_table, result.kerning_tables + i * character_count);
for (u32 j = first_character; j <= last_character; j += 1) for (u32 j = first_character; j <= last_character; j += 1)
{ {
auto kerning_advance = stbtt_GetCodepointKernAdvance(&font_info, i, j); let(kerning_advance, stbtt_GetCodepointKernAdvance(&font_info, i, j));
kerning_table[j] = (s32)roundf(kerning_advance * scale_factor); kerning_table[j] = (s32)roundf(kerning_advance * scale_factor);
} }
@ -78,16 +119,16 @@ TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, Texture
character->width = width; character->width = width;
character->height = height; character->height = height;
auto* source = bitmap; let(source, bitmap);
auto* destination = result.pointer; let(destination, result.pointer);
for (u32 bitmap_y = 0; bitmap_y < height; bitmap_y += 1) for (u32 bitmap_y = 0; bitmap_y < height; bitmap_y += 1)
{ {
for (u32 bitmap_x = 0; bitmap_x < width; bitmap_x += 1) for (u32 bitmap_x = 0; bitmap_x < width; bitmap_x += 1)
{ {
auto source_index = bitmap_y * width + bitmap_x; let(source_index, bitmap_y * width + bitmap_x);
auto destination_index = (bitmap_y + y) * result.width + (bitmap_x + x); let(destination_index, (bitmap_y + y) * result.width + (bitmap_x + x));
auto value = source[source_index]; let(value, source[source_index]);
destination[destination_index] = ((u32)value << 24) | 0xffffff; destination[destination_index] = ((u32)value << 24) | 0xffffff;
} }
} }
@ -108,17 +149,17 @@ TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, Texture
return result; return result;
} }
uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas) fn uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas)
{ {
auto height = atlas->ascent - atlas->descent; let(height, atlas->ascent - atlas->descent);
u32 x_offset = 0; u32 x_offset = 0;
u32 y_offset = height; u32 y_offset = height;
for (u64 i = 0; i < string.length; i += 1) for (u64 i = 0; i < string.length; i += 1)
{ {
auto ch = string.pointer[i]; let(ch, string.pointer[i]);
auto* character = &atlas->characters[ch]; let(character, &atlas->characters[ch]);
auto kerning = (atlas->kerning_tables + ch * 256)[string.pointer[i + 1]]; let(kerning, (atlas->kerning_tables + ch * 256)[string.pointer[i + 1]]);
x_offset += character->advance + kerning; x_offset += character->advance + kerning;
} }

View File

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <std/base.h>
#include <std/os.h>
STRUCT(FontCharacter) STRUCT(FontCharacter)
{ {
u32 advance; u32 advance;
@ -34,7 +31,5 @@ STRUCT(TextureAtlasCreate)
u32 text_height; u32 text_height;
}; };
#include <std/render.h> fn TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create);
fn uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas);
EXPORT TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create);
EXPORT uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas);

View File

@ -1,6 +1,6 @@
#include <std/format.h> #include <std/format.h>
u32 format_hexadecimal(String buffer, u64 hexadecimal) fn u32 format_hexadecimal(String buffer, u64 hexadecimal)
{ {
u64 value = hexadecimal; u64 value = hexadecimal;
if (value) if (value)
@ -35,7 +35,7 @@ u32 format_hexadecimal(String buffer, u64 hexadecimal)
} }
} }
u32 format_decimal(String buffer, u64 decimal) fn u32 format_decimal(String buffer, u64 decimal)
{ {
u64 value = decimal; u64 value = decimal;
if (value) if (value)
@ -299,9 +299,10 @@ global_variable const u64 DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] =
// Best case: use 128-bit type. // Best case: use 128-bit type.
fn u64 mul_shift_64(const u64 m, const u64* const mul, const s32 j) fn u64 mul_shift_64(const u64 m, const u64* const mul, const s32 j)
{ {
const u128 b0 = ((u128) m) * mul[0]; const u128 b0 = u128_u64_mul(u128_from_u64(m), mul[0]);
const u128 b2 = ((u128) m) * mul[1]; const u128 b2 = u128_u64_mul(u128_from_u64(m), mul[1]);
return (u64) (((b0 >> 64) + b2) >> (j - 64)); return u64_from_u128(u128_shift_right(u128_u64_add(b2, u128_shift_right_by_64(b0)), j - 64));
// return (u64) (((b0 >> 64) + b2) >> (j - 64));
} }
fn u64 mul_shift_all_64(const u64 m, const u64* const mul, const s32 j, u64* const vp, u64* const vm, const u32 mmShift) fn u64 mul_shift_all_64(const u64 m, const u64* const mul, const s32 j, u64* const vp, u64* const vm, const u32 mmShift)
@ -526,27 +527,27 @@ fn u8 multiple_of_power_of_2(const u64 value, const u32 p) {
return (value & ((1ull << p) - 1)) == 0; return (value & ((1ull << p) - 1)) == 0;
} }
static inline uint64_t div5(const uint64_t x) { fn inline uint64_t div5(const uint64_t x) {
return x / 5; return x / 5;
} }
static inline uint64_t div10(const uint64_t x) { fn inline uint64_t div10(const uint64_t x) {
return x / 10; return x / 10;
} }
static inline uint64_t div100(const uint64_t x) { fn inline uint64_t div100(const uint64_t x) {
return x / 100; return x / 100;
} }
static inline uint64_t div1e8(const uint64_t x) { fn inline uint64_t div1e8(const uint64_t x) {
return x / 100000000; return x / 100000000;
} }
static inline uint64_t div1e9(const uint64_t x) { fn inline uint64_t div1e9(const uint64_t x) {
return x / 1000000000; return x / 1000000000;
} }
static inline uint32_t mod1e9(const uint64_t x) { fn inline uint32_t mod1e9(const uint64_t x) {
return (uint32_t) (x - 1000000000 * div1e9(x)); return (uint32_t) (x - 1000000000 * div1e9(x));
} }
@ -572,7 +573,7 @@ may_be_unused fn Double double_transform(u64 ieee_mantissa, u32 ieee_exponent)
} }
u8 is_even = (m2 & 1) == 0; u8 is_even = (m2 & 1) == 0;
auto accept_bounds = is_even; let(accept_bounds, is_even);
u64 mv = 4 * m2; u64 mv = 4 * m2;
u32 mm_shift = (ieee_mantissa != 0) | (ieee_exponent <= 1); u32 mm_shift = (ieee_mantissa != 0) | (ieee_exponent <= 1);
@ -742,8 +743,8 @@ may_be_unused fn Double double_transform(u64 ieee_mantissa, u32 ieee_exponent)
may_be_unused fn SmallIntResult small_int(u64 ieee_mantissa, u32 ieee_exponent) may_be_unused fn SmallIntResult small_int(u64 ieee_mantissa, u32 ieee_exponent)
{ {
SmallIntResult result = {}; SmallIntResult result = {};
auto m2 = ((u64)1 << double_mantissa_bits) | ieee_mantissa; let(m2, ((u64)1 << double_mantissa_bits) | ieee_mantissa);
auto e2 = (s32)ieee_exponent - double_bias - double_mantissa_bits; let(e2, (s32)ieee_exponent - double_bias - double_mantissa_bits);
if (e2 > 0) if (e2 > 0)
{ {
@ -805,7 +806,7 @@ STRUCT(floating_decimal_64)
fn u8* digits2(u64 value) fn u8* digits2(u64 value)
{ {
auto str = strlit("00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"); String str = strlit("00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899");
return str.pointer + (value * 2); return str.pointer + (value * 2);
} }
@ -815,9 +816,9 @@ fn void write_float_decimal(String buffer, u64* value, u64 count)
while (i + 2 < count) while (i + 2 < count)
{ {
auto c = cast_to(u8, u64, *value % 100); let_cast(u8, c, *value % 100);
*value /= 100; *value /= 100;
auto ptr = digits2(c); let(ptr, digits2(c));
buffer.pointer[count - i - 1] = ptr[1]; buffer.pointer[count - i - 1] = ptr[1];
buffer.pointer[count - i - 2] = ptr[0]; buffer.pointer[count - i - 2] = ptr[0];
i += 2; i += 2;
@ -825,7 +826,7 @@ fn void write_float_decimal(String buffer, u64* value, u64 count)
while (i < count) while (i < count)
{ {
auto c = cast_to(u8, u64, *value % 10); let(c, cast_to(u8, *value % 10));
*value /= 10; *value /= 10;
buffer.pointer[count - i - 1] = '0' + c; buffer.pointer[count - i - 1] = '0' + c;
@ -833,22 +834,20 @@ fn void write_float_decimal(String buffer, u64* value, u64 count)
} }
} }
fn u64 format_float(String buffer, f64 value_double)
u64 format_float(String buffer, f64 value_double)
{ {
auto value_int = *(u64*)&value_double; let(value_int, *(u64*)&value_double);
u64 buffer_i = 0; u64 buffer_i = 0;
const u8 ieee_sign = ((value_int >> (double_mantissa_bits + double_exponent_bits)) & 1) != 0; const u8 ieee_sign = ((value_int >> (double_mantissa_bits + double_exponent_bits)) & 1) != 0;
const auto ieee_mantissa = value_int & (((u64)1 << double_mantissa_bits) - 1); let(ieee_mantissa, value_int & (((u64)1 << double_mantissa_bits) - 1));
const auto ieee_exponent = (u32)((value_int >> double_mantissa_bits) & (((u32)1 << double_exponent_bits) - 1)); let(ieee_exponent, (u32)((value_int >> double_mantissa_bits) & (((u32)1 << double_exponent_bits) - 1)));
if (ieee_exponent == (((u32)1 << double_exponent_bits) - 1) || (ieee_exponent == 0 && ieee_mantissa == 0)) if (ieee_exponent == (((u32)1 << double_exponent_bits) - 1) || (ieee_exponent == 0 && ieee_mantissa == 0))
{ {
if (ieee_mantissa) if (ieee_mantissa)
{ {
auto nan = strlit("NaN"); String nan = strlit("NaN");
memcpy(&buffer.pointer[buffer_i], nan.pointer, nan.length); memcpy(&buffer.pointer[buffer_i], nan.pointer, nan.length);
buffer_i += nan.length; buffer_i += nan.length;
} }
@ -862,13 +861,13 @@ u64 format_float(String buffer, f64 value_double)
if (ieee_exponent) if (ieee_exponent)
{ {
auto inf = strlit("Infinity"); String inf = strlit("Infinity");
memcpy(&buffer.pointer[buffer_i], inf.pointer, inf.length); memcpy(&buffer.pointer[buffer_i], inf.pointer, inf.length);
buffer_i += inf.length; buffer_i += inf.length;
} }
else else
{ {
auto e0 = strlit("0E0"); String e0 = strlit("0E0");
memcpy(&buffer.pointer[buffer_i], e0.pointer, e0.length); memcpy(&buffer.pointer[buffer_i], e0.pointer, e0.length);
buffer_i += e0.length; buffer_i += e0.length;
} }
@ -876,7 +875,7 @@ u64 format_float(String buffer, f64 value_double)
} }
else else
{ {
auto small_int_result = small_int(ieee_mantissa, ieee_exponent); let(small_int_result, small_int(ieee_mantissa, ieee_exponent));
Double result; Double result;
if (small_int_result.is_small_int) if (small_int_result.is_small_int)
{ {
@ -934,8 +933,8 @@ u64 format_float(String buffer, f64 value_double)
const uint32_t d0 = (d % 100) << 1; const uint32_t d0 = (d % 100) << 1;
const uint32_t d1 = (d / 100) << 1; const uint32_t d1 = (d / 100) << 1;
auto base_index = buffer_i + olength; let(base_index, buffer_i + olength);
auto base = buffer.pointer + base_index; let(base, buffer.pointer + base_index);
memcpy(base - 1, DIGIT_TABLE + c0, 2); memcpy(base - 1, DIGIT_TABLE + c0, 2);
memcpy(base - 3, DIGIT_TABLE + c1, 2); memcpy(base - 3, DIGIT_TABLE + c1, 2);
memcpy(base - 5, DIGIT_TABLE + d0, 2); memcpy(base - 5, DIGIT_TABLE + d0, 2);
@ -944,7 +943,7 @@ u64 format_float(String buffer, f64 value_double)
i += 8; i += 8;
} }
auto output2 = (u32) output; let(output2, (u32)output);
while (output2 >= 10000) while (output2 >= 10000)
{ {
@ -956,7 +955,7 @@ u64 format_float(String buffer, f64 value_double)
output2 /= 10000; output2 /= 10000;
const u32 c0 = (c % 100) << 1; const u32 c0 = (c % 100) << 1;
const u32 c1 = (c / 100) << 1; const u32 c1 = (c / 100) << 1;
auto base_index = buffer_i + olength - i; let(base_index, buffer_i + olength - i);
memcpy(buffer.pointer + base_index - 1, DIGIT_TABLE + c0, 2); memcpy(buffer.pointer + base_index - 1, DIGIT_TABLE + c0, 2);
memcpy(buffer.pointer + base_index - 3, DIGIT_TABLE + c1, 2); memcpy(buffer.pointer + base_index - 3, DIGIT_TABLE + c1, 2);
@ -1022,7 +1021,7 @@ u64 format_float(String buffer, f64 value_double)
} break; } break;
case FLOAT_FORMAT_DECIMAL: case FLOAT_FORMAT_DECIMAL:
{ {
auto dp_offset = result.exponent + cast_to(s32, u32, olength); let(dp_offset, result.exponent + cast_to(s32, olength));
if (dp_offset <= 0) if (dp_offset <= 0)
{ {
@ -1030,9 +1029,9 @@ u64 format_float(String buffer, f64 value_double)
buffer.pointer[buffer_i + 1] = '.'; buffer.pointer[buffer_i + 1] = '.';
buffer_i += 2; buffer_i += 2;
// auto dp_index = buffer_i; // let(dp_index, buffer_i);
auto dp_poffset = (u32)(-dp_offset); let(dp_poffset, (u32)(-dp_offset));
memset(buffer.pointer + buffer_i, '0', dp_poffset); memset(buffer.pointer + buffer_i, '0', dp_poffset);
buffer_i += dp_poffset; buffer_i += dp_poffset;
write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, olength); write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, olength);
@ -1040,13 +1039,13 @@ u64 format_float(String buffer, f64 value_double)
} }
else else
{ {
auto dp_uoffset = (u64)dp_offset; let(dp_uoffset, (u64)dp_offset);
if (dp_uoffset >= olength) if (dp_uoffset >= olength)
{ {
write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, olength); write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, olength);
buffer_i += olength; buffer_i += olength;
auto length = dp_uoffset - olength; let(length, dp_uoffset - olength);
auto memset_slice = s_get_slice(u8, buffer, buffer_i, buffer_i + length); String memset_slice = s_get_slice(u8, buffer, buffer_i, buffer_i + length);
memset(memset_slice.pointer, 0, length); memset(memset_slice.pointer, 0, length);
buffer_i += length; buffer_i += length;
} }
@ -1054,7 +1053,7 @@ u64 format_float(String buffer, f64 value_double)
{ {
write_float_decimal(s_get_slice(u8, buffer, buffer_i + dp_uoffset + 1, buffer.length), &output, olength - dp_uoffset); write_float_decimal(s_get_slice(u8, buffer, buffer_i + dp_uoffset + 1, buffer.length), &output, olength - dp_uoffset);
buffer.pointer[buffer_i + dp_uoffset] = '.'; buffer.pointer[buffer_i + dp_uoffset] = '.';
// auto dp_index = buffer_i + dp_uoffset + 1; // let(dp_index, buffer_i + dp_uoffset + 1);
write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, dp_uoffset); write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, dp_uoffset);
buffer_i += olength + 1; buffer_i += olength + 1;
} }
@ -1066,7 +1065,7 @@ u64 format_float(String buffer, f64 value_double)
return buffer_i; return buffer_i;
} }
String format_string_va(String buffer, const char* format, va_list args) fn String format_string_va(String buffer, const char* format, va_list args)
{ {
u8* it = (u8*)format; u8* it = (u8*)format;
u64 buffer_i = 0; u64 buffer_i = 0;
@ -1083,7 +1082,7 @@ String format_string_va(String buffer, const char* format, va_list args)
if (*it == brace_open) if (*it == brace_open)
{ {
it += 1; it += 1;
auto next_ch = *it; let(next_ch, *it);
if (next_ch == brace_open) if (next_ch == brace_open)
{ {
@ -1107,7 +1106,7 @@ String format_string_va(String buffer, const char* format, va_list args)
{ {
it += 1; it += 1;
done = 1; done = 1;
auto* cstring = va_arg(args, const u8*); let_va_arg(const u8*, cstring, args);
while (*cstring) while (*cstring)
{ {
buffer.pointer[buffer_i] = *cstring; buffer.pointer[buffer_i] = *cstring;
@ -1119,7 +1118,7 @@ String format_string_va(String buffer, const char* format, va_list args)
} }
else else
{ {
auto character = cast_to(u8, u32, va_arg(args, u32)); let_cast(u8, character, cast_to(u8, va_arg(args, u32)));
buffer.pointer[buffer_i] = character; buffer.pointer[buffer_i] = character;
buffer_i += 1; buffer_i += 1;
done = 1; done = 1;
@ -1172,7 +1171,7 @@ String format_string_va(String buffer, const char* format, va_list args)
u8* bit_count_end = it; u8* bit_count_end = it;
u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end)); u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end));
typedef enum IntegerFormat : u8 typedef enum IntegerFormat
{ {
INTEGER_FORMAT_HEXADECIMAL, INTEGER_FORMAT_HEXADECIMAL,
INTEGER_FORMAT_DECIMAL, INTEGER_FORMAT_DECIMAL,
@ -1221,13 +1220,13 @@ String format_string_va(String buffer, const char* format, va_list args)
unreachable(); unreachable();
} }
auto buffer_slice = s_get_slice(u8, buffer, buffer_i, buffer.length); String buffer_slice = s_get_slice(u8, buffer, buffer_i, buffer.length);
switch (format) switch (format)
{ {
case INTEGER_FORMAT_HEXADECIMAL: case INTEGER_FORMAT_HEXADECIMAL:
{ {
auto written_characters = format_hexadecimal(buffer_slice, original_value); let(written_characters, format_hexadecimal(buffer_slice, original_value));
buffer_i += written_characters; buffer_i += written_characters;
} break; } break;
case INTEGER_FORMAT_DECIMAL: case INTEGER_FORMAT_DECIMAL:
@ -1246,7 +1245,7 @@ String format_string_va(String buffer, const char* format, va_list args)
value = (u64)original_value; value = (u64)original_value;
} }
auto written_characters = format_decimal(buffer_slice, value); let(written_characters, format_decimal(buffer_slice, value));
buffer_i += written_characters; buffer_i += written_characters;
} break; } break;
case INTEGER_FORMAT_OCTAL: case INTEGER_FORMAT_OCTAL:
@ -1280,7 +1279,7 @@ String format_string_va(String buffer, const char* format, va_list args)
u8* bit_count_end = it; u8* bit_count_end = it;
u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end)); u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end));
typedef enum IntegerFormat : u8 typedef enum IntegerFormat
{ {
INTEGER_FORMAT_HEXADECIMAL, INTEGER_FORMAT_HEXADECIMAL,
INTEGER_FORMAT_DECIMAL, INTEGER_FORMAT_DECIMAL,
@ -1329,18 +1328,18 @@ String format_string_va(String buffer, const char* format, va_list args)
unreachable(); unreachable();
} }
auto buffer_slice = s_get_slice(u8, buffer, buffer_i, buffer.length); let(buffer_slice, s_get_slice(u8, buffer, buffer_i, buffer.length));
switch (format) switch (format)
{ {
case INTEGER_FORMAT_HEXADECIMAL: case INTEGER_FORMAT_HEXADECIMAL:
{ {
auto written_characters = format_hexadecimal(buffer_slice, original_value); let(written_characters, format_hexadecimal(buffer_slice, original_value));
buffer_i += written_characters; buffer_i += written_characters;
} break; } break;
case INTEGER_FORMAT_DECIMAL: case INTEGER_FORMAT_DECIMAL:
{ {
auto written_characters = format_decimal(buffer_slice, original_value); let(written_characters, format_decimal(buffer_slice, original_value));
buffer_i += written_characters; buffer_i += written_characters;
} break; } break;
case INTEGER_FORMAT_OCTAL: case INTEGER_FORMAT_OCTAL:
@ -1372,11 +1371,11 @@ String format_string_va(String buffer, const char* format, va_list args)
return (String) { .pointer = buffer.pointer, .length = buffer_i }; return (String) { .pointer = buffer.pointer, .length = buffer_i };
} }
String format_string(String buffer, const char* format, ...) fn String format_string(String buffer, const char* format, ...)
{ {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
auto result = format_string_va(buffer, format, args); let(result, format_string_va(buffer, format, args));
va_end(args); va_end(args);
return result; return result;
} }

6
bootstrap/std/format.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <std/base.h>
fn String format_string(String buffer, const char* format, ...);
fn String format_string_va(String buffer, const char* format, va_list args);

View File

@ -0,0 +1,148 @@
#pragma once
global_variable Renderer renderer_memory;
fn NSString* apple_string(String string)
{
NSString* result = [[NSString alloc] initWithBytes:string.pointer length:string.length encoding:NSUTF8StringEncoding];
return result;
}
fn Renderer* rendering_initialize(Arena* arena)
{
Renderer* renderer = &renderer_memory;
@autoreleasepool {
renderer->device = MTLCreateSystemDefaultDevice();
String shader_source = file_read(arena, strlit("bootstrap/std/shaders/rect.metal"));
NSString* apple_shader_source = apple_string(shader_source);
NSError* error = nil;
id<MTLLibrary> library = [renderer->device newLibraryWithSource: apple_shader_source options:nil error:&error];
if (!library)
{
// Inspect the error
NSLog(@"Error Domain: %@", error.domain);
NSLog(@"Error Code: %ld", (long)error.code);
NSLog(@"Localized Description: %@", error.localizedDescription);
NSDictionary *userInfo = error.userInfo;
if (userInfo) {
NSLog(@"Additional Info: %@", userInfo);
}
// Take action based on the error
if ([error.domain isEqualToString:MTLLibraryErrorDomain]) {
NSLog(@"Metal Library Compilation Error. Check the shader source.");
} else {
NSLog(@"Unexpected error occurred.");
}
}
id<MTLFunction> vertex = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragment = [library newFunctionWithName:@"fragment_main"];
MTLRenderPipelineDescriptor* pipeline_descriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipeline_descriptor.vertexFunction = vertex;
pipeline_descriptor.fragmentFunction = fragment;
pipeline_descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
id<MTLRenderPipelineState> pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_descriptor error:&error];
if (!pipeline_state)
{
// Inspect the error
NSLog(@"Error Domain: %@", error.domain);
NSLog(@"Error Code: %ld", (long)error.code);
NSLog(@"Localized Description: %@", error.localizedDescription);
NSDictionary *userInfo = error.userInfo;
if (userInfo) {
NSLog(@"Additional Info: %@", userInfo);
}
}
id<MTLCommandQueue> command_queue = [renderer->device newCommandQueue];
}
return renderer;
}
global_variable RenderWindow render_window_memory;
fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window)
{
RenderWindow* render_window = &render_window_memory;
CAMetalLayer* layer = [CAMetalLayer layer];
render_window->layer = layer;
layer.device = renderer->device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
layer.framebufferOnly = true;
layer.frame = window.frame;
window.contentView.layer = layer;
window.opaque = true;
window.backgroundColor = nil;
return render_window;
}
fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window)
{
@autoreleasepool {
id<CAMetalDrawable> drawable = [window->layer nextDrawable];
MTLRenderPassDescriptor* render_pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassColorAttachmentDescriptor* color_attachment = render_pass_descriptor.colorAttachments[0];
color_attachment.clearColor = MTLClearColorMake(1, 1, 1, 1);
color_attachment.storeAction = MTLStoreActionStore;
color_attachment.texture = drawable.texture;
id<MTLCommandBuffer> command_buffer = [renderer->command_queue commandBuffer];
id<MTLRenderCommandEncoder> render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor];
[render_command_encoder setRenderPipelineState: renderer->pipeline_state];
[render_command_encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
[render_command_encoder endEncoding];
[command_buffer presentDrawable:drawable];
[command_buffer commit];
}
}
fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
{
// todo();
}
fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory)
{
todo();
}
fn void window_rect_texture_update_begin(RenderWindow* window)
{
todo();
}
fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas)
{
todo();
}
fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index)
{
todo();
}
fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window)
{
todo();
}
fn void window_render_rect(RenderWindow* window, RectDraw draw)
{
// todo();
}
fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset)
{
// todo();
}

View File

@ -0,0 +1,16 @@
#pragma once
#import <Metal/Metal.h>
STRUCT(Renderer)
{
id<MTLDevice> device;
id<MTLCommandQueue> command_queue;
id<MTLRenderPipelineState> pipeline_state;
};
STRUCT(RenderWindow)
{
CAMetalLayer* layer;
};

View File

@ -1,26 +1,10 @@
#include <std/os.h> #pragma once
#include <std/string.h> #include <std/string.h>
#include <std/format.h> #include <std/format.h>
#ifdef _WIN32 #include <std/string.c>
#define WIN32_LEAN_AND_MEAN 1 #include <std/format.c>
#include <Windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/ptrace.h>
#endif
#if LINK_LIBC
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#endif
#if _WIN32 #if _WIN32
global_variable u64 cpu_frequency; global_variable u64 cpu_frequency;
@ -32,31 +16,31 @@ global_variable u64 cpu_frequency;
#endif #endif
#endif #endif
Timestamp os_timestamp() fn Timestamp os_timestamp()
{ {
Timestamp result; Timestamp result;
#if _WIN32 #if _WIN32
LARGE_INTEGER li; LARGE_INTEGER li;
QueryPerformanceCounter(&li); QueryPerformanceCounter(&li);
result.value = li.QuadPart; result.value = u128_from_u64(li.QuadPart);
#else #else
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
result.value = ((u128)ts.tv_sec << 64) | ts.tv_nsec; result.value = u128_u64_or(u128_shift_left(u128_from_u64(ts.tv_sec), 64), ts.tv_nsec);
#endif #endif
return result; return result;
} }
f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit) fn f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit)
{ {
f64 result; f64 result;
#if _WIN32 #if _WIN32
auto start_tick = (s64)start.value; let(start_tick, (s64)u64_from_u128(start.value));
auto end_tick = (s64)end.value; let(end_tick, (s64)u64_from_u128(end.value));
auto seconds = (f64)(end_tick - start_tick) / cpu_frequency; let(seconds, (f64)(end_tick - start_tick) / cpu_frequency);
switch (time_unit) switch (time_unit)
{ {
@ -74,15 +58,16 @@ f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit)
break; break;
} }
#else #else
auto segmented_nanoseconds = (s64)end.value - (s64)start.value; let(segmented_nanoseconds, (s64)u64_from_u128(end.value) - (s64)u64_from_u128(start.value));
auto segmented_seconds = (s64)(end.value >> 64) - (s64)(start.value >> 64); let(segmented_seconds, (s64)u128_shift_right_by_64(end.value) - (s64)u128_shift_right_by_64(start.value));
if (segmented_nanoseconds < 0) if (segmented_nanoseconds < 0)
{ {
segmented_seconds -= 1; segmented_seconds -= 1;
segmented_nanoseconds += 1000000000; segmented_nanoseconds += 1000000000;
} }
auto total_ns = segmented_seconds * 1000000000 + segmented_nanoseconds; let(total_ns, segmented_seconds * 1000000000 + segmented_nanoseconds);
switch (time_unit) switch (time_unit)
{ {
@ -104,10 +89,10 @@ f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit)
return result; return result;
} }
FileDescriptor os_stdout_get() fn FileDescriptor os_stdout_get()
{ {
#if _WIN32 #if _WIN32
auto handle = GetStdHandle(STD_OUTPUT_HANDLE); let(handle, GetStdHandle(STD_OUTPUT_HANDLE));
assert(handle != INVALID_HANDLE_VALUE); assert(handle != INVALID_HANDLE_VALUE);
return handle; return handle;
#else #else
@ -115,11 +100,11 @@ FileDescriptor os_stdout_get()
#endif #endif
} }
String path_dir(String string) fn String path_dir(String string)
{ {
String result = {}; String result = {};
auto index = string_last_ch(string, '/'); let(index, string_last_ch(string, '/'));
if (index != -1) if (index != STRING_NO_MATCH)
{ {
result = s_get_slice(u8, string, 0, index); result = s_get_slice(u8, string, 0, index);
} }
@ -127,34 +112,34 @@ String path_dir(String string)
return result; return result;
} }
String path_base(String string) fn String path_base(String string)
{ {
String result = {}; String result = {};
auto maybe_index = string_last_ch(string, '/'); let(index, string_last_ch(string, '/'));
if (maybe_index != -1) if (index != STRING_NO_MATCH)
{ {
auto index = cast_to(u64, s64, maybe_index);
result = s_get_slice(u8, string, index + 1, string.length); result = s_get_slice(u8, string, index + 1, string.length);
} }
#if _WIN32 #if _WIN32
if (!result.pointer) if (!result.pointer)
{ {
auto maybe_index = string_last_ch(string, '\\'); let(index, string_last_ch(string, '\\'));
auto index = cast_to(u64, s64, maybe_index); if (index != STRING_NO_MATCH)
result = s_get_slice(u8, string, index + 1, string.length); {
result = s_get_slice(u8, string, index + 1, string.length);
}
} }
#endif #endif
return result; return result;
} }
String path_no_extension(String string) fn String path_no_extension(String string)
{ {
String result = {}; String result = {};
auto maybe_index = string_last_ch(string, '.'); let(index, string_last_ch(string, '.'));
if (maybe_index != -1) if (index != STRING_NO_MATCH)
{ {
auto index = cast_to(u64, s64, maybe_index);
result = s_get_slice(u8, string, 0, index); result = s_get_slice(u8, string, 0, index);
} }
@ -603,7 +588,7 @@ may_be_unused fn void* posix_mmap(void* address, size_t length, int protection_f
return mmap(address, length, protection_flags, map_flags, fd, offset); return mmap(address, length, protection_flags, map_flags, fd, offset);
#else #else
#ifdef __linux__ #ifdef __linux__
return (void*) syscall6(syscall_x86_64_mmap, (s64)address, cast_to(s64, u64, length), protection_flags, map_flags, fd, offset); return (void*) syscall6(syscall_x86_64_mmap, (s64)address, cast_to(s64, length), protection_flags, map_flags, fd, offset);
#else #else
#error "Unsupported operating system for static linking" #error "Unsupported operating system for static linking"
#endif #endif
@ -616,7 +601,7 @@ may_be_unused fn int syscall_mprotect(void *address, size_t length, int protecti
return mprotect(address, length, protection_flags); return mprotect(address, length, protection_flags);
#else #else
#ifdef __linux__ #ifdef __linux__
return cast_to(s32, s64, syscall3(syscall_x86_64_mprotect, (s64)address, cast_to(s64, u64, length), protection_flags)); return cast_to(s32, syscall3(syscall_x86_64_mprotect, (s64)address, cast_to(s64, length), protection_flags));
#else #else
return mprotect(address, length, protection_flags); return mprotect(address, length, protection_flags);
#endif #endif
@ -629,7 +614,7 @@ may_be_unused fn int syscall_open(const char *file_path, int flags, int mode)
return open(file_path, flags, mode); return open(file_path, flags, mode);
#else #else
#ifdef __linux__ #ifdef __linux__
return cast_to(s32, s64, syscall3(syscall_x86_64_open, (s64)file_path, flags, mode)); return cast_to(s32, syscall3(syscall_x86_64_open, (s64)file_path, flags, mode));
#else #else
return open(file_path, flags, mode); return open(file_path, flags, mode);
#endif #endif
@ -642,7 +627,7 @@ may_be_unused fn int syscall_close(int fd)
return close(fd); return close(fd);
#else #else
#ifdef __linux__ #ifdef __linux__
return cast_to(s32, s64, syscall1(syscall_x86_64_close, fd)); return cast_to(s32, syscall1(syscall_x86_64_close, fd));
#else #else
return close(fd); return close(fd);
#endif #endif
@ -655,7 +640,7 @@ fn int syscall_fstat(int fd, struct stat *buffer)
return fstat(fd, buffer); return fstat(fd, buffer);
#else #else
#ifdef __linux__ #ifdef __linux__
return cast_to(s32, s64, syscall2(syscall_x86_64_fstat, fd, (s64)buffer)); return cast_to(s32, syscall2(syscall_x86_64_fstat, fd, (s64)buffer));
#else #else
return fstat(fd, buffer); return fstat(fd, buffer);
#endif #endif
@ -694,7 +679,7 @@ may_be_unused fn int syscall_mkdir(String path, u32 mode)
#if LINK_LIBC #if LINK_LIBC
return mkdir((char*)path.pointer, mode); return mkdir((char*)path.pointer, mode);
#else #else
return cast_to(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path.pointer, (s64)mode)); return cast_to(s32, syscall2(syscall_x86_64_mkdir, (s64)path.pointer, (s64)mode));
#endif #endif
} }
@ -704,7 +689,7 @@ may_be_unused fn int syscall_rmdir(String path)
#if LINK_LIBC #if LINK_LIBC
return rmdir((char*)path.pointer); return rmdir((char*)path.pointer);
#else #else
return cast_to(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path.pointer)); return cast_to(s32, syscall1(syscall_x86_64_rmdir, (s64)path.pointer));
#endif #endif
} }
@ -714,7 +699,7 @@ may_be_unused fn int syscall_unlink(String path)
#if LINK_LIBC #if LINK_LIBC
return unlink((char*)path.pointer); return unlink((char*)path.pointer);
#else #else
return cast_to(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path.pointer)); return cast_to(s32, syscall1(syscall_x86_64_unlink, (s64)path.pointer));
#endif #endif
} }
@ -723,7 +708,7 @@ may_be_unused fn pid_t syscall_fork()
#if LINK_LIBC #if LINK_LIBC
return fork(); return fork();
#else #else
return cast_to(s32, s64, syscall0(syscall_x86_64_fork)); return cast_to(s32, syscall0(syscall_x86_64_fork));
#endif #endif
} }
@ -742,7 +727,7 @@ may_be_unused fn pid_t syscall_waitpid(pid_t pid, int* status, int options)
#if LINK_LIBC #if LINK_LIBC
return waitpid(pid, status, options); return waitpid(pid, status, options);
#else #else
return cast_to(s32, s64, syscall4(syscall_x86_64_wait4, pid, (s64)status, options, 0)); return cast_to(s32, syscall4(syscall_x86_64_wait4, pid, (s64)status, options, 0));
#endif #endif
} }
@ -751,11 +736,11 @@ may_be_unused fn int syscall_gettimeofday(struct timeval* tv, struct timezone* t
#if LINK_LIBC #if LINK_LIBC
return gettimeofday(tv, tz); return gettimeofday(tv, tz);
#else #else
return cast_to(s32, s64, syscall2(syscall_x86_64_gettimeofday, (s64)tv, (s64)tz)); return cast_to(s32, syscall2(syscall_x86_64_gettimeofday, (s64)tv, (s64)tz));
#endif #endif
} }
may_be_unused [[noreturn]] [[gnu::cold]] fn void syscall_exit(int status) may_be_unused BB_NORETURN BB_COLD fn void syscall_exit(int status)
{ {
#if LINK_LIBC #if LINK_LIBC
_exit(status); _exit(status);
@ -784,12 +769,12 @@ may_be_unused fn u64 os_timer_get()
#else #else
struct timeval tv; struct timeval tv;
syscall_gettimeofday(&tv, 0); syscall_gettimeofday(&tv, 0);
auto result = os_timer_freq() * cast_to(u64, s64, tv.tv_sec) + cast_to(u64, s64, tv.tv_usec); let(result, os_timer_freq() * cast_to(u64, tv.tv_sec) + cast_to(u64, tv.tv_usec));
return result; return result;
#endif #endif
} }
u8 os_file_descriptor_is_valid(FileDescriptor fd) fn u8 os_file_descriptor_is_valid(FileDescriptor fd)
{ {
#if _WIN32 #if _WIN32
return fd != INVALID_HANDLE_VALUE; return fd != INVALID_HANDLE_VALUE;
@ -798,7 +783,7 @@ u8 os_file_descriptor_is_valid(FileDescriptor fd)
#endif #endif
} }
FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions) fn FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions)
{ {
assert(path.pointer[path.length] == 0); assert(path.pointer[path.length] == 0);
#if _WIN32 #if _WIN32
@ -816,7 +801,7 @@ FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermission
dwFlagsAndAttributes |= flags.directory * FILE_FLAG_BACKUP_SEMANTICS; dwFlagsAndAttributes |= flags.directory * FILE_FLAG_BACKUP_SEMANTICS;
HANDLE hTemplateFile = 0; HANDLE hTemplateFile = 0;
auto handle = CreateFileA(string_to_c(path), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); let(handle, CreateFileA(string_to_c(path), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile));
return handle; return handle;
#else #else
int posix_flags = 0; int posix_flags = 0;
@ -836,12 +821,12 @@ FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermission
{ {
posix_permissions = 0644; posix_permissions = 0644;
} }
auto result = syscall_open((char*)path.pointer, posix_flags, posix_permissions); let(result, syscall_open((char*)path.pointer, posix_flags, posix_permissions));
return result; return result;
#endif #endif
} }
u64 os_file_get_size(FileDescriptor fd) fn u64 os_file_get_size(FileDescriptor fd)
{ {
#if _WIN32 #if _WIN32
LARGE_INTEGER file_size; LARGE_INTEGER file_size;
@ -852,20 +837,20 @@ u64 os_file_get_size(FileDescriptor fd)
struct stat stat_buffer; struct stat stat_buffer;
int stat_result = syscall_fstat(fd, &stat_buffer); int stat_result = syscall_fstat(fd, &stat_buffer);
assert(stat_result == 0); assert(stat_result == 0);
auto size = cast_to(u64, s64, stat_buffer.st_size); let_cast(u64, size, stat_buffer.st_size);
return size; return size;
#endif #endif
} }
void os_file_write(FileDescriptor fd, String content) fn void os_file_write(FileDescriptor fd, String content)
{ {
#if _WIN32 #if _WIN32
DWORD bytes_written = 0; DWORD bytes_written = 0;
BOOL result = WriteFile(fd, content.pointer, cast_to(u32, u64, content.length), &bytes_written, 0); BOOL result = WriteFile(fd, content.pointer, cast_to(u32, content.length), &bytes_written, 0);
assert(result != 0); assert(result != 0);
#else #else
auto result = syscall_write(fd, content.pointer, content.length); let(result, syscall_write(fd, content.pointer, content.length));
assert(cast_to(u64, s64, result) == content.length); assert(cast_to(u64, result) == content.length);
#endif #endif
} }
@ -878,15 +863,15 @@ may_be_unused fn u64 os_file_read(FileDescriptor fd, String buffer, u64 byte_cou
{ {
#if _WIN32 #if _WIN32
DWORD read = 0; DWORD read = 0;
BOOL result = ReadFile(fd, buffer.pointer, cast_to(u32, u64, byte_count), &read, 0); BOOL result = ReadFile(fd, buffer.pointer, cast_to(u32, byte_count), &read, 0);
assert(result != 0); assert(result != 0);
bytes_read = read; bytes_read = read;
#else #else
auto result = syscall_read(fd, buffer.pointer, byte_count); let(result, syscall_read(fd, buffer.pointer, byte_count));
assert(result > 0); assert(result > 0);
if (result > 0) if (result > 0)
{ {
bytes_read = cast_to(u64, s64, result); assign_cast(bytes_read, result);
} }
#endif #endif
} }
@ -894,18 +879,18 @@ may_be_unused fn u64 os_file_read(FileDescriptor fd, String buffer, u64 byte_cou
return bytes_read; return bytes_read;
} }
void os_file_close(FileDescriptor fd) fn void os_file_close(FileDescriptor fd)
{ {
#if _WIN32 #if _WIN32
BOOL result = CloseHandle(fd); BOOL result = CloseHandle(fd);
assert(result != 0); assert(result != 0);
#else #else
auto result = syscall_close(fd); let(result, syscall_close(fd));
assert(result == 0); assert(result == 0);
#endif #endif
} }
void calibrate_cpu_timer() fn void calibrate_cpu_timer()
{ {
#ifndef SILENT #ifndef SILENT
#if _WIN32 #if _WIN32
@ -925,7 +910,7 @@ void calibrate_cpu_timer()
while (os_elapsed < os_wait_time) while (os_elapsed < os_wait_time)
{ {
auto os_end = os_timer_get(); let(os_end, os_timer_get());
os_elapsed = os_end - os_start; os_elapsed = os_end - os_start;
} }
@ -937,7 +922,7 @@ void calibrate_cpu_timer()
#endif #endif
} }
u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map) fn u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map)
{ {
#if _WIN32 #if _WIN32
DWORD map_flags = 0; DWORD map_flags = 0;
@ -956,7 +941,7 @@ u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserv
#endif #endif
} }
void os_commit(void* address, u64 size) fn void os_commit(void* address, u64 size)
{ {
#if _WIN32 #if _WIN32
VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE); VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
@ -966,7 +951,7 @@ void os_commit(void* address, u64 size)
#endif #endif
} }
void os_directory_make(String path) fn void os_directory_make(String path)
{ {
assert(path.pointer[path.length] == 0); assert(path.pointer[path.length] == 0);
#if _WIN32 #if _WIN32
@ -976,7 +961,12 @@ void os_directory_make(String path)
#endif #endif
} }
void print(const char* format, ...) BB_NORETURN BB_COLD fn void os_exit(u32 exit_code)
{
exit(exit_code);
}
fn void print(const char* format, ...)
{ {
#ifndef SILENT #ifndef SILENT
u8 stack_buffer[16*1024]; u8 stack_buffer[16*1024];
@ -993,13 +983,13 @@ void print(const char* format, ...)
static_assert(sizeof(Arena) == 64); static_assert(sizeof(Arena) == 64);
const global_variable u64 minimum_position = sizeof(Arena); const global_variable u64 minimum_position = sizeof(Arena);
Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size) fn Arena* arena_initialize(u64 reserved_size, u64 granularity, u64 initial_size)
{ {
auto protection_flags = (OSReserveProtectionFlags) { OSReserveProtectionFlags protection_flags = {
.read = 1, .read = 1,
.write = 1, .write = 1,
}; };
auto map_flags = (OSReserveMapFlags) { OSReserveMapFlags map_flags = {
.priv = 1, .priv = 1,
.anon = 1, .anon = 1,
.noreserve = 1, .noreserve = 1,
@ -1015,12 +1005,12 @@ Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size)
return arena; return arena;
} }
Arena* arena_init_default(u64 initial_size) fn Arena* arena_initialize_default(u64 initial_size)
{ {
return arena_init(default_size, minimum_granularity, initial_size); return arena_initialize(default_size, minimum_granularity, initial_size);
} }
u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment) fn u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment)
{ {
u64 aligned_offset = align_forward(arena->position, alignment); u64 aligned_offset = align_forward(arena->position, alignment);
u64 aligned_size_after = aligned_offset + size; u64 aligned_size_after = aligned_offset + size;
@ -1034,13 +1024,13 @@ u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment)
arena->os_position = committed_size; arena->os_position = committed_size;
} }
auto* result = (u8*)arena + aligned_offset; let(result, (u8*)arena + aligned_offset);
arena->position = aligned_size_after; arena->position = aligned_size_after;
assert(arena->position <= arena->os_position); assert(arena->position <= arena->os_position);
return result; return result;
} }
String arena_join_string(Arena* arena, Slice(String) pieces) fn String arena_join_string(Arena* arena, Slice(String) pieces)
{ {
u64 size = 0; u64 size = 0;
for (u64 i = 0; i < pieces.length; i += 1) for (u64 i = 0; i < pieces.length; i += 1)
@ -1050,7 +1040,7 @@ String arena_join_string(Arena* arena, Slice(String) pieces)
} }
u8* pointer = arena_allocate_bytes(arena, size + 1, 1); u8* pointer = arena_allocate_bytes(arena, size + 1, 1);
auto* it = pointer; let(it, pointer);
for (u64 i = 0; i < pieces.length; i += 1) for (u64 i = 0; i < pieces.length; i += 1)
{ {
String piece = pieces.pointer[i]; String piece = pieces.pointer[i];
@ -1063,19 +1053,16 @@ String arena_join_string(Arena* arena, Slice(String) pieces)
return (String) { .pointer = pointer, .length = size }; return (String) { .pointer = pointer, .length = size };
} }
fn void arena_reset(Arena* arena)
void arena_reset(Arena* arena)
{ {
arena->position = minimum_position; arena->position = minimum_position;
memset(arena + 1, 0, arena->position - minimum_position); memset(arena + 1, 0, arena->position - minimum_position);
} }
#define transmute(D, source) *(D*)&source fn String file_read(Arena* arena, String path)
String file_read(Arena* arena, String path)
{ {
String result = {}; String result = {};
auto file_descriptor = os_file_open(path, (OSFileOpenFlags) { let(file_descriptor, os_file_open(path, (OSFileOpenFlags) {
.truncate = 0, .truncate = 0,
.executable = 0, .executable = 0,
.write = 0, .write = 0,
@ -1083,11 +1070,11 @@ String file_read(Arena* arena, String path)
.create = 0, .create = 0,
}, (OSFilePermissions) { }, (OSFilePermissions) {
.readable = 1, .readable = 1,
}); }));
if (os_file_descriptor_is_valid(file_descriptor)) if (os_file_descriptor_is_valid(file_descriptor))
{ {
auto file_size = os_file_get_size(file_descriptor); let(file_size, os_file_get_size(file_descriptor));
if (file_size > 0) if (file_size > 0)
{ {
result = (String){ result = (String){
@ -1112,10 +1099,10 @@ String file_read(Arena* arena, String path)
return result; return result;
} }
void file_write(FileWriteOptions options) fn void file_write(FileWriteOptions options)
{ {
print("Writing file \"{s}\"...\n", options.path); print("Writing file \"{s}\"...\n", options.path);
auto fd = os_file_open(options.path, (OSFileOpenFlags) { let(fd, os_file_open(options.path, (OSFileOpenFlags) {
.write = 1, .write = 1,
.truncate = 1, .truncate = 1,
.create = 1, .create = 1,
@ -1124,36 +1111,40 @@ void file_write(FileWriteOptions options)
.readable = 1, .readable = 1,
.writable = 1, .writable = 1,
.executable = options.executable, .executable = options.executable,
}); }));
assert(os_file_descriptor_is_valid(fd)); assert(os_file_descriptor_is_valid(fd));
os_file_write(fd, options.content); os_file_write(fd, options.content);
os_file_close(fd); os_file_close(fd);
} }
void run_command(Arena* arena, CStringSlice arguments, char* envp[]) fn void run_command(Arena* arena, CStringSlice arguments, char* envp[], RunCommandOptions run_options)
{ {
print("Running command:\n");
assert(arguments.length > 0); assert(arguments.length > 0);
assert(arguments.pointer[arguments.length - 1] == 0); assert(arguments.pointer[arguments.length - 1] == 0);
for (u32 i = 0; i < arguments.length - 1; i += 1)
if (run_options.debug)
{ {
char* argument = arguments.pointer[i]; print("Running command:\n");
print("{cstr} ", argument); for (u32 i = 0; i < arguments.length - 1; i += 1)
{
char* argument = arguments.pointer[i];
print("{cstr} ", argument);
}
print("\n");
} }
print("\n");
#if _WIN32 #if _WIN32
auto start_timestamp = os_timestamp(); let(start_timestamp, os_timestamp());
u32 length = 0; u32 length = 0;
for (u32 i = 0; i < arguments.length; i += 1) for (u32 i = 0; i < arguments.length; i += 1)
{ {
auto argument = arguments.pointer[i]; let(argument, arguments.pointer[i]);
if (argument) if (argument)
{ {
auto string_len = strlen(argument); let(string_len, strlen(argument));
length += cast_to(u32, u64, string_len + 1); length += cast_to(u32, string_len + 1);
} }
} }
@ -1161,10 +1152,10 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
u32 byte_i = 0; u32 byte_i = 0;
for (u32 i = 0; i < arguments.length; i += 1) for (u32 i = 0; i < arguments.length; i += 1)
{ {
auto argument = arguments.pointer[i]; let(argument, arguments.pointer[i]);
if (argument) if (argument)
{ {
auto len = strlen(argument); let(len, strlen(argument));
memcpy(&bytes[byte_i], argument, len); memcpy(&bytes[byte_i], argument, len);
byte_i += len; byte_i += len;
bytes[byte_i] = ' '; bytes[byte_i] = ' ';
@ -1172,7 +1163,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
} }
} }
bytes[byte_i - 1] = 0; bytes[byte_i - 1] = 0;
auto end_timestamp = os_timestamp(); let(end_timestamp, os_timestamp());
PROCESS_INFORMATION process_information = {}; PROCESS_INFORMATION process_information = {};
STARTUPINFOA startup_info = {}; STARTUPINFOA startup_info = {};
@ -1181,19 +1172,27 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
auto handle_inheritance = 1; let(handle_inheritance, 1);
auto start = os_timestamp(); let(start, os_timestamp());
if (CreateProcessA(0, bytes, 0, 0, handle_inheritance, 0, 0, 0, &startup_info, &process_information)) if (CreateProcessA(0, bytes, 0, 0, handle_inheritance, 0, 0, 0, &startup_info, &process_information))
{ {
WaitForSingleObject(process_information.hProcess, INFINITE); WaitForSingleObject(process_information.hProcess, INFINITE);
auto end = os_timestamp(); let(end, os_timestamp());
auto ms = os_resolve_timestamps(start, end, TIME_UNIT_MILLISECONDS); let(ms, os_resolve_timestamps(start, end, TIME_UNIT_MILLISECONDS));
if (run_options.debug)
{
print("Process ran in {f64} ms\n", ms);
}
print("Process ran in {f64} ms\n", ms);
DWORD exit_code; DWORD exit_code;
if (GetExitCodeProcess(process_information.hProcess, &exit_code)) if (GetExitCodeProcess(process_information.hProcess, &exit_code))
{ {
print("Process ran with exit code: 0x{u32:x}\n", exit_code); if (run_options.debug)
{
print("Process ran with exit code: 0x{u32:x}\n", exit_code);
}
if (exit_code != 0) if (exit_code != 0)
{ {
failed_execution(); failed_execution();
@ -1209,8 +1208,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
} }
else else
{ {
print("Failure\n"); let(err, GetLastError());
auto err = GetLastError();
LPSTR lpMsgBuf; LPSTR lpMsgBuf;
DWORD bufSize = FormatMessageA( DWORD bufSize = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@ -1221,9 +1219,10 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
0, 0,
NULL NULL
); );
unused(bufSize); print("CreateProcessA call failed: {cstr}\n", lpMsgBuf);
todo(); todo();
} }
unused(start_timestamp); unused(start_timestamp);
unused(end_timestamp); unused(end_timestamp);
unused(envp); unused(envp);
@ -1236,13 +1235,13 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
todo(); todo();
} }
auto start_timestamp = os_timestamp(); let(start_timestamp, os_timestamp());
if (pid == 0) if (pid == 0)
{ {
// close(pipes[0]); // close(pipes[0]);
// fcntl(pipes[1], F_SETFD, FD_CLOEXEC); // fcntl(pipes[1], F_SETFD, FD_CLOEXEC);
auto result = syscall_execve(arguments.pointer[0], arguments.pointer, envp); let(result, syscall_execve(arguments.pointer[0], arguments.pointer, envp));
#if LINK_LIBC #if LINK_LIBC
my_panic("Execve failed! Error: {cstr}\n", strerror(errno)); my_panic("Execve failed! Error: {cstr}\n", strerror(errno));
#else #else
@ -1254,36 +1253,44 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
int status = 0; int status = 0;
int options = 0; int options = 0;
pid_t result = syscall_waitpid(pid, &status, options); pid_t result = syscall_waitpid(pid, &status, options);
auto end_timestamp = os_timestamp(); let(end_timestamp, os_timestamp());
int success = 0; int success = 0;
if (result == pid) if (result == pid)
{ {
print("{cstr} ", arguments.pointer[0]); if (run_options.debug)
{
print("{cstr} ", arguments.pointer[0]);
if (WIFEXITED(status))
{
let(exit_code, WEXITSTATUS(status));
print("exited with code {u32}\n", exit_code);
}
else if (WIFSIGNALED(status))
{
let(signal_code, WTERMSIG(status));
print("was signaled: {u32}\n", signal_code);
}
else if (WIFSTOPPED(status))
{
let(stopped_code, WSTOPSIG(status));
print("was stopped: {u32}\n", stopped_code);
}
else
{
print("terminated unexpectedly with status {u32}\n", status);
}
}
if (WIFEXITED(status)) if (WIFEXITED(status))
{ {
auto exit_code = WEXITSTATUS(status); let(exit_code, WEXITSTATUS(status));
success = exit_code == 0; success = exit_code == 0;
print("exited with code {u32}\n", exit_code);
}
else if (WIFSIGNALED(status))
{
auto signal_code = WTERMSIG(status);
print("was signaled: {u32}\n", signal_code);
}
else if (WIFSTOPPED(status))
{
auto stopped_code = WSTOPSIG(status);
print("was stopped: {u32}\n", stopped_code);
}
else
{
print("terminated unexpectedly with status {u32}\n", status);
} }
} }
else if (result == -1) else if (result == -1)
{ {
auto waitpid_error = errno; let(waitpid_error, errno);
print("Error waiting for process termination: {u32}\n", waitpid_error); print("Error waiting for process termination: {u32}\n", waitpid_error);
trap(); trap();
} }
@ -1294,37 +1301,37 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
if (!success) if (!success)
{ {
print("Program failed to run!\n"); print("Program failed to run successfully!\n");
failed_execution(); failed_execution();
} }
auto ms = os_resolve_timestamps(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS);
auto ticks = if (run_options.debug)
#if LINK_LIBC {
0 let(ms, os_resolve_timestamps(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS));
#else u32 ticks = 0;
cpu_frequency != 0 #if LINK_LIBC == 0
ticks = cpu_frequency != 0;
#endif #endif
; print("Command run successfully in {f64} {cstr}\n", ms, ticks ? "ticks" : "ms");
print("Command run successfully in {f64} {cstr}\n", ms, ticks ? "ticks" : "ms"); }
} }
#endif #endif
} }
u8 os_is_being_debugged() fn u8 os_is_being_debugged()
{ {
u8 result = 0; u8 result = 0;
#if _WIN32 #if _WIN32
result = IsDebuggerPresent(); result = IsDebuggerPresent();
#else #else
auto request =
#ifdef __APPLE__ #ifdef __APPLE__
PT_TRACE_ME; let(request, PT_TRACE_ME);
#else #else
PTRACE_TRACEME; let(request, PTRACE_TRACEME);
#endif #endif
if (ptrace(request, 0, 0, 0) == -1) if (ptrace(request, 0, 0, 0) == -1)
{ {
auto error = errno; let(error, errno);
if (error == EPERM) if (error == EPERM)
{ {
result = 1; result = 1;
@ -1335,7 +1342,7 @@ u8 os_is_being_debugged()
return result; return result;
} }
void print_string(String message) fn void print_string(String message)
{ {
#ifndef SILENT #ifndef SILENT
// TODO: check writes // TODO: check writes
@ -1347,9 +1354,105 @@ void print_string(String message)
#endif #endif
} }
fn String os_get_environment_variable(const char* name)
{
String result = {};
char* env = getenv(name);
if (env)
{
result = cstr(env);
}
return result;
}
fn u64 os_readlink(String path, String buffer)
{
u64 result = 0;
assert(path.pointer[path.length] == 0);
let(sys_result, readlink(string_to_c(path), string_to_c(buffer), buffer.length));
if (sys_result > 0)
{
assign_cast(result, sys_result);
}
return result;
}
fn String os_readlink_allocate(Arena* arena, String path)
{
String result = {};
u8 buffer[4096];
let(bytes, os_readlink(path, (String)array_to_slice(buffer)));
if (bytes > 0)
{
result.pointer = arena_allocate(arena, u8, bytes + 1);
result.length = bytes;
memcpy(result.pointer, buffer, bytes);
result.pointer[bytes] = 0;
}
return result;
}
#ifndef _WIN32
fn String os_realpath(String path, String buffer)
{
String result = {};
assert(path.pointer[path.length] == 0);
char* system_result = realpath(string_to_c(path), string_to_c(buffer));
if (system_result)
{
result = cstr(system_result);
}
return result;
}
#endif
fn void os_free(void* pointer)
{
free(pointer);
}
#if _WIN32 #if _WIN32
HANDLE os_windows_get_module_handle() fn HANDLE os_windows_get_module_handle()
{ {
return GetModuleHandleW(0); return GetModuleHandleW(0);
} }
#endif #endif
// TODO: structure this better
#if _WIN32
fn OSLibrary os_library_load(const char* library_name)
{
OSLibrary library = {};
library.handle = LoadLibraryA(library_name);
return library;
}
fn OSSymbol os_symbol_load(OSLibrary library, const char* symbol_name)
{
OSSymbol symbol = GetProcAddress(library.handle, symbol_name);
return symbol;
}
#else
fn OSLibrary os_library_load(const char* library_name)
{
OSLibrary library = {};
library.handle = dlopen(library_name, RTLD_NOW | RTLD_LOCAL);
return library;
}
fn OSSymbol os_symbol_load(OSLibrary library, const char* symbol_name)
{
OSSymbol symbol = dlsym(library.handle, symbol_name);
return symbol;
}
#endif
fn u8 os_library_is_valid(OSLibrary library)
{
return library.handle != 0;
}

161
bootstrap/std/os.h Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
#else
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/ptrace.h>
#endif
typedef enum TimeUnit
{
TIME_UNIT_NANOSECONDS,
TIME_UNIT_MICROSECONDS,
TIME_UNIT_MILLISECONDS,
TIME_UNIT_SECONDS,
} TimeUnit;
STRUCT(RunCommandOptions)
{
u64 debug:1;
};
STRUCT(Timestamp)
{
u128 value;
};
STRUCT(OSFileOpenFlags)
{
u32 truncate:1;
u32 executable:1;
u32 write:1;
u32 read:1;
u32 create:1;
u32 directory:1;
};
STRUCT(OSFilePermissions)
{
u8 readable:1;
u8 writable:1;
u8 executable:1;
};
STRUCT(OSReserveProtectionFlags)
{
u32 read:1;
u32 write:1;
u32 execute:1;
u32 reserved:29;
};
STRUCT(OSReserveMapFlags)
{
u32 priv:1;
u32 anon:1;
u32 noreserve:1;
u32 reserved:29;
};
STRUCT(Arena)
{
u64 reserved_size;
u64 position;
u64 os_position;
u64 granularity;
u8 reserved[4 * 8];
};
STRUCT(FileWriteOptions)
{
String path;
String content;
u8 executable;
};
#if __APPLE__
#define MY_PAGE_SIZE KB(16)
#else
#define MY_PAGE_SIZE KB(4)
#endif
const global_variable u64 page_size = MY_PAGE_SIZE;
global_variable u64 minimum_granularity = MY_PAGE_SIZE;
// global_variable u64 middle_granularity = MB(2);
global_variable u64 default_size = GB(4);
fn void print(const char* format, ...);
fn void run_command(Arena* arena, CStringSlice arguments, char* envp[], RunCommandOptions options);
fn String file_read(Arena* arena, String path);
fn void file_write(FileWriteOptions options);
fn String path_dir(String string);
fn String path_base(String string);
fn String path_no_extension(String string);
fn Arena* arena_initialize(u64 reserved_size, u64 granularity, u64 initial_size);
fn Arena* arena_initialize_default(u64 initial_size);
fn void arena_clear(Arena* arena);
fn String arena_join_string(Arena* arena, Slice(String) pieces);
fn u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment);
fn void arena_reset(Arena* arena);
#define arena_allocate(arena, T, count) (T*)(arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)))
#define arena_allocate_slice(arena, T, count) (Slice(T)){ .pointer = arena_allocate(arena, T, count), .length = count }
fn u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map);
fn void os_commit(void* address, u64 size);
fn u8 os_file_descriptor_is_valid(FileDescriptor fd);
fn FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions);
fn void os_file_close(FileDescriptor fd);
fn u64 os_file_get_size(FileDescriptor fd);
fn void os_file_write(FileDescriptor fd, String content);
fn FileDescriptor os_stdout_get();
fn void os_directory_make(String path);
BB_NORETURN BB_COLD fn void os_exit(u32 exit_code);
fn void calibrate_cpu_timer();
fn void print_string(String string);
fn Timestamp os_timestamp();
fn f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit);
fn u8 os_is_being_debugged();
#if _WIN32
typedef void* HANDLE;
fn HANDLE os_windows_get_module_handle();
#endif
#if _WIN32
#define EXECUTABLE_EXTENSION ".exe"
#else
#define EXECUTABLE_EXTENSION ""
#endif
STRUCT(OSLibrary)
{
void* handle;
};
typedef void* OSSymbol;
fn OSLibrary os_library_load(const char* library_name);
fn OSSymbol os_symbol_load(OSLibrary library, const char* symbol_name);

60
bootstrap/std/project.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#ifndef BUILD_DIR
#define BUILD_DIR "cache"
#endif
typedef enum RenderingBackend
{
RENDERING_BACKEND_NONE,
RENDERING_BACKEND_METAL,
RENDERING_BACKEND_VULKAN,
RENDERING_BACKEND_DIRECTX12,
RENDERING_BACKEND_COUNT,
} RenderingBackend;
typedef enum WindowingBackend
{
WINDOWING_BACKEND_NONE,
WINDOWING_BACKEND_WIN32,
WINDOWING_BACKEND_X11,
WINDOWING_BACKEND_WAYLAND,
WINDOWING_BACKEND_COCOA,
WINDOWING_BACKEND_COUNT,
} WindowingBackend;
fn u8 rendering_backend_is_valid(RenderingBackend rendering_backend)
{
u8 valid = rendering_backend != RENDERING_BACKEND_COUNT;
if (valid && rendering_backend != RENDERING_BACKEND_NONE)
{
#ifdef __linux__
valid = rendering_backend == RENDERING_BACKEND_VULKAN;
#elif __APPLE__
valid = rendering_backend == RENDERING_BACKEND_METAL || rendering_backend == RENDERING_BACKEND_VULKAN;
#elif _WIN32
valid = rendering_backend == RENDERING_BACKEND_DIRECTX12 || rendering_backend == RENDERING_BACKEND_VULKAN;
#endif
}
return valid;
}
fn u8 windowing_backend_is_valid(WindowingBackend windowing_backend)
{
u8 valid = windowing_backend != WINDOWING_BACKEND_COUNT;
if (valid && windowing_backend != WINDOWING_BACKEND_NONE)
{
#ifdef __linux__
valid = windowing_backend == WINDOWING_BACKEND_WAYLAND || windowing_backend == WINDOWING_BACKEND_X11;
#elif _WIN32
valid = windowing_backend == WINDOWING_BACKEND_WIN32;
#elif __APPLE__
valid = windowing_backend == WINDOWING_BACKEND_COCOA;
#endif
}
return valid;
}

View File

@ -1 +0,0 @@
#include <Metal.hpp>

15
bootstrap/std/rendering.c Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <std/font_provider.c>
#if BB_RENDERING_BACKEND_VULKAN
#include <std/vulkan_rendering.c>
#endif
#if BB_RENDERING_BACKEND_METAL
#include <std/metal_rendering.c>
#endif
#if BB_RENDERING_BACKEND_DIRECTX12
#include <std/directx12_rendering.c>
#endif

334
bootstrap/std/rendering.h Normal file
View File

@ -0,0 +1,334 @@
#pragma once
#if BB_RENDERING_BACKEND_VULKAN
#include <std/vulkan_rendering.h>
#endif
#if BB_RENDERING_BACKEND_METAL
#include <std/metal_rendering.h>
#endif
#if BB_RENDERING_BACKEND_DIRECTX12
#include <std/directx12_rendering.h>
#endif
#ifdef __clang__
#define BB_HAS_NATIVE_FLOAT2 1
#define BB_HAS_NATIVE_FLOAT3 1
#define BB_HAS_NATIVE_FLOAT4 1
#define BB_HAS_NATIVE_UINT2 1
#define BB_HAS_NATIVE_UINT3 1
#define BB_HAS_NATIVE_UINT4 1
#else
#define BB_HAS_NATIVE_FLOAT2 0
#define BB_HAS_NATIVE_FLOAT3 0
#define BB_HAS_NATIVE_FLOAT4 0
#define BB_HAS_NATIVE_UINT2 0
#define BB_HAS_NATIVE_UINT3 0
#define BB_HAS_NATIVE_UINT4 0
#endif
#if BB_HAS_NATIVE_FLOAT2
declare_vector_type(float, 2, float2);
#else
UNION(float2)
{
struct
{
float x, y;
};
float v[2];
};
#endif
#if BB_HAS_NATIVE_FLOAT3
declare_vector_type(float, 3, float3);
#else
UNION(float3)
{
struct
{
float x, y, z;
};
float v[3];
};
#endif
#if BB_HAS_NATIVE_FLOAT4
declare_vector_type(float, 4, float4);
#else
UNION(float4)
{
struct
{
float x, y, z, w;
};
float v[4];
};
#endif
#if BB_HAS_NATIVE_UINT2
declare_vector_type(uint, 2, uint2);
#else
UNION(uint2)
{
struct
{
uint x, y;
};
uint v[2];
};
#endif
#if BB_HAS_NATIVE_UINT3
declare_vector_type(uint, 3, uint3);
#else
UNION(uint3)
{
struct
{
uint x, y, z;
};
uint v[3];
};
#endif
#if BB_HAS_NATIVE_UINT4
declare_vector_type(uint, 4, uint4);
#else
UNION(uint4)
{
struct
{
uint x, y, z, w;
};
uint v[4];
};
#endif
typedef float2 vec2;
typedef float3 vec3;
typedef float4 vec4;
#if BB_HAS_NATIVE_FLOAT2
#define VEC2(_x, y) ((vec2){_x, _y})
#else
#define VEC2(_x, _y) ((vec2){ .x = _x, .y = _y})
#endif
#if BB_HAS_NATIVE_FLOAT3
#define VEC3(_x, _y, _z) ((vec3){_x, _y, _z})
#else
#define VEC3(_x, _y, _z) ((vec3){ .x = _x, .y = _y, .z = _z})
#endif
#if BB_HAS_NATIVE_FLOAT4
#define VEC4(_x, _y, _z, _w) ((vec4){_x, _y, _z, _w})
#else
#define VEC4(_x, _y, _z, _w) ((vec4){ .x = _x, .y = _y, .z = _z, .w = _w})
#endif
fn float2 float2_add(float2 a, float2 b)
{
#if BB_HAS_NATIVE_FLOAT2
return a + b;
#else
float2 result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
#endif
}
fn float2 float2_sub(float2 a, float2 b)
{
#if BB_HAS_NATIVE_FLOAT2
return a - b;
#else
float2 result;
result.x = a.x - b.x;
result.y = a.y - b.y;
return result;
#endif
}
UNION(F32Interval2)
{
struct
{
vec2 min;
vec2 max;
};
struct
{
float2 p0;
float2 p1;
};
struct
{
f32 x0;
f32 y0;
f32 x1;
f32 y1;
};
float2 v[2];
};
static_assert(sizeof(F32Interval2) == 4 * sizeof(f32));
typedef struct Renderer Renderer;
typedef struct RenderWindow RenderWindow;
typedef struct Pipeline Pipeline;
STRUCT(RectDraw)
{
F32Interval2 vertex;
F32Interval2 texture;
vec4 colors[4];
u32 texture_index;
};
#include "../std/shaders/rect.inc"
typedef struct RectVertex RectVertex;
decl_vb(RectVertex);
typedef enum BBPipeline
{
BB_PIPELINE_RECT,
BB_PIPELINE_COUNT,
} BBPipeline;
typedef enum RenderFontType
{
RENDER_FONT_TYPE_MONOSPACE,
RENDER_FONT_TYPE_PROPORTIONAL,
RENDER_FONT_TYPE_COUNT,
} RenderFontType;
typedef enum RectTextureSlot
{
RECT_TEXTURE_SLOT_WHITE,
RECT_TEXTURE_SLOT_MONOSPACE_FONT,
RECT_TEXTURE_SLOT_PROPORTIONAL_FONT,
RECT_TEXTURE_SLOT_COUNT
} RectTextureSlot;
typedef enum TextureFormat
{
TEXTURE_FORMAT_R8_UNORM,
TEXTURE_FORMAT_R8G8B8A8_SRGB,
} TextureFormat;
STRUCT(TextureMemory)
{
void* pointer;
u32 width;
u32 height;
u32 depth;
TextureFormat format;
};
ENUM(ShaderStage, u8,
SHADER_STAGE_VERTEX,
SHADER_STAGE_FRAGMENT,
);
STRUCT(PipelineCreate)
{
Slice(u16) shader_source_indices;
u16 layout_index;
};
declare_slice(PipelineCreate);
STRUCT(PushConstantRange)
{
u16 offset;
u16 size;
ShaderStage stage;
};
declare_slice(PushConstantRange);
ENUM(DescriptorType, u8,
DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER,
DESCRIPTOR_TYPE_COUNT,
);
STRUCT(DescriptorSetLayoutBinding)
{
u8 binding;
DescriptorType type;
ShaderStage stage;
u8 count;
};
declare_slice(DescriptorSetLayoutBinding);
STRUCT(DescriptorSetLayoutCreate)
{
Slice(DescriptorSetLayoutBinding) bindings;
};
declare_slice(DescriptorSetLayoutCreate);
STRUCT(PipelineLayoutCreate)
{
Slice(PushConstantRange) push_constant_ranges;
Slice(DescriptorSetLayoutCreate) descriptor_set_layouts;
};
declare_slice(PipelineLayoutCreate);
STRUCT(GraphicsPipelinesCreate)
{
Slice(String) shader_binaries;
Slice(PipelineLayoutCreate) layouts;
Slice(PipelineCreate) pipelines;
};
STRUCT(PipelineIndex)
{
u32 value;
};
STRUCT(PipelineLayoutIndex)
{
u32 value;
};
STRUCT(DescriptorSetIndex)
{
u32 value;
};
ENUM(BufferType, u8,
BUFFER_TYPE_VERTEX,
BUFFER_TYPE_INDEX,
BUFFER_TYPE_STAGING,
);
STRUCT(HostBufferCopy)
{
String source;
u64 destination_offset;
};
declare_slice(HostBufferCopy);
STRUCT(LocalBufferCopyRegion)
{
u64 source_offset;
u64 destination_offset;
u64 size;
};
declare_slice(LocalBufferCopyRegion);
#include <std/font_provider.h>
fn Renderer* rendering_initialize(Arena* arena);
fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window);
fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window);
fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window);
fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory);
fn void window_rect_texture_update_begin(RenderWindow* window);
fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas);
fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index);
fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window);
fn void window_render_rect(RenderWindow* window, RectDraw draw);
fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset);

View File

@ -0,0 +1,122 @@
#include <metal_stdlib>
using namespace metal;
// Structs to match buffer data
struct RectVertex
{
float2 p0; // Bottom-left corner position
float2 extent; // Rectangle size (width and height)
float2 uv0; // UV coordinates for bottom-left
float corner_radius; // Corner radius
float softness; // Edge softness
float4 colors[4]; // Per-vertex colors
uint texture_index; // Texture index
};
struct RectFragmentShaderInput
{
float4 vertex_position [[position]];
float4 color; // Vertex color
float2 uv; // UV coordinates
float2 position; // Position in world space
float2 center; // Center of the rectangle
float2 half_size; // Half dimensions of the rectangle
float corner_radius; // Corner radius
float softness; // Edge softness
};
// Push constants (Metal uses constant buffers or argument buffers for this)
struct PushConstants
{
device const RectVertex* vertex_buffer;
float width; // Screen width
float height; // Screen height
};
// Vertex shader main function
vertex RectFragmentShaderInput vertex_main(const device PushConstants& push_constants [[buffer(0)]], uint vertex_id [[vertex_id]])
{
// Constants for normalized quad corners
constexpr float2 vertices[] = {
float2(-1.0, -1.0),
float2( 1.0, -1.0),
float2(-1.0, 1.0),
float2( 1.0, 1.0)
};
// Fetch the vertex data from the buffer
const RectVertex v = push_constants.vertex_buffer[vertex_id / 4];
// Rectangle calculations
float2 p0 = v.p0;
float2 p1 = p0 + v.extent;
float2 position_center = (p1 + p0) * 0.5;
float2 half_size = (p1 - p0) * 0.5;
float2 position = vertices[vertex_id % 4] * half_size + position_center;
// Screen-space transformation
float2 ndc_position = float2(2.0 * position.x / push_constants.width - 1.0,
2.0 * position.y / push_constants.height - 1.0);
// Texture UV calculations
float2 uv0 = v.uv0;
float2 uv1 = uv0 + v.extent;
float2 texture_center = (uv1 + uv0) * 0.5;
float2 uv = vertices[vertex_id % 4] * half_size + texture_center;
// Output to fragment shader
RectFragmentShaderInput out_data;
out_data.color = v.colors[vertex_id % 4];
out_data.uv = uv;
out_data.position = position;
out_data.vertex_position = float4(ndc_position.x, ndc_position.y, 0, 1);
out_data.center = position_center;
out_data.half_size = half_size;
out_data.corner_radius = v.corner_radius;
out_data.softness = v.softness;
return out_data;
}
// Uniform buffer for textures
struct FragmentUniforms
{
array<texture2d<float>, 100> textures; // Array of textures
sampler texture_sampler; // Sampler for texture sampling
};
// Calculate the signed distance field (SDF) for a rounded rectangle
float rounded_rect_sdf(float2 position, float2 center, float2 half_size, float radius)
{
float2 r2 = float2(radius, radius);
float2 d2_no_r2 = abs(center - position) - half_size;
float2 d2 = d2_no_r2 + r2;
float negative_euclidean_distance = min(max(d2.x, d2.y), 0.0);
float positive_euclidean_distance = length(max(d2, 0.0));
return negative_euclidean_distance + positive_euclidean_distance - radius;
}
// Fragment shader function
fragment float4 fragment_main(RectFragmentShaderInput inputs [[stage_in]], constant FragmentUniforms &uniforms [[buffer(0)]], uint texture_index)
{
// Texture size
float2 texture_size = float2(uniforms.textures[texture_index].get_width(), uniforms.textures[texture_index].get_height());
float2 uv = float2(inputs.uv.x / texture_size.x, inputs.uv.y / texture_size.y);
// Sample texture
float4 sampled = uniforms.textures[texture_index].sample(uniforms.texture_sampler, uv);
// Compute softness padding
float softness = inputs.softness;
float softness_padding_scalar = max(0.0, softness * 2.0 - 1.0);
float2 softness_padding = float2(softness_padding_scalar, softness_padding_scalar);
// Compute signed distance
float distance = rounded_rect_sdf(inputs.position, inputs.center, inputs.half_size - softness_padding, inputs.corner_radius);
// Compute SDF factor
float sdf_factor = 1.0 - smoothstep(0.0, 2.0 * softness, distance);
// Compute final color
return inputs.color * sampled * sdf_factor;
}

View File

@ -1,8 +1,8 @@
#include <std/string.h> #include <std/string.h>
s32 string_first_ch(String string, u8 ch) u64 string_first_ch(String string, u8 ch)
{ {
s32 result = -1; u64 result = STRING_NO_MATCH;
for (u64 i = 0; i < string.length; i += 1) for (u64 i = 0; i < string.length; i += 1)
{ {
if (string.pointer[i] == ch) if (string.pointer[i] == ch)
@ -15,16 +15,16 @@ s32 string_first_ch(String string, u8 ch)
return result; return result;
} }
s64 string_last_ch(String string, u8 ch) u64 string_last_ch(String string, u8 ch)
{ {
s64 result = -1; u64 result = STRING_NO_MATCH;
u64 i = string.length; u64 i = string.length;
while (i > 0) while (i > 0)
{ {
i -= 1; i -= 1;
if (string.pointer[i] == ch) if (string.pointer[i] == ch)
{ {
result = cast_to(s64, u64, i); result = i;
break; break;
} }
} }
@ -47,8 +47,8 @@ u8 string_starts_with(String string, String start)
u64 i; u64 i;
for (i = 0; i < start.length; i += 1) for (i = 0; i < start.length; i += 1)
{ {
auto start_ch = start.pointer[i]; let(start_ch, start.pointer[i]);
auto string_ch = string.pointer[i]; let(string_ch, string.pointer[i]);
if (unlikely(string_ch != start_ch)) if (unlikely(string_ch != start_ch))
{ {
break; break;
@ -72,8 +72,8 @@ u8 string_ends_with(String string, String end)
u64 offset = string.length - end.length; u64 offset = string.length - end.length;
for (i = 0; i < end.length; i += 1) for (i = 0; i < end.length; i += 1)
{ {
auto start_ch = end.pointer[i]; let(start_ch, end.pointer[i]);
auto string_ch = string.pointer[i + offset]; let(string_ch, string.pointer[i + offset]);
if (unlikely(string_ch != start_ch)) if (unlikely(string_ch != start_ch))
{ {
break; break;
@ -86,9 +86,9 @@ u8 string_ends_with(String string, String end)
return result; return result;
} }
u64 string_first_ocurrence(String string, String substring) u64 string_first_occurrence(String string, String substring)
{ {
s32 result = UINT64_MAX; u64 result = STRING_NO_MATCH;
if (substring.length < string.length) if (substring.length < string.length)
{ {
@ -99,7 +99,7 @@ u64 string_first_ocurrence(String string, String substring)
break; break;
} }
auto s = s_get_slice(u8, string, i, i + substring.length); String s = s_get_slice(u8, string, i, i + substring.length);
if (s_equal(s, substring)) if (s_equal(s, substring))
{ {
result = i; result = i;
@ -122,7 +122,12 @@ u64 string_first_ocurrence(String string, String substring)
return result; return result;
} }
u64 string_last_ocurrence(String string, String substring) fn u64 string_last_occurrence(String string, String substring)
{ {
todo(); todo();
} }
fn u8 string_contains(String string, String substring)
{
return string_first_occurrence(string, substring) != STRING_NO_MATCH;
}

10
bootstrap/std/string.h Normal file
View File

@ -0,0 +1,10 @@
#include <std/base.h>
#define STRING_NO_MATCH UINT64_MAX
fn u64 string_first_ch(String string, u8 ch);
fn u64 string_last_ch(String string, u8 ch);
fn u8 string_starts_with(String string, String start);
fn u8 string_ends_with(String string, String end);
fn u64 string_first_occurrence(String string, String substring);
fn u64 string_last_occurrence(String string, String substring);

View File

@ -1,14 +1,13 @@
#include <std/ui_builder.h> #pragma once
#include <std/render.h>
UI_Signal ui_button(String string) fn UI_Signal ui_button(String string)
{ {
auto* widget = ui_widget_make((UI_WidgetFlags) { let(widget, ui_widget_make((UI_WidgetFlags) {
.draw_text = 1, .draw_text = 1,
.draw_background = 1, .draw_background = 1,
.mouse_clickable = 1, .mouse_clickable = 1,
.keyboard_pressable = 1, .keyboard_pressable = 1,
}, string); }, string));
UI_Signal signal = ui_signal_from_widget(widget); UI_Signal signal = ui_signal_from_widget(widget);
return signal; return signal;

View File

@ -3,4 +3,4 @@
#include <std/base.h> #include <std/base.h>
#include <std/ui_core.h> #include <std/ui_core.h>
EXPORT UI_Signal ui_button(String string); fn UI_Signal ui_button(String string);

View File

@ -13,27 +13,25 @@
// https://www.rfleury.com/p/ui-bonus-1-simple-single-line-text // https://www.rfleury.com/p/ui-bonus-1-simple-single-line-text
// https://www.rfleury.com/p/codebase-walkthrough-multi-window // https://www.rfleury.com/p/codebase-walkthrough-multi-window
#include <std/ui_core.h> #pragma once
#include <std/format.h>
#include <std/string.h>
UI_State* ui_state = 0; global_variable UI_State* ui_state = 0;
fn void ui_autopop(UI_State* state) fn void ui_autopop(UI_State* state)
{ {
auto* restrict stack_end = (u32*)((u8*)&state->stacks + sizeof(state->stacks)); let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
auto* restrict bitset_pointer = (u64*)&state->stack_autopops; let(bitset_pointer, (u64*)&state->stack_autopops);
u64 bitset_index = 0; u64 bitset_index = 0;
for (auto* restrict stack_pointer = (u32*)&state->stacks; stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32)) for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{ {
auto bitset = *bitset_pointer; let(bitset, *bitset_pointer);
auto shift_value = 1 << bitset_index; let(shift_value, 1 << bitset_index);
auto autopop = (bitset & shift_value) != 0; let(autopop, (bitset & shift_value) != 0);
auto mask = ~shift_value; let(mask, ~shift_value);
*bitset_pointer = bitset & mask; *bitset_pointer = bitset & mask;
auto* restrict length_pointer = stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)); let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
auto current_length = *length_pointer; let(current_length, *length_pointer);
assert(!autopop | current_length); assert(!autopop | current_length);
*length_pointer -= autopop; *length_pointer -= autopop;
@ -43,19 +41,19 @@ fn void ui_autopop(UI_State* state)
} }
} }
void ui_state_select(UI_State* state) fn void ui_state_select(UI_State* state)
{ {
ui_state = state; ui_state = state;
} }
UI_State* ui_state_get() fn UI_State* ui_state_get()
{ {
return ui_state; return ui_state;
} }
fn Arena* ui_build_arena() fn Arena* ui_build_arena()
{ {
auto* arena = ui_state->build_arenas[ui_state->build_count % array_length(ui_state->build_arenas)]; let(arena, ui_state->build_arenas[ui_state->build_count % array_length(ui_state->build_arenas)]);
return arena; return arena;
} }
@ -65,9 +63,9 @@ fn UI_Key ui_key_null()
return key; return key;
} }
UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window) fn UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window)
{ {
Arena* arena = arena_init(GB(8), MB(2), MB(2)); Arena* arena = arena_initialize(GB(8), MB(2), MB(2));
UI_State* state = arena_allocate(arena, UI_State, 1); UI_State* state = arena_allocate(arena, UI_State, 1);
state->renderer = renderer; state->renderer = renderer;
state->render_window = window; state->render_window = window;
@ -77,7 +75,7 @@ UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window)
for (u64 i = 0; i < array_length(state->build_arenas); i += 1) for (u64 i = 0; i < array_length(state->build_arenas); i += 1)
{ {
state->build_arenas[i] = arena_init(GB(8), MB(2), MB(2)); state->build_arenas[i] = arena_initialize(GB(8), MB(2), MB(2));
} }
state->stack_nulls = (UI_StateStackNulls){ state->stack_nulls = (UI_StateStackNulls){
@ -87,11 +85,11 @@ UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window)
.pref_height = {}, .pref_height = {},
}; };
auto* stack_end = (u32*)((u8*)&state->stacks + sizeof(state->stacks)); let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
for (auto* stack_pointer = (u32*)&state->stacks; stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32)) for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{ {
auto* length_pointer = stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)); let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
assert(*length_pointer == 0); assert(*length_pointer == 0);
} }
@ -100,18 +98,17 @@ UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window)
fn u64 ui_widget_index_from_key(UI_Key key) fn u64 ui_widget_index_from_key(UI_Key key)
{ {
auto length = ui_state->widget_table.length; let(length, ui_state->widget_table.length);
assert(is_power_of_two(length)); assert(is_power_of_two(length));
return key.value & (length - 1); return key.value & (length - 1);
} }
auto text_end_delimiter = strlit("##");
auto hash_start_delimiter = strlit("###");
fn String ui_text_from_key_string(String string) fn String ui_text_from_key_string(String string)
{ {
String result = string; String result = string;
auto index = string_first_ocurrence(string, text_end_delimiter); String text_end_delimiter = strlit("##");
let(index, string_first_occurrence(string, text_end_delimiter));
if (index < string.length) if (index < string.length)
{ {
result.length = index; result.length = index;
@ -122,7 +119,8 @@ fn String ui_text_from_key_string(String string)
fn String ui_hash_from_key_string(String string) fn String ui_hash_from_key_string(String string)
{ {
String result = string; String result = string;
auto index = string_first_ocurrence(string, hash_start_delimiter); String hash_start_delimiter = strlit("###");
let(index, string_first_occurrence(string, hash_start_delimiter));
if (index < string.length) if (index < string.length)
{ {
result = s_get_slice(u8, string, index, string.length); result = s_get_slice(u8, string, index, string.length);
@ -153,9 +151,9 @@ fn UI_Key ui_key_from_string_format(UI_Key seed, char* format, ...)
u8 buffer[256]; u8 buffer[256];
va_list args; va_list args;
va_start(args, format); va_start(args, format);
auto string = format_string_va((String)array_to_slice(buffer), format, args); let(string, format_string_va((String)array_to_slice(buffer), format, args));
va_end(args); va_end(args);
auto result = ui_key_from_string(seed, string); let(result, ui_key_from_string(seed, string));
return result; return result;
} }
@ -164,13 +162,13 @@ fn u8 ui_key_equal(UI_Key a, UI_Key b)
return a.value == b.value; return a.value == b.value;
} }
UI_Widget* ui_widget_from_key(UI_Key key) fn UI_Widget* ui_widget_from_key(UI_Key key)
{ {
UI_Widget* result = 0; UI_Widget* result = 0;
if (!ui_key_equal(key, ui_key_null())) if (!ui_key_equal(key, ui_key_null()))
{ {
auto index = ui_widget_index_from_key(key); let(index, ui_widget_index_from_key(key));
for (UI_Widget* widget = ui_state->widget_table.pointer[index].first; widget; widget = widget->hash_next) for (UI_Widget* widget = ui_state->widget_table.pointer[index].first; widget; widget = widget->hash_next)
{ {
if (ui_key_equal(widget->key, key)) if (ui_key_equal(widget->key, key))
@ -184,10 +182,10 @@ UI_Widget* ui_widget_from_key(UI_Key key)
return result; return result;
} }
UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key) fn UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
{ {
auto* widget = ui_widget_from_key(key); let(widget, ui_widget_from_key(key));
static auto count = 0; static let(count, 0);
count += 1; count += 1;
if (widget) if (widget)
@ -202,12 +200,12 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
u8 first_frame = 0; u8 first_frame = 0;
if (!widget) if (!widget)
{ {
auto index = ui_widget_index_from_key(key); let(index, ui_widget_index_from_key(key));
first_frame = 1; first_frame = 1;
widget = arena_allocate(ui_state->arena, UI_Widget, 1); widget = arena_allocate(ui_state->arena, UI_Widget, 1);
auto* table_widget_slot = &ui_state->widget_table.pointer[index]; let(table_widget_slot, &ui_state->widget_table.pointer[index]);
if (!table_widget_slot->last) if (!table_widget_slot->last)
{ {
table_widget_slot->first = widget; table_widget_slot->first = widget;
@ -221,7 +219,7 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
} }
} }
auto* parent = ui_top(parent); let(parent, ui_top(parent));
if (parent) if (parent)
{ {
@ -232,7 +230,7 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
} }
else else
{ {
auto* previous_last = parent->last; let(previous_last, parent->last);
previous_last->next = widget; previous_last->next = widget;
widget->previous = previous_last; widget->previous = previous_last;
parent->last = widget; parent->last = widget;
@ -266,15 +264,15 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
return widget; return widget;
} }
UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string) fn UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string)
{ {
// TODO: // TODO:
auto seed = ui_key_null(); let(seed, ui_key_null());
auto hash_string = ui_hash_from_key_string(string); let(hash_string, ui_hash_from_key_string(string));
auto key = ui_key_from_string(seed, hash_string); let(key, ui_key_from_string(seed, hash_string));
auto* widget = ui_widget_make_from_key(flags, key); let(widget, ui_widget_make_from_key(flags, key));
if (flags.draw_text) if (flags.draw_text)
{ {
@ -284,25 +282,25 @@ UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string)
return widget; return widget;
} }
UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...) fn UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...)
{ {
va_list args; va_list args;
u8 buffer[4096]; u8 buffer[4096];
va_start(args, format); va_start(args, format);
auto string = format_string_va((String)array_to_slice(buffer), format, args); let(string, format_string_va((String)array_to_slice(buffer), format, args));
va_end(args); va_end(args);
auto* result = ui_widget_make(flags, string); let(result, ui_widget_make(flags, string));
return result; return result;
} }
UI_Signal ui_signal_from_widget(UI_Widget* widget) fn UI_Signal ui_signal_from_widget(UI_Widget* widget)
{ {
auto rect = widget->rect; let(rect, widget->rect);
auto mouse_position = ui_state->mouse_position; let(mouse_position, ui_state->mouse_position);
UI_Signal signal = { UI_Signal signal = {
.clicked_left = .clicked_left =
(widget->flags.mouse_clickable & (ui_state->mouse_button_events[OS_EVENT_MOUSE_LEFT].action == OS_EVENT_MOUSE_RELEASE)) & (widget->flags.mouse_clickable & (ui_state->mouse_button_events[WINDOWING_EVENT_MOUSE_LEFT].action == WINDOWING_EVENT_MOUSE_RELEASE)) &
((mouse_position.x >= rect.x0) & (mouse_position.x <= rect.x1)) & ((mouse_position.x >= rect.x0) & (mouse_position.x <= rect.x1)) &
((mouse_position.y >= rect.y0) & (mouse_position.y <= rect.y1)), ((mouse_position.y >= rect.y0) & (mouse_position.y <= rect.y1)),
}; };
@ -311,16 +309,16 @@ UI_Signal ui_signal_from_widget(UI_Widget* widget)
fn void ui_stack_reset(UI_State* state) fn void ui_stack_reset(UI_State* state)
{ {
auto* stack_end = (u32*)((u8*)&state->stacks + sizeof(state->stacks)); let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
for (auto* stack_pointer = (u32*)&state->stacks; stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32)) for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{ {
auto* length_pointer = stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)); let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
*length_pointer = 0; *length_pointer = 0;
} }
} }
UI_Size ui_pixels(u32 width, f32 strictness) fn UI_Size ui_pixels(u32 width, f32 strictness)
{ {
return (UI_Size) { return (UI_Size) {
.kind = UI_SIZE_PIXEL_COUNT, .kind = UI_SIZE_PIXEL_COUNT,
@ -329,7 +327,7 @@ UI_Size ui_pixels(u32 width, f32 strictness)
}; };
} }
UI_Size ui_percentage(f32 percentage, f32 strictness) fn UI_Size ui_percentage(f32 percentage, f32 strictness)
{ {
return (UI_Size) { return (UI_Size) {
.kind = UI_SIZE_PERCENTAGE, .kind = UI_SIZE_PERCENTAGE,
@ -338,9 +336,9 @@ UI_Size ui_percentage(f32 percentage, f32 strictness)
}; };
} }
UI_Size ui_em(f32 value, f32 strictness) fn UI_Size ui_em(f32 value, f32 strictness)
{ {
auto font_size = ui_top(font_size); let(font_size, ui_top(font_size));
assert(font_size); assert(font_size);
return (UI_Size) { return (UI_Size) {
.kind = UI_SIZE_PIXEL_COUNT, .kind = UI_SIZE_PIXEL_COUNT,
@ -349,44 +347,44 @@ UI_Size ui_em(f32 value, f32 strictness)
}; };
} }
u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue) fn u8 ui_build_begin(WindowingInstance* window, f64 frame_time, WindowingEventQueue* event_queue)
{ {
ui_state->build_count += 1; ui_state->build_count += 1;
auto* build_arena = ui_build_arena(); let(build_arena, ui_build_arena());
arena_reset(build_arena); arena_reset(build_arena);
ui_state->frame_time = frame_time; ui_state->frame_time = frame_time;
ui_state->os_window = os_window; ui_state->window = window;
ui_stack_reset(ui_state); ui_stack_reset(ui_state);
u8 open = 1; u8 open = 1;
auto mouse_button_count = 0; let(mouse_button_count, 0);
for (u32 generic_event_index = 0; open & (generic_event_index < event_queue->descriptors.length); generic_event_index += 1) for (u32 generic_event_index = 0; open & (generic_event_index < event_queue->descriptors.length); generic_event_index += 1)
{ {
auto event_descriptor = event_queue->descriptors.pointer[generic_event_index]; let(event_descriptor, event_queue->descriptors.pointer[generic_event_index]);
u32 event_index = event_descriptor.index; u32 event_index = event_descriptor.index;
switch (event_descriptor.type) switch (event_descriptor.type)
{ {
case OS_EVENT_TYPE_MOUSE_BUTTON: case WINDOWING_EVENT_TYPE_MOUSE_BUTTON:
{ {
auto button = event_queue->mouse_buttons.pointer[event_index]; let(button, event_queue->mouse_buttons.pointer[event_index]);
auto previous_button_event = ui_state->mouse_button_events[button.button]; let(previous_button_event, ui_state->mouse_button_events[button.button]);
switch (button.event.action) switch (button.event.action)
{ {
case OS_EVENT_MOUSE_RELAX: case WINDOWING_EVENT_MOUSE_RELAX:
unreachable(); unreachable();
case OS_EVENT_MOUSE_RELEASE: case WINDOWING_EVENT_MOUSE_RELEASE:
{ {
assert(previous_button_event.action == OS_EVENT_MOUSE_PRESS); assert(previous_button_event.action == WINDOWING_EVENT_MOUSE_PRESS);
} break; } break;
case OS_EVENT_MOUSE_PRESS: case WINDOWING_EVENT_MOUSE_PRESS:
{ {
// TODO: handle properly // TODO: handle properly
assert(previous_button_event.action == OS_EVENT_MOUSE_RELAX || mouse_button_count); assert(previous_button_event.action == WINDOWING_EVENT_MOUSE_RELAX || mouse_button_count);
} break; } break;
case OS_EVENT_MOUSE_REPEAT: case WINDOWING_EVENT_MOUSE_REPEAT:
{ {
unreachable(); unreachable();
} break; } break;
@ -395,27 +393,27 @@ u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
ui_state->mouse_button_events[button.button] = button.event; ui_state->mouse_button_events[button.button] = button.event;
mouse_button_count += 1; mouse_button_count += 1;
} break; } break;
case OS_EVENT_TYPE_WINDOW_FOCUS: case WINDOWING_EVENT_TYPE_WINDOW_FOCUS:
{ {
} break; } break;
case OS_EVENT_TYPE_CURSOR_POSITION: case WINDOWING_EVENT_TYPE_CURSOR_POSITION:
{ {
auto mouse_position = event_queue->cursor_positions.pointer[event_index]; let(mouse_position, event_queue->cursor_positions.pointer[event_index]);
ui_state->mouse_position = (UI_MousePosition) { ui_state->mouse_position = (UI_MousePosition) {
.x = mouse_position.x, .x = mouse_position.x,
.y = mouse_position.y, .y = mouse_position.y,
}; };
} break; } break;
case OS_EVENT_TYPE_CURSOR_ENTER: case WINDOWING_EVENT_TYPE_CURSOR_ENTER:
{ {
todo(); todo();
} break; } break;
case OS_EVENT_TYPE_WINDOW_POSITION: case WINDOWING_EVENT_TYPE_WINDOW_POSITION:
{ {
// event_queue->window_positions.pointer[event_index]; // event_queue->window_positions.pointer[event_index];
// todo(); // todo();
} break; } break;
case OS_EVENT_TYPE_WINDOW_CLOSE: case WINDOWING_EVENT_TYPE_WINDOW_CLOSE:
{ {
open = 0; open = 0;
} break; } break;
@ -426,7 +424,7 @@ u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
{ {
for (u64 i = 0; i < ui_state->widget_table.length; i += 1) for (u64 i = 0; i < ui_state->widget_table.length; i += 1)
{ {
auto* widget_table_element = &ui_state->widget_table.pointer[i]; let(widget_table_element, &ui_state->widget_table.pointer[i]);
for (UI_Widget* widget = widget_table_element->first, *next = 0; widget; widget = next) for (UI_Widget* widget = widget_table_element->first, *next = 0; widget; widget = next)
{ {
next = widget->hash_next; next = widget->hash_next;
@ -457,19 +455,20 @@ u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
} }
} }
auto framebuffer_size = os_window_framebuffer_size_get(os_window); let(framebuffer_size, windowing_get_instance_framebuffer_size(window));
ui_push_next_only(pref_width, ui_pixels(framebuffer_size.width, 1.0f)); ui_push_next_only(pref_width, ui_pixels(framebuffer_size.width, 1.0f));
ui_push_next_only(pref_height, ui_pixels(framebuffer_size.height, 1.0f)); ui_push_next_only(pref_height, ui_pixels(framebuffer_size.height, 1.0f));
ui_push_next_only(child_layout_axis, AXIS2_Y); ui_push_next_only(child_layout_axis, AXIS2_Y);
auto* root = ui_widget_make_format((UI_WidgetFlags) {}, "window_root_{u64}", os_window); let(root, ui_widget_make_format((UI_WidgetFlags) {}, "window_root_{u64}", window));
assert(!ui_state->stack_autopops.child_layout_axis); assert(!ui_state->stack_autopops.child_layout_axis);
ui_push(parent, root); ui_push(parent, root);
ui_push(font_size, 12); ui_push(font_size, 12);
ui_push(text_color, ((float4) { 0.9, 0.9, 0.02, 1 }));
ui_push(background_color, ((float4) { 0.1, 0.1, 0.1, 1 })); ui_push(text_color, VEC4(0.9, 0.9, 0.02, 1));
ui_push(background_color, VEC4(0.1, 0.1, 0.1, 1));
ui_push(pref_width, ui_percentage(1.0, 0.0)); ui_push(pref_width, ui_percentage(1.0, 0.0));
ui_push(pref_height, ui_percentage(1.0, 0.0)); ui_push(pref_height, ui_percentage(1.0, 0.0));
// ui_push(pref_height, ui_em(1.8, 0.0)); // ui_push(pref_height, ui_em(1.8, 0.0));
@ -482,13 +481,17 @@ fn void ui_compute_independent_sizes(UI_Widget* widget)
{ {
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1) for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{ {
auto pref_size = widget->pref_size[axis]; let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind) switch (pref_size.kind)
{ {
default: break; case UI_SIZE_COUNT: unreachable(); default: break; case UI_SIZE_COUNT: unreachable();
case UI_SIZE_PIXEL_COUNT: case UI_SIZE_PIXEL_COUNT:
{ {
#if BB_HAS_NATIVE_FLOAT2
widget->computed_size[axis] = floorf(widget->pref_size[axis].value); widget->computed_size[axis] = floorf(widget->pref_size[axis].value);
#else
widget->computed_size.v[axis] = floorf(widget->pref_size[axis].value);
#endif
} break; } break;
} }
} }
@ -504,7 +507,7 @@ fn void ui_compute_upward_dependent_sizes(UI_Widget* widget)
// TODO: optimize loop out if possible // TODO: optimize loop out if possible
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1) for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{ {
auto pref_size = widget->pref_size[axis]; let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind) switch (pref_size.kind)
{ {
default: break; case UI_SIZE_COUNT: unreachable(); default: break; case UI_SIZE_COUNT: unreachable();
@ -514,7 +517,11 @@ fn void ui_compute_upward_dependent_sizes(UI_Widget* widget)
{ {
if (ancestor->pref_size[axis].kind != UI_SIZE_BY_CHILDREN) if (ancestor->pref_size[axis].kind != UI_SIZE_BY_CHILDREN)
{ {
#if BB_HAS_NATIVE_FLOAT2
widget->computed_size[axis] = floorf(ancestor->computed_size[axis] * widget->pref_size[axis].value); widget->computed_size[axis] = floorf(ancestor->computed_size[axis] * widget->pref_size[axis].value);
#else
widget->computed_size.v[axis] = floorf(ancestor->computed_size.v[axis] * widget->pref_size[axis].value);
#endif
break; break;
} }
} }
@ -537,7 +544,7 @@ fn void ui_compute_downward_dependent_sizes(UI_Widget* widget)
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1) for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{ {
auto pref_size = widget->pref_size[axis]; let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind) switch (pref_size.kind)
{ {
default: break; case UI_SIZE_COUNT: unreachable(); default: break; case UI_SIZE_COUNT: unreachable();
@ -553,7 +560,11 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
{ {
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1) for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{ {
auto available_space = widget->computed_size[axis]; #if BB_HAS_NATIVE_FLOAT2
let(available_space, widget->computed_size[axis]);
#else
let(available_space, widget->computed_size.v[axis]);
#endif
f32 taken_space = 0; f32 taken_space = 0;
f32 total_fixup_budget = 0; f32 total_fixup_budget = 0;
@ -565,18 +576,30 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
{ {
if (axis == widget->child_layout_axis) if (axis == widget->child_layout_axis)
{ {
#if BB_HAS_NATIVE_FLOAT2
taken_space += child_widget->computed_size[axis]; taken_space += child_widget->computed_size[axis];
#else
taken_space += child_widget->computed_size.v[axis];
#endif
} }
else else
{ {
#if BB_HAS_NATIVE_FLOAT2
taken_space = MAX(taken_space, child_widget->computed_size[axis]); taken_space = MAX(taken_space, child_widget->computed_size[axis]);
#else
taken_space = MAX(taken_space, child_widget->computed_size.v[axis]);
#endif
} }
auto fixup_budget_this_child = child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness); #if BB_HAS_NATIVE_FLOAT2
let(fixup_budget_this_child, child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness));
#else
let(fixup_budget_this_child, child_widget->computed_size.v[axis] * (1 - child_widget->pref_size[axis].strictness));
#endif
total_fixup_budget += fixup_budget_this_child; total_fixup_budget += fixup_budget_this_child;
} }
} }
auto conflict = taken_space - available_space; let(conflict, taken_space - available_space);
if (conflict > 0 && total_fixup_budget > 0) if (conflict > 0 && total_fixup_budget > 0)
{ {
@ -584,7 +607,11 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
{ {
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis))) if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{ {
auto fixup_budget_this_child = child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness); #if BB_HAS_NATIVE_FLOAT2
let(fixup_budget_this_child, child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness));
#else
let(fixup_budget_this_child, child_widget->computed_size.v[axis] * (1 - child_widget->pref_size[axis].strictness));
#endif
f32 fixup_size_this_child = 0; f32 fixup_size_this_child = 0;
if (axis == widget->child_layout_axis) if (axis == widget->child_layout_axis)
@ -593,11 +620,19 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
} }
else else
{ {
#if BB_HAS_NATIVE_FLOAT2
fixup_size_this_child = child_widget->computed_size[axis] - available_space; fixup_size_this_child = child_widget->computed_size[axis] - available_space;
#else
fixup_size_this_child = child_widget->computed_size.v[axis] - available_space;
#endif
} }
fixup_size_this_child = CLAMP(0, fixup_size_this_child, fixup_budget_this_child); fixup_size_this_child = CLAMP(0, fixup_size_this_child, fixup_budget_this_child);
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_size[axis] = floorf(child_widget->computed_size[axis] - fixup_size_this_child); child_widget->computed_size[axis] = floorf(child_widget->computed_size[axis] - fixup_size_this_child);
#else
child_widget->computed_size.v[axis] = floorf(child_widget->computed_size.v[axis] - fixup_size_this_child);
#endif
} }
} }
} }
@ -611,8 +646,13 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
{ {
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis))) if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{ {
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_relative_position[axis] = p; child_widget->computed_relative_position[axis] = p;
p += child_widget->computed_size[axis]; p += child_widget->computed_size[axis];
#else
child_widget->computed_relative_position.v[axis] = p;
p += child_widget->computed_size.v[axis];
#endif
} }
} }
} }
@ -622,22 +662,32 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
{ {
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis))) if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{ {
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_relative_position[axis] = 0; child_widget->computed_relative_position[axis] = 0;
#else
child_widget->computed_relative_position.v[axis] = 0;
#endif
} }
} }
} }
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next) for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{ {
auto last_relative_rect = child_widget->relative_rect; let(last_relative_rect, child_widget->relative_rect);
#if BB_HAS_NATIVE_FLOAT2
child_widget->relative_rect.p0[axis] = child_widget->computed_relative_position[axis]; child_widget->relative_rect.p0[axis] = child_widget->computed_relative_position[axis];
child_widget->relative_rect.p1[axis] = child_widget->relative_rect.p0[axis] + child_widget->computed_size[axis]; child_widget->relative_rect.p1[axis] = child_widget->relative_rect.p0[axis] + child_widget->computed_size[axis];
#else
child_widget->relative_rect.p0.v[axis] = child_widget->computed_relative_position.v[axis];
child_widget->relative_rect.p1.v[axis] = child_widget->relative_rect.p0.v[axis] + child_widget->computed_size.v[axis];
#endif
float2 last_corner_01 = { last_relative_rect.x0, last_relative_rect.y1 }; float2 last_corner_01 = { last_relative_rect.x0, last_relative_rect.y1 };
float2 last_corner_10 = { last_relative_rect.x1, last_relative_rect.y0 }; float2 last_corner_10 = { last_relative_rect.x1, last_relative_rect.y0 };
float2 this_corner_01 = { child_widget->relative_rect.x0, child_widget->relative_rect.y1 }; float2 this_corner_01 = { child_widget->relative_rect.x0, child_widget->relative_rect.y1 };
float2 this_corner_10 = { child_widget->relative_rect.x1, child_widget->relative_rect.y0 }; float2 this_corner_10 = { child_widget->relative_rect.x1, child_widget->relative_rect.y0 };
#if BB_HAS_NATIVE_FLOAT2
child_widget->relative_corner_delta[CORNER_00][axis] = child_widget->relative_rect.p0[axis] - last_relative_rect.p0[axis]; child_widget->relative_corner_delta[CORNER_00][axis] = child_widget->relative_rect.p0[axis] - last_relative_rect.p0[axis];
child_widget->relative_corner_delta[CORNER_01][axis] = this_corner_01[axis] - last_corner_01[axis]; child_widget->relative_corner_delta[CORNER_01][axis] = this_corner_01[axis] - last_corner_01[axis];
child_widget->relative_corner_delta[CORNER_10][axis] = this_corner_10[axis] - last_corner_10[axis]; child_widget->relative_corner_delta[CORNER_10][axis] = this_corner_10[axis] - last_corner_10[axis];
@ -651,6 +701,21 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
child_widget->rect.p0[axis] = floorf(child_widget->rect.p0[axis]); child_widget->rect.p0[axis] = floorf(child_widget->rect.p0[axis]);
child_widget->rect.p1[axis] = floorf(child_widget->rect.p1[axis]); child_widget->rect.p1[axis] = floorf(child_widget->rect.p1[axis]);
} }
#else
child_widget->relative_corner_delta[CORNER_00].v[axis] = child_widget->relative_rect.p0.v[axis] - last_relative_rect.p0.v[axis];
child_widget->relative_corner_delta[CORNER_01].v[axis] = this_corner_01.v[axis] - last_corner_01.v[axis];
child_widget->relative_corner_delta[CORNER_10].v[axis] = this_corner_10.v[axis] - last_corner_10.v[axis];
child_widget->relative_corner_delta[CORNER_11].v[axis] = child_widget->relative_rect.p1.v[axis] - last_relative_rect.p1.v[axis];
child_widget->rect.p0.v[axis] = widget->rect.p0.v[axis] + child_widget->relative_rect.p0.v[axis] - widget->view_offset.v[axis];
child_widget->rect.p1.v[axis] = child_widget->rect.p0.v[axis] + child_widget->computed_size.v[axis];
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
child_widget->rect.p0.v[axis] = floorf(child_widget->rect.p0.v[axis]);
child_widget->rect.p1.v[axis] = floorf(child_widget->rect.p1.v[axis]);
}
#endif
} }
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next) for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
@ -660,15 +725,15 @@ fn void ui_resolve_conflicts(UI_Widget* widget)
} }
} }
void ui_build_end() fn void ui_build_end()
{ {
// Clear release button presses // Clear release button presses
for (u32 i = 0; i < array_length(ui_state->mouse_button_events); i += 1) for (u32 i = 0; i < array_length(ui_state->mouse_button_events); i += 1)
{ {
auto* event = &ui_state->mouse_button_events[i]; let(event, &ui_state->mouse_button_events[i]);
if (event->action == OS_EVENT_MOUSE_RELEASE) if (event->action == WINDOWING_EVENT_MOUSE_RELEASE)
{ {
event->action = OS_EVENT_MOUSE_RELAX; event->action = WINDOWING_EVENT_MOUSE_RELAX;
} }
} }
@ -690,10 +755,10 @@ STRUCT(WidgetIterator)
#define ui_widget_recurse_depth_first_preorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, next), offset_of(UI_Widget, first)) #define ui_widget_recurse_depth_first_preorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, next), offset_of(UI_Widget, first))
#define ui_widget_recurse_depth_first_postorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, previous), offset_of(UI_Widget, last)) #define ui_widget_recurse_depth_first_postorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, previous), offset_of(UI_Widget, last))
WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offset, u64 child_offset) fn WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offset, u64 child_offset)
{ {
WidgetIterator it = {}; WidgetIterator it = {};
auto* child = member_from_offset(widget, UI_Widget*, child_offset); let(child, member_from_offset(widget, UI_Widget*, child_offset));
if (child) if (child)
{ {
it.next = child; it.next = child;
@ -703,7 +768,7 @@ WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offs
{ {
for (UI_Widget* w = widget; w; w = w->parent) for (UI_Widget* w = widget; w; w = w->parent)
{ {
auto* sibling = member_from_offset(w, UI_Widget*, sibling_offset); let(sibling, member_from_offset(w, UI_Widget*, sibling_offset));
if (sibling) if (sibling)
{ {
it.next = sibling; it.next = sibling;
@ -717,7 +782,7 @@ WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offs
return it; return it;
} }
void ui_draw() fn void ui_draw()
{ {
UI_Widget* root = ui_state->root; UI_Widget* root = ui_state->root;
@ -743,4 +808,3 @@ void ui_draw()
widget = ui_widget_recurse_depth_first_postorder(widget).next; widget = ui_widget_recurse_depth_first_postorder(widget).next;
} }
} }

View File

@ -1,17 +1,11 @@
#pragma once #pragma once
#include <std/base.h> ENUM(UI_SizeKind, u8,
#include <std/window.h>
#include <std/os.h>
#include <std/render.h>
typedef enum UI_SizeKind : u8
{
UI_SIZE_PIXEL_COUNT, UI_SIZE_PIXEL_COUNT,
UI_SIZE_PERCENTAGE, UI_SIZE_PERCENTAGE,
UI_SIZE_BY_CHILDREN, UI_SIZE_BY_CHILDREN,
UI_SIZE_COUNT, UI_SIZE_COUNT,
} UI_SizeKind; );
STRUCT(UI_Size) STRUCT(UI_Size)
{ {
@ -33,8 +27,7 @@ STRUCT(UI_MousePosition)
f64 y; f64 y;
}; };
typedef enum UI_WidgetFlagEnum : u64 ENUM(UI_WidgetFlagEnum, u64,
{
UI_WIDGET_FLAG_DISABLED = 1 << 0, UI_WIDGET_FLAG_DISABLED = 1 << 0,
UI_WIDGET_FLAG_MOUSE_CLICKABLE = 1 << 1, UI_WIDGET_FLAG_MOUSE_CLICKABLE = 1 << 1,
UI_WIDGET_FLAG_KEYBOARD_PRESSABLE = 1 << 2, UI_WIDGET_FLAG_KEYBOARD_PRESSABLE = 1 << 2,
@ -44,7 +37,7 @@ typedef enum UI_WidgetFlagEnum : u64
UI_WIDGET_FLAG_OVERFLOW_Y = 1 << 6, UI_WIDGET_FLAG_OVERFLOW_Y = 1 << 6,
UI_WIDGET_FLAG_FLOATING_X = 1 << 7, UI_WIDGET_FLAG_FLOATING_X = 1 << 7,
UI_WIDGET_FLAG_FLOATING_Y = 1 << 8, UI_WIDGET_FLAG_FLOATING_Y = 1 << 8,
} UI_WidgetFlagEnum; );
UNION(UI_WidgetFlags) UNION(UI_WidgetFlags)
{ {
@ -153,7 +146,7 @@ STRUCT(UI_State)
Arena* build_arenas[2]; Arena* build_arenas[2];
Renderer* renderer; Renderer* renderer;
RenderWindow* render_window; RenderWindow* render_window;
OSWindow os_window; WindowingInstance* window;
u64 build_count; u64 build_count;
f64 frame_time; f64 frame_time;
UI_Widget* root; UI_Widget* root;
@ -161,7 +154,7 @@ STRUCT(UI_State)
Slice(UI_WidgetSlot) widget_table; Slice(UI_WidgetSlot) widget_table;
UI_Widget* free_widget_list; UI_Widget* free_widget_list;
u64 free_widget_count; u64 free_widget_count;
OSEventMouseButtonEvent mouse_button_events[OS_EVENT_MOUSE_BUTTON_COUNT]; WindowingEventMouseButtonEvent mouse_button_events[WINDOWING_EVENT_MOUSE_BUTTON_COUNT];
u8 focused:1; u8 focused:1;
UI_StateStacks stacks; UI_StateStacks stacks;
@ -190,8 +183,6 @@ STRUCT(UI_Signal)
}; };
}; };
extern UI_State* ui_state;
#define ui_stack_autopop_set(field_name, value) ui_state->stack_autopops.field_name = (value) #define ui_stack_autopop_set(field_name, value) ui_state->stack_autopops.field_name = (value)
#define ui_stack_push_impl(field_name, value, auto_pop_value) do \ #define ui_stack_push_impl(field_name, value, auto_pop_value) do \
{\ {\
@ -199,34 +190,34 @@ extern UI_State* ui_state;
ui_stack_autopop_set(field_name, auto_pop_value);\ ui_stack_autopop_set(field_name, auto_pop_value);\
} while (0) } while (0)
fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size)
{
auto length = stack->length;
assert(length > 0);
auto next_length = length - 1;
auto index = next_length;
auto* result = &stack->pointer[index * element_size];
stack->length = next_length;
return result;
}
#define ui_push(field_name, value) ui_stack_push_impl(field_name, value, 0) #define ui_push(field_name, value) ui_stack_push_impl(field_name, value, 0)
#define ui_push_next_only(field_name, value) ui_stack_push_impl(field_name, value, 1) #define ui_push_next_only(field_name, value) ui_stack_push_impl(field_name, value, 1)
#define ui_pop(field_name) (typeof(ui_state->stacks.field_name.pointer)) ui_pop_generic((VirtualBuffer(u8)*)&ui_state->stacks.field_name, sizeof(*ui_state->stacks.field_name.pointer)) #define ui_pop(field_name) (typeof(ui_state->stacks.field_name.pointer)) ui_pop_generic((VirtualBuffer(u8)*)&ui_state->stacks.field_name, sizeof(*ui_state->stacks.field_name.pointer))
#define ui_top(field_name) (ui_state->stacks.field_name.length ? ui_state->stacks.field_name.pointer[ui_state->stacks.field_name.length - 1] : ui_state->stack_nulls.field_name) #define ui_top(field_name) (ui_state->stacks.field_name.length ? ui_state->stacks.field_name.pointer[ui_state->stacks.field_name.length - 1] : ui_state->stack_nulls.field_name)
EXPORT UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window); fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size)
EXPORT void ui_state_select(UI_State* state); {
EXPORT u8 ui_build_begin(OSWindow window, f64 frame_time, OSEventQueue* event_queue); let(length, stack->length);
EXPORT void ui_build_end();
EXPORT void ui_draw();
EXPORT UI_Signal ui_signal_from_widget(UI_Widget* widget);
EXPORT UI_State* ui_state_get();
EXPORT UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string); assert(length > 0);
EXPORT UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...); let(next_length, length - 1);
EXPORT UI_Size ui_pixels(u32 width, f32 strictness); let(index, next_length);
EXPORT UI_Size ui_percentage(f32 percentage, f32 strictness); let(result, &stack->pointer[index * element_size]);
EXPORT UI_Size ui_em(f32 value, f32 strictness); stack->length = next_length;
return result;
}
fn UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window);
fn void ui_state_select(UI_State* state);
fn u8 ui_build_begin(WindowingInstance* window, f64 frame_time, WindowingEventQueue* event_queue);
fn void ui_build_end();
fn void ui_draw();
fn UI_Signal ui_signal_from_widget(UI_Widget* widget);
fn UI_State* ui_state_get();
fn UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string);
fn UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...);
fn UI_Size ui_pixels(u32 width, f32 strictness);
fn UI_Size ui_percentage(f32 percentage, f32 strictness);
fn UI_Size ui_em(f32 value, f32 strictness);

View File

@ -1,7 +1,7 @@
#include <std/virtual_buffer.h> #include <std/virtual_buffer.h>
#include <std/os.h> #include <std/os.h>
void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{ {
u32 old_capacity = vb->capacity; u32 old_capacity = vb->capacity;
u32 wanted_capacity = vb->length + item_count; u32 wanted_capacity = vb->length + item_count;
@ -13,20 +13,20 @@ void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_c
vb->pointer = os_reserve(0, item_size * UINT32_MAX, (OSReserveProtectionFlags) {}, (OSReserveMapFlags) { .priv = 1, .anon = 1, .noreserve = 1 }); vb->pointer = os_reserve(0, item_size * UINT32_MAX, (OSReserveProtectionFlags) {}, (OSReserveMapFlags) { .priv = 1, .anon = 1, .noreserve = 1 });
} }
u32 old_page_capacity = cast_to(u32, u64, align_forward(old_capacity * item_size, minimum_granularity)); let_cast(u32, old_page_capacity, align_forward(old_capacity * item_size, minimum_granularity));
u32 new_page_capacity = cast_to(u32, u64, align_forward(wanted_capacity * item_size, minimum_granularity)); let_cast(u32, new_page_capacity, align_forward(wanted_capacity * item_size, minimum_granularity));
u32 commit_size = new_page_capacity - old_page_capacity; let(commit_size, new_page_capacity - old_page_capacity);
void* commit_pointer = vb->pointer + old_page_capacity; void* commit_pointer = vb->pointer + old_page_capacity;
os_commit(commit_pointer, commit_size); os_commit(commit_pointer, commit_size);
u32 new_capacity = new_page_capacity / item_size; let(new_capacity, new_page_capacity / item_size);
vb->capacity = new_capacity; vb->capacity = new_capacity;
} }
} }
u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{ {
u32 index = vb->length; u32 index = vb->length;
assert(vb->capacity >= index + item_count); assert(vb->capacity >= index + item_count);
@ -34,29 +34,29 @@ u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 ite
return vb->pointer + (index * item_size); return vb->pointer + (index * item_size);
} }
u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{ {
vb_generic_ensure_capacity(vb, item_size, item_count); vb_generic_ensure_capacity(vb, item_size, item_count);
return vb_generic_add_assume_capacity(vb, item_size, item_count); return vb_generic_add_assume_capacity(vb, item_size, item_count);
} }
u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes) fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes)
{ {
auto len = cast_to(u32, u64, bytes.length); let_cast(u32, len, bytes.length);
vb_generic_ensure_capacity(vb, sizeof(u8), len); vb_generic_ensure_capacity(vb, sizeof(u8), len);
auto* pointer = vb_generic_add_assume_capacity(vb, sizeof(u8), len); let(pointer, vb_generic_add_assume_capacity(vb, sizeof(u8), len));
memcpy(pointer, bytes.pointer, len); memcpy(pointer, bytes.pointer, len);
return pointer; return pointer;
} }
void vb_copy_string(VirtualBuffer(u8)* buffer, String string) fn void vb_copy_string(VirtualBuffer(u8)* buffer, String string)
{ {
auto length = cast_to(u32, u64, string.length); let_cast(u32, length, string.length);
auto* pointer = vb_add(buffer, length); let(pointer, vb_add(buffer, length));
memcpy(pointer, string.pointer, length); memcpy(pointer, string.pointer, length);
} }
u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string) fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string)
{ {
assert(string.pointer[string.length] == 0); assert(string.pointer[string.length] == 0);
string.length += 1; string.length += 1;

View File

@ -45,9 +45,9 @@ decl_vb(f64);
#define vb_copy_any_array(vb, arr) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(arr)), (arr), sizeof(arr)) #define vb_copy_any_array(vb, arr) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(arr)), (arr), sizeof(arr))
#define vb_copy_any_slice(vb, slice) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(*((slice).pointer)) * (slice).length), (slice).pointer, sizeof(*((slice).pointer)) * (slice).length) #define vb_copy_any_slice(vb, slice) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(*((slice).pointer)) * (slice).length), (slice).pointer, sizeof(*((slice).pointer)) * (slice).length)
void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes); fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes);
void vb_copy_string(VirtualBuffer(u8)* buffer, String string); fn void vb_copy_string(VirtualBuffer(u8)* buffer, String string);
u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string); fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string);

View File

@ -1,9 +1,10 @@
#include <std/render.h> #pragma once
#include <std/string.h>
#include <std/image_loader.h>
#include <std/virtual_buffer.h>
#include <volk.h> #define vulkan_load_function_generic(fn, load_fn, context) fn = (PFN_ ## fn) load_fn(context, #fn)
#define vulkan_load_instance_function(instance, fn) vulkan_load_function_generic(fn, vkGetInstanceProcAddr, instance)
#define vulkan_load_device_function(device, fn) vulkan_load_function_generic(fn, vkGetDeviceProcAddr, device)
global_variable OSLibrary vulkan_library;
#define MAX_SWAPCHAIN_IMAGE_COUNT (16) #define MAX_SWAPCHAIN_IMAGE_COUNT (16)
#define MAX_FRAME_COUNT (2) #define MAX_FRAME_COUNT (2)
@ -115,7 +116,6 @@ STRUCT(IndexBuffer)
STRUCT(Renderer) STRUCT(Renderer)
{ {
VkInstance instance;
VkAllocationCallbacks* allocator; VkAllocationCallbacks* allocator;
VkPhysicalDevice physical_device; VkPhysicalDevice physical_device;
VkDevice device; VkDevice device;
@ -192,11 +192,11 @@ STRUCT(VulkanCopyImageArgs)
VulkanCopyImage destination; VulkanCopyImage destination;
}; };
global_variable Renderer renderer_memory; global_variable Renderer renderer_memory;
global_variable RenderWindow renderer_window_memory; global_variable RenderWindow renderer_window_memory;
global_variable VulkanTexture textures[MAX_TEXTURE_COUNT]; global_variable VulkanTexture textures[MAX_TEXTURE_COUNT];
global_variable u32 texture_count; global_variable u32 texture_count;
global_variable VkInstance instance;
fn String vulkan_result_to_string(VkResult result) fn String vulkan_result_to_string(VkResult result)
{ {
@ -255,7 +255,7 @@ fn String vulkan_result_to_string(VkResult result)
} }
} }
[[noreturn]] [[gnu::cold]] fn void wrong_vulkan_result(VkResult result, String call_string, String file, int line) BB_NORETURN BB_COLD fn void wrong_vulkan_result(VkResult result, String call_string, String file, int line)
{ {
unused(result); unused(result);
unused(call_string); unused(call_string);
@ -270,15 +270,15 @@ fn void buffer_copy_to_local_command(VkCommandBuffer command_buffer, Slice(Local
{ {
for (u64 i = 0; i < copies.length; i += 1) for (u64 i = 0; i < copies.length; i += 1)
{ {
auto copy = copies.pointer[i]; let(copy, copies.pointer[i]);
auto* source_buffer = &copy.source; let(source_buffer, &copy.source);
auto* destination_buffer = &copy.destination; let(destination_buffer, &copy.destination);
VkBufferCopy2 buffer_copies[MAX_LOCAL_BUFFER_COPY_COUNT]; VkBufferCopy2 buffer_copies[MAX_LOCAL_BUFFER_COPY_COUNT];
for (u64 i = 0; i < copy.regions.length; i += 1) for (u64 i = 0; i < copy.regions.length; i += 1)
{ {
auto copy_region = copy.regions.pointer[i]; let(copy_region, copy.regions.pointer[i]);
buffer_copies[i] = (VkBufferCopy2) { buffer_copies[i] = (VkBufferCopy2) {
.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2,
.pNext = 0, .pNext = 0,
@ -301,16 +301,16 @@ fn void buffer_copy_to_local_command(VkCommandBuffer command_buffer, Slice(Local
} }
} }
void buffer_copy_to_host(VulkanBuffer buffer, Slice(HostBufferCopy) regions) fn void buffer_copy_to_host(VulkanBuffer buffer, Slice(HostBufferCopy) regions)
{ {
assert(buffer.type == BUFFER_TYPE_STAGING); assert(buffer.type == BUFFER_TYPE_STAGING);
auto* buffer_pointer = (u8*)buffer.address; let(buffer_pointer, (u8*)buffer.address);
for (u64 i = 0; i < regions.length; i += 1) for (u64 i = 0; i < regions.length; i += 1)
{ {
auto region = regions.pointer[i]; let(region, regions.pointer[i]);
auto* destination = buffer_pointer + region.destination_offset; let(destination, buffer_pointer + region.destination_offset);
assert(destination + region.source.length <= (u8*)buffer.address + buffer.size); assert(destination + region.source.length <= (u8*)buffer.address + buffer.size);
#define USE_MEMCPY 1 #define USE_MEMCPY 1
#if USE_MEMCPY #if USE_MEMCPY
@ -396,7 +396,7 @@ fn GPUMemory vk_allocate_memory(VkDevice device, const VkAllocationCallbacks* al
u32 memory_type_index; u32 memory_type_index;
for (memory_type_index = 0; memory_type_index < memory_properties.memoryTypeCount; memory_type_index += 1) for (memory_type_index = 0; memory_type_index < memory_properties.memoryTypeCount; memory_type_index += 1)
{ {
auto memory_type = memory_properties.memoryTypes[memory_type_index]; let(memory_type, memory_properties.memoryTypes[memory_type_index]);
if ((memory_requirements.memoryTypeBits & (1 << memory_type_index)) != 0 && (memory_type.propertyFlags & flags) == flags) if ((memory_requirements.memoryTypeBits & (1 << memory_type_index)) != 0 && (memory_type.propertyFlags & flags) == flags)
{ {
@ -498,7 +498,7 @@ fn VulkanBuffer buffer_create(Renderer* renderer, u64 size, BufferType type)
VkMemoryPropertyFlags memory_flags = VkMemoryPropertyFlags memory_flags =
(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT * is_dst) | (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT * is_dst) |
((VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) * is_src); ((VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) * is_src);
auto result = vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, size, usage, memory_flags); let(result, vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, size, usage, memory_flags));
result.type = type; result.type = type;
return result; return result;
} }
@ -524,7 +524,7 @@ fn u8 vk_layer_is_supported(String layer_name)
{ {
VkLayerProperties* properties = &layers[i]; VkLayerProperties* properties = &layers[i];
auto candidate_layer_name = cstr(properties->layerName); let(candidate_layer_name, cstr(properties->layerName));
if (s_equal(candidate_layer_name, layer_name)) if (s_equal(candidate_layer_name, layer_name))
{ {
supported = 1; supported = 1;
@ -549,7 +549,7 @@ fn String message_severity_to_string(VkDebugUtilsMessageSeverityFlagBitsEXT mess
return strlit("WARNING"); return strlit("WARNING");
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
return strlit("ERROR"); return strlit("ERROR");
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT: default:
unreachable(); unreachable();
} }
} }
@ -566,9 +566,10 @@ fn String message_type_to_string(VkDebugUtilsMessageTypeFlagBitsEXT message_type
return strlit("PERFORMANCE"); return strlit("PERFORMANCE");
case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT:
return strlit("DEVICE_ADDRESS_BINDING"); return strlit("DEVICE_ADDRESS_BINDING");
case VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT: default:
unreachable(); unreachable();
} }
} }
fn VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) fn VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
@ -638,7 +639,7 @@ fn void immediate_end(ImmediateContext context)
vkok(vkQueueSubmit2(context.queue, array_length(submit_info), submit_info, context.fence)); vkok(vkQueueSubmit2(context.queue, array_length(submit_info), submit_info, context.fence));
VkBool32 wait_all = 1; VkBool32 wait_all = 1;
auto timeout = ~(u64)0; let(timeout, ~(u64)0);
vkok(vkWaitForFences(context.device, array_length(fences), fences, wait_all, timeout)); vkok(vkWaitForFences(context.device, array_length(fences), fences, wait_all, timeout));
} }
@ -775,21 +776,82 @@ fn void vk_image_copy(VkCommandBuffer command_buffer, VulkanCopyImageArgs args)
vkCmdBlitImage2(command_buffer, &blit_info); vkCmdBlitImage2(command_buffer, &blit_info);
} }
fn Renderer* rendering_initialize(Arena* arena)
Renderer* renderer_initialize(Arena* arena)
{ {
Renderer* renderer = &renderer_memory; Renderer* renderer = &renderer_memory;
vkok(volkInitialize());
auto api_version = volkGetInstanceVersion(); #ifdef __linux__
vulkan_library = os_library_load("libvulkan.so.1");
#elif _WIN32
vulkan_library = os_library_load("vulkan-1.dll");
#elif __APPLE__
vulkan_library = os_library_load("libvulkan.dylib");
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("libvulkan.1.dylib");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("libMoltenVK.dylib");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("vulkan.framework/vulkan");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("MoltenVK.framework/MoltenVK");
}
#endif
if (!os_library_is_valid(vulkan_library))
{
failed_execution();
}
vkGetInstanceProcAddr = os_symbol_load(vulkan_library, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr)
{
failed_execution();
}
vulkan_load_instance_function(0, vkEnumerateInstanceVersion);
vulkan_load_instance_function(0, vkCreateInstance);
u32 api_version = 0;
if (vkEnumerateInstanceVersion)
{
vkok(vkEnumerateInstanceVersion(&api_version));
}
if (!api_version)
{
if (vkCreateInstance)
{
api_version = VK_API_VERSION_1_0;
}
}
if (!api_version)
{
failed_execution();
}
if (api_version < VK_API_VERSION_1_3) if (api_version < VK_API_VERSION_1_3)
{ {
failed_execution(); failed_execution();
} }
// Proceed to load Vulkan instance-level functions
vulkan_load_instance_function(0, vkEnumerateInstanceLayerProperties);
{ {
#if BB_DEBUG #if BB_DEBUG
auto debug_layer = strlit("VK_LAYER_KHRONOS_validation"); let(debug_layer, strlit("VK_LAYER_KHRONOS_validation"));
if (!vk_layer_is_supported(debug_layer)) if (!vk_layer_is_supported(debug_layer))
{ {
failed_execution(); failed_execution();
@ -798,7 +860,7 @@ Renderer* renderer_initialize(Arena* arena)
{ {
string_to_c(debug_layer), string_to_c(debug_layer),
}; };
auto layer_count = array_length(layers); let(layer_count, array_length(layers));
#else #else
const char** layers = 0; const char** layers = 0;
u32 layer_count = 0; u32 layer_count = 0;
@ -852,11 +914,10 @@ Renderer* renderer_initialize(Arena* arena)
}; };
#endif #endif
auto* pNext =
#if BB_DEBUG #if BB_DEBUG
enable_shader_debug_printf ? (void*)&validation_features : (void*)&msg_ci; let(pNext, enable_shader_debug_printf ? (void*)&validation_features : (void*)&msg_ci);
#else #else
(void*)0; void* pNext = 0;
#endif #endif
VkApplicationInfo app_info = { VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
@ -877,19 +938,40 @@ Renderer* renderer_initialize(Arena* arena)
.enabledExtensionCount = array_length(extensions), .enabledExtensionCount = array_length(extensions),
}; };
vkok(vkCreateInstance(&ci, renderer->allocator, &renderer->instance)); vkok(vkCreateInstance(&ci, renderer->allocator, &instance));
volkLoadInstance(renderer->instance);
#if BB_DEBUG #if BB_DEBUG
vulkan_load_instance_function(instance, vkCreateDebugUtilsMessengerEXT);
VkDebugUtilsMessengerEXT messenger; VkDebugUtilsMessengerEXT messenger;
vkok(vkCreateDebugUtilsMessengerEXT(renderer->instance, &msg_ci, renderer->allocator, &messenger)); vkok(vkCreateDebugUtilsMessengerEXT(instance, &msg_ci, renderer->allocator, &messenger));
#endif #endif
} }
vulkan_load_instance_function(instance, vkGetDeviceProcAddr);
vulkan_load_instance_function(instance, vkEnumeratePhysicalDevices);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceMemoryProperties);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceProperties);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceQueueFamilyProperties);
vulkan_load_instance_function(instance, vkCreateDevice);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceSurfacePresentModesKHR);
#ifdef VK_USE_PLATFORM_XCB_KHR
vulkan_load_instance_function(instance, vkCreateXcbSurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
vulkan_load_instance_function(instance, vkCreateWin32SurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
vulkan_load_instance_function(instance, vkCreateWin32SurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
vulkan_load_instance_function(instance, vkCreateMetalSurfaceEXT);
#endif
{ {
u32 physical_device_count; u32 physical_device_count;
VkPhysicalDevice physical_devices[256]; VkPhysicalDevice physical_devices[256];
vkok(vkEnumeratePhysicalDevices(renderer->instance, &physical_device_count, 0)); vkok(vkEnumeratePhysicalDevices(instance, &physical_device_count, 0));
if (physical_device_count == 0) if (physical_device_count == 0)
{ {
@ -901,7 +983,7 @@ Renderer* renderer_initialize(Arena* arena)
failed_execution(); failed_execution();
} }
vkok(vkEnumeratePhysicalDevices(renderer->instance, &physical_device_count, physical_devices)); vkok(vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices));
renderer->physical_device = physical_devices[0]; renderer->physical_device = physical_devices[0];
} }
@ -1026,6 +1108,61 @@ Renderer* renderer_initialize(Arena* arena)
vkok(vkCreateDevice(renderer->physical_device, &ci, renderer->allocator, &renderer->device)); vkok(vkCreateDevice(renderer->physical_device, &ci, renderer->allocator, &renderer->device));
} }
// Load device table
vulkan_load_device_function(renderer->device, vkCreateSwapchainKHR);
vulkan_load_device_function(renderer->device, vkDestroySwapchainKHR);
vulkan_load_device_function(renderer->device, vkGetSwapchainImagesKHR);
vulkan_load_device_function(renderer->device, vkGetImageMemoryRequirements);
vulkan_load_device_function(renderer->device, vkGetBufferMemoryRequirements);
vulkan_load_device_function(renderer->device, vkMapMemory);
vulkan_load_device_function(renderer->device, vkUnmapMemory);
vulkan_load_device_function(renderer->device, vkAllocateMemory);
vulkan_load_device_function(renderer->device, vkBindImageMemory);
vulkan_load_device_function(renderer->device, vkBindBufferMemory);
vulkan_load_device_function(renderer->device, vkBindBufferMemory);
vulkan_load_device_function(renderer->device, vkGetDeviceQueue);
vulkan_load_device_function(renderer->device, vkCreateCommandPool);
vulkan_load_device_function(renderer->device, vkAllocateCommandBuffers);
vulkan_load_device_function(renderer->device, vkCreateFence);
vulkan_load_device_function(renderer->device, vkCreateSemaphore);
vulkan_load_device_function(renderer->device, vkCreateSampler);
vulkan_load_device_function(renderer->device, vkCreateShaderModule);
vulkan_load_device_function(renderer->device, vkCreateDescriptorSetLayout);
vulkan_load_device_function(renderer->device, vkCreatePipelineLayout);
vulkan_load_device_function(renderer->device, vkCreateGraphicsPipelines);
vulkan_load_device_function(renderer->device, vkCreateImage);
vulkan_load_device_function(renderer->device, vkCreateImageView);
vulkan_load_device_function(renderer->device, vkCreateBuffer);
vulkan_load_device_function(renderer->device, vkCreateDescriptorPool);
vulkan_load_device_function(renderer->device, vkAllocateDescriptorSets);
vulkan_load_device_function(renderer->device, vkResetFences);
vulkan_load_device_function(renderer->device, vkResetCommandBuffer);
vulkan_load_device_function(renderer->device, vkBeginCommandBuffer);
vulkan_load_device_function(renderer->device, vkCmdPipelineBarrier2);
vulkan_load_device_function(renderer->device, vkCmdCopyBufferToImage);
vulkan_load_device_function(renderer->device, vkEndCommandBuffer);
vulkan_load_device_function(renderer->device, vkQueueSubmit2);
vulkan_load_device_function(renderer->device, vkWaitForFences);
vulkan_load_device_function(renderer->device, vkUpdateDescriptorSets);
vulkan_load_device_function(renderer->device, vkAcquireNextImageKHR);
vulkan_load_device_function(renderer->device, vkGetBufferDeviceAddress);
vulkan_load_device_function(renderer->device, vkCmdCopyBuffer2);
vulkan_load_device_function(renderer->device, vkCmdSetViewport);
vulkan_load_device_function(renderer->device, vkCmdSetScissor);
vulkan_load_device_function(renderer->device, vkCmdBeginRendering);
vulkan_load_device_function(renderer->device, vkCmdEndRendering);
vulkan_load_device_function(renderer->device, vkCmdBindPipeline);
vulkan_load_device_function(renderer->device, vkCmdBindDescriptorSets);
vulkan_load_device_function(renderer->device, vkCmdBindIndexBuffer);
vulkan_load_device_function(renderer->device, vkCmdPushConstants);
vulkan_load_device_function(renderer->device, vkCmdDrawIndexed);
vulkan_load_device_function(renderer->device, vkCmdBlitImage2);
vulkan_load_device_function(renderer->device, vkQueuePresentKHR);
vulkan_load_device_function(renderer->device, vkDeviceWaitIdle);
vulkan_load_device_function(renderer->device, vkDestroyImageView);
vulkan_load_device_function(renderer->device, vkDestroyImage);
vulkan_load_device_function(renderer->device, vkFreeMemory);
vkGetDeviceQueue(renderer->device, graphics_queue_family_index, 0, &renderer->graphics_queue); vkGetDeviceQueue(renderer->device, graphics_queue_family_index, 0, &renderer->graphics_queue);
renderer->immediate.device = renderer->device; renderer->immediate.device = renderer->device;
@ -1079,8 +1216,8 @@ Renderer* renderer_initialize(Arena* arena)
} }
String shader_binaries[] = { String shader_binaries[] = {
strlit(BUILD_DIR "/rect.vert.spv"), strlit(BUILD_DIR "/" "rect.vert.spv"),
strlit(BUILD_DIR "/rect.frag.spv"), strlit(BUILD_DIR "/" "rect.frag.spv"),
}; };
PipelineLayoutCreate pipeline_layouts[] = { PipelineLayoutCreate pipeline_layouts[] = {
@ -1112,17 +1249,17 @@ Renderer* renderer_initialize(Arena* arena)
.layout_index = 0, .layout_index = 0,
}, },
}; };
auto create_data = (GraphicsPipelinesCreate) { GraphicsPipelinesCreate create_data = {
.layouts = array_to_slice(pipeline_layouts), .layouts = array_to_slice(pipeline_layouts),
.pipelines = array_to_slice(pipeline_create), .pipelines = array_to_slice(pipeline_create),
.shader_binaries = array_to_slice(shader_binaries), .shader_binaries = array_to_slice(shader_binaries),
}; };
auto graphics_pipeline_count = create_data.pipelines.length; let(graphics_pipeline_count, create_data.pipelines.length);
assert(graphics_pipeline_count); assert(graphics_pipeline_count);
auto pipeline_layout_count = create_data.layouts.length; let(pipeline_layout_count, create_data.layouts.length);
assert(pipeline_layout_count); assert(pipeline_layout_count);
assert(pipeline_layout_count <= graphics_pipeline_count); assert(pipeline_layout_count <= graphics_pipeline_count);
auto shader_count = create_data.shader_binaries.length; let(shader_count, create_data.shader_binaries.length);
VkPipeline pipeline_handles[BB_PIPELINE_COUNT]; VkPipeline pipeline_handles[BB_PIPELINE_COUNT];
VkPipelineShaderStageCreateInfo shader_create_infos[MAX_SHADER_MODULE_COUNT_PER_PIPELINE]; VkPipelineShaderStageCreateInfo shader_create_infos[MAX_SHADER_MODULE_COUNT_PER_PIPELINE];
@ -1246,13 +1383,13 @@ Renderer* renderer_initialize(Arena* arena)
.stencilAttachmentFormat = 0, .stencilAttachmentFormat = 0,
}; };
auto* shader_modules = arena_allocate(arena, VkShaderModule, shader_count); let(shader_modules, arena_allocate(arena, VkShaderModule, shader_count));
for (u64 i = 0; i < shader_count; i += 1) for (u64 i = 0; i < shader_count; i += 1)
{ {
String shader_binary_path = create_data.shader_binaries.pointer[i]; String shader_binary_path = create_data.shader_binaries.pointer[i];
auto binary = file_read(arena, shader_binary_path); let(binary, file_read(arena, shader_binary_path));
VkShaderModuleCreateInfo create_info = { VkShaderModuleCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
@ -1265,10 +1402,10 @@ Renderer* renderer_initialize(Arena* arena)
for (u64 pipeline_index = 0; pipeline_index < pipeline_layout_count; pipeline_index += 1) for (u64 pipeline_index = 0; pipeline_index < pipeline_layout_count; pipeline_index += 1)
{ {
auto create = create_data.layouts.pointer[pipeline_index]; let(create, create_data.layouts.pointer[pipeline_index]);
auto descriptor_set_layout_count = create.descriptor_set_layouts.length; let(descriptor_set_layout_count, create.descriptor_set_layouts.length);
auto push_constant_range_count = create.push_constant_ranges.length; let(push_constant_range_count, create.push_constant_ranges.length);
auto* pipeline = &renderer->pipelines[pipeline_index]; let(pipeline, &renderer->pipelines[pipeline_index]);
pipeline->descriptor_set_count = descriptor_set_layout_count; pipeline->descriptor_set_count = descriptor_set_layout_count;
pipeline->push_constant_range_count = push_constant_range_count; pipeline->push_constant_range_count = push_constant_range_count;
@ -1281,14 +1418,14 @@ Renderer* renderer_initialize(Arena* arena)
for (u64 descriptor_set_layout_index = 0; descriptor_set_layout_index < descriptor_set_layout_count; descriptor_set_layout_index += 1) for (u64 descriptor_set_layout_index = 0; descriptor_set_layout_index < descriptor_set_layout_count; descriptor_set_layout_index += 1)
{ {
auto set_layout_create = create.descriptor_set_layouts.pointer[descriptor_set_layout_index]; let(set_layout_create, create.descriptor_set_layouts.pointer[descriptor_set_layout_index]);
auto binding_count = set_layout_create.bindings.length; let(binding_count, set_layout_create.bindings.length);
auto* descriptor_set_layout_bindings = &pipeline->descriptor_set_layout_bindings[descriptor_set_layout_index]; let(descriptor_set_layout_bindings, &pipeline->descriptor_set_layout_bindings[descriptor_set_layout_index]);
descriptor_set_layout_bindings->count = binding_count; descriptor_set_layout_bindings->count = binding_count;
for (u64 binding_index = 0; binding_index < binding_count; binding_index += 1) for (u64 binding_index = 0; binding_index < binding_count; binding_index += 1)
{ {
auto binding_descriptor = set_layout_create.bindings.pointer[binding_index]; let(binding_descriptor, set_layout_create.bindings.pointer[binding_index]);
VkDescriptorType descriptor_type = vulkan_descriptor_type(binding_descriptor.type); VkDescriptorType descriptor_type = vulkan_descriptor_type(binding_descriptor.type);
@ -1321,7 +1458,7 @@ Renderer* renderer_initialize(Arena* arena)
for (u64 push_constant_index = 0; push_constant_index < push_constant_range_count; push_constant_index += 1) for (u64 push_constant_index = 0; push_constant_index < push_constant_range_count; push_constant_index += 1)
{ {
auto push_constant_descriptor = create.push_constant_ranges.pointer[push_constant_index]; let(push_constant_descriptor, create.push_constant_ranges.pointer[push_constant_index]);
pipeline->push_constant_ranges[push_constant_index] = (VkPushConstantRange) { pipeline->push_constant_ranges[push_constant_index] = (VkPushConstantRange) {
.stageFlags = vulkan_shader_stage(push_constant_descriptor.stage), .stageFlags = vulkan_shader_stage(push_constant_descriptor.stage),
.offset = push_constant_descriptor.offset, .offset = push_constant_descriptor.offset,
@ -1344,8 +1481,8 @@ Renderer* renderer_initialize(Arena* arena)
for (u64 i = 0; i < graphics_pipeline_count; i += 1) for (u64 i = 0; i < graphics_pipeline_count; i += 1)
{ {
auto create = create_data.pipelines.pointer[i]; let(create, create_data.pipelines.pointer[i]);
auto pipeline_shader_count = create.shader_source_indices.length; let(pipeline_shader_count, create.shader_source_indices.length);
if (pipeline_shader_count > MAX_SHADER_MODULE_COUNT_PER_PIPELINE) if (pipeline_shader_count > MAX_SHADER_MODULE_COUNT_PER_PIPELINE)
{ {
failed_execution(); failed_execution();
@ -1353,8 +1490,8 @@ Renderer* renderer_initialize(Arena* arena)
for (u64 i = 0; i < pipeline_shader_count; i += 1) for (u64 i = 0; i < pipeline_shader_count; i += 1)
{ {
auto shader_index = create.shader_source_indices.pointer[i]; let(shader_index, create.shader_source_indices.pointer[i]);
auto shader_source_path = create_data.shader_binaries.pointer[shader_index]; let(shader_source_path, create_data.shader_binaries.pointer[shader_index]);
shader_create_infos[i] = (VkPipelineShaderStageCreateInfo) { shader_create_infos[i] = (VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@ -1543,43 +1680,82 @@ fn void swapchain_recreate(Renderer* renderer, RenderWindow* window)
}); });
} }
// typedef void GLFWwindow; fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window)
// extern VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window)
{ {
RenderWindow* result = &renderer_window_memory; // TODO: truly allocate
vkok((VkResult)window_create_surface(renderer->instance, window, renderer->allocator, (void**)&result->surface)); RenderWindow* render_window = &renderer_window_memory;
swapchain_recreate(renderer, result); #if BB_WINDOWING_BACKEND_X11
VkXcbSurfaceCreateInfoKHR create_info = {
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
.pNext = 0,
.flags = 0,
.connection = xcb_connection_get(),
.window = xcb_window_from_windowing_instance(window),
};
vkok(vkCreateXcbSurfaceKHR(instance, &create_info, renderer->allocator, &render_window->surface));
#endif
#if BB_WINDOWING_BACKEND_WIN32
VkWin32SurfaceCreateInfoKHR create_info = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.pNext = 0,
.flags = 0,
.hinstance = windowing_connection.instance,
.hwnd = window->handle,
};
vkok(vkCreateWin32SurfaceKHR(instance, &create_info, renderer->allocator, &render_window->surface));
#endif
#if BB_WINDOWING_BACKEND_COCOA
CAMetalLayer* layer = [CAMetalLayer layer];
layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
layer.framebufferOnly = true;
layer.frame = window.frame;
window.contentView.layer = layer;
window.opaque = true;
window.backgroundColor = nil;
VkMetalSurfaceCreateInfoEXT create_info = {
.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
.pNext = 0,
.flags = 0,
.pLayer = [CAMetalLayer layer],
};
vkCreateMetalSurfaceEXT(instance, &create_info, renderer->allocator, &render_window->surface);
#endif
WindowingSize window_size = windowing_get_instance_framebuffer_size(window);
swapchain_recreate(renderer, render_window);
for (u64 frame_index = 0; frame_index < MAX_FRAME_COUNT; frame_index += 1) for (u64 frame_index = 0; frame_index < MAX_FRAME_COUNT; frame_index += 1)
{ {
for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1) for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1)
{ {
result->frames[frame_index].pipeline_instantiations[pipeline_index].vertex_buffer.gpu.type = BUFFER_TYPE_VERTEX; render_window->frames[frame_index].pipeline_instantiations[pipeline_index].vertex_buffer.gpu.type = BUFFER_TYPE_VERTEX;
result->frames[frame_index].pipeline_instantiations[pipeline_index].index_buffer.gpu.type = BUFFER_TYPE_INDEX; render_window->frames[frame_index].pipeline_instantiations[pipeline_index].index_buffer.gpu.type = BUFFER_TYPE_INDEX;
result->frames[frame_index].pipeline_instantiations[pipeline_index].transient_buffer.type = BUFFER_TYPE_STAGING; render_window->frames[frame_index].pipeline_instantiations[pipeline_index].transient_buffer.type = BUFFER_TYPE_STAGING;
} }
} }
for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1) for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1)
{ {
auto* pipeline_descriptor = &renderer->pipelines[pipeline_index]; let(pipeline_descriptor, &renderer->pipelines[pipeline_index]);
auto* pipeline_instantiation = &result->pipeline_instantiations[pipeline_index]; let(pipeline_instantiation, &render_window->pipeline_instantiations[pipeline_index]);
u16 descriptor_type_counter[DESCRIPTOR_TYPE_COUNT] = {}; u16 descriptor_type_counter[DESCRIPTOR_TYPE_COUNT] = {};
for (u64 descriptor_index = 0; descriptor_index < pipeline_descriptor->descriptor_set_count; descriptor_index += 1) for (u64 descriptor_index = 0; descriptor_index < pipeline_descriptor->descriptor_set_count; descriptor_index += 1)
{ {
auto* descriptor_set_layout_bindings = &pipeline_descriptor->descriptor_set_layout_bindings[descriptor_index]; let(descriptor_set_layout_bindings, &pipeline_descriptor->descriptor_set_layout_bindings[descriptor_index]);
for (u64 binding_index = 0; binding_index < descriptor_set_layout_bindings->count; binding_index += 1) for (u64 binding_index = 0; binding_index < descriptor_set_layout_bindings->count; binding_index += 1)
{ {
auto* binding_descriptor = &descriptor_set_layout_bindings->buffer[binding_index]; let(binding_descriptor, &descriptor_set_layout_bindings->buffer[binding_index]);
auto descriptor_type = descriptor_type_from_vulkan(binding_descriptor->descriptorType); let(descriptor_type, descriptor_type_from_vulkan(binding_descriptor->descriptorType));
auto* counter_ptr = &descriptor_type_counter[descriptor_type]; let(counter_ptr, &descriptor_type_counter[descriptor_type]);
auto old_counter = *counter_ptr; let(old_counter, *counter_ptr);
*counter_ptr = old_counter + binding_descriptor->descriptorCount; *counter_ptr = old_counter + binding_descriptor->descriptorCount;
} }
} }
@ -1589,10 +1765,10 @@ RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window)
for (DescriptorType i = 0; i < DESCRIPTOR_TYPE_COUNT; i += 1) for (DescriptorType i = 0; i < DESCRIPTOR_TYPE_COUNT; i += 1)
{ {
auto count = descriptor_type_counter[i]; let(count, descriptor_type_counter[i]);
if (count) if (count)
{ {
auto* pool_size = &pool_sizes[pool_size_count]; let(pool_size, &pool_sizes[pool_size_count]);
pool_size_count += 1; pool_size_count += 1;
*pool_size = (VkDescriptorPoolSize) { *pool_size = (VkDescriptorPoolSize) {
@ -1643,7 +1819,7 @@ RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window)
.flags = 0, .flags = 0,
}; };
WindowFrame* frame = &result->frames[i]; WindowFrame* frame = &render_window->frames[i];
vkok(vkCreateCommandPool(renderer->device, &command_pool_create_info, renderer->allocator, &frame->command_pool)); vkok(vkCreateCommandPool(renderer->device, &command_pool_create_info, renderer->allocator, &frame->command_pool));
VkCommandBufferAllocateInfo command_buffer_allocate_info = { VkCommandBufferAllocateInfo command_buffer_allocate_info = {
@ -1660,7 +1836,7 @@ RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window)
frame->bound_pipeline = BB_PIPELINE_COUNT; frame->bound_pipeline = BB_PIPELINE_COUNT;
} }
return result; return render_window;
} }
fn WindowFrame* window_frame(RenderWindow* window) fn WindowFrame* window_frame(RenderWindow* window)
@ -1668,10 +1844,10 @@ fn WindowFrame* window_frame(RenderWindow* window)
return &window->frames[window->frame_index % MAX_FRAME_COUNT]; return &window->frames[window->frame_index % MAX_FRAME_COUNT];
} }
void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window) fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window)
{ {
auto* frame = window_frame(window); let(frame, window_frame(window));
auto timeout = ~(u64)0; let(timeout, ~(u64)0);
u32 fence_count = 1; u32 fence_count = 1;
VkBool32 wait_all = 1; VkBool32 wait_all = 1;
@ -1695,14 +1871,14 @@ void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window)
// Reset frame data // Reset frame data
for (u32 i = 0; i < array_length(window->pipeline_instantiations); i += 1) for (u32 i = 0; i < array_length(window->pipeline_instantiations); i += 1)
{ {
auto* pipeline_instantiation = &frame->pipeline_instantiations[i]; let(pipeline_instantiation, &frame->pipeline_instantiations[i]);
pipeline_instantiation->vertex_buffer.cpu.length = 0; pipeline_instantiation->vertex_buffer.cpu.length = 0;
pipeline_instantiation->vertex_buffer.count = 0; pipeline_instantiation->vertex_buffer.count = 0;
pipeline_instantiation->index_buffer.cpu.length = 0; pipeline_instantiation->index_buffer.cpu.length = 0;
} }
} }
void buffer_destroy(Renderer* renderer, VulkanBuffer buffer) fn void buffer_destroy(Renderer* renderer, VulkanBuffer buffer)
{ {
if (buffer.handle) if (buffer.handle)
{ {
@ -1729,9 +1905,9 @@ fn void buffer_ensure_capacity(Renderer* renderer, VulkanBuffer* buffer, u64 nee
} }
} }
void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
{ {
auto* frame = window_frame(window); let(frame, window_frame(window));
VkCommandBufferBeginInfo command_buffer_begin_info = { VkCommandBufferBeginInfo command_buffer_begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@ -1741,13 +1917,13 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1) for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1)
{ {
auto* frame_pipeline_instantiation = &frame->pipeline_instantiations[i]; let(frame_pipeline_instantiation, &frame->pipeline_instantiations[i]);
if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length)) if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length))
{ {
auto new_vertex_buffer_size = frame_pipeline_instantiation->vertex_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->vertex_buffer.cpu.pointer); let(new_vertex_buffer_size, frame_pipeline_instantiation->vertex_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->vertex_buffer.cpu.pointer));
auto new_index_buffer_size = frame_pipeline_instantiation->index_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->index_buffer.cpu.pointer); let(new_index_buffer_size, frame_pipeline_instantiation->index_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->index_buffer.cpu.pointer));
auto new_transient_buffer_size = new_vertex_buffer_size + new_index_buffer_size; let(new_transient_buffer_size, new_vertex_buffer_size + new_index_buffer_size);
buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->transient_buffer, new_transient_buffer_size); buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->transient_buffer, new_transient_buffer_size);
buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->vertex_buffer.gpu, new_vertex_buffer_size); buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->vertex_buffer.gpu, new_vertex_buffer_size);
@ -1857,9 +2033,9 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1) for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1)
{ {
auto* pipeline = &renderer->pipelines[i]; let(pipeline, &renderer->pipelines[i]);
auto* pipeline_instantiation = &window->pipeline_instantiations[i]; let(pipeline_instantiation, &window->pipeline_instantiations[i]);
auto* frame_pipeline_instantiation = &frame->pipeline_instantiations[i]; let(frame_pipeline_instantiation, &frame->pipeline_instantiations[i]);
if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length)) if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length))
{ {
@ -1883,15 +2059,14 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
} }
// Send vertex buffer and screen dimensions to the shader // Send vertex buffer and screen dimensions to the shader
auto push_constants = (GPUDrawPushConstants) GPUDrawPushConstants push_constants = {
{
.vertex_buffer = frame_pipeline_instantiation->vertex_buffer.gpu.address, .vertex_buffer = frame_pipeline_instantiation->vertex_buffer.gpu.address,
.width = window->width, .width = window->width,
.height = window->height, .height = window->height,
}; };
{ {
auto push_constant_range = pipeline->push_constant_ranges[0]; let(push_constant_range, pipeline->push_constant_ranges[0]);
vkCmdPushConstants(frame->command_buffer, pipeline->layout, push_constant_range.stageFlags, push_constant_range.offset, push_constant_range.size, &push_constants); vkCmdPushConstants(frame->command_buffer, pipeline->layout, push_constant_range.stageFlags, push_constant_range.offset, push_constant_range.size, &push_constants);
frame->push_constants = push_constants; frame->push_constants = push_constants;
} }
@ -2032,15 +2207,17 @@ fn u32 format_channel_count(TextureFormat format)
case TEXTURE_FORMAT_R8G8B8A8_SRGB: case TEXTURE_FORMAT_R8G8B8A8_SRGB:
return 4; return 4;
} }
unreachable();
} }
TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory) fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory)
{ {
assert(texture_memory.depth == 1); assert(texture_memory.depth == 1);
auto texture_index = texture_count; let(texture_index, texture_count);
texture_count += 1; texture_count += 1;
auto* texture = &textures[texture_index]; let(texture, &textures[texture_index]);
texture->image = vk_image_create(renderer->device, renderer->allocator, renderer->memory_properties, (VulkanImageCreate) { texture->image = vk_image_create(renderer->device, renderer->allocator, renderer->memory_properties, (VulkanImageCreate) {
.width = texture_memory.width, .width = texture_memory.width,
.height = texture_memory.height, .height = texture_memory.height,
@ -2050,10 +2227,10 @@ TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_m
}); });
texture->sampler = renderer->sampler; texture->sampler = renderer->sampler;
auto image_size = (u64)texture_memory.depth * texture_memory.width * texture_memory.height * format_channel_count(texture_memory.format); let(image_size, (u64)texture_memory.depth * texture_memory.width * texture_memory.height * format_channel_count(texture_memory.format));
VkBufferUsageFlags buffer_usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; VkBufferUsageFlags buffer_usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VkMemoryPropertyFlags buffer_memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; VkMemoryPropertyFlags buffer_memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
auto transfer_buffer = vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, image_size, buffer_usage_flags, buffer_memory_flags); let(transfer_buffer, vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, image_size, buffer_usage_flags, buffer_memory_flags));
memcpy((void*)transfer_buffer.address, texture_memory.pointer, image_size); memcpy((void*)transfer_buffer.address, texture_memory.pointer, image_size);
immediate_start(renderer->immediate); immediate_start(renderer->immediate);
@ -2093,15 +2270,15 @@ TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_m
return (TextureIndex) { .value = texture_index }; return (TextureIndex) { .value = texture_index };
} }
void window_draw_indexed(RenderWindow* window, u32 index_count, u32 instance_count, u32 first_index, s32 vertex_offset, u32 first_instance) fn void window_draw_indexed(RenderWindow* window, u32 index_count, u32 instance_count, u32 first_index, s32 vertex_offset, u32 first_instance)
{ {
auto* frame = window_frame(window); let(frame, window_frame(window));
vkCmdDrawIndexed(frame->command_buffer, index_count, instance_count, first_index, vertex_offset, first_instance); vkCmdDrawIndexed(frame->command_buffer, index_count, instance_count, first_index, vertex_offset, first_instance);
} }
fn void window_texture_update_begin(RenderWindow* window, BBPipeline pipeline_index, u32 descriptor_count) fn void window_texture_update_begin(RenderWindow* window, BBPipeline pipeline_index, u32 descriptor_count)
{ {
auto* pipeline_instantiation = &window->pipeline_instantiations[pipeline_index]; let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
assert(descriptor_count <= array_length(pipeline_instantiation->texture_descriptors)); assert(descriptor_count <= array_length(pipeline_instantiation->texture_descriptors));
pipeline_instantiation->descriptor_set_update = (VkWriteDescriptorSet) { pipeline_instantiation->descriptor_set_update = (VkWriteDescriptorSet) {
@ -2118,14 +2295,14 @@ fn void window_texture_update_begin(RenderWindow* window, BBPipeline pipeline_in
}; };
} }
void window_rect_texture_update_begin(RenderWindow* window) fn void window_rect_texture_update_begin(RenderWindow* window)
{ {
window_texture_update_begin(window, BB_PIPELINE_RECT, RECT_TEXTURE_SLOT_COUNT); window_texture_update_begin(window, BB_PIPELINE_RECT, RECT_TEXTURE_SLOT_COUNT);
} }
fn void window_queue_pipeline_texture_update(RenderWindow* window, BBPipeline pipeline_index, u32 resource_slot, TextureIndex texture_index) fn void window_queue_pipeline_texture_update(RenderWindow* window, BBPipeline pipeline_index, u32 resource_slot, TextureIndex texture_index)
{ {
auto* pipeline_instantiation = &window->pipeline_instantiations[pipeline_index]; let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
VkDescriptorImageInfo* descriptor_image = &pipeline_instantiation->texture_descriptors[resource_slot]; VkDescriptorImageInfo* descriptor_image = &pipeline_instantiation->texture_descriptors[resource_slot];
VulkanTexture* texture = &textures[texture_index.value]; VulkanTexture* texture = &textures[texture_index.value];
*descriptor_image = (VkDescriptorImageInfo) { *descriptor_image = (VkDescriptorImageInfo) {
@ -2135,22 +2312,22 @@ fn void window_queue_pipeline_texture_update(RenderWindow* window, BBPipeline pi
}; };
} }
void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index) fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index)
{ {
window_queue_pipeline_texture_update(window, BB_PIPELINE_RECT, slot, texture_index); window_queue_pipeline_texture_update(window, BB_PIPELINE_RECT, slot, texture_index);
} }
void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas) fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas)
{ {
static_assert(RECT_TEXTURE_SLOT_MONOSPACE_FONT < RECT_TEXTURE_SLOT_PROPORTIONAL_FONT); static_assert(RECT_TEXTURE_SLOT_MONOSPACE_FONT < RECT_TEXTURE_SLOT_PROPORTIONAL_FONT);
auto slot = RECT_TEXTURE_SLOT_MONOSPACE_FONT + type; let(slot, RECT_TEXTURE_SLOT_MONOSPACE_FONT + type);
window_queue_rect_texture_update(window, slot, atlas.texture); window_queue_rect_texture_update(window, slot, atlas.texture);
renderer->fonts[type] = atlas; renderer->fonts[type] = atlas;
} }
fn void window_texture_update_end(Renderer* renderer, RenderWindow* window, BBPipeline pipeline_index) fn void window_texture_update_end(Renderer* renderer, RenderWindow* window, BBPipeline pipeline_index)
{ {
auto* pipeline_instantiation = &window->pipeline_instantiations[pipeline_index]; let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
u32 descriptor_copy_count = 0; u32 descriptor_copy_count = 0;
VkCopyDescriptorSet* descriptor_copies = 0; VkCopyDescriptorSet* descriptor_copies = 0;
VkWriteDescriptorSet descriptor_set_writes[] = { VkWriteDescriptorSet descriptor_set_writes[] = {
@ -2159,41 +2336,41 @@ fn void window_texture_update_end(Renderer* renderer, RenderWindow* window, BBPi
vkUpdateDescriptorSets(renderer->device, array_length(descriptor_set_writes), descriptor_set_writes, descriptor_copy_count, descriptor_copies); vkUpdateDescriptorSets(renderer->device, array_length(descriptor_set_writes), descriptor_set_writes, descriptor_copy_count, descriptor_copies);
} }
void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window) fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window)
{ {
window_texture_update_end(renderer, window, BB_PIPELINE_RECT); window_texture_update_end(renderer, window, BB_PIPELINE_RECT);
} }
u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index, String vertex_memory, u32 vertex_count) fn u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index, String vertex_memory, u32 vertex_count)
{ {
auto* frame = window_frame(window); let(frame, window_frame(window));
auto* vertex_buffer = &frame->pipeline_instantiations[pipeline_index].vertex_buffer; let(vertex_buffer, &frame->pipeline_instantiations[pipeline_index].vertex_buffer);
vb_copy_string(&vertex_buffer->cpu, vertex_memory); vb_copy_string(&vertex_buffer->cpu, vertex_memory);
auto vertex_offset = vertex_buffer->count; let(vertex_offset, vertex_buffer->count);
vertex_buffer->count = vertex_offset + vertex_count; vertex_buffer->count = vertex_offset + vertex_count;
return vertex_offset; return vertex_offset;
} }
void window_pipeline_add_indices(RenderWindow* window, BBPipeline pipeline_index, Slice(u32) indices) fn void window_pipeline_add_indices(RenderWindow* window, BBPipeline pipeline_index, Slice(u32) indices)
{ {
auto* frame = window_frame(window); let(frame, window_frame(window));
auto* index_pointer = vb_add(&frame->pipeline_instantiations[pipeline_index].index_buffer.cpu, indices.length); let(index_pointer, vb_add(&frame->pipeline_instantiations[pipeline_index].index_buffer.cpu, indices.length));
memcpy(index_pointer, indices.pointer, indices.length * sizeof(*indices.pointer)); memcpy(index_pointer, indices.pointer, indices.length * sizeof(*indices.pointer));
} }
void window_render_rect(RenderWindow* window, RectDraw draw) fn void window_render_rect(RenderWindow* window, RectDraw draw)
{ {
auto p0 = draw.vertex.p0; let(p0, draw.vertex.p0);
auto uv0 = draw.texture.p0; let(uv0, draw.texture.p0);
if (draw.texture.p1.x != 0) if (draw.texture.p1.x != 0)
{ {
assert(draw.texture.p1.x - draw.texture.p0.x == draw.vertex.p1.x - draw.vertex.p0.x); assert(draw.texture.p1.x - draw.texture.p0.x == draw.vertex.p1.x - draw.vertex.p0.x);
assert(draw.texture.p1.y - draw.texture.p0.y == draw.vertex.p1.y - draw.vertex.p0.y); assert(draw.texture.p1.y - draw.texture.p0.y == draw.vertex.p1.y - draw.vertex.p0.y);
} }
auto corner_radius = 5.0f; let(corner_radius, 5.0f);
auto extent = draw.vertex.p1 - p0; let(extent, float2_sub(draw.vertex.p1, p0));
RectVertex vertices[] = { RectVertex vertices[] = {
(RectVertex) { (RectVertex) {
.p0 = p0, .p0 = p0,
@ -2233,7 +2410,7 @@ void window_render_rect(RenderWindow* window, RectDraw draw)
}, },
}; };
auto vertex_offset = window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)); let(vertex_offset, window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)));
u32 indices[] = { u32 indices[] = {
vertex_offset + 0, vertex_offset + 0,
@ -2248,25 +2425,25 @@ void window_render_rect(RenderWindow* window, RectDraw draw)
} }
// TODO: support gradient // TODO: support gradient
void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset) fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset)
{ {
auto* texture_atlas = &renderer->fonts[font_type]; let(texture_atlas, &renderer->fonts[font_type]);
auto height = texture_atlas->ascent - texture_atlas->descent; let(height, texture_atlas->ascent - texture_atlas->descent);
auto texture_index = texture_atlas->texture.value; let(texture_index, texture_atlas->texture.value);
for (u64 i = 0; i < string.length; i += 1) for (u64 i = 0; i < string.length; i += 1)
{ {
auto ch = string.pointer[i]; let(ch, string.pointer[i]);
auto* character = &texture_atlas->characters[ch]; let(character, &texture_atlas->characters[ch]);
auto uv_x = character->x; let(uv_x, character->x);
auto uv_y = character->y; let(uv_y, character->y);
auto char_width = character->width; let(char_width, character->width);
auto char_height = character->height; let(char_height, character->height);
auto pos_x = x_offset; let(pos_x, x_offset);
auto pos_y = y_offset + character->y_offset + height + texture_atlas->descent; // Offset of the height to render the character from the bottom (y + height) up (y) let(pos_y, y_offset + character->y_offset + height + texture_atlas->descent); // Offset of the height to render the character from the bottom (y + height) up (y)
vec2 p0 = { pos_x, pos_y }; vec2 p0 = { pos_x, pos_y };
vec2 uv0 = { uv_x, uv_y }; vec2 uv0 = { uv_x, uv_y };
vec2 extent = { char_width, char_height }; vec2 extent = { char_width, char_height };
@ -2307,7 +2484,7 @@ void window_render_text(Renderer* renderer, RenderWindow* window, String string,
}, },
}; };
auto vertex_offset = window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)); let(vertex_offset, window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)));
u32 indices[] = { u32 indices[] = {
vertex_offset + 0, vertex_offset + 0,
@ -2320,14 +2497,7 @@ void window_render_text(Renderer* renderer, RenderWindow* window, String string,
window_pipeline_add_indices(window, BB_PIPELINE_RECT, (Slice(u32))array_to_slice(indices)); window_pipeline_add_indices(window, BB_PIPELINE_RECT, (Slice(u32))array_to_slice(indices));
auto kerning = (texture_atlas->kerning_tables + ch * 256)[string.pointer[i + 1]]; u32 kerning = (texture_atlas->kerning_tables + ch * 256)[string.pointer[i + 1]];
x_offset += character->advance + kerning; x_offset += character->advance + kerning;
} }
} }
uint2 renderer_font_compute_string_rect(Renderer* renderer, RenderFontType type, String string)
{
auto* texture_atlas = &renderer->fonts[type];
auto result = texture_atlas_compute_string_rect(string, texture_atlas);
return result;
}

View File

@ -0,0 +1,130 @@
#pragma once
#if BB_WINDOWING_BACKEND_X11
#define VK_USE_PLATFORM_XCB_KHR
#endif
#if BB_WINDOWING_BACKEND_COCOA
#define VK_USE_PLATFORM_METAL_EXT
#endif
#if BB_WINDOWING_BACKEND_WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
// Code from Volk
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#ifndef VULKAN_H_
#if defined(VK_USE_PLATFORM_WIN32_KHR)
#include <vulkan/vk_platform.h>
#include <vulkan/vulkan_core.h>
/* When VK_USE_PLATFORM_WIN32_KHR is defined, instead of including vulkan.h directly, we include individual parts of the SDK
* This is necessary to avoid including <windows.h> which is very heavy - it takes 200ms to parse without WIN32_LEAN_AND_MEAN
* and 100ms to parse with it. vulkan_win32.h only needs a few symbols that are easy to redefine ourselves.
*/
typedef unsigned long DWORD;
typedef const wchar_t* LPCWSTR;
typedef void* HANDLE;
typedef struct HINSTANCE__* HINSTANCE;
typedef struct HWND__* HWND;
typedef struct HMONITOR__* HMONITOR;
typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES;
#include <vulkan/vulkan_win32.h>
#ifdef VK_ENABLE_BETA_EXTENSIONS
#include <vulkan/vulkan_beta.h>
#endif
#else
#include <vulkan/vulkan.h>
#endif
#endif
#define vulkan_function_pointer(n) PFN_ ## n n
#define vulkan_global_function_pointer(n) global_variable vulkan_function_pointer(n)
// INSTANCE FUNCTIONS START
// These functions require no instance
vulkan_global_function_pointer(vkGetInstanceProcAddr);
vulkan_global_function_pointer(vkEnumerateInstanceVersion);
vulkan_global_function_pointer(vkEnumerateInstanceLayerProperties);
vulkan_global_function_pointer(vkCreateInstance);
// These functions require an instance as a parameter
vulkan_global_function_pointer(vkGetDeviceProcAddr);
vulkan_global_function_pointer(vkCreateDebugUtilsMessengerEXT);
vulkan_global_function_pointer(vkEnumeratePhysicalDevices);
vulkan_global_function_pointer(vkGetPhysicalDeviceMemoryProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceQueueFamilyProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
vulkan_global_function_pointer(vkGetPhysicalDeviceSurfacePresentModesKHR);
vulkan_global_function_pointer(vkCreateDevice);
#if defined(VK_KHR_xcb_surface)
vulkan_global_function_pointer(vkCreateXcbSurfaceKHR);
#endif
#if defined(VK_KHR_win32_surface)
vulkan_global_function_pointer(vkCreateWin32SurfaceKHR);
#endif
#if defined(VK_EXT_metal_surface)
vulkan_global_function_pointer(vkCreateMetalSurfaceEXT);
#endif
// INSTANCE FUNCTIONS END
vulkan_global_function_pointer(vkCreateSwapchainKHR);
vulkan_global_function_pointer(vkCmdCopyBuffer2);
vulkan_global_function_pointer(vkAllocateMemory);
vulkan_global_function_pointer(vkCreateBuffer);
vulkan_global_function_pointer(vkGetBufferMemoryRequirements);
vulkan_global_function_pointer(vkBindBufferMemory);
vulkan_global_function_pointer(vkMapMemory);
vulkan_global_function_pointer(vkGetBufferDeviceAddress);
vulkan_global_function_pointer(vkResetFences);
vulkan_global_function_pointer(vkResetCommandBuffer);
vulkan_global_function_pointer(vkBeginCommandBuffer);
vulkan_global_function_pointer(vkEndCommandBuffer);
vulkan_global_function_pointer(vkQueueSubmit2);
vulkan_global_function_pointer(vkWaitForFences);
vulkan_global_function_pointer(vkCreateImage);
vulkan_global_function_pointer(vkGetImageMemoryRequirements);
vulkan_global_function_pointer(vkBindImageMemory);
vulkan_global_function_pointer(vkCreateImageView);
vulkan_global_function_pointer(vkCmdPipelineBarrier2);
vulkan_global_function_pointer(vkCmdBlitImage2);
vulkan_global_function_pointer(vkGetDeviceQueue);
vulkan_global_function_pointer(vkCreateCommandPool);
vulkan_global_function_pointer(vkAllocateCommandBuffers);
vulkan_global_function_pointer(vkCreateFence);
vulkan_global_function_pointer(vkCreateSampler);
vulkan_global_function_pointer(vkCreateShaderModule);
vulkan_global_function_pointer(vkCreateDescriptorSetLayout);
vulkan_global_function_pointer(vkCreatePipelineLayout);
vulkan_global_function_pointer(vkCreateGraphicsPipelines);
vulkan_global_function_pointer(vkDestroyImageView);
vulkan_global_function_pointer(vkDestroyImage);
vulkan_global_function_pointer(vkFreeMemory);
vulkan_global_function_pointer(vkDeviceWaitIdle);
vulkan_global_function_pointer(vkDestroySwapchainKHR);
vulkan_global_function_pointer(vkGetSwapchainImagesKHR);
vulkan_global_function_pointer(vkCreateDescriptorPool);
vulkan_global_function_pointer(vkAllocateDescriptorSets);
vulkan_global_function_pointer(vkCreateSemaphore);
vulkan_global_function_pointer(vkAcquireNextImageKHR);
vulkan_global_function_pointer(vkDestroyBuffer);
vulkan_global_function_pointer(vkUnmapMemory);
vulkan_global_function_pointer(vkCmdSetViewport);
vulkan_global_function_pointer(vkCmdSetScissor);
vulkan_global_function_pointer(vkCmdBeginRendering);
vulkan_global_function_pointer(vkCmdBindPipeline);
vulkan_global_function_pointer(vkCmdBindDescriptorSets);
vulkan_global_function_pointer(vkCmdBindIndexBuffer);
vulkan_global_function_pointer(vkCmdPushConstants);
vulkan_global_function_pointer(vkCmdDrawIndexed);
vulkan_global_function_pointer(vkCmdEndRendering);
vulkan_global_function_pointer(vkQueuePresentKHR);
vulkan_global_function_pointer(vkCmdCopyBufferToImage);
vulkan_global_function_pointer(vkUpdateDescriptorSets);

View File

@ -0,0 +1 @@
#pragma once

View File

@ -0,0 +1 @@
#pragma once

View File

@ -0,0 +1,59 @@
#pragma once
#pragma comment(lib, "user32")
fn LRESULT window_callback(HWND window, UINT message, WPARAM w_parameter, LPARAM l_parameter)
{
return DefWindowProcW(window, message, w_parameter, l_parameter);
}
fn u8 windowing_initialize()
{
HINSTANCE instance = GetModuleHandleW(0);
windowing_connection.instance = instance;
WNDCLASSEXW window_class = {
.cbSize = sizeof(window_class),
.lpfnWndProc = window_callback,
.hInstance = instance,
.lpszClassName = L"window",
.hCursor = LoadCursorA(0, IDC_ARROW),
.hIcon = LoadIcon(instance, MAKEINTRESOURCE(1)),
.style = CS_VREDRAW|CS_HREDRAW,
};
RegisterClassExW(&window_class);
windowing_connection.window_class = window_class;
return 1;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate)
{
// TODO:
WindowingInstance* window = &windowing_instances[0];
window->handle = CreateWindowExW(WS_EX_APPWINDOW, L"window", L"Bloat Buster", WS_OVERLAPPEDWINDOW | WS_SIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, instantiate.size.width, instantiate.size.height, 0, 0, windowing_connection.instance, 0);
ShowWindow(window->handle, SW_SHOW);
return window;
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
RECT area;
GetClientRect(instance->handle, &area);
WindowingSize size = {
.width = area.right,
.height = area.bottom,
};
return size;
}
fn void windowing_poll_events()
{
MSG msg;
HWND handle;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}

View File

@ -0,0 +1,13 @@
#pragma once
STRUCT(WindowingConnection)
{
HINSTANCE instance;
WNDCLASSEXW window_class;
};
STRUCT(WindowingInstance)
{
HWND handle;
};

View File

@ -1,434 +0,0 @@
#include <std/window.h>
#ifndef __APPLE__
#include <volk.h>
#endif
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
global_variable OSWindowingCallbacks callbacks;
// TODO: thread local
global_variable OSEventQueue* event_queue = 0;
fn void monitor_callback(GLFWmonitor* monitor, int event)
{
unused(monitor);
unused(event);
todo();
}
fn void joystick_callback(int joystick_id, int event)
{
unused(joystick_id);
unused(event);
todo();
}
fn void bitset_list_add(VirtualBuffer(OSEventBitset)* list, u32* counter, u64 value)
{
auto event_index = *counter;
if (unlikely(event_index % OS_EVENT_BITSET_SIZE == 0))
{
*vb_add(list, 1) = (OSEventBitset) {
.value = 0,
};
}
auto bitset_index = event_index / OS_EVENT_BITSET_SIZE;
u64 bit_index = event_index % OS_EVENT_BITSET_SIZE;
list->pointer[bitset_index].value |= (value << bit_index);
}
void os_windowing_init(OSWindowingInitializationOptions options)
{
#ifdef __linux__
int platform_hint = options.should_use_x11 ? GLFW_PLATFORM_X11 : GLFW_PLATFORM_WAYLAND;
glfwInitHint(GLFW_PLATFORM, platform_hint);
#endif
if (glfwInit() != GLFW_TRUE)
{
failed_execution();
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
// From GLFW documentation:
// This is called when a monitor is connected to or disconnected from the system.
glfwSetMonitorCallback(&monitor_callback);
glfwSetJoystickCallback(&joystick_callback);
callbacks = options.callback;
}
fn void glfw_window_drop_callback(GLFWwindow* window, int path_count, const char* paths[])
{
void* context = glfwGetWindowUserPointer(window);
auto* drop_callback = callbacks.window_drop;
print("DROP\n");
if (drop_callback)
{
drop_callback(window, context, (CStringSlice) { .pointer = (char**)paths, .length = path_count });
}
}
fn void glfw_window_scroll_callback(GLFWwindow* window, double x, double y)
{
void* context = glfwGetWindowUserPointer(window);
auto* scroll_callback = callbacks.window_scroll;
print("SCROLL\n");
if (scroll_callback)
{
scroll_callback(window, context, x, y);
}
}
fn void glfw_window_cursor_enter_callback(GLFWwindow* window, int entered)
{
void* context = glfwGetWindowUserPointer(window);
auto* cursor_enter_callback = callbacks.window_cursor_enter;
print("CURSOR_ENTER: {u32}\n", entered);
auto event_index = event_queue->cursor_enter_count;
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.type = OS_EVENT_TYPE_WINDOW_FOCUS,
.index = event_index,
};
bitset_list_add(&event_queue->cursor_enters, &event_queue->cursor_enter_count, entered);
if (cursor_enter_callback)
{
cursor_enter_callback(window, context, entered);
}
}
fn void glfw_window_cursor_position_callback(GLFWwindow* window, double x, double y)
{
void* context = glfwGetWindowUserPointer(window);
auto* cursor_position_callback = callbacks.window_cursor_position;
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.index = event_queue->cursor_positions.length,
.type = OS_EVENT_TYPE_CURSOR_POSITION,
};
*vb_add(&event_queue->cursor_positions, 1) = (OSEventCursorPosition) {
.x = x,
.y = y,
};
if (cursor_position_callback)
{
cursor_position_callback(window, context, x, y);
}
}
fn void glfw_window_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
void* context = glfwGetWindowUserPointer(window);
auto* mouse_button_callback = callbacks.window_mouse_button;
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.index = event_queue->mouse_buttons.length,
.type = OS_EVENT_TYPE_MOUSE_BUTTON,
};
print("Button: {u32:x}. Action: {u32:x}. Mods: {u32:x}\n", button, action, mods);
OSEventMouseButtonAction os_action;
switch (action)
{
case GLFW_RELEASE: os_action = OS_EVENT_MOUSE_RELEASE; break;
case GLFW_PRESS: os_action = OS_EVENT_MOUSE_PRESS; break;
case GLFW_REPEAT: os_action = OS_EVENT_MOUSE_REPEAT; break;
default: unreachable();
}
OSEventMouseButtonKind mouse_button = 0;
switch (button)
{
case GLFW_MOUSE_BUTTON_1:
mouse_button = OS_EVENT_MOUSE_BUTTON_1;
break;
case GLFW_MOUSE_BUTTON_2:
mouse_button = OS_EVENT_MOUSE_BUTTON_2;
break;
case GLFW_MOUSE_BUTTON_3:
mouse_button = OS_EVENT_MOUSE_BUTTON_3;
break;
case GLFW_MOUSE_BUTTON_4:
mouse_button = OS_EVENT_MOUSE_BUTTON_4;
break;
case GLFW_MOUSE_BUTTON_5:
mouse_button = OS_EVENT_MOUSE_BUTTON_5;
break;
case GLFW_MOUSE_BUTTON_6:
mouse_button = OS_EVENT_MOUSE_BUTTON_6;
break;
case GLFW_MOUSE_BUTTON_7:
mouse_button = OS_EVENT_MOUSE_BUTTON_7;
break;
case GLFW_MOUSE_BUTTON_8:
mouse_button = OS_EVENT_MOUSE_BUTTON_8;
break;
}
*vb_add(&event_queue->mouse_buttons, 1) = (OSEventMouseButton) {
.button = mouse_button,
.event = (OSEventMouseButtonEvent){
.action = os_action,
.mod_shift = !!(mods & GLFW_MOD_SHIFT),
.mod_control = !!(mods & GLFW_MOD_CONTROL),
.mod_alt = !!(mods & GLFW_MOD_ALT),
.mod_super = !!(mods & GLFW_MOD_SUPER),
.mod_caps_lock = !!(mods & GLFW_MOD_CAPS_LOCK),
.mod_num_lock = !!(mods & GLFW_MOD_NUM_LOCK),
},
};
if (mouse_button_callback)
{
mouse_button_callback(window, context, button, action, mods);
}
}
fn void glfw_window_character_modifier_callback(GLFWwindow* window, unsigned int codepoint, int mods)
{
void* context = glfwGetWindowUserPointer(window);
auto* character_modifier_callback = callbacks.window_character_modifier;
// print("CHAR_MODIFIER. Codepoint: {u32}. Mods: {u32}\n", codepoint, mods);
if (character_modifier_callback)
{
character_modifier_callback(window, context, codepoint, mods);
}
}
fn void glfw_window_character_callback(GLFWwindow* window, unsigned int codepoint)
{
void* context = glfwGetWindowUserPointer(window);
auto* character_callback = callbacks.window_character;
// print("CHAR. Codepoint: {u32}\n", codepoint);
if (character_callback)
{
character_callback(window, context, codepoint);
}
}
fn void glfw_window_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
void* context = glfwGetWindowUserPointer(window);
auto* key_callback = callbacks.window_key;
print("Key: {u32}. Scancode: {u32}. Action: {u32}. Mods: {u32}\n", key, scancode, action, mods);
if (key_callback)
{
key_callback(window, context, key, scancode, action, mods);
}
}
fn void glfw_window_content_scale_callback(GLFWwindow* window, float x, float y)
{
void* context = glfwGetWindowUserPointer(window);
print("CONTENT_SCALE\n");
auto* content_scale_callback = callbacks.window_content_scale;
if (content_scale_callback)
{
content_scale_callback(window, context, x, y);
}
}
fn void glfw_window_maximize_callback(GLFWwindow* window, int maximized)
{
void* context = glfwGetWindowUserPointer(window);
print("MAXIMIZE\n");
auto* maximize_callback = callbacks.window_maximize;
if (maximize_callback)
{
maximize_callback(window, context, maximized);
}
}
fn void glfw_window_iconify_callback(GLFWwindow* window, int iconified)
{
void* context = glfwGetWindowUserPointer(window);
print("ICONIFY\n");
auto* iconify_callback = callbacks.window_iconify;
if (iconify_callback)
{
iconify_callback(window, context, iconified);
}
}
fn void glfw_window_focus_callback(GLFWwindow* window, int focused)
{
void* context = glfwGetWindowUserPointer(window);
print("FOCUS\n");
auto* focus_callback = callbacks.window_focus;
auto event_index = event_queue->window_focuses_count;
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.type = OS_EVENT_TYPE_WINDOW_FOCUS,
.index = event_index,
};
bitset_list_add(&event_queue->window_focuses, &event_queue->window_focuses_count, focused);
if (focus_callback)
{
focus_callback(window, context, focused);
}
}
fn void glfw_window_close_callback(GLFWwindow* window)
{
void* context = glfwGetWindowUserPointer(window);
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.type = OS_EVENT_TYPE_WINDOW_CLOSE,
};
auto* close_callback = callbacks.window_close;
if (close_callback)
{
close_callback(window, context);
}
}
fn void glfw_window_position_callback(GLFWwindow* window, int x, int y)
{
void* context = glfwGetWindowUserPointer(window);
print("WINDOW_POSITION: {u32}x{u32}\n", x, y);
auto* position_callback = callbacks.window_position;
*vb_add(&event_queue->descriptors, 1) = (OSEventDescriptor) {
.index = event_queue->window_positions.length,
.type = OS_EVENT_TYPE_WINDOW_POSITION,
};
*vb_add(&event_queue->window_positions, 1) = (OSEventWindowPosition) {
.x = x,
.y = y,
};
if (position_callback)
{
position_callback(window, context, x, y);
}
}
fn void glfw_window_size_callback(GLFWwindow* window, int width, int height)
{
void* context = glfwGetWindowUserPointer(window);
print("WINDOW_SIZE\n");
auto* window_resize_callback = callbacks.window_resize;
if (window_resize_callback)
{
window_resize_callback(window, context, width, height);
}
}
fn void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
void* context = glfwGetWindowUserPointer(window);
print("FRAMEBUFFER_SIZE\n");
auto* framebuffer_resize_callback = callbacks.framebuffer_resize;
if (framebuffer_resize_callback)
{
framebuffer_resize_callback(window, context, width, height);
}
}
fn void glfw_window_refresh_callback(GLFWwindow* w)
{
void* context = glfwGetWindowUserPointer(w);
print("REFRESH\n");
auto refresh_callback = callbacks.window_refresh;
if (refresh_callback)
{
refresh_callback(w, context);
}
}
OSWindow os_window_create(OSWindowCreate create)
{
// glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
GLFWmonitor* monitor = 0;
GLFWwindow* share = 0;
GLFWwindow* window = glfwCreateWindow(create.size.width, create.size.height, string_to_c(create.name), monitor, share);
glfwSetWindowUserPointer(window, create.context);
glfwSetWindowPosCallback(window, &glfw_window_position_callback);
glfwSetWindowSizeCallback(window, &glfw_window_size_callback);
glfwSetWindowCloseCallback(window, &glfw_window_close_callback);
glfwSetWindowFocusCallback(window, &glfw_window_focus_callback);
glfwSetWindowIconifyCallback(window, &glfw_window_iconify_callback); // Minimize callback
glfwSetWindowMaximizeCallback(window, &glfw_window_maximize_callback);
glfwSetFramebufferSizeCallback(window, &glfw_framebuffer_size_callback);
glfwSetWindowRefreshCallback(window, &glfw_window_refresh_callback);
glfwSetWindowContentScaleCallback(window, &glfw_window_content_scale_callback);
glfwSetKeyCallback(window, &glfw_window_key_callback);
glfwSetCharCallback(window, &glfw_window_character_callback);
glfwSetCharModsCallback(window, &glfw_window_character_modifier_callback);
glfwSetMouseButtonCallback(window, &glfw_window_mouse_button_callback);
glfwSetCursorPosCallback(window, &glfw_window_cursor_position_callback);
glfwSetCursorEnterCallback(window, &glfw_window_cursor_enter_callback);
glfwSetScrollCallback(window, &glfw_window_scroll_callback);
glfwSetDropCallback(window, &glfw_window_drop_callback);
return window;
}
u8 os_window_should_close(OSWindow window)
{
return glfwWindowShouldClose(window);
}
fn void os_event_queue_reset(OSEventQueue* queue)
{
queue->descriptors.length = 0;
queue->mouse_buttons.length = 0;
queue->cursor_positions.length = 0;
}
fn u8 os_event_bitset_list(VirtualBuffer(OSEventBitset) bitset, u32 index)
{
auto bitset_index = index / bitset.length;
auto bit_index = index % bitset.length;
return !!(bitset.pointer[bitset_index].value & bit_index);
}
u8 os_event_queue_get_window_focus(OSEventQueue* queue, u32 index)
{
assert(index < queue->window_focuses.length);
auto result = os_event_bitset_list(queue->window_focuses, index);
return result;
}
void os_poll_events(OSEventQueue* queue)
{
os_event_queue_reset(queue);
event_queue = queue;
assert(queue->descriptors.length == 0);
glfwPollEvents();
}
OSWindowSize os_window_framebuffer_size_get(OSWindow window)
{
int width;
int height;
glfwGetFramebufferSize(window, &width, &height);
return (OSWindowSize)
{
.width = width,
.height = height,
};
}
OSCursorPosition os_window_cursor_position_get(OSWindow window)
{
OSCursorPosition result;
glfwGetCursorPos(window, &result.x, &result.y);
return result;
}
int window_create_surface(void* instance, OSWindow window, const void* allocator, void** surface)
{
return glfwCreateWindowSurface(instance, window, allocator, (VkSurfaceKHR*)surface);
}

15
bootstrap/std/windowing.c Normal file
View File

@ -0,0 +1,15 @@
#if BB_WINDOWING_BACKEND_X11
#include <std/x11_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_WAYLAND
#include <std/wayland_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_COCOA
#include <std/cocoa_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_WIN32
#include <std/win32_windowing.c>
#endif

206
bootstrap/std/windowing.h Normal file
View File

@ -0,0 +1,206 @@
#pragma once
#if BB_WINDOWING_BACKEND_X11
#include <std/x11_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_WAYLAND
#include <std/wayland_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_COCOA
#include <std/cocoa_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_WIN32
#include <std/win32_windowing.h>
#endif
typedef enum WindowingEventType
{
WINDOWING_EVENT_TYPE_MOUSE_BUTTON,
WINDOWING_EVENT_TYPE_CURSOR_POSITION,
WINDOWING_EVENT_TYPE_CURSOR_ENTER,
WINDOWING_EVENT_TYPE_WINDOW_FOCUS,
WINDOWING_EVENT_TYPE_WINDOW_POSITION,
WINDOWING_EVENT_TYPE_WINDOW_CLOSE,
} WindowingEventType;
STRUCT(WindowingEventDescriptor)
{
u32 index:24;
WindowingEventType type:8;
};
static_assert(sizeof(WindowingEventDescriptor) == 4);
decl_vb(WindowingEventDescriptor);
ENUM_START(WindowingEventMouseButtonKind, u8)
{
WINDOWING_EVENT_MOUSE_BUTTON_1 = 0,
WINDOWING_EVENT_MOUSE_BUTTON_2 = 1,
WINDOWING_EVENT_MOUSE_BUTTON_3 = 2,
WINDOWING_EVENT_MOUSE_BUTTON_4 = 3,
WINDOWING_EVENT_MOUSE_BUTTON_5 = 4,
WINDOWING_EVENT_MOUSE_BUTTON_6 = 5,
WINDOWING_EVENT_MOUSE_BUTTON_7 = 6,
WINDOWING_EVENT_MOUSE_BUTTON_8 = 7,
WINDOWING_EVENT_MOUSE_LEFT = WINDOWING_EVENT_MOUSE_BUTTON_1,
WINDOWING_EVENT_MOUSE_RIGHT = WINDOWING_EVENT_MOUSE_BUTTON_2,
WINDOWING_EVENT_MOUSE_MIDDLE = WINDOWING_EVENT_MOUSE_BUTTON_3,
}
ENUM_END(WindowingEventMouseButtonKind);
#define WINDOWING_EVENT_MOUSE_BUTTON_COUNT (WINDOWING_EVENT_MOUSE_BUTTON_8 + 1)
ENUM_START(WindowingEventMouseButtonAction, u8)
{
WINDOWING_EVENT_MOUSE_RELAX = 0,
WINDOWING_EVENT_MOUSE_RELEASE = 1,
WINDOWING_EVENT_MOUSE_PRESS = 2,
WINDOWING_EVENT_MOUSE_REPEAT = 3,
} ENUM_END(WindowingEventMouseButtonAction);
STRUCT(WindowingEventMouseButtonEvent)
{
WindowingEventMouseButtonAction action:2;
u8 mod_shift:1;
u8 mod_control:1;
u8 mod_alt:1;
u8 mod_super:1;
u8 mod_caps_lock:1;
u8 mod_num_lock:1;
};
STRUCT(WindowingEventMouseButton)
{
WindowingEventMouseButtonKind button:3;
u8 reserved:5;
WindowingEventMouseButtonEvent event;
};
static_assert(sizeof(WindowingEventMouseButton) == sizeof(u16));
decl_vb(WindowingEventMouseButton);
#define WINDOWING_EVENT_BITSET_SIZE (64)
STRUCT(WindowingEventBitset)
{
u64 value;
};
decl_vb(WindowingEventBitset);
STRUCT(WindowingEventCursorPosition)
{
f64 x;
f64 y;
};
decl_vb(WindowingEventCursorPosition);
STRUCT(WindowingEventWindowPosition)
{
u32 x;
u32 y;
};
decl_vb(WindowingEventWindowPosition);
STRUCT(WindowingEventQueue)
{
VirtualBuffer(WindowingEventDescriptor) descriptors;
VirtualBuffer(WindowingEventMouseButton) mouse_buttons;
VirtualBuffer(WindowingEventBitset) window_focuses;
u32 window_focuses_count;
u32 cursor_enter_count;
VirtualBuffer(WindowingEventBitset) cursor_enters;
VirtualBuffer(WindowingEventCursorPosition) cursor_positions;
VirtualBuffer(WindowingEventWindowPosition) window_positions;
};
// typedef void OSFramebufferResize(OSWindow window, void* context, u32 width, u32 height);
// typedef void OSWindowResize(OSWindow window, void* context, u32 width, u32 height);
// typedef void OSWindowRefresh(OSWindow window, void* context);
// typedef void OSWindowPosition(OSWindow window, void* context, u32 x, u32 y);
// typedef void OSWindowClose(OSWindow window, void* context);
// typedef void OSWindowFocus(OSWindow window, void* context, u8 focused);
// typedef void OSWindowIconify(OSWindow window, void* context, u8 iconified);
// typedef void OSWindowMaximize(OSWindow window, void* context, u8 maximized);
// typedef void OSWindowContentScale(OSWindow window, void* context, f32 x, f32 y);
// typedef void OSWindowKey(OSWindow window, void* context, s32 key, s32 scancode, s32 action, s32 mods);
// typedef void OSWindowCharacter(OSWindow window, void* context, u32 codepoint);
// typedef void OSWindowCharacterModifier(OSWindow window, void* context, u32 codepoint, s32 mods);
// typedef void OSWindowMouseButton(OSWindow window, void* context, s32 button, s32 action, s32 mods);
// typedef void OSWindowCursorPosition(OSWindow window, void* context, f64 x, f64 y);
// typedef void OSWindowCursorEnter(OSWindow window, void* context, u8 entered);
// typedef void OSWindowScroll(OSWindow window, void* context, f64 x, f64 y);
// typedef void OSWindowDrop(OSWindow window, void* context, CStringSlice paths);
// STRUCT(OSWindowingCallbacks)
// {
// // OSFramebufferResize* framebuffer_resize;
// // OSWindowResize* window_resize;
// // OSWindowRefresh* window_refresh;
// // OSWindowPosition* window_position;
// // OSWindowClose* window_close;
// // OSWindowFocus* window_focus;
// // OSWindowIconify* window_iconify;
// // OSWindowMaximize* window_maximize;
// // OSWindowContentScale* window_content_scale;
// // OSWindowKey* window_key;
// // OSWindowCharacter* window_character;
// // OSWindowCharacterModifier* window_character_modifier;
// // OSWindowMouseButton* window_mouse_button;
// // OSWindowCursorPosition* window_cursor_position;
// // OSWindowCursorEnter* window_cursor_enter;
// // OSWindowScroll* window_scroll;
// // OSWindowDrop* window_drop;
// };
// STRUCT(OSWindowCreate)
// {
// String name;
// OSWindowSize size;
// void* context;
// // OSWindowResize* resize_callback;
// // OSWindowRefresh* refresh_callback;
// };
STRUCT(WindowingCursorPosition)
{
f64 x;
f64 y;
};
// NEW API START
STRUCT(WindowingOffset)
{
u32 x;
u32 y;
};
STRUCT(WindowingSize)
{
u32 width;
u32 height;
};
STRUCT(WindowingInstantiate)
{
String name;
WindowingOffset offset;
WindowingSize size;
void* context;
};
fn u8 windowing_initialize();
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate);
fn void windowing_poll_events();
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* window);
// NEW API END
// fn OSWindow os_window_create(OSWindowCreate create);
// fn u8 os_window_should_close(OSWindow window);
// fn OSCursorPosition os_window_cursor_position_get(OSWindow window);
//
// fn u8 os_event_queue_get_window_focus(OSEventQueue* queue, u32 index);
#ifndef __APPLE__
global_variable WindowingConnection windowing_connection;
global_variable WindowingInstance windowing_instances[256];
#endif

View File

@ -0,0 +1,197 @@
#pragma once
global_variable xcb_window_t windowing_instance_handles[256];
typedef enum WindowingEvent : u32
{
WINDOWING_EVENT_CLOSE,
WINDOWING_EVENT_COUNT,
} WindowingEvent;
fn xcb_connection_t* xcb_connection_get()
{
return windowing_connection.handle;
}
fn xcb_window_t xcb_window_from_windowing_instance(WindowingInstance* instance)
{
return instance->handle;
}
fn void x11_intern_atoms(u32 atom_count, String* names, xcb_intern_atom_cookie_t* cookies, xcb_intern_atom_reply_t** replies)
{
xcb_connection_t* connection = windowing_connection.handle;
for (u64 i = 0; i < atom_count; i += 1)
{
String atom_name = names[i];
cookies[i] = xcb_intern_atom(connection, 0, atom_name.length, string_to_c(atom_name));
}
for (u64 i = 0; i < atom_count; i += 1)
{
replies[i] = xcb_intern_atom_reply(connection, cookies[i], 0);
}
}
typedef enum X11Atom
{
X11_ATOM_WM_PROTOCOLS,
X11_ATOM_WM_DELETE_WINDOW,
X11_ATOM_COUNT,
} X11Atom;
global_variable String atom_names[X11_ATOM_COUNT] = {
strlit("WM_PROTOCOLS"),
strlit("WM_DELETE_WINDOW"),
};
global_variable xcb_intern_atom_reply_t* atom_replies[array_length(atom_names)];
global_variable xcb_intern_atom_cookie_t atom_cookies[array_length(atom_names)];
fn u8 windowing_initialize()
{
u8 result = 0;
windowing_connection.handle = xcb_connect(0, 0);
if (windowing_connection.handle)
{
if (!xcb_connection_has_error(windowing_connection.handle))
{
windowing_connection.setup = xcb_get_setup(windowing_connection.handle);
if (windowing_connection.setup)
{
x11_intern_atoms(array_length(atom_names), atom_names, atom_cookies, atom_replies);
if (atom_replies[X11_ATOM_WM_PROTOCOLS])
{
if (atom_replies[X11_ATOM_WM_DELETE_WINDOW])
{
result = 1;
}
}
}
}
}
return result;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate create)
{
xcb_connection_t* connection = windowing_connection.handle;
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(windowing_connection.setup);
xcb_screen_t *screen = iter.data;
/* Create a window */
xcb_window_t window_handle = xcb_generate_id(connection);
u32 i;
for (i = 0; i < array_length(windowing_instance_handles); i += 1)
{
xcb_window_t* window_handle_pointer = &windowing_instance_handles[i];
if (!*window_handle_pointer)
{
*window_handle_pointer = window_handle;
break;
}
}
WindowingInstance* window = &windowing_instances[i];
window->handle = window_handle;
u32 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
u32 value_list[] = {
screen->black_pixel,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
xcb_create_window(
connection, /* Connection */
XCB_COPY_FROM_PARENT, /* Depth (same as parent) */
window_handle, /* Window ID */
screen->root, /* Parent window (root) */
create.offset.x, create.offset.y, /* X, Y */
create.size.width, create.size.height,
10, /* Border width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* Class */
screen->root_visual, /* Visual */
value_mask, /* Value mask */
value_list /* Value list */
);
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window_handle, atom_replies[X11_ATOM_WM_PROTOCOLS]->atom, XCB_ATOM_ATOM, 32, 1, &atom_replies[X11_ATOM_WM_DELETE_WINDOW]->atom);
xcb_map_window(connection, window_handle);
/* Flush requests to the X server */
xcb_flush(connection);
return window;
}
fn void windowing_poll_events()
{
xcb_generic_event_t *event;
xcb_connection_t* connection = windowing_connection.handle;
while ((event = xcb_poll_for_event(connection)))
{
switch (event->response_type & ~0x80) {
case XCB_EXPOSE:
break;
case XCB_KEY_PRESS:
break;
case XCB_CLIENT_MESSAGE:
{
let_pointer_cast(xcb_client_message_event_t, client_message_event, event);
if (client_message_event->data.data32[0] == atom_replies[X11_ATOM_WM_DELETE_WINDOW]->atom)
{
xcb_window_t window_handle = client_message_event->window;
u32 i;
u32 window_handle_count = array_length(windowing_instance_handles);
for (i = 0; i < window_handle_count; i += 1)
{
xcb_window_t* window_handle_pointer = &windowing_instance_handles[i];
if (window_handle == *window_handle_pointer)
{
windowing_instances[i].handle = 0;
*window_handle_pointer = 0;
// TODO: For now do this
os_exit(0);
break;
}
}
if (i == window_handle_count)
{
os_exit(1);
}
}
else
{
trap();
}
} break;
case XCB_DESTROY_NOTIFY:
trap();
default:
break;
}
os_free(event);
}
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
WindowingSize result = {};
xcb_connection_t* connection = windowing_connection.handle;
xcb_window_t window = instance->handle;
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connection, window);
xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(connection, cookie, 0);
result.width = reply->width;
result.height = reply->height;
os_free(reply);
return result;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <xcb/xcb.h>
STRUCT(WindowingConnection)
{
xcb_connection_t* handle;
const xcb_setup_t* setup;
};
STRUCT(WindowingInstance)
{
xcb_window_t handle;
};
fn xcb_connection_t* xcb_connection_get();
fn xcb_window_t xcb_window_from_windowing_instance(WindowingInstance* instance);

23
build.bat Normal file
View File

@ -0,0 +1,23 @@
@echo off
setlocal enableextensions
if not defined BB_CI (
set BB_CI=0
)
if not defined BB_BUILD_TYPE (
set BB_BUILD_TYPE=debug
)
set BUILD_DIR=cache
mkdir %BUILD_DIR% > NUL 2>&1
set BUILD_OUT=cache\build.exe
if "%BB_CI%" == "0" (
%VK_SDK_PATH%\Bin\glslangValidator.exe -V bootstrap\std\shaders\rect.vert -o cache\rect.vert.spv --quiet || exit /b 1
%VK_SDK_PATH%\Bin\glslangValidator.exe -V bootstrap\std\shaders\rect.frag -o cache\rect.frag.spv --quiet || exit /b 1
)
cl /Zi /Y- /Gm- /std:clatest /diagnostics:caret -FC /nologo build.c /Fd%BUILD_DIR%\ /Fo%BUILD_DIR%\ /Fe%BUILD_OUT% -Ibootstrap -DBB_TIMETRACE=0 -DBB_BUILD_TYPE=\"%BB_BUILD_TYPE%\" -DBB_CI=%BB_CI% /link /INCREMENTAL:NO || exit /b 1
%BUILD_OUT%

725
build.c Normal file
View File

@ -0,0 +1,725 @@
#include <std/base.h>
#include <std/os.h>
#include <std/project.h>
#include <std/base.c>
#include <std/os.c>
typedef enum C_Compiler
{
C_COMPILER_GCC,
C_COMPILER_CLANG,
C_COMPILER_MSVC,
C_COMPILER_TCC,
C_COMPILER_COUNT,
} C_Compiler;
global_variable char* c_compiler_names[] = {
"gcc",
"clang",
"cl",
"tcc",
};
typedef enum CompilerArgumentStyle
{
COMPILER_ARGUMENT_STYLE_GNU,
COMPILER_ARGUMENT_STYLE_MSVC,
COMPILER_ARGUMENT_STYLE_COUNT,
} CompilerArgumentStyle;
global_variable C_Compiler preferred_c_compiler = C_COMPILER_COUNT;
global_variable char** environment_pointer;
typedef enum BuildType
{
BUILD_TYPE_DEBUG,
BUILD_TYPE_RELEASE_SAFE,
BUILD_TYPE_RELEASE_FAST,
BUILD_TYPE_RELEASE_SMALL,
BUILD_TYPE_COUNT,
} BuildType;
const char* build_type_strings[BUILD_TYPE_COUNT] = {
"debug",
"release_safe",
"release_fast",
"release_small",
};
char* optimization_switches[COMPILER_ARGUMENT_STYLE_COUNT][BUILD_TYPE_COUNT] = {
[COMPILER_ARGUMENT_STYLE_GNU] = {
[BUILD_TYPE_DEBUG] = "-O0",
[BUILD_TYPE_RELEASE_SAFE] = "-O2",
[BUILD_TYPE_RELEASE_FAST] = "-O3",
[BUILD_TYPE_RELEASE_SMALL] = "-Oz",
},
[COMPILER_ARGUMENT_STYLE_MSVC] = {
[BUILD_TYPE_DEBUG] = "/Od",
[BUILD_TYPE_RELEASE_SAFE] = "/Ox",
[BUILD_TYPE_RELEASE_FAST] = "/O2",
[BUILD_TYPE_RELEASE_SMALL] = "/O1",
},
};
STRUCT(CompileFlags)
{
u64 colored_output:1;
u64 debug:1;
u64 error_limit:1;
u64 time_trace:1;
};
STRUCT(CompileOptions)
{
String source_path;
String output_path;
String compiler_path;
RenderingBackend rendering_backend;
WindowingBackend windowing_backend;
BuildType build_type;
CompileFlags flags;
};
typedef enum CompilerSwitch
{
COMPILER_SWITCH_DEBUG_INFO,
COMPILER_SWITCH_COUNT,
} CompilerSwitch;
global_variable char* compiler_switches[COMPILER_ARGUMENT_STYLE_COUNT][COMPILER_SWITCH_COUNT] = {
[COMPILER_ARGUMENT_STYLE_GNU] = {
[COMPILER_SWITCH_DEBUG_INFO] = "-g",
},
[COMPILER_ARGUMENT_STYLE_MSVC] = {
[COMPILER_SWITCH_DEBUG_INFO] = "/Zi",
},
};
fn String file_find_in_path(Arena* arena, String file, String path_env)
{
String result = {};
assert(path_env.pointer);
String path_it = path_env;
u8 buffer[4096];
#if _WIN32
u8 env_path_separator = ';';
u8 path_separator = '\\';
#else
u8 env_path_separator = ':';
u8 path_separator = '/';
#endif
while (path_it.length)
{
let(index, string_first_ch(path_it, env_path_separator));
index = unlikely(index == STRING_NO_MATCH) ? path_it.length : index;
let(path_chunk, s_get_slice(u8, path_it, 0, index));
u64 i = 0;
memcpy(&buffer[i], path_chunk.pointer, path_chunk.length);
i += path_chunk.length;
buffer[i] = path_separator;
i += 1;
memcpy(&buffer[i], file.pointer, file.length);
i += file.length;
#if _WIN32
String exe_extension = strlit(".exe");
memcpy(&buffer[i], exe_extension.pointer, exe_extension.length);
i += exe_extension.length;
#endif
buffer[i] = 0;
i += 1;
let(total_length, i - 1);
OSFileOpenFlags flags = {
.read = 1,
};
OSFilePermissions permissions = {
.readable = 1,
.writable = 1,
};
String path = { .pointer = buffer, .length = total_length };
FileDescriptor fd = os_file_open(path, flags, permissions);
if (os_file_descriptor_is_valid(fd))
{
os_file_close(fd);
result.pointer = arena_allocate(arena, u8, total_length + 1);
memcpy(result.pointer, buffer, total_length + 1);
result.length = total_length;
break;
}
String new_path = s_get_slice(u8, path_it, index + (index != path_it.length), path_it.length);
assert(new_path.length < path_env.length);
path_it = new_path;
}
return result;
}
fn C_Compiler c_compiler_from_path(String path)
{
C_Compiler result = C_COMPILER_COUNT;
let(last_ch_slash, string_last_ch(path, '/'));
let(start, last_ch_slash);
#if _WIN32
let(last_ch_backslash, string_last_ch(path, '\\'));
start = MIN(last_ch_slash, last_ch_backslash);
#endif
assert(start != STRING_NO_MATCH); // This ensures us the path is not just the executable name
let(compiler_name, s_get_slice(u8, path, start + 1, path.length));
for (C_Compiler i = 0; i < C_COMPILER_COUNT; i += 1)
{
let(candidate_compiler_name, cstr(c_compiler_names[i]));
if (string_contains(compiler_name, candidate_compiler_name))
{
result = i;
break;
}
}
return result;
}
fn u8 c_compiler_is_supported_by_os(C_Compiler compiler)
{
#ifdef __linux__
switch (compiler)
{
case C_COMPILER_TCC: case C_COMPILER_GCC: case C_COMPILER_CLANG: return 1;
case C_COMPILER_MSVC: return 0;
case C_COMPILER_COUNT: unreachable();
}
#elif __APPLE__
switch (compiler)
{
case C_COMPILER_TCC: case C_COMPILER_CLANG: return 1;
case C_COMPILER_MSVC: case C_COMPILER_GCC: return 0;
case C_COMPILER_COUNT: unreachable();
}
#elif _WIN32
switch (compiler)
{
case C_COMPILER_MSVC: case C_COMPILER_TCC: case C_COMPILER_CLANG: return 1;
case C_COMPILER_GCC: return 0;
}
#endif
unreachable();
}
fn String c_compiler_to_string(C_Compiler c_compiler)
{
switch (c_compiler)
{
case C_COMPILER_GCC: return strlit("gcc");
case C_COMPILER_MSVC: return strlit("MSVC");
case C_COMPILER_CLANG: return strlit("clang");
case C_COMPILER_TCC: return strlit("tcc");
default: unreachable();
}
}
// Returns the absolute path of a C compiler
fn String get_c_compiler_path(Arena* arena)
{
String cc_path = {};
String cc_env = os_get_environment_variable("CC");
String path_env = os_get_environment_variable("PATH");
if (cc_env.pointer)
{
cc_path = cc_env;
}
#ifndef _WIN32
else
{
cc_path = file_find_in_path(arena, strlit("cc"), path_env);
}
#endif
if (!cc_path.pointer)
{
#if _WIN32
cc_path = strlit("cl.exe");
#elif defined(__APPLE__)
cc_path = strlit("clang");
#elif defined(__linux__)
cc_path = strlit("clang");
#else
#error "Operating system not supported"
#endif
}
let(no_path_sep, string_first_ch(cc_path, '/') == STRING_NO_MATCH);
#ifdef _WIN32
no_path_sep = no_path_sep && string_first_ch(cc_path, '\\') == STRING_NO_MATCH;
#endif
if (no_path_sep)
{
cc_path = file_find_in_path(arena, cc_path, path_env);
}
#ifndef _WIN32
if (cc_path.pointer)
{
u8 buffer[4096];
let(realpath, os_realpath(cc_path, (String)array_to_slice(buffer)));
if (!s_equal(realpath, cc_path))
{
cc_path.pointer = arena_allocate(arena, u8, realpath.length + 1);
cc_path.length = realpath.length;
memcpy(cc_path.pointer, realpath.pointer, realpath.length);
cc_path.pointer[cc_path.length] = 0;
}
}
#endif
#if __APPLE__
if (s_equal(cc_path, strlit("/usr/bin/cc")))
{
cc_path = strlit("/usr/bin/clang");
}
#endif
if (preferred_c_compiler != C_COMPILER_COUNT && c_compiler_is_supported_by_os(preferred_c_compiler))
{
String find_result = file_find_in_path(arena, c_compiler_to_string(preferred_c_compiler), path_env);
if (find_result.pointer)
{
cc_path = find_result;
}
}
return cc_path;
}
fn u8 c_compiler_supports_colored_output(C_Compiler compiler)
{
// TODO: fix
switch (compiler)
{
case C_COMPILER_GCC: case C_COMPILER_CLANG: return 1;
case C_COMPILER_TCC: case C_COMPILER_MSVC: return 0;
default: unreachable();
}
}
fn char* c_compiler_get_error_limit_switch(C_Compiler compiler)
{
// TODO: fix
switch (compiler)
{
case C_COMPILER_CLANG: return "-ferror-limit=1";
case C_COMPILER_GCC: return "-fmax-errors=1";
case C_COMPILER_MSVC: case C_COMPILER_TCC: return 0;
default: unreachable();
}
}
fn char* c_compiler_get_highest_c_standard_flag(C_Compiler compiler)
{
switch (compiler)
{
case C_COMPILER_CLANG: case C_COMPILER_GCC: return "-std=gnu2x";
case C_COMPILER_MSVC: return "/std:clatest";
case C_COMPILER_TCC: return "-std=gnu2x"; // TODO: does it do anything in TCC?
default: unreachable();
}
}
fn RenderingBackend rendering_backend_parse_env(String env)
{
unused(env);
todo();
}
fn RenderingBackend rendering_backend_pick()
{
RenderingBackend rendering_backend = RENDERING_BACKEND_COUNT;
#if BB_CI
rendering_backend = RENDERING_BACKEND_NONE;
#else
char* env = getenv("BB_RENDERING_BACKEND");
if (env)
{
rendering_backend = rendering_backend_parse_env(cstr(env));
}
if (!rendering_backend_is_valid(rendering_backend))
{
#ifdef __linux__
rendering_backend = RENDERING_BACKEND_VULKAN;
#elif defined(__APPLE__)
rendering_backend = RENDERING_BACKEND_METAL;
#elif _WIN32
rendering_backend = RENDERING_BACKEND_VULKAN;
#endif
}
#endif
return rendering_backend;
}
fn WindowingBackend windowing_backend_parse_env(String env)
{
unused(env);
todo();
}
fn WindowingBackend windowing_backend_pick()
{
WindowingBackend windowing_backend = WINDOWING_BACKEND_COUNT;
#if BB_CI
windowing_backend = WINDOWING_BACKEND_NONE;
#else
// Only done for Linux because it is the only operating system in which two windowing backends officially coexist
#ifdef __linux__
char* env = getenv("BB_WINDOWING_BACKEND");
if (env)
{
windowing_backend = windowing_backend_parse_env(cstr(env));
}
#endif
if (!windowing_backend_is_valid(windowing_backend))
{
#ifdef __linux__
// Prefer X11 over Wayland because:
// 1) It works both on Wayland and on X11 desktops
// 2) It works with debugging tools like RenderDoc
windowing_backend = WINDOWING_BACKEND_X11;
#elif _WIN32
windowing_backend = WINDOWING_BACKEND_WIN32;
#elif __APPLE__
windowing_backend = WINDOWING_BACKEND_COCOA;
#endif
}
#endif
return windowing_backend;
}
fn u8 c_compiler_supports_time_trace(C_Compiler compiler)
{
switch (compiler)
{
case C_COMPILER_CLANG: return 1;
default: return 0;
case C_COMPILER_COUNT: unreachable();
}
}
fn BuildType build_type_pick()
{
String build_type_string = strlit(BB_BUILD_TYPE);
BuildType build_type;
for (build_type = 0; build_type < BUILD_TYPE_COUNT; build_type += 1)
{
if (s_equal(build_type_string, cstr(build_type_strings[build_type])))
{
break;
}
}
return build_type;
}
fn void compile_program(Arena* arena, CompileOptions options)
{
if (!options.compiler_path.pointer)
{
char* cc_env = getenv("CC");
if (options.flags.debug)
{
print("Could not find a valid compiler for CC: \"{cstr}\"\n", cc_env ? cc_env : "");
print("PATH: {cstr}\n", getenv("PATH"));
}
failed_execution();
}
if (options.flags.debug)
{
print("C compiler path: {s}\n", options.compiler_path);
}
C_Compiler c_compiler = c_compiler_from_path(options.compiler_path);
if (c_compiler != C_COMPILER_COUNT)
{
String compiler_name = c_compiler_to_string(c_compiler);
if (options.flags.debug)
{
print("Identified compiler as {s}\n", compiler_name);
}
}
else
{
print("Unrecognized C compiler: {s}\n", options.compiler_path);
os_exit(1);
}
char* args[4096];
u64 arg_i = 0;
#define add_arg(arg) args[arg_i++] = (arg)
add_arg(string_to_c(options.compiler_path));
if (c_compiler == C_COMPILER_MSVC)
{
add_arg("/nologo");
}
#if __APPLE__
add_arg("-x");
add_arg("objective-c");
#endif
add_arg(string_to_c(options.source_path));
if (c_compiler == C_COMPILER_MSVC)
{
String strings[] = {
strlit("/Fe"),
options.output_path,
};
String arg = arena_join_string(arena, (Slice(String))array_to_slice(strings));
add_arg(string_to_c(arg));
add_arg("/Fo" BUILD_DIR "\\");
add_arg("/Fd" BUILD_DIR "\\");
}
else
{
add_arg("-o");
add_arg(string_to_c(options.output_path));
}
add_arg("-Ibootstrap");
add_arg("-Idependencies/stb");
char* c_include_path = getenv("C_INCLUDE_PATH");
if (c_include_path)
{
String c_include_path_string = cstr(c_include_path);
u64 previous_i = 0;
for (u64 i = 0; i < c_include_path_string.length; i += 1)
{
u8 ch = c_include_path_string.pointer[i];
if (ch == ':')
{
todo();
}
}
String strings[] = {
strlit("-I"),
s_get_slice(u8, c_include_path_string, previous_i, c_include_path_string.length),
};
String arg = arena_join_string(arena, (Slice(String))array_to_slice(strings));
add_arg(string_to_c(arg));
}
let(debug_info, options.build_type != BUILD_TYPE_RELEASE_SMALL);
if (debug_info)
{
add_arg(compiler_switches[c_compiler == C_COMPILER_MSVC][COMPILER_SWITCH_DEBUG_INFO]);
}
if (c_compiler != C_COMPILER_TCC)
{
add_arg(optimization_switches[c_compiler == C_COMPILER_MSVC][options.build_type]);
}
if (options.flags.colored_output && c_compiler_supports_colored_output(c_compiler))
{
add_arg("-fdiagnostics-color=auto");
}
if (options.flags.error_limit)
{
char* error_limit = c_compiler_get_error_limit_switch(c_compiler);
if (error_limit)
{
add_arg(error_limit);
}
}
if (options.flags.time_trace && c_compiler_supports_time_trace(c_compiler))
{
add_arg("-ftime-trace");
}
if (c_compiler == C_COMPILER_MSVC)
{
add_arg("/diagnostics:caret");
}
else
{
add_arg("-fdiagnostics-show-option");
}
add_arg(c_compiler_get_highest_c_standard_flag(c_compiler));
switch (options.windowing_backend)
{
case WINDOWING_BACKEND_NONE:
{
add_arg("-DBB_WINDOWING_BACKEND_NONE=1");
} break;
case WINDOWING_BACKEND_WIN32:
{
add_arg("-DBB_WINDOWING_BACKEND_WIN32=1");
} break;
case WINDOWING_BACKEND_COCOA:
{
add_arg("-DBB_WINDOWING_BACKEND_COCOA=1");
} break;
case WINDOWING_BACKEND_X11:
{
add_arg("-DBB_WINDOWING_BACKEND_X11=1");
} break;
case WINDOWING_BACKEND_WAYLAND:
{
add_arg("-DBB_WINDOWING_BACKEND_WAYLAND=1");
} break;
case WINDOWING_BACKEND_COUNT: unreachable();
}
switch (options.rendering_backend)
{
case RENDERING_BACKEND_NONE:
{
add_arg("-DBB_RENDERING_BACKEND_NONE=1");
} break;
case RENDERING_BACKEND_METAL:
{
add_arg("-DBB_RENDERING_BACKEND_METAL=1");
} break;
case RENDERING_BACKEND_DIRECTX12:
{
add_arg("-DBB_RENDERING_BACKEND_DIRECTX12=1");
} break;
case RENDERING_BACKEND_VULKAN:
{
add_arg("-DBB_RENDERING_BACKEND_VULKAN=1");
#if _WIN32
char* vk_sdk_path = getenv("VK_SDK_PATH");
if (vk_sdk_path)
{
if (c_compiler == C_COMPILER_MSVC)
{
String strings[] = {
strlit("-I"),
cstr(vk_sdk_path),
strlit("\\Include"),
};
String arg = arena_join_string(arena, (Slice(String))array_to_slice(strings));
add_arg(string_to_c(arg));
}
else
{
todo();
}
}
else
{
print("VK_SDK_PATH environment variable not found\n");
}
#endif
} break;
case RENDERING_BACKEND_COUNT: unreachable();
}
#ifndef _WIN32
add_arg("-lm");
#endif
switch (options.windowing_backend)
{
case WINDOWING_BACKEND_NONE:
{
} break;
case WINDOWING_BACKEND_WIN32:
{
} break;
case WINDOWING_BACKEND_COCOA:
{
add_arg("-framework");
add_arg("AppKit");
} break;
case WINDOWING_BACKEND_X11:
{
add_arg("-lxcb");
} break;
case WINDOWING_BACKEND_WAYLAND:
{
} break;
case WINDOWING_BACKEND_COUNT: unreachable();
}
switch (options.rendering_backend)
{
case RENDERING_BACKEND_NONE:
{
} break;
case RENDERING_BACKEND_METAL:
{
add_arg("-framework");
add_arg("Metal");
add_arg("-framework");
add_arg("QuartzCore");
} break;
case RENDERING_BACKEND_DIRECTX12:
{
} break;
case RENDERING_BACKEND_VULKAN:
{
#if __APPLE__
add_arg("-framework");
add_arg("QuartzCore");
#endif
} break;
case RENDERING_BACKEND_COUNT: unreachable();
}
add_arg(0);
CStringSlice arguments = { .pointer = args, .length = arg_i };
RunCommandOptions run_options = {
.debug = options.flags.debug,
};
run_command(arena, arguments, environment_pointer, run_options);
}
int main(int argc, char* argv[], char** envp)
{
environment_pointer = envp;
Arena* arena = arena_initialize_default(KB(64));
CompileOptions compile_options = {
.compiler_path = get_c_compiler_path(arena),
.source_path = strlit("bootstrap/bloat-buster/bb.c"),
.output_path = strlit("cache/bb" EXECUTABLE_EXTENSION),
.windowing_backend = windowing_backend_pick(),
.rendering_backend = rendering_backend_pick(),
.build_type = build_type_pick(),
.flags = {
.colored_output = 1,
.error_limit = 1,
.debug = 1,
.time_trace = BB_TIMETRACE,
},
};
compile_program(arena, compile_options);
return 0;
}

42
build.sh Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -eu
if [[ -z "${BB_CI-}" ]]; then
BB_CI=0
fi
if [[ -z "${BB_BUILD_TYPE-}" ]]; then
BB_BUILD_TYPE=debug
fi
BUILD_DIR=cache
mkdir -p $BUILD_DIR
if [[ "${BB_CI}" == "0" ]]; then
glslangValidator -V bootstrap/std/shaders/rect.vert -o $BUILD_DIR/rect.vert.spv --quiet
glslangValidator -V bootstrap/std/shaders/rect.frag -o $BUILD_DIR/rect.frag.spv --quiet
fi
BUILD_OUT=$BUILD_DIR/build
C_COMPILER=cc
TIME_TRACE=1
BB_TIMETRACE=0
GCC_ARGS=
CLANG_ARGS=
TIME_TRACE_ARG=
if [[ $C_COMPILER == "clang"* ]]; then
CLANG_ARGS=-ferror-limit=1
if [[ "$TIME_TRACE" == "1" ]]; then
CLANG_ARGS="$CLANG_ARGS -ftime-trace"
BB_TIMETRACE=1
else
CLANG_ARGS="$CLANG_ARGS -ftime-trace"
fi
elif [[ $C_COMPILER == "gcc"* ]]; then
GCC_ARGS=-fmax-errors=1
fi
$C_COMPILER build.c -g -o $BUILD_OUT -Ibootstrap -std=gnu2x $CLANG_ARGS $GCC_ARGS -DBB_TIMETRACE=$BB_TIMETRACE -DBB_CI=$BB_CI -DBB_BUILD_TYPE=\"$BB_BUILD_TYPE\"
$BUILD_OUT $@
exit 0

View File

@ -1,48 +0,0 @@
# Usage:
# cmake -P GenerateMappings.cmake <path/to/mappings.h.in> <path/to/mappings.h>
set(source_url "https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt")
set(source_path "${CMAKE_CURRENT_BINARY_DIR}/gamecontrollerdb.txt")
set(template_path "${CMAKE_ARGV3}")
set(target_path "${CMAKE_ARGV4}")
if (NOT EXISTS "${template_path}")
message(FATAL_ERROR "Failed to find template file ${template_path}")
endif()
file(DOWNLOAD "${source_url}" "${source_path}"
STATUS download_status
TLS_VERIFY on)
list(GET download_status 0 status_code)
list(GET download_status 1 status_message)
if (status_code)
message(FATAL_ERROR "Failed to download ${source_url}: ${status_message}")
endif()
file(STRINGS "${source_path}" lines)
foreach(line ${lines})
if (line MATCHES "^[0-9a-fA-F]")
if (line MATCHES "platform:Windows")
if (GLFW_WIN32_MAPPINGS)
string(APPEND GLFW_WIN32_MAPPINGS "\n")
endif()
string(APPEND GLFW_WIN32_MAPPINGS "\"${line}\",")
elseif (line MATCHES "platform:Mac OS X")
if (GLFW_COCOA_MAPPINGS)
string(APPEND GLFW_COCOA_MAPPINGS "\n")
endif()
string(APPEND GLFW_COCOA_MAPPINGS "\"${line}\",")
elseif (line MATCHES "platform:Linux")
if (GLFW_LINUX_MAPPINGS)
string(APPEND GLFW_LINUX_MAPPINGS "\n")
endif()
string(APPEND GLFW_LINUX_MAPPINGS "\"${line}\",")
endif()
endif()
endforeach()
configure_file("${template_path}" "${target_path}" @ONLY NEWLINE_STYLE UNIX)
file(REMOVE "${source_path}")

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

View File

@ -1,29 +0,0 @@
if (NOT EXISTS "@GLFW_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: \"@GLFW_BINARY_DIR@/install_manifest.txt\"")
endif()
file(READ "@GLFW_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach (file ${files})
message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
if (EXISTS "$ENV{DESTDIR}${file}")
exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval)
if (NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
endif()
elseif (IS_SYMLINK "$ENV{DESTDIR}${file}")
EXEC_PROGRAM("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval)
if (NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing symlink \"$ENV{DESTDIR}${file}\"")
endif()
else()
message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
endif()
endforeach()

View File

@ -1,13 +0,0 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
Name: GLFW
Description: A multi-platform library for OpenGL, window and input
Version: @GLFW_VERSION@
URL: https://www.glfw.org/
Requires.private: @GLFW_PKG_CONFIG_REQUIRES_PRIVATE@
Libs: -L${libdir} -l@GLFW_LIB_NAME@@GLFW_LIB_NAME_SUFFIX@
Libs.private: @GLFW_PKG_CONFIG_LIBS_PRIVATE@
Cflags: -I${includedir}

View File

@ -1,3 +0,0 @@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake")

View File

@ -1,13 +0,0 @@
# Define the environment for cross-compiling with 32-bit MinGW-w64 Clang
SET(CMAKE_SYSTEM_NAME Windows) # Target system name
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_C_COMPILER "i686-w64-mingw32-clang")
SET(CMAKE_CXX_COMPILER "i686-w64-mingw32-clang++")
SET(CMAKE_RC_COMPILER "i686-w64-mingw32-windres")
SET(CMAKE_RANLIB "i686-w64-mingw32-ranlib")
# Configure the behaviour of the find commands
SET(CMAKE_FIND_ROOT_PATH "/usr/i686-w64-mingw32")
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -1,13 +0,0 @@
# Define the environment for cross-compiling with 32-bit MinGW-w64 GCC
SET(CMAKE_SYSTEM_NAME Windows) # Target system name
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_C_COMPILER "i686-w64-mingw32-gcc")
SET(CMAKE_CXX_COMPILER "i686-w64-mingw32-g++")
SET(CMAKE_RC_COMPILER "i686-w64-mingw32-windres")
SET(CMAKE_RANLIB "i686-w64-mingw32-ranlib")
# Configure the behaviour of the find commands
SET(CMAKE_FIND_ROOT_PATH "/usr/i686-w64-mingw32")
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -1,17 +0,0 @@
# Find EpollShim
# Once done, this will define
#
# EPOLLSHIM_FOUND - System has EpollShim
# EPOLLSHIM_INCLUDE_DIRS - The EpollShim include directories
# EPOLLSHIM_LIBRARIES - The libraries needed to use EpollShim
find_path(EPOLLSHIM_INCLUDE_DIRS NAMES sys/epoll.h sys/timerfd.h HINTS /usr/local/include/libepoll-shim)
find_library(EPOLLSHIM_LIBRARIES NAMES epoll-shim libepoll-shim HINTS /usr/local/lib)
if (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
set(EPOLLSHIM_FOUND TRUE)
endif (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(EpollShim DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS)
mark_as_advanced(EPOLLSHIM_INCLUDE_DIRS EPOLLSHIM_LIBRARIES)

View File

@ -1,18 +0,0 @@
# Try to find OSMesa on a Unix system
#
# This will define:
#
# OSMESA_LIBRARIES - Link these to use OSMesa
# OSMESA_INCLUDE_DIR - Include directory for OSMesa
#
# Copyright (c) 2014 Brandon Schaefer <brandon.schaefer@canonical.com>
if (NOT WIN32)
find_package (PkgConfig)
pkg_check_modules (PKG_OSMESA QUIET osmesa)
set (OSMESA_INCLUDE_DIR ${PKG_OSMESA_INCLUDE_DIRS})
set (OSMESA_LIBRARIES ${PKG_OSMESA_LIBRARIES})
endif ()

View File

@ -1,13 +0,0 @@
# Define the environment for cross-compiling with 64-bit MinGW-w64 Clang
SET(CMAKE_SYSTEM_NAME Windows) # Target system name
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_C_COMPILER "x86_64-w64-mingw32-clang")
SET(CMAKE_CXX_COMPILER "x86_64-w64-mingw32-clang++")
SET(CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres")
SET(CMAKE_RANLIB "x86_64-w64-mingw32-ranlib")
# Configure the behaviour of the find commands
SET(CMAKE_FIND_ROOT_PATH "/usr/x86_64-w64-mingw32")
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -1,13 +0,0 @@
# Define the environment for cross-compiling with 64-bit MinGW-w64 GCC
SET(CMAKE_SYSTEM_NAME Windows) # Target system name
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_C_COMPILER "x86_64-w64-mingw32-gcc")
SET(CMAKE_CXX_COMPILER "x86_64-w64-mingw32-g++")
SET(CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres")
SET(CMAKE_RANLIB "x86_64-w64-mingw32-ranlib")
# Configure the behaviour of the find commands
SET(CMAKE_FIND_ROOT_PATH "/usr/x86_64-w64-mingw32")
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -1,165 +0,0 @@
cmake_minimum_required(VERSION 3.4...3.28 FATAL_ERROR)
project(GLFW VERSION 3.4.0 LANGUAGES C)
if (POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
endif()
if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
string(COMPARE EQUAL "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}" GLFW_STANDALONE)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ${GLFW_STANDALONE})
option(GLFW_BUILD_TESTS "Build the GLFW test programs" ${GLFW_STANDALONE})
option(GLFW_BUILD_DOCS "Build the GLFW documentation" ON)
option(GLFW_INSTALL "Generate installation target" ON)
include(GNUInstallDirs)
include(CMakeDependentOption)
if (GLFW_USE_OSMESA)
message(FATAL_ERROR "GLFW_USE_OSMESA has been removed; set the GLFW_PLATFORM init hint")
endif()
if (DEFINED GLFW_USE_WAYLAND AND UNIX AND NOT APPLE)
message(FATAL_ERROR
"GLFW_USE_WAYLAND has been removed; delete the CMake cache and set GLFW_BUILD_WAYLAND and GLFW_BUILD_X11 instead")
endif()
cmake_dependent_option(GLFW_BUILD_WIN32 "Build support for Win32" ON "WIN32" OFF)
cmake_dependent_option(GLFW_BUILD_COCOA "Build support for Cocoa" ON "APPLE" OFF)
cmake_dependent_option(GLFW_BUILD_X11 "Build support for X11" ON "UNIX;NOT APPLE" OFF)
cmake_dependent_option(GLFW_BUILD_WAYLAND "Build support for Wayland" ON "UNIX;NOT APPLE" OFF)
cmake_dependent_option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF
"WIN32" OFF)
cmake_dependent_option(USE_MSVC_RUNTIME_LIBRARY_DLL "Use MSVC runtime library DLL" ON
"MSVC" OFF)
set(GLFW_LIBRARY_TYPE "${GLFW_LIBRARY_TYPE}" CACHE STRING
"Library type override for GLFW (SHARED, STATIC, OBJECT, or empty to follow BUILD_SHARED_LIBS)")
if (GLFW_LIBRARY_TYPE)
if (GLFW_LIBRARY_TYPE STREQUAL "SHARED")
set(GLFW_BUILD_SHARED_LIBRARY TRUE)
else()
set(GLFW_BUILD_SHARED_LIBRARY FALSE)
endif()
else()
set(GLFW_BUILD_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
endif()
list(APPEND CMAKE_MODULE_PATH "${GLFW_SOURCE_DIR}/CMake/modules")
find_package(Threads REQUIRED)
#--------------------------------------------------------------------
# Report backend selection
#--------------------------------------------------------------------
if (GLFW_BUILD_WIN32)
message(STATUS "Including Win32 support")
endif()
if (GLFW_BUILD_COCOA)
message(STATUS "Including Cocoa support")
endif()
if (GLFW_BUILD_WAYLAND)
message(STATUS "Including Wayland support")
endif()
if (GLFW_BUILD_X11)
message(STATUS "Including X11 support")
endif()
#--------------------------------------------------------------------
# Apply Microsoft C runtime library option
# This is here because it also applies to tests and examples
#--------------------------------------------------------------------
if (MSVC AND NOT USE_MSVC_RUNTIME_LIBRARY_DLL)
if (CMAKE_VERSION VERSION_LESS 3.15)
foreach (flag CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELWITHDEBINFO)
if (flag MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
endif()
if (flag MATCHES "/MDd")
string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
endif()
endforeach()
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()
#--------------------------------------------------------------------
# Create generated files
#--------------------------------------------------------------------
include(CMakePackageConfigHelpers)
set(GLFW_CONFIG_PATH "${CMAKE_INSTALL_LIBDIR}/cmake/glfw3")
configure_package_config_file(CMake/glfw3Config.cmake.in
src/glfw3Config.cmake
INSTALL_DESTINATION "${GLFW_CONFIG_PATH}"
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(src/glfw3ConfigVersion.cmake
VERSION ${GLFW_VERSION}
COMPATIBILITY SameMajorVersion)
#--------------------------------------------------------------------
# Add subdirectories
#--------------------------------------------------------------------
add_subdirectory(src)
if (GLFW_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if (GLFW_BUILD_TESTS)
add_subdirectory(tests)
endif()
if (GLFW_BUILD_DOCS)
add_subdirectory(docs)
endif()
#--------------------------------------------------------------------
# Install files other than the library
# The library is installed by src/CMakeLists.txt
#--------------------------------------------------------------------
if (GLFW_INSTALL)
install(DIRECTORY include/GLFW DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h)
install(FILES "${GLFW_BINARY_DIR}/src/glfw3Config.cmake"
"${GLFW_BINARY_DIR}/src/glfw3ConfigVersion.cmake"
DESTINATION "${GLFW_CONFIG_PATH}")
install(EXPORT glfwTargets FILE glfw3Targets.cmake
EXPORT_LINK_INTERFACE_LIBRARIES
DESTINATION "${GLFW_CONFIG_PATH}")
install(FILES "${GLFW_BINARY_DIR}/src/glfw3.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
# Only generate this target if no higher-level project already has
if (NOT TARGET uninstall)
configure_file(CMake/cmake_uninstall.cmake.in
cmake_uninstall.cmake IMMEDIATE @ONLY)
add_custom_target(uninstall
"${CMAKE_COMMAND}" -P
"${GLFW_BINARY_DIR}/cmake_uninstall.cmake")
set_target_properties(uninstall PROPERTIES FOLDER "GLFW3")
endif()
endif()

View File

@ -1,297 +0,0 @@
# Acknowledgements
GLFW exists because people around the world donated their time and lent their
skills. This list only includes contributions to the main repository and
excludes other invaluable contributions like language bindings and text and
video tutorials.
- Bobyshev Alexander
- Laurent Aphecetche
- Matt Arsenault
- Takuro Ashie
- ashishgamedev
- David Avedissian
- Luca Bacci
- Keith Bauer
- John Bartholomew
- Coşku Baş
- Bayemite
- Niklas Behrens
- Andrew Belt
- Nevyn Bengtsson
- Niklas Bergström
- Denis Bernard
- BiBi
- Doug Binks
- blanco
- Waris Boonyasiriwat
- Kyle Brenneman
- Rok Breulj
- TheBrokenRail
- Kai Burjack
- Martin Capitanio
- Nicolas Caramelli
- David Carlier
- Arturo Castro
- Chi-kwan Chan
- Victor Chernyakin
- TheChocolateOre
- Ali Chraghi
- Joseph Chua
- Ian Clarkson
- Michał Cichoń
- Lambert Clara
- Anna Clarke
- Josh Codd
- Yaron Cohen-Tal
- Omar Cornut
- Andrew Corrigan
- Bailey Cosier
- Noel Cower
- CuriouserThing
- Bill Currie
- Jason Daly
- danhambleton
- Jarrod Davis
- Olivier Delannoy
- Paul R. Deppe
- Michael Dickens
- Роман Донченко
- Mario Dorn
- Wolfgang Draxinger
- Jonathan Dummer
- Ralph Eastwood
- Fredrik Ehnbom
- Robin Eklind
- Jan Ekström
- Siavash Eliasi
- Ahmad Fatoum
- Nikita Fediuchin
- Felipe Ferreira
- Michael Fogleman
- forworldm
- Jason Francis
- Gerald Franz
- Mário Freitas
- GeO4d
- Marcus Geelnard
- Gegy
- ghuser404
- Charles Giessen
- Ryan C. Gordon
- Stephen Gowen
- Kovid Goyal
- Kevin Grandemange
- Eloi Marín Gratacós
- Grzesiek11
- Stefan Gustavson
- Andrew Gutekanst
- Stephen Gutekanst
- Jonathan Hale
- Daniel Hauser
- hdf89shfdfs
- Moritz Heinemann
- Sylvain Hellegouarch
- Björn Hempel
- Matthew Henry
- heromyth
- Lucas Hinderberger
- Paul Holden
- Hajime Hoshi
- Warren Hu
- Charles Huber
- Brent Huisman
- Florian Hülsmann
- illustris
- InKryption
- IntellectualKitty
- Aaron Jacobs
- JannikGM
- Erik S. V. Jansson
- jjYBdx4IL
- Peter Johnson
- Toni Jovanoski
- Arseny Kapoulkine
- Cem Karan
- Osman Keskin
- Koray Kilinc
- Josh Kilmer
- Byunghoon Kim
- Cameron King
- Peter Knut
- Christoph Kubisch
- Yuri Kunde Schlesner
- Rokas Kupstys
- Konstantin Käfer
- Eric Larson
- Guillaume Lebrun
- Francis Lecavalier
- Jong Won Lee
- Robin Leffmann
- Glenn Lewis
- Shane Liesegang
- Anders Lindqvist
- Leon Linhart
- Marco Lizza
- lo-v-ol
- Eyal Lotem
- Aaron Loucks
- Ned Loynd
- Luflosi
- lukect
- Tristam MacDonald
- Jean-Luc Mackail
- Hans Mackowiak
- Ramiro Magno
- Дмитри Малышев
- Zbigniew Mandziejewicz
- Adam Marcus
- Célestin Marot
- Kyle McDonald
- David V. McKay
- David Medlock
- Bryce Mehring
- Jonathan Mercier
- Marcel Metz
- Liam Middlebrook
- mightgoyardstill
- Ave Milia
- Icyllis Milica
- Jonathan Miller
- Kenneth Miller
- Bruce Mitchener
- Jack Moffitt
- Ravi Mohan
- Jeff Molofee
- Alexander Monakov
- Pierre Morel
- Jon Morton
- Pierre Moulon
- Martins Mozeiko
- Pascal Muetschard
- James Murphy
- Julian Møller
- Julius Häger
- Nat!
- NateIsStalling
- ndogxj
- F. Nedelec
- n3rdopolis
- Kristian Nielsen
- Joel Niemelä
- Victor Nova
- Kamil Nowakowski
- onox
- Denis Ovod
- Ozzy
- Andri Pálsson
- luz paz
- Peoro
- Braden Pellett
- Christopher Pelloux
- Michael Pennington
- Arturo J. Pérez
- Vladimir Perminov
- Olivier Perret
- Anthony Pesch
- Orson Peters
- Emmanuel Gil Peyrot
- Cyril Pichard
- Pilzschaf
- Keith Pitt
- Stanislav Podgorskiy
- Konstantin Podsvirov
- Nathan Poirier
- Pokechu22
- Alexandre Pretyman
- Pablo Prietz
- przemekmirek
- pthom
- Martin Pulec
- Guillaume Racicot
- Juan Ramos
- Christian Rauch
- Philip Rideout
- Eddie Ringle
- Max Risuhin
- Joe Roback
- Jorge Rodriguez
- Jari Ronkainen
- Luca Rood
- Ed Ropple
- Aleksey Rybalkin
- Mikko Rytkönen
- Riku Salminen
- Yoshinori Sano
- Brandon Schaefer
- Sebastian Schuberth
- Scr3amer
- Jan Schuerkamp
- Christian Sdunek
- Matt Sealey
- Steve Sexton
- Arkady Shapkin
- Mingjie Shen
- Ali Sherief
- Yoshiki Shibukawa
- Dmitri Shuralyov
- Joao da Silva
- Daniel Sieger
- Daljit Singh
- Michael Skec
- Daniel Skorupski
- Slemmie
- Anthony Smith
- Bradley Smith
- Cliff Smolinsky
- Patrick Snape
- Erlend Sogge Heggen
- Olivier Sohn
- Julian Squires
- Johannes Stein
- Pontus Stenetorp
- Michael Stocker
- Justin Stoecker
- Elviss Strazdins
- Paul Sultana
- Nathan Sweet
- TTK-Bandit
- Nuno Teixeira
- Jared Tiala
- Sergey Tikhomirov
- Arthur Tombs
- TronicLabs
- Ioannis Tsakpinis
- Samuli Tuomola
- Matthew Turner
- urraka
- Elias Vanderstuyft
- Stef Velzel
- Jari Vetoniemi
- Ricardo Vieira
- Nicholas Vitovitch
- Vladimír Vondruš
- Simon Voordouw
- Corentin Wallez
- Torsten Walluhn
- Patrick Walton
- Jim Wang
- Xo Wang
- Andre Weissflog
- Jay Weisskopf
- Frank Wille
- Andy Williams
- Joel Winarske
- Richard A. Wilkes
- Tatsuya Yatagawa
- Ryogo Yoshimura
- Lukas Zanner
- Andrey Zholos
- Aihui Zhu
- Santi Zupancic
- Jonas Ådahl
- Lasse Öörni
- Leonard König
- All the unmentioned and anonymous contributors in the GLFW community, for bug
reports, patches, feedback, testing and encouragement

View File

@ -1,23 +0,0 @@
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would
be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

View File

@ -1,458 +0,0 @@
# GLFW
[![Build status](https://github.com/glfw/glfw/actions/workflows/build.yml/badge.svg)](https://github.com/glfw/glfw/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/0kf0ct9831i5l6sp/branch/master?svg=true)](https://ci.appveyor.com/project/elmindreda/glfw)
## Introduction
GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan
application development. It provides a simple, platform-independent API for
creating windows, contexts and surfaces, reading input, handling events, etc.
GLFW natively supports Windows, macOS and Linux and other Unix-like systems. On
Linux both Wayland and X11 are supported.
GLFW is licensed under the [zlib/libpng
license](https://www.glfw.org/license.html).
You can [download](https://www.glfw.org/download.html) the latest stable release
as source or Windows binaries. Each release starting with 3.0 also has
a corresponding [annotated tag](https://github.com/glfw/glfw/releases) with
source and binary archives.
The [documentation](https://www.glfw.org/docs/latest/) is available online and is
included in all source and binary archives. See the [release
notes](https://www.glfw.org/docs/latest/news.html) for new features, caveats and
deprecations in the latest release. For more details see the [version
history](https://www.glfw.org/changelog.html).
The `master` branch is the stable integration branch and _should_ always compile
and run on all supported platforms, although details of newly added features may
change until they have been included in a release. New features and many bug
fixes live in [other branches](https://github.com/glfw/glfw/branches/all) until
they are stable enough to merge.
If you are new to GLFW, you may find the
[tutorial](https://www.glfw.org/docs/latest/quick.html) for GLFW 3 useful. If
you have used GLFW 2 in the past, there is a [transition
guide](https://www.glfw.org/docs/latest/moving.html) for moving to the GLFW
3 API.
GLFW exists because of the contributions of [many people](CONTRIBUTORS.md)
around the world, whether by reporting bugs, providing community support, adding
features, reviewing or testing code, debugging, proofreading docs, suggesting
features or fixing bugs.
## Compiling GLFW
GLFW is written primarily in C99, with parts of macOS support being written in
Objective-C. GLFW itself requires only the headers and libraries for your OS
and window system. It does not need any additional headers for context creation
APIs (WGL, GLX, EGL, NSGL, OSMesa) or rendering APIs (OpenGL, OpenGL ES, Vulkan)
to enable support for them.
GLFW supports compilation on Windows with Visual C++ 2013 and later, MinGW and
MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC
and Clang. It will likely compile in other environments as well, but this is
not regularly tested.
There are [pre-compiled binaries](https://www.glfw.org/download.html) available
for all supported compilers on Windows and macOS.
See the [compilation guide](https://www.glfw.org/docs/latest/compile.html) for
more information about how to compile GLFW yourself.
## Using GLFW
See the [documentation](https://www.glfw.org/docs/latest/) for tutorials, guides
and the API reference.
## Contributing to GLFW
See the [contribution
guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
more information.
## System requirements
GLFW supports Windows XP and later and macOS 10.8 and later. Linux and other
Unix-like systems running the X Window System are supported even without
a desktop environment or modern extensions, although some features require
a running window or clipboard manager. The OSMesa backend requires Mesa 6.3.
See the [compatibility guide](https://www.glfw.org/docs/latest/compat.html)
in the documentation for more information.
## Dependencies
GLFW itself needs only CMake 3.1 or later and the headers and libraries for your
OS and window system.
The examples and test programs depend on a number of tiny libraries. These are
located in the `deps/` directory.
- [getopt\_port](https://github.com/kimgr/getopt_port/) for examples
with command-line options
- [TinyCThread](https://github.com/tinycthread/tinycthread) for threaded
examples
- [glad2](https://github.com/Dav1dde/glad) for loading OpenGL and Vulkan
functions
- [linmath.h](https://github.com/datenwolf/linmath.h) for linear algebra in
examples
- [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) for test and example UI
- [stb\_image\_write](https://github.com/nothings/stb) for writing images to disk
The documentation is generated with [Doxygen](https://doxygen.org/) if CMake can
find that tool.
## Reporting bugs
Bugs are reported to our [issue tracker](https://github.com/glfw/glfw/issues).
Please check the [contribution
guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
information on what to include when reporting a bug.
## Changelog since 3.3.10
- Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958)
- Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
`GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to
specify the desired platform (#1958)
- Added `glfwGetPlatform` function to query what platform was selected (#1655,#1958)
- Added `glfwPlatformSupported` function to query if a platform is supported
(#1655,#1958)
- Added `glfwInitAllocator` for setting a custom memory allocator (#544,#1628,#1947)
- Added `GLFWallocator` struct and `GLFWallocatefun`, `GLFWreallocatefun` and
`GLFWdeallocatefun` types (#544,#1628,#1947)
- Added `glfwGetWindowTitle` function for querying window title (#1448,#1909,#2482)
- Added `glfwInitVulkanLoader` for using a non-default Vulkan loader (#1374,#1890)
- Added `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR`,
`GLFW_RESIZE_ALL_CURSOR` and `GLFW_NOT_ALLOWED_CURSOR` cursor shapes (#427)
- Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427)
- Added `GLFW_RESIZE_NS_CURSOR` alias for `GLFW_VRESIZE_CURSOR` (#427)
- Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427)
- Added `GLFW_MOUSE_PASSTHROUGH` window hint for letting mouse input pass
through the window (#1236,#1568)
- Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window
content area (#58)
- Added `GLFW_POSITION_X` and `GLFW_POSITION_Y` window hints for initial position
(#1603,#1747)
- Added `GLFW_SCALE_FRAMEBUFFER` window hint for Wayland and macOS scaling (#2457)
- Added `GLFW_ANY_POSITION` hint value for letting the window manager choose (#1603,#1747)
- Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958)
- Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692)
- Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692)
- Added `GLFW_WAYLAND_APP_ID` window hint string for Wayland app\_id selection
(#2121,#2122)
- Added `GLFW_ANGLE_PLATFORM_TYPE` init hint and `GLFW_ANGLE_PLATFORM_TYPE_*`
values to select ANGLE backend (#1380)
- Added `GLFW_X11_XCB_VULKAN_SURFACE` init hint for selecting X11 Vulkan
surface extension (#1793)
- Added `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access to the window menu
- Added `GLFW_WIN32_SHOWDEFAULT` window hint for applying the parent process
show command (#2359)
- Added `GLFW_NATIVE_INCLUDE_NONE` for disabling inclusion of native headers (#1348)
- Added `GLFW_BUILD_WIN32` CMake option for enabling Win32 support (#1958)
- Added `GLFW_BUILD_COCOA` CMake option for enabling Cocoa support (#1958)
- Added `GLFW_BUILD_X11` CMake option for enabling X11 support (#1958)
- Added `GLFW_LIBRARY_TYPE` CMake variable for overriding the library type
(#279,#1307,#1497,#1574,#1928)
- Added support for `XDG_SESSION_TYPE` environment variable
- Added `GLFW_PKG_CONFIG_REQUIRES_PRIVATE` and `GLFW_PKG_CONFIG_LIBS_PRIVATE` CMake
variables exposing pkg-config dependencies (#1307)
- Made joystick subsystem initialize at first use (#1284,#1646)
- Made `GLFW_DOUBLEBUFFER` a read-only window attribute
- Made Wayland the preferred platform over X11 if both are available (#2035)
- Updated the minimum required CMake version to 3.4
- Updated gamepad mappings from upstream
- Renamed `GLFW_USE_WAYLAND` CMake option to `GLFW_BUILD_WAYLAND` (#1958)
- Disabled tests and examples by default when built as a CMake subdirectory
- Removed `GLFW_USE_OSMESA` CMake option enabling the Null platform (#1958)
- Removed CMake generated configuration header
- Bugfix: `glfwGetVideoMode` returned an invalid mode on error (#1292)
- [Win32] Added a version info resource to the GLFW DLL
- [Win32] Made hidden helper window use its own window class
- [Win32] Bugfix: The foreground lock timeout was overridden, ignoring the user
- [Cocoa] Added `glfwGetCocoaView` native access function (#2235)
- [Cocoa] Moved main menu creation to GLFW initialization time (#1649)
- [Cocoa] Bugfix: Touching event queue from secondary thread before main thread
would abort (#1649)
- [Wayland] Added support for `glfwRequestWindowAttention` (#2287)
- [Wayland] Added support for `glfwFocusWindow`
- [Wayland] Added support for `GLFW_RESIZABLE` (#2203)
- [Wayland] Added support for fractional scaling of window contents
- [Wayland] Added dynamic loading of all Wayland libraries
- [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled
- [Wayland] Bugfix: `GLFW_HOVERED` was true when the cursor was over any
fallback window decoration
- [Wayland] Bugfix: Fallback decorations allowed resizing to invalid size
(#2204)
- [X11] Bugfix: Termination would segfault if the IM had been destroyed
- [X11] Bugfix: Any IM started after initialization would not be detected
- [Linux] Bugfix: Joystick evdev fds remained open in forks (#2446)
- [POSIX] Removed use of deprecated function `gettimeofday`
- [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled
- [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072)
- [NSGL] Removed enforcement of forward-compatible flag for core contexts
- [NSGL] Bugfix: A core profile OpenGL context was returned if 3.2+
compatibility profile was requested
- [EGL] Added platform selection via the `EGL_EXT_platform_base` extension
(#442)
- [EGL] Added ANGLE backend selection via `EGL_ANGLE_platform_angle` extension
(#1380)
## Changelog since 3.3
- Added `GLFW_WAYLAND_LIBDECOR` init hint for disabling libdecor support (#1639,#1693)
- Bugfix: The CMake config-file package used an absolute path and was not
relocatable (#1470)
- Bugfix: Video modes with a duplicate screen area were discarded (#1555,#1556)
- Bugfix: Compiling with -Wextra-semi caused warnings (#1440)
- Bugfix: Built-in mappings failed because some OEMs re-used VID/PID (#1583)
- Bugfix: Some extension loader headers did not prevent default OpenGL header
inclusion (#1695)
- Bugfix: Buffers were swapped at creation on single-buffered windows (#1873)
- Bugfix: Gamepad mapping updates could spam `GLFW_INVALID_VALUE` due to
incompatible controllers sharing hardware ID (#1763)
- Bugfix: Native access functions for context handles did not check that the API matched
- Bugfix: `glfwMakeContextCurrent` would access TLS slot before initialization
- Bugfix: `glfwSetGammaRamp` could emit `GLFW_INVALID_VALUE` before initialization
- Bugfix: `glfwGetJoystickUserPointer` returned `NULL` during disconnection (#2092)
- Bugfix: `glfwGetKeyScancode` returned `0` on error when initialized instead of `-1`
- Bugfix: Failure to make a newly created context current could cause segfault (#2327)
- [Win32] Disabled framebuffer transparency on Windows 7 when DWM windows are
opaque (#1512)
- [Win32] Bugfix: `GLFW_INCLUDE_VULKAN` plus `VK_USE_PLATFORM_WIN32_KHR` caused
symbol redefinition (#1524)
- [Win32] Bugfix: The cursor position event was emitted before its cursor enter
event (#1490)
- [Win32] Bugfix: The window hint `GLFW_MAXIMIZED` did not move or resize the
window (#1499)
- [Win32] Bugfix: Disabled cursor mode interfered with some non-client actions
- [Win32] Bugfix: Super key was not released after Win+V hotkey (#1622)
- [Win32] Bugfix: `glfwGetKeyName` could access out of bounds and return an
invalid pointer
- [Win32] Bugfix: Some synthetic key events were reported as `GLFW_KEY_UNKNOWN`
(#1623)
- [Win32] Bugfix: Non-BMP Unicode codepoint input was reported as UTF-16
- [Win32] Bugfix: Monitor functions could return invalid values after
configuration change (#1761)
- [Win32] Bugfix: Initialization would segfault on Windows 8 (not 8.1) (#1775)
- [Win32] Bugfix: Duplicate size events were not filtered (#1610)
- [Win32] Bugfix: Full screen windows were incorrectly resized by DPI changes
(#1582)
- [Win32] Bugfix: `GLFW_SCALE_TO_MONITOR` had no effect on systems older than
Windows 10 version 1703 (#1511)
- [Win32] Bugfix: `USE_MSVC_RUNTIME_LIBRARY_DLL` had no effect on CMake 3.15 or
later (#1783,#1796)
- [Win32] Bugfix: Compilation with LLVM for Windows failed (#1807,#1824,#1874)
- [Win32] Bugfix: Content scale queries could fail silently (#1615)
- [Win32] Bugfix: Content scales could have garbage values if monitor was recently
disconnected (#1615)
- [Win32] Bugfix: A window created maximized and undecorated would cover the whole
monitor (#1806)
- [Win32] Bugfix: The default restored window position was lost when creating a maximized
window
- [Win32] Bugfix: `glfwMaximizeWindow` would make a hidden window visible
- [Win32] Bugfix: `Alt+PrtSc` would emit `GLFW_KEY_UNKNOWN` and a different
scancode than `PrtSc` (#1993)
- [Win32] Bugfix: `GLFW_KEY_PAUSE` scancode from `glfwGetKeyScancode` did not
match event scancode (#1993)
- [Win32] Bugfix: Instance-local operations used executable instance (#469,#1296,#1395)
- [Win32] Bugfix: The OSMesa library was not unloaded on termination
- [Win32] Bugfix: Right shift emitted `GLFW_KEY_UNKNOWN` when using a CJK IME (#2050)
- [Win32] Bugfix: `glfwWaitEventsTimeout` did not return for some sent messages (#2408)
- [Win32] Bugfix: Fix pkg-config for dynamic library on Windows (#2386, #2420)
- [Win32] Bugfix: XInput could reportedly provide invalid DPad bit masks (#2291)
- [Win32] Bugfix: Rapid clipboard calls could fail due to Clipboard History
- [Win32] Bugfix: Disabled cursor mode doesn't work right when connected over RDP (#1276,#1279,#2431)
- [Cocoa] Added support for `VK_EXT_metal_surface` (#1619)
- [Cocoa] Added locating the Vulkan loader at runtime in an application bundle
- [Cocoa] Changed `EGLNativeWindowType` from `NSView` to `CALayer` (#1169)
- [Cocoa] Changed F13 key to report Print Screen for cross-platform consistency
(#1786)
- [Cocoa] Disabled macOS fullscreen when `GLFW_RESIZABLE` is false
- [Cocoa] Removed dependency on the CoreVideo framework
- [Cocoa] Bugfix: `glfwSetWindowSize` used a bottom-left anchor point (#1553)
- [Cocoa] Bugfix: Window remained on screen after destruction until event poll
(#1412)
- [Cocoa] Bugfix: Event processing before window creation would assert (#1543)
- [Cocoa] Bugfix: Undecorated windows could not be iconified on recent macOS
- [Cocoa] Bugfix: Non-BMP Unicode codepoint input was reported as UTF-16
(#1635)
- [Cocoa] Bugfix: Failing to retrieve the refresh rate of built-in displays
could leak memory
- [Cocoa] Bugfix: Objective-C files were compiled as C with CMake 3.19 (#1787)
- [Cocoa] Bugfix: Duplicate video modes were not filtered out (#1830)
- [Cocoa] Bugfix: Menu bar was not clickable on macOS 10.15+ until it lost and
regained focus (#1648,#1802)
- [Cocoa] Bugfix: Monitor name query could segfault on macOS 11 (#1809,#1833)
- [Cocoa] Bugfix: The install name of the installed dylib was relative (#1504)
- [Cocoa] Bugfix: The MoltenVK layer contents scale was updated only after
related events were emitted
- [Cocoa] Bugfix: Moving the cursor programmatically would freeze it for
a fraction of a second (#1962)
- [Cocoa] Bugfix: `kIOMasterPortDefault` was deprecated in macOS 12.0 (#1980)
- [Cocoa] Bugfix: `kUTTypeURL` was deprecated in macOS 12.0 (#2003)
- [Cocoa] Bugfix: A connected Apple AirPlay would emit a useless error (#1791)
- [Cocoa] Bugfix: The EGL and OSMesa libraries were not unloaded on termination
- [Cocoa] Bugfix: `GLFW_MAXIMIZED` was always true when `GLFW_RESIZABLE` was false
- [Cocoa] Bugfix: Changing `GLFW_DECORATED` in macOS fullscreen would abort
application (#1886)
- [Cocoa] Bugfix: Setting a monitor from macOS fullscreen would abort
application (#2110)
- [Cocoa] Bugfix: The Vulkan loader was not loaded from the `Frameworks` bundle
subdirectory (#2113,#2120)
- [Cocoa] Bugfix: Compilation failed on OS X 10.8 due to unconditional use of 10.9+
symbols (#2161)
- [Cocoa] Bugfix: Querying joystick elements could reportedly segfault on macOS
13 Ventura (#2320)
- [X11] Bugfix: The CMake files did not check for the XInput headers (#1480)
- [X11] Bugfix: Key names were not updated when the keyboard layout changed
(#1462,#1528)
- [X11] Bugfix: Decorations could not be enabled after window creation (#1566)
- [X11] Bugfix: Content scale fallback value could be inconsistent (#1578)
- [X11] Bugfix: `glfwMaximizeWindow` had no effect on hidden windows
- [X11] Bugfix: Clearing `GLFW_FLOATING` on a hidden window caused invalid read
- [X11] Bugfix: Changing `GLFW_FLOATING` on a hidden window could silently fail
- [X11] Bugfix: Disabled cursor mode was interrupted by indicator windows
- [X11] Bugfix: Monitor physical dimensions could be reported as zero mm
- [X11] Bugfix: Window position events were not emitted during resizing (#1613)
- [X11] Bugfix: `glfwFocusWindow` could terminate on older WMs or without a WM
- [X11] Bugfix: Querying a disconnected monitor could segfault (#1602)
- [X11] Bugfix: IME input of CJK was broken for "C" locale (#1587,#1636)
- [X11] Bugfix: Xlib errors caused by other parts of the application could be
reported as GLFW errors
- [X11] Bugfix: A handle race condition could cause a `BadWindow` error (#1633)
- [X11] Bugfix: XKB path used keysyms instead of physical locations for
non-printable keys (#1598)
- [X11] Bugfix: Function keys were mapped to `GLFW_KEY_UNKNOWN` for some layout
combinations (#1598)
- [X11] Bugfix: Keys pressed simultaneously with others were not always
reported (#1112,#1415,#1472,#1616)
- [X11] Bugfix: Some window attributes were not applied on leaving fullscreen
(#1863)
- [X11] Bugfix: Changing `GLFW_FLOATING` could leak memory
- [X11] Bugfix: Icon pixel format conversion worked only by accident, relying on
undefined behavior (#1986)
- [X11] Bugfix: Dynamic loading on OpenBSD failed due to soname differences
- [X11] Bugfix: Waiting for events would fail if file descriptor was too large
(#2024)
- [X11] Bugfix: Joystick events could lead to busy-waiting (#1872)
- [X11] Bugfix: `glfwWaitEvents*` did not continue for joystick events
- [X11] Bugfix: `glfwPostEmptyEvent` could be ignored due to race condition
(#379,#1281,#1285,#2033)
- [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences
- [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951)
- [X11] Bugfix: The OSMesa libray was not unloaded on termination
- [X11] Bugfix: A malformed response during selection transfer could cause a segfault
- [X11] Bugfix: Some calls would reset Xlib to the default error handler (#2108)
- [Wayland] Added improved fallback window decorations via libdecor (#1639,#1693)
- [Wayland] Added support for key names via xkbcommon
- [Wayland] Added support for file path drop events (#2040)
- [Wayland] Added support for more human-readable monitor names where available
- [Wayland] Disabled alpha channel for opaque windows on systems lacking
`EGL_EXT_present_opaque` (#1895)
- [Wayland] Removed support for `wl_shell` (#1443)
- [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432)
- [Wayland] Bugfix: Repeated keys could be reported with `NULL` window (#1704)
- [Wayland] Bugfix: Retrieving partial framebuffer size would segfault
- [Wayland] Bugfix: Scrolling offsets were inverted compared to other platforms
(#1463)
- [Wayland] Bugfix: Client-Side Decorations were destroyed in the wrong order
(#1798)
- [Wayland] Bugfix: Monitors physical size could report zero (#1784,#1792)
- [Wayland] Bugfix: Some keys were not repeating in Wayland (#1908)
- [Wayland] Bugfix: Non-arrow cursors are offset from the hotspot (#1706,#1899)
- [Wayland] Bugfix: The `O_CLOEXEC` flag was not defined on FreeBSD
- [Wayland] Bugfix: Key repeat could lead to a race condition (#1710)
- [Wayland] Bugfix: Activating a window would emit two input focus events
- [Wayland] Bugfix: Disable key repeat mechanism when window loses input focus
- [Wayland] Bugfix: Window hiding and showing did not work (#1492,#1731)
- [Wayland] Bugfix: A key being repeated was not released when window lost focus
- [Wayland] Bugfix: Showing a hidden window did not emit a window refresh event
- [Wayland] Bugfix: Full screen window creation did not ignore `GLFW_VISIBLE`
- [Wayland] Bugfix: Some keys were reported as wrong key or `GLFW_KEY_UNKNOWN`
- [Wayland] Bugfix: Text input did not repeat along with key repeat
- [Wayland] Bugfix: `glfwPostEmptyEvent` sometimes had no effect (#1520,#1521)
- [Wayland] Bugfix: `glfwSetClipboardString` would fail if set to result of
`glfwGetClipboardString`
- [Wayland] Bugfix: Data source creation error would cause double free at termination
- [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat
- [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang
- [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string
- [Wayland] Bugfix: MIME type matching was not performed for clipboard string
- [Wayland] Bugfix: The OSMesa library was not unloaded on termination
- [Wayland] Bugfix: `glfwCreateWindow` could emit `GLFW_FEATURE_UNAVAILABLE`
- [Wayland] Bugfix: Lock key modifier bits were only set when lock keys were pressed
- [Wayland] Bugfix: A window leaving full screen mode would be iconified (#1995)
- [Wayland] Bugfix: A window leaving full screen mode ignored its desired size
- [Wayland] Bugfix: `glfwSetWindowMonitor` did not update windowed mode size
- [Wayland] Bugfix: `glfwRestoreWindow` would make a full screen window windowed
- [Wayland] Bugfix: A window maximized or restored by the user would enter an
inconsistent state
- [Wayland] Bugfix: Window maximization events were not emitted
- [Wayland] Bugfix: `glfwRestoreWindow` assumed it was always in windowed mode
- [Wayland] Bugfix: `glfwSetWindowSize` would resize a full screen window
- [Wayland] Bugfix: A window content scale event would be emitted every time
the window resized
- [Wayland] Bugfix: If `glfwInit` failed it would close stdin
- [Wayland] Bugfix: Manual resizing with fallback decorations behaved erratically
(#1991,#2115,#2127)
- [Wayland] Bugfix: Size limits included frame size for fallback decorations
- [Wayland] Bugfix: Updating `GLFW_DECORATED` had no effect on server-side
decorations
- [Wayland] Bugfix: A monitor would be reported as connected again if its scale
changed
- [Wayland] Bugfix: `glfwTerminate` would segfault if any monitor had changed
scale
- [Wayland] Bugfix: Window content scale events were not emitted when monitor
scale changed
- [Wayland] Bugfix: `glfwSetWindowAspectRatio` reported an error instead of
applying the specified ratio
- [Wayland] Bugfix: `GLFW_MAXIMIZED` window hint had no effect
- [Wayland] Bugfix: `glfwRestoreWindow` had no effect before first show
- [Wayland] Bugfix: Hiding and then showing a window caused program abort on
wlroots compositors (#1268)
- [Wayland] Bugfix: `GLFW_DECORATED` was ignored when showing a window with XDG
decorations
- [Wayland] Bugfix: Connecting a mouse after `glfwInit` would segfault (#1450)
- [Wayland] Bugfix: Joysticks connected after `glfwInit` were not detected (#2198)
- [Wayland] Bugfix: Fallback decorations emitted `GLFW_CURSOR_UNAVAILABLE` errors
- [Linux] Bugfix: Joysticks without buttons were ignored (#2042,#2043)
- [Linux] Bugfix: A small amount of memory could leak if initialization failed (#2229)
- [NSGL] Bugfix: `GLFW_COCOA_RETINA_FRAMEBUFFER` had no effect on newer
macOS versions (#1442)
- [NSGL] Bugfix: Workaround for swap interval on 10.14 broke on 10.12 (#1483)
- [NSGL] Bugfix: Defining `GL_SILENCE_DEPRECATION` externally caused
a duplicate definition warning (#1840)
- [EGL] Added loading of glvnd `libOpenGL.so.0` where available for OpenGL
- [EGL] Bugfix: The `GLFW_DOUBLEBUFFER` context attribute was ignored (#1843)
- [EGL] Bugfix: Setting `GLFW_CONTEXT_DEBUG` caused creation to fail (#2348)
- [GLX] Added loading of glvnd `libGLX.so.0` where available
- [GLX] Bugfix: Context creation failed if GLX 1.4 was not exported by GLX library
## Contact
On [glfw.org](https://www.glfw.org/) you can find the latest version of GLFW, as
well as news, documentation and other information about the project.
If you have questions related to the use of GLFW, we have a
[forum](https://discourse.glfw.org/).
If you have a bug to report, a patch to submit or a feature you'd like to
request, please file it in the
[issue tracker](https://github.com/glfw/glfw/issues) on GitHub.
Finally, if you're interested in helping out with the development of GLFW or
porting it to your favorite platform, join us on the forum or GitHub.

View File

@ -1,230 +0,0 @@
/* Copyright (c) 2012, Kim Gräsman
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Kim Gräsman nor the names of contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL KIM GRÄSMAN BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "getopt.h"
#include <stddef.h>
#include <string.h>
const int no_argument = 0;
const int required_argument = 1;
const int optional_argument = 2;
char* optarg;
int optopt;
/* The variable optind [...] shall be initialized to 1 by the system. */
int optind = 1;
int opterr;
static char* optcursor = NULL;
/* Implemented based on [1] and [2] for optional arguments.
optopt is handled FreeBSD-style, per [3].
Other GNU and FreeBSD extensions are purely accidental.
[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
*/
int getopt(int argc, char* const argv[], const char* optstring) {
int optchar = -1;
const char* optdecl = NULL;
optarg = NULL;
opterr = 0;
optopt = 0;
/* Unspecified, but we need it to avoid overrunning the argv bounds. */
if (optind >= argc)
goto no_more_optchars;
/* If, when getopt() is called argv[optind] is a null pointer, getopt()
shall return -1 without changing optind. */
if (argv[optind] == NULL)
goto no_more_optchars;
/* If, when getopt() is called *argv[optind] is not the character '-',
getopt() shall return -1 without changing optind. */
if (*argv[optind] != '-')
goto no_more_optchars;
/* If, when getopt() is called argv[optind] points to the string "-",
getopt() shall return -1 without changing optind. */
if (strcmp(argv[optind], "-") == 0)
goto no_more_optchars;
/* If, when getopt() is called argv[optind] points to the string "--",
getopt() shall return -1 after incrementing optind. */
if (strcmp(argv[optind], "--") == 0) {
++optind;
goto no_more_optchars;
}
if (optcursor == NULL || *optcursor == '\0')
optcursor = argv[optind] + 1;
optchar = *optcursor;
/* FreeBSD: The variable optopt saves the last known option character
returned by getopt(). */
optopt = optchar;
/* The getopt() function shall return the next option character (if one is
found) from argv that matches a character in optstring, if there is
one that matches. */
optdecl = strchr(optstring, optchar);
if (optdecl) {
/* [I]f a character is followed by a colon, the option takes an
argument. */
if (optdecl[1] == ':') {
optarg = ++optcursor;
if (*optarg == '\0') {
/* GNU extension: Two colons mean an option takes an
optional arg; if there is text in the current argv-element
(i.e., in the same word as the option name itself, for example,
"-oarg"), then it is returned in optarg, otherwise optarg is set
to zero. */
if (optdecl[2] != ':') {
/* If the option was the last character in the string pointed to by
an element of argv, then optarg shall contain the next element
of argv, and optind shall be incremented by 2. If the resulting
value of optind is greater than argc, this indicates a missing
option-argument, and getopt() shall return an error indication.
Otherwise, optarg shall point to the string following the
option character in that element of argv, and optind shall be
incremented by 1.
*/
if (++optind < argc) {
optarg = argv[optind];
} else {
/* If it detects a missing option-argument, it shall return the
colon character ( ':' ) if the first character of optstring
was a colon, or a question-mark character ( '?' ) otherwise.
*/
optarg = NULL;
optchar = (optstring[0] == ':') ? ':' : '?';
}
} else {
optarg = NULL;
}
}
optcursor = NULL;
}
} else {
/* If getopt() encounters an option character that is not contained in
optstring, it shall return the question-mark ( '?' ) character. */
optchar = '?';
}
if (optcursor == NULL || *++optcursor == '\0')
++optind;
return optchar;
no_more_optchars:
optcursor = NULL;
return -1;
}
/* Implementation based on [1].
[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
*/
int getopt_long(int argc, char* const argv[], const char* optstring,
const struct option* longopts, int* longindex) {
const struct option* o = longopts;
const struct option* match = NULL;
int num_matches = 0;
size_t argument_name_length = 0;
const char* current_argument = NULL;
int retval = -1;
optarg = NULL;
optopt = 0;
if (optind >= argc)
return -1;
if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
return getopt(argc, argv, optstring);
/* It's an option; starts with -- and is longer than two chars. */
current_argument = argv[optind] + 2;
argument_name_length = strcspn(current_argument, "=");
for (; o->name; ++o) {
if (strncmp(o->name, current_argument, argument_name_length) == 0) {
match = o;
++num_matches;
}
}
if (num_matches == 1) {
/* If longindex is not NULL, it points to a variable which is set to the
index of the long option relative to longopts. */
if (longindex)
*longindex = (int) (match - longopts);
/* If flag is NULL, then getopt_long() shall return val.
Otherwise, getopt_long() returns 0, and flag shall point to a variable
which shall be set to val if the option is found, but left unchanged if
the option is not found. */
if (match->flag)
*(match->flag) = match->val;
retval = match->flag ? 0 : match->val;
if (match->has_arg != no_argument) {
optarg = strchr(argv[optind], '=');
if (optarg != NULL)
++optarg;
if (match->has_arg == required_argument) {
/* Only scan the next argv for required arguments. Behavior is not
specified, but has been observed with Ubuntu and Mac OSX. */
if (optarg == NULL && ++optind < argc) {
optarg = argv[optind];
}
if (optarg == NULL)
retval = ':';
}
} else if (strchr(argv[optind], '=')) {
/* An argument was provided to a non-argument option.
I haven't seen this specified explicitly, but both GNU and BSD-based
implementations show this behavior.
*/
retval = '?';
}
} else {
/* Unknown option or ambiguous match. */
retval = '?';
}
++optind;
return retval;
}

View File

@ -1,57 +0,0 @@
/* Copyright (c) 2012, Kim Gräsman
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Kim Gräsman nor the names of contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL KIM GRÄSMAN BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef INCLUDED_GETOPT_PORT_H
#define INCLUDED_GETOPT_PORT_H
#if defined(__cplusplus)
extern "C" {
#endif
extern const int no_argument;
extern const int required_argument;
extern const int optional_argument;
extern char* optarg;
extern int optind, opterr, optopt;
struct option {
const char* name;
int has_arg;
int* flag;
int val;
};
int getopt(int argc, char* const argv[], const char* optstring);
int getopt_long(int argc, char* const argv[],
const char* optstring, const struct option* longopts, int* longindex);
#if defined(__cplusplus)
}
#endif
#endif // INCLUDED_GETOPT_PORT_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,606 +0,0 @@
#ifndef LINMATH_H
#define LINMATH_H
#include <string.h>
#include <math.h>
#include <string.h>
/* 2021-03-21 Camilla Löwy <elmindreda@elmindreda.org>
* - Replaced double constants with float equivalents
*/
#ifdef LINMATH_NO_INLINE
#define LINMATH_H_FUNC static
#else
#define LINMATH_H_FUNC static inline
#endif
#define LINMATH_H_DEFINE_VEC(n) \
typedef float vec##n[n]; \
LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = a[i] + b[i]; \
} \
LINMATH_H_FUNC void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = a[i] - b[i]; \
} \
LINMATH_H_FUNC void vec##n##_scale(vec##n r, vec##n const v, float const s) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = v[i] * s; \
} \
LINMATH_H_FUNC float vec##n##_mul_inner(vec##n const a, vec##n const b) \
{ \
float p = 0.f; \
int i; \
for(i=0; i<n; ++i) \
p += b[i]*a[i]; \
return p; \
} \
LINMATH_H_FUNC float vec##n##_len(vec##n const v) \
{ \
return sqrtf(vec##n##_mul_inner(v,v)); \
} \
LINMATH_H_FUNC void vec##n##_norm(vec##n r, vec##n const v) \
{ \
float k = 1.f / vec##n##_len(v); \
vec##n##_scale(r, v, k); \
} \
LINMATH_H_FUNC void vec##n##_min(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = a[i]<b[i] ? a[i] : b[i]; \
} \
LINMATH_H_FUNC void vec##n##_max(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = a[i]>b[i] ? a[i] : b[i]; \
} \
LINMATH_H_FUNC void vec##n##_dup(vec##n r, vec##n const src) \
{ \
int i; \
for(i=0; i<n; ++i) \
r[i] = src[i]; \
}
LINMATH_H_DEFINE_VEC(2)
LINMATH_H_DEFINE_VEC(3)
LINMATH_H_DEFINE_VEC(4)
LINMATH_H_FUNC void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b)
{
r[0] = a[1]*b[2] - a[2]*b[1];
r[1] = a[2]*b[0] - a[0]*b[2];
r[2] = a[0]*b[1] - a[1]*b[0];
}
LINMATH_H_FUNC void vec3_reflect(vec3 r, vec3 const v, vec3 const n)
{
float p = 2.f * vec3_mul_inner(v, n);
int i;
for(i=0;i<3;++i)
r[i] = v[i] - p*n[i];
}
LINMATH_H_FUNC void vec4_mul_cross(vec4 r, vec4 const a, vec4 const b)
{
r[0] = a[1]*b[2] - a[2]*b[1];
r[1] = a[2]*b[0] - a[0]*b[2];
r[2] = a[0]*b[1] - a[1]*b[0];
r[3] = 1.f;
}
LINMATH_H_FUNC void vec4_reflect(vec4 r, vec4 const v, vec4 const n)
{
float p = 2.f*vec4_mul_inner(v, n);
int i;
for(i=0;i<4;++i)
r[i] = v[i] - p*n[i];
}
typedef vec4 mat4x4[4];
LINMATH_H_FUNC void mat4x4_identity(mat4x4 M)
{
int i, j;
for(i=0; i<4; ++i)
for(j=0; j<4; ++j)
M[i][j] = i==j ? 1.f : 0.f;
}
LINMATH_H_FUNC void mat4x4_dup(mat4x4 M, mat4x4 const N)
{
int i;
for(i=0; i<4; ++i)
vec4_dup(M[i], N[i]);
}
LINMATH_H_FUNC void mat4x4_row(vec4 r, mat4x4 const M, int i)
{
int k;
for(k=0; k<4; ++k)
r[k] = M[k][i];
}
LINMATH_H_FUNC void mat4x4_col(vec4 r, mat4x4 const M, int i)
{
int k;
for(k=0; k<4; ++k)
r[k] = M[i][k];
}
LINMATH_H_FUNC void mat4x4_transpose(mat4x4 M, mat4x4 const N)
{
// Note: if M and N are the same, the user has to
// explicitly make a copy of M and set it to N.
int i, j;
for(j=0; j<4; ++j)
for(i=0; i<4; ++i)
M[i][j] = N[j][i];
}
LINMATH_H_FUNC void mat4x4_add(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
int i;
for(i=0; i<4; ++i)
vec4_add(M[i], a[i], b[i]);
}
LINMATH_H_FUNC void mat4x4_sub(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
int i;
for(i=0; i<4; ++i)
vec4_sub(M[i], a[i], b[i]);
}
LINMATH_H_FUNC void mat4x4_scale(mat4x4 M, mat4x4 const a, float k)
{
int i;
for(i=0; i<4; ++i)
vec4_scale(M[i], a[i], k);
}
LINMATH_H_FUNC void mat4x4_scale_aniso(mat4x4 M, mat4x4 const a, float x, float y, float z)
{
vec4_scale(M[0], a[0], x);
vec4_scale(M[1], a[1], y);
vec4_scale(M[2], a[2], z);
vec4_dup(M[3], a[3]);
}
LINMATH_H_FUNC void mat4x4_mul(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
mat4x4 temp;
int k, r, c;
for(c=0; c<4; ++c) for(r=0; r<4; ++r) {
temp[c][r] = 0.f;
for(k=0; k<4; ++k)
temp[c][r] += a[k][r] * b[c][k];
}
mat4x4_dup(M, temp);
}
LINMATH_H_FUNC void mat4x4_mul_vec4(vec4 r, mat4x4 const M, vec4 const v)
{
int i, j;
for(j=0; j<4; ++j) {
r[j] = 0.f;
for(i=0; i<4; ++i)
r[j] += M[i][j] * v[i];
}
}
LINMATH_H_FUNC void mat4x4_translate(mat4x4 T, float x, float y, float z)
{
mat4x4_identity(T);
T[3][0] = x;
T[3][1] = y;
T[3][2] = z;
}
LINMATH_H_FUNC void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z)
{
vec4 t = {x, y, z, 0};
vec4 r;
int i;
for (i = 0; i < 4; ++i) {
mat4x4_row(r, M, i);
M[3][i] += vec4_mul_inner(r, t);
}
}
LINMATH_H_FUNC void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 const a, vec3 const b)
{
int i, j;
for(i=0; i<4; ++i) for(j=0; j<4; ++j)
M[i][j] = i<3 && j<3 ? a[i] * b[j] : 0.f;
}
LINMATH_H_FUNC void mat4x4_rotate(mat4x4 R, mat4x4 const M, float x, float y, float z, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
vec3 u = {x, y, z};
if(vec3_len(u) > 1e-4) {
vec3_norm(u, u);
mat4x4 T;
mat4x4_from_vec3_mul_outer(T, u, u);
mat4x4 S = {
{ 0, u[2], -u[1], 0},
{-u[2], 0, u[0], 0},
{ u[1], -u[0], 0, 0},
{ 0, 0, 0, 0}
};
mat4x4_scale(S, S, s);
mat4x4 C;
mat4x4_identity(C);
mat4x4_sub(C, C, T);
mat4x4_scale(C, C, c);
mat4x4_add(T, T, C);
mat4x4_add(T, T, S);
T[3][3] = 1.f;
mat4x4_mul(R, M, T);
} else {
mat4x4_dup(R, M);
}
}
LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {
{1.f, 0.f, 0.f, 0.f},
{0.f, c, s, 0.f},
{0.f, -s, c, 0.f},
{0.f, 0.f, 0.f, 1.f}
};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {
{ c, 0.f, -s, 0.f},
{ 0.f, 1.f, 0.f, 0.f},
{ s, 0.f, c, 0.f},
{ 0.f, 0.f, 0.f, 1.f}
};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {
{ c, s, 0.f, 0.f},
{ -s, c, 0.f, 0.f},
{ 0.f, 0.f, 1.f, 0.f},
{ 0.f, 0.f, 0.f, 1.f}
};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 const M)
{
float s[6];
float c[6];
s[0] = M[0][0]*M[1][1] - M[1][0]*M[0][1];
s[1] = M[0][0]*M[1][2] - M[1][0]*M[0][2];
s[2] = M[0][0]*M[1][3] - M[1][0]*M[0][3];
s[3] = M[0][1]*M[1][2] - M[1][1]*M[0][2];
s[4] = M[0][1]*M[1][3] - M[1][1]*M[0][3];
s[5] = M[0][2]*M[1][3] - M[1][2]*M[0][3];
c[0] = M[2][0]*M[3][1] - M[3][0]*M[2][1];
c[1] = M[2][0]*M[3][2] - M[3][0]*M[2][2];
c[2] = M[2][0]*M[3][3] - M[3][0]*M[2][3];
c[3] = M[2][1]*M[3][2] - M[3][1]*M[2][2];
c[4] = M[2][1]*M[3][3] - M[3][1]*M[2][3];
c[5] = M[2][2]*M[3][3] - M[3][2]*M[2][3];
/* Assumes it is invertible */
float idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] );
T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet;
T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet;
T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet;
T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet;
T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet;
T[1][1] = ( M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet;
T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet;
T[1][3] = ( M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet;
T[2][0] = ( M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet;
T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet;
T[2][2] = ( M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet;
T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet;
T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet;
T[3][1] = ( M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet;
T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet;
T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet;
}
LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 const M)
{
mat4x4_dup(R, M);
float s = 1.f;
vec3 h;
vec3_norm(R[2], R[2]);
s = vec3_mul_inner(R[1], R[2]);
vec3_scale(h, R[2], s);
vec3_sub(R[1], R[1], h);
vec3_norm(R[1], R[1]);
s = vec3_mul_inner(R[0], R[2]);
vec3_scale(h, R[2], s);
vec3_sub(R[0], R[0], h);
s = vec3_mul_inner(R[0], R[1]);
vec3_scale(h, R[1], s);
vec3_sub(R[0], R[0], h);
vec3_norm(R[0], R[0]);
}
LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f)
{
M[0][0] = 2.f*n/(r-l);
M[0][1] = M[0][2] = M[0][3] = 0.f;
M[1][1] = 2.f*n/(t-b);
M[1][0] = M[1][2] = M[1][3] = 0.f;
M[2][0] = (r+l)/(r-l);
M[2][1] = (t+b)/(t-b);
M[2][2] = -(f+n)/(f-n);
M[2][3] = -1.f;
M[3][2] = -2.f*(f*n)/(f-n);
M[3][0] = M[3][1] = M[3][3] = 0.f;
}
LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f)
{
M[0][0] = 2.f/(r-l);
M[0][1] = M[0][2] = M[0][3] = 0.f;
M[1][1] = 2.f/(t-b);
M[1][0] = M[1][2] = M[1][3] = 0.f;
M[2][2] = -2.f/(f-n);
M[2][0] = M[2][1] = M[2][3] = 0.f;
M[3][0] = -(r+l)/(r-l);
M[3][1] = -(t+b)/(t-b);
M[3][2] = -(f+n)/(f-n);
M[3][3] = 1.f;
}
LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f)
{
/* NOTE: Degrees are an unhandy unit to work with.
* linmath.h uses radians for everything! */
float const a = 1.f / tanf(y_fov / 2.f);
m[0][0] = a / aspect;
m[0][1] = 0.f;
m[0][2] = 0.f;
m[0][3] = 0.f;
m[1][0] = 0.f;
m[1][1] = a;
m[1][2] = 0.f;
m[1][3] = 0.f;
m[2][0] = 0.f;
m[2][1] = 0.f;
m[2][2] = -((f + n) / (f - n));
m[2][3] = -1.f;
m[3][0] = 0.f;
m[3][1] = 0.f;
m[3][2] = -((2.f * f * n) / (f - n));
m[3][3] = 0.f;
}
LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 const eye, vec3 const center, vec3 const up)
{
/* Adapted from Android's OpenGL Matrix.java. */
/* See the OpenGL GLUT documentation for gluLookAt for a description */
/* of the algorithm. We implement it in a straightforward way: */
/* TODO: The negation of of can be spared by swapping the order of
* operands in the following cross products in the right way. */
vec3 f;
vec3_sub(f, center, eye);
vec3_norm(f, f);
vec3 s;
vec3_mul_cross(s, f, up);
vec3_norm(s, s);
vec3 t;
vec3_mul_cross(t, s, f);
m[0][0] = s[0];
m[0][1] = t[0];
m[0][2] = -f[0];
m[0][3] = 0.f;
m[1][0] = s[1];
m[1][1] = t[1];
m[1][2] = -f[1];
m[1][3] = 0.f;
m[2][0] = s[2];
m[2][1] = t[2];
m[2][2] = -f[2];
m[2][3] = 0.f;
m[3][0] = 0.f;
m[3][1] = 0.f;
m[3][2] = 0.f;
m[3][3] = 1.f;
mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]);
}
typedef float quat[4];
#define quat_add vec4_add
#define quat_sub vec4_sub
#define quat_norm vec4_norm
#define quat_scale vec4_scale
#define quat_mul_inner vec4_mul_inner
LINMATH_H_FUNC void quat_identity(quat q)
{
q[0] = q[1] = q[2] = 0.f;
q[3] = 1.f;
}
LINMATH_H_FUNC void quat_mul(quat r, quat const p, quat const q)
{
vec3 w;
vec3_mul_cross(r, p, q);
vec3_scale(w, p, q[3]);
vec3_add(r, r, w);
vec3_scale(w, q, p[3]);
vec3_add(r, r, w);
r[3] = p[3]*q[3] - vec3_mul_inner(p, q);
}
LINMATH_H_FUNC void quat_conj(quat r, quat const q)
{
int i;
for(i=0; i<3; ++i)
r[i] = -q[i];
r[3] = q[3];
}
LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 const axis) {
vec3 axis_norm;
vec3_norm(axis_norm, axis);
float s = sinf(angle / 2);
float c = cosf(angle / 2);
vec3_scale(r, axis_norm, s);
r[3] = c;
}
LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat const q, vec3 const v)
{
/*
* Method by Fabian 'ryg' Giessen (of Farbrausch)
t = 2 * cross(q.xyz, v)
v' = v + q.w * t + cross(q.xyz, t)
*/
vec3 t;
vec3 q_xyz = {q[0], q[1], q[2]};
vec3 u = {q[0], q[1], q[2]};
vec3_mul_cross(t, q_xyz, v);
vec3_scale(t, t, 2);
vec3_mul_cross(u, q_xyz, t);
vec3_scale(t, t, q[3]);
vec3_add(r, v, t);
vec3_add(r, r, u);
}
LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat const q)
{
float a = q[3];
float b = q[0];
float c = q[1];
float d = q[2];
float a2 = a*a;
float b2 = b*b;
float c2 = c*c;
float d2 = d*d;
M[0][0] = a2 + b2 - c2 - d2;
M[0][1] = 2.f*(b*c + a*d);
M[0][2] = 2.f*(b*d - a*c);
M[0][3] = 0.f;
M[1][0] = 2*(b*c - a*d);
M[1][1] = a2 - b2 + c2 - d2;
M[1][2] = 2.f*(c*d + a*b);
M[1][3] = 0.f;
M[2][0] = 2.f*(b*d + a*c);
M[2][1] = 2.f*(c*d - a*b);
M[2][2] = a2 - b2 - c2 + d2;
M[2][3] = 0.f;
M[3][0] = M[3][1] = M[3][2] = 0.f;
M[3][3] = 1.f;
}
LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 const M, quat const q)
{
/* XXX: The way this is written only works for orthogonal matrices. */
/* TODO: Take care of non-orthogonal case. */
quat_mul_vec3(R[0], q, M[0]);
quat_mul_vec3(R[1], q, M[1]);
quat_mul_vec3(R[2], q, M[2]);
R[3][0] = R[3][1] = R[3][2] = 0.f;
R[0][3] = M[0][3];
R[1][3] = M[1][3];
R[2][3] = M[2][3];
R[3][3] = M[3][3]; // typically 1.0, but here we make it general
}
LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 const M)
{
float r=0.f;
int i;
int perm[] = { 0, 1, 2, 0, 1 };
int *p = perm;
for(i = 0; i<3; i++) {
float m = M[i][i];
if( m < r )
continue;
m = r;
p = &perm[i];
}
r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] );
if(r < 1e-6) {
q[0] = 1.f;
q[1] = q[2] = q[3] = 0.f;
return;
}
q[0] = r/2.f;
q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.f*r);
q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.f*r);
q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.f*r);
}
LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 const _b, float s)
{
vec2 a; memcpy(a, _a, sizeof(a));
vec2 b; memcpy(b, _b, sizeof(b));
float z_a = 0.f;
float z_b = 0.f;
if(vec2_len(a) < 1.f) {
z_a = sqrtf(1.f - vec2_mul_inner(a, a));
} else {
vec2_norm(a, a);
}
if(vec2_len(b) < 1.f) {
z_b = sqrtf(1.f - vec2_mul_inner(b, b));
} else {
vec2_norm(b, b);
}
vec3 a_ = {a[0], a[1], z_a};
vec3 b_ = {b[0], b[1], z_b};
vec3 c_;
vec3_mul_cross(c_, a_, b_);
float const angle = acos(vec3_mul_inner(a_, b_)) * s;
mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle);
}
#endif

View File

@ -1,117 +0,0 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER within this package.
*/
#if defined(_MSC_VER) && !defined(_MSC_EXTENSIONS)
#define NONAMELESSUNION 1
#endif
#if defined(NONAMELESSSTRUCT) && \
!defined(NONAMELESSUNION)
#define NONAMELESSUNION 1
#endif
#if defined(NONAMELESSUNION) && \
!defined(NONAMELESSSTRUCT)
#define NONAMELESSSTRUCT 1
#endif
#if !defined(__GNU_EXTENSION)
#if defined(__GNUC__) || defined(__GNUG__)
#define __GNU_EXTENSION __extension__
#else
#define __GNU_EXTENSION
#endif
#endif /* __extension__ */
#ifndef __ANONYMOUS_DEFINED
#define __ANONYMOUS_DEFINED
#if defined(__GNUC__) || defined(__GNUG__)
#define _ANONYMOUS_UNION __extension__
#define _ANONYMOUS_STRUCT __extension__
#else
#define _ANONYMOUS_UNION
#define _ANONYMOUS_STRUCT
#endif
#ifndef NONAMELESSUNION
#define _UNION_NAME(x)
#define _STRUCT_NAME(x)
#else /* NONAMELESSUNION */
#define _UNION_NAME(x) x
#define _STRUCT_NAME(x) x
#endif
#endif /* __ANONYMOUS_DEFINED */
#ifndef DUMMYUNIONNAME
# ifdef NONAMELESSUNION
# define DUMMYUNIONNAME u
# define DUMMYUNIONNAME1 u1 /* Wine uses this variant */
# define DUMMYUNIONNAME2 u2
# define DUMMYUNIONNAME3 u3
# define DUMMYUNIONNAME4 u4
# define DUMMYUNIONNAME5 u5
# define DUMMYUNIONNAME6 u6
# define DUMMYUNIONNAME7 u7
# define DUMMYUNIONNAME8 u8
# define DUMMYUNIONNAME9 u9
# else /* NONAMELESSUNION */
# define DUMMYUNIONNAME
# define DUMMYUNIONNAME1 /* Wine uses this variant */
# define DUMMYUNIONNAME2
# define DUMMYUNIONNAME3
# define DUMMYUNIONNAME4
# define DUMMYUNIONNAME5
# define DUMMYUNIONNAME6
# define DUMMYUNIONNAME7
# define DUMMYUNIONNAME8
# define DUMMYUNIONNAME9
# endif
#endif /* DUMMYUNIONNAME */
#if !defined(DUMMYUNIONNAME1) /* MinGW does not define this one */
# ifdef NONAMELESSUNION
# define DUMMYUNIONNAME1 u1 /* Wine uses this variant */
# else
# define DUMMYUNIONNAME1 /* Wine uses this variant */
# endif
#endif /* DUMMYUNIONNAME1 */
#ifndef DUMMYSTRUCTNAME
# ifdef NONAMELESSUNION
# define DUMMYSTRUCTNAME s
# define DUMMYSTRUCTNAME1 s1 /* Wine uses this variant */
# define DUMMYSTRUCTNAME2 s2
# define DUMMYSTRUCTNAME3 s3
# define DUMMYSTRUCTNAME4 s4
# define DUMMYSTRUCTNAME5 s5
# else
# define DUMMYSTRUCTNAME
# define DUMMYSTRUCTNAME1 /* Wine uses this variant */
# define DUMMYSTRUCTNAME2
# define DUMMYSTRUCTNAME3
# define DUMMYSTRUCTNAME4
# define DUMMYSTRUCTNAME5
# endif
#endif /* DUMMYSTRUCTNAME */
/* These are for compatibility with the Wine source tree */
#ifndef WINELIB_NAME_AW
# ifdef __MINGW_NAME_AW
# define WINELIB_NAME_AW __MINGW_NAME_AW
# else
# ifdef UNICODE
# define WINELIB_NAME_AW(func) func##W
# else
# define WINELIB_NAME_AW(func) func##A
# endif
# endif
#endif /* WINELIB_NAME_AW */
#ifndef DECL_WINELIB_TYPE_AW
# ifdef __MINGW_TYPEDEF_AW
# define DECL_WINELIB_TYPE_AW __MINGW_TYPEDEF_AW
# else
# define DECL_WINELIB_TYPE_AW(type) typedef WINELIB_NAME_AW(type) type;
# endif
#endif /* DECL_WINELIB_TYPE_AW */

File diff suppressed because it is too large Load Diff

View File

@ -1,239 +0,0 @@
/*
* The Wine project - Xinput Joystick Library
* Copyright 2008 Andrew Fenn
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __WINE_XINPUT_H
#define __WINE_XINPUT_H
#include <windef.h>
/*
* Bitmasks for the joysticks buttons, determines what has
* been pressed on the joystick, these need to be mapped
* to whatever device you're using instead of an xbox 360
* joystick
*/
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
#define XINPUT_GAMEPAD_START 0x0010
#define XINPUT_GAMEPAD_BACK 0x0020
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
#define XINPUT_GAMEPAD_A 0x1000
#define XINPUT_GAMEPAD_B 0x2000
#define XINPUT_GAMEPAD_X 0x4000
#define XINPUT_GAMEPAD_Y 0x8000
/*
* Defines the flags used to determine if the user is pushing
* down on a button, not holding a button, etc
*/
#define XINPUT_KEYSTROKE_KEYDOWN 0x0001
#define XINPUT_KEYSTROKE_KEYUP 0x0002
#define XINPUT_KEYSTROKE_REPEAT 0x0004
/*
* Defines the codes which are returned by XInputGetKeystroke
*/
#define VK_PAD_A 0x5800
#define VK_PAD_B 0x5801
#define VK_PAD_X 0x5802
#define VK_PAD_Y 0x5803
#define VK_PAD_RSHOULDER 0x5804
#define VK_PAD_LSHOULDER 0x5805
#define VK_PAD_LTRIGGER 0x5806
#define VK_PAD_RTRIGGER 0x5807
#define VK_PAD_DPAD_UP 0x5810
#define VK_PAD_DPAD_DOWN 0x5811
#define VK_PAD_DPAD_LEFT 0x5812
#define VK_PAD_DPAD_RIGHT 0x5813
#define VK_PAD_START 0x5814
#define VK_PAD_BACK 0x5815
#define VK_PAD_LTHUMB_PRESS 0x5816
#define VK_PAD_RTHUMB_PRESS 0x5817
#define VK_PAD_LTHUMB_UP 0x5820
#define VK_PAD_LTHUMB_DOWN 0x5821
#define VK_PAD_LTHUMB_RIGHT 0x5822
#define VK_PAD_LTHUMB_LEFT 0x5823
#define VK_PAD_LTHUMB_UPLEFT 0x5824
#define VK_PAD_LTHUMB_UPRIGHT 0x5825
#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826
#define VK_PAD_LTHUMB_DOWNLEFT 0x5827
#define VK_PAD_RTHUMB_UP 0x5830
#define VK_PAD_RTHUMB_DOWN 0x5831
#define VK_PAD_RTHUMB_RIGHT 0x5832
#define VK_PAD_RTHUMB_LEFT 0x5833
#define VK_PAD_RTHUMB_UPLEFT 0x5834
#define VK_PAD_RTHUMB_UPRIGHT 0x5835
#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836
#define VK_PAD_RTHUMB_DOWNLEFT 0x5837
/*
* Deadzones are for analogue joystick controls on the joypad
* which determine when input should be assumed to be in the
* middle of the pad. This is a threshold to stop a joypad
* controlling the game when the player isn't touching the
* controls.
*/
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
/*
* Defines what type of abilities the type of joystick has
* DEVTYPE_GAMEPAD is available for all joysticks, however
* there may be more specific identifiers for other joysticks
* which are being used.
*/
#define XINPUT_DEVTYPE_GAMEPAD 0x01
#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
#define XINPUT_DEVSUBTYPE_FLIGHT_SICK 0x04
#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
#define XINPUT_DEVSUBTYPE_GUITAR 0x06
#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
/*
* These are used with the XInputGetCapabilities function to
* determine the abilities to the joystick which has been
* plugged in.
*/
#define XINPUT_CAPS_VOICE_SUPPORTED 0x0004
#define XINPUT_FLAG_GAMEPAD 0x00000001
/*
* Defines the status of the battery if one is used in the
* attached joystick. The first two define if the joystick
* supports a battery. Disconnected means that the joystick
* isn't connected. Wired shows that the joystick is a wired
* joystick.
*/
#define BATTERY_DEVTYPE_GAMEPAD 0x00
#define BATTERY_DEVTYPE_HEADSET 0x01
#define BATTERY_TYPE_DISCONNECTED 0x00
#define BATTERY_TYPE_WIRED 0x01
#define BATTERY_TYPE_ALKALINE 0x02
#define BATTERY_TYPE_NIMH 0x03
#define BATTERY_TYPE_UNKNOWN 0xFF
#define BATTERY_LEVEL_EMPTY 0x00
#define BATTERY_LEVEL_LOW 0x01
#define BATTERY_LEVEL_MEDIUM 0x02
#define BATTERY_LEVEL_FULL 0x03
/*
* How many joysticks can be used with this library. Games that
* use the xinput library will not go over this number.
*/
#define XUSER_MAX_COUNT 4
#define XUSER_INDEX_ANY 0x000000FF
/*
* Defines the structure of an xbox 360 joystick.
*/
typedef struct _XINPUT_GAMEPAD {
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
typedef struct _XINPUT_STATE {
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE, *PXINPUT_STATE;
/*
* Defines the structure of how much vibration is set on both the
* right and left motors in a joystick. If you're not using a 360
* joystick you will have to map these to your device.
*/
typedef struct _XINPUT_VIBRATION {
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION, *PXINPUT_VIBRATION;
/*
* Defines the structure for what kind of abilities the joystick has
* such abilities are things such as if the joystick has the ability
* to send and receive audio, if the joystick is in fact a driving
* wheel or perhaps if the joystick is some kind of dance pad or
* guitar.
*/
typedef struct _XINPUT_CAPABILITIES {
BYTE Type;
BYTE SubType;
WORD Flags;
XINPUT_GAMEPAD Gamepad;
XINPUT_VIBRATION Vibration;
} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES;
/*
* Defines the structure for a joystick input event which is
* retrieved using the function XInputGetKeystroke
*/
typedef struct _XINPUT_KEYSTROKE {
WORD VirtualKey;
WCHAR Unicode;
WORD Flags;
BYTE UserIndex;
BYTE HidCode;
} XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE;
typedef struct _XINPUT_BATTERY_INFORMATION
{
BYTE BatteryType;
BYTE BatteryLevel;
} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION;
#ifdef __cplusplus
extern "C" {
#endif
void WINAPI XInputEnable(WINBOOL);
DWORD WINAPI XInputSetState(DWORD, XINPUT_VIBRATION*);
DWORD WINAPI XInputGetState(DWORD, XINPUT_STATE*);
DWORD WINAPI XInputGetKeystroke(DWORD, DWORD, PXINPUT_KEYSTROKE);
DWORD WINAPI XInputGetCapabilities(DWORD, DWORD, XINPUT_CAPABILITIES*);
DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD, GUID*, GUID*);
DWORD WINAPI XInputGetBatteryInformation(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*);
#ifdef __cplusplus
}
#endif
#endif /* __WINE_XINPUT_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,381 +0,0 @@
/*
* Nuklear - v1.32.0 - public domain
* no warrenty implied; use at your own risk.
* authored from 2015-2017 by Micha Mettke
*/
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_GLFW_GL2_H_
#define NK_GLFW_GL2_H_
#include <GLFW/glfw3.h>
enum nk_glfw_init_state{
NK_GLFW3_DEFAULT = 0,
NK_GLFW3_INSTALL_CALLBACKS
};
NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state);
NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas);
NK_API void nk_glfw3_font_stash_end(void);
NK_API void nk_glfw3_new_frame(void);
NK_API void nk_glfw3_render(enum nk_anti_aliasing);
NK_API void nk_glfw3_shutdown(void);
NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
#endif
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_GLFW_GL2_IMPLEMENTATION
#ifndef NK_GLFW_TEXT_MAX
#define NK_GLFW_TEXT_MAX 256
#endif
#ifndef NK_GLFW_DOUBLE_CLICK_LO
#define NK_GLFW_DOUBLE_CLICK_LO 0.02
#endif
#ifndef NK_GLFW_DOUBLE_CLICK_HI
#define NK_GLFW_DOUBLE_CLICK_HI 0.2
#endif
struct nk_glfw_device {
struct nk_buffer cmds;
struct nk_draw_null_texture null;
GLuint font_tex;
};
struct nk_glfw_vertex {
float position[2];
float uv[2];
nk_byte col[4];
};
static struct nk_glfw {
GLFWwindow *win;
int width, height;
int display_width, display_height;
struct nk_glfw_device ogl;
struct nk_context ctx;
struct nk_font_atlas atlas;
struct nk_vec2 fb_scale;
unsigned int text[NK_GLFW_TEXT_MAX];
int text_len;
struct nk_vec2 scroll;
double last_button_click;
int is_double_click_down;
struct nk_vec2 double_click_pos;
} glfw;
NK_INTERN void
nk_glfw3_device_upload_atlas(const void *image, int width, int height)
{
struct nk_glfw_device *dev = &glfw.ogl;
glGenTextures(1, &dev->font_tex);
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image);
}
NK_API void
nk_glfw3_render(enum nk_anti_aliasing AA)
{
/* setup global state */
struct nk_glfw_device *dev = &glfw.ogl;
glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* setup viewport/project */
glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0f, glfw.width, glfw.height, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
{
GLsizei vs = sizeof(struct nk_glfw_vertex);
size_t vp = offsetof(struct nk_glfw_vertex, position);
size_t vt = offsetof(struct nk_glfw_vertex, uv);
size_t vc = offsetof(struct nk_glfw_vertex, col);
/* convert from command queue into draw list and draw to screen */
const struct nk_draw_command *cmd;
const nk_draw_index *offset = NULL;
struct nk_buffer vbuf, ebuf;
/* fill convert configuration */
struct nk_convert_config config;
static const struct nk_draw_vertex_layout_element vertex_layout[] = {
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
{NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
{NK_VERTEX_LAYOUT_END}
};
NK_MEMSET(&config, 0, sizeof(config));
config.vertex_layout = vertex_layout;
config.vertex_size = sizeof(struct nk_glfw_vertex);
config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
config.null = dev->null;
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.global_alpha = 1.0f;
config.shape_AA = AA;
config.line_AA = AA;
/* convert shapes into vertexes */
nk_buffer_init_default(&vbuf);
nk_buffer_init_default(&ebuf);
nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config);
/* setup vertex buffer pointer */
{const void *vertices = nk_buffer_memory_const(&vbuf);
glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp));
glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt));
glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));}
/* iterate over and execute each draw command */
offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf);
nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds)
{
if (!cmd->elem_count) continue;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor(
(GLint)(cmd->clip_rect.x * glfw.fb_scale.x),
(GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y),
(GLint)(cmd->clip_rect.w * glfw.fb_scale.x),
(GLint)(cmd->clip_rect.h * glfw.fb_scale.y));
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
offset += cmd->elem_count;
}
nk_clear(&glfw.ctx);
nk_buffer_free(&vbuf);
nk_buffer_free(&ebuf);
}
/* default OpenGL state */
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
}
NK_API void
nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
{
(void)win;
if (glfw.text_len < NK_GLFW_TEXT_MAX)
glfw.text[glfw.text_len++] = codepoint;
}
NK_API void
nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
{
(void)win; (void)xoff;
glfw.scroll.x += (float)xoff;
glfw.scroll.y += (float)yoff;
}
NK_API void
nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
double x, y;
if (button != GLFW_MOUSE_BUTTON_LEFT) return;
glfwGetCursorPos(window, &x, &y);
if (action == GLFW_PRESS) {
double dt = glfwGetTime() - glfw.last_button_click;
if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) {
glfw.is_double_click_down = nk_true;
glfw.double_click_pos = nk_vec2((float)x, (float)y);
}
glfw.last_button_click = glfwGetTime();
} else glfw.is_double_click_down = nk_false;
}
NK_INTERN void
nk_glfw3_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
{
const char *text = glfwGetClipboardString(glfw.win);
if (text) nk_textedit_paste(edit, text, nk_strlen(text));
(void)usr;
}
NK_INTERN void
nk_glfw3_clipboard_copy(nk_handle usr, const char *text, int len)
{
char *str = 0;
(void)usr;
if (!len) return;
str = (char*)malloc((size_t)len+1);
if (!str) return;
NK_MEMCPY(str, text, (size_t)len);
str[len] = '\0';
glfwSetClipboardString(glfw.win, str);
free(str);
}
NK_API struct nk_context*
nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state)
{
glfw.win = win;
if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
glfwSetCharCallback(win, nk_glfw3_char_callback);
glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
}
nk_init_default(&glfw.ctx, 0);
glfw.ctx.clip.copy = nk_glfw3_clipboard_copy;
glfw.ctx.clip.paste = nk_glfw3_clipboard_paste;
glfw.ctx.clip.userdata = nk_handle_ptr(0);
nk_buffer_init_default(&glfw.ogl.cmds);
glfw.is_double_click_down = nk_false;
glfw.double_click_pos = nk_vec2(0, 0);
return &glfw.ctx;
}
NK_API void
nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas)
{
nk_font_atlas_init_default(&glfw.atlas);
nk_font_atlas_begin(&glfw.atlas);
*atlas = &glfw.atlas;
}
NK_API void
nk_glfw3_font_stash_end(void)
{
const void *image; int w, h;
image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
nk_glfw3_device_upload_atlas(image, w, h);
nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null);
if (glfw.atlas.default_font)
nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle);
}
NK_API void
nk_glfw3_new_frame(void)
{
int i;
double x, y;
struct nk_context *ctx = &glfw.ctx;
struct GLFWwindow *win = glfw.win;
glfwGetWindowSize(win, &glfw.width, &glfw.height);
glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height);
glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width;
glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height;
nk_input_begin(ctx);
for (i = 0; i < glfw.text_len; ++i)
nk_input_unicode(ctx, glfw.text[i]);
/* optional grabbing behavior */
if (ctx->input.mouse.grab)
glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
else if (ctx->input.mouse.ungrab)
glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
} else {
nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_COPY, 0);
nk_input_key(ctx, NK_KEY_PASTE, 0);
nk_input_key(ctx, NK_KEY_CUT, 0);
nk_input_key(ctx, NK_KEY_SHIFT, 0);
}
glfwGetCursorPos(win, &x, &y);
nk_input_motion(ctx, (int)x, (int)y);
if (ctx->input.mouse.grabbed) {
glfwSetCursorPos(glfw.win, (double)ctx->input.mouse.prev.x, (double)ctx->input.mouse.prev.y);
ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
}
nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw.double_click_pos.x, (int)glfw.double_click_pos.y, glfw.is_double_click_down);
nk_input_scroll(ctx, glfw.scroll);
nk_input_end(&glfw.ctx);
glfw.text_len = 0;
glfw.scroll = nk_vec2(0,0);
}
NK_API
void nk_glfw3_shutdown(void)
{
struct nk_glfw_device *dev = &glfw.ogl;
nk_font_atlas_clear(&glfw.atlas);
nk_free(&glfw.ctx);
glDeleteTextures(1, &dev->font_tex);
nk_buffer_free(&dev->cmds);
NK_MEMSET(&glfw, 0, sizeof(glfw));
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,594 +0,0 @@
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/* 2013-01-06 Camilla Löwy <elmindreda@glfw.org>
*
* Added casts from time_t to DWORD to avoid warnings on VC++.
* Fixed time retrieval on POSIX systems.
*/
#include "tinycthread.h"
#include <stdlib.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#elif defined(_TTHREAD_WIN32_)
#include <process.h>
#include <sys/timeb.h>
#endif
/* Standard, good-to-have defines */
#ifndef NULL
#define NULL (void*)0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
int mtx_init(mtx_t *mtx, int type)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
mtx->mRecursive = type & mtx_recursive;
InitializeCriticalSection(&mtx->mHandle);
return thrd_success;
#else
int ret;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
if (type & mtx_recursive)
{
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
}
ret = pthread_mutex_init(mtx, &attr);
pthread_mutexattr_destroy(&attr);
return ret == 0 ? thrd_success : thrd_error;
#endif
}
void mtx_destroy(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mtx->mHandle);
#else
pthread_mutex_destroy(mtx);
#endif
}
int mtx_lock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mtx->mHandle);
if (!mtx->mRecursive)
{
while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */
mtx->mAlreadyLocked = TRUE;
}
return thrd_success;
#else
return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
{
/* FIXME! */
(void)mtx;
(void)ts;
return thrd_error;
}
int mtx_trylock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy;
if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked)
{
LeaveCriticalSection(&mtx->mHandle);
ret = thrd_busy;
}
return ret;
#else
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
#endif
}
int mtx_unlock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
LeaveCriticalSection(&mtx->mHandle);
return thrd_success;
#else
return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
#endif
}
#if defined(_TTHREAD_WIN32_)
#define _CONDITION_EVENT_ONE 0
#define _CONDITION_EVENT_ALL 1
#endif
int cnd_init(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
cond->mWaitersCount = 0;
/* Init critical section */
InitializeCriticalSection(&cond->mWaitersCountLock);
/* Init events */
cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
{
cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
return thrd_error;
}
cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
return thrd_error;
}
return thrd_success;
#else
return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
#endif
}
void cnd_destroy(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
}
if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
}
DeleteCriticalSection(&cond->mWaitersCountLock);
#else
pthread_cond_destroy(cond);
#endif
}
int cnd_signal(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_broadcast(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
#endif
}
#if defined(_TTHREAD_WIN32_)
static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
{
int result, lastWaiter;
/* Increment number of waiters */
EnterCriticalSection(&cond->mWaitersCountLock);
++ cond->mWaitersCount;
LeaveCriticalSection(&cond->mWaitersCountLock);
/* Release the mutex while waiting for the condition (will decrease
the number of waiters when done)... */
mtx_unlock(mtx);
/* Wait for either event to become signaled due to cnd_signal() or
cnd_broadcast() being called */
result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
if (result == WAIT_TIMEOUT)
{
return thrd_timeout;
}
else if (result == (int)WAIT_FAILED)
{
return thrd_error;
}
/* Check if we are the last waiter */
EnterCriticalSection(&cond->mWaitersCountLock);
-- cond->mWaitersCount;
lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
(cond->mWaitersCount == 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we are the last waiter to be notified to stop waiting, reset the event */
if (lastWaiter)
{
if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
return thrd_error;
}
}
/* Re-acquire the mutex */
mtx_lock(mtx);
return thrd_success;
}
#endif
int cnd_wait(cnd_t *cond, mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
return _cnd_timedwait_win32(cond, mtx, INFINITE);
#else
return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &now) == 0)
{
DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 +
(ts->tv_nsec - now.tv_nsec + 500000) / 1000000);
return _cnd_timedwait_win32(cond, mtx, delta);
}
else
return thrd_error;
#else
int ret;
ret = pthread_cond_timedwait(cond, mtx, ts);
if (ret == ETIMEDOUT)
{
return thrd_timeout;
}
return ret == 0 ? thrd_success : thrd_error;
#endif
}
/** Information to pass to the new thread (what to run). */
typedef struct {
thrd_start_t mFunction; /**< Pointer to the function to be executed. */
void * mArg; /**< Function argument for the thread function. */
} _thread_start_info;
/* Thread wrapper function. */
#if defined(_TTHREAD_WIN32_)
static unsigned WINAPI _thrd_wrapper_function(void * aArg)
#elif defined(_TTHREAD_POSIX_)
static void * _thrd_wrapper_function(void * aArg)
#endif
{
thrd_start_t fun;
void *arg;
int res;
#if defined(_TTHREAD_POSIX_)
void *pres;
#endif
/* Get thread startup information */
_thread_start_info *ti = (_thread_start_info *) aArg;
fun = ti->mFunction;
arg = ti->mArg;
/* The thread is responsible for freeing the startup information */
free((void *)ti);
/* Call the actual client thread function */
res = fun(arg);
#if defined(_TTHREAD_WIN32_)
return res;
#else
pres = malloc(sizeof(int));
if (pres != NULL)
{
*(int*)pres = res;
}
return pres;
#endif
}
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{
/* Fill out the thread startup information (passed to the thread wrapper,
which will eventually free it) */
_thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
if (ti == NULL)
{
return thrd_nomem;
}
ti->mFunction = func;
ti->mArg = arg;
/* Create the thread */
#if defined(_TTHREAD_WIN32_)
*thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL);
#elif defined(_TTHREAD_POSIX_)
if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
{
*thr = 0;
}
#endif
/* Did we fail to create the thread? */
if(!*thr)
{
free(ti);
return thrd_error;
}
return thrd_success;
}
thrd_t thrd_current(void)
{
#if defined(_TTHREAD_WIN32_)
return GetCurrentThread();
#else
return pthread_self();
#endif
}
int thrd_detach(thrd_t thr)
{
/* FIXME! */
(void)thr;
return thrd_error;
}
int thrd_equal(thrd_t thr0, thrd_t thr1)
{
#if defined(_TTHREAD_WIN32_)
return thr0 == thr1;
#else
return pthread_equal(thr0, thr1);
#endif
}
void thrd_exit(int res)
{
#if defined(_TTHREAD_WIN32_)
ExitThread(res);
#else
void *pres = malloc(sizeof(int));
if (pres != NULL)
{
*(int*)pres = res;
}
pthread_exit(pres);
#endif
}
int thrd_join(thrd_t thr, int *res)
{
#if defined(_TTHREAD_WIN32_)
if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
{
return thrd_error;
}
if (res != NULL)
{
DWORD dwRes;
GetExitCodeThread(thr, &dwRes);
*res = dwRes;
}
#elif defined(_TTHREAD_POSIX_)
void *pres;
int ires = 0;
if (pthread_join(thr, &pres) != 0)
{
return thrd_error;
}
if (pres != NULL)
{
ires = *(int*)pres;
free(pres);
}
if (res != NULL)
{
*res = ires;
}
#endif
return thrd_success;
}
int thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
{
struct timespec now;
#if defined(_TTHREAD_WIN32_)
DWORD delta;
#else
long delta;
#endif
/* Get the current time */
if (clock_gettime(CLOCK_REALTIME, &now) != 0)
return -2; // FIXME: Some specific error code?
#if defined(_TTHREAD_WIN32_)
/* Delta in milliseconds */
delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 +
(time_point->tv_nsec - now.tv_nsec + 500000) / 1000000);
if (delta > 0)
{
Sleep(delta);
}
#else
/* Delta in microseconds */
delta = (time_point->tv_sec - now.tv_sec) * 1000000L +
(time_point->tv_nsec - now.tv_nsec + 500L) / 1000L;
/* On some systems, the usleep argument must be < 1000000 */
while (delta > 999999L)
{
usleep(999999);
delta -= 999999L;
}
if (delta > 0L)
{
usleep((useconds_t)delta);
}
#endif
/* We don't support waking up prematurely (yet) */
if (remaining)
{
remaining->tv_sec = 0;
remaining->tv_nsec = 0;
}
return 0;
}
void thrd_yield(void)
{
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#else
sched_yield();
#endif
}
int tss_create(tss_t *key, tss_dtor_t dtor)
{
#if defined(_TTHREAD_WIN32_)
/* FIXME: The destructor function is not supported yet... */
if (dtor != NULL)
{
return thrd_error;
}
*key = TlsAlloc();
if (*key == TLS_OUT_OF_INDEXES)
{
return thrd_error;
}
#else
if (pthread_key_create(key, dtor) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
void tss_delete(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
TlsFree(key);
#else
pthread_key_delete(key);
#endif
}
void *tss_get(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
return TlsGetValue(key);
#else
return pthread_getspecific(key);
#endif
}
int tss_set(tss_t key, void *val)
{
#if defined(_TTHREAD_WIN32_)
if (TlsSetValue(key, val) == 0)
{
return thrd_error;
}
#else
if (pthread_setspecific(key, val) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
#if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_)
int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct _timeb tb;
_ftime(&tb);
ts->tv_sec = (time_t)tb.time;
ts->tv_nsec = 1000000L * (long)tb.millitm;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = (time_t)tv.tv_sec;
ts->tv_nsec = 1000L * (long)tv.tv_usec;
#endif
return 0;
}
#endif // _TTHREAD_EMULATE_CLOCK_GETTIME_

View File

@ -1,443 +0,0 @@
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _TINYCTHREAD_H_
#define _TINYCTHREAD_H_
/**
* @file
* @mainpage TinyCThread API Reference
*
* @section intro_sec Introduction
* TinyCThread is a minimal, portable implementation of basic threading
* classes for C.
*
* They closely mimic the functionality and naming of the C11 standard, and
* should be easily replaceable with the corresponding standard variants.
*
* @section port_sec Portability
* The Win32 variant uses the native Win32 API for implementing the thread
* classes, while for other systems, the POSIX threads API (pthread) is used.
*
* @section misc_sec Miscellaneous
* The following special keywords are available: #_Thread_local.
*
* For more detailed information, browse the different sections of this
* documentation. A good place to start is:
* tinycthread.h.
*/
/* Which platform are we on? */
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
#if defined(_TTHREAD_POSIX_)
#undef _FEATURES_H
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#endif
/* Generic includes */
#include <time.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <sys/time.h>
#include <pthread.h>
#elif defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#endif
/* Workaround for missing TIME_UTC: If time.h doesn't provide TIME_UTC,
it's quite likely that libc does not support it either. Hence, fall back to
the only other supported time specifier: CLOCK_REALTIME (and if that fails,
we're probably emulating clock_gettime anyway, so anything goes). */
#ifndef TIME_UTC
#ifdef CLOCK_REALTIME
#define TIME_UTC CLOCK_REALTIME
#else
#define TIME_UTC 0
#endif
#endif
/* Workaround for missing clock_gettime (most Windows compilers, afaik) */
#if defined(_TTHREAD_WIN32_) || defined(__APPLE_CC__)
#define _TTHREAD_EMULATE_CLOCK_GETTIME_
/* Emulate struct timespec */
#if defined(_TTHREAD_WIN32_)
struct _ttherad_timespec {
time_t tv_sec;
long tv_nsec;
};
#define timespec _ttherad_timespec
#endif
/* Emulate clockid_t */
typedef int _tthread_clockid_t;
#define clockid_t _tthread_clockid_t
/* Emulate clock_gettime */
int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts);
#define clock_gettime _tthread_clock_gettime
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
#endif
/** TinyCThread version (major number). */
#define TINYCTHREAD_VERSION_MAJOR 1
/** TinyCThread version (minor number). */
#define TINYCTHREAD_VERSION_MINOR 1
/** TinyCThread version (full version). */
#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
/**
* @def _Thread_local
* Thread local storage keyword.
* A variable that is declared with the @c _Thread_local keyword makes the
* value of the variable local to each thread (known as thread-local storage,
* or TLS). Example usage:
* @code
* // This variable is local to each thread.
* _Thread_local int variable;
* @endcode
* @note The @c _Thread_local keyword is a macro that maps to the corresponding
* compiler directive (e.g. @c __declspec(thread)).
* @note This directive is currently not supported on Mac OS X (it will give
* a compiler error), since compile-time TLS is not supported in the Mac OS X
* executable format. Also, some older versions of MinGW (before GCC 4.x) do
* not support this directive.
* @hideinitializer
*/
/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 */
#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define _Thread_local __thread
#else
#define _Thread_local __declspec(thread)
#endif
#endif
/* Macros */
#define TSS_DTOR_ITERATIONS 0
/* Function return values */
#define thrd_error 0 /**< The requested operation failed */
#define thrd_success 1 /**< The requested operation succeeded */
#define thrd_timeout 2 /**< The time specified in the call was reached without acquiring the requested resource */
#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
/* Mutex types */
#define mtx_plain 1
#define mtx_timed 2
#define mtx_try 4
#define mtx_recursive 8
/* Mutex */
#if defined(_TTHREAD_WIN32_)
typedef struct {
CRITICAL_SECTION mHandle; /* Critical section handle */
int mAlreadyLocked; /* TRUE if the mutex is already locked */
int mRecursive; /* TRUE if the mutex is recursive */
} mtx_t;
#else
typedef pthread_mutex_t mtx_t;
#endif
/** Create a mutex object.
* @param mtx A mutex object.
* @param type Bit-mask that must have one of the following six values:
* @li @c mtx_plain for a simple non-recursive mutex
* @li @c mtx_timed for a non-recursive mutex that supports timeout
* @li @c mtx_try for a non-recursive mutex that supports test and return
* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
* @li @c mtx_try | @c mtx_recursive (same as @c mtx_try, but recursive)
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_init(mtx_t *mtx, int type);
/** Release any resources used by the given mutex.
* @param mtx A mutex object.
*/
void mtx_destroy(mtx_t *mtx);
/** Lock the given mutex.
* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
* the calling thread already has a lock on the mutex, this call will block
* forever.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_lock(mtx_t *mtx);
/** NOT YET IMPLEMENTED.
*/
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
/** Try to lock the given mutex.
* The specified mutex shall support either test and return or timeout. If the
* mutex is already locked, the function returns without blocking.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_busy if the resource
* requested is already in use, or @ref thrd_error if the request could not be
* honored.
*/
int mtx_trylock(mtx_t *mtx);
/** Unlock the given mutex.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_unlock(mtx_t *mtx);
/* Condition variable */
#if defined(_TTHREAD_WIN32_)
typedef struct {
HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
unsigned int mWaitersCount; /* Count of the number of waiters. */
CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
} cnd_t;
#else
typedef pthread_cond_t cnd_t;
#endif
/** Create a condition variable object.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_init(cnd_t *cond);
/** Release any resources used by the given condition variable.
* @param cond A condition variable object.
*/
void cnd_destroy(cnd_t *cond);
/** Signal a condition variable.
* Unblocks one of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_signal(cnd_t *cond);
/** Broadcast a condition variable.
* Unblocks all of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_broadcast(cnd_t *cond);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
* before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_wait(cnd_t *cond, mtx_t *mtx);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast, or until after the specified time. When the calling thread
* becomes unblocked it locks the mutex before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @param xt A point in time at which the request will time out (absolute time).
* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
* specified in the call was reached without acquiring the requested resource, or
* @ref thrd_error if the request could not be honored.
*/
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
/* Thread */
#if defined(_TTHREAD_WIN32_)
typedef HANDLE thrd_t;
#else
typedef pthread_t thrd_t;
#endif
/** Thread start function.
* Any thread that is started with the @ref thrd_create() function must be
* started through a function of this type.
* @param arg The thread argument (the @c arg argument of the corresponding
* @ref thrd_create() call).
* @return The thread return value, which can be obtained by another thread
* by using the @ref thrd_join() function.
*/
typedef int (*thrd_start_t)(void *arg);
/** Create a new thread.
* @param thr Identifier of the newly created thread.
* @param func A function pointer to the function that will be executed in
* the new thread.
* @param arg An argument to the thread function.
* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
* be allocated for the thread requested, or @ref thrd_error if the request
* could not be honored.
* @note A threads identifier may be reused for a different thread once the
* original thread has exited and either been detached or joined to another
* thread.
*/
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
/** Identify the calling thread.
* @return The identifier of the calling thread.
*/
thrd_t thrd_current(void);
/** NOT YET IMPLEMENTED.
*/
int thrd_detach(thrd_t thr);
/** Compare two thread identifiers.
* The function determines if two thread identifiers refer to the same thread.
* @return Zero if the two thread identifiers refer to different threads.
* Otherwise a nonzero value is returned.
*/
int thrd_equal(thrd_t thr0, thrd_t thr1);
/** Terminate execution of the calling thread.
* @param res Result code of the calling thread.
*/
void thrd_exit(int res);
/** Wait for a thread to terminate.
* The function joins the given thread with the current thread by blocking
* until the other thread has terminated.
* @param thr The thread to join with.
* @param res If this pointer is not NULL, the function will store the result
* code of the given thread in the integer pointed to by @c res.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int thrd_join(thrd_t thr, int *res);
/** Put the calling thread to sleep.
* Suspend execution of the calling thread.
* @param time_point A point in time at which the thread will resume (absolute time).
* @param remaining If non-NULL, this parameter will hold the remaining time until
* time_point upon return. This will typically be zero, but if
* the thread was woken up by a signal that is not ignored before
* time_point was reached @c remaining will hold a positive
* time.
* @return 0 (zero) on successful sleep, or -1 if an interrupt occurred.
*/
int thrd_sleep(const struct timespec *time_point, struct timespec *remaining);
/** Yield execution to another thread.
* Permit other threads to run, even if the current thread would ordinarily
* continue to run.
*/
void thrd_yield(void);
/* Thread local storage */
#if defined(_TTHREAD_WIN32_)
typedef DWORD tss_t;
#else
typedef pthread_key_t tss_t;
#endif
/** Destructor function for a thread-specific storage.
* @param val The value of the destructed thread-specific storage.
*/
typedef void (*tss_dtor_t)(void *val);
/** Create a thread-specific storage.
* @param key The unique key identifier that will be set if the function is
* successful.
* @param dtor Destructor function. This can be NULL.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
* @note The destructor function is not supported under Windows. If @c dtor is
* not NULL when calling this function under Windows, the function will fail
* and return @ref thrd_error.
*/
int tss_create(tss_t *key, tss_dtor_t dtor);
/** Delete a thread-specific storage.
* The function releases any resources used by the given thread-specific
* storage.
* @param key The key that shall be deleted.
*/
void tss_delete(tss_t key);
/** Get the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @return The value for the current thread held in the given thread-specific
* storage.
*/
void *tss_get(tss_t key);
/** Set the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @param val The value of the thread-specific storage to set for the current
* thread.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int tss_set(tss_t key, void *val);
#endif /* _TINYTHREAD_H_ */

View File

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="fractional_scale_v1">
<copyright>
Copyright © 2022 Kenny Levinsen
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 (including the next
paragraph) 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.
</copyright>
<description summary="Protocol for requesting fractional surface scales">
This protocol allows a compositor to suggest for surfaces to render at
fractional scales.
A client can submit scaled content by utilizing wp_viewport. This is done by
creating a wp_viewport object for the surface and setting the destination
rectangle to the surface size before the scale factor is applied.
The buffer size is calculated by multiplying the surface size by the
intended scale.
The wl_surface buffer scale should remain set to 1.
If a surface has a surface-local size of 100 px by 50 px and wishes to
submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should
be used and the wp_viewport destination rectangle should be 100 px by 50 px.
For toplevel surfaces, the size is rounded halfway away from zero. The
rounding algorithm for subsurface position and size is not defined.
</description>
<interface name="wp_fractional_scale_manager_v1" version="1">
<description summary="fractional surface scale information">
A global interface for requesting surfaces to use fractional scales.
</description>
<request name="destroy" type="destructor">
<description summary="unbind the fractional surface scale interface">
Informs the server that the client will not be using this protocol
object anymore. This does not affect any other objects,
wp_fractional_scale_v1 objects included.
</description>
</request>
<enum name="error">
<entry name="fractional_scale_exists" value="0"
summary="the surface already has a fractional_scale object associated"/>
</enum>
<request name="get_fractional_scale">
<description summary="extend surface interface for scale information">
Create an add-on object for the the wl_surface to let the compositor
request fractional scales. If the given wl_surface already has a
wp_fractional_scale_v1 object associated, the fractional_scale_exists
protocol error is raised.
</description>
<arg name="id" type="new_id" interface="wp_fractional_scale_v1"
summary="the new surface scale info interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
</interface>
<interface name="wp_fractional_scale_v1" version="1">
<description summary="fractional scale interface to a wl_surface">
An additional interface to a wl_surface object which allows the compositor
to inform the client of the preferred scale.
</description>
<request name="destroy" type="destructor">
<description summary="remove surface scale information for surface">
Destroy the fractional scale object. When this object is destroyed,
preferred_scale events will no longer be sent.
</description>
</request>
<event name="preferred_scale">
<description summary="notify of new preferred scale">
Notification of a new preferred scale for this surface that the
compositor suggests that the client should use.
The sent scale is the numerator of a fraction with a denominator of 120.
</description>
<arg name="scale" type="uint" summary="the new preferred scale"/>
</event>
</interface>
</protocol>

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle_inhibit_unstable_v1">
<copyright>
Copyright © 2015 Samsung Electronics Co., Ltd
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 (including the next
paragraph) 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.
</copyright>
<interface name="zwp_idle_inhibit_manager_v1" version="1">
<description summary="control behavior when display idles">
This interface permits inhibiting the idle behavior such as screen
blanking, locking, and screensaving. The client binds the idle manager
globally, then creates idle-inhibitor objects for each surface.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Destroy the inhibit manager.
</description>
</request>
<request name="create_inhibitor">
<description summary="create a new inhibitor object">
Create a new inhibitor object associated with the given surface.
</description>
<arg name="id" type="new_id" interface="zwp_idle_inhibitor_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface that inhibits the idle behavior"/>
</request>
</interface>
<interface name="zwp_idle_inhibitor_v1" version="1">
<description summary="context object for inhibiting idle behavior">
An idle inhibitor prevents the output that the associated surface is
visible on from being set to a state where it is not visually usable due
to lack of user interaction (e.g. blanked, dimmed, locked, set to power
save, etc.) Any screensaver processes are also blocked from displaying.
If the surface is destroyed, unmapped, becomes occluded, loses
visibility, or otherwise becomes not visually relevant for the user, the
idle inhibitor will not be honored by the compositor; if the surface
subsequently regains visibility the inhibitor takes effect once again.
Likewise, the inhibitor isn't honored if the system was already idled at
the time the inhibitor was established, although if the system later
de-idles and re-idles the inhibitor will take effect.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Remove the inhibitor effect from the associated wl_surface.
</description>
</request>
</interface>
</protocol>

View File

@ -1,339 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="pointer_constraints_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
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 (including the next
paragraph) 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.
</copyright>
<description summary="protocol for constraining pointer motions">
This protocol specifies a set of interfaces used for adding constraints to
the motion of a pointer. Possible constraints include confining pointer
motions to a given region, or locking it to its current position.
In order to constrain the pointer, a client must first bind the global
interface "wp_pointer_constraints" which, if a compositor supports pointer
constraints, is exposed by the registry. Using the bound global object, the
client uses the request that corresponds to the type of constraint it wants
to make. See wp_pointer_constraints for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_pointer_constraints_v1" version="1">
<description summary="constrain the movement of a pointer">
The global interface exposing pointer constraining functionality. It
exposes two requests: lock_pointer for locking the pointer to its
position, and confine_pointer for locking the pointer to a region.
The lock_pointer and confine_pointer requests create the objects
wp_locked_pointer and wp_confined_pointer respectively, and the client can
use these objects to interact with the lock.
For any surface, only one lock or confinement may be active across all
wl_pointer objects of the same seat. If a lock or confinement is requested
when another lock or confinement is active or requested on the same surface
and with any of the wl_pointer objects of the same seat, an
'already_constrained' error will be raised.
</description>
<enum name="error">
<description summary="wp_pointer_constraints error values">
These errors can be emitted in response to wp_pointer_constraints
requests.
</description>
<entry name="already_constrained" value="1"
summary="pointer constraint already requested on that surface"/>
</enum>
<enum name="lifetime">
<description summary="constraint lifetime">
These values represent different lifetime semantics. They are passed
as arguments to the factory requests to specify how the constraint
lifetimes should be managed.
</description>
<entry name="oneshot" value="1">
<description summary="the pointer constraint is defunct once deactivated">
A oneshot pointer constraint will never reactivate once it has been
deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
<entry name="persistent" value="2">
<description summary="the pointer constraint may reactivate">
A persistent pointer constraint may again reactivate once it has
been deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the pointer constraints manager object">
Used by the client to notify the server that it will no longer use this
pointer constraints object.
</description>
</request>
<request name="lock_pointer">
<description summary="lock pointer to a position">
The lock_pointer request lets the client request to disable movements of
the virtual pointer (i.e. the cursor), effectively locking the pointer
to a position. This request may not take effect immediately; in the
future, when the compositor deems implementation-specific constraints
are satisfied, the pointer lock will be activated and the compositor
sends a locked event.
The protocol provides no guarantee that the constraints are ever
satisfied, and does not require the compositor to send an error if the
constraints cannot ever be satisfied. It is thus possible to request a
lock that will never activate.
There may not be another pointer constraint of any kind requested or
active on the surface for any of the wl_pointer objects of the seat of
the passed pointer when requesting a lock. If there is, an error will be
raised. See general pointer lock documentation for more details.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the lock to activate. It is up to the compositor whether to
warp the pointer or require some kind of user interaction for the lock
to activate. If the region is null the surface input region is used.
A surface may receive pointer focus without the lock being activated.
The request creates a new object wp_locked_pointer which is used to
interact with the lock as well as receive updates about its state. See
the the description of wp_locked_pointer for further information.
Note that while a pointer is locked, the wl_pointer objects of the
corresponding seat will not emit any wl_pointer.motion events, but
relative motion events will still be emitted via wp_relative_pointer
objects of the same seat. wl_pointer.axis and wl_pointer.button events
are unaffected.
</description>
<arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be locked"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="lock lifetime"/>
</request>
<request name="confine_pointer">
<description summary="confine pointer to a region">
The confine_pointer request lets the client request to confine the
pointer cursor to a given region. This request may not take effect
immediately; in the future, when the compositor deems implementation-
specific constraints are satisfied, the pointer confinement will be
activated and the compositor sends a confined event.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the confinement to activate. It is up to the compositor
whether to warp the pointer or require some kind of user interaction for
the confinement to activate. If the region is null the surface input
region is used.
The request will create a new object wp_confined_pointer which is used
to interact with the confinement as well as receive updates about its
state. See the the description of wp_confined_pointer for further
information.
</description>
<arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be confined"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="confinement lifetime"/>
</request>
</interface>
<interface name="zwp_locked_pointer_v1" version="1">
<description summary="receive relative pointer motion events">
The wp_locked_pointer interface represents a locked pointer state.
While the lock of this object is active, the wl_pointer objects of the
associated seat will not emit any wl_pointer.motion events.
This object will send the event 'locked' when the lock is activated.
Whenever the lock is activated, it is guaranteed that the locked surface
will already have received pointer focus and that the pointer will be
within the region passed to the request creating this object.
To unlock the pointer, send the destroy request. This will also destroy
the wp_locked_pointer object.
If the compositor decides to unlock the pointer the unlocked event is
sent. See wp_locked_pointer.unlock for details.
When unlocking, the compositor may warp the cursor position to the set
cursor position hint. If it does, it will not result in any relative
motion events emitted via wp_relative_pointer.
If the surface the lock was requested on is destroyed and the lock is not
yet activated, the wp_locked_pointer object is now defunct and must be
destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the locked pointer object">
Destroy the locked pointer object. If applicable, the compositor will
unlock the pointer.
</description>
</request>
<request name="set_cursor_position_hint">
<description summary="set the pointer cursor position hint">
Set the cursor position hint relative to the top left corner of the
surface.
If the client is drawing its own cursor, it should update the position
hint to the position of its own cursor. A compositor may use this
information to warp the pointer upon unlock in order to avoid pointer
jumps.
The cursor position hint is double buffered. The new hint will only take
effect when the associated surface gets it pending state applied. See
wl_surface.commit for details.
</description>
<arg name="surface_x" type="fixed"
summary="surface-local x coordinate"/>
<arg name="surface_y" type="fixed"
summary="surface-local y coordinate"/>
</request>
<request name="set_region">
<description summary="set a new lock region">
Set a new region used to lock the pointer.
The new lock region is double-buffered. The new lock region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
For details about the lock region, see wp_locked_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="locked">
<description summary="lock activation event">
Notification that the pointer lock of the seat's pointer is activated.
</description>
</event>
<event name="unlocked">
<description summary="lock deactivation event">
Notification that the pointer lock of the seat's pointer is no longer
active. If this is a oneshot pointer lock (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer lock (see
wp_pointer_constraints.lifetime) this pointer lock may again
reactivate in the future.
</description>
</event>
</interface>
<interface name="zwp_confined_pointer_v1" version="1">
<description summary="confined pointer object">
The wp_confined_pointer interface represents a confined pointer state.
This object will send the event 'confined' when the confinement is
activated. Whenever the confinement is activated, it is guaranteed that
the surface the pointer is confined to will already have received pointer
focus and that the pointer will be within the region passed to the request
creating this object. It is up to the compositor to decide whether this
requires some user interaction and if the pointer will warp to within the
passed region if outside.
To unconfine the pointer, send the destroy request. This will also destroy
the wp_confined_pointer object.
If the compositor decides to unconfine the pointer the unconfined event is
sent. The wp_confined_pointer object is at this point defunct and should
be destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the confined pointer object">
Destroy the confined pointer object. If applicable, the compositor will
unconfine the pointer.
</description>
</request>
<request name="set_region">
<description summary="set a new confine region">
Set a new region used to confine the pointer.
The new confine region is double-buffered. The new confine region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
If the confinement is active when the new confinement region is applied
and the pointer ends up outside of newly applied region, the pointer may
warped to a position within the new confinement region. If warped, a
wl_pointer.motion event will be emitted, but no
wp_relative_pointer.relative_motion event.
The compositor may also, instead of using the new region, unconfine the
pointer.
For details about the confine region, see wp_confined_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="confined">
<description summary="pointer confined">
Notification that the pointer confinement of the seat's pointer is
activated.
</description>
</event>
<event name="unconfined">
<description summary="pointer unconfined">
Notification that the pointer confinement of the seat's pointer is no
longer active. If this is a oneshot pointer confinement (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer confinement (see
wp_pointer_constraints.lifetime) this pointer confinement may again
reactivate in the future.
</description>
</event>
</interface>
</protocol>

View File

@ -1,136 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="relative_pointer_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
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 (including the next
paragraph) 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.
</copyright>
<description summary="protocol for relative pointer motion events">
This protocol specifies a set of interfaces used for making clients able to
receive relative pointer events not obstructed by barriers (such as the
monitor edge or other pointer barriers).
To start receiving relative pointer events, a client must first bind the
global interface "wp_relative_pointer_manager" which, if a compositor
supports relative pointer motion events, is exposed by the registry. After
having created the relative pointer manager proxy object, the client uses
it to create the actual relative pointer object using the
"get_relative_pointer" request given a wl_pointer. The relative pointer
motion events will then, when applicable, be transmitted via the proxy of
the newly created relative pointer object. See the documentation of the
relative pointer interface for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_relative_pointer_manager_v1" version="1">
<description summary="get relative pointer objects">
A global interface used for getting the relative pointer object for a
given pointer.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the relative pointer manager object">
Used by the client to notify the server that it will no longer use this
relative pointer manager object.
</description>
</request>
<request name="get_relative_pointer">
<description summary="get a relative pointer object">
Create a relative pointer interface given a wl_pointer object. See the
wp_relative_pointer interface for more details.
</description>
<arg name="id" type="new_id" interface="zwp_relative_pointer_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
</interface>
<interface name="zwp_relative_pointer_v1" version="1">
<description summary="relative pointer object">
A wp_relative_pointer object is an extension to the wl_pointer interface
used for emitting relative pointer events. It shares the same focus as
wl_pointer objects of the same seat and will only emit events when it has
focus.
</description>
<request name="destroy" type="destructor">
<description summary="release the relative pointer object"/>
</request>
<event name="relative_motion">
<description summary="relative pointer motion">
Relative x/y pointer motion from the pointer of the seat associated with
this object.
A relative motion is in the same dimension as regular wl_pointer motion
events, except they do not represent an absolute position. For example,
moving a pointer from (x, y) to (x', y') would have the equivalent
relative motion (x' - x, y' - y). If a pointer motion caused the
absolute pointer position to be clipped by for example the edge of the
monitor, the relative motion is unaffected by the clipping and will
represent the unclipped motion.
This event also contains non-accelerated motion deltas. The
non-accelerated delta is, when applicable, the regular pointer motion
delta as it was before having applied motion acceleration and other
transformations such as normalization.
Note that the non-accelerated delta does not represent 'raw' events as
they were read from some device. Pointer motion acceleration is device-
and configuration-specific and non-accelerated deltas and accelerated
deltas may have the same value on some devices.
Relative motions are not coupled to wl_pointer.motion events, and can be
sent in combination with such events, but also independently. There may
also be scenarios where wl_pointer.motion is sent, but there is no
relative motion. The order of an absolute and relative motion event
originating from the same physical motion is not guaranteed.
If the client needs button events or focus state, it can receive them
from a wl_pointer object of the same seat that the wp_relative_pointer
object is associated with.
</description>
<arg name="utime_hi" type="uint"
summary="high 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="utime_lo" type="uint"
summary="low 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="dx" type="fixed"
summary="the x component of the motion vector"/>
<arg name="dy" type="fixed"
summary="the y component of the motion vector"/>
<arg name="dx_unaccel" type="fixed"
summary="the x component of the unaccelerated motion vector"/>
<arg name="dy_unaccel" type="fixed"
summary="the y component of the unaccelerated motion vector"/>
</event>
</interface>
</protocol>

View File

@ -1,180 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="viewporter">
<copyright>
Copyright © 2013-2016 Collabora, Ltd.
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 (including the next
paragraph) 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.
</copyright>
<interface name="wp_viewporter" version="1">
<description summary="surface cropping and scaling">
The global interface exposing surface cropping and scaling
capabilities is used to instantiate an interface extension for a
wl_surface object. This extended interface will then allow
cropping and scaling the surface contents, effectively
disconnecting the direct relationship between the buffer and the
surface size.
</description>
<request name="destroy" type="destructor">
<description summary="unbind from the cropping and scaling interface">
Informs the server that the client will not be using this
protocol object anymore. This does not affect any other objects,
wp_viewport objects included.
</description>
</request>
<enum name="error">
<entry name="viewport_exists" value="0"
summary="the surface already has a viewport object associated"/>
</enum>
<request name="get_viewport">
<description summary="extend surface interface for crop and scale">
Instantiate an interface extension for the given wl_surface to
crop and scale its content. If the given wl_surface already has
a wp_viewport object associated, the viewport_exists
protocol error is raised.
</description>
<arg name="id" type="new_id" interface="wp_viewport"
summary="the new viewport interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
</interface>
<interface name="wp_viewport" version="1">
<description summary="crop and scale interface to a wl_surface">
An additional interface to a wl_surface object, which allows the
client to specify the cropping and scaling of the surface
contents.
This interface works with two concepts: the source rectangle (src_x,
src_y, src_width, src_height), and the destination size (dst_width,
dst_height). The contents of the source rectangle are scaled to the
destination size, and content outside the source rectangle is ignored.
This state is double-buffered, and is applied on the next
wl_surface.commit.
The two parts of crop and scale state are independent: the source
rectangle, and the destination size. Initially both are unset, that
is, no scaling is applied. The whole of the current wl_buffer is
used as the source, and the surface size is as defined in
wl_surface.attach.
If the destination size is set, it causes the surface size to become
dst_width, dst_height. The source (rectangle) is scaled to exactly
this size. This overrides whatever the attached wl_buffer size is,
unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
has no content and therefore no size. Otherwise, the size is always
at least 1x1 in surface local coordinates.
If the source rectangle is set, it defines what area of the wl_buffer is
taken as the source. If the source rectangle is set and the destination
size is not set, then src_width and src_height must be integers, and the
surface size becomes the source rectangle size. This results in cropping
without scaling. If src_width or src_height are not integers and
destination size is not set, the bad_size protocol error is raised when
the surface state is applied.
The coordinate transformations from buffer pixel coordinates up to
the surface-local coordinates happen in the following order:
1. buffer_transform (wl_surface.set_buffer_transform)
2. buffer_scale (wl_surface.set_buffer_scale)
3. crop and scale (wp_viewport.set*)
This means, that the source rectangle coordinates of crop and scale
are given in the coordinates after the buffer transform and scale,
i.e. in the coordinates that would be the surface-local coordinates
if the crop and scale was not applied.
If src_x or src_y are negative, the bad_value protocol error is raised.
Otherwise, if the source rectangle is partially or completely outside of
the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
when the surface state is applied. A NULL wl_buffer does not raise the
out_of_buffer error.
If the wl_surface associated with the wp_viewport is destroyed,
all wp_viewport requests except 'destroy' raise the protocol error
no_surface.
If the wp_viewport object is destroyed, the crop and scale
state is removed from the wl_surface. The change will be applied
on the next wl_surface.commit.
</description>
<request name="destroy" type="destructor">
<description summary="remove scaling and cropping from the surface">
The associated wl_surface's crop and scale state is removed.
The change is applied on the next wl_surface.commit.
</description>
</request>
<enum name="error">
<entry name="bad_value" value="0"
summary="negative or zero values in width or height"/>
<entry name="bad_size" value="1"
summary="destination size is not integer"/>
<entry name="out_of_buffer" value="2"
summary="source rectangle extends outside of the content area"/>
<entry name="no_surface" value="3"
summary="the wl_surface was destroyed"/>
</enum>
<request name="set_source">
<description summary="set the source rectangle for cropping">
Set the source rectangle of the associated wl_surface. See
wp_viewport for the description, and relation to the wl_buffer
size.
If all of x, y, width and height are -1.0, the source rectangle is
unset instead. Any other set of values where width or height are zero
or negative, or x or y are negative, raise the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
</description>
<arg name="x" type="fixed" summary="source rectangle x"/>
<arg name="y" type="fixed" summary="source rectangle y"/>
<arg name="width" type="fixed" summary="source rectangle width"/>
<arg name="height" type="fixed" summary="source rectangle height"/>
</request>
<request name="set_destination">
<description summary="set the surface size for scaling">
Set the destination size of the associated wl_surface. See
wp_viewport for the description, and relation to the wl_buffer
size.
If width is -1 and height is -1, the destination size is unset
instead. Any other pair of values for width and height that
contains zero or negative values raises the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
</description>
<arg name="width" type="int" summary="surface width"/>
<arg name="height" type="int" summary="surface height"/>
</request>
</interface>
</protocol>

File diff suppressed because it is too large Load Diff

View File

@ -1,200 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_activation_v1">
<copyright>
Copyright © 2020 Aleix Pol Gonzalez &lt;aleixpol@kde.org&gt;
Copyright © 2020 Carlos Garnacho &lt;carlosg@gnome.org&gt;
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 (including the next
paragraph) 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.
</copyright>
<description summary="Protocol for requesting activation of surfaces">
The way for a client to pass focus to another toplevel is as follows.
The client that intends to activate another toplevel uses the
xdg_activation_v1.get_activation_token request to get an activation token.
This token is then forwarded to the client, which is supposed to activate
one of its surfaces, through a separate band of communication.
One established way of doing this is through the XDG_ACTIVATION_TOKEN
environment variable of a newly launched child process. The child process
should unset the environment variable again right after reading it out in
order to avoid propagating it to other child processes.
Another established way exists for Applications implementing the D-Bus
interface org.freedesktop.Application, which should get their token under
activation-token on their platform_data.
In general activation tokens may be transferred across clients through
means not described in this protocol.
The client to be activated will then pass the token
it received to the xdg_activation_v1.activate request. The compositor can
then use this token to decide how to react to the activation request.
The token the activating client gets may be ineffective either already at
the time it receives it, for example if it was not focused, for focus
stealing prevention. The activating client will have no way to discover
the validity of the token, and may still forward it to the to be activated
client.
The created activation token may optionally get information attached to it
that can be used by the compositor to identify the application that we
intend to activate. This can for example be used to display a visual hint
about what application is being started.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<interface name="xdg_activation_v1" version="1">
<description summary="interface for activating surfaces">
A global interface used for informing the compositor about applications
being activated or started, or for applications to request to be
activated.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_activation object">
Notify the compositor that the xdg_activation object will no longer be
used.
The child objects created via this interface are unaffected and should
be destroyed separately.
</description>
</request>
<request name="get_activation_token">
<description summary="requests a token">
Creates an xdg_activation_token_v1 object that will provide
the initiating client with a unique token for this activation. This
token should be offered to the clients to be activated.
</description>
<arg name="id" type="new_id" interface="xdg_activation_token_v1"/>
</request>
<request name="activate">
<description summary="notify new interaction being available">
Requests surface activation. It's up to the compositor to display
this information as desired, for example by placing the surface above
the rest.
The compositor may know who requested this by checking the activation
token and might decide not to follow through with the activation if it's
considered unwanted.
Compositors can ignore unknown activation tokens when an invalid
token is passed.
</description>
<arg name="token" type="string" summary="the activation token of the initiating client"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the wl_surface to activate"/>
</request>
</interface>
<interface name="xdg_activation_token_v1" version="1">
<description summary="an exported activation handle">
An object for setting up a token and receiving a token handle that can
be passed as an activation token to another client.
The object is created using the xdg_activation_v1.get_activation_token
request. This object should then be populated with the app_id, surface
and serial information and committed. The compositor shall then issue a
done event with the token. In case the request's parameters are invalid,
the compositor will provide an invalid token.
</description>
<enum name="error">
<entry name="already_used" value="0"
summary="The token has already been used previously"/>
</enum>
<request name="set_serial">
<description summary="specifies the seat and serial of the activating event">
Provides information about the seat and serial event that requested the
token.
The serial can come from an input or focus event. For instance, if a
click triggers the launch of a third-party client, the launcher client
should send a set_serial request with the serial and seat from the
wl_pointer.button event.
Some compositors might refuse to activate toplevels when the token
doesn't have a valid and recent enough event serial.
Must be sent before commit. This information is optional.
</description>
<arg name="serial" type="uint"
summary="the serial of the event that triggered the activation"/>
<arg name="seat" type="object" interface="wl_seat"
summary="the wl_seat of the event"/>
</request>
<request name="set_app_id">
<description summary="specifies the application being activated">
The requesting client can specify an app_id to associate the token
being created with it.
Must be sent before commit. This information is optional.
</description>
<arg name="app_id" type="string"
summary="the application id of the client being activated."/>
</request>
<request name="set_surface">
<description summary="specifies the surface requesting activation">
This request sets the surface requesting the activation. Note, this is
different from the surface that will be activated.
Some compositors might refuse to activate toplevels when the token
doesn't have a requesting surface.
Must be sent before commit. This information is optional.
</description>
<arg name="surface" type="object" interface="wl_surface"
summary="the requesting surface"/>
</request>
<request name="commit">
<description summary="issues the token request">
Requests an activation token based on the different parameters that
have been offered through set_serial, set_surface and set_app_id.
</description>
</request>
<event name="done">
<description summary="the exported activation token">
The 'done' event contains the unique token of this activation request
and notifies that the provider is done.
</description>
<arg name="token" type="string" summary="the exported activation token"/>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_activation_token_v1 object">
Notify the compositor that the xdg_activation_token_v1 object will no
longer be used. The received token stays valid.
</description>
</request>
</interface>
</protocol>

View File

@ -1,156 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_decoration_unstable_v1">
<copyright>
Copyright © 2018 Simon Ser
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 (including the next
paragraph) 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.
</copyright>
<interface name="zxdg_decoration_manager_v1" version="1">
<description summary="window decoration manager">
This interface allows a compositor to announce support for server-side
decorations.
A window decoration is a set of window controls as deemed appropriate by
the party managing them, such as user interface components used to move,
resize and change a window's state.
A client can use this protocol to request being decorated by a supporting
compositor.
If compositor and client do not negotiate the use of a server-side
decoration using this protocol, clients continue to self-decorate as they
see fit.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the decoration manager object">
Destroy the decoration manager. This doesn't destroy objects created
with the manager.
</description>
</request>
<request name="get_toplevel_decoration">
<description summary="create a new toplevel decoration object">
Create a new decoration object associated with the given toplevel.
Creating an xdg_toplevel_decoration from an xdg_toplevel which has a
buffer attached or committed is a client error, and any attempts by a
client to attach or manipulate a buffer prior to the first
xdg_toplevel_decoration.configure event must also be treated as
errors.
</description>
<arg name="id" type="new_id" interface="zxdg_toplevel_decoration_v1"/>
<arg name="toplevel" type="object" interface="xdg_toplevel"/>
</request>
</interface>
<interface name="zxdg_toplevel_decoration_v1" version="1">
<description summary="decoration object for a toplevel surface">
The decoration object allows the compositor to toggle server-side window
decorations for a toplevel surface. The client can request to switch to
another mode.
The xdg_toplevel_decoration object must be destroyed before its
xdg_toplevel.
</description>
<enum name="error">
<entry name="unconfigured_buffer" value="0"
summary="xdg_toplevel has a buffer attached before configure"/>
<entry name="already_constructed" value="1"
summary="xdg_toplevel already has a decoration object"/>
<entry name="orphaned" value="2"
summary="xdg_toplevel destroyed before the decoration object"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the decoration object">
Switch back to a mode without any server-side decorations at the next
commit.
</description>
</request>
<enum name="mode">
<description summary="window decoration modes">
These values describe window decoration modes.
</description>
<entry name="client_side" value="1"
summary="no server-side window decoration"/>
<entry name="server_side" value="2"
summary="server-side window decoration"/>
</enum>
<request name="set_mode">
<description summary="set the decoration mode">
Set the toplevel surface decoration mode. This informs the compositor
that the client prefers the provided decoration mode.
After requesting a decoration mode, the compositor will respond by
emitting an xdg_surface.configure event. The client should then update
its content, drawing it without decorations if the received mode is
server-side decorations. The client must also acknowledge the configure
when committing the new content (see xdg_surface.ack_configure).
The compositor can decide not to use the client's mode and enforce a
different mode instead.
Clients whose decoration mode depend on the xdg_toplevel state may send
a set_mode request in response to an xdg_surface.configure event and wait
for the next xdg_surface.configure event to prevent unwanted state.
Such clients are responsible for preventing configure loops and must
make sure not to send multiple successive set_mode requests with the
same decoration mode.
</description>
<arg name="mode" type="uint" enum="mode" summary="the decoration mode"/>
</request>
<request name="unset_mode">
<description summary="unset the decoration mode">
Unset the toplevel surface decoration mode. This informs the compositor
that the client doesn't prefer a particular decoration mode.
This request has the same semantics as set_mode.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to change its decoration mode. The
configured state should not be applied immediately. Clients must send an
ack_configure in response to this event. See xdg_surface.configure and
xdg_surface.ack_configure for details.
A configure event can be sent at any time. The specified mode must be
obeyed by the client.
</description>
<arg name="mode" type="uint" enum="mode" summary="the decoration mode"/>
</event>
</interface>
</protocol>

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More